From ae9fc63bfd1e0f811352dfb02ba234915b901cb9 Mon Sep 17 00:00:00 2001 From: Valery Yatsko Date: Wed, 2 Apr 2008 06:25:08 +0400 Subject: [PATCH] Trying to migrate on ratbox3's listener. --- include/client.h | 2 +- include/listener.h | 41 +- src/listener.c | 1049 +++++++++++++++++++++----------------------- src/newconf.c | 6 +- 4 files changed, 535 insertions(+), 563 deletions(-) diff --git a/include/client.h b/include/client.h index 943098e..81a0800 100644 --- a/include/client.h +++ b/include/client.h @@ -609,7 +609,7 @@ extern void close_connection(struct Client *); extern void init_uid(void); extern char *generate_uid(void); -void allocate_away(struct Client *); +void allocate_away(struct Client *); void free_away(struct Client *); #endif /* INCLUDED_client_h */ diff --git a/include/listener.h b/include/listener.h index 9bab533..da8e951 100644 --- a/include/listener.h +++ b/include/listener.h @@ -29,26 +29,25 @@ #include "ircd_defs.h" -struct Client; - -struct Listener -{ - struct Listener *next; /* list node pointer */ - const char *name; /* listener name */ - int fd; /* file descriptor */ - int ref_count; /* number of connection references */ - int active; /* current state of listener */ - int index; /* index into poll array */ - struct irc_sockaddr_storage addr; - struct DNSQuery *dns_query; - char vhost[HOSTLEN + 1]; /* virtual name of listener */ -}; - -extern void add_listener(int port, const char *vaddr_ip, int family); -extern void close_listener(struct Listener *listener); -extern void close_listeners(void); -extern const char *get_listener_name(const struct Listener *listener); -extern void show_ports(struct Client *client); -extern void free_listener(struct Listener *); +struct Client; + +struct Listener +{ + rb_dlink_node node; + const char *name; /* listener name */ + rb_fde_t *F; /* file descriptor */ + int ref_count; /* number of connection references */ + int active; /* current state of listener */ + int ssl; /* ssl listener */ + struct rb_sockaddr_storage addr; + char vhost[HOSTLEN + 1]; /* virtual name of listener */ +}; + +void add_listener(int port, const char *vaddr_ip, int family, int ssl); +void close_listener(struct Listener *listener); +void close_listeners(void); +const char *get_listener_name(struct Listener *listener); +void show_ports(struct Client *client); +void free_listener(struct Listener *); #endif /* INCLUDED_listener_h */ diff --git a/src/listener.c b/src/listener.c index e8647cf..db14a35 100644 --- a/src/listener.c +++ b/src/listener.c @@ -42,541 +42,514 @@ #include "s_conf.h" #include "hostmask.h" -#ifndef INADDR_NONE -#define INADDR_NONE ((unsigned int) 0xffffffff) -#endif - -#if defined(NO_IN6ADDR_ANY) && defined(IPV6) -static const struct in6_addr in6addr_any = -{ { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }; -#endif - -static PF accept_connection; - -static listener_t *ListenerPollList = NULL; - -static listener_t * -make_listener(struct irc_sockaddr_storage *addr) -{ - listener_t *listener = (listener_t *) rb_malloc(sizeof(listener_t)); - s_assert(0 != listener); - - listener->name = me.name; - listener->fd = -1; - memcpy(&listener->addr, addr, sizeof(struct irc_sockaddr_storage)); - listener->next = NULL; - return listener; -} - -void -free_listener(listener_t *listener) -{ - s_assert(NULL != listener); - if(listener == NULL) - return; - /* - * remove from listener list - */ - if(listener == ListenerPollList) - ListenerPollList = listener->next; - else - { - listener_t *prev = ListenerPollList; - for (; prev; prev = prev->next) - { - if(listener == prev->next) - { - prev->next = listener->next; - break; - } - } - } - - /* free */ - rb_free(listener); -} - -#define PORTNAMELEN 6 /* ":31337" */ - -/* - * get_listener_name - return displayable listener name and port - * returns "host.foo.org:6667" for a given listener - */ -const char * -get_listener_name(const listener_t *listener) -{ - static char buf[HOSTLEN + HOSTLEN + PORTNAMELEN + 4]; - int port = 0; - - s_assert(NULL != listener); - if(listener == NULL) - return NULL; - -#ifdef IPV6 - if(listener->addr.ss_family == AF_INET6) - port = ntohs(((const struct sockaddr_in6 *)&listener->addr)->sin6_port); - else -#endif - port = ntohs(((const struct sockaddr_in *)&listener->addr)->sin_port); - - rb_snprintf(buf, sizeof(buf), "%s[%s/%u]", me.name, listener->name, port); - return buf; -} - -/* - * show_ports - send port listing to a client - * inputs - pointer to client to show ports to - * output - none - * side effects - show ports - */ -void -show_ports(struct Client *source_p) -{ - listener_t *listener = 0; - - for (listener = ListenerPollList; listener; listener = listener->next) - { - sendto_one_numeric(source_p, RPL_STATSPLINE, - form_str(RPL_STATSPLINE), 'P', -#ifdef IPV6 - ntohs(listener->addr.ss_family == AF_INET ? ((struct sockaddr_in *)&listener->addr)->sin_port : - ((struct sockaddr_in6 *)&listener->addr)->sin6_port), -#else - ntohs(((struct sockaddr_in *)&listener->addr)->sin_port), -#endif - IsOperAdmin(source_p) ? listener->name : me.name, - listener->ref_count, (listener->active) ? "active" : "disabled"); - } -} - -/* - * inetport - create a listener socket in the AF_INET or AF_INET6 domain, - * bind it to the port given in 'port' and listen to it - * returns true (1) if successful false (0) on error. - * - * If the operating system has a define for SOMAXCONN, use it, otherwise - * use RATBOX_SOMAXCONN - */ -#ifdef SOMAXCONN -#undef RATBOX_SOMAXCONN -#define RATBOX_SOMAXCONN SOMAXCONN -#endif - -static int -inetport(listener_t *listener) -{ - int fd; - int opt = 1; - - /* - * At first, open a new socket - */ - - fd = rb_socket(listener->addr.ss_family, SOCK_STREAM, 0, "Listener socket"); - -#ifdef IPV6 - if(listener->addr.ss_family == AF_INET6) - { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&listener->addr; - if(!IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &in6addr_any)) - { - inetntop(AF_INET6, &in6->sin6_addr, listener->vhost, sizeof(listener->vhost)); - listener->name = listener->vhost; - } - } else -#endif - { - struct sockaddr_in *in = (struct sockaddr_in *)&listener->addr; - if(in->sin_addr.s_addr != INADDR_ANY) - { - inetntop(AF_INET, &in->sin_addr, listener->vhost, sizeof(listener->vhost)); - listener->name = listener->vhost; - } - } - - /* - * At one point, we enforced a strange arbitrary limit here. - * We no longer do this, and just check if the fd is valid or not. - * -nenolod - */ - if(fd == -1) - { - report_error("opening listener socket %s:%s", - get_listener_name(listener), - get_listener_name(listener), errno); - return 0; - } - - /* - * XXX - we don't want to do all this crap for a listener - * set_sock_opts(listener); - */ - if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt))) - { - report_error("setting SO_REUSEADDR for listener %s:%s", - get_listener_name(listener), - get_listener_name(listener), errno); - rb_close(fd); - return 0; - } - - /* - * 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(bind(fd, (struct sockaddr *) &listener->addr, GET_SS_LEN(listener->addr))) - { - report_error("binding listener socket %s:%s", - get_listener_name(listener), - get_listener_name(listener), errno); - rb_close(fd); - return 0; - } - - if(listen(fd, RATBOX_SOMAXCONN)) - { - report_error("listen failed for %s:%s", - get_listener_name(listener), - get_listener_name(listener), errno); - rb_close(fd); - return 0; - } - - listener->fd = fd; - - /* Listen completion events are READ events .. */ - - accept_connection(fd, listener); - return 1; -} - -static listener_t * -find_listener(struct irc_sockaddr_storage *addr) -{ - listener_t *listener = NULL; - listener_t *last_closed = NULL; - - for (listener = ListenerPollList; listener; listener = listener->next) - { - if(addr->ss_family != listener->addr.ss_family) - continue; - - switch(addr->ss_family) - { - case AF_INET: - { - struct sockaddr_in *in4 = (struct sockaddr_in *)addr; - struct sockaddr_in *lin4 = (struct sockaddr_in *)&listener->addr; - if(in4->sin_addr.s_addr == lin4->sin_addr.s_addr && - in4->sin_port == lin4->sin_port ) - { - if(listener->fd == -1) - last_closed = listener; - else - return(listener); - } - break; - } -#ifdef IPV6 - case AF_INET6: - { - struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; - struct sockaddr_in6 *lin6 =(struct sockaddr_in6 *)&listener->addr; - if(IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &lin6->sin6_addr) && - in6->sin6_port == lin6->sin6_port) - { - if(listener->fd == -1) - last_closed = listener; - else - return(listener); - } - break; - - } -#endif - - default: - break; - } - } - return last_closed; -} - - -/* - * add_listener- create a new listener - * port - the port number to listen on - * vhost_ip - if non-null must contain a valid IP address string in - * the format "255.255.255.255" - */ -void -add_listener(int port, const char *vhost_ip, int family) -{ - listener_t *listener; - struct irc_sockaddr_storage vaddr; - - /* - * if no port in conf line, don't bother - */ - if(port == 0) - return; - memset(&vaddr, 0, sizeof(vaddr)); - vaddr.ss_family = family; - - if(vhost_ip != NULL) - { - if(family == AF_INET) - { - if(inetpton(family, vhost_ip, &((struct sockaddr_in *)&vaddr)->sin_addr) <= 0) - return; - } -#ifdef IPV6 - else - { - if(inetpton(family, vhost_ip, &((struct sockaddr_in6 *)&vaddr)->sin6_addr) <= 0) - return; - - } -#endif - } else - { - switch(family) - { - case AF_INET: - ((struct sockaddr_in *)&vaddr)->sin_addr.s_addr = INADDR_ANY; - break; -#ifdef IPV6 - case AF_INET6: - memcpy(&((struct sockaddr_in6 *)&vaddr)->sin6_addr, &in6addr_any, sizeof(struct in6_addr)); - break; - default: - return; -#endif - } - } - switch(family) - { - case AF_INET: - SET_SS_LEN(vaddr, sizeof(struct sockaddr_in)); - ((struct sockaddr_in *)&vaddr)->sin_port = htons(port); - break; -#ifdef IPV6 - case AF_INET6: - SET_SS_LEN(vaddr, sizeof(struct sockaddr_in6)); - ((struct sockaddr_in6 *)&vaddr)->sin6_port = htons(port); - break; -#endif - default: - break; - } - if((listener = find_listener(&vaddr))) - { - if(listener->fd > -1) - return; - } - else - { - listener = make_listener(&vaddr); - listener->next = ListenerPollList; - ListenerPollList = listener; - } - - listener->fd = -1; - - if(inetport(listener)) - listener->active = 1; - else - close_listener(listener); -} - -/* - * close_listener - close a single listener - */ -void -close_listener(listener_t *listener) -{ - s_assert(listener != NULL); - if(listener == NULL) - return; - if(listener->fd >= 0) - { - rb_close(listener->fd); - listener->fd = -1; - } - - listener->active = 0; - - if(listener->ref_count) - return; - - free_listener(listener); -} - -/* - * close_listeners - close and free all listeners that are not being used - */ -void -close_listeners() -{ - listener_t *listener; - listener_t *listener_next = 0; - /* - * close all 'extra' listening ports we have - */ - for (listener = ListenerPollList; listener; listener = listener_next) - { - listener_next = listener->next; - close_listener(listener); - } -} - -#define DLINE_WARNING "ERROR :You have been D-lined.\r\n" - -/* - * add_connection - 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 sent to the auth module for verification, and not put in - * any client list yet. - */ -static void -add_connection(listener_t *listener, int fd, struct sockaddr *sai, int exempt) -{ - struct Client *new_client; - s_assert(NULL != listener); - - /* - * get the client socket name from the socket - * the client has already been checked out in accept_connection - */ - new_client = make_client(NULL); - - memcpy(&new_client->localClient->ip, sai, sizeof(struct irc_sockaddr_storage)); - - /* - * copy address to 'sockhost' as a string, copy it to host too - * so we have something valid to put into error messages... - */ - inetntop_sock((struct sockaddr *)&new_client->localClient->ip, new_client->sockhost, - sizeof(new_client->sockhost)); - - - strlcpy(new_client->host, new_client->sockhost, sizeof(new_client->host)); - - new_client->localClient->F = rb_add_fd(fd); - - new_client->localClient->listener = listener; - ++listener->ref_count; - - if(!exempt) - { - if(check_reject(new_client)) - return; - if(add_unknown_ip(new_client)) - return; - } - - start_auth(new_client); -} - - -static void -accept_connection(int pfd, void *data) -{ - static time_t last_oper_notice = 0; - struct irc_sockaddr_storage sai; - socklen_t addrlen = sizeof(sai); - int fd; - listener_t *listener = data; - struct ConfItem *aconf; - char buf[BUFSIZE]; - - s_assert(listener != NULL); - if(listener == NULL) - return; - /* - * 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. - */ - - fd = rb_accept(listener->fd, (struct sockaddr *)&sai, &addrlen); - if(fd < 0) - { - /* Re-register a new IO request for the next accept .. */ - rb_setselect(listener->fd, FDLIST_SERVICE, - COMM_SELECT_READ, accept_connection, listener, 0); - return; - } - - /* This needs to be done here, otherwise we break dlines */ - mangle_mapped_sockaddr((struct sockaddr *)&sai); - - /* - * check for connection limit - * TBD: this is stupid... either we have a socket or we don't. -nenolod - */ - if((rb_get_maxconnections() - 10) < fd) - { - ++ServerStats->is_ref; - /* - * slow down the whining to opers bit - */ - if((last_oper_notice + 20) <= rb_current_time()) - { - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "All connections in use. (%s)", - get_listener_name(listener)); - last_oper_notice = rb_current_time(); - } - - write(fd, "ERROR :All connections in use\r\n", 32); - rb_close(fd); - /* Re-register a new IO request for the next accept .. */ - rb_setselect(listener->fd, FDLIST_SERVICE, - COMM_SELECT_READ, accept_connection, listener, 0); - return; - } - - /* Do an initial check we aren't connecting too fast or with too many - * from this IP... */ - aconf = find_dline((struct sockaddr *) &sai, sai.ss_family); - /* check it wasn't an exempt */ - if (aconf != NULL && (aconf->status & CONF_EXEMPTDLINE) == 0) - { - ServerStats->is_ref++; - - if(ConfigFileEntry.dline_with_reason) - { - if (rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", aconf->passwd) >= (sizeof(buf)-1)) - { - buf[sizeof(buf) - 3] = '\r'; - buf[sizeof(buf) - 2] = '\n'; - buf[sizeof(buf) - 1] = '\0'; - } - } - else - rb_sprintf(buf, "ERROR :You have been D-lined.\r\n"); - - write(fd, buf, strlen(buf)); - rb_close(fd); - - /* Re-register a new IO request for the next accept .. */ - rb_setselect(listener->fd, FDLIST_SERVICE, - COMM_SELECT_READ, accept_connection, listener, 0); - return; - } - - ServerStats->is_ac++; - add_connection(listener, fd, (struct sockaddr *)&sai, aconf ? 1 : 0); - - /* Re-register a new IO request for the next accept .. */ - rb_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, - accept_connection, listener, 0); -} +static rb_dlink_list listener_list; +static int accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, void *data); +static void accept_callback(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t addrlen, void *data); + +static struct Listener * +make_listener(struct rb_sockaddr_storage *addr) +{ + struct Listener *listener = rb_malloc(sizeof(struct Listener)); + s_assert(0 != listener); + listener->name = ServerInfo.name; /* me.name may not be valid yet -- jilles */ + listener->F = NULL; + memcpy(&listener->addr, addr, sizeof(struct rb_sockaddr_storage)); + return listener; +} + +void +free_listener(struct Listener *listener) +{ + s_assert(NULL != listener); + if(listener == NULL) + return; + + rb_dlinkDelete(&listener->node, &listener_list); + rb_free(listener); +} + +#define PORTNAMELEN 6 /* ":31337" */ + +/* + * get_listener_name - return displayable listener name and port + * returns "host.foo.org:6667" for a given listener + */ +const char * +get_listener_name(struct Listener *listener) +{ + static char buf[HOSTLEN + HOSTLEN + PORTNAMELEN + 4]; + int port = 0; + + s_assert(NULL != listener); + if(listener == NULL) + return NULL; + +#ifdef IPV6 + if(GET_SS_FAMILY(&listener->addr) == AF_INET6) + port = ntohs(((const struct sockaddr_in6 *)&listener->addr)->sin6_port); + else +#endif + port = ntohs(((const struct sockaddr_in *)&listener->addr)->sin_port); + + rb_snprintf(buf, sizeof(buf), "%s[%s/%u]", me.name, listener->name, port); + return buf; +} + +/* + * show_ports - send port listing to a client + * inputs - pointer to client to show ports to + * output - none + * side effects - show ports + */ +void +show_ports(struct Client *source_p) +{ + struct Listener *listener; + rb_dlink_node *ptr; + + RB_DLINK_FOREACH(ptr, listener_list.head) + { + listener = ptr->data; + sendto_one_numeric(source_p, RPL_STATSPLINE, + form_str(RPL_STATSPLINE), 'P', +#ifdef IPV6 + ntohs(GET_SS_FAMILY(&listener->addr) == AF_INET ? ((struct sockaddr_in *)&listener->addr)->sin_port : + ((struct sockaddr_in6 *)&listener->addr)->sin6_port), +#else + ntohs(((struct sockaddr_in *)&listener->addr)->sin_port), +#endif + IsOperAdmin(source_p) ? listener->name : me.name, + listener->ref_count, (listener->active) ? "active" : "disabled", + listener->ssl ? " ssl" : ""); + } +} + +/* + * inetport - create a listener socket in the AF_INET or AF_INET6 domain, + * bind it to the port given in 'port' and listen to it + * returns true (1) if successful false (0) on error. + * + * If the operating system has a define for SOMAXCONN, use it, otherwise + * use RATBOX_SOMAXCONN + */ +#ifdef SOMAXCONN +#undef RATBOX_SOMAXCONN +#define RATBOX_SOMAXCONN SOMAXCONN +#endif + +static int +inetport(struct Listener *listener) +{ + rb_fde_t *F; + int ret; + int opt = 1; + + /* + * At first, open a new socket + */ + + F = rb_socket(GET_SS_FAMILY(&listener->addr), SOCK_STREAM, 0, "Listener socket"); + +#ifdef IPV6 + if(GET_SS_FAMILY(&listener->addr) == AF_INET6) + { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&listener->addr; + if(!IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &in6addr_any)) + { + rb_inet_ntop(AF_INET6, &in6->sin6_addr, listener->vhost, sizeof(listener->vhost)); + listener->name = listener->vhost; + } + } else +#endif + { + struct sockaddr_in *in = (struct sockaddr_in *)&listener->addr; + if(in->sin_addr.s_addr != INADDR_ANY) + { + rb_inet_ntop(AF_INET, &in->sin_addr, listener->vhost, sizeof(listener->vhost)); + listener->name = listener->vhost; + } + } + + + if(F == NULL) + { + report_error("opening listener socket %s:%s", + get_listener_name(listener), + get_listener_name(listener), errno); + return 0; + } + else if((maxconnections - 10) < rb_get_fd(F)) /* XXX this is kinda bogus*/ + { + report_error("no more connections left for listener %s:%s", + get_listener_name(listener), + get_listener_name(listener), errno); + rb_close(F); + return 0; + } + /* + * XXX - we don't want to do all this crap for a listener + * set_sock_opts(listener); + */ + if(setsockopt(rb_get_fd(F), SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt))) + { + report_error("setting SO_REUSEADDR for listener %s:%s", + get_listener_name(listener), + get_listener_name(listener), errno); + rb_close(F); + return 0; + } + + /* + * 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(bind(rb_get_fd(F), (struct sockaddr *) &listener->addr, GET_SS_LEN(&listener->addr))) + { + report_error("binding listener socket %s:%s", + get_listener_name(listener), + get_listener_name(listener), errno); + rb_close(F); + return 0; + } + + if((ret = rb_listen(F, RATBOX_SOMAXCONN))) + { + report_error("listen failed for %s:%s", + get_listener_name(listener), + get_listener_name(listener), errno); + rb_close(F); + return 0; + } + + listener->F = F; + + rb_accept_tcp(listener->F, accept_precallback, accept_callback, listener); + return 1; +} + +static struct Listener * +find_listener(struct rb_sockaddr_storage *addr) +{ + struct Listener *listener = NULL; + struct Listener *last_closed = NULL; + rb_dlink_node *ptr; + + RB_DLINK_FOREACH(ptr, listener_list.head) + { + listener = ptr->data; + if(GET_SS_FAMILY(addr) != GET_SS_FAMILY(&listener->addr)) + continue; + + switch(GET_SS_FAMILY(addr)) + { + case AF_INET: + { + struct sockaddr_in *in4 = (struct sockaddr_in *)addr; + struct sockaddr_in *lin4 = (struct sockaddr_in *)&listener->addr; + if(in4->sin_addr.s_addr == lin4->sin_addr.s_addr && + in4->sin_port == lin4->sin_port ) + { + if(listener->F == NULL) + last_closed = listener; + else + return(listener); + } + break; + } +#ifdef IPV6 + case AF_INET6: + { + struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr; + struct sockaddr_in6 *lin6 =(struct sockaddr_in6 *)&listener->addr; + if(IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &lin6->sin6_addr) && + in6->sin6_port == lin6->sin6_port) + { + if(listener->F == NULL) + last_closed = listener; + else + return(listener); + } + break; + + } +#endif + + default: + break; + } + } + return last_closed; +} + +/* + * add_listener- create a new listener + * port - the port number to listen on + * vhost_ip - if non-null must contain a valid IP address string in + * the format "255.255.255.255" + */ +void +add_listener(int port, const char *vhost_ip, int family, int ssl) +{ + struct Listener *listener; + struct rb_sockaddr_storage vaddr; + + /* + * if no port in conf line, don't bother + */ + if(port == 0) + return; + + memset(&vaddr, 0, sizeof(vaddr)); + GET_SS_FAMILY(&vaddr) = family; + + if(vhost_ip != NULL) + { + if(rb_inet_pton_sock(vhost_ip, (struct sockaddr *)&vaddr) <= 0) + return; + } else + { + switch(family) + { + case AF_INET: + ((struct sockaddr_in *)&vaddr)->sin_addr.s_addr = INADDR_ANY; + break; +#ifdef IPV6 + case AF_INET6: + memcpy(&((struct sockaddr_in6 *)&vaddr)->sin6_addr, &in6addr_any, sizeof(struct in6_addr)); + break; +#endif + default: + return; + } + } + switch(family) + { + case AF_INET: + SET_SS_LEN(&vaddr, sizeof(struct sockaddr_in)); + ((struct sockaddr_in *)&vaddr)->sin_port = htons(port); + break; +#ifdef IPV6 + case AF_INET6: + SET_SS_LEN(&vaddr, sizeof(struct sockaddr_in6)); + ((struct sockaddr_in6 *)&vaddr)->sin6_port = htons(port); + break; +#endif + default: + break; + } + if((listener = find_listener(&vaddr))) + { + if(listener->F != NULL) + return; + } + else + { + listener = make_listener(&vaddr); + rb_dlinkAdd(listener, &listener->node, &listener_list); + } + + listener->F = NULL; + listener->ssl = ssl; + if(inetport(listener)) + listener->active = 1; + else + close_listener(listener); +} + +/* + * close_listener - close a single listener + */ +void +close_listener(struct Listener *listener) +{ + s_assert(listener != NULL); + if(listener == NULL) + return; + if(listener->F != NULL) + { + rb_close(listener->F); + listener->F = NULL; + } + + listener->active = 0; + + if(listener->ref_count) + return; + + free_listener(listener); +} + +/* + * close_listeners - close and free all listeners that are not being used + */ +void +close_listeners() +{ + struct Listener *listener; + rb_dlink_node *ptr, *next; + + RB_DLINK_FOREACH_SAFE(ptr, next, listener_list.head) + { + listener = ptr->data; + close_listener(listener); + } +} + +/* + * add_connection - 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 sent to the auth module for verification, and not put in + * any client list yet. + */ +static void +add_connection(struct Listener *listener, rb_fde_t *F, struct sockaddr *sai, struct sockaddr *lai, void *ssl_ctl) +{ + struct Client *new_client; + s_assert(NULL != listener); + /* + * get the client socket name from the socket + * the client has already been checked out in accept_connection + */ + new_client = make_client(NULL); + + memcpy(&new_client->localClient->ip, sai, sizeof(struct rb_sockaddr_storage)); + new_client->localClient->lip = rb_malloc(sizeof(struct rb_sockaddr_storage)); + memcpy(new_client->localClient->lip, lai, sizeof(struct rb_sockaddr_storage)); + + /* + * copy address to 'sockhost' as a string, copy it to host too + * so we have something valid to put into error messages... + */ + rb_inet_ntop_sock((struct sockaddr *)&new_client->localClient->ip, new_client->sockhost, + sizeof(new_client->sockhost)); + + + rb_strlcpy(new_client->host, new_client->sockhost, sizeof(new_client->host)); + +#ifdef IPV6 + if(GET_SS_FAMILY(&new_client->localClient->ip) == AF_INET6 && ConfigFileEntry.dot_in_ip6_addr == 1) + { + rb_strlcat(new_client->host, ".", sizeof(new_client->host)); + } +#endif + + new_client->localClient->F = F; + add_to_cli_fd_hash(new_client); + new_client->localClient->listener = listener; + new_client->localClient->ssl_ctl = ssl_ctl; + if(ssl_ctl != NULL || rb_fd_ssl(F)) + SetSSL(new_client); + + ++listener->ref_count; + + start_auth(new_client); +} + +static time_t last_oper_notice = 0; + +static const char *toofast = "ERROR :Reconnecting too fast, throttled.\r\n"; + +static int +accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, void *data) +{ + struct Listener *listener = (struct Listener *)data; + char buf[BUFSIZE]; + struct ConfItem *aconf; + + if(listener->ssl && (!ssl_ok || !get_ssld_count())) + { + fprintf(stderr, "closed socket\n"); + rb_close(F); + return 0; + } + + if((maxconnections - 10) < rb_get_fd(F)) /* XXX this is kinda bogus */ + { + ++ServerStats.is_ref; + /* + * slow down the whining to opers bit + */ + if((last_oper_notice + 20) <= rb_current_time()) + { + sendto_realops_flags(UMODE_ALL, L_ALL, + "All connections in use. (%s)", + get_listener_name(listener)); + last_oper_notice = rb_current_time(); + } + + rb_write(F, "ERROR :All connections in use\r\n", 32); + rb_close(F); + /* Re-register a new IO request for the next accept .. */ + return 0; + } + + aconf = find_dline(addr); + if(aconf != NULL && (aconf->status & CONF_EXEMPTDLINE)) + return 1; + + /* Do an initial check we aren't connecting too fast or with too many + * from this IP... */ + if(aconf != NULL) + { + ServerStats.is_ref++; + + if(ConfigFileEntry.dline_with_reason) + { + if (rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", aconf->passwd) >= (int)(sizeof(buf)-1)) + { + buf[sizeof(buf) - 3] = '\r'; + buf[sizeof(buf) - 2] = '\n'; + buf[sizeof(buf) - 1] = '\0'; + } + } + else + strcpy(buf, "ERROR :You have been D-lined.\r\n"); + + rb_write(F, buf, strlen(buf)); + rb_close(F); + return 0; + } + + if(check_reject(F, addr)) + return 0; + + if(throttle_add(addr)) + { + rb_write(F, toofast, strlen(toofast)); + rb_close(F); + return 0; + } + + return 1; +} + +static void +accept_ssld(rb_fde_t *F, struct sockaddr *addr, struct sockaddr *laddr, struct Listener *listener) +{ + ssl_ctl_t *ctl; + rb_fde_t *xF[2]; + rb_socketpair(AF_UNIX, SOCK_STREAM, 0, &xF[0], &xF[1], "Incoming ssld Connection"); + ctl = start_ssld_accept(F, xF[1], rb_get_fd(xF[0])); /* this will close F for us */ + add_connection(listener, xF[0], addr, laddr, ctl); +} + +static void +accept_callback(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t addrlen, void *data) +{ + struct Listener *listener = data; + struct rb_sockaddr_storage lip; + unsigned int locallen = sizeof(struct rb_sockaddr_storage); + + ServerStats.is_ac++; + if(getsockname(rb_get_fd(F), (struct sockaddr *) &lip, &locallen) < 0) + { + /* this shouldn't fail so... */ + /* XXX add logging of this */ + rb_close(F); + } + if(listener->ssl) + accept_ssld(F, addr, (struct sockaddr *)&lip, listener); + else + add_connection(listener, F, addr, (struct sockaddr *)&lip, NULL); +} diff --git a/src/newconf.c b/src/newconf.c index e36882f..4125e21 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -758,9 +758,9 @@ conf_set_listen_port(void *data) } if(listener_address == NULL) { - add_listener(args->v.number, listener_address, AF_INET); + add_listener(args->v.number, listener_address, AF_INET, 0); #ifdef IPV6 - add_listener(args->v.number, listener_address, AF_INET6); + add_listener(args->v.number, listener_address, AF_INET6, 0); #endif } else @@ -773,7 +773,7 @@ conf_set_listen_port(void *data) #endif family = AF_INET; - add_listener(args->v.number, listener_address, family); + add_listener(args->v.number, listener_address, family, 0); }