ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/branches/newio/src/s_bsd.c
Revision: 2408
Committed: Thu Jul 18 19:57:58 2013 UTC (10 years, 9 months ago) by michael
Content type: text/x-csrc
File size: 27492 byte(s)
Log Message:
- ioengine changes as of 18JUL13

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * s_bsd.c: Network functions.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 * Copyright (C) 1999 Thomas Helvey
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 * USA
22 *
23 * $Id$
24 */
25
26 #include "stdinc.h"
27 #include <netinet/in_systm.h>
28 #include <netinet/ip.h>
29 #include <netinet/tcp.h>
30 #include "list.h"
31 #include "s_bsd.h"
32 #include "client.h"
33 #include "dbuf.h"
34 #include "irc_string.h"
35 #include "ircd.h"
36 #include "listener.h"
37 #include "numeric.h"
38 #include "packet.h"
39 #include "irc_res.h"
40 #include "restart.h"
41 #include "s_auth.h"
42 #include "conf.h"
43 #include "log.h"
44 #include "s_serv.h"
45 #include "send.h"
46 #include "memory.h"
47 #include "s_user.h"
48 #include "msgq.h"
49 #include "ioengine.h"
50 #include "dbuf.h"
51
52
53 dlink_list connection_list;
54
55 static void client_sock_callback(struct Event *);
56 static void client_timer_callback(struct Event *);
57
58 /** Temporary buffer for reading data from a peer. */
59 static char readbuf[SERVER_TCP_WINDOW];
60
61 /*
62 * report_error text constants
63 */
64 const char* const ACCEPT_ERROR_MSG = "error accepting connection for %s: %s";
65 const char* const BIND_ERROR_MSG = "bind error for %s: %s";
66 const char* const CONNECT_ERROR_MSG = "connect to host %s failed: %s";
67 const char* const CONNLIMIT_ERROR_MSG = "connect limit exceeded for %s: %s";
68 const char* const LISTEN_ERROR_MSG = "listen error for %s: %s";
69 const char* const NONB_ERROR_MSG = "error setting non-blocking for %s: %s";
70 const char* const PEERNAME_ERROR_MSG = "getpeername failed for %s: %s";
71 const char* const POLL_ERROR_MSG = "poll error for %s: %s";
72 const char* const REGISTER_ERROR_MSG = "registering %s: %s";
73 const char* const REUSEADDR_ERROR_MSG = "error setting SO_REUSEADDR for %s: %s";
74 const char* const SELECT_ERROR_MSG = "select error for %s: %s";
75 const char* const SETBUFS_ERROR_MSG = "error setting buffer size for %s: %s";
76 const char* const SOCKET_ERROR_MSG = "error creating socket for %s: %s";
77 const char* const TOS_ERROR_MSG = "error setting TOS for %s: %s";
78
79
80 static int
81 is_blocked(int error)
82 {
83 return EWOULDBLOCK == error
84 #ifdef ENOMEM
85 || ENOMEM == error
86 #endif
87 #ifdef ENOBUFS
88 || ENOBUFS == error
89 #endif
90 || EAGAIN == error;
91 }
92
93 /* get_sockerr - get the error value from the socket or the current errno
94 *
95 * Get the *real* error from the socket (well try to anyway..).
96 * This may only work when SO_DEBUG is enabled but its worth the
97 * gamble anyway.
98 */
99 int
100 get_sockerr(int fd)
101 {
102 int errtmp = errno;
103 #ifdef SO_ERROR
104 int err = 0;
105 socklen_t len = sizeof(err);
106
107 if (-1 < fd && !getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len))
108 {
109 if (err)
110 errtmp = err;
111 }
112 errno = errtmp;
113 #endif
114 return errtmp;
115 }
116
117 /** Mark a socket's address as reusable.
118 * @param[in] fd %Socket file descriptor to manipulate.
119 * @return Non-zero on success, or zero on failure.
120 */
121 int
122 os_set_reuseaddr(int fd)
123 {
124 unsigned int opt = 1;
125
126 return !setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
127 }
128
129 /** Set a socket's send and receive buffer sizes.
130 * @param[in] fd %Socket file descriptor to manipulate.
131 * @param[in] ssize New send buffer size.
132 * @param[in] rsize New receive buffer size.
133 * @return Non-zero on success, or zero on failure.
134 */
135 int
136 os_set_sockbufs(int fd, unsigned int ssize, unsigned int rsize)
137 {
138 unsigned int sopt = ssize;
139 unsigned int ropt = rsize;
140
141 return !setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &ropt, sizeof(ropt)) &&
142 !setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sopt, sizeof(sopt));
143 }
144
145 /** Set a socket's "type of service" value.
146 * @param[in] fd %Socket file descriptor to manipulate.
147 * @param[in] tos New type of service value to use.
148 * @return Non-zero on success, or zero on failure.
149 */
150 int
151 os_set_tos(int fd, int tos)
152 {
153 #if defined(IP_TOS) && defined(IPPROTO_IP)
154 unsigned int opt = tos;
155
156 return !setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
157 #else
158 return 1;
159 #endif
160 }
161
162 /** Disable IP options on a socket.
163 * @param[in] fd %Socket file descriptor to manipulate.
164 * @return Non-zero on success, or zero on failure.
165 */
166 int
167 os_disable_options(int fd)
168 {
169 #if defined(IP_OPTIONS) && defined(IPPROTO_IP)
170 return !setsockopt(fd, IPPROTO_IP, IP_OPTIONS, NULL, 0);
171 #else
172 return 1;
173 #endif
174 }
175
176 /** Start listening on a socket.
177 * @param[in] fd Disconnected file descriptor.
178 * @param[in] backlog Maximum number of un-accept()ed connections to keep.
179 * @return Non-zero on success; zero on error.
180 */
181 int
182 os_set_listen(int fd, int backlog)
183 {
184 return !listen(fd, backlog);
185 }
186
187 /** Attempt to read from a non-blocking socket.
188 * @param[in] fd File descriptor to read from.
189 * @param[out] buf Output buffer to read into.
190 * @param[in] length Number of bytes to read.
191 * @param[out] count_out Receives number of bytes actually read.
192 * @return An IOResult value indicating status.
193 */
194 IOResult
195 os_recv_nonb(int fd, char *buf, unsigned int length, unsigned int *count_out)
196 {
197 int res = 0;
198
199 assert(buf);
200 assert(count_out);
201
202 if ((res = recv(fd, buf, length, 0)) > 0)
203 {
204 *count_out = (unsigned int)res;
205 return IO_SUCCESS;
206 }
207
208 if (res == 0)
209 {
210 *count_out = 0;
211 errno = 0; /* Or ECONNRESET? */
212 return IO_FAILURE;
213 }
214
215 *count_out = 0;
216 return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE;
217 }
218
219 /** Attempt to read from a non-blocking UDP socket.
220 * @param[in] fd File descriptor to read from.
221 * @param[out] buf Output buffer to read into.
222 * @param[in] length Number of bytes to read.
223 * @param[out] length_out Receives number of bytes actually read.
224 * @param[out] addr_out Peer address that sent the message.
225 * @return An IOResult value indicating status.
226 */
227 IOResult
228 os_recvfrom_nonb(int fd, char *buf, unsigned int length,
229 unsigned int *length_out,
230 struct irc_ssaddr *addr)
231 {
232 unsigned int len = sizeof(struct irc_ssaddr);
233 int res = 0;
234
235 assert(buf);
236 assert(length_out);
237 assert(addr_out);
238
239 res = recvfrom(fd, buf, length, 0, (struct sockaddr *)addr, &len);
240
241 if (res > -1)
242 {
243 *length_out = (unsigned int)res;
244 return IO_SUCCESS;
245 }
246
247 *length_out = 0;
248 return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE;
249 }
250
251 /** Attempt to write on a connected socket.
252 * @param[in] fd File descriptor to write to.
253 * @param[in] buf Output buffer to send from.
254 * @param[in] length Number of bytes to write.
255 * @param[out] count_out Receives number of bytes actually written.
256 * @return An IOResult value indicating status.
257 */
258 IOResult
259 os_send_nonb(int fd, const char *buf, unsigned int length,
260 unsigned int *count_out)
261 {
262 int res = 0;
263
264 assert(buf);
265 assert(count_out);
266
267 if ((res = send(fd, buf, length, 0)) > -1)
268 {
269 *count_out = (unsigned int)res;
270 return IO_SUCCESS;
271 }
272
273 *count_out = 0;
274 return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE;
275 }
276
277 /** Attempt a vectored write on a connected socket.
278 * @param[in] fd File descriptor to write to.
279 * @param[in] buf Message queue to send from.
280 * @param[out] count_in Number of bytes mapped from \a buf.
281 * @param[out] count_out Receives number of bytes actually written.
282 * @return An IOResult value indicating status.
283 */
284 IOResult
285 os_sendv_nonb(int fd, struct MsgQ *buf, unsigned int *count_in,
286 unsigned int *count_out)
287 {
288 int res = 0, count = 0;
289 struct iovec iov[IOV_MAX];
290
291 assert(buf);
292 assert(count_in);
293 assert(count_out);
294
295 *count_in = 0;
296 count = msgq_mapiov(buf, iov, IOV_MAX, count_in);
297
298 if ((res = writev(fd, iov, count)) > -1)
299 {
300 *count_out = (unsigned int)res;
301 return IO_SUCCESS;
302 }
303
304 *count_out = 0;
305 return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE;
306 }
307
308 /** Attempt to write on a non-blocking UDP socket.
309 * @param[in] fd File descriptor to write to.
310 * @param[in] buf Output buffer to send from.
311 * @param[in] length Number of bytes to write.
312 * @param[out] count_out Receives number of bytes actually written.
313 * @param[in] flags Flags for call to sendto().
314 * @param[in] peer Destination address of the message.
315 * @return An IOResult value indicating status.
316 */
317 IOResult os_sendto_nonb(int fd, const char *buf, unsigned int length,
318 unsigned int *count_out, unsigned int flags,
319 struct irc_ssaddr *peer)
320 {
321 int res = 0, size = 0;
322
323 assert(buf);
324
325 if ((res = sendto(fd, buf, length, flags, (struct sockaddr *)&peer, peer->ss_len)) > -1)
326 {
327 if (count_out)
328 *count_out = (unsigned int)res;
329 return IO_SUCCESS;
330 }
331
332 if (count_out)
333 *count_out = 0;
334 return is_blocked(errno) ? IO_BLOCKED : IO_FAILURE;
335 }
336
337 /** Start a non-blocking connection.
338 * @param[in] fd Disconnected file descriptor.
339 * @param[in] sin Target address for connection.
340 * @return IOResult code indicating status.
341 */
342 IOResult os_connect_nonb(int fd, const struct irc_ssaddr *addr)
343 {
344 int size;
345
346 size = sockaddr_from_irc(&addr, sin, fd);
347 if (connect(fd, (struct sockaddr*) &addr, size))
348 return (errno == EINPROGRESS) ? IO_BLOCKED : IO_FAILURE;
349 return IO_SUCCESS;
350 }
351
352 int
353 os_accept(int fd, struct irc_ssaddr *addr)
354 {
355 int new_fd = 0;
356 socklen_t addrlen = sizeof(struct irc_ssaddr);
357
358 new_fd = accept(fd, (struct sockaddr *)addr, &addrlen);
359 if (new_fd < 0)
360 return -1;
361
362 remove_ipv6_mapping(addr);
363 setup_socket(new_fd);
364 return new_fd;
365 }
366
367 int
368 os_socket(int family, int sock_type)
369 {
370 int fd;
371
372 fd = socket(family, sock_type, 0);
373 if (fd < 0)
374 return -1;
375 setup_socket(fd);
376 return fd;
377 }
378
379 /*
380 * report_error - report an error from an errno.
381 * Record error to log and also send a copy to all *LOCAL* opers online.
382 *
383 * text is a *format* string for outputing error. It must
384 * contain only two '%s', the first will be replaced
385 * by the sockhost from the client_p, and the latter will
386 * be taken from sys_errlist[errno].
387 *
388 * client_p if not NULL, is the *LOCAL* client associated with
389 * the error.
390 *
391 * Cannot use perror() within daemon. stderr is closed in
392 * ircd and cannot be used. And, worse yet, it might have
393 * been reassigned to a normal connection...
394 *
395 * Actually stderr is still there IFF ircd was run with -s --Rodder
396 */
397
398 void
399 report_error(int level, const char *text, const char *who, int error)
400 {
401 int errtmp = errno; /* debug may change 'errno' */
402 const char *errmsg = (error) ? strerror(error) : "";
403
404 if (!errmsg)
405 errmsg = "Unknown error";
406
407 if (EmptyString(who))
408 who = "unknown";
409
410 sendto_realops_flags(UMODE_DEBUG, level, SEND_NOTICE,
411 text, who, errmsg);
412 ilog(LOG_TYPE_IRCD, text, who, errmsg);
413 errno = errtmp;
414 }
415
416 /*
417 * setup_socket()
418 *
419 * Set the socket non-blocking, and other wonderful bits.
420 */
421 static void
422 setup_socket(int fd)
423 {
424 int opt = 1;
425
426 setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
427
428 #ifdef IPTOS_LOWDELAY
429 opt = IPTOS_LOWDELAY;
430 setsockopt(fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt));
431 #endif
432
433 fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
434 }
435
436 /*
437 * close_connection
438 * Close the physical connection. This function must make
439 * MyConnect(client_p) == FALSE, and set client_p->from == NULL.
440 */
441 void
442 close_connection(struct Client *client_p)
443 {
444 dlink_node *ptr = NULL;
445
446 assert(client_p);
447
448 if (IsClient(client_p))
449 {
450 ++ServerStats.is_cl;
451 ServerStats.is_cbs += client_p->localClient->send.bytes;
452 ServerStats.is_cbr += client_p->localClient->recv.bytes;
453 ServerStats.is_cti += CurrentTime - client_p->localClient->firsttime;
454 }
455 else if (IsServer(client_p))
456 {
457 ++ServerStats.is_sv;
458 ServerStats.is_sbs += client_p->localClient->send.bytes;
459 ServerStats.is_sbr += client_p->localClient->recv.bytes;
460 ServerStats.is_sti += CurrentTime - client_p->localClient->firsttime;
461
462 DLINK_FOREACH(ptr, server_items.head)
463 {
464 struct MaskItem *conf = ptr->data;
465
466 if (irccmp(conf->name, client_p->name))
467 continue;
468
469 /*
470 * Reset next-connect cycle of all connect{} blocks that match
471 * this servername.
472 */
473 conf->until = CurrentTime + conf->class->con_freq;
474 }
475 }
476 else
477 ++ServerStats.is_ni;
478
479 if (s_fd(&client_p->localClient->socket)> -1)
480 {
481 flush_connections(client_p);
482 dlinkDelete(&client_p->localClient->node, &connection_list);
483 close(s_fd(&client_p->localClient->socket));
484 socket_del(&client_p->localClient->socket); /* Queue a socket delete */
485 s_fd(&client_p->localClient->socket) = -1;
486 }
487
488 AddFlag(client_p, FLAGS_DEADSOCKET);
489
490 MsgQClear(&client_p->localClient->sendQ);
491 client_drop_sendq(client_p->localClient);
492 DBufClear(&client_p->localClient->recvQ);
493
494 MyFree(client_p->localClient->passwd);
495 client_p->localClient->passwd = NULL;
496
497 detach_conf(client_p, CONF_CLIENT|CONF_OPER|CONF_SERVER);
498 client_p->from = NULL; /* ...this should catch them! >:) --msa */
499
500 if (client_p->localClient->listener)
501 {
502 assert(0 < client_p->localClient->listener->ref_count);
503 listener_release(client_p->localClient->listener);
504 client_p->localClient->listener = NULL;
505 }
506 }
507
508 /*
509 * add_connection - creates a client which has just connected to us on
510 * the given fd. The sockhost field is initialized with the ip# of the host.
511 * An unique id is calculated now, in case it is needed for auth.
512 * The client is sent to the auth module for verification, and not put in
513 * any client list yet.
514 */
515 void
516 add_connection(struct Listener *listener, struct irc_ssaddr *irn, int fd)
517 {
518 struct Client *new_client = make_client(NULL);
519
520 fd_open(&new_client->localClient->fd, fd, 1,
521 (listener->flags & LISTENER_SSL) ?
522 "Incoming SSL connection" : "Incoming connection");
523
524 /*
525 * copy address to 'sockhost' as a string, copy it to host too
526 * so we have something valid to put into error messages...
527 */
528 memcpy(&new_client->localClient->ip, irn, sizeof(struct irc_ssaddr));
529
530 getnameinfo((struct sockaddr *)&new_client->localClient->ip,
531 new_client->localClient->ip.ss_len, new_client->sockhost,
532 sizeof(new_client->sockhost), NULL, 0, NI_NUMERICHOST);
533 new_client->localClient->aftype = new_client->localClient->ip.ss.ss_family;
534
535 #ifdef HAVE_LIBGEOIP
536 /* XXX IPV6 SUPPORT XXX */
537 if (irn->ss.ss_family == AF_INET && geoip_ctx)
538 {
539 const struct sockaddr_in *v4 = (const struct sockaddr_in *)&new_client->localClient->ip;
540 new_client->localClient->country_id = GeoIP_id_by_ipnum(geoip_ctx, (unsigned long)ntohl(v4->sin_addr.s_addr));
541 }
542 #endif
543
544 if (new_client->sockhost[0] == ':' && new_client->sockhost[1] == ':')
545 {
546 strlcpy(new_client->host, "0", sizeof(new_client->host));
547 strlcpy(new_client->host+1, new_client->sockhost, sizeof(new_client->host)-1);
548 memmove(new_client->sockhost+1, new_client->sockhost, sizeof(new_client->sockhost)-1);
549 new_client->sockhost[0] = '0';
550 }
551 else
552 strlcpy(new_client->host, new_client->sockhost, sizeof(new_client->host));
553
554
555 s_fd(&new_client->localClient->socket) = fd;
556 if (!socket_add(&new_client->localClient->socket, client_sock_callback,
557 new_client->localClient, SS_CONNECTED, 0, fd))
558 {
559 ++ServerStats.is_ref;
560 // write(fd, register_message, strlen(register_message));
561 close(fd);
562 s_fd(&new_client->localClient->socket) = -1;
563 return;
564 }
565
566 new_client->localClient->freeflag |= FREEFLAG_SOCKET;
567 new_client->localClient->listener = listener;
568 ++listener->ref_count;
569
570 /* If we've made it this far we can put the client on the auth query pile */
571 start_auth(new_client);
572 }
573
574 /** Determines whether to tell the events engine we're interested in
575 * writable events.
576 * \param client_p Client for which to decide this.
577 */
578 void
579 update_write(struct Client *client_p)
580 {
581 /*
582 * If there are messages that need to be sent along, or if the client
583 * is in the middle of a /list, then we need to tell the engine that
584 * we're interested in writable events--otherwise, we need to drop
585 * that interest.
586 */
587 socket_events(&client_p->localClient->socket,
588 ((MsgQLength(&client_p->localClient->sendQ) || client_p->localClient->list_task) ?
589 SOCK_ACTION_ADD : SOCK_ACTION_DEL) | SOCK_EVENT_WRITABLE);
590 }
591
592 /** Read a 'packet' of data from a connection and process it. Read in
593 * 8k chunks to give a better performance rating (for server
594 * connections). Do some tricky stuff for client connections to make
595 * sure they don't do any flooding >:-) -avalon
596 * @param client_p Client from which to read data.
597 * @param socket_ready If non-zero, more data can be read from the client's socket.
598 * @return Positive number on success, zero on connection-fatal failure, negative
599 * if user is killed.
600 */
601 static int
602 read_packet(struct Client *client_p, int socket_ready)
603 {
604 unsigned int dolen = 0, length = 0;
605
606 if (socket_ready && !(IsClient(client_p) &&
607 DBufLength&(client_p->localClient->recvQ) > get_recvq(&client_p->localClient->confs)))
608 {
609 switch (os_recv_nonb(cli_fd(client_p), readbuf, sizeof(readbuf), &length))
610 {
611 case IO_SUCCESS:
612 if (length)
613 {
614 client_p->localClient->lasttime = CurrentTime;
615
616 if (client_p->localClient->lasttime > client_p->localClient->since)
617 client_p->localClient->since = CurrentTime;
618
619 ClearPingSent(client_p);
620 DelFlag(client_p, FLAGS_NOEWLINE);
621 }
622 break;
623
624 case IO_BLOCKED:
625 break;
626 case IO_FAILURE:
627 client_p->localClient->error = errno;
628 /* AddFlag(client_p, FLAGS_DEADSOCKET); */
629 return 0;
630 }
631 }
632
633 /*
634 * For server connections, we process as many as we can without
635 * worrying about the time of day or anything :)
636 */
637 if (length > 0 && IsServer(client_p))
638 return server_dopacket(client_p, readbuf, length);
639 else if (length > 0 && (IsHandshake(client_p) || IsConnecting(client_p)))
640 return connect_dopacket(client_p, readbuf, length);
641 else
642 {
643 /*
644 * Before we even think of parsing what we just read, stick
645 * it on the end of the receive queue and do it when its
646 * turn comes around.
647 */
648 if (length > 0 && !dbuf_put(&client_p->localClient->recvQ, readbuf, length))
649 return exit_client(client_p, client_p, "dbuf_put fail");
650
651 if (DBufLength(&client_p->localClient_recvQ) > get_recvq(&client_p->localClient->confs))
652 return exit_client(client_p, client_p, "Excess Flood");
653
654 while (DBufLength(&client_p->localClient->recvQ) && !NoNewLine(client_p) &&
655 (IsTrusted(client_p) || client_p->localClient->since - CurrentTime < 10))
656 {
657 dolen = dbuf_getmsg(&client_p->localClient->recvQ,
658 client_p->localClient->buffer, IRCD_BUFSIZE);
659
660 /*
661 * Devious looking...whats it do ? well..if a client
662 * sends a *long* message without any CR or LF, then
663 * dbuf_getmsg fails and we pull it out using this
664 * loop which just gets the next 512 bytes and then
665 * deletes the rest of the buffer contents.
666 * -avalon
667 */
668 if (dolen == 0)
669 {
670 if (DBufLength(&client_p->localClient->recvQ) < 510)
671 AddFlag(client_p, FLAGS_NONEWLINE);
672 else
673 {
674 /*
675 * More than 512 bytes in the line - drop the input and yell
676 * at the client.
677 */
678 DBufClear(&client_p->localClient->recvQ);
679 sendto_one(client_p, form_str(ERR_INPUTTOOLONG), me.name,
680 client_p->name[0] ? client_p->name : "*");
681 }
682 }
683 else if (client_dopacket(client_p, dolen) == CPTR_KILLED)
684 return CPTR_KILLED;
685
686 /*
687 * If it has become registered as a Server
688 * then skip the per-message parsing below.
689 */
690 if (IsHandshake(client_p) || IsServer(client_p))
691 {
692 while (-1)
693 {
694 dolen = dbuf_get(&client_p->localClient->recvQ, readbuf, sizeof(readbuf));
695
696 if (dolen <= 0)
697 return 1;
698 if (dolen == 0)
699 {
700 if (DBufLength(&client_p->localClient->recvQ) < 510)
701 AddFlag(client_p, FLAGS_NONEWLINE);
702 else
703 DBufClear(&client_p->localClient->recvQ);
704 }
705 else if ((IsServer(client_p) &&
706 server_dopacket(client_p, readbuf, dolen) == CPTR_KILLED) ||
707 (!IsServer(client_p) &&
708 connect_dopacket(client_p, readbuf, dolen) == CPTR_KILLED))
709 return CPTR_KILLED;
710 }
711 }
712 }
713
714 /* If there's still data to process, wait 2 seconds first */
715 if (DBufLength(&client_p->localClient->recvQ) && !HasFlag(client_p, FLAGS_NONEWLINE) &&
716 !t_onqueue(&client_p->localClient->proc))
717 {
718 client_p->localClient->freeflag |= FREEFLAG_TIMER;
719 timer_add(&client_p->localClient->proc, client_timer_callback, client_p->localClient,
720 TT_RELATIVE, 2);
721 }
722 }
723 return 1;
724 }
725
726 /** Attempt to send a sequence of bytes to the connection.
727 * As a side effect, updates \a client_p's FLAGS_BLOCKED setting
728 * and sendB/sendK fields.
729 * @param client_p Client that should receive data.
730 * @param buf Message buffer to send to client.
731 * @return Negative on connection-fatal error; otherwise
732 * number of bytes sent.
733 */
734 unsigned int
735 deliver_it(struct Client *client_p, struct MsgQ *buf)
736 {
737 unsigned int bytes_written = 0, bytes_count = 0;
738
739 assert(client_p);
740
741 switch (os_sendv_nonb(s_fd(&client_p->localClient->socket), buf, &bytes_count, &bytes_written))
742 {
743 case IO_SUCCESS:
744 DelFlag(client_p, FLAGS_BLOCKED);
745
746 client_p->localClient->send.bytes += bytes_written;
747 me.localClient->send.bytes += bytes_written;
748
749 /* A partial write implies that future writes will block. */
750 if (bytes_written < bytes_count)
751 AddFlag(client_p, FLAGS_BLOCKED);
752 break;
753 case IO_BLOCKED:
754 AddFlag(client_p, FLAGS_BLOCKED);
755 break;
756 case IO_FAILURE:
757 client_p->localClient->error = errno;
758 AddFlag(client_p, FLAGS_DEADSOCKET);
759 break;
760 }
761
762 return bytes_written;
763 }
764
765 /*
766 * remove_ipv6_mapping() - Removes IPv4-In-IPv6 mapping from an address
767 * OSes with IPv6 mapping listening on both
768 * AF_INET and AF_INET6 map AF_INET connections inside AF_INET6 structures
769 *
770 */
771 void
772 remove_ipv6_mapping(struct irc_ssaddr *addr)
773 {
774 if (addr->ss.ss_family == AF_INET6)
775 {
776 if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)addr)->sin6_addr))
777 {
778 struct sockaddr_in6 v6;
779 struct sockaddr_in *v4 = (struct sockaddr_in *)addr;
780
781 memcpy(&v6, addr, sizeof(v6));
782 memset(v4, 0, sizeof(struct sockaddr_in));
783 memcpy(&v4->sin_addr, &v6.sin6_addr.s6_addr[12], sizeof(v4->sin_addr));
784
785 addr->ss.ss_family = AF_INET;
786 addr->ss_len = sizeof(struct sockaddr_in);
787 }
788 else
789 addr->ss_len = sizeof(struct sockaddr_in6);
790 }
791 else
792 addr->ss_len = sizeof(struct sockaddr_in);
793 }
794
795 /** Process events on a client socket.
796 * @param ev Socket event structure that has a struct Connection as
797 * its associated data.
798 */
799 static void
800 client_sock_callback(struct Event *ev)
801 {
802 struct Client *client_p = NULL;
803 struct Connection *con = NULL;
804 const char *fmt = "%s";
805 const char *fallback = NULL;
806
807 assert(ev_socket(ev));
808 assert(s_data(ev_socket(ev)));
809
810 con = s_data(ev_socket(ev));
811
812 assert(con->localClient || ev_type(ev) == ET_DESTROY);
813
814 client_p = con->from;
815
816 assert(!client_p || con == client_p->localClient);
817
818 switch (ev_type(ev))
819 {
820 case ET_DESTROY:
821 con->freeflag &= ~FREEFLAG_SOCKET;
822
823 if (!con_freeflag(con) && !client_p)
824 free_connection(con);
825 break;
826
827 case ET_CONNECT: /* Socket connection completed */
828 if (!completed_connection(client_p) || IsDead(client_p))
829 fallback = client_p->info;
830 break;
831
832 case ET_ERROR: /* An error occurred */
833 fallback = client_p->info;
834 client_p->error = ev_data(ev);
835
836 /*
837 * If the OS told us we have a bad file descriptor, we should
838 * record that for future reference.
839 */
840 if (client_p->localClient->error == EBADF)
841 s_fd(&client_p->localClient->socket) = -1;
842
843 if (s_state(&con->socket) == SS_CONNECTING)
844 {
845 completed_connection(client_p);
846
847 /*
848 * For some reason, the os_get_sockerr() in completed_connect()
849 * can return 0 even when ev_data(ev) indicates a real error, so
850 * re-assign the client error here.
851 */
852 client_p->localClient->error = ev_data(ev);
853 break;
854 }
855
856 /*FALLTHROUGH*/
857 case ET_EOF: /* End of file on socket */
858 AddFlag(client_p, FLAGS_DEADSOCKET);
859
860 if ((IsServer(client_p) || IsHandshake(client_p)) && client_p->localClient->error == 0)
861 {
862 exit_client_msg(client_p, client_p, "Server %s closed the connection (%s)",
863 client_p->name, client_p->servptr->last_error_msg);
864 return;
865 }
866 else
867 {
868 fmt = "Read error: %s";
869 fallback = "EOF from client";
870 }
871
872 break;
873
874 case ET_WRITE: /* Socket is writable */
875 DelFlag(client_p, FLAGS_BLOCKED);
876
877 if (client_p->localClient->list_task && MsgQLength(&client_p->localClient->sendQ) < 2048)
878 list_next_channels(client_p);
879 send_queued(client_p);
880 break;
881
882 case ET_READ: /* Socket is readable */
883 if (!IsDead(client_p))
884 {
885 if (read_packet(client_p, 1) == 0) /* Error while reading packet */
886 fallback = "EOF from client";
887 }
888
889 break;
890
891 default:
892 assert(0 && "Unrecognized socket event in client_sock_callback()");
893 break;
894 }
895
896 assert(!client_p || !client_p->localClient || con == client_p->localClient);
897
898 if (fallback)
899 {
900 const char *msg = (client_p->localClient->error) ? strerror(client_p->localClient->error) : fallback;
901
902 if (!msg)
903 msg = "Unknown error";
904 exit_client_msg(client_p, client_p, fmt, msg);
905 }
906 }
907
908 /** Process a timer on client socket.
909 * @param ev Timer event that has a struct Connection as its
910 * associated data.
911 */
912 static void
913 client_timer_callback(struct Event *ev)
914 {
915 struct Client *client_p = NULL;
916 struct Connection *con = NULL;
917
918 assert(ev_timer(ev));
919 assert(t_data(ev_timer(ev)));
920 assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev));
921
922 con = t_data(ev_timer(ev));
923
924 assert(con->client || ev_type(ev) == ET_DESTROY);
925
926 client_p = con->from;
927
928 assert(!client_p || con == client_p->localClient);
929
930 if (ev_type(ev) == ET_DESTROY)
931 {
932 conf->freeflag &= ~FREEFLAG_TIMER; /* Timer has expired... */
933
934 if (!con->freeflag && !client_p)
935 free_connection(con); /* Client is being destroyed */
936 }
937 else
938 read_packet(client_p, 0); /* read_packet will re-add timer if needed */
939
940 assert(!client_p || !client_p->localClient || con == client_p->localClient);
941 }

Properties

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