ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/s_bsd.c
Revision: 4439
Committed: Sat Aug 9 18:36:19 2014 UTC (12 years, 1 month ago) by michael
Content type: text/x-csrc
File size: 21544 byte(s)
Error occurred while calculating annotation data.
Log Message:
- Renamed various variables

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

Properties

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