ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/s_bsd.c
Revision: 4461
Committed: Wed Aug 13 17:05:26 2014 UTC (11 years ago) by michael
Content type: text/x-csrc
File size: 21660 byte(s)
Log Message:
- Removed stupid PF typedef in fdlist.h which prevented both gcc and clang from spitting out
  a warning about the first argument of s_bsd:ssl_handshake() being an 'int' when it really
  has to be a 'fde_t' pointer.
- Fixed first argument of s_bsd:ssl_handshake() which should be a 'fde_t' pointer instead of an 'int'.

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2014 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 /*! \file s_bsd.c
23 * \brief Network functions.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #ifdef HAVE_LIBCRYPTO
29 #include "rsa.h"
30 #endif
31 #include <netinet/in_systm.h>
32 #include <netinet/ip.h>
33 #include <netinet/tcp.h>
34 #include "list.h"
35 #include "fdlist.h"
36 #include "s_bsd.h"
37 #include "client.h"
38 #include "dbuf.h"
39 #include "event.h"
40 #include "irc_string.h"
41 #include "ircd.h"
42 #include "listener.h"
43 #include "numeric.h"
44 #include "packet.h"
45 #include "res.h"
46 #include "restart.h"
47 #include "auth.h"
48 #include "conf.h"
49 #include "log.h"
50 #include "server.h"
51 #include "send.h"
52 #include "memory.h"
53 #include "user.h"
54
55
56 static const char *comm_err_str[] = { "Comm OK", "Error during bind()",
57 "Error during DNS lookup", "connect timeout", "Error during connect()",
58 "Comm Error" };
59
60 static void comm_connect_callback(fde_t *, int);
61 static void comm_connect_timeout(fde_t *, void *);
62 static void comm_connect_dns_callback(void *, const struct irc_ssaddr *, const char *, size_t);
63 static void comm_connect_tryconnect(fde_t *, void *);
64
65
66 /* get_sockerr - get the error value from the socket or the current errno
67 *
68 * Get the *real* error from the socket (well try to anyway..).
69 * This may only work when SO_DEBUG is enabled but its worth the
70 * gamble anyway.
71 */
72 int
73 get_sockerr(int fd)
74 {
75 int errtmp = errno;
76 #ifdef SO_ERROR
77 int err = 0;
78 socklen_t len = sizeof(err);
79
80 if (-1 < fd && !getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len))
81 {
82 if (err)
83 errtmp = err;
84 }
85 errno = errtmp;
86 #endif
87 return errtmp;
88 }
89
90 /*
91 * report_error - report an error from an errno.
92 * Record error to log and also send a copy to all *LOCAL* opers online.
93 *
94 * text is a *format* string for outputing error. It must
95 * contain only two '%s', the first will be replaced
96 * by the sockhost from the client_p, and the latter will
97 * be taken from sys_errlist[errno].
98 *
99 * client_p if not NULL, is the *LOCAL* client associated with
100 * the error.
101 *
102 * Cannot use perror() within daemon. stderr is closed in
103 * ircd and cannot be used. And, worse yet, it might have
104 * been reassigned to a normal connection...
105 *
106 * Actually stderr is still there IFF ircd was run with -s --Rodder
107 */
108
109 void
110 report_error(int level, const char* text, const char* who, int error)
111 {
112 who = (who) ? who : "";
113
114 sendto_realops_flags(UMODE_DEBUG, level, SEND_NOTICE,
115 text, who, strerror(error));
116 ilog(LOG_TYPE_IRCD, text, who, strerror(error));
117 }
118
119 /*
120 * setup_socket()
121 *
122 * Set the socket non-blocking, and other wonderful bits.
123 */
124 static void
125 setup_socket(int fd)
126 {
127 int opt = 1;
128
129 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
130
131 #ifdef IPTOS_LOWDELAY
132 opt = IPTOS_LOWDELAY;
133 setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
134 #endif
135
136 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
137 }
138
139 /*
140 * close_connection
141 * Close the physical connection. This function must make
142 * MyConnect(client_p) == FALSE, and set client_p->from == NULL.
143 */
144 void
145 close_connection(struct Client *client_p)
146 {
147 dlink_node *ptr = NULL;
148
149 assert(client_p);
150
151 if (!IsDead(client_p))
152 {
153 /* attempt to flush any pending dbufs. Evil, but .. -- adrian */
154 /* there is still a chance that we might send data to this socket
155 * even if it is marked as blocked (COMM_SELECT_READ handler is called
156 * before COMM_SELECT_WRITE). Let's try, nothing to lose.. -adx
157 */
158 DelFlag(client_p, FLAGS_BLOCKED);
159 send_queued_write(client_p);
160 }
161
162 if (IsClient(client_p))
163 {
164 ++ServerStats.is_cl;
165 ServerStats.is_cbs += client_p->localClient->send.bytes;
166 ServerStats.is_cbr += client_p->localClient->recv.bytes;
167 ServerStats.is_cti += CurrentTime - client_p->localClient->firsttime;
168 }
169 else if (IsServer(client_p))
170 {
171 ++ServerStats.is_sv;
172 ServerStats.is_sbs += client_p->localClient->send.bytes;
173 ServerStats.is_sbr += client_p->localClient->recv.bytes;
174 ServerStats.is_sti += CurrentTime - client_p->localClient->firsttime;
175
176 DLINK_FOREACH(ptr, server_items.head)
177 {
178 struct MaskItem *conf = ptr->data;
179
180 if (irccmp(conf->name, client_p->name))
181 continue;
182
183 /*
184 * Reset next-connect cycle of all connect{} blocks that match
185 * this servername.
186 */
187 conf->until = CurrentTime + conf->class->con_freq;
188 }
189 }
190 else
191 ++ServerStats.is_ni;
192
193 #ifdef HAVE_LIBCRYPTO
194 if (client_p->localClient->fd.ssl)
195 {
196 SSL_set_shutdown(client_p->localClient->fd.ssl, SSL_RECEIVED_SHUTDOWN);
197
198 if (!SSL_shutdown(client_p->localClient->fd.ssl))
199 SSL_shutdown(client_p->localClient->fd.ssl);
200 }
201 #endif
202 if (client_p->localClient->fd.flags.open)
203 fd_close(&client_p->localClient->fd);
204
205 dbuf_clear(&client_p->localClient->buf_sendq);
206 dbuf_clear(&client_p->localClient->buf_recvq);
207
208 MyFree(client_p->localClient->password);
209 client_p->localClient->password = NULL;
210
211 detach_conf(client_p, CONF_CLIENT|CONF_OPER|CONF_SERVER);
212 }
213
214 #ifdef HAVE_LIBCRYPTO
215 /*
216 * ssl_handshake - let OpenSSL initialize the protocol. Register for
217 * read/write events if necessary.
218 */
219 static void
220 ssl_handshake(fde_t *fd, void *data)
221 {
222 struct Client *client_p = data;
223 X509 *cert = NULL;
224 int ret = 0;
225
226 if ((ret = SSL_accept(client_p->localClient->fd.ssl)) <= 0)
227 {
228 if ((CurrentTime - client_p->localClient->firsttime) > 30)
229 {
230 exit_client(client_p, "Timeout during SSL handshake");
231 return;
232 }
233
234 switch (SSL_get_error(client_p->localClient->fd.ssl, ret))
235 {
236 case SSL_ERROR_WANT_WRITE:
237 comm_setselect(&client_p->localClient->fd, COMM_SELECT_WRITE,
238 ssl_handshake, client_p, 30);
239 return;
240
241 case SSL_ERROR_WANT_READ:
242 comm_setselect(&client_p->localClient->fd, COMM_SELECT_READ,
243 ssl_handshake, client_p, 30);
244 return;
245
246 default:
247 exit_client(client_p, "Error during SSL handshake");
248 return;
249 }
250 }
251
252 comm_settimeout(&client_p->localClient->fd, 0, NULL, NULL);
253
254 if ((cert = SSL_get_peer_certificate(client_p->localClient->fd.ssl)))
255 {
256 int res = SSL_get_verify_result(client_p->localClient->fd.ssl);
257 char buf[EVP_MAX_MD_SIZE * 2 + 1] = "";
258 unsigned char md[EVP_MAX_MD_SIZE] = "";
259
260 if (res == X509_V_OK || res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ||
261 res == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ||
262 res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
263 {
264 unsigned int n = 0;
265
266 if (X509_digest(cert, ConfigServerInfo.message_digest_algorithm, md, &n))
267 {
268 binary_to_hex(md, buf, n);
269 client_p->certfp = xstrdup(buf);
270 }
271 }
272 else
273 ilog(LOG_TYPE_IRCD, "Client %s!%s@%s gave bad SSL client certificate: %d",
274 client_p->name, client_p->username, client_p->host, res);
275 X509_free(cert);
276 }
277
278 start_auth(client_p);
279 }
280 #endif
281
282 /*
283 * add_connection - creates a client which has just connected to us on
284 * the given fd. The sockhost field is initialized with the ip# of the host.
285 * An unique id is calculated now, in case it is needed for auth.
286 * The client is sent to the auth module for verification, and not put in
287 * any client list yet.
288 */
289 void
290 add_connection(struct Listener *listener, struct irc_ssaddr *irn, int fd)
291 {
292 struct Client *client_p = make_client(NULL);
293
294 fd_open(&client_p->localClient->fd, fd, 1,
295 (listener->flags & LISTENER_SSL) ?
296 "Incoming SSL connection" : "Incoming connection");
297
298 /*
299 * copy address to 'sockhost' as a string, copy it to host too
300 * so we have something valid to put into error messages...
301 */
302 memcpy(&client_p->localClient->ip, irn, sizeof(struct irc_ssaddr));
303
304 getnameinfo((struct sockaddr *)&client_p->localClient->ip,
305 client_p->localClient->ip.ss_len, client_p->sockhost,
306 sizeof(client_p->sockhost), NULL, 0, NI_NUMERICHOST);
307 client_p->localClient->aftype = client_p->localClient->ip.ss.ss_family;
308
309 #ifdef HAVE_LIBGEOIP
310 /* XXX IPV6 SUPPORT XXX */
311 if (irn->ss.ss_family == AF_INET && geoip_ctx)
312 {
313 const struct sockaddr_in *v4 = (const struct sockaddr_in *)&client_p->localClient->ip;
314 client_p->localClient->country_id = GeoIP_id_by_ipnum(geoip_ctx, (unsigned long)ntohl(v4->sin_addr.s_addr));
315 }
316 #endif
317
318 if (client_p->sockhost[0] == ':' && client_p->sockhost[1] == ':')
319 {
320 strlcpy(client_p->host, "0", sizeof(client_p->host));
321 strlcpy(client_p->host+1, client_p->sockhost, sizeof(client_p->host)-1);
322 memmove(client_p->sockhost+1, client_p->sockhost, sizeof(client_p->sockhost)-1);
323 client_p->sockhost[0] = '0';
324 }
325 else
326 strlcpy(client_p->host, client_p->sockhost, sizeof(client_p->host));
327
328 client_p->localClient->listener = listener;
329 ++listener->ref_count;
330
331 #ifdef HAVE_LIBCRYPTO
332 if (listener->flags & LISTENER_SSL)
333 {
334 if ((client_p->localClient->fd.ssl = SSL_new(ConfigServerInfo.server_ctx)) == NULL)
335 {
336 ilog(LOG_TYPE_IRCD, "SSL_new() ERROR! -- %s",
337 ERR_error_string(ERR_get_error(), NULL));
338
339 SetDead(client_p);
340 exit_client(client_p, "SSL_new failed");
341 return;
342 }
343
344 AddFlag(client_p, FLAGS_SSL);
345 SSL_set_fd(client_p->localClient->fd.ssl, fd);
346 ssl_handshake(NULL, client_p);
347 }
348 else
349 #endif
350 start_auth(client_p);
351 }
352
353 /*
354 * stolen from squid - its a neat (but overused! :) routine which we
355 * can use to see whether we can ignore this errno or not. It is
356 * generally useful for non-blocking network IO related errnos.
357 * -- adrian
358 */
359 int
360 ignoreErrno(int ierrno)
361 {
362 switch (ierrno)
363 {
364 case EINPROGRESS:
365 case EWOULDBLOCK:
366 #if EAGAIN != EWOULDBLOCK
367 case EAGAIN:
368 #endif
369 case EALREADY:
370 case EINTR:
371 #ifdef ERESTART
372 case ERESTART:
373 #endif
374 return 1;
375 default:
376 return 0;
377 }
378 }
379
380 /*
381 * comm_settimeout() - set the socket timeout
382 *
383 * Set the timeout for the fd
384 */
385 void
386 comm_settimeout(fde_t *fd, time_t timeout, void (*callback)(fde_t *, void *), void *cbdata)
387 {
388 assert(fd->flags.open);
389
390 fd->timeout = CurrentTime + (timeout / 1000);
391 fd->timeout_handler = callback;
392 fd->timeout_data = cbdata;
393 }
394
395 /*
396 * comm_setflush() - set a flush function
397 *
398 * A flush function is simply a function called if found during
399 * comm_timeouts(). Its basically a second timeout, except in this case
400 * I'm too lazy to implement multiple timeout functions! :-)
401 * its kinda nice to have it separate, since this is designed for
402 * flush functions, and when comm_close() is implemented correctly
403 * with close functions, we _actually_ don't call comm_close() here ..
404 * -- originally Adrian's notes
405 * comm_close() is replaced with fd_close() in fdlist.c
406 */
407 void
408 comm_setflush(fde_t *fd, time_t timeout, void (*callback)(fde_t *, void *), void *cbdata)
409 {
410 assert(fd->flags.open);
411
412 fd->flush_timeout = CurrentTime + (timeout / 1000);
413 fd->flush_handler = callback;
414 fd->flush_data = cbdata;
415 }
416
417 /*
418 * comm_checktimeouts() - check the socket timeouts
419 *
420 * All this routine does is call the given callback/cbdata, without closing
421 * down the file descriptor. When close handlers have been implemented,
422 * this will happen.
423 */
424 void
425 comm_checktimeouts(void *unused)
426 {
427 int i;
428 fde_t *F;
429 void (*hdl)(fde_t *, void *);
430 void *data;
431
432 for (i = 0; i < FD_HASH_SIZE; i++)
433 for (F = fd_hash[i]; F != NULL; F = fd_next_in_loop)
434 {
435 assert(F->flags.open);
436 fd_next_in_loop = F->hnext;
437
438 /* check flush functions */
439 if (F->flush_handler && F->flush_timeout > 0 &&
440 F->flush_timeout < CurrentTime)
441 {
442 hdl = F->flush_handler;
443 data = F->flush_data;
444 comm_setflush(F, 0, NULL, NULL);
445 hdl(F, data);
446 }
447
448 /* check timeouts */
449 if (F->timeout_handler && F->timeout > 0 &&
450 F->timeout < CurrentTime)
451 {
452 /* Call timeout handler */
453 hdl = F->timeout_handler;
454 data = F->timeout_data;
455 comm_settimeout(F, 0, NULL, NULL);
456 hdl(F, data);
457 }
458 }
459 }
460
461 /*
462 * void comm_connect_tcp(int fd, const char *host, unsigned short port,
463 * struct sockaddr *clocal, int socklen,
464 * CNCB *callback, void *data, int aftype, int timeout)
465 * Input: An fd to connect with, a host and port to connect to,
466 * a local sockaddr to connect from + length(or NULL to use the
467 * default), a callback, the data to pass into the callback, the
468 * address family.
469 * Output: None.
470 * Side-effects: A non-blocking connection to the host is started, and
471 * if necessary, set up for selection. The callback given
472 * may be called now, or it may be called later.
473 */
474 void
475 comm_connect_tcp(fde_t *fd, const char *host, unsigned short port,
476 struct sockaddr *clocal, int socklen, CNCB *callback,
477 void *data, int aftype, int timeout)
478 {
479 struct addrinfo hints, *res;
480 char portname[PORTNAMELEN + 1];
481
482 assert(callback);
483 fd->connect.callback = callback;
484 fd->connect.data = data;
485
486 fd->connect.hostaddr.ss.ss_family = aftype;
487 fd->connect.hostaddr.ss_port = htons(port);
488
489 /* Note that we're using a passed sockaddr here. This is because
490 * generally you'll be bind()ing to a sockaddr grabbed from
491 * getsockname(), so this makes things easier.
492 * XXX If NULL is passed as local, we should later on bind() to the
493 * virtual host IP, for completeness.
494 * -- adrian
495 */
496 if ((clocal != NULL) && (bind(fd->fd, clocal, socklen) < 0))
497 {
498 /* Failure, call the callback with COMM_ERR_BIND */
499 comm_connect_callback(fd, COMM_ERR_BIND);
500 /* ... and quit */
501 return;
502 }
503
504 /* Next, if we have been given an IP, get the addr and skip the
505 * DNS check (and head direct to comm_connect_tryconnect().
506 */
507 memset(&hints, 0, sizeof(hints));
508 hints.ai_family = AF_UNSPEC;
509 hints.ai_socktype = SOCK_STREAM;
510 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
511
512 snprintf(portname, sizeof(portname), "%d", port);
513
514 if (getaddrinfo(host, portname, &hints, &res))
515 {
516 /* Send the DNS request, for the next level */
517 if (aftype == AF_INET6)
518 gethost_byname_type(comm_connect_dns_callback, fd, host, T_AAAA);
519 else
520 gethost_byname_type(comm_connect_dns_callback, fd, host, T_A);
521 }
522 else
523 {
524 /* We have a valid IP, so we just call tryconnect */
525 /* Make sure we actually set the timeout here .. */
526 assert(res != NULL);
527 memcpy(&fd->connect.hostaddr, res->ai_addr, res->ai_addrlen);
528 fd->connect.hostaddr.ss_len = res->ai_addrlen;
529 fd->connect.hostaddr.ss.ss_family = res->ai_family;
530 freeaddrinfo(res);
531 comm_settimeout(fd, timeout*1000, comm_connect_timeout, NULL);
532 comm_connect_tryconnect(fd, NULL);
533 }
534 }
535
536 /*
537 * comm_connect_callback() - call the callback, and continue with life
538 */
539 static void
540 comm_connect_callback(fde_t *fd, int status)
541 {
542 CNCB *hdl;
543
544 /* This check is gross..but probably necessary */
545 if (fd->connect.callback == NULL)
546 return;
547
548 /* Clear the connect flag + handler */
549 hdl = fd->connect.callback;
550 fd->connect.callback = NULL;
551
552 /* Clear the timeout handler */
553 comm_settimeout(fd, 0, NULL, NULL);
554
555 /* Call the handler */
556 hdl(fd, status, fd->connect.data);
557 }
558
559 /*
560 * comm_connect_timeout() - this gets called when the socket connection
561 * times out. This *only* can be called once connect() is initially
562 * called ..
563 */
564 static void
565 comm_connect_timeout(fde_t *fd, void *unused)
566 {
567 /* error! */
568 comm_connect_callback(fd, COMM_ERR_TIMEOUT);
569 }
570
571 /*
572 * comm_connect_dns_callback() - called at the completion of the DNS request
573 *
574 * The DNS request has completed, so if we've got an error, return it,
575 * otherwise we initiate the connect()
576 */
577 static void
578 comm_connect_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name, size_t namelength)
579 {
580 fde_t *F = vptr;
581
582 if (!addr)
583 {
584 comm_connect_callback(F, COMM_ERR_DNS);
585 return;
586 }
587
588 /* No error, set a 10 second timeout */
589 comm_settimeout(F, 30*1000, comm_connect_timeout, NULL);
590
591 /* Copy over the DNS reply info so we can use it in the connect() */
592 /*
593 * Note we don't fudge the refcount here, because we aren't keeping
594 * the DNS record around, and the DNS cache is gone anyway..
595 * -- adrian
596 */
597 memcpy(&F->connect.hostaddr, addr, addr->ss_len);
598 /* The cast is hacky, but safe - port offset is same on v4 and v6 */
599 ((struct sockaddr_in *) &F->connect.hostaddr)->sin_port = F->connect.hostaddr.ss_port;
600 F->connect.hostaddr.ss_len = addr->ss_len;
601
602 /* Now, call the tryconnect() routine to try a connect() */
603 comm_connect_tryconnect(F, NULL);
604 }
605
606 /* static void comm_connect_tryconnect(int fd, void *unused)
607 * Input: The fd, the handler data(unused).
608 * Output: None.
609 * Side-effects: Try and connect with pending connect data for the FD. If
610 * we succeed or get a fatal error, call the callback.
611 * Otherwise, it is still blocking or something, so register
612 * to select for a write event on this FD.
613 */
614 static void
615 comm_connect_tryconnect(fde_t *fd, void *unused)
616 {
617 int retval;
618
619 /* This check is needed or re-entrant s_bsd_* like sigio break it. */
620 if (fd->connect.callback == NULL)
621 return;
622
623 /* Try the connect() */
624 retval = connect(fd->fd, (struct sockaddr *) &fd->connect.hostaddr,
625 fd->connect.hostaddr.ss_len);
626
627 /* Error? */
628 if (retval < 0)
629 {
630 /*
631 * If we get EISCONN, then we've already connect()ed the socket,
632 * which is a good thing.
633 * -- adrian
634 */
635 if (errno == EISCONN)
636 comm_connect_callback(fd, COMM_OK);
637 else if (ignoreErrno(errno))
638 /* Ignore error? Reschedule */
639 comm_setselect(fd, COMM_SELECT_WRITE, comm_connect_tryconnect,
640 NULL, 0);
641 else
642 /* Error? Fail with COMM_ERR_CONNECT */
643 comm_connect_callback(fd, COMM_ERR_CONNECT);
644 return;
645 }
646
647 /* If we get here, we've suceeded, so call with COMM_OK */
648 comm_connect_callback(fd, COMM_OK);
649 }
650
651 /*
652 * comm_errorstr() - return an error string for the given error condition
653 */
654 const char *
655 comm_errstr(int error)
656 {
657 if (error < 0 || error >= COMM_ERR_MAX)
658 return "Invalid error number!";
659 return comm_err_str[error];
660 }
661
662 /*
663 * comm_open() - open a socket
664 *
665 * This is a highly highly cut down version of squid's comm_open() which
666 * for the most part emulates socket(), *EXCEPT* it fails if we're about
667 * to run out of file descriptors.
668 */
669 int
670 comm_open(fde_t *F, int family, int sock_type, int proto, const char *note)
671 {
672 int fd;
673
674 /* First, make sure we aren't going to run out of file descriptors */
675 if (number_fd >= hard_fdlimit)
676 {
677 errno = ENFILE;
678 return -1;
679 }
680
681 /*
682 * Next, we try to open the socket. We *should* drop the reserved FD
683 * limit if/when we get an error, but we can deal with that later.
684 * XXX !!! -- adrian
685 */
686 fd = socket(family, sock_type, proto);
687 if (fd < 0)
688 return -1; /* errno will be passed through, yay.. */
689
690 setup_socket(fd);
691
692 /* update things in our fd tracking */
693 fd_open(F, fd, 1, note);
694 return 0;
695 }
696
697 /*
698 * comm_accept() - accept an incoming connection
699 *
700 * This is a simple wrapper for accept() which enforces FD limits like
701 * comm_open() does. Returned fd must be either closed or tagged with
702 * fd_open (this function no longer does it).
703 */
704 int
705 comm_accept(struct Listener *lptr, struct irc_ssaddr *addr)
706 {
707 int newfd;
708 socklen_t addrlen = sizeof(struct irc_ssaddr);
709
710 if (number_fd >= hard_fdlimit)
711 {
712 errno = ENFILE;
713 return -1;
714 }
715
716 memset(addr, 0, sizeof(struct irc_ssaddr));
717
718 /*
719 * Next, do the accept(). if we get an error, we should drop the
720 * reserved fd limit, but we can deal with that when comm_open()
721 * also does it. XXX -- adrian
722 */
723 newfd = accept(lptr->fd.fd, (struct sockaddr *)addr, &addrlen);
724 if (newfd < 0)
725 return -1;
726
727 remove_ipv6_mapping(addr);
728
729 setup_socket(newfd);
730
731 /* .. and return */
732 return newfd;
733 }
734
735 /*
736 * remove_ipv6_mapping() - Removes IPv4-In-IPv6 mapping from an address
737 * OSes with IPv6 mapping listening on both
738 * AF_INET and AF_INET6 map AF_INET connections inside AF_INET6 structures
739 *
740 */
741 void
742 remove_ipv6_mapping(struct irc_ssaddr *addr)
743 {
744 if (addr->ss.ss_family == AF_INET6)
745 {
746 if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr))
747 {
748 struct sockaddr_in6 v6;
749 struct sockaddr_in *v4 = (struct sockaddr_in *)addr;
750
751 memcpy(&v6, addr, sizeof(v6));
752 memset(v4, 0, sizeof(struct sockaddr_in));
753 memcpy(&v4->sin_addr, &v6.sin6_addr.s6_addr[12], sizeof(v4->sin_addr));
754
755 addr->ss.ss_family = AF_INET;
756 addr->ss_len = sizeof(struct sockaddr_in);
757 }
758 else
759 addr->ss_len = sizeof(struct sockaddr_in6);
760 }
761 else
762 addr->ss_len = sizeof(struct sockaddr_in);
763 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision