unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

socket.c (38815B)

      1 /*
      2  *   Unreal Internet Relay Chat Daemon, src/socket.c
      3  *   Copyright (C) 1990 Jarkko Oikarinen and
      4  *                      University of Oulu, Computing Center
      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 1, or (at your option)
      9  *   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., 675 Mass Ave, Cambridge, MA 02139, USA.
     19  */
     20 
     21 /** @file
     22  * @brief Socket functions such as reading, writing, connecting.
     23  *
     24  * The actual data parsing functions (for incoming data) are in
     25  * src/parse.c.
     26  */
     27 
     28 #include "unrealircd.h"
     29 #include "dns.h"
     30 
     31 int OpenFiles = 0;    /* GLOBAL - number of files currently open */
     32 int readcalls = 0;
     33 
     34 void completed_connection(int, int, void *);
     35 void set_sock_opts(int, Client *, SocketType);
     36 void set_ipv6_opts(int);
     37 void close_listener(ConfigItem_listen *listener);
     38 static char readbuf[BUFSIZE];
     39 char zlinebuf[BUFSIZE];
     40 extern char *version;
     41 MODVAR time_t last_allinuse = 0;
     42 
     43 void start_of_normal_client_handshake(Client *client);
     44 void proceed_normal_client_handshake(Client *client, struct hostent *he);
     45 
     46 /** Close all connections - only used when we terminate the server (eg: /DIE or SIGTERM) */
     47 void close_connections(void)
     48 {
     49 	Client *client;
     50 
     51 	list_for_each_entry(client, &lclient_list, lclient_node)
     52 	{
     53 		if (client->local->fd >= 0)
     54 		{
     55 			fd_close(client->local->fd);
     56 			client->local->fd = -2;
     57 		}
     58 	}
     59 
     60 	list_for_each_entry(client, &unknown_list, lclient_node)
     61 	{
     62 		if (client->local->fd >= 0)
     63 		{
     64 			fd_close(client->local->fd);
     65 			client->local->fd = -2;
     66 		}
     67 
     68 		if (client->local->authfd >= 0)
     69 		{
     70 			fd_close(client->local->authfd);
     71 			client->local->fd = -1;
     72 		}
     73 	}
     74 
     75 	list_for_each_entry(client, &control_list, lclient_node)
     76 	{
     77 		if (client->local->fd >= 0)
     78 		{
     79 			fd_close(client->local->fd);
     80 			client->local->fd = -2;
     81 		}
     82 	}
     83 
     84 	close_unbound_listeners();
     85 
     86 	OpenFiles = 0;
     87 
     88 #ifdef _WIN32
     89 	WSACleanup();
     90 #endif
     91 }
     92 
     93 /** Accept an incoming connection.
     94  * @param listener	The listen { } block configuration data.
     95  * @returns 1 if the connection was accepted (even if it was rejected),
     96  * 0 if there is no more work to do (accept returned an error).
     97  */
     98 static int listener_accept_wrapper(ConfigItem_listen *listener)
     99 {
    100 	int cli_fd;
    101 
    102 	if ((cli_fd = fd_accept(listener->fd)) < 0)
    103 	{
    104 		if ((ERRNO != P_EWOULDBLOCK) && (ERRNO != P_ECONNABORTED))
    105 		{
    106 			/* Trouble! accept() returns a strange error.
    107 			 * Previously in such a case we would just log/broadcast the error and return,
    108 			 * causing this message to be triggered at a rate of XYZ per second (100% CPU).
    109 			 * Now we close & re-start the listener.
    110 			 * Of course the underlying cause of this issue should be investigated, as this
    111 			 * is very much a workaround.
    112 			 */
    113 			if (listener->file)
    114 			{
    115 				unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR", NULL, "Cannot accept incoming connection on file $file: $socket_error",
    116 					   log_data_socket_error(listener->fd),
    117 					   log_data_string("file", listener->file));
    118 			} else {
    119 				unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: $socket_error",
    120 					   log_data_socket_error(listener->fd),
    121 					   log_data_string("listen_ip", listener->ip),
    122 					   log_data_integer("listen_port", listener->port));
    123 			}
    124 			close_listener(listener);
    125 			start_listeners();
    126 		}
    127 		return 0;
    128 	}
    129 
    130 	ircstats.is_ac++;
    131 
    132 	set_sock_opts(cli_fd, NULL, listener->socket_type);
    133 
    134 	/* Allow connections to the control socket, even if maxclients is reached */
    135 	if (listener->options & LISTENER_CONTROL)
    136 	{
    137 		/* ... but not unlimited ;) */
    138 		if ((++OpenFiles >= maxclients+(CLIENTS_RESERVE/2)) || (cli_fd >= maxclients+(CLIENTS_RESERVE/2)))
    139 		{
    140 			ircstats.is_ref++;
    141 			if (last_allinuse < TStime() - 15)
    142 			{
    143 				unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on file $file: All connections in use",
    144 					   log_data_string("file", listener->file));
    145 				last_allinuse = TStime();
    146 			}
    147 			fd_close(cli_fd);
    148 			--OpenFiles;
    149 			return 1;
    150 		}
    151 	} else
    152 	{
    153 		if ((++OpenFiles >= maxclients) || (cli_fd >= maxclients))
    154 		{
    155 			ircstats.is_ref++;
    156 			if (last_allinuse < TStime() - 15)
    157 			{
    158 				if (listener->file)
    159 				{
    160 					unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on file $file: All connections in use",
    161 						   log_data_string("file", listener->file));
    162 				} else {
    163 					unreal_log(ULOG_FATAL, "listen", "ACCEPT_ERROR_MAXCLIENTS", NULL, "Cannot accept incoming connection on IP \"$listen_ip\" port $listen_port: All connections in use",
    164 						   log_data_string("listen_ip", listener->ip),
    165 						   log_data_integer("listen_port", listener->port));
    166 				}
    167 				last_allinuse = TStime();
    168 			}
    169 
    170 			(void)send(cli_fd, "ERROR :All connections in use\r\n", 31, 0);
    171 
    172 			fd_close(cli_fd);
    173 			--OpenFiles;
    174 			return 1;
    175 		}
    176 	}
    177 
    178 	/* add_connection() may fail. we just don't care. */
    179 	add_connection(listener, cli_fd);
    180 	return 1;
    181 }
    182 
    183 /** Accept an incoming connection.
    184  * @param listener_fd	The file descriptor of a listen() socket.
    185  * @param data		The listen { } block configuration data.
    186  */
    187 static void listener_accept(int listener_fd, int revents, void *data)
    188 {
    189 	int i;
    190 
    191 	/* Accept clients, but only up to a maximum in each run,
    192 	 * as to allow some CPU available to existing clients.
    193 	 * Better refuse or lag a few new clients than become
    194 	 * unresponse to existing clients.
    195 	 */
    196 	for (i=0; i < 100; i++)
    197 		if (!listener_accept_wrapper((ConfigItem_listen *)data))
    198 			break;
    199 }
    200 
    201 int unreal_listen_inet(ConfigItem_listen *listener)
    202 {
    203 	const char *ip = listener->ip;
    204 	int port = listener->port;
    205 
    206 	if (BadPtr(ip))
    207 		ip = "*";
    208 
    209 	if (*ip == '*')
    210 	{
    211 		if (listener->socket_type == SOCKET_TYPE_IPV6)
    212 			ip = "::";
    213 		else
    214 			ip = "0.0.0.0";
    215 	}
    216 
    217 	/* At first, open a new socket */
    218 	if (listener->fd >= 0)
    219 		abort(); /* Socket already exists but we are asked to create and listen on one. Bad! */
    220 
    221 	if (port == 0)
    222 		abort(); /* Impossible as well, right? */
    223 
    224 	listener->fd = fd_socket(listener->socket_type == SOCKET_TYPE_IPV6 ? AF_INET6 : AF_INET, SOCK_STREAM, 0, "Listener socket");
    225 	if (listener->fd < 0)
    226 	{
    227 		unreal_log(ULOG_FATAL, "listen", "LISTEN_SOCKET_ERROR", NULL,
    228 		           "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error",
    229 			   log_data_socket_error(-1),
    230 			   log_data_string("listen_ip", ip),
    231 			   log_data_integer("listen_port", port));
    232 		return -1;
    233 	}
    234 
    235 	if (++OpenFiles >= maxclients)
    236 	{
    237 		unreal_log(ULOG_FATAL, "listen", "LISTEN_ERROR_MAXCLIENTS", NULL,
    238 		           "Could not listen on IP \"$listen_ip\" on port $listen_port: all connections in use",
    239 		           log_data_string("listen_ip", ip),
    240 		           log_data_integer("listen_port", port));
    241 		fd_close(listener->fd);
    242 		listener->fd = -1;
    243 		--OpenFiles;
    244 		return -1;
    245 	}
    246 
    247 	set_sock_opts(listener->fd, NULL, listener->socket_type);
    248 
    249 	if (!unreal_bind(listener->fd, ip, port, listener->socket_type))
    250 	{
    251 		unreal_log(ULOG_FATAL, "listen", "LISTEN_BIND_ERROR", NULL,
    252 		           "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error",
    253 		           log_data_socket_error(listener->fd),
    254 		           log_data_string("listen_ip", ip),
    255 		           log_data_integer("listen_port", port));
    256 		fd_close(listener->fd);
    257 		listener->fd = -1;
    258 		--OpenFiles;
    259 		return -1;
    260 	}
    261 
    262 	if (listen(listener->fd, LISTEN_SIZE) < 0)
    263 	{
    264 		unreal_log(ULOG_FATAL, "listen", "LISTEN_LISTEN_ERROR", NULL,
    265 		           "Could not listen on IP \"$listen_ip\" on port $listen_port: $socket_error",
    266 		           log_data_socket_error(listener->fd),
    267 		           log_data_string("listen_ip", ip),
    268 		           log_data_integer("listen_port", port));
    269 		fd_close(listener->fd);
    270 		listener->fd = -1;
    271 		--OpenFiles;
    272 		return -1;
    273 	}
    274 
    275 #ifdef TCP_DEFER_ACCEPT
    276 	if (listener->options & LISTENER_DEFER_ACCEPT)
    277 	{
    278 		int yes = 1;
    279 
    280 		(void)setsockopt(listener->fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &yes, sizeof(int));
    281 	}
    282 #endif
    283 
    284 #ifdef SO_ACCEPTFILTER
    285 	if (listener->options & LISTENER_DEFER_ACCEPT)
    286 	{
    287 		struct accept_filter_arg afa;
    288 
    289 		memset(&afa, '\0', sizeof afa);
    290 		strlcpy(afa.af_name, "dataready", sizeof afa.af_name);
    291 		(void)setsockopt(listener->fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof afa);
    292 	}
    293 #endif
    294 
    295 	fd_setselect(listener->fd, FD_SELECT_READ, listener_accept, listener);
    296 
    297 	return 0;
    298 }
    299 
    300 int unreal_listen_unix(ConfigItem_listen *listener)
    301 {
    302 	if (listener->socket_type != SOCKET_TYPE_UNIX)
    303 		abort(); /* "impossible" */
    304 
    305 	/* At first, open a new socket */
    306 	if (listener->fd >= 0)
    307 		abort(); /* Socket already exists but we are asked to create and listen on one. Bad! */
    308 
    309 	listener->fd = fd_socket(AF_UNIX, SOCK_STREAM, 0, "Listener socket (UNIX)");
    310 	if (listener->fd < 0)
    311 	{
    312 		unreal_log(ULOG_FATAL, "listen", "LISTEN_SOCKET_ERROR", NULL,
    313 		           "Could not create UNIX domain socket for $file: $socket_error",
    314 			   log_data_socket_error(-1),
    315 			   log_data_string("file", listener->file));
    316 		return -1;
    317 	}
    318 
    319 	if (++OpenFiles >= maxclients)
    320 	{
    321 		unreal_log(ULOG_FATAL, "listen", "LISTEN_ERROR_MAXCLIENTS", NULL,
    322 		           "Could not create UNIX domain socket for $file: all connections in use",
    323 		           log_data_string("file", listener->file));
    324 		fd_close(listener->fd);
    325 		listener->fd = -1;
    326 		--OpenFiles;
    327 		return -1;
    328 	}
    329 
    330 	set_sock_opts(listener->fd, NULL, listener->socket_type);
    331 
    332 	if (!unreal_bind(listener->fd, listener->file, listener->mode, SOCKET_TYPE_UNIX))
    333 	{
    334 		unreal_log(ULOG_FATAL, "listen", "LISTEN_BIND_ERROR", NULL,
    335 		           "Could not listen on UNIX domain socket $file: $socket_error",
    336 		           log_data_socket_error(listener->fd),
    337 		           log_data_string("file", listener->file));
    338 		fd_close(listener->fd);
    339 		listener->fd = -1;
    340 		--OpenFiles;
    341 		return -1;
    342 	}
    343 
    344 	if (listen(listener->fd, LISTEN_SIZE) < 0)
    345 	{
    346 		unreal_log(ULOG_FATAL, "listen", "LISTEN_LISTEN_ERROR", NULL,
    347 		           "Could not listen on UNIX domain socket $file: $socket_error",
    348 		           log_data_socket_error(listener->fd),
    349 		           log_data_string("file", listener->file));
    350 		fd_close(listener->fd);
    351 		listener->fd = -1;
    352 		--OpenFiles;
    353 		return -1;
    354 	}
    355 
    356 	fd_setselect(listener->fd, FD_SELECT_READ, listener_accept, listener);
    357 
    358 	return 0;
    359 }
    360 
    361 /** Create a listener port.
    362  * @param listener	The listen { } block configuration
    363  * @returns 0 on success and <0 on error. Yeah, confusing.
    364  */
    365 int unreal_listen(ConfigItem_listen *listener)
    366 {
    367 	if ((listener->socket_type == SOCKET_TYPE_IPV4) || (listener->socket_type == SOCKET_TYPE_IPV6))
    368 		return unreal_listen_inet(listener);
    369 	return unreal_listen_unix(listener);
    370 }
    371 
    372 /** Activate a listen { } block */
    373 int add_listener(ConfigItem_listen *listener)
    374 {
    375 	if (unreal_listen(listener))
    376 	{
    377 		/* Error is already handled upstream */
    378 		listener->fd = -2;
    379 	}
    380 
    381 	if (listener->fd >= 0)
    382 	{
    383 		listener->options |= LISTENER_BOUND;
    384 		return 1;
    385 	}
    386 	else
    387 	{
    388 		listener->fd = -1;
    389 		return -1;
    390 	}
    391 }
    392 
    393 /** Close the listener socket, but do not free it (yet).
    394  * This will only close the socket so no new clients are accepted.
    395  * It also marks the listener as no longer "bound".
    396  * Once the last client exits the listener will actually be freed.
    397  * @param listener	The listen { } block.
    398  */
    399 void close_listener(ConfigItem_listen *listener)
    400 {
    401 	if (listener->fd >= 0)
    402 	{
    403 		unreal_log(ULOG_INFO, "listen", "LISTEN_REMOVED", NULL,
    404 			   "UnrealIRCd is now no longer listening on $listen_ip:$listen_port",
    405 			   log_data_string("listen_ip", listener->ip),
    406 			   log_data_integer("listen_port", listener->port));
    407 		fd_close(listener->fd);
    408 		--OpenFiles;
    409 	}
    410 
    411 	listener->options &= ~LISTENER_BOUND;
    412 	listener->fd = -1;
    413 	/* We can already free the TLS context, since it is only
    414 	 * used for new connections, which we no longer accept.
    415 	 */
    416 	if (listener->ssl_ctx)
    417 	{
    418 		SSL_CTX_free(listener->ssl_ctx);
    419 		listener->ssl_ctx = NULL;
    420 	}
    421 }
    422 
    423 /** Close all listeners that were pending to be closed. */
    424 void close_unbound_listeners(void)
    425 {
    426 	ConfigItem_listen *aconf, *aconf_next;
    427 
    428 	/* close all 'extra' listening ports we have */
    429 	for (aconf = conf_listen; aconf != NULL; aconf = aconf_next)
    430 	{
    431 		aconf_next = aconf->next;
    432 		if (aconf->flag.temporary)
    433 			close_listener(aconf);
    434 	}
    435 }
    436 
    437 int maxclients = 1024 - CLIENTS_RESERVE;
    438 
    439 /** Check the maximum number of sockets (users) that we can handle - called on startup.
    440  */
    441 void check_user_limit(void)
    442 {
    443 #ifdef RLIMIT_FD_MAX
    444 	struct rlimit limit;
    445 	long m;
    446 
    447 	if (!getrlimit(RLIMIT_FD_MAX, &limit))
    448 	{
    449 		if (limit.rlim_max < MAXCONNECTIONS)
    450 			m = limit.rlim_max;
    451 		else
    452 			m = MAXCONNECTIONS;
    453 
    454 		/* Adjust soft limit (if necessary, which is often the case) */
    455 		if (m != limit.rlim_cur)
    456 		{
    457 			limit.rlim_cur = limit.rlim_max = m;
    458 			if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
    459 			{
    460 				/* HACK: if it's mac os X then don't error... */
    461 #ifndef OSXTIGER
    462 				fprintf(stderr, "error setting maximum number of open files to %ld\n",
    463 					(long)limit.rlim_cur);
    464 				exit(-1);
    465 #endif // OSXTIGER
    466 			}
    467 		}
    468 		/* This can only happen if it is due to resource limits (./Config already rejects <100) */
    469 		if (m < 100)
    470 		{
    471 			fprintf(stderr, "\nERROR: Your OS has a limit placed on this account.\n"
    472 			                "This machine only allows UnrealIRCd to handle a maximum of %ld open connections/files, which is VERY LOW.\n"
    473 			                "Please check with your system administrator to bump this limit.\n"
    474 			                "The recommended ulimit -n setting is at least 1024 and "
    475 			                "preferably 4096.\n"
    476 			                "Note that this error is often seen on small web shells that are not meant for running IRC servers.\n",
    477 			                m);
    478 			exit(-1);
    479 		}
    480 		maxclients = m - CLIENTS_RESERVE;
    481 	}
    482 #endif // RLIMIT_FD_MAX
    483 
    484 #ifndef _WIN32
    485 #ifdef BACKEND_SELECT
    486 	if (MAXCONNECTIONS > FD_SETSIZE)
    487 	{
    488 		fprintf(stderr, "MAXCONNECTIONS (%d) is higher than FD_SETSIZE (%d)\n", MAXCONNECTIONS, FD_SETSIZE);
    489 		fprintf(stderr, "You should not see this error on Linux or FreeBSD\n");
    490 		fprintf(stderr, "You might need to recompile the IRCd and answer a lower value to the MAXCONNECTIONS question in ./Config\n");
    491 		exit(-1);
    492 	}
    493 #endif
    494 #endif
    495 #ifdef _WIN32
    496 	maxclients = MAXCONNECTIONS - CLIENTS_RESERVE;
    497 #endif
    498 }
    499 
    500 /** Initialize some systems - called on startup */
    501 void init_sys(void)
    502 {
    503 #ifndef _WIN32
    504 	/* Create new session / set process group */
    505 	(void)setsid();
    506 #endif
    507 
    508 	init_resolver(1);
    509 	return;
    510 }
    511 
    512 /** Replace a file descriptor (*NIX only).
    513  * See close_std_descriptors() as for why.
    514  * @param oldfd: the old FD to close and re-use
    515  * @param name: descriptive string of the old fd, eg: "stdin".
    516  * @param mode: an open() mode, such as O_WRONLY.
    517  */
    518 void replacefd(int oldfd, char *name, int mode)
    519 {
    520 #ifndef _WIN32
    521 	int newfd = open("/dev/null", mode);
    522 	if (newfd < 0)
    523 	{
    524 		fprintf(stderr, "Warning: could not open /dev/null\n");
    525 		return;
    526 	}
    527 	if (oldfd < 0)
    528 	{
    529 		fprintf(stderr, "Warning: could not replace %s (invalid fd)\n", name);
    530 		return;
    531 	}
    532 	if (dup2(newfd, oldfd) < 0)
    533 	{
    534 		fprintf(stderr, "Warning: could not replace %s (dup2 error)\n", name);
    535 		return;
    536 	}
    537 #endif
    538 }
    539 
    540 /** Mass close standard file descriptors (stdin, stdout, stderr).
    541  * We used to really just close them here (or in init_sys() actually),
    542  * making the fd's available for other purposes such as internet sockets.
    543  * For safety we now dup2() them to /dev/null. This in case someone
    544  * accidentally does a fprintf(stderr,..) somewhere in the code or some
    545  * library outputs error messages to stderr (such as libc with heap
    546  * errors). We don't want any IRC client to receive such a thing!
    547  */
    548 void close_std_descriptors(void)
    549 {
    550 #if !defined(_WIN32) && !defined(NOCLOSEFD)
    551 	replacefd(fileno(stdin), "stdin", O_RDONLY);
    552 	replacefd(fileno(stdout), "stdout", O_WRONLY);
    553 	replacefd(fileno(stderr), "stderr", O_WRONLY);
    554 #endif
    555 }
    556 
    557 /** Do an ident lookup if necessary.
    558  * @param client	The incoming client
    559  */
    560 void consider_ident_lookup(Client *client)
    561 {
    562 	char buf[BUFSIZE];
    563 
    564 	/* If ident checking is disabled or it's an outgoing connect, then no ident check */
    565 	if ((IDENT_CHECK == 0) || (client->server && IsHandshake(client)) || IsUnixSocket(client))
    566 	{
    567 		ClearIdentLookupSent(client);
    568 		ClearIdentLookup(client);
    569 		return;
    570 	}
    571 	RunHook(HOOKTYPE_IDENT_LOOKUP, client);
    572 
    573 	return;
    574 }
    575 
    576 
    577 /** Called when TCP/IP connection is established (outgoing server connect) */
    578 void completed_connection(int fd, int revents, void *data)
    579 {
    580 	Client *client = data;
    581 	ConfigItem_link *aconf = client->server ? client->server->conf : NULL;
    582 
    583 	if (IsHandshake(client))
    584 	{
    585 		/* Due to delayed unreal_tls_connect call */
    586 		start_server_handshake(client);
    587 		fd_setselect(fd, FD_SELECT_READ, read_packet, client);
    588 		return;
    589 	}
    590 
    591 	SetHandshake(client);
    592 
    593 	if (!aconf)
    594 	{
    595 		unreal_log(ULOG_ERROR, "link", "BUG_LOST_CONFIGURATION_ON_CONNECT", client,
    596 		           "Lost configuration while connecting to $client.details");
    597 		return;
    598 	}
    599 
    600 	if (!client->local->ssl && !(aconf->outgoing.options & CONNECT_INSECURE))
    601 	{
    602 		sendto_one(client, NULL, "STARTTLS");
    603 	} else
    604 	{
    605 		start_server_handshake(client);
    606 	}
    607 
    608 	if (!IsDeadSocket(client))
    609 		consider_ident_lookup(client);
    610 
    611 	fd_setselect(fd, FD_SELECT_READ, read_packet, client);
    612 }
    613 
    614 /** Close the physical connection.
    615  * @param client	The client connection to close (LOCAL!)
    616  */
    617 void close_connection(Client *client)
    618 {
    619 	RunHook(HOOKTYPE_CLOSE_CONNECTION, client);
    620 	/* This function must make MyConnect(client) == FALSE,
    621 	 * and set client->direction == NULL.
    622 	 */
    623 	if (IsServer(client))
    624 	{
    625 		ircstats.is_sv++;
    626 		ircstats.is_sti += TStime() - client->local->creationtime;
    627 	}
    628 	else if (IsUser(client))
    629 	{
    630 		ircstats.is_cl++;
    631 		ircstats.is_cti += TStime() - client->local->creationtime;
    632 	}
    633 	else
    634 		ircstats.is_ni++;
    635 
    636 	/*
    637 	 * remove outstanding DNS queries.
    638 	 */
    639 	unrealdns_delreq_bycptr(client);
    640 
    641 	if (client->local->authfd >= 0)
    642 	{
    643 		fd_close(client->local->authfd);
    644 		client->local->authfd = -1;
    645 		--OpenFiles;
    646 	}
    647 
    648 	if (client->local->fd >= 0)
    649 	{
    650 		send_queued(client);
    651 		if (IsTLS(client) && client->local->ssl) {
    652 			SSL_set_shutdown(client->local->ssl, SSL_RECEIVED_SHUTDOWN);
    653 			SSL_smart_shutdown(client->local->ssl);
    654 			SSL_free(client->local->ssl);
    655 			client->local->ssl = NULL;
    656 		}
    657 		fd_close(client->local->fd);
    658 		client->local->fd = -2;
    659 		--OpenFiles;
    660 		DBufClear(&client->local->sendQ);
    661 		DBufClear(&client->local->recvQ);
    662 	}
    663 
    664 	client->direction = NULL;
    665 }
    666 
    667 /** Set IPv6 socket options, if possible. */
    668 void set_ipv6_opts(int fd)
    669 {
    670 #if defined(IPV6_V6ONLY)
    671 	int opt = 1;
    672 	(void)setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&opt, sizeof(opt));
    673 #endif
    674 }
    675 
    676 /** This sets the *OS* socket buffers.
    677  * This shouldn't be needed anymore, but I've left the function here.
    678  */
    679 void set_socket_buffers(int fd, int rcvbuf, int sndbuf)
    680 {
    681 	int opt;
    682 
    683 	opt = rcvbuf;
    684 	setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&opt, sizeof(opt));
    685 
    686 	opt = sndbuf;
    687 	setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&opt, sizeof(opt));
    688 }
    689 
    690 /** Set the appropriate socket options */
    691 void set_sock_opts(int fd, Client *client, SocketType socket_type)
    692 {
    693 	int opt;
    694 
    695 	if (socket_type == SOCKET_TYPE_IPV6)
    696 		set_ipv6_opts(fd);
    697 
    698 	if ((socket_type == SOCKET_TYPE_IPV4) || (socket_type == SOCKET_TYPE_IPV6))
    699 	{
    700 #ifdef SO_REUSEADDR
    701 		opt = 1;
    702 		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt)) < 0)
    703 		{
    704 			unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client,
    705 				   "Could not setsockopt(SO_REUSEADDR): $socket_error",
    706 				   log_data_socket_error(-1));
    707 		}
    708 #endif
    709 
    710 #if defined(SO_USELOOPBACK) && !defined(_WIN32)
    711 		opt = 1;
    712 		if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (void *)&opt, sizeof(opt)) < 0)
    713 		{
    714 			unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client,
    715 				   "Could not setsockopt(SO_USELOOPBACK): $socket_error",
    716 				   log_data_socket_error(-1));
    717 		}
    718 #endif
    719 
    720 	}
    721 
    722 	/* The following code applies to all socket types: IPv4, IPv6, UNIX domain sockets */
    723 
    724 	/* Set to non blocking: */
    725 #if !defined(_WIN32)
    726 	if ((opt = fcntl(fd, F_GETFL, 0)) == -1)
    727 	{
    728 		if (client)
    729 		{
    730 			unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client,
    731 				   "Could not get socket options (F_GETFL): $socket_error",
    732 				   log_data_socket_error(-1));
    733 		}
    734 	}
    735 	else if (fcntl(fd, F_SETFL, opt | O_NONBLOCK) == -1)
    736 	{
    737 		if (client)
    738 		{
    739 			unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client,
    740 				   "Could not get socket options (F_SETFL): $socket_error",
    741 				   log_data_socket_error(-1));
    742 		}
    743 	}
    744 #else
    745 	opt = 1;
    746 	if (ioctlsocket(fd, FIONBIO, &opt) < 0)
    747 	{
    748 		if (client)
    749 		{
    750 			unreal_log(ULOG_WARNING, "socket", "SOCKET_ERROR_SETSOCKOPTS", client,
    751 				   "Could not ioctlsocket FIONBIO: $socket_error",
    752 				   log_data_socket_error(-1));
    753 		}
    754 	}
    755 #endif
    756 }
    757 
    758 /** Returns 1 if using a loopback IP (127.0.0.1) or
    759  * using a local IP number on the same machine (effectively the same;
    760  * no network traffic travels outside this machine).
    761  * @param ip	The IP address to check
    762  * @returns 1 if loopback, 0 if not.
    763  */
    764 int is_loopback_ip(char *ip)
    765 {
    766 	ConfigItem_listen *e;
    767 
    768 	if (!strcmp(ip, "127.0.0.1") || !strcmp(ip, "0:0:0:0:0:0:0:1") || !strcmp(ip, "0:0:0:0:0:ffff:127.0.0.1"))
    769 		return 1;
    770 
    771 	for (e = conf_listen; e; e = e->next)
    772 	{
    773 		if ((e->options & LISTENER_BOUND) && e->ip && !strcmp(ip, e->ip))
    774 			return 1;
    775 	}
    776 	return 0;
    777 }
    778 
    779 /** Retrieve the remote IP address and port of a socket.
    780  * @param client	Client to check
    781  * @param fd		File descriptor
    782  * @param port		Remote port (will be written)
    783  * @returns The IP address
    784  */
    785 const char *getpeerip(Client *client, int fd, int *port)
    786 {
    787 	static char ret[HOSTLEN+1];
    788 
    789 	if (IsIPV6(client))
    790 	{
    791 		struct sockaddr_in6 addr;
    792 		int len = sizeof(addr);
    793 
    794 		if (getpeername(fd, (struct sockaddr *)&addr, &len) < 0)
    795 			return NULL;
    796 		*port = ntohs(addr.sin6_port);
    797 		return inetntop(AF_INET6, &addr.sin6_addr.s6_addr, ret, sizeof(ret));
    798 	} else
    799 	{
    800 		struct sockaddr_in addr;
    801 		int len = sizeof(addr);
    802 
    803 		if (getpeername(fd, (struct sockaddr *)&addr, &len) < 0)
    804 			return NULL;
    805 		*port = ntohs(addr.sin_port);
    806 		return inetntop(AF_INET, &addr.sin_addr.s_addr, ret, sizeof(ret));
    807 	}
    808 }
    809 
    810 /** Process the incoming connection which has just been accepted.
    811  * This creates a client structure for the user.
    812  * The sockhost field is initialized with the ip# of the host.
    813  * The client is added to the linked list of clients but isnt added to any
    814  * hash tables yuet since it doesnt have a name.
    815  * @param listener	The listen { } block on which the client was accepted.
    816  * @param fd		The file descriptor of the client
    817  * @returns The new client, or NULL in case of trouble.
    818  * @note  When NULL is returned, the client at socket 'fd' will be
    819  *        closed by this function and OpenFiles is adjusted appropriately.
    820  */
    821 Client *add_connection(ConfigItem_listen *listener, int fd)
    822 {
    823 	Client *client;
    824 	const char *ip;
    825 	int port = 0;
    826 	Hook *h;
    827 
    828 	client = make_client(NULL, &me);
    829 	client->local->socket_type = listener->socket_type;
    830 	client->local->listener = listener;
    831 	client->local->listener->clients++;
    832 
    833 	if (listener->socket_type == SOCKET_TYPE_UNIX)
    834 		ip = listener->spoof_ip ? listener->spoof_ip : "127.0.0.1";
    835 	else
    836 		ip = getpeerip(client, fd, &port);
    837 
    838 	if (!ip)
    839 	{
    840 		/* On Linux 2.4 and FreeBSD the socket may just have been disconnected
    841 		 * so it's not a serious error and can happen quite frequently -- Syzop
    842 		 */
    843 		if (ERRNO != P_ENOTCONN)
    844 		{
    845 			unreal_log(ULOG_ERROR, "listen", "ACCEPT_ERROR", NULL,
    846 			           "Failed to accept new client: unable to get IP address: $socket_error",
    847 				   log_data_socket_error(fd),
    848 				   log_data_string("listen_ip", listener->ip),
    849 				   log_data_integer("listen_port", listener->port));
    850 		}
    851 refuse_client:
    852 			ircstats.is_ref++;
    853 			client->local->fd = -2;
    854 			if (!list_empty(&client->client_node))
    855 				list_del(&client->client_node);
    856 			if (!list_empty(&client->lclient_node))
    857 				list_del(&client->lclient_node);
    858 			free_client(client);
    859 			fd_close(fd);
    860 			--OpenFiles;
    861 			return NULL;
    862 	}
    863 
    864 	/* Fill in sockhost & ip ASAP */
    865 	set_sockhost(client, ip);
    866 	safe_strdup(client->ip, ip);
    867 	client->local->port = port;
    868 	client->local->fd = fd;
    869 
    870 	/* Tag loopback connections */
    871 	if (is_loopback_ip(client->ip))
    872 	{
    873 		ircstats.is_loc++;
    874 		SetLocalhost(client);
    875 	}
    876 
    877 	add_client_to_list(client);
    878 	irccounts.unknown++;
    879 	client->status = CLIENT_STATUS_UNKNOWN;
    880 	list_add(&client->lclient_node, &unknown_list);
    881 
    882 	for (h = Hooks[HOOKTYPE_ACCEPT]; h; h = h->next)
    883 	{
    884 		int value = (*(h->func.intfunc))(client);
    885 		if (value == HOOK_DENY)
    886 		{
    887 			irccounts.unknown--;
    888 			goto refuse_client;
    889 		}
    890 		if (value != HOOK_CONTINUE)
    891 			break;
    892 	}
    893 
    894 	if ((listener->options & LISTENER_TLS) && ctx_server)
    895 	{
    896 		SSL_CTX *ctx = listener->ssl_ctx ? listener->ssl_ctx : ctx_server;
    897 
    898 		if (ctx)
    899 		{
    900 			SetTLSAcceptHandshake(client);
    901 			if ((client->local->ssl = SSL_new(ctx)) == NULL)
    902 			{
    903 				irccounts.unknown--;
    904 				goto refuse_client;
    905 			}
    906 			SetTLS(client);
    907 			SSL_set_fd(client->local->ssl, fd);
    908 			SSL_set_nonblocking(client->local->ssl);
    909 			SSL_set_ex_data(client->local->ssl, tls_client_index, client);
    910 			if (!unreal_tls_accept(client, fd))
    911 			{
    912 				SSL_set_shutdown(client->local->ssl, SSL_RECEIVED_SHUTDOWN);
    913 				SSL_smart_shutdown(client->local->ssl);
    914 				SSL_free(client->local->ssl);
    915 				irccounts.unknown--;
    916 				goto refuse_client;
    917 			}
    918 		}
    919 	} else
    920 	{
    921 		listener->start_handshake(client);
    922 	}
    923 	return client;
    924 }
    925 
    926 /** Start of normal client handshake - DNS and ident lookups, etc.
    927  * @param client	The client
    928  * @note This is called directly after accept() -> add_connection() for plaintext.
    929  *       For TLS connections this is called after the TLS handshake is completed.
    930  */
    931 void start_of_normal_client_handshake(Client *client)
    932 {
    933 	struct hostent *he;
    934 
    935 	client->status = CLIENT_STATUS_UNKNOWN; /* reset, to be sure (TLS handshake has ended) */
    936 
    937 	RunHook(HOOKTYPE_HANDSHAKE, client);
    938 
    939 	if (!DONT_RESOLVE && !IsUnixSocket(client))
    940 	{
    941 		if (should_show_connect_info(client))
    942 			sendto_one(client, NULL, ":%s %s", me.name, REPORT_DO_DNS);
    943 		he = unrealdns_doclient(client);
    944 
    945 		if (client->local->hostp)
    946 			goto doauth; /* Race condition detected, DNS has been done, continue with auth */
    947 
    948 		if (!he)
    949 		{
    950 			/* Resolving in progress */
    951 			SetDNSLookup(client);
    952 		} else {
    953 			/* Host was in our cache */
    954 			client->local->hostp = he;
    955 			if (should_show_connect_info(client))
    956 				sendto_one(client, NULL, ":%s %s", me.name, REPORT_FIN_DNSC);
    957 		}
    958 	}
    959 
    960 doauth:
    961 	consider_ident_lookup(client);
    962 	fd_setselect(client->local->fd, FD_SELECT_READ, read_packet, client);
    963 }
    964 
    965 /** Called when DNS lookup has been completed and we can proceed with the client handshake.
    966  * @param client	The client
    967  * @param he		The resolved or unresolved host
    968  */
    969 void proceed_normal_client_handshake(Client *client, struct hostent *he)
    970 {
    971 	ClearDNSLookup(client);
    972 	client->local->hostp = he;
    973 	if (should_show_connect_info(client))
    974 	{
    975 		sendto_one(client, NULL, ":%s %s",
    976 		           me.name,
    977 		           client->local->hostp ? REPORT_FIN_DNS : REPORT_FAIL_DNS);
    978 	}
    979 }
    980 
    981 /** Read a packet from a client.
    982  * @param fd		File descriptor
    983  * @param revents	Read events (ignored)
    984  * @param data		Associated data (the client)
    985  */
    986 void read_packet(int fd, int revents, void *data)
    987 {
    988 	Client *client = data;
    989 	int length = 0;
    990 	time_t now = TStime();
    991 	Hook *h;
    992 	int processdata;
    993 
    994 	/* Don't read from dead sockets */
    995 	if (IsDeadSocket(client))
    996 	{
    997 		fd_setselect(fd, FD_SELECT_READ, NULL, client);
    998 		return;
    999 	}
   1000 
   1001 	SET_ERRNO(0);
   1002 
   1003 	fd_setselect(fd, FD_SELECT_READ, read_packet, client);
   1004 	/* Restore handling of writes towards send_queued_cb(), since
   1005 	 * it may be overwritten in an earlier call to read_packet(),
   1006 	 * to handle (TLS) writes by read_packet(), see below under
   1007 	 * SSL_ERROR_WANT_WRITE.
   1008 	 */
   1009 	fd_setselect(fd, FD_SELECT_WRITE, send_queued_cb, client);
   1010 
   1011 	while (1)
   1012 	{
   1013 		if (IsTLS(client) && client->local->ssl != NULL)
   1014 		{
   1015 			length = SSL_read(client->local->ssl, readbuf, sizeof(readbuf));
   1016 
   1017 			if (length < 0)
   1018 			{
   1019 				int err = SSL_get_error(client->local->ssl, length);
   1020 
   1021 				switch (err)
   1022 				{
   1023 				case SSL_ERROR_WANT_WRITE:
   1024 					fd_setselect(fd, FD_SELECT_READ, NULL, client);
   1025 					fd_setselect(fd, FD_SELECT_WRITE, read_packet, client);
   1026 					length = -1;
   1027 					SET_ERRNO(P_EWOULDBLOCK);
   1028 					break;
   1029 				case SSL_ERROR_WANT_READ:
   1030 					fd_setselect(fd, FD_SELECT_READ, read_packet, client);
   1031 					length = -1;
   1032 					SET_ERRNO(P_EWOULDBLOCK);
   1033 					break;
   1034 				case SSL_ERROR_SYSCALL:
   1035 					break;
   1036 				case SSL_ERROR_SSL:
   1037 					if (ERRNO == P_EAGAIN)
   1038 						break;
   1039 				default:
   1040 					/*length = 0;
   1041 					SET_ERRNO(0);
   1042 					^^ why this? we should error. -- todo: is errno correct?
   1043 					*/
   1044 					break;
   1045 				}
   1046 			}
   1047 		}
   1048 		else
   1049 			length = recv(client->local->fd, readbuf, sizeof(readbuf), 0);
   1050 
   1051 		if (length <= 0)
   1052 		{
   1053 			if (length < 0 && ((ERRNO == P_EWOULDBLOCK) || (ERRNO == P_EAGAIN) || (ERRNO == P_EINTR)))
   1054 				return;
   1055 
   1056 			if (IsServer(client) || client->server) /* server or outgoing connection */
   1057 				lost_server_link(client, NULL);
   1058 
   1059 			exit_client(client, NULL, ERRNO ? "Read error" : "Connection closed");
   1060 			return;
   1061 		}
   1062 
   1063 		client->local->last_msg_received = now;
   1064 		if (client->local->last_msg_received > client->local->fake_lag)
   1065 			client->local->fake_lag = client->local->last_msg_received;
   1066 		/* FIXME: Is this correct? I have my doubts. */
   1067 		ClearPingSent(client);
   1068 
   1069 		ClearPingWarning(client);
   1070 
   1071 		processdata = 1;
   1072 		for (h = Hooks[HOOKTYPE_RAWPACKET_IN]; h; h = h->next)
   1073 		{
   1074 			processdata = (*(h->func.intfunc))(client, readbuf, &length);
   1075 			if (processdata == 0)
   1076 				break; /* if hook tells to ignore the data, then break now */
   1077 			if (processdata < 0)
   1078 				return; /* if hook tells client is dead, return now */
   1079 		}
   1080 
   1081 		if (processdata && !process_packet(client, readbuf, length, 0))
   1082 			return;
   1083 
   1084 		/* bail on short read! */
   1085 		if (length < sizeof(readbuf))
   1086 			return;
   1087 	}
   1088 }
   1089 
   1090 /** Process input from clients that may have been deliberately delayed due to fake lag */
   1091 void process_clients(void)
   1092 {
   1093 	Client *client;
   1094         
   1095 	/* Problem:
   1096 	 * When processing a client, that current client may exit due to eg QUIT.
   1097 	 * Similarly, current->next may be killed due to /KILL.
   1098 	 * When a client is killed, in the past we were not allowed to touch it anymore
   1099 	 * so that was a bit problematic. Now we can touch current->next, but it may
   1100 	 * have been removed from the lclient_list or unknown_list.
   1101 	 * In other words, current->next->next may be NULL even though there are more
   1102 	 * clients on the list.
   1103 	 * This is why the whole thing is wrapped in an additional do { } while() loop
   1104 	 * to make sure we re-run the list if we ended prematurely.
   1105 	 * We could use some kind of 'tagging' to mark already processed clients.
   1106 	 * However, parse_client_queued() already takes care not to read (fake) lagged
   1107 	 * clients, and we don't actually read/recv anything in the meantime, so clients
   1108 	 * in the beginning of the list won't benefit, they won't get higher prio.
   1109 	 * Another alternative is not to run the loop again, but that WOULD be
   1110 	 * unfair to clients later in the list which wouldn't be processed then
   1111 	 * under a heavy (kill) load scenario.
   1112 	 * I think the chosen solution is best, though it remains silly. -- Syzop
   1113 	 */
   1114 
   1115 	do {
   1116 		list_for_each_entry(client, &lclient_list, lclient_node)
   1117 		{
   1118 			if ((client->local->fd >= 0) && DBufLength(&client->local->recvQ) && !IsDead(client))
   1119 			{
   1120 				parse_client_queued(client);
   1121 				if (IsDead(client))
   1122 					break;
   1123 			}
   1124 		}
   1125 	} while(&client->lclient_node != &lclient_list);
   1126 
   1127 	do {
   1128 		list_for_each_entry(client, &unknown_list, lclient_node)
   1129 		{
   1130 			if ((client->local->fd >= 0) && DBufLength(&client->local->recvQ) && !IsDead(client))
   1131 			{
   1132 				parse_client_queued(client);
   1133 				if (IsDead(client) || (client->status > CLIENT_STATUS_UNKNOWN))
   1134 					break;
   1135 			}
   1136 		}
   1137 	} while(&client->lclient_node != &unknown_list);
   1138 
   1139 	do {
   1140 		list_for_each_entry(client, &control_list, lclient_node)
   1141 		{
   1142 			if ((client->local->fd >= 0) && DBufLength(&client->local->recvQ) && !IsDead(client))
   1143 			{
   1144 				parse_client_queued(client);
   1145 				if (IsDead(client))
   1146 					break;
   1147 			}
   1148 		}
   1149 	} while(&client->lclient_node != &control_list);
   1150 
   1151 
   1152 }
   1153 
   1154 /** Check if 'ip' is a valid IP address, and if so what type.
   1155  * @param ip	The IP address
   1156  * @retval 4	Valid IPv4 address
   1157  * @retval 6	Valid IPv6 address
   1158  * @retval 0	Invalid IP address (eg: a hostname)
   1159  */
   1160 int is_valid_ip(const char *ip)
   1161 {
   1162 	char scratch[64];
   1163 
   1164 	if (BadPtr(ip))
   1165 		return 0;
   1166 
   1167 	if (inet_pton(AF_INET, ip, scratch) == 1)
   1168 		return 4; /* IPv4 */
   1169 
   1170 	if (inet_pton(AF_INET6, ip, scratch) == 1)
   1171 		return 6; /* IPv6 */
   1172 
   1173 	return 0; /* not an IP address */
   1174 }
   1175 
   1176 /** Checks if the system is IPv6 capable.
   1177  * IPv6 is always available at compile time (libs, headers), but the OS may
   1178  * not have IPv6 enabled (or ipv6 kernel module not loaded). So we better check..
   1179  */
   1180 int ipv6_capable(void)
   1181 {
   1182 	int s = socket(AF_INET6, SOCK_STREAM, 0);
   1183 	if (s < 0)
   1184 		return 0; /* NO ipv6 */
   1185 
   1186 	CLOSE_SOCK(s);
   1187 	return 1; /* YES */
   1188 }
   1189 
   1190 /** Return 1 if UNIX sockets of type SOCK_STREAM are supported, and 0 otherwise */
   1191 int unix_sockets_capable(void)
   1192 {
   1193 	int fd = fd_socket(AF_UNIX, SOCK_STREAM, 0, "Testing UNIX socket");
   1194 	if (fd < 0)
   1195 		return 0;
   1196 	fd_close(fd);
   1197 	return 1;
   1198 }
   1199 
   1200 /** Attempt to deliver data to a client.
   1201  * This function is only called from send_queued() and will deal
   1202  * with sending to the TLS or plaintext connection.
   1203  * @param cptr The client
   1204  * @param str  The string to send
   1205  * @param len  The length of the string
   1206  * @param want_read In case of TLS it may happen that SSL_write()
   1207  *                  needs to READ data. If this happens then this
   1208  *                  function will set *want_read to 1.
   1209  *                  The upper layer should then call us again when
   1210  *                  there is data ready to be READ.
   1211  * @retval <0  Some fatal error occurred, (but not EWOULDBLOCK).
   1212  *             This return is a request to close the socket and
   1213  *             clean up the link.
   1214  * @retval >=0 No real error occurred, returns the number of
   1215  *             bytes actually transferred. EWOULDBLOCK and other
   1216  *             possibly similar conditions should be mapped to
   1217  *             zero return. Upper level routine will have to
   1218  *             decide what to do with those unwritten bytes...
   1219  */
   1220 int deliver_it(Client *client, char *str, int len, int *want_read)
   1221 {
   1222 	int  retval;
   1223 
   1224 	*want_read = 0;
   1225 
   1226 	if (IsDeadSocket(client) ||
   1227 	    (!IsServer(client) && !IsUser(client) && !IsHandshake(client) &&
   1228 	     !IsTLSHandshake(client) && !IsUnknown(client) &&
   1229 	     !IsControl(client) && !IsRPC(client)))
   1230 	{
   1231 		return -1;
   1232 	}
   1233 
   1234 	if (IsTLS(client) && client->local->ssl != NULL)
   1235 	{
   1236 		retval = SSL_write(client->local->ssl, str, len);
   1237 
   1238 		if (retval < 0)
   1239 		{
   1240 			switch (SSL_get_error(client->local->ssl, retval))
   1241 			{
   1242 			case SSL_ERROR_WANT_READ:
   1243 				SET_ERRNO(P_EWOULDBLOCK);
   1244 				*want_read = 1;
   1245 				return 0;
   1246 			case SSL_ERROR_WANT_WRITE:
   1247 				SET_ERRNO(P_EWOULDBLOCK);
   1248 				break;
   1249 			case SSL_ERROR_SYSCALL:
   1250 				break;
   1251 			case SSL_ERROR_SSL:
   1252 				if (ERRNO == P_EAGAIN)
   1253 					break;
   1254 				/* FALLTHROUGH */
   1255 			default:
   1256 				return -1; /* hm.. why was this 0?? we have an error! */
   1257 			}
   1258 		}
   1259 	}
   1260 	else
   1261 		retval = send(client->local->fd, str, len, 0);
   1262 	/*
   1263 	   ** Convert WOULDBLOCK to a return of "0 bytes moved". This
   1264 	   ** should occur only if socket was non-blocking. Note, that
   1265 	   ** all is Ok, if the 'write' just returns '0' instead of an
   1266 	   ** error and errno=EWOULDBLOCK.
   1267 	   **
   1268 	   ** ...now, would this work on VMS too? --msa
   1269 	 */
   1270 # ifndef _WIN32
   1271 	if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN ||
   1272 	    errno == ENOBUFS))
   1273 # else
   1274 		if (retval < 0 && (WSAGetLastError() == WSAEWOULDBLOCK ||
   1275 		    WSAGetLastError() == WSAENOBUFS))
   1276 # endif
   1277 			retval = 0;
   1278 
   1279 	if (retval > 0)
   1280 	{
   1281 		client->local->traffic.bytes_sent += retval;
   1282 		me.local->traffic.bytes_sent += retval;
   1283 	}
   1284 
   1285 	return (retval);
   1286 }
   1287 
   1288 /** Initiate an outgoing connection, the actual connect() call. */
   1289 int unreal_connect(int fd, const char *ip, int port, SocketType socket_type)
   1290 {
   1291 	int n;
   1292 
   1293 	if (socket_type == SOCKET_TYPE_IPV6)
   1294 	{
   1295 		struct sockaddr_in6 server;
   1296 		memset(&server, 0, sizeof(server));
   1297 		server.sin6_family = AF_INET6;
   1298 		inet_pton(AF_INET6, ip, &server.sin6_addr);
   1299 		server.sin6_port = htons(port);
   1300 		n = connect(fd, (struct sockaddr *)&server, sizeof(server));
   1301 	}
   1302 	else if (socket_type == SOCKET_TYPE_IPV4)
   1303 	{
   1304 		struct sockaddr_in server;
   1305 		memset(&server, 0, sizeof(server));
   1306 		server.sin_family = AF_INET;
   1307 		inet_pton(AF_INET, ip, &server.sin_addr);
   1308 		server.sin_port = htons(port);
   1309 		n = connect(fd, (struct sockaddr *)&server, sizeof(server));
   1310 	} else
   1311 	{
   1312 		struct sockaddr_un server;
   1313 		memset(&server, 0, sizeof(server));
   1314 		server.sun_family = AF_UNIX;
   1315 		strlcpy(server.sun_path, ip, sizeof(server.sun_path));
   1316 		n = connect(fd, (struct sockaddr *)&server, sizeof(server));
   1317 	}
   1318 
   1319 #ifndef _WIN32
   1320 	if (n < 0 && (errno != EINPROGRESS))
   1321 #else
   1322 	if (n < 0 && (WSAGetLastError() != WSAEINPROGRESS) && (WSAGetLastError() != WSAEWOULDBLOCK))
   1323 #endif
   1324 	{
   1325 		return 0; /* FATAL ERROR */
   1326 	}
   1327 
   1328 	return 1; /* SUCCESS (probably still in progress) */
   1329 }
   1330 
   1331 /** Bind to an IP/port (port may be 0 for auto).
   1332  * @returns 0 on failure, other on success.
   1333  */
   1334 int unreal_bind(int fd, const char *ip, int port, SocketType socket_type)
   1335 {
   1336 	if (socket_type == SOCKET_TYPE_IPV4)
   1337 	{
   1338 		struct sockaddr_in server;
   1339 		memset(&server, 0, sizeof(server));
   1340 		server.sin_family = AF_INET;
   1341 		server.sin_port = htons(port);
   1342 		if (inet_pton(AF_INET, ip, &server.sin_addr.s_addr) != 1)
   1343 			return 0;
   1344 		return !bind(fd, (struct sockaddr *)&server, sizeof(server));
   1345 	}
   1346 	else if (socket_type == SOCKET_TYPE_IPV6)
   1347 	{
   1348 		struct sockaddr_in6 server;
   1349 		memset(&server, 0, sizeof(server));
   1350 		server.sin6_family = AF_INET6;
   1351 		server.sin6_port = htons(port);
   1352 		if (inet_pton(AF_INET6, ip, &server.sin6_addr.s6_addr) != 1)
   1353 			return 0;
   1354 		return !bind(fd, (struct sockaddr *)&server, sizeof(server));
   1355 	} else
   1356 	{
   1357 		struct sockaddr_un server;
   1358 		mode_t saved_umask, new_umask;
   1359 		int ret;
   1360 
   1361 		if (port == 0)
   1362 			new_umask = 077;
   1363 		else
   1364 			new_umask = port ^ 0777;
   1365 
   1366 		unlink(ip); /* (ignore errors) */
   1367 
   1368 		memset(&server, 0, sizeof(server));
   1369 		server.sun_family = AF_UNIX;
   1370 		strlcpy(server.sun_path, ip, sizeof(server.sun_path));
   1371 		saved_umask = umask(new_umask);
   1372 		ret = !bind(fd, (struct sockaddr *)&server, sizeof(server));
   1373 		umask(saved_umask);
   1374 
   1375 		return ret;
   1376 	}
   1377 }
   1378 
   1379 #ifdef _WIN32
   1380 void init_winsock(void)
   1381 {
   1382 	WSADATA WSAData;
   1383 	if (WSAStartup(MAKEWORD(1, 1), &WSAData) != 0)
   1384 	{
   1385 		MessageBox(NULL, "Unable to initialize WinSock", "UnrealIRCD Initalization Error", MB_OK);
   1386 		fprintf(stderr, "Unable to initialize WinSock\n");
   1387 		exit(1);
   1388 	}
   1389 }
   1390 #endif