X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_bsd.c;h=c63f5654d947983684e94d23c1aa959c369ca64b;hb=refs%2Fheads%2Fupstream;hp=0fe5702ef05902a649bcceefe20506aa92bd7ab4;hpb=6c6f21e86b44e933449b988779b41c65c9df8949;p=ircu2.10.12-pk.git diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 0fe5702..c63f565 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -17,155 +17,94 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/** @file + * @brief Functions that now (or in the past) relied on BSD APIs. + * @version $Id$ + */ +#include "config.h" -#include "sys.h" -#include -#include -#if HAVE_SYS_FILE_H -#include -#endif -#if HAVE_SYS_IOCTL_H -#include -#endif -#ifdef SOL2 -#include -#endif -#ifdef UNIXPORT -#include -#endif -#include -#ifdef USE_POLL -#ifndef HAVE_POLL_H -#undef USE_POLL -#else /* HAVE_POLL_H */ -#ifdef HAVE_STROPTS_H -#include -#endif -#include -#endif /* HAVE_POLL_H */ -#endif /* USE_POLL */ -#include -#if HAVE_FCNTL_H -#include -#endif -#if HAVE_UNISTD_H -#include -#endif -#include -#include -#include -#ifdef USE_SYSLOG -#include -#endif -#include -#include -#include -#include -#include -#include "h.h" -#include "res.h" -#include "struct.h" -#include "s_bsd.h" -#include "s_serv.h" -#include "numeric.h" -#include "send.h" -#include "s_conf.h" -#include "s_misc.h" #include "s_bsd.h" +#include "client.h" +#include "IPcheck.h" +#include "channel.h" +#include "class.h" #include "hash.h" -#include "s_err.h" +#include "ircd_alloc.h" +#include "ircd_log.h" +#include "ircd_features.h" +#include "ircd_osdep.h" +#include "ircd_reply.h" +#include "ircd_snprintf.h" +#include "ircd_string.h" #include "ircd.h" -#include "support.h" -#include "s_auth.h" -#include "class.h" +#include "list.h" +#include "listener.h" +#include "msg.h" +#include "msgq.h" +#include "numeric.h" +#include "numnicks.h" #include "packet.h" -#include "s_ping.h" -#include "channel.h" -#include "version.h" #include "parse.h" -#include "common.h" -#include "bsd.h" -#include "numnicks.h" -#include "s_user.h" -#include "sprintf_irc.h" #include "querycmds.h" -#include "IPcheck.h" +#include "res.h" +#include "s_auth.h" +#include "s_conf.h" +#include "s_debug.h" +#include "s_misc.h" +#include "s_user.h" +#include "send.h" +#include "ssl.h" +#include "struct.h" +#include "sys.h" +#include "uping.h" +#include "version.h" + +/* #include -- Now using assert in ircd_log.h */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -RCSTAG_CC("$Id$"); - -#ifndef IN_LOOPBACKNET -#define IN_LOOPBACKNET 0x7f -#endif - -aClient *loc_clients[MAXCONNECTIONS]; -int highest_fd = 0, udpfd = -1, resfd = -1; -unsigned int readcalls = 0; -static struct sockaddr_in mysk; -static void polludp(); - -static struct sockaddr *connect_inet(aConfItem *, aClient *, int *); -static int completed_connection(aClient *); -static int check_init(aClient *, char *); -static void do_dns_async(), set_sock_opts(int, aClient *); -#ifdef UNIXPORT -static struct sockaddr *connect_unix(aConfItem *, aClient *, int *); -static void add_unixconnection(aClient *, int); -static char unixpath[256]; -#endif -static char readbuf[8192]; -#ifdef USE_POLL -static struct pollfd poll_fds[MAXCONNECTIONS + 1]; -static aClient *poll_cptr[MAXCONNECTIONS + 1]; -#endif /* USE_POLL */ -#ifdef VIRTUAL_HOST -struct sockaddr_in vserv; -#endif -static int running_in_background; - -#ifdef GODMODE -#ifndef NODNS -#define NODNS -#endif -#ifndef NOFLOODCONTROL -#define NOFLOODCONTROL -#endif -#endif +/** Array of my own clients, indexed by file descriptor. */ +struct Client* LocalClientArray[MAXCONNECTIONS]; +/** Maximum file descriptor in current use. */ +int HighestFd = -1; +/** Default local address for outbound IPv4 connections. */ +struct irc_sockaddr VirtualHost_v4; +/** Default local address for outbound IPv6 connections. */ +struct irc_sockaddr VirtualHost_v6; +/** Temporary buffer for reading data from a peer. */ +static char readbuf[SERVER_TCP_WINDOW]; /* - * Try and find the correct name to use with getrlimit() for setting the max. - * number of files allowed to be open by this process. - */ -#ifdef RLIMIT_FDMAX -#define RLIMIT_FD_MAX RLIMIT_FDMAX -#else -#ifdef RLIMIT_NOFILE -#define RLIMIT_FD_MAX RLIMIT_NOFILE -#else -#ifdef RLIMIT_OPEN_MAX -#define RLIMIT_FD_MAX RLIMIT_OPEN_MAX -#else -#undef RLIMIT_FD_MAX -#endif -#endif -#endif - -#if !defined(USE_POLL) -#if FD_SETSIZE < (MAXCONNECTIONS + 4) -/* - * Sanity check - * - * All operating systems work when MAXCONNECTIONS <= 252. - * Most operating systems work when MAXCONNECTIONS <= 1020 and FD_SETSIZE is - * updated correctly in the system headers (on BSD systems our sys.h has - * defined FD_SETSIZE to MAXCONNECTIONS+4 before including the system's headers - * but sys/types.h might have abruptly redefined it so the check is still - * done), you might already need to recompile your kernel. - * For larger FD_SETSIZE your milage may vary (kernel patches may be needed). - * The check is _NOT_ done if we will not use FD_SETS at all (USE_POLL) + * report_error text constants */ -#error "FD_SETSIZE is too small or MAXCONNECTIONS too large." -#endif -#endif +const char* const ACCEPT_ERROR_MSG = "error accepting connection for %s: %s"; +const char* const BIND_ERROR_MSG = "bind error for %s: %s"; +const char* const CONNECT_ERROR_MSG = "connect to host %s failed: %s"; +const char* const CONNLIMIT_ERROR_MSG = "connect limit exceeded for %s: %s"; +const char* const LISTEN_ERROR_MSG = "listen error for %s: %s"; +const char* const NONB_ERROR_MSG = "error setting non-blocking for %s: %s"; +const char* const PEERNAME_ERROR_MSG = "getpeername failed for %s: %s"; +const char* const POLL_ERROR_MSG = "poll error for %s: %s"; +const char* const REGISTER_ERROR_MSG = "registering %s: %s"; +const char* const REUSEADDR_ERROR_MSG = "error setting SO_REUSEADDR for %s: %s"; +const char* const SELECT_ERROR_MSG = "select error for %s: %s"; +const char* const SETBUFS_ERROR_MSG = "error setting buffer size for %s: %s"; +const char* const SOCKET_ERROR_MSG = "error creating socket for %s: %s"; +const char* const TOS_ERROR_MSG = "error setting TOS for %s: %s"; + + +static void client_sock_callback(struct Event* ev); +static void client_timer_callback(struct Event* ev); + /* * Cannot use perror() within daemon. stderr is closed in @@ -173,1373 +112,549 @@ static int running_in_background; * been reassigned to a normal connection... */ -/* - * report_error - * - * This a replacement for perror(). Record error to log and - * also send a copy to all *LOCAL* opers online. - * - * text is a *format* string for outputting error. It must - * contain only two '%s', the first will be replaced - * by the sockhost from the cptr, and the latter will - * be taken from sys_errlist[errno]. - * - * cptr if not NULL, is the *LOCAL* client associated with - * the error. +/** Replacement for perror(). Record error to log. Send a copy to all + * *LOCAL* opers, but only if no errors were sent to them in the last + * 20 seconds. + * @param text A *format* string for outputting error. It must contain + * only two '%s', the first will be replaced by the sockhost from the + * cptr, and the latter will be taken from sys_errlist[errno]. + * @param who The client associated with the error. + * @param err The errno value to display. */ -void report_error(char *text, aClient *cptr) +void report_error(const char* text, const char* who, int err) { - Reg1 int errtmp = errno; /* debug may change 'errno' */ - Reg2 char *host; - int err; - size_t len = sizeof(err); + static time_t last_notice = 0; + int errtmp = errno; /* debug may change 'errno' */ + const char* errmsg = (err) ? strerror(err) : ""; - host = (cptr) ? get_client_name(cptr, FALSE) : ""; + if (!errmsg) + errmsg = "Unknown error"; - Debug((DEBUG_ERROR, text, host, strerror(errtmp))); + if (EmptyString(who)) + who = "unknown"; - /* - * Get the *real* error from the socket (well try to anyway..). - * This may only work when SO_DEBUG is enabled but its worth the - * gamble anyway. - */ -#if defined(SO_ERROR) && !defined(SOL2) - if (cptr && !IsMe(cptr) && cptr->fd >= 0) - if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len)) - if (err) - errtmp = err; -#endif - sendto_ops(text, host, strerror(errtmp)); -#ifdef USE_SYSLOG - syslog(LOG_WARNING, text, host, strerror(errtmp)); -#endif - if (!running_in_background) - { - fprintf(stderr, text, host, strerror(errtmp)); - fprintf(stderr, "\n"); - fflush(stderr); - } - return; + sendto_opmask_butone_ratelimited(0, SNO_OLDSNO, &last_notice, text, who, errmsg); + log_write(LS_SOCKET, L_ERROR, 0, text, who, errmsg); + errno = errtmp; } -/* - * inetport - * - * Create a socket in the AF_INET domain, bind it to the port given in - * 'port' and listen to it. Connections are accepted to this socket - * depending on the IP# mask given by 'name'. Returns the fd of the - * socket created or -1 on error. + +/** Called when resolver query finishes. If the DNS lookup was + * successful, start the connection; otherwise notify opers of the + * failure. + * @param vptr The struct ConfItem representing the Connect block. + * @param hp A pointer to the DNS lookup results (NULL on failure). */ -int inetport(aClient *cptr, char *name, unsigned short int port) +static void connect_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) { - static struct sockaddr_in server; - int ad[4], opt; - size_t len = sizeof(server); - char ipname[20]; - - ad[0] = ad[1] = ad[2] = ad[3] = 0; - - /* - * do it this way because building ip# from separate values for each - * byte requires endian knowledge or some nasty messing. Also means - * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-) - */ - sscanf(name, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]); - sprintf_irc(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]); - - if (cptr != &me) - { - sprintf(cptr->sockhost, "%-.42s.%u", name, port); - strcpy(cptr->name, me.name); - } - /* - * At first, open a new socket - */ - if (cptr->fd == -1) - { - alarm(2); - cptr->fd = socket(AF_INET, SOCK_STREAM, 0); - alarm(0); - if (cptr->fd < 0 && errno == EAGAIN) - { - sendto_ops("opening stream socket %s: No more sockets", - get_client_name(cptr, TRUE)); - return -1; - } + struct ConfItem* aconf = (struct ConfItem*) vptr; + assert(aconf); + aconf->dns_pending = 0; + if (addr) { + memcpy(&aconf->address, addr, sizeof(aconf->address)); + connect_server(aconf, 0); } - if (cptr->fd < 0) - { - report_error("opening stream socket %s: %s", cptr); - return -1; - } - else if (cptr->fd >= MAXCLIENTS) - { - sendto_ops("No more connections allowed (%s)", cptr->name); - close(cptr->fd); - return -1; - } - - opt = 1; - setsockopt(cptr->fd, SOL_SOCKET, SO_REUSEADDR, (OPT_TYPE *)&opt, sizeof(opt)); - - /* - * Bind a port to listen for new connections if port is non-null, - * else assume it is already open and try get something from it. - */ - if (port) - { - server.sin_family = AF_INET; -#ifndef VIRTUAL_HOST - server.sin_addr.s_addr = INADDR_ANY; -#else - if (vserv.sin_addr.s_addr == 0) /* Not already initialised ? */ - { - struct hostent *hep; - memset(&vserv, 0, sizeof(vserv)); - vserv.sin_family = AF_INET; - hep = gethostbyname(me.name); /* Use name from M: line */ - if (hep && hep->h_addrtype == AF_INET && hep->h_addr_list[0] && - !hep->h_addr_list[1]) - memcpy(&vserv.sin_addr, hep->h_addr_list[0], sizeof(struct in_addr)); - else - { - report_error("Error creating virtual host %s: %s", cptr); - return -1; - } - } - server.sin_addr = vserv.sin_addr; -#endif -#ifdef TESTNET - server.sin_port = htons(port + 10000); -#else - server.sin_port = htons(port); -#endif - if (bind(cptr->fd, (struct sockaddr *)&server, sizeof(server)) == -1) - { - report_error("binding stream socket %s: %s", cptr); - close(cptr->fd); - return -1; - } - } - if (getsockname(cptr->fd, (struct sockaddr *)&server, &len)) - { - report_error("getsockname failed for %s: %s", cptr); - close(cptr->fd); - return -1; - } - - if (cptr == &me) /* KLUDGE to get it work... */ - { - char buf[1024]; - -#ifdef TESTNET - sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*", - ntohs(server.sin_port) - 10000); -#else - sprintf_irc(buf, rpl_str(RPL_MYPORTIS), me.name, "*", - ntohs(server.sin_port)); -#endif - write(1, buf, strlen(buf)); - } - if (cptr->fd > highest_fd) - highest_fd = cptr->fd; - cptr->ip.s_addr = inet_addr(ipname); -#ifdef TESTNET - cptr->port = ntohs(server.sin_port) - 10000; -#else - cptr->port = ntohs(server.sin_port); -#endif - listen(cptr->fd, 128); /* Use listen port backlog of 128 */ - loc_clients[cptr->fd] = cptr; - - return 0; + else + sendto_opmask_butone(0, SNO_OLDSNO, "Connect to %s failed: host lookup", + aconf->name); } -#ifdef UNIXPORT -/* - * unixport - * - * Create a socket and bind it to a filename which is comprised of the path - * (directory where file is placed) and port (actual filename created). - * Set directory permissions as rwxr-xr-x so other users can connect to the - * file which is 'forced' to rwxrwxrwx (different OS's have different need of - * modes so users can connect to the socket). +/** Closes all file descriptors. + * @param close_stderr If non-zero, also close stderr. */ -int unixport(aClient *cptr, char *path, unsigned short int port) +void close_connections(int close_stderr) { - struct sockaddr_un un; - - alarm(2); - cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0); - alarm(0); - if (cptr->fd == -1 && errno == EAGAIN) - { - sendto_ops("error opening unix domain socket %s: No more sockets", - get_client_name(cptr, TRUE)); - return -1; - } - if (cptr->fd == -1) - { - report_error("error opening unix domain socket %s: %s", cptr); - return -1; - } - else if (cptr->fd >= MAXCLIENTS) - { - sendto_ops("No more connections allowed (%s)", cptr->name); - close(cptr->fd); - cptr->fd = -1; - return -1; - } - - un.sun_family = AF_UNIX; -#if HAVE_MKDIR - mkdir(path, 0755); -#else - if (chmod(path, 0755) == -1) - { - sendto_ops("error 'chmod 0755 %s': %s", path, strerror(errno)); -#ifdef USE_SYSLOG - syslog(LOG_WARNING, "error 'chmod 0755 %s': %s", path, strerror(errno)); -#endif - close(cptr->fd); - cptr->fd = -1; - return -1; - } -#endif - sprintf_irc(unixpath, "%s/%u", path, port); - unlink(unixpath); - strncpy(un.sun_path, unixpath, sizeof(un.sun_path) - 1); - un.sun_path[sizeof(un.sun_path) - 1] = 0; - strcpy(cptr->name, me.name); - errno = 0; - get_sockhost(cptr, unixpath); - - if (bind(cptr->fd, (struct sockaddr *)&un, strlen(unixpath) + 2) == -1) + int i; + if (close_stderr) { - report_error("error binding unix socket %s: %s", cptr); - close(cptr->fd); - return -1; + close(0); + close(1); + close(2); } - if (cptr->fd > highest_fd) - highest_fd = cptr->fd; - listen(cptr->fd, 5); - chmod(unixpath, 0777); - cptr->flags |= FLAGS_UNIX; - cptr->port = 0; - loc_clients[cptr->fd] = cptr; - - return 0; + for (i = 3; i < MAXCONNECTIONS; ++i) + close(i); } -#endif -/* - * add_listener - * - * Create a new client which is essentially the stub like 'me' to be used - * for a socket that is passive (listen'ing for connections to be accepted). +/** Initialize process fd limit to MAXCONNECTIONS. */ -int add_listener(aConfItem *aconf) +int init_connection_limits(void) { - aClient *cptr; - - cptr = make_client(NULL, STAT_ME); - cptr->flags = FLAGS_LISTEN; - cptr->acpt = cptr; - cptr->from = cptr; - strncpy(cptr->name, aconf->host, sizeof(cptr->name) - 1); - cptr->name[sizeof(cptr->name) - 1] = 0; -#ifdef UNIXPORT - if (*aconf->host == '/') - { - if (unixport(cptr, aconf->host, aconf->port)) - cptr->fd = -2; + int limit = os_set_fdlimit(MAXCONNECTIONS); + if (0 == limit) + return 1; + if (limit < 0) { + fprintf(stderr, "error setting max fds to %d: %s\n", limit, strerror(errno)); } - else -#endif - if (inetport(cptr, aconf->host, aconf->port)) - cptr->fd = -2; - - if (cptr->fd >= 0) - { - cptr->confs = make_link(); - cptr->confs->next = NULL; - cptr->confs->value.aconf = aconf; - set_non_blocking(cptr->fd, cptr); + else if (limit > 0) { + fprintf(stderr, "ircd fd table too big\nHard Limit: %d IRC max: %d\n", + limit, MAXCONNECTIONS); + fprintf(stderr, "set MAXCONNECTIONS to a smaller value"); } - else - free_client(cptr); return 0; } -/* - * close_listeners - * - * Close and free all clients which are marked as having their socket open - * and in a state where they can accept connections. Unix sockets have - * the path to the socket unlinked for cleanliness. +/** Set up address and port and make a connection. + * @param aconf Provides the connection information. + * @param cptr Client structure for the peer. + * @return Non-zero on success; zero on failure. */ -void close_listeners(void) +static int connect_inet(struct ConfItem* aconf, struct Client* cptr) { - Reg1 aClient *cptr; - Reg2 int i; - Reg3 aConfItem *aconf; + const struct irc_sockaddr *local; + IOResult result; + int family = 0; + assert(0 != aconf); + assert(0 != cptr); /* - * close all 'extra' listening ports we have and unlink the file - * name if it was a unix socket. + * Might as well get sockhost from here, the connection is attempted + * with it so if it fails its useless. */ - for (i = highest_fd; i >= 0; i--) - { - if (!(cptr = loc_clients[i])) - continue; - if (!IsMe(cptr) || cptr == &me || !IsListening(cptr)) - continue; - aconf = cptr->confs->value.aconf; - - if (IsIllegal(aconf) && aconf->clients == 0) - { -#ifdef UNIXPORT - if (IsUnixSocket(cptr)) - { - sprintf_irc(unixpath, "%s/%u", aconf->host, aconf->port); - unlink(unixpath); - } -#endif - close_connection(cptr); - } - } -} - -/* - * init_sys - */ -void init_sys(void) -{ - Reg1 int fd; -#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) - struct rlimit limit; + if (irc_in_addr_valid(&aconf->origin.addr)) + local = &aconf->origin; + else if (irc_in_addr_is_ipv4(&aconf->address.addr)) { + local = &VirtualHost_v4; + family = AF_INET; + } else + local = &VirtualHost_v6; + cli_fd(cptr) = os_socket(local, SOCK_STREAM, cli_name(cptr), family); + if (cli_fd(cptr) < 0) + return 0; - if (!getrlimit(RLIMIT_FD_MAX, &limit)) - { -#ifdef pyr - if (limit.rlim_cur < MAXCONNECTIONS) -#else - if (limit.rlim_max < MAXCONNECTIONS) -#endif - { - fprintf(stderr, "ircd fd table too big\n"); - fprintf(stderr, "Hard Limit: " LIMIT_FMT " IRC max: %d\n", -#ifdef pyr - limit.rlim_cur, -#else - limit.rlim_max, -#endif - (int)MAXCONNECTIONS); - fprintf(stderr, "Fix MAXCONNECTIONS\n"); - exit(-1); - } -#ifndef pyr - limit.rlim_cur = limit.rlim_max; /* make soft limit the max */ - if (setrlimit(RLIMIT_FD_MAX, &limit) == -1) - { - fprintf(stderr, "error setting max fd's to " LIMIT_FMT "\n", - limit.rlim_cur); - exit(-1); - } -#endif - } -#endif /* defined(HAVE_SETRLIMIT) && defined(RLIMIT_FD_MAX) */ -#ifdef DEBUGMODE - if (1) - { - static char logbuf[BUFSIZ]; -#if SETVBUF_REVERSED - setvbuf(stderr, _IOLBF, logbuf, sizeof(logbuf)); -#else - setvbuf(stderr, logbuf, _IOLBF, sizeof(logbuf)); -#endif + /* + * save connection info in client + */ + memcpy(&cli_ip(cptr), &aconf->address.addr, sizeof(cli_ip(cptr))); + ircd_ntoa_r(cli_sock_ip(cptr), &cli_ip(cptr)); + /* + * we want a big buffer for server connections + */ + if (!os_set_sockbufs(cli_fd(cptr), feature_int(FEAT_SOCKSENDBUF), feature_int(FEAT_SOCKRECVBUF))) { + cli_error(cptr) = errno; + report_error(SETBUFS_ERROR_MSG, cli_name(cptr), errno); + close(cli_fd(cptr)); + cli_fd(cptr) = -1; + return 0; } -#endif - - for (fd = 3; fd < MAXCONNECTIONS; fd++) - { - close(fd); - loc_clients[fd] = NULL; + /* + * Set the TOS bits - this is nonfatal if it doesn't stick. + */ + if (!os_set_tos(cli_fd(cptr), feature_int(FEAT_TOS_SERVER))) { + report_error(TOS_ERROR_MSG, cli_name(cptr), errno); } - loc_clients[1] = NULL; - close(1); - - if (bootopt & BOOT_TTY) /* debugging is going to a tty */ - goto init_dgram; - if (!(bootopt & BOOT_DEBUG)) - close(2); - - if (((bootopt & BOOT_CONSOLE) || isatty(0)) && - !(bootopt & BOOT_INETD)) - { - if (fork()) - exit(0); - running_in_background = 1; -#ifdef TIOCNOTTY - if ((fd = open("/dev/tty", O_RDWR)) >= 0) - { - ioctl(fd, TIOCNOTTY, (char *)NULL); - close(fd); - } -#endif -#if defined(HPUX) || defined(SOL2) || defined(_SEQUENT_) || \ - defined(_POSIX_SOURCE) || defined(SVR4) - setsid(); -#else - setpgid(0, 0); -#endif - close(0); /* fd 0 opened by inetd */ - loc_clients[0] = NULL; + if ((result = os_connect_nonb(cli_fd(cptr), &aconf->address)) == IO_FAILURE) { + cli_error(cptr) = errno; + report_error(CONNECT_ERROR_MSG, cli_name(cptr), errno); + close(cli_fd(cptr)); + cli_fd(cptr) = -1; + return 0; } -init_dgram: - resfd = init_resolver(); - - return; -} - -void write_pidfile(void) -{ -#ifdef PPATH - int fd; - char buff[20]; - if ((fd = open(PPATH, O_CREAT | O_WRONLY, 0600)) >= 0) - { - memset(buff, 0, sizeof(buff)); - sprintf(buff, "%5d\n", (int)getpid()); - if (write(fd, buff, strlen(buff)) == -1) - Debug((DEBUG_NOTICE, "Error writing to pid file %s", PPATH)); - close(fd); - return; + + if (!socket_add(&(cli_socket(cptr)), client_sock_callback, + (void*) cli_connect(cptr), + (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING, + SOCK_EVENT_READABLE, cli_fd(cptr))) { + cli_error(cptr) = ENFILE; + report_error(REGISTER_ERROR_MSG, cli_name(cptr), ENFILE); + close(cli_fd(cptr)); + cli_fd(cptr) = -1; + return 0; } -#ifdef DEBUGMODE - else - Debug((DEBUG_NOTICE, "Error opening pid file \"%s\": %s", - PPATH, strerror(errno))); -#endif -#endif + + if(aconf->usessl) { + struct SSLConnection *ssl = ssl_create_connect(cli_fd(cptr), cptr, SSLData_Client); + cli_connect(cptr)->con_ssl = ssl; + if(ssl_handshake(ssl)) { + unsigned int events = 0; + if(ssl_wantread(ssl)) + events |= SOCK_EVENT_READABLE; + if(ssl_wantwrite(ssl)) + events |= SOCK_EVENT_WRITABLE; + socket_events(&(cli_socket(cptr)), SOCK_ACTION_SET | events); + result = IO_BLOCKED; + } + } + + cli_freeflag(cptr) |= FREEFLAG_SOCKET; + return 1; } -/* - * Initialize the various name strings used to store hostnames. This is set - * from either the server's sockhost (if client fd is a tty or localhost) - * or from the ip# converted into a string. 0 = success, -1 = fail. +/** Attempt to send a sequence of bytes to the connection. + * As a side effect, updates \a cptr's FLAG_BLOCKED setting + * and sendB/sendK fields. + * @param cptr Client that should receive data. + * @param buf Message buffer to send to client. + * @return Negative on connection-fatal error; otherwise + * number of bytes sent. */ -static int check_init(aClient *cptr, char *sockn) +unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf) { - struct sockaddr_in sk; - size_t len = sizeof(struct sockaddr_in); - sockn[HOSTLEN] = 0; - -#ifdef UNIXPORT - if (IsUnixSocket(cptr)) - { - strncpy(sockn, cptr->acpt->sockhost, HOSTLEN); - get_sockhost(cptr, sockn); - return 0; - } -#endif - - /* If descriptor is a tty, special checking... */ - if (isatty(cptr->fd)) - { - strncpy(sockn, me.sockhost, HOSTLEN); - memset(&sk, 0, sizeof(struct sockaddr_in)); - } - else if (getpeername(cptr->fd, (struct sockaddr *)&sk, &len) == -1) - { - report_error("connect failure: %s %s", cptr); - return -1; - } - strcpy(sockn, inetntoa(sk.sin_addr)); - if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET) - { - cptr->hostp = NULL; - strncpy(sockn, me.sockhost, HOSTLEN); - } - memcpy(&cptr->ip, &sk.sin_addr, sizeof(struct in_addr)); -#ifdef TESTNET - cptr->port = ntohs(sk.sin_port) - 10000; -#else - cptr->port = ntohs(sk.sin_port); -#endif - - return 0; + unsigned int bytes_written = 0; + unsigned int bytes_count = 0; + IOResult result; + assert(0 != cptr); + + if(cli_connect(cptr)->con_ssl) { + result = ssl_send_encrypt(cli_connect(cptr)->con_ssl, buf, &bytes_count, &bytes_written); + } else { + result = os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written); + } + + switch (result) { + case IO_SUCCESS: + ClrFlag(cptr, FLAG_BLOCKED); + + cli_sendB(cptr) += bytes_written; + cli_sendB(&me) += bytes_written; + /* A partial write implies that future writes will block. */ + if (bytes_written < bytes_count) + SetFlag(cptr, FLAG_BLOCKED); + break; + case IO_BLOCKED: + SetFlag(cptr, FLAG_BLOCKED); + break; + case IO_FAILURE: + cli_error(cptr) = errno; + SetFlag(cptr, FLAG_DEADSOCKET); + break; + } + return bytes_written; } -/* - * Ordinary client access check. Look for conf lines which have the same - * status as the flags passed. +/** Complete non-blocking connect()-sequence. Check access and + * terminate connection, if trouble detected. + * @param cptr Client to which we have connected, with all ConfItem structs attached. + * @return Zero on failure (caller should exit_client()), non-zero on success. */ -enum AuthorizationCheckResult check_client(aClient *cptr) +int completed_connection(struct Client* cptr) { - static char sockname[HOSTLEN + 1]; - Reg2 struct hostent *hp = NULL; - Reg3 int i; - enum AuthorizationCheckResult acr; - - ClearAccess(cptr); - Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]", - cptr->name, inetntoa(cptr->ip))); + struct ConfItem *aconf; + time_t newts; + struct Client *acptr; + int i; - if (check_init(cptr, sockname)) - return ACR_BAD_SOCKET; + assert(0 != cptr); - if (!IsUnixSocket(cptr)) - hp = cptr->hostp; /* - * Verify that the host to ip mapping is correct both ways and that - * the ip#(s) for the socket is listed for the host. + * get the socket status from the fd first to check if + * connection actually succeeded */ - if (hp) - { - for (i = 0; hp->h_addr_list[i]; i++) - if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr))) - break; - if (!hp->h_addr_list[i]) - { - sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]", - inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr)); - hp = NULL; - } - } - - if ((acr = attach_Iline(cptr, hp, sockname))) - { - Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name, sockname)); - return acr; + if ((cli_error(cptr) = os_get_sockerr(cli_fd(cptr)))) { + const char* msg = strerror(cli_error(cptr)); + if (!msg) + msg = "Unknown error"; + sendto_opmask_butone(0, SNO_OLDSNO, "Connection failed to %s: %s", + cli_name(cptr), msg); + return 0; } - - Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname)); - - if (inet_netof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) || - inet_netof(cptr->ip) == inet_netof(mysk.sin_addr)) - { - ircstp->is_loc++; - cptr->flags |= FLAGS_LOCAL; + if (!(aconf = find_conf_byname(cli_confs(cptr), cli_name(cptr), CONF_SERVER))) { + sendto_opmask_butone(0, SNO_OLDSNO, "Lost Server Line for %s", cli_name(cptr)); + return 0; } - return ACR_OK; -} + if (s_state(&(cli_socket(cptr))) == SS_CONNECTING) + socket_state(&(cli_socket(cptr)), SS_CONNECTED); -#define CFLAG CONF_CONNECT_SERVER -#define NFLAG CONF_NOCONNECT_SERVER + if (!EmptyString(aconf->passwd)) + sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd); -/* - * check_server() - * - * Check access for a server given its name (passed in cptr struct). - * Must check for all C/N lines which have a name which matches the - * name given and a host which matches. A host alias which is the - * same as the server name is also acceptable in the host field of a - * C/N line. - * - * Returns - * 0 = Success - * -1 = Access denied - * -2 = Bad socket. - */ -int check_server(aClient *cptr) -{ - Reg1 const char *name; - Reg2 aConfItem *c_conf = NULL, *n_conf = NULL; - struct hostent *hp = NULL; - Link *lp; - char abuff[HOSTLEN + USERLEN + 2]; - char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1]; - int i; - - name = cptr->name; - Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]", name, cptr->sockhost)); - - if (IsUnknown(cptr) && !attach_confs(cptr, name, CFLAG | NFLAG)) - { - Debug((DEBUG_DNS, "No C/N lines for %s", name)); - return -1; - } - lp = cptr->confs; /* - * We initiated this connection so the client should have a C and N - * line already attached after passing through the connec_server() - * function earlier. + * Create a unique timestamp */ - if (IsConnecting(cptr) || IsHandshake(cptr)) - { - c_conf = find_conf(lp, name, CFLAG); - n_conf = find_conf(lp, name, NFLAG); - if (!c_conf || !n_conf) - { - sendto_ops("Connecting Error: %s[%s]", name, cptr->sockhost); - det_confs_butmask(cptr, 0); - return -1; + newts = TStime(); + for (i = HighestFd; i > -1; --i) { + if ((acptr = LocalClientArray[i]) && + (IsServer(acptr) || IsHandshake(acptr))) { + if (cli_serv(acptr)->timestamp >= newts) + newts = cli_serv(acptr)->timestamp + 1; } } -#ifdef UNIXPORT - if (IsUnixSocket(cptr)) - { - if (!c_conf) - c_conf = find_conf(lp, name, CFLAG); - if (!n_conf) - n_conf = find_conf(lp, name, NFLAG); - } -#endif + assert(0 != cli_serv(cptr)); + cli_serv(cptr)->timestamp = newts; + SetHandshake(cptr); /* - * If the servername is a hostname, either an alias (CNAME) or - * real name, then check with it as the host. Use gethostbyname() - * to check for servername as hostname. + * Make us timeout after twice the timeout for DNS look ups */ - if (!IsUnixSocket(cptr) && !cptr->hostp) - { - Reg1 aConfItem *aconf; - - aconf = count_cnlines(lp); - if (aconf) - { - Reg1 char *s; - Link lin; - - /* - * Do a lookup for the CONF line *only* and not - * the server connection else we get stuck in a - * nasty state since it takes a SERVER message to - * get us here and we cant interrupt that very well. - */ - ClearAccess(cptr); - lin.value.aconf = aconf; - lin.flags = ASYNC_CONF; - nextdnscheck = 1; - if ((s = strchr(aconf->host, '@'))) - s++; - else - s = aconf->host; - Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s)); - hp = gethost_byname(s, &lin); - } - } + cli_lasttime(cptr) = CurrentTime; + ClearPingSent(cptr); - lp = cptr->confs; + sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s", + cli_name(&me), cli_serv(&me)->timestamp, newts, + MAJOR_PROTOCOL, NumServCap(&me), + feature_bool(FEAT_HUB) ? "h" : "", cli_info(&me)); - ClearAccess(cptr); - if (check_init(cptr, sockname)) - return -2; + return (IsDead(cptr)) ? 0 : 1; +} -check_serverback: - if (hp) - { - for (i = 0; hp->h_addr_list[i]; i++) - if (!memcmp(hp->h_addr_list[i], &cptr->ip, sizeof(struct in_addr))) - break; - if (!hp->h_addr_list[i]) - { - sendto_op_mask(SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%08x]", - inetntoa(cptr->ip), hp->h_name, *((unsigned int *)hp->h_addr)); - hp = NULL; - } - } - else if (cptr->hostp) - { - hp = cptr->hostp; - goto check_serverback; - } +/** Close the physical connection. Side effects: MyConnect(cptr) + * becomes false and cptr->from becomes NULL. + * @param cptr Client to disconnect. + */ +void close_connection(struct Client *cptr) +{ + struct ConfItem* aconf; - if (hp) + if (IsServer(cptr)) { + ServerStats->is_sv++; + ServerStats->is_sbs += cli_sendB(cptr); + ServerStats->is_sbr += cli_receiveB(cptr); + ServerStats->is_sti += CurrentTime - cli_firsttime(cptr); /* - * If we are missing a C or N line from above, search for - * it under all known hostnames we have for this ip#. + * If the connection has been up for a long amount of time, schedule + * a 'quick' reconnect, else reset the next-connect cycle. */ - for (i = 0, name = hp->h_name; name; name = hp->h_aliases[i++]) - { - strncpy(fullname, name, sizeof(fullname) - 1); - fullname[sizeof(fullname) - 1] = 0; - add_local_domain(fullname, HOSTLEN - strlen(fullname)); - Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname, fullname)); - sprintf_irc(abuff, "%s@%s", cptr->username, fullname); - if (!c_conf) - c_conf = find_conf_host(lp, abuff, CFLAG); - if (!n_conf) - n_conf = find_conf_host(lp, abuff, NFLAG); - if (c_conf && n_conf) - { - get_sockhost(cptr, fullname); - break; - } + if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) { + /* + * Reschedule a faster reconnect, if this was a automatically + * connected configuration entry. (Note that if we have had + * a rehash in between, the status has been changed to + * CONF_ILLEGAL). But only do this if it was a "good" link. + */ + aconf->hold = CurrentTime; + aconf->hold += ((aconf->hold - cli_since(cptr) > + feature_int(FEAT_HANGONGOODLINK)) ? + feature_int(FEAT_HANGONRETRYDELAY) : ConfConFreq(aconf)); +/* if (nextconnect > aconf->hold) */ +/* nextconnect = aconf->hold; */ } - name = cptr->name; - - /* - * Check for C and N lines with the hostname portion the ip number - * of the host the server runs on. This also checks the case where - * there is a server connecting from 'localhost'. - */ - if (IsUnknown(cptr) && (!c_conf || !n_conf)) - { - sprintf_irc(abuff, "%s@%s", cptr->username, sockname); - if (!c_conf) - c_conf = find_conf_host(lp, abuff, CFLAG); - if (!n_conf) - n_conf = find_conf_host(lp, abuff, NFLAG); } - /* - * Attach by IP# only if all other checks have failed. - * It is quite possible to get here with the strange things that can - * happen when using DNS in the way the irc server does. -avalon - */ - if (!hp) - { - if (!c_conf) - c_conf = find_conf_ip(lp, (char *)&cptr->ip, cptr->username, CFLAG); - if (!n_conf) - n_conf = find_conf_ip(lp, (char *)&cptr->ip, cptr->username, NFLAG); + else if (IsUser(cptr)) { + ServerStats->is_cl++; + ServerStats->is_cbs += cli_sendB(cptr); + ServerStats->is_cbr += cli_receiveB(cptr); + ServerStats->is_cti += CurrentTime - cli_firsttime(cptr); } else - for (i = 0; hp->h_addr_list[i]; i++) - { - if (!c_conf) - c_conf = find_conf_ip(lp, hp->h_addr_list[i], cptr->username, CFLAG); - if (!n_conf) - n_conf = find_conf_ip(lp, hp->h_addr_list[i], cptr->username, NFLAG); - } - /* - * detach all conf lines that got attached by attach_confs() - */ - det_confs_butmask(cptr, 0); - /* - * if no C or no N lines, then deny access - */ - if (!c_conf || !n_conf) - { - get_sockhost(cptr, sockname); - Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s] c %p n %p", - name, cptr->username, cptr->sockhost, c_conf, n_conf)); - return -1; - } - /* - * attach the C and N lines to the client structure for later use. - */ - attach_conf(cptr, n_conf); - attach_conf(cptr, c_conf); - attach_confs(cptr, name, CONF_HUB | CONF_LEAF | CONF_UWORLD); - - if ((c_conf->ipnum.s_addr == INADDR_NONE) && !IsUnixSocket(cptr)) - memcpy(&c_conf->ipnum, &cptr->ip, sizeof(struct in_addr)); - if (!IsUnixSocket(cptr)) - get_sockhost(cptr, c_conf->host); - - Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", name, cptr->sockhost)); - return 0; -} -#undef CFLAG -#undef NFLAG + ServerStats->is_ni++; + + if(cli_connect(cptr)->con_ssl) { + ssl_free_connection(cli_connect(cptr)->con_ssl); + cli_connect(cptr)->con_ssl = NULL; + } + + if (-1 < cli_fd(cptr)) { + flush_connections(cptr); + LocalClientArray[cli_fd(cptr)] = 0; + close(cli_fd(cptr)); + socket_del(&(cli_socket(cptr))); /* queue a socket delete */ + cli_fd(cptr) = -1; + } + SetFlag(cptr, FLAG_DEADSOCKET); + + MsgQClear(&(cli_sendQ(cptr))); + client_drop_sendq(cli_connect(cptr)); + DBufClear(&(cli_recvQ(cptr))); + memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); + set_snomask(cptr, 0, SNO_SET); -/* - * completed_connection - * - * Complete non-blocking connect()-sequence. Check access and - * terminate connection, if trouble detected. - * - * Return TRUE, if successfully completed - * FALSE, if failed and ClientExit - */ -static int completed_connection(aClient *cptr) -{ - aConfItem *aconf; - time_t newts; - aClient *acptr; - int i; + det_confs_butmask(cptr, 0); - aconf = find_conf(cptr->confs, cptr->name, CONF_CONNECT_SERVER); - if (!aconf) - { - sendto_ops("Lost C-Line for %s", get_client_name(cptr, FALSE)); - return -1; + if (cli_listener(cptr)) { + release_listener(cli_listener(cptr)); + cli_listener(cptr) = 0; } - if (!BadPtr(aconf->passwd)) - sendto_one(cptr, "PASS :%s", aconf->passwd); - aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER); - if (!aconf) - { - sendto_ops("Lost N-Line for %s", get_client_name(cptr, FALSE)); - return -1; - } - make_server(cptr); - /* Create a unique timestamp */ - newts = TStime(); - for (i = highest_fd; i >= 0; i--) - { - if (!(acptr = loc_clients[i]) || (!IsServer(acptr) && !IsHandshake(acptr))) - continue; - if (acptr->serv->timestamp >= newts) - newts = acptr->serv->timestamp + 1; + for ( ; HighestFd > 0; --HighestFd) { + if (LocalClientArray[HighestFd]) + break; } - cptr->serv->timestamp = newts; - SetHandshake(cptr); - /* Make us timeout after twice the timeout for DNS look ups */ - cptr->lasttime = now; - cptr->flags |= FLAGS_PINGSENT; - sendto_one(cptr, "SERVER %s 1 " TIME_T_FMT " " TIME_T_FMT " J%s %s%s :%s", - my_name_for_link(me.name, aconf), me.serv->timestamp, - newts, MAJOR_PROTOCOL, NumServCap(&me), me.info); - if (!IsDead(cptr)) - start_auth(cptr); - - return (IsDead(cptr)) ? -1 : 0; } -/* - * close_connection - * - * Close the physical connection. This function must make - * MyConnect(cptr) == FALSE, and set cptr->from == NULL. +/** Close all unregistered connections. + * @param source Oper who requested the close. + * @return Number of closed connections. */ -void close_connection(aClient *cptr) +int net_close_unregistered_connections(struct Client* source) { - Reg1 aConfItem *aconf; - Reg2 int i, j; - int empty = cptr->fd; + int i; + struct Client* cptr; + int count = 0; + assert(0 != source); - if (IsServer(cptr)) - { - ircstp->is_sv++; - ircstp->is_sbs += cptr->sendB; - ircstp->is_sbr += cptr->receiveB; - ircstp->is_sks += cptr->sendK; - ircstp->is_skr += cptr->receiveK; - ircstp->is_sti += now - cptr->firsttime; - if (ircstp->is_sbs > 1023) - { - ircstp->is_sks += (ircstp->is_sbs >> 10); - ircstp->is_sbs &= 0x3ff; - } - if (ircstp->is_sbr > 1023) - { - ircstp->is_skr += (ircstp->is_sbr >> 10); - ircstp->is_sbr &= 0x3ff; + for (i = HighestFd; i > 0; --i) { + if ((cptr = LocalClientArray[i]) && !IsRegistered(cptr)) { + send_reply(source, RPL_CLOSING, get_client_name(source, HIDE_IP)); + exit_client(source, cptr, &me, "Oper Closing"); + ++count; } } - else if (IsUser(cptr)) - { - ircstp->is_cl++; - ircstp->is_cbs += cptr->sendB; - ircstp->is_cbr += cptr->receiveB; - ircstp->is_cks += cptr->sendK; - ircstp->is_ckr += cptr->receiveK; - ircstp->is_cti += now - cptr->firsttime; - if (ircstp->is_cbs > 1023) - { - ircstp->is_cks += (ircstp->is_cbs >> 10); - ircstp->is_cbs &= 0x3ff; - } - if (ircstp->is_cbr > 1023) - { - ircstp->is_ckr += (ircstp->is_cbr >> 10); - ircstp->is_cbr &= 0x3ff; - } - } - else - ircstp->is_ni++; - - /* - * Remove outstanding DNS queries. - */ - del_queries((char *)cptr); - /* - * If the connection has been up for a long amount of time, schedule - * a 'quick' reconnect, else reset the next-connect cycle. - */ + return count; +} - if ((aconf = find_conf_exact(cptr->name, cptr->username, - cptr->sockhost, CONF_CONNECT_SERVER))) - { - /* - * Reschedule a faster reconnect, if this was a automaticly - * connected configuration entry. (Note that if we have had - * a rehash in between, the status has been changed to - * CONF_ILLEGAL). But only do this if it was a "good" link. - */ - aconf->hold = now; - aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ? - HANGONRETRYDELAY : ConfConFreq(aconf); - if (nextconnect > aconf->hold) - nextconnect = aconf->hold; - } +/** Creates a client which has just connected to us on the given fd. + * The sockhost field is initialized with the ip# of the host. + * The client is not added to the linked list of clients, it is + * passed off to the auth handler for dns and ident queries. + * @param listener Listening socket that received the connection. + * @param fd File descriptor of new connection. + */ +void add_connection(struct Listener* listener, int fd) { + struct irc_sockaddr addr; + struct Client *new_client; + time_t next_target = 0; - if (cptr->authfd >= 0) - close(cptr->authfd); + const char* const throttle_message = + "ERROR :Your host is trying to (re)connect too fast -- throttled\r\n"; + /* 12345678901234567890123456789012345679012345678901234567890123456 */ + const char* const register_message = + "ERROR :Unable to complete your registration\r\n"; - if (cptr->fd >= 0) - { - flush_connections(cptr->fd); - loc_clients[cptr->fd] = NULL; - close(cptr->fd); - cptr->fd = -2; - } + assert(0 != listener); - DBufClear(&cptr->sendQ); - DBufClear(&cptr->recvQ); - memset(cptr->passwd, 0, sizeof(cptr->passwd)); - set_snomask(cptr, 0, SNO_SET); /* - * Clean up extra sockets from P-lines which have been discarded. + * Removed preliminary access check. Full check is performed in m_server and + * m_user instead. Also connection time out help to get rid of unwanted + * connections. */ - if (cptr->acpt != &me && cptr->acpt != cptr) - { - aconf = cptr->acpt->confs->value.aconf; - if (aconf->clients > 0) - aconf->clients--; - if (!aconf->clients && IsIllegal(aconf)) - close_connection(cptr->acpt); + if (!os_get_peername(fd, &addr) || !os_set_nonblocking(fd)) { + ++ServerStats->is_ref; + close(fd); + return; } - - for (; highest_fd > 0; highest_fd--) - if (loc_clients[highest_fd]) - break; - - det_confs_butmask(cptr, 0); - /* - * fd remap to keep loc_clients[i] filled at the bottom. + * Disable IP (*not* TCP) options. In particular, this makes it impossible + * to use source routing to connect to the server. If we didn't do this + * (and if intermediate networks didn't drop source-routed packets), an + * attacker could successfully IP spoof us...and even return the anti-spoof + * ping, because the options would cause the packet to be routed back to + * the spoofer's machine. When we disable the IP options, we delete the + * source route, and the normal routing takes over. */ - if (empty > 0) - if ((j = highest_fd) > (i = empty) && !IsLog(loc_clients[j])) - { - if (IsListening(loc_clients[j])) - return; - if (dup2(j, i) == -1) - return; - loc_clients[i] = loc_clients[j]; - loc_clients[i]->fd = i; - loc_clients[j] = NULL; - close(j); - while (!loc_clients[highest_fd]) - highest_fd--; - } - - return; -} + os_disable_options(fd); -/* - * set_sock_opts - */ -static void set_sock_opts(int fd, aClient *cptr) -{ - size_t opt; -#ifdef SO_REUSEADDR - opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, - (OPT_TYPE *)&opt, sizeof(opt)) < 0) - report_error("setsockopt(SO_REUSEADDR) %s: %s", cptr); -#endif -#ifdef SO_USELOOPBACK - opt = 1; - if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, - (OPT_TYPE *)&opt, sizeof(opt)) < 0) - report_error("setsockopt(SO_USELOOPBACK) %s: %s", cptr); -#endif -#ifdef SO_RCVBUF - opt = 8192; - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0) - report_error("setsockopt(SO_RCVBUF) %s: %s", cptr); -#endif -#ifdef SO_SNDBUF -#ifdef _SEQUENT_ -/* - * Seems that Sequent freezes up if the receving buffer is a different size - * to the sending buffer (maybe a tcp window problem too). - */ - opt = 8192; -#else - opt = 8192; -#endif - if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (OPT_TYPE *)&opt, sizeof(opt)) < 0) - report_error("setsockopt(SO_SNDBUF) %s: %s", cptr); -#endif -#if defined(IP_OPTIONS) && defined(IPPROTO_IP) + if (listener_server(listener)) { - char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2; - - opt = sizeof(readbuf) / 8; - if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)t, &opt) < 0) - report_error("getsockopt(IP_OPTIONS) %s: %s", cptr); - else if (opt > 0 && opt != sizeof(readbuf) / 8) - { - for (*readbuf = '\0'; opt > 0; opt--, s += 3) - sprintf(s, "%02x:", *t++); - *s = '\0'; - sendto_ops("Connection %s using IP opts: (%s)", - get_client_name(cptr, TRUE), readbuf); - } - if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (OPT_TYPE *)NULL, 0) < 0) - report_error("setsockopt(IP_OPTIONS) %s: %s", cptr); + new_client = make_client(0, STAT_UNKNOWN_SERVER); } -#endif -} - -int get_sockerr(aClient *cptr) -{ - int errtmp = errno, err = 0; - size_t len = sizeof(err); -#if defined(SO_ERROR) && !defined(SOL2) - if (cptr->fd >= 0) - if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (OPT_TYPE *)&err, &len)) - if (err) - errtmp = err; -#endif - return errtmp; -} - -/* - * set_non_blocking - * - * Set the client connection into non-blocking mode. If your - * system doesn't support this, you can make this a dummy - * function (and get all the old problems that plagued the - * blocking version of IRC--not a problem if you are a - * lightly loaded node...) - */ -void set_non_blocking(int fd, aClient *cptr) -{ - int res; -#ifndef NBLOCK_SYSV - int nonb = 0; -#endif - - /* - * NOTE: consult ALL your relevant manual pages *BEFORE* changing - * these ioctl's. There are quite a few variations on them, - * as can be seen by the PCS one. They are *NOT* all the same. - * Heed this well. - Avalon. - */ -#ifdef NBLOCK_POSIX - nonb |= O_NONBLOCK; -#endif -#ifdef NBLOCK_BSD - nonb |= O_NDELAY; -#endif -#ifdef NBLOCK_SYSV - /* This portion of code might also apply to NeXT. -LynX */ - res = 1; - - if (ioctl(fd, FIONBIO, &res) < 0) - report_error("ioctl(fd,FIONBIO) failed for %s: %s", cptr); -#else - if ((res = fcntl(fd, F_GETFL, 0)) == -1) - report_error("fcntl(fd, F_GETFL) failed for %s: %s", cptr); - else if (fcntl(fd, F_SETFL, res | nonb) == -1) - report_error("fcntl(fd, F_SETL, nonb) failed for %s: %s", cptr); -#endif - return; -} - -extern unsigned short server_port; - -/* - * Creates a client which has just connected to us on the given fd. - * The sockhost field is initialized with the ip# of the host. - * The client is added to the linked list of clients but isnt added to any - * hash tables yet since it doesn't have a name. - */ -aClient *add_connection(aClient *cptr, int fd, int type) -{ - Link lin; - aClient *acptr; - aConfItem *aconf = NULL; - acptr = - make_client(NULL, - (cptr->port == server_port) ? STAT_UNKNOWN_SERVER : STAT_UNKNOWN_USER); - - if (cptr != &me) - aconf = cptr->confs->value.aconf; - /* - * Removed preliminary access check. Full check is performed in - * m_server and m_user instead. Also connection time out help to - * get rid of unwanted connections. - */ - if (type == ADCON_TTY) /* If descriptor is a tty, - special checking... */ - get_sockhost(acptr, cptr->sockhost); else { - Reg1 char *s, *t; - struct sockaddr_in addr; - size_t len = sizeof(struct sockaddr_in); - - if (getpeername(fd, (struct sockaddr *)&addr, &len) == -1) - { - report_error("Failed in connecting to %s: %s", cptr); - add_con_refuse: - ircstp->is_ref++; - acptr->fd = -2; - free_client(acptr); - close(fd); - return NULL; - } - /* Don't want to add "Failed in connecting to" here.. */ - if (aconf && IsIllegal(aconf)) - goto add_con_refuse; - /* - * Copy ascii address to 'sockhost' just in case. Then we - * have something valid to put into error messages... - */ - get_sockhost(acptr, inetntoa(addr.sin_addr)); - memcpy(&acptr->ip, &addr.sin_addr, sizeof(struct in_addr)); -#ifdef TESTNET - acptr->port = ntohs(addr.sin_port) - 10000; -#else - acptr->port = ntohs(addr.sin_port); -#endif - /* - * Check that this socket (client) is allowed to accept - * connections from this IP#. + * Add this local client to the IPcheck registry. + * + * If they're throttled, murder them, but tell them why first. */ - for (s = (char *)&cptr->ip, t = (char *)&acptr->ip, len = 4; - len > 0; len--, s++, t++) + if (!IPcheck_local_connect(&addr.addr, &next_target)) { - if (!*s) - continue; - if (*s != *t) - break; - } - - if (len) - goto add_con_refuse; - - lin.flags = ASYNC_CLIENT; - lin.value.cptr = acptr; -#ifdef NODNS - if (!strcmp("127.0.0.1", inetntoa(addr.sin_addr))) - { - static struct hostent lhe = { "localhost", NULL, 0, 0, NULL }; - acptr->hostp = &lhe; - if (!DoingAuth(acptr)) - SetAccess(acptr); - } - else - { -#endif - Debug((DEBUG_DNS, "lookup %s", inetntoa(addr.sin_addr))); - acptr->hostp = gethost_byaddr(&acptr->ip, &lin); - if (!acptr->hostp) - SetDNS(acptr); - nextdnscheck = 1; -#ifdef NODNS + ++ServerStats->is_ref; + write(fd, throttle_message, strlen(throttle_message)); + close(fd); + return; } -#endif + new_client = make_client(0, STAT_UNKNOWN_USER); + SetIPChecked(new_client); } - if (aconf) - aconf->clients++; - acptr->fd = fd; - if (fd > highest_fd) - highest_fd = fd; - loc_clients[fd] = acptr; - acptr->acpt = cptr; - Count_newunknown(nrof); - add_client_to_list(acptr); - set_non_blocking(acptr->fd, acptr); - set_sock_opts(acptr->fd, acptr); - /* - * Add this local client to the IPcheck registry. - * If it is a connection to a user port and if the site has been throttled, - * reject the user. + * Copy ascii address to 'sockhost' just in case. Then we have something + * valid to put into error messages... */ - if (IPcheck_local_connect(acptr) == -1 && IsUserPort(acptr)) - { - ircstp->is_ref++; - exit_client(cptr, acptr, &me, - "Your host is trying to (re)connect too fast -- throttled"); - return NULL; - } + ircd_ntoa_r(cli_sock_ip(new_client), &addr.addr); + strcpy(cli_sockhost(new_client), cli_sock_ip(new_client)); + memcpy(&cli_ip(new_client), &addr.addr, sizeof(cli_ip(new_client))); - start_auth(acptr); - return acptr; -} - -#ifdef UNIXPORT -static void add_unixconnection(aClient *cptr, int fd) -{ - aClient *acptr; - aConfItem *aconf = NULL; + if (next_target) + cli_nexttarget(new_client) = next_target; - acptr = make_client(NULL, STAT_UNKNOWN); - - /* - * Copy ascii address to 'sockhost' just in case. Then we - * have something valid to put into error messages... - */ - get_sockhost(acptr, me.sockhost); - if (cptr != &me) - aconf = cptr->confs->value.aconf; - if (aconf) - { - if (IsIllegal(aconf)) - { - ircstp->is_ref++; - acptr->fd = -2; - free_client(acptr); - close(fd); - return; - } - else - aconf->clients++; + cli_fd(new_client) = fd; + if (!socket_add(&(cli_socket(new_client)), client_sock_callback, + (void*) cli_connect(new_client), SS_CONNECTED, 0, fd)) { + ++ServerStats->is_ref; + write(fd, register_message, strlen(register_message)); + close(fd); + cli_fd(new_client) = -1; + return; + } + cli_freeflag(new_client) |= FREEFLAG_SOCKET; + cli_listener(new_client) = listener; + ++listener->ref_count; + + Count_newunknown(UserStats); + + if(listener_ssl(listener)) { + struct Connection* con = cli_connect(new_client); + con->con_ssl = ssl_start_handshake_listener(listener->ssl_listener, fd, new_client, SSLData_Client); + unsigned int events = 0; + if(ssl_wantread(con->con_ssl)) + events |= SOCK_EVENT_READABLE; + if(ssl_wantwrite(con->con_ssl)) + events |= SOCK_EVENT_WRITABLE; + socket_events(&(cli_socket(new_client)), SOCK_ACTION_SET | events); + } else { + /* if we've made it this far we can put the client on the auth query pile */ + start_auth(new_client); } - acptr->fd = fd; - if (fd > highest_fd) - highest_fd = fd; - loc_clients[fd] = acptr; - acptr->acpt = cptr; - SetUnixSock(acptr); - memcpy(&acptr->ip, &me.ip, sizeof(struct in_addr)); - - Count_newunknown(nrof); - add_client_to_list(acptr); - set_non_blocking(acptr->fd, acptr); - set_sock_opts(acptr->fd, acptr); - SetAccess(acptr); - return; } -#endif -/* - * select/poll convert macro's by Run. - * - * The names are chosen to reflect what they means when NOT using poll(). +/** Determines whether to tell the events engine we're interested in + * writable events. + * @param cptr Client for which to decide this. */ -#ifndef USE_POLL -typedef fd_set *fd_setp_t; -#define RFD_ISSET(fd, rfd, index) FD_ISSET((fd), (rfd)) -#define WFD_ISSET(fd, wfd, index) FD_ISSET((fd), (wfd)) -#define RFD_SET(fd, rfd, index, cptr) FD_SET((fd), (rfd)) -#define WFD_SET(fd, wfd, index, cptr) FD_SET((fd), (wfd)) -#define RWFD_SET(fd, wfd, index) FD_SET((fd), (wfd)) -#define RFD_CLR_OUT(fd, rfd, index) FD_CLR((fd), (rfd)) -#define WFD_CLR_OUT(fd, wfd, index) FD_CLR((fd), (wfd)) -#define LOC_FD(index) (index) -#define LOC_CLIENTS(index) loc_clients[index] -#define HIGHEST_INDEX highest_fd -#else /* USE_POLL */ -typedef unsigned int fd_setp_t; /* Actually, an index to poll_fds[] */ -#ifdef _AIX -#define POLLREADFLAGS (POLLIN|POLLMSG) -#else -# if defined(POLLMSG) && defined(POLLIN) && defined(POLLRDNORM) -# define POLLREADFLAGS (POLLMSG|POLLIN|POLLRDNORM) -# else -# if defined(POLLIN) && defined(POLLRDNORM) -# define POLLREADFLAGS (POLLIN|POLLRDNORM) -# else -# if defined(POLLIN) -# define POLLREADFLAGS POLLIN -# else -# if defined(POLLRDNORM) -# define POLLREADFLAGS POLLRDNORM -# endif -# endif -# endif -# endif -#endif -#if defined(POLLOUT) && defined(POLLWRNORM) -#define POLLWRITEFLAGS (POLLOUT|POLLWRNORM) -#else -# if defined(POLLOUT) -# define POLLWRITEFLAGS POLLOUT -# else -# if defined(POLLWRNORM) -# define POLLWRITEFLAGS POLLWRNORM -# endif -# endif -#endif -#ifdef POLLHUP -#define POLLERRORS (POLLHUP|POLLERR) -#else -#define POLLERRORS POLLERR -#endif -#define RFD_ISSET(fd, rfd, index) \ - ((poll_fds[index].revents & POLLREADFLAGS) || \ - ((poll_fds[index].events & POLLREADFLAGS) && \ - (poll_fds[index].revents & POLLERRORS))) -#define WFD_ISSET(fd, wfd, index) \ - ((poll_fds[index].revents & POLLWRITEFLAGS) || \ - ((poll_fds[index].events & POLLWRITEFLAGS) && \ - (poll_fds[index].revents & POLLERRORS))) -#define RFD_SET(fdes, rfd, index, cptr) \ - do { \ - poll_fds[index].fd = fdes; \ - poll_cptr[index] = cptr; \ - poll_fds[index].events = POLLREADFLAGS; \ - added = TRUE; \ - } while(0) -#define WFD_SET(fdes, wfd, index, cptr) \ - do { \ - poll_fds[index].fd = fdes; \ - poll_cptr[index] = cptr; \ - if (added) \ - poll_fds[index].events |= POLLWRITEFLAGS; \ - else \ - { \ - poll_fds[index].events = POLLWRITEFLAGS; \ - added = TRUE; \ - } \ - } while(0) -/* This identical to WFD_SET() when used after a call to RFD_SET(): */ -#define RWFD_SET(fd, wfd, index) poll_fds[index].events |= POLLWRITEFLAGS -/* [RW]FD_CLR_OUT() clears revents, not events */ -#define RFD_CLR_OUT(fd, rfd, index) poll_fds[index].revents &= ~POLLREADFLAGS -#define WFD_CLR_OUT(fd, wfd, index) poll_fds[index].revents &= ~POLLWRITEFLAGS -#define LOC_FD(index) (poll_fds[index].fd) -#define LOC_CLIENTS(index) (poll_cptr[index]) -#define HIGHEST_INDEX (currfd_index - 1) -#endif /* USE_POLL */ +void update_write(struct Client* cptr) +{ + /* If there are messages that need to be sent along, or if the client + * is in the middle of a /list, then we need to tell the engine that + * we're interested in writable events--otherwise, we need to drop + * that interest. + */ + socket_events(&(cli_socket(cptr)), + ((MsgQLength(&cli_sendQ(cptr)) || cli_listing(cptr)) ? + SOCK_ACTION_ADD : SOCK_ACTION_DEL) | SOCK_EVENT_WRITABLE); +} -/* - * read_packet - * - * Read a 'packet' of data from a connection and process it. Read in 8k - * chunks to give a better performance rating (for server connections). - * Do some tricky stuff for client connections to make sure they don't do - * any flooding >:-) -avalon +/** Read a 'packet' of data from a connection and process it. Read in + * 8k chunks to give a better performance rating (for server + * connections). Do some tricky stuff for client connections to make + * sure they don't do any flooding >:-) -avalon + * @param cptr Client from which to read data. + * @param socket_ready If non-zero, more data can be read from the client's socket. + * @return Positive number on success, zero on connection-fatal failure, negative + * if user is killed. */ -static int read_packet(aClient *cptr, fd_setp_t rfd) +static int read_packet(struct Client *cptr, int socket_ready) { - size_t dolen = 0; - int length = 0; - int done; - - if (RFD_ISSET(cptr->fd, rfd, rfd) && - !(IsUser(cptr) && DBufLength(&cptr->recvQ) > 6090)) - { - errno = 0; - length = recv(cptr->fd, readbuf, sizeof(readbuf), 0); - - cptr->lasttime = now; - if (cptr->lasttime > cptr->since) - cptr->since = cptr->lasttime; - cptr->flags &= ~(FLAGS_PINGSENT | FLAGS_NONL); - /* - * If not ready, fake it so it isnt closed - */ - if (length == -1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) - return 1; - if (length <= 0) - return length; + unsigned int dolen = 0; + unsigned int length = 0; + + if (socket_ready && + !(IsUser(cptr) && + DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) { + + /* Handle SSL Sockets + */ + int recvret; + if(cli_connect(cptr)->con_ssl) { + recvret = ssl_recv_decrypt(cli_connect(cptr)->con_ssl, readbuf, sizeof(readbuf), &length); + } else { + recvret = os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length); + } + switch (recvret) { + case IO_SUCCESS: + if (length) + { + cli_lasttime(cptr) = CurrentTime; + ClearPingSent(cptr); + ClrFlag(cptr, FLAG_NONL); + if (cli_lasttime(cptr) > cli_since(cptr)) + cli_since(cptr) = cli_lasttime(cptr); + } + break; + case IO_BLOCKED: + break; + case IO_FAILURE: + cli_error(cptr) = errno; + /* SetFlag(cptr, FLAG_DEADSOCKET); */ + return 0; + } } - + /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ - if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) - { - if (length > 0) - if ((done = dopacket(cptr, readbuf, length))) - return done; - } + if (length > 0 && IsServer(cptr)) + return server_dopacket(cptr, readbuf, length); + else if (length > 0 && (IsHandshake(cptr) || IsConnecting(cptr))) + return connect_dopacket(cptr, readbuf, length); else { /* @@ -1547,39 +662,18 @@ static int read_packet(aClient *cptr, fd_setp_t rfd) * it on the end of the receive queue and do it when its * turn comes around. */ - if (!dbuf_put(&cptr->recvQ, readbuf, length)) + if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); -#ifndef NOFLOODCONTROL - if (IsUser(cptr) && DBufLength(&cptr->recvQ) > CLIENT_FLOOD) + int HasUnlimitFlood = HasPriv(cptr, PRIV_UNLIMIT_FLOOD); + + if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD) && !HasUnlimitFlood) return exit_client(cptr, cptr, &me, "Excess Flood"); -#endif - while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr) -#ifndef NOFLOODCONTROL - && (IsTrusted(cptr) || cptr->since - now < 10) -#endif - ) + while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && + (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10 || HasUnlimitFlood)) { - /* - * If it has become registered as a Server - * then skip the per-message parsing below. - */ - if (IsServer(cptr)) - { - /* - * XXX - this blindly deletes data if no cr/lf is received at - * the end of a lot of messages and the data stored in the - * dbuf is greater than sizeof(readbuf) - */ - dolen = dbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf)); - if (0 == dolen) - break; - if ((done = dopacket(cptr, readbuf, dolen))) - return done; - break; - } - dolen = dbuf_getmsg(&cptr->recvQ, cptr->buffer, BUFSIZE); + dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* * Devious looking...whats it do ? well..if a client * sends a *long* message without any CR or LF, then @@ -1588,1028 +682,332 @@ static int read_packet(aClient *cptr, fd_setp_t rfd) * deletes the rest of the buffer contents. * -avalon */ - if (0 == dolen) - { - if (DBufLength(&cptr->recvQ) < 510) - { - cptr->flags |= FLAGS_NONL; - break; - } - DBufClear(&cptr->recvQ); - break; - } - else if (CPTR_KILLED == client_dopacket(cptr, dolen)) - return CPTR_KILLED; - } - } - return 1; -} - -/* - * Check all connections for new connections and input data that is to be - * processed. Also check for connections with data queued and whether we can - * write it out. - * - * Don't ever use ZERO for `delay', unless you mean to poll and then - * you have to have sleep/wait somewhere else in the code.--msa - */ -int read_message(time_t delay) -{ - Reg1 aClient *cptr; - Reg2 int nfds; - struct timeval wait; -#ifdef pyr - struct timeval nowt; - unsigned long us; -#endif - time_t delay2 = delay; - unsigned long usec = 0; - int res, length, fd, i; - int auth = 0, ping = 0; -#ifndef USE_POLL - fd_set read_set, write_set; -#else /* USE_POLL */ - unsigned int currfd_index = 0; - unsigned int udpfdindex = 0; - unsigned int resfdindex = 0; - unsigned long timeout; - int added; -#endif /* USE_POLL */ - -#ifdef pyr - gettimeofday(&nowt, NULL); - now = nowt.tv_sec; -#endif - - for (res = 0;;) - { -#ifndef USE_POLL - FD_ZERO(&read_set); - FD_ZERO(&write_set); -#endif /* not USE_POLL */ - for (i = highest_fd; i >= 0; i--) - { -#ifdef USE_POLL - added = FALSE; -#endif /* USE_POLL */ - if (!(cptr = loc_clients[i])) - continue; - if (IsLog(cptr)) - continue; - if (DoingAuth(cptr)) - { - auth++; - Debug((DEBUG_NOTICE, "auth on %p %d", cptr, i)); - RFD_SET(cptr->authfd, &read_set, currfd_index, cptr); - if (cptr->flags & FLAGS_WRAUTH) - RWFD_SET(cptr->authfd, &write_set, currfd_index); - } - if (IsPing(cptr)) + if (dolen == 0) { - ping++; - Debug((DEBUG_NOTICE, "open ping on %p %d", cptr, i)); - if (!cptr->firsttime || now <= cptr->firsttime) - { - RFD_SET(i, &read_set, currfd_index, cptr); - delay2 = 1; - if (DoPing(cptr) && now > cptr->lasttime) - RWFD_SET(i, &write_set, currfd_index); - } - else - { - del_queries((char *)cptr); - end_ping(cptr); - } -#ifdef USE_POLL - if (added) - currfd_index++; -#endif /* USE_POLL */ - continue; + if (DBufLength(&(cli_recvQ(cptr))) < 510) + SetFlag(cptr, FLAG_NONL); + else + { + /* More than 512 bytes in the line - drop the input and yell + * at the client. + */ + DBufClear(&(cli_recvQ(cptr))); + send_reply(cptr, ERR_INPUTTOOLONG); + } } - if (DoingDNS(cptr) || DoingAuth(cptr)) - { -#ifdef USE_POLL - if (added) - currfd_index++; -#endif /* USE_POLL */ - continue; - } - if (IsMe(cptr) && IsListening(cptr)) - RFD_SET(i, &read_set, currfd_index, cptr); - else if (!IsMe(cptr)) - { - if (DBufLength(&cptr->recvQ) && delay2 > 2) - delay2 = 1; - if (DBufLength(&cptr->recvQ) < 4088) - RFD_SET(i, &read_set, currfd_index, cptr); - if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) || - (cptr->listing && DBufLength(&cptr->sendQ) < 2048)) -#ifndef pyr - WFD_SET(i, &write_set, currfd_index, cptr); -#else /* pyr */ - { - if (!(cptr->flags & FLAGS_BLOCKED)) - WFD_SET(i, &write_set, currfd_index, cptr); - else - delay2 = 0, usec = 500000; - } - if (now - cptr->lw.tv_sec && nowt.tv_usec - cptr->lw.tv_usec < 0) - us = 1000000; - else - us = 0; - us += nowt.tv_usec; - if (us - cptr->lw.tv_usec > 500000) - cptr->flags &= ~FLAGS_BLOCKED; -#endif /* pyr */ - } -#ifdef USE_POLL - if (added) - currfd_index++; -#endif /* USE_POLL */ - } - - if (udpfd >= 0) - { - RFD_SET(udpfd, &read_set, currfd_index, NULL); -#ifdef USE_POLL - udpfdindex = currfd_index; - currfd_index++; -#endif /* USE_POLL */ - } - if (resfd >= 0) - { - RFD_SET(resfd, &read_set, currfd_index, NULL); -#ifdef USE_POLL - resfdindex = currfd_index; - currfd_index++; -#endif /* USE_POLL */ - } - - wait.tv_sec = MIN(delay2, delay); - wait.tv_usec = usec; -#ifndef USE_POLL -#ifdef HPUX - nfds = select(FD_SETSIZE, (int *)&read_set, (int *)&write_set, 0, &wait); -#else - nfds = select(FD_SETSIZE, &read_set, &write_set, 0, &wait); -#endif -#else /* USE_POLL */ - timeout = (wait.tv_sec * 1000) + (wait.tv_usec / 1000); - nfds = poll(poll_fds, currfd_index, timeout); -#endif /* USE_POLL */ - now = time(NULL); - if (nfds == -1 && errno == EINTR) - return -1; - else if (nfds >= 0) - break; - report_error("select %s: %s", &me); - res++; - if (res > 5) - restart("too many select errors"); - sleep(10); - now += 10; - } - - if (udpfd >= 0 && RFD_ISSET(udpfd, &read_set, udpfdindex)) - { - polludp(); - nfds--; - RFD_CLR_OUT(udpfd, &read_set, udpfdindex); - } - /* - * Check fd sets for the ping fd's (if set and valid!) first - * because these can not be processed using the normal loops below. - * And we want them to be as fast as possible. - * -Run - */ - for (i = HIGHEST_INDEX; (ping > 0) && (i >= 0); i--) - { - if (!(cptr = LOC_CLIENTS(i))) - continue; - if (!IsPing(cptr)) - continue; - ping--; - if ((nfds > 0) && RFD_ISSET(cptr->fd, &read_set, i)) - { - nfds--; - RFD_CLR_OUT(cptr->fd, &read_set, i); - read_ping(cptr); /* This can RunFree(cptr) ! */ - } - else if ((nfds > 0) && WFD_ISSET(cptr->fd, &write_set, i)) - { - nfds--; - cptr->lasttime = now; - WFD_CLR_OUT(cptr->fd, &write_set, i); - send_ping(cptr); /* This can RunFree(cptr) ! */ - } - } - if (resfd >= 0 && RFD_ISSET(resfd, &read_set, resfdindex)) - { - do_dns_async(); - nfds--; - RFD_CLR_OUT(resfd, &read_set, resfdindex); - } - /* - * Check fd sets for the auth fd's (if set and valid!) first - * because these can not be processed using the normal loops below. - * -avalon - */ - for (i = HIGHEST_INDEX; (auth > 0) && (i >= 0); i--) - { - if (!(cptr = LOC_CLIENTS(i))) - continue; - if (cptr->authfd < 0) - continue; - auth--; - if ((nfds > 0) && WFD_ISSET(cptr->authfd, &write_set, i)) - { - nfds--; - send_authports(cptr); - } - else if ((nfds > 0) && RFD_ISSET(cptr->authfd, &read_set, i)) - { - nfds--; - read_authports(cptr); - } - } - for (i = HIGHEST_INDEX; i >= 0; i--) - if ((cptr = LOC_CLIENTS(i)) && RFD_ISSET(i, &read_set, i) && - IsListening(cptr)) - { - RFD_CLR_OUT(i, &read_set, i); - nfds--; - cptr->lasttime = now; + else if (client_dopacket(cptr, dolen) == CPTR_KILLED) + return CPTR_KILLED; /* - * There may be many reasons for error return, but - * in otherwise correctly working environment the - * probable cause is running out of file descriptors - * (EMFILE, ENFILE or others?). The man pages for - * accept don't seem to list these as possible, - * although it's obvious that it may happen here. - * Thus no specific errors are tested at this - * point, just assume that connections cannot - * be accepted until some old is closed first. + * If it has become registered as a Server + * then skip the per-message parsing below. */ - if ((fd = accept(LOC_FD(i), NULL, NULL)) < 0) - { - if (errno != EWOULDBLOCK) - report_error("accept() failed%s: %s", NULL); - break; - } -#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECTS) - { /* get an early log of all connections --dl */ - static struct sockaddr_in peer; - static int len; - len = sizeof(peer); - getpeername(fd, (struct sockaddr *)&peer, &len); - syslog(LOG_DEBUG, "Conn: %s", inetntoa(peer.sin_addr)); - } -#endif - ircstp->is_ac++; - if (fd >= MAXCLIENTS) + if (IsHandshake(cptr) || IsServer(cptr)) { - /* Don't send more messages then one every 10 minutes */ - static int count; - static time_t last_time; - ircstp->is_ref++; - ++count; - if (last_time < now - (time_t) 600) - { - if (count > 0) - { - if (!last_time) - last_time = me.since; - sendto_ops - ("All connections in use! Had to refuse %d clients in the last " - STIME_T_FMT " minutes", count, (now - last_time) / 60); - } - else - sendto_ops("All connections in use. (%s)", get_client_name(cptr, - TRUE)); - count = 0; - last_time = now; - } - send(fd, "ERROR :All connections in use\r\n", 32, 0); - close(fd); - break; + while (-1) + { + dolen = dbuf_get(&(cli_recvQ(cptr)), readbuf, sizeof(readbuf)); + if (dolen <= 0) + return 1; + else if (dolen == 0) + { + if (DBufLength(&(cli_recvQ(cptr))) < 510) + SetFlag(cptr, FLAG_NONL); + else + DBufClear(&(cli_recvQ(cptr))); + } + else if ((IsServer(cptr) && + server_dopacket(cptr, readbuf, dolen) == CPTR_KILLED) || + (!IsServer(cptr) && + connect_dopacket(cptr, readbuf, dolen) == CPTR_KILLED)) + return CPTR_KILLED; + } } - /* - * Use of add_connection (which never fails :) meLazy - */ -#ifdef UNIXPORT - if (IsUnixSocket(cptr)) - add_unixconnection(cptr, fd); - else -#endif - if (!add_connection(cptr, fd, ADCON_SOCKET)) - continue; - nextping = now; - if (!cptr->acpt) - cptr->acpt = &me; } - for (i = HIGHEST_INDEX; i >= 0; i--) - { - if (!(cptr = LOC_CLIENTS(i)) || IsMe(cptr)) - continue; -#ifdef USE_POLL - if (DoingDNS(cptr) || DoingAuth(cptr) || !(cptr = loc_clients[LOC_FD(i)])) - continue; -#endif /* USE_POLL */ -#ifdef DEBUGMODE - if (IsLog(cptr)) - continue; -#endif - if (WFD_ISSET(i, &write_set, i)) + /* If there's still data to process, wait 2 seconds first */ + if (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && + !t_onqueue(&(cli_proc(cptr)))) { - int write_err = 0; - nfds--; - /* - * ...room for writing, empty some queue then... - */ - cptr->flags &= ~FLAGS_BLOCKED; - if (IsConnecting(cptr)) - write_err = completed_connection(cptr); - if (!write_err) - { - if (cptr->listing && DBufLength(&cptr->sendQ) < 2048) - list_next_channels(cptr, 64); - send_queued(cptr); - } - if (IsDead(cptr) || write_err) - { - deadsocket: - if (RFD_ISSET(i, &read_set, i)) - { - nfds--; - RFD_CLR_OUT(i, &read_set, i); - } - exit_client(cptr, cptr, &me, - IsDead(cptr) ? LastDeadComment(cptr) : strerror(get_sockerr(cptr))); - continue; - } + Debug((DEBUG_LIST, "Adding client process timer for %C", cptr)); + cli_freeflag(cptr) |= FREEFLAG_TIMER; + timer_add(&(cli_proc(cptr)), client_timer_callback, cli_connect(cptr), + TT_RELATIVE, 2); } - length = 1; /* for fall through case */ - if ((!NoNewLine(cptr) || RFD_ISSET(i, &read_set, i)) && !IsDead(cptr)) -#ifndef USE_POLL - length = read_packet(cptr, &read_set); -#else /* USE_POLL */ - length = read_packet(cptr, i); -#endif /* USE_POLL */ -#if 0 - /* Bullshit, why would we want to flush sockets while using non-blocking? - * This uses > 4% cpu! --Run */ - if (length > 0) - flush_connections(LOC_FD(i)); -#endif - if ((length != CPTR_KILLED) && IsDead(cptr)) - goto deadsocket; - if (!RFD_ISSET(i, &read_set, i) && length > 0) - continue; - nfds--; - readcalls++; - if (length > 0) - continue; - - /* - * ...hmm, with non-blocking sockets we might get - * here from quite valid reasons, although.. why - * would select report "data available" when there - * wasn't... So, this must be an error anyway... --msa - * actually, EOF occurs when read() returns 0 and - * in due course, select() returns that fd as ready - * for reading even though it ends up being an EOF. -avalon - */ - Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d %d", LOC_FD(i), errno, length)); - - if (length == CPTR_KILLED) - continue; - - if ((IsServer(cptr) || IsHandshake(cptr)) && errno == 0 && length == 0) - exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)", - get_client_name(cptr, FALSE), cptr->serv->last_error_msg); - else - exit_client_msg(cptr, cptr, &me, "Read error to %s: %s", - get_client_name(cptr, FALSE), (length < 0) ? - strerror(get_sockerr(cptr)) : "EOF from client"); } - return 0; + return 1; } -/* - * connect_server +/** Start a connection to another server. + * @param aconf Connect block data for target server. + * @param by Client who requested the connection (if any). + * @return Non-zero on success; zero on failure. */ -int connect_server(aConfItem *aconf, aClient *by, struct hostent *hp) +int connect_server(struct ConfItem* aconf, struct Client* by) { - Reg1 struct sockaddr *svp; - Reg2 aClient *cptr, *c2ptr; - Reg3 char *s; - int errtmp, len; + struct Client* cptr = 0; + assert(0 != aconf); - Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s", - aconf->name, aconf->host, inetntoa(aconf->ipnum))); + if (aconf->dns_pending) { + sendto_opmask_butone(0, SNO_OLDSNO, "Server %s connect DNS pending", + aconf->name); + return 0; + } + Debug((DEBUG_NOTICE, "Connect to %s[@%s]", aconf->name, + ircd_ntoa(&aconf->address.addr))); - if ((c2ptr = FindClient(aconf->name))) - { - if (IsServer(c2ptr) || IsMe(c2ptr)) - { - sendto_ops("Server %s already present from %s", - aconf->name, c2ptr->from->name); - if (by && IsUser(by) && !MyUser(by)) - { -#ifndef NO_PROTOCOL9 - if (Protocol(by->from) < 10) - sendto_one(by, ":%s NOTICE %s :Server %s already present from %s", - me.name, by->name, aconf->name, c2ptr->from->name); - else -#endif - sendto_one(by, "%s NOTICE %s%s :Server %s already present from %s", - NumServ(&me), NumNick(by), aconf->name, c2ptr->from->name); + if ((cptr = FindClient(aconf->name))) { + if (IsServer(cptr) || IsMe(cptr)) { + sendto_opmask_butone(0, SNO_OLDSNO, "Server %s already present from %s", + aconf->name, cli_name(cli_from(cptr))); + if (by && IsUser(by) && !MyUser(by)) { + sendcmdto_one(&me, CMD_NOTICE, by, "%C :Server %s already present " + "from %s", by, aconf->name, cli_name(cli_from(cptr))); } - return -1; + return 0; } - else if (IsHandshake(c2ptr) || IsConnecting(c2ptr)) - { - if (by && IsUser(by)) - { - if (MyUser(by) || Protocol(by->from) < 10) - sendto_one(by, ":%s NOTICE %s :Connection to %s already in progress", - me.name, by->name, get_client_name(c2ptr, TRUE)); - else - sendto_one(by, - "%s NOTICE %s%s :Connection to %s already in progress", - NumServ(&me), NumNick(by), get_client_name(c2ptr, TRUE)); + else if (IsHandshake(cptr) || IsConnecting(cptr)) { + if (by && IsUser(by)) { + sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connection to %s already in " + "progress", by, cli_name(cptr)); } - return -1; + return 0; } } - /* - * If we dont know the IP# for this host and itis a hostname and + * If we don't know the IP# for this host and it is a hostname and * not a ip# string, then try and find the appropriate host record. */ - if ((!aconf->ipnum.s_addr) -#ifdef UNIXPORT - && ((aconf->host[2]) != '/') /* needed for Unix domain -- dl */ -#endif - ) - { - Link lin; - - lin.flags = ASYNC_CONNECT; - lin.value.aconf = aconf; - nextdnscheck = 1; - s = strchr(aconf->host, '@'); - s++; /* should NEVER be NULL */ - if ((aconf->ipnum.s_addr = inet_addr(s)) == INADDR_NONE) - { - aconf->ipnum.s_addr = INADDR_ANY; - hp = gethost_byname(s, &lin); - Debug((DEBUG_NOTICE, "co_sv: hp %p ac %p na %s ho %s", - hp, aconf, aconf->name, s)); - if (!hp) - return 0; - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); - } + if (!irc_in_addr_valid(&aconf->address.addr) + && !ircd_aton(&aconf->address.addr, aconf->host)) { + char buf[HOSTLEN + 1]; + + host_from_uh(buf, aconf->host, HOSTLEN); + gethost_byname(buf, connect_dns_callback, aconf); + aconf->dns_pending = 1; + return 0; } - cptr = make_client(NULL, STAT_UNKNOWN); - cptr->hostp = hp; + cptr = make_client(NULL, STAT_UNKNOWN_SERVER); + /* * Copy these in so we have something for error detection. */ - strncpy(cptr->name, aconf->name, sizeof(cptr->name) - 1); - cptr->name[sizeof(cptr->name) - 1] = 0; - strncpy(cptr->sockhost, aconf->host, HOSTLEN); - cptr->sockhost[HOSTLEN] = 0; - -#ifdef UNIXPORT - if (aconf->host[2] == '/') /* (/ starts a 2), Unix domain -- dl */ - svp = connect_unix(aconf, cptr, &len); - else - svp = connect_inet(aconf, cptr, &len); -#else - svp = connect_inet(aconf, cptr, &len); -#endif + ircd_strncpy(cli_name(cptr), aconf->name, HOSTLEN); + ircd_strncpy(cli_sockhost(cptr), aconf->host, HOSTLEN); - if (!svp) - { - if (cptr->fd >= 0) - close(cptr->fd); - cptr->fd = -2; - if (by && IsUser(by) && !MyUser(by)) - { -#ifndef NO_PROTOCOL9 - if (Protocol(by->from) < 10) - sendto_one(by, ":%s NOTICE %s :Couldn't connect to %s", - me.name, by->name, get_client_name(cptr, TRUE)); - else -#endif - sendto_one(by, "%s NOTICE %s%s :Couldn't connect to %s", - NumServ(&me), NumNick(by), get_client_name(cptr, TRUE)); - } - free_client(cptr); - return -1; - } + /* + * Attach config entries to client here rather than in + * completed_connection. This to avoid null pointer references + */ + attach_confs_byhost(cptr, aconf->host, CONF_SERVER); - set_non_blocking(cptr->fd, cptr); - set_sock_opts(cptr->fd, cptr); - signal(SIGALRM, dummy); - alarm(4); - if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS) - { - int err = get_sockerr(cptr); - errtmp = errno; /* other system calls may eat errno */ - alarm(0); - report_error("Connect to host %s failed: %s", cptr); - if (by && IsUser(by) && !MyUser(by)) - { -#ifndef NO_PROTOCOL9 - if (Protocol(by->from) < 10) - sendto_one(by, ":%s NOTICE %s :Connect to host %s failed: %s", - me.name, by->name, get_client_name(cptr, TRUE), strerror(err)); - else -#endif - sendto_one(by, "%s NOTICE %s%s :Connect to host %s failed: %s", - NumServ(&me), NumNick(by), get_client_name(cptr, TRUE), - strerror(err)); + if (!find_conf_byhost(cli_confs(cptr), aconf->host, CONF_SERVER)) { + sendto_opmask_butone(0, SNO_OLDSNO, "Host %s is not enabled for " + "connecting: no Connect block", aconf->name); + if (by && IsUser(by) && !MyUser(by)) { + sendcmdto_one(&me, CMD_NOTICE, by, "%C :Connect to host %s failed: no " + "Connect block", by, aconf->name); } - close(cptr->fd); - cptr->fd = -2; + det_confs_butmask(cptr, 0); free_client(cptr); - errno = errtmp; - if (errno == EINTR) - errno = ETIMEDOUT; - return -1; + return 0; } - alarm(0); - /* - * Attach config entries to client here rather than in - * completed_connection. This to avoid null pointer references - * when name returned by gethostbyaddr matches no C lines - * (could happen in 2.6.1a when host and servername differ). - * No need to check access and do gethostbyaddr calls. - * There must at least be one as we got here C line... meLazy + * attempt to connect to the server in the conf line */ - attach_confs_host(cptr, aconf->host, - CONF_NOCONNECT_SERVER | CONF_CONNECT_SERVER); - - if (!find_conf_host(cptr->confs, aconf->host, CONF_NOCONNECT_SERVER) || - !find_conf_host(cptr->confs, aconf->host, CONF_CONNECT_SERVER)) - { - sendto_ops("Host %s is not enabled for connecting:no C/N-line", - aconf->host); - if (by && IsUser(by) && !MyUser(by)) - { -#ifndef NO_PROTOCOL9 - if (Protocol(by->from) < 10) - sendto_one(by, - ":%s NOTICE %s :Connect to host %s failed: no C/N-lines", - me.name, by->name, get_client_name(cptr, TRUE)); - else -#endif - sendto_one(by, - "%s NOTICE %s%s :Connect to host %s failed: no C/N-lines", - NumServ(&me), NumNick(by), get_client_name(cptr, TRUE)); + if (!connect_inet(aconf, cptr)) { + if (by && IsUser(by) && !MyUser(by)) { + sendcmdto_one(&me, CMD_NOTICE, by, "%C :Couldn't connect to %s", by, + cli_name(cptr)); } det_confs_butmask(cptr, 0); - close(cptr->fd); - cptr->fd = -2; free_client(cptr); - return (-1); + return 0; } /* + * NOTE: if we're here we have a valid C:Line and the client should + * have started the connection and stored the remote address/port and + * ip address name in itself + * * The socket has been connected or connect is in progress. */ make_server(cptr); - if (by && IsUser(by)) - { - sprintf_irc(cptr->serv->by, "%s%s", NumNick(by)); - if (cptr->serv->user) - free_user(cptr->serv->user, NULL); - cptr->serv->user = by->user; - by->user->refcnt++; - } - else - { - *cptr->serv->by = '\0'; - if (cptr->serv->user) - free_user(cptr->serv->user, NULL); - cptr->serv->user = NULL; - } - cptr->serv->up = &me; - if (cptr->fd > highest_fd) - highest_fd = cptr->fd; - loc_clients[cptr->fd] = cptr; - cptr->acpt = &me; + if (by && IsUser(by)) { + ircd_snprintf(0, cli_serv(cptr)->by, sizeof(cli_serv(cptr)->by), "%s%s", + NumNick(by)); + assert(0 == cli_serv(cptr)->user); + cli_serv(cptr)->user = cli_user(by); + cli_user(by)->refcnt++; + } + else { + *(cli_serv(cptr))->by = '\0'; + /* strcpy(cptr->serv->by, "Auto"); */ + } + cli_serv(cptr)->up = &me; SetConnecting(cptr); - get_sockhost(cptr, aconf->host); - Count_newunknown(nrof); + if (cli_fd(cptr) > HighestFd) + HighestFd = cli_fd(cptr); + + LocalClientArray[cli_fd(cptr)] = cptr; + + Count_newunknown(UserStats); + /* Actually we lie, the connect hasn't succeeded yet, but we have a valid + * cptr, so we register it now. + * Maybe these two calls should be merged. + */ add_client_to_list(cptr); hAddClient(cptr); - nextping = now; +/* nextping = CurrentTime; */ - return 0; + return (s_state(&cli_socket(cptr)) == SS_CONNECTED) ? + completed_connection(cptr) : 1; } -static struct sockaddr *connect_inet(aConfItem *aconf, aClient *cptr, int *lenp) +/** Find the real hostname for the host running the server (or one which + * matches the server's name) and its primary IP#. Hostname is stored + * in the client structure passed as a pointer. + */ +void init_server_identity(void) { - static struct sockaddr_in server; - Reg3 struct hostent *hp; - - /* - * Might as well get sockhost from here, the connection is attempted - * with it so if it fails its useless. - */ - alarm(2); - cptr->fd = socket(AF_INET, SOCK_STREAM, 0); - alarm(0); - if (cptr->fd == -1 && errno == EAGAIN) - { - sendto_ops("opening stream socket to server %s: No more sockets", - get_client_name(cptr, TRUE)); - return NULL; - } - if (cptr->fd == -1) - { - report_error("opening stream socket to server %s: %s", cptr); - return NULL; - } - if (cptr->fd >= MAXCLIENTS) - { - sendto_ops("No more connections allowed (%s)", cptr->name); - return NULL; - } - mysk.sin_port = 0; - memset(&server, 0, sizeof(server)); - server.sin_family = AF_INET; - get_sockhost(cptr, aconf->host); - -#ifdef VIRTUAL_HOST - mysk.sin_addr = vserv.sin_addr; -#endif - - /* - * Bind to a local IP# (with unknown port - let unix decide) so - * we have some chance of knowing the IP# that gets used for a host - * with more than one IP#. - */ - /* No we don't bind it, not all OS's can handle connecting with - * an already bound socket, different ip# might occur anyway - * leading to a freezing select() on this side for some time. - * I had this on my Linux 1.1.88 --Run - */ -#ifdef VIRTUAL_HOST - /* - * No, we do bind it if we have virtual host support. If we don't - * explicitly bind it, it will default to IN_ADDR_ANY and we lose - * due to the other server not allowing our base IP --smg - */ - if (bind(cptr->fd, (struct sockaddr *)&mysk, sizeof(mysk)) == -1) - { - report_error("error binding to local port for %s: %s", cptr); - return NULL; - } -#endif + const struct LocalConf* conf = conf_get_local(); + assert(0 != conf); - /* - * By this point we should know the IP# of the host listed in the - * conf line, whether as a result of the hostname lookup or the ip# - * being present instead. If we dont know it, then the connect fails. - */ - if (isDigit(*aconf->host) && (aconf->ipnum.s_addr == INADDR_NONE)) - aconf->ipnum.s_addr = inet_addr(aconf->host); - if (aconf->ipnum.s_addr == INADDR_NONE) - { - hp = cptr->hostp; - if (!hp) - { - Debug((DEBUG_FATAL, "%s: unknown host", aconf->host)); - return NULL; - } - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); - } - memcpy(&server.sin_addr, &aconf->ipnum, sizeof(struct in_addr)); - memcpy(&cptr->ip, &aconf->ipnum, sizeof(struct in_addr)); -#ifdef TESTNET - server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum) + 10000); -#else - server.sin_port = htons(((aconf->port > 0) ? aconf->port : portnum)); -#endif - *lenp = sizeof(server); - return (struct sockaddr *)&server; + ircd_strncpy(cli_name(&me), conf->name, HOSTLEN); + SetYXXServerName(&me, conf->numeric); } -#ifdef UNIXPORT -/* - * connect_unix - * - * Build a socket structure for cptr so that it can connet to the unix - * socket defined by the conf structure aconf. +/** Process events on a client socket. + * @param ev Socket event structure that has a struct Connection as + * its associated data. */ -static struct sockaddr *connect_unix(aConfItem *aconf, aClient *cptr, int *lenp) +static void client_sock_callback(struct Event* ev) { - static struct sockaddr_un sock; + struct Client* cptr; + struct Connection* con; + char *fmt = "%s"; + char *fallback = 0; - alarm(2); - cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0); - alarm(0); - if (cptr->fd == -1 && errno == EAGAIN) - { - sendto_ops("Unix domain connect to host %s failed: No more sockets", - get_client_name(cptr, TRUE)); - return NULL; - } - if (cptr->fd == -1) - { - report_error("Unix domain connect to host %s failed: %s", cptr); - return NULL; - } - else if (cptr->fd >= MAXCLIENTS) - { - sendto_ops("No more connections allowed (%s)", cptr->name); - return NULL; - } + assert(0 != ev_socket(ev)); + assert(0 != s_data(ev_socket(ev))); - get_sockhost(cptr, aconf->host); - /* +2 needed for working Unix domain -- dl */ - strncpy(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path) - 1); - sock.sun_path[sizeof(sock.sun_path) - 1] = 0; - sock.sun_family = AF_UNIX; - *lenp = strlen(sock.sun_path) + 2; + con = (struct Connection*) s_data(ev_socket(ev)); - SetUnixSock(cptr); - return (struct sockaddr *)&sock; -} + assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); -#endif + cptr = con_client(con); -/* - * Find the real hostname for the host running the server (or one which - * matches the server's name) and its primary IP#. Hostname is stored - * in the client structure passed as a pointer. - */ -void get_my_name(aClient *cptr, char *name, size_t len) -{ - static char tmp[HOSTLEN + 1]; -#if HAVE_UNAME - struct utsname utsn; -#endif - struct hostent *hp; - char *cname = cptr->name; - size_t len2; + assert(0 == cptr || con == cli_connect(cptr)); - /* - * Setup local socket structure to use for binding to. - */ - memset(&mysk, 0, sizeof(mysk)); - mysk.sin_family = AF_INET; + switch (ev_type(ev)) { + case ET_DESTROY: + con_freeflag(con) &= ~FREEFLAG_SOCKET; -#if HAVE_UNAME - if (uname(&utsn) == -1) - return; - len2 = strlen(utsn.nodename); - if (len2 > len) - len2 = len; - strncpy(name, utsn.nodename, len2); -#else /* HAVE_GETHOSTNAME */ - if (gethostname(name, len) == -1) - return; -#endif - name[len] = '\0'; + if (!con_freeflag(con) && !cptr) + free_connection(con); + break; - /* Assume that a name containing '.' is a FQDN */ - if (!strchr(name, '.')) - add_local_domain(name, len - strlen(name)); + case ET_CONNECT: /* socket connection completed */ + if(cli_connect(cptr)->con_ssl) { + ssl_start_handshake_connect(cli_connect(cptr)->con_ssl); + } + else if (!completed_connection(cptr) || IsDead(cptr)) + fallback = cli_info(cptr); + break; - /* - * If hostname gives another name than cname, then check if there is - * a CNAME record for cname pointing to hostname. If so accept - * cname as our name. meLazy - */ - if (BadPtr(cname)) - return; - if ( -#ifndef NODNS - /* I don't have DNS while testing, this delays too much */ - (hp = gethostbyname(cname)) || -#endif - (hp = gethostbyname(name))) - { - const char *hname; - int i = 0; + case ET_ERROR: /* an error occurred */ + fallback = cli_info(cptr); + cli_error(cptr) = ev_data(ev); + /* If the OS told us we have a bad file descriptor, we should + * record that for future reference. + */ + if (cli_error(cptr) == EBADF) + cli_fd(cptr) = -1; + if (s_state(&(con_socket(con))) == SS_CONNECTING) { + completed_connection(cptr); + /* for some reason, the os_get_sockerr() in completed_connect() + * can return 0 even when ev_data(ev) indicates a real error, so + * re-assign the client error here. + */ + cli_error(cptr) = ev_data(ev); + break; + } + /*FALLTHROUGH*/ + case ET_EOF: /* end of file on socket */ + Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d", cli_fd(cptr), + cli_error(cptr))); + SetFlag(cptr, FLAG_DEADSOCKET); + if ((IsServer(cptr) || IsHandshake(cptr)) && cli_error(cptr) == 0) { + exit_client_msg(cptr, cptr, &me, "Server %s closed the connection (%s)", + cli_name(cptr), cli_serv(cptr)->last_error_msg); + return; + } else { + fmt = "Read error: %s"; + fallback = "EOF from client"; + } + break; - for (hname = hp->h_name; hname; hname = hp->h_aliases[i++]) - { - strncpy(tmp, hname, sizeof(tmp) - 1); - add_local_domain(tmp, sizeof(tmp) - 1 - strlen(tmp)); + case ET_WRITE: /* socket is writable */ + ClrFlag(cptr, FLAG_BLOCKED); + if (cli_listing(cptr) && MsgQLength(&(cli_sendQ(cptr))) < 2048) + list_next_channels(cptr); + Debug((DEBUG_SEND, "Sending queued data to %C", cptr)); + send_queued(cptr); + break; - /* - * Copy the matching name over and store the - * 'primary' IP# as 'myip' which is used - * later for making the right one is used - * for connecting to other hosts. - */ - if (!strCasediff(me.name, tmp)) - break; + case ET_READ: /* socket is readable */ + if (!IsDead(cptr)) { + Debug((DEBUG_DEBUG, "Reading data from %C", cptr)); + if (read_packet(cptr, 1) == 0) /* error while reading packet */ + fallback = "EOF from client"; } - if (strCasediff(me.name, tmp)) - strncpy(name, hp->h_name, len); - else - strncpy(name, tmp, len); - memcpy(&mysk.sin_addr, hp->h_addr, sizeof(struct in_addr)); - Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE))); - } - return; -} + break; -/* - * Setup a UDP socket and listen for incoming packets - */ -int setup_ping(void) -{ - struct sockaddr_in from; - int on = 1; - - memset(&from, 0, sizeof(from)); -#ifdef VIRTUAL_HOST - from.sin_addr = vserv.sin_addr; -#else - from.sin_addr.s_addr = htonl(INADDR_ANY); -#endif -#ifdef TESTNET - from.sin_port = htons(atoi(UDP_PORT) + 10000); -#else - from.sin_port = htons(atoi(UDP_PORT)); -#endif - from.sin_family = AF_INET; - - if ((udpfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) - { - Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno))); - return -1; - } - if (setsockopt(udpfd, SOL_SOCKET, SO_REUSEADDR, - (OPT_TYPE *)&on, sizeof(on)) == -1) - { -#ifdef USE_SYSLOG - syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd); -#endif - Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s", strerror(errno))); - close(udpfd); - udpfd = -1; - return -1; + default: + assert(0 && "Unrecognized socket event in client_sock_callback()"); + break; } - on = 0; - setsockopt(udpfd, SOL_SOCKET, SO_BROADCAST, (char *)&on, sizeof(on)); - if (bind(udpfd, (struct sockaddr *)&from, sizeof(from)) == -1) - { -#ifdef USE_SYSLOG - syslog(LOG_ERR, "bind udp.%d fd %d : %m", from.sin_port, udpfd); -#endif - Debug((DEBUG_ERROR, "bind : %s", strerror(errno))); - close(udpfd); - udpfd = -1; - return -1; - } - if (fcntl(udpfd, F_SETFL, FNDELAY) == -1) - { - Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno))); - close(udpfd); - udpfd = -1; - return -1; + + assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); + + if (fallback) { + const char* msg = (cli_error(cptr)) ? strerror(cli_error(cptr)) : fallback; + if (!msg) + msg = "Unknown error"; + exit_client_msg(cptr, cptr, &me, fmt, msg); } - return udpfd; } -/* - * max # of pings set to 15/sec. +/** Process a timer on client socket. + * @param ev Timer event that has a struct Connection as its + * associated data. */ -static void polludp(void) +static void client_timer_callback(struct Event* ev) { - Reg1 char *s; - struct sockaddr_in from; - int n; - size_t fromlen = sizeof(from); - static time_t last = 0; - static int cnt = 0, mlen = 0; - - /* - * find max length of data area of packet. - */ - if (!mlen) - { - mlen = sizeof(readbuf) - strlen(me.name) - strlen(version); - mlen -= 6; - if (mlen < 0) - mlen = 0; - } - Debug((DEBUG_DEBUG, "udp poll")); + struct Client* cptr; + struct Connection* con; - n = recvfrom(udpfd, readbuf, mlen, 0, (struct sockaddr *)&from, &fromlen); - if (now == last) - if (++cnt > 14) - return; - cnt = 0; - last = now; + assert(0 != ev_timer(ev)); + assert(0 != t_data(ev_timer(ev))); + assert(ET_DESTROY == ev_type(ev) || ET_EXPIRE == ev_type(ev)); - if (n == -1) - { - if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) - return; - else - { - report_error("udp port recvfrom (%s): %s", &me); - return; - } - } - ircstp->is_udp++; - if (n < 19) - return; + con = (struct Connection*) t_data(ev_timer(ev)); - s = readbuf + n; - /* - * attach my name and version for the reply - */ - *readbuf |= 1; - strcpy(s, me.name); - s += strlen(s) + 1; - strcpy(s, version); - s += strlen(s); - sendto(udpfd, readbuf, s - readbuf, 0, - (struct sockaddr *)&from, sizeof(from)); - return; -} + assert(0 != con_client(con) || ev_type(ev) == ET_DESTROY); -/* - * do_dns_async - * - * Called when the fd returned from init_resolver() has been selected for - * reading. - */ -static void do_dns_async(void) -{ - static Link ln; - aClient *cptr; - aConfItem *aconf; - struct hostent *hp; + cptr = con_client(con); - ln.flags = ASYNC_NONE; - hp = get_res((char *)&ln); + assert(0 == cptr || con == cli_connect(cptr)); - Debug((DEBUG_DNS, "%p = get_res(%d,%p)", hp, ln.flags, ln.value.cptr)); + if (ev_type(ev)== ET_DESTROY) { + con_freeflag(con) &= ~FREEFLAG_TIMER; /* timer has expired... */ - switch (ln.flags) - { - case ASYNC_NONE: - /* - * No reply was processed that was outstanding or had a client - * still waiting. - */ - break; - case ASYNC_CLIENT: - if ((cptr = ln.value.cptr)) - { - del_queries((char *)cptr); - ClearDNS(cptr); - if (!DoingAuth(cptr)) - SetAccess(cptr); - cptr->hostp = hp; - } - break; - case ASYNC_CONNECT: - aconf = ln.value.aconf; - if (hp && aconf) - { - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); - connect_server(aconf, NULL, hp); - } - else - sendto_ops("Connect to %s failed: host lookup", - (aconf) ? aconf->host : "unknown"); - break; - case ASYNC_PING: - cptr = ln.value.cptr; - del_queries((char *)cptr); - if (hp) - { - memcpy(&cptr->ip, hp->h_addr, sizeof(struct in_addr)); - if (ping_server(cptr) == -1) - end_ping(cptr); - } - else - { - sendto_ops("Udp ping to %s failed: host lookup", cptr->sockhost); - end_ping(cptr); - } - break; - case ASYNC_CONF: - aconf = ln.value.aconf; - if (hp && aconf) - memcpy(&aconf->ipnum, hp->h_addr, sizeof(struct in_addr)); - break; - default: - break; + if (!con_freeflag(con) && !cptr) + free_connection(con); /* client is being destroyed */ + } else { + Debug((DEBUG_LIST, "Client process timer for %C expired; processing", + cptr)); + read_packet(cptr, 0); /* read_packet will re-add timer if needed */ } + + assert(0 == cptr || 0 == cli_connect(cptr) || con == cli_connect(cptr)); }