ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/s_bsd.c
Revision: 4415
Committed: Thu Aug 7 14:09:36 2014 UTC (11 years ago) by michael
Content type: text/x-csrc
File size: 21503 byte(s)
Log Message:
- Removed ipv6 detection. We now assume all systems that run hybrid have
  ipv6 availability and sockaddr_storage.

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 PF comm_connect_timeout;
62 static void comm_connect_dns_callback(void *, const struct irc_ssaddr *, const char *, size_t);
63 static PF comm_connect_tryconnect;
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->passwd);
209 detach_conf(client_p, CONF_CLIENT|CONF_OPER|CONF_SERVER);
210 }
211
212 #ifdef HAVE_LIBCRYPTO
213 /*
214 * ssl_handshake - let OpenSSL initialize the protocol. Register for
215 * read/write events if necessary.
216 */
217 static void
218 ssl_handshake(int fd, struct Client *client_p)
219 {
220 X509 *cert = NULL;
221 int ret = 0;
222
223 if ((ret = SSL_accept(client_p->localClient->fd.ssl)) <= 0)
224 {
225 if ((CurrentTime - client_p->localClient->firsttime) > 30)
226 {
227 exit_client(client_p, "Timeout during SSL handshake");
228 return;
229 }
230
231 switch (SSL_get_error(client_p->localClient->fd.ssl, ret))
232 {
233 case SSL_ERROR_WANT_WRITE:
234 comm_setselect(&client_p->localClient->fd, COMM_SELECT_WRITE,
235 (PF *)ssl_handshake, client_p, 30);
236 return;
237
238 case SSL_ERROR_WANT_READ:
239 comm_setselect(&client_p->localClient->fd, COMM_SELECT_READ,
240 (PF *)ssl_handshake, client_p, 30);
241 return;
242
243 default:
244 exit_client(client_p, "Error during SSL handshake");
245 return;
246 }
247 }
248
249 comm_settimeout(&client_p->localClient->fd, 0, NULL, NULL);
250
251 if ((cert = SSL_get_peer_certificate(client_p->localClient->fd.ssl)))
252 {
253 int res = SSL_get_verify_result(client_p->localClient->fd.ssl);
254 char buf[EVP_MAX_MD_SIZE * 2 + 1] = "";
255 unsigned char md[EVP_MAX_MD_SIZE] = "";
256
257 if (res == X509_V_OK || res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ||
258 res == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE ||
259 res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)
260 {
261 unsigned int n = 0;
262
263 if (X509_digest(cert, ConfigServerInfo.message_digest_algorithm, md, &n))
264 {
265 binary_to_hex(md, buf, n);
266 client_p->certfp = xstrdup(buf);
267 }
268 }
269 else
270 ilog(LOG_TYPE_IRCD, "Client %s!%s@%s gave bad SSL client certificate: %d",
271 client_p->name, client_p->username, client_p->host, res);
272 X509_free(cert);
273 }
274
275 start_auth(client_p);
276 }
277 #endif
278
279 /*
280 * add_connection - creates a client which has just connected to us on
281 * the given fd. The sockhost field is initialized with the ip# of the host.
282 * An unique id is calculated now, in case it is needed for auth.
283 * The client is sent to the auth module for verification, and not put in
284 * any client list yet.
285 */
286 void
287 add_connection(struct Listener *listener, struct irc_ssaddr *irn, int fd)
288 {
289 struct Client *client_p = make_client(NULL);
290
291 fd_open(&client_p->localClient->fd, fd, 1,
292 (listener->flags & LISTENER_SSL) ?
293 "Incoming SSL connection" : "Incoming connection");
294
295 /*
296 * copy address to 'sockhost' as a string, copy it to host too
297 * so we have something valid to put into error messages...
298 */
299 memcpy(&client_p->localClient->ip, irn, sizeof(struct irc_ssaddr));
300
301 getnameinfo((struct sockaddr *)&client_p->localClient->ip,
302 client_p->localClient->ip.ss_len, client_p->sockhost,
303 sizeof(client_p->sockhost), NULL, 0, NI_NUMERICHOST);
304 client_p->localClient->aftype = client_p->localClient->ip.ss.ss_family;
305
306 #ifdef HAVE_LIBGEOIP
307 /* XXX IPV6 SUPPORT XXX */
308 if (irn->ss.ss_family == AF_INET && geoip_ctx)
309 {
310 const struct sockaddr_in *v4 = (const struct sockaddr_in *)&client_p->localClient->ip;
311 client_p->localClient->country_id = GeoIP_id_by_ipnum(geoip_ctx, (unsigned long)ntohl(v4->sin_addr.s_addr));
312 }
313 #endif
314
315 if (client_p->sockhost[0] == ':' && client_p->sockhost[1] == ':')
316 {
317 strlcpy(client_p->host, "0", sizeof(client_p->host));
318 strlcpy(client_p->host+1, client_p->sockhost, sizeof(client_p->host)-1);
319 memmove(client_p->sockhost+1, client_p->sockhost, sizeof(client_p->sockhost)-1);
320 client_p->sockhost[0] = '0';
321 }
322 else
323 strlcpy(client_p->host, client_p->sockhost, sizeof(client_p->host));
324
325 client_p->localClient->listener = listener;
326 ++listener->ref_count;
327
328 #ifdef HAVE_LIBCRYPTO
329 if (listener->flags & LISTENER_SSL)
330 {
331 if ((client_p->localClient->fd.ssl = SSL_new(ConfigServerInfo.server_ctx)) == NULL)
332 {
333 ilog(LOG_TYPE_IRCD, "SSL_new() ERROR! -- %s",
334 ERR_error_string(ERR_get_error(), NULL));
335
336 SetDead(client_p);
337 exit_client(client_p, "SSL_new failed");
338 return;
339 }
340
341 AddFlag(client_p, FLAGS_SSL);
342 SSL_set_fd(client_p->localClient->fd.ssl, fd);
343 ssl_handshake(0, client_p);
344 }
345 else
346 #endif
347 start_auth(client_p);
348 }
349
350 /*
351 * stolen from squid - its a neat (but overused! :) routine which we
352 * can use to see whether we can ignore this errno or not. It is
353 * generally useful for non-blocking network IO related errnos.
354 * -- adrian
355 */
356 int
357 ignoreErrno(int ierrno)
358 {
359 switch (ierrno)
360 {
361 case EINPROGRESS:
362 case EWOULDBLOCK:
363 #if EAGAIN != EWOULDBLOCK
364 case EAGAIN:
365 #endif
366 case EALREADY:
367 case EINTR:
368 #ifdef ERESTART
369 case ERESTART:
370 #endif
371 return 1;
372 default:
373 return 0;
374 }
375 }
376
377 /*
378 * comm_settimeout() - set the socket timeout
379 *
380 * Set the timeout for the fd
381 */
382 void
383 comm_settimeout(fde_t *fd, time_t timeout, PF *callback, void *cbdata)
384 {
385 assert(fd->flags.open);
386
387 fd->timeout = CurrentTime + (timeout / 1000);
388 fd->timeout_handler = callback;
389 fd->timeout_data = cbdata;
390 }
391
392 /*
393 * comm_setflush() - set a flush function
394 *
395 * A flush function is simply a function called if found during
396 * comm_timeouts(). Its basically a second timeout, except in this case
397 * I'm too lazy to implement multiple timeout functions! :-)
398 * its kinda nice to have it separate, since this is designed for
399 * flush functions, and when comm_close() is implemented correctly
400 * with close functions, we _actually_ don't call comm_close() here ..
401 * -- originally Adrian's notes
402 * comm_close() is replaced with fd_close() in fdlist.c
403 */
404 void
405 comm_setflush(fde_t *fd, time_t timeout, PF *callback, void *cbdata)
406 {
407 assert(fd->flags.open);
408
409 fd->flush_timeout = CurrentTime + (timeout / 1000);
410 fd->flush_handler = callback;
411 fd->flush_data = cbdata;
412 }
413
414 /*
415 * comm_checktimeouts() - check the socket timeouts
416 *
417 * All this routine does is call the given callback/cbdata, without closing
418 * down the file descriptor. When close handlers have been implemented,
419 * this will happen.
420 */
421 void
422 comm_checktimeouts(void *notused)
423 {
424 int i;
425 fde_t *F;
426 PF *hdl;
427 void *data;
428
429 for (i = 0; i < FD_HASH_SIZE; i++)
430 for (F = fd_hash[i]; F != NULL; F = fd_next_in_loop)
431 {
432 assert(F->flags.open);
433 fd_next_in_loop = F->hnext;
434
435 /* check flush functions */
436 if (F->flush_handler && F->flush_timeout > 0 &&
437 F->flush_timeout < CurrentTime)
438 {
439 hdl = F->flush_handler;
440 data = F->flush_data;
441 comm_setflush(F, 0, NULL, NULL);
442 hdl(F, data);
443 }
444
445 /* check timeouts */
446 if (F->timeout_handler && F->timeout > 0 &&
447 F->timeout < CurrentTime)
448 {
449 /* Call timeout handler */
450 hdl = F->timeout_handler;
451 data = F->timeout_data;
452 comm_settimeout(F, 0, NULL, NULL);
453 hdl(F, data);
454 }
455 }
456 }
457
458 /*
459 * void comm_connect_tcp(int fd, const char *host, unsigned short port,
460 * struct sockaddr *clocal, int socklen,
461 * CNCB *callback, void *data, int aftype, int timeout)
462 * Input: An fd to connect with, a host and port to connect to,
463 * a local sockaddr to connect from + length(or NULL to use the
464 * default), a callback, the data to pass into the callback, the
465 * address family.
466 * Output: None.
467 * Side-effects: A non-blocking connection to the host is started, and
468 * if necessary, set up for selection. The callback given
469 * may be called now, or it may be called later.
470 */
471 void
472 comm_connect_tcp(fde_t *fd, const char *host, unsigned short port,
473 struct sockaddr *clocal, int socklen, CNCB *callback,
474 void *data, int aftype, int timeout)
475 {
476 struct addrinfo hints, *res;
477 char portname[PORTNAMELEN + 1];
478
479 assert(callback);
480 fd->connect.callback = callback;
481 fd->connect.data = data;
482
483 fd->connect.hostaddr.ss.ss_family = aftype;
484 fd->connect.hostaddr.ss_port = htons(port);
485
486 /* Note that we're using a passed sockaddr here. This is because
487 * generally you'll be bind()ing to a sockaddr grabbed from
488 * getsockname(), so this makes things easier.
489 * XXX If NULL is passed as local, we should later on bind() to the
490 * virtual host IP, for completeness.
491 * -- adrian
492 */
493 if ((clocal != NULL) && (bind(fd->fd, clocal, socklen) < 0))
494 {
495 /* Failure, call the callback with COMM_ERR_BIND */
496 comm_connect_callback(fd, COMM_ERR_BIND);
497 /* ... and quit */
498 return;
499 }
500
501 /* Next, if we have been given an IP, get the addr and skip the
502 * DNS check (and head direct to comm_connect_tryconnect().
503 */
504 memset(&hints, 0, sizeof(hints));
505 hints.ai_family = AF_UNSPEC;
506 hints.ai_socktype = SOCK_STREAM;
507 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
508
509 snprintf(portname, sizeof(portname), "%d", port);
510
511 if (getaddrinfo(host, portname, &hints, &res))
512 {
513 /* Send the DNS request, for the next level */
514 if (aftype == AF_INET6)
515 gethost_byname_type(comm_connect_dns_callback, fd, host, T_AAAA);
516 else
517 gethost_byname_type(comm_connect_dns_callback, fd, host, T_A);
518 }
519 else
520 {
521 /* We have a valid IP, so we just call tryconnect */
522 /* Make sure we actually set the timeout here .. */
523 assert(res != NULL);
524 memcpy(&fd->connect.hostaddr, res->ai_addr, res->ai_addrlen);
525 fd->connect.hostaddr.ss_len = res->ai_addrlen;
526 fd->connect.hostaddr.ss.ss_family = res->ai_family;
527 freeaddrinfo(res);
528 comm_settimeout(fd, timeout*1000, comm_connect_timeout, NULL);
529 comm_connect_tryconnect(fd, NULL);
530 }
531 }
532
533 /*
534 * comm_connect_callback() - call the callback, and continue with life
535 */
536 static void
537 comm_connect_callback(fde_t *fd, int status)
538 {
539 CNCB *hdl;
540
541 /* This check is gross..but probably necessary */
542 if (fd->connect.callback == NULL)
543 return;
544
545 /* Clear the connect flag + handler */
546 hdl = fd->connect.callback;
547 fd->connect.callback = NULL;
548
549 /* Clear the timeout handler */
550 comm_settimeout(fd, 0, NULL, NULL);
551
552 /* Call the handler */
553 hdl(fd, status, fd->connect.data);
554 }
555
556 /*
557 * comm_connect_timeout() - this gets called when the socket connection
558 * times out. This *only* can be called once connect() is initially
559 * called ..
560 */
561 static void
562 comm_connect_timeout(fde_t *fd, void *notused)
563 {
564 /* error! */
565 comm_connect_callback(fd, COMM_ERR_TIMEOUT);
566 }
567
568 /*
569 * comm_connect_dns_callback() - called at the completion of the DNS request
570 *
571 * The DNS request has completed, so if we've got an error, return it,
572 * otherwise we initiate the connect()
573 */
574 static void
575 comm_connect_dns_callback(void *vptr, const struct irc_ssaddr *addr, const char *name, size_t namelength)
576 {
577 fde_t *F = vptr;
578
579 if (!addr)
580 {
581 comm_connect_callback(F, COMM_ERR_DNS);
582 return;
583 }
584
585 /* No error, set a 10 second timeout */
586 comm_settimeout(F, 30*1000, comm_connect_timeout, NULL);
587
588 /* Copy over the DNS reply info so we can use it in the connect() */
589 /*
590 * Note we don't fudge the refcount here, because we aren't keeping
591 * the DNS record around, and the DNS cache is gone anyway..
592 * -- adrian
593 */
594 memcpy(&F->connect.hostaddr, addr, addr->ss_len);
595 /* The cast is hacky, but safe - port offset is same on v4 and v6 */
596 ((struct sockaddr_in *) &F->connect.hostaddr)->sin_port = F->connect.hostaddr.ss_port;
597 F->connect.hostaddr.ss_len = addr->ss_len;
598
599 /* Now, call the tryconnect() routine to try a connect() */
600 comm_connect_tryconnect(F, NULL);
601 }
602
603 /* static void comm_connect_tryconnect(int fd, void *notused)
604 * Input: The fd, the handler data(unused).
605 * Output: None.
606 * Side-effects: Try and connect with pending connect data for the FD. If
607 * we succeed or get a fatal error, call the callback.
608 * Otherwise, it is still blocking or something, so register
609 * to select for a write event on this FD.
610 */
611 static void
612 comm_connect_tryconnect(fde_t *fd, void *notused)
613 {
614 int retval;
615
616 /* This check is needed or re-entrant s_bsd_* like sigio break it. */
617 if (fd->connect.callback == NULL)
618 return;
619
620 /* Try the connect() */
621 retval = connect(fd->fd, (struct sockaddr *) &fd->connect.hostaddr,
622 fd->connect.hostaddr.ss_len);
623
624 /* Error? */
625 if (retval < 0)
626 {
627 /*
628 * If we get EISCONN, then we've already connect()ed the socket,
629 * which is a good thing.
630 * -- adrian
631 */
632 if (errno == EISCONN)
633 comm_connect_callback(fd, COMM_OK);
634 else if (ignoreErrno(errno))
635 /* Ignore error? Reschedule */
636 comm_setselect(fd, COMM_SELECT_WRITE, comm_connect_tryconnect,
637 NULL, 0);
638 else
639 /* Error? Fail with COMM_ERR_CONNECT */
640 comm_connect_callback(fd, COMM_ERR_CONNECT);
641 return;
642 }
643
644 /* If we get here, we've suceeded, so call with COMM_OK */
645 comm_connect_callback(fd, COMM_OK);
646 }
647
648 /*
649 * comm_errorstr() - return an error string for the given error condition
650 */
651 const char *
652 comm_errstr(int error)
653 {
654 if (error < 0 || error >= COMM_ERR_MAX)
655 return "Invalid error number!";
656 return comm_err_str[error];
657 }
658
659 /*
660 * comm_open() - open a socket
661 *
662 * This is a highly highly cut down version of squid's comm_open() which
663 * for the most part emulates socket(), *EXCEPT* it fails if we're about
664 * to run out of file descriptors.
665 */
666 int
667 comm_open(fde_t *F, int family, int sock_type, int proto, const char *note)
668 {
669 int fd;
670
671 /* First, make sure we aren't going to run out of file descriptors */
672 if (number_fd >= hard_fdlimit)
673 {
674 errno = ENFILE;
675 return -1;
676 }
677
678 /*
679 * Next, we try to open the socket. We *should* drop the reserved FD
680 * limit if/when we get an error, but we can deal with that later.
681 * XXX !!! -- adrian
682 */
683 fd = socket(family, sock_type, proto);
684 if (fd < 0)
685 return -1; /* errno will be passed through, yay.. */
686
687 setup_socket(fd);
688
689 /* update things in our fd tracking */
690 fd_open(F, fd, 1, note);
691 return 0;
692 }
693
694 /*
695 * comm_accept() - accept an incoming connection
696 *
697 * This is a simple wrapper for accept() which enforces FD limits like
698 * comm_open() does. Returned fd must be either closed or tagged with
699 * fd_open (this function no longer does it).
700 */
701 int
702 comm_accept(struct Listener *lptr, struct irc_ssaddr *addr)
703 {
704 int newfd;
705 socklen_t addrlen = sizeof(struct irc_ssaddr);
706
707 if (number_fd >= hard_fdlimit)
708 {
709 errno = ENFILE;
710 return -1;
711 }
712
713 memset(addr, 0, sizeof(struct irc_ssaddr));
714
715 /*
716 * Next, do the accept(). if we get an error, we should drop the
717 * reserved fd limit, but we can deal with that when comm_open()
718 * also does it. XXX -- adrian
719 */
720 newfd = accept(lptr->fd.fd, (struct sockaddr *)addr, &addrlen);
721 if (newfd < 0)
722 return -1;
723
724 remove_ipv6_mapping(addr);
725
726 setup_socket(newfd);
727
728 /* .. and return */
729 return newfd;
730 }
731
732 /*
733 * remove_ipv6_mapping() - Removes IPv4-In-IPv6 mapping from an address
734 * OSes with IPv6 mapping listening on both
735 * AF_INET and AF_INET6 map AF_INET connections inside AF_INET6 structures
736 *
737 */
738 void
739 remove_ipv6_mapping(struct irc_ssaddr *addr)
740 {
741 if (addr->ss.ss_family == AF_INET6)
742 {
743 if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr))
744 {
745 struct sockaddr_in6 v6;
746 struct sockaddr_in *v4 = (struct sockaddr_in *)addr;
747
748 memcpy(&v6, addr, sizeof(v6));
749 memset(v4, 0, sizeof(struct sockaddr_in));
750 memcpy(&v4->sin_addr, &v6.sin6_addr.s6_addr[12], sizeof(v4->sin_addr));
751
752 addr->ss.ss_family = AF_INET;
753 addr->ss_len = sizeof(struct sockaddr_in);
754 }
755 else
756 addr->ss_len = sizeof(struct sockaddr_in6);
757 }
758 else
759 addr->ss_len = sizeof(struct sockaddr_in);
760 }

Properties

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