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; static time_t last_oper_notice = 0; int len; if(listener->ssl && (!ssl_ok || !get_ssld_count())) { 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_snomask(SNO_GENERAL, 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, addr->sa_family); 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) { len = rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", get_user_ban_reason(aconf)); if (len >= (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_connection(int pfd, void *data) { static time_t last_oper_notice = 0; struct irc_sockaddr_storage sai; socklen_t addrlen = sizeof(sai); int fd; struct Listener *listener = data; struct ConfItem *aconf; char buf[BUFSIZE]; s_assert(listener != NULL); if(listener == NULL) return; for(;;) /* loop until something breaks us out */ { /* * 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 = comm_accept(listener->fd, (struct sockaddr *) &sai, &addrlen); /* This needs to be done here, otherwise we break dlines */ mangle_mapped_sockaddr((struct sockaddr *) &sai); if(fd < 0) { /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener); return; } /* * check for connection limit */ if((maxconnections - 10) < fd) { ++ServerStats->is_ref; /* * slow down the whining to opers bit */ if((last_oper_notice + 20) <= CurrentTime) { sendto_realops_flags(UMODE_ALL, L_ALL, "All connections in use. (%s)", get_listener_name(listener)); last_oper_notice = CurrentTime; } write(fd, "ERROR :All connections in use\r\n", 32); comm_close(fd); /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener); return; } /* Do an initial check we aren't connecting too fast or with too many * from this IP... */ if((aconf = conf_connect_allowed((struct sockaddr *) &sai, sai.ss_family)) != NULL) { ServerStats->is_ref++; if(ConfigFileEntry.dline_with_reason) { if(ircsnprintf (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 ircsprintf(buf, "ERROR :You have been D-lined.\r\n"); write(fd, buf, strlen(buf)); comm_close(fd); /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener); return; } ServerStats->is_ac++; add_connection(listener, fd, (struct sockaddr *) &sai); } /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener); }
static int inetport(struct Listener *listener) { rb_fde_t *F; int opt = 1; const char *errstr; /* * At first, open a new socket */ F = rb_socket(GET_SS_FAMILY(&listener->addr), SOCK_STREAM, 0, "Listener socket"); #ifdef RB_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)) { 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) { sendto_realops_snomask(SNO_GENERAL, L_ALL, "Cannot open socket for listener on port %d", get_listener_port(listener)); ilog(L_MAIN, "Cannot open socket for listener %s", get_listener_name(listener)); return 0; } else if((maxconnections - 10) < rb_get_fd(F)) /* XXX this is kinda bogus*/ { ilog_error("no more connections left for listener"); sendto_realops_snomask(SNO_GENERAL, L_ALL, "No more connections left for listener on port %d", get_listener_port(listener)); ilog(L_MAIN, "No more connections left for listener %s", get_listener_name(listener)); 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))) { errstr = strerror(rb_get_sockerr(F)); sendto_realops_snomask(SNO_GENERAL, L_ALL, "Cannot set SO_REUSEADDR for listener on port %d: %s", get_listener_port(listener), errstr); ilog(L_MAIN, "Cannot set SO_REUSEADDR for listener %s: %s", get_listener_name(listener), errstr); 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))) { errstr = strerror(rb_get_sockerr(F)); sendto_realops_snomask(SNO_GENERAL, L_ALL, "Cannot bind for listener on port %d: %s", get_listener_port(listener), errstr); ilog(L_MAIN, "Cannot bind for listener %s: %s", get_listener_name(listener), errstr); rb_close(F); return 0; } if(rb_listen(F, CHARYBDIS_SOMAXCONN, listener->defer_accept)) { errstr = strerror(rb_get_sockerr(F)); sendto_realops_snomask(SNO_GENERAL, L_ALL, "Cannot listen() for listener on port %d: %s", get_listener_port(listener), errstr); ilog(L_MAIN, "Cannot listen() for listener %s: %s", get_listener_name(listener), errstr); rb_close(F); return 0; } listener->F = F; rb_accept_tcp(listener->F, accept_precallback, accept_callback, listener); return 1; }
static int inetport(struct Listener *listener) { int fd; int opt = 1; /* * At first, open a new socket */ fd = comm_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; } } if(fd == -1) { report_error("opening listener socket %s:%s", get_listener_name(listener), get_listener_name(listener), errno); return 0; } else if((maxconnections - 10) < fd) { report_error("no more connections left for listener %s:%s", get_listener_name(listener), get_listener_name(listener), errno); comm_close(fd); 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); comm_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); comm_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); comm_close(fd); return 0; } listener->fd = fd; /* Listen completion events are READ events .. */ accept_connection(fd, listener); return 1; }
static void accept_connection(int pfd, void *data) { static time_t last_oper_notice = 0; struct irc_sockaddr sai; struct irc_inaddr addr; int fd; int pe; struct Listener * listener = data; 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 = comm_accept(listener->fd, &sai); copy_s_addr(IN_ADDR(addr), S_ADDR(sai)); #ifdef IPV6 if((IN6_IS_ADDR_V4MAPPED(&IN_ADDR2(addr))) || (IN6_IS_ADDR_V4COMPAT(&IN_ADDR2(addr)))) { memmove(&addr.sins.sin.s_addr, addr.sins.sin6.s6_addr+12, sizeof(struct in_addr)); sai.sins.sin.sin_family = AF_INET; } #endif if (fd < 0) { /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener, 0); return; } /* * check for connection limit */ if ((MAXCONNECTIONS - 10) < fd) { ++ServerStats->is_ref; /* * slow down the whining to opers bit */ if((last_oper_notice + 20) <= CurrentTime) { sendto_realops_flags(UMODE_ALL, L_ALL,"All connections in use. (%s)", get_listener_name(listener)); last_oper_notice = CurrentTime; } send(fd, "ERROR :All connections in use\r\n", 32, 0); fd_close(fd); /* Re-register a new IO request for the next accept .. */ comm_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... */ if ((pe = conf_connect_allowed(&addr, sai.sins.sin.sin_family)) != 0) { ServerStats->is_ref++; /* XXX - this can only be BANNED_CLIENT? */ switch (pe) { case BANNED_CLIENT: send(fd, DLINE_WARNING, sizeof(DLINE_WARNING)-1, 0); break; } fd_close(fd); /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener, 0); return; } ServerStats->is_ac++; add_connection(listener, fd); /* Re-register a new IO request for the next accept .. */ comm_setselect(listener->fd, FDLIST_SERVICE, COMM_SELECT_READ, accept_connection, listener, 0); }
static int inetport(struct Listener* listener) { struct irc_sockaddr lsin; int fd; int opt = 1; /* * At first, open a new socket */ fd = comm_open(DEF_FAM, SOCK_STREAM, 0, "Listener socket"); #ifdef IPV6 if (!IN6_ARE_ADDR_EQUAL((struct in6_addr *)&listener->addr, &in6addr_any)) { #else if (INADDR_ANY != listener->addr.sins.sin.s_addr) { #endif inetntop(DEF_FAM, &IN_ADDR(listener->addr), listener->vhost, HOSTLEN); listener->name = listener->vhost; } if (fd == -1) { report_error(L_ALL, "opening listener socket %s:%s", get_listener_name(listener), errno); return 0; } else if ((HARD_FDLIMIT - 10) < fd) { report_error(L_ALL, "no more connections left for listener %s:%s", get_listener_name(listener), errno); fd_close(fd); 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(L_ALL, "setting SO_REUSEADDR for listener %s:%s", get_listener_name(listener), errno); fd_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. */ memset(&lsin, 0, sizeof(struct irc_sockaddr)); S_FAM(lsin) = DEF_FAM; copy_s_addr(S_ADDR(lsin), IN_ADDR(listener->addr)); S_PORT(lsin) = htons(listener->port); if (bind(fd, (struct sockaddr*) &SOCKADDR(lsin), sizeof(struct irc_sockaddr))) { report_error(L_ALL, "binding listener socket %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } if (listen(fd, HYBRID_SOMAXCONN)) { report_error(L_ALL, "listen failed for %s:%s", get_listener_name(listener), errno); fd_close(fd); return 0; } /* * XXX - this should always work, performance will suck if it doesn't */ if (!set_non_blocking(fd)) report_error(L_ALL, NONB_ERROR_MSG, get_listener_name(listener), errno); listener->fd = fd; /* Listen completion events are READ events .. */ accept_connection(fd, listener); return 1; } static struct Listener* find_listener(int port, struct irc_inaddr *addr) { struct Listener* listener = NULL; struct Listener* last_closed = NULL; for (listener = ListenerPollList; listener; listener = listener->next) { if ( (port == listener->port) && (!memcmp(&PIN_ADDR(addr), &IN_ADDR(listener->addr), sizeof(struct irc_inaddr)))) { /* Try to return an open listener, otherwise reuse a closed one */ if (listener->fd == -1) last_closed = listener; else return listener; } } 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) { struct Listener* listener; struct irc_inaddr vaddr; /* * if no port in conf line, don't bother */ if (port == 0) return; #ifdef IPV6 copy_s_addr(IN_ADDR(vaddr), &in6addr_any); #else copy_s_addr(IN_ADDR(vaddr), INADDR_ANY); #endif if (vhost_ip) { if(inetpton(DEF_FAM, vhost_ip, &IN_ADDR(vaddr)) <= 0) return; } if ((listener = find_listener(port, &vaddr))) { if (listener->fd > -1) return; } else { listener = make_listener(port, &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(struct Listener* listener) { assert(listener != NULL); if(listener == NULL) return; if (listener->fd >= 0) { fd_close(listener->fd); listener->fd = -1; } listener->active = 0; if (listener->ref_count) return; free_listener(listener); }
void accept_connection(struct Listener* listener) { static time_t last_oper_notice = 0; struct SOCKADDR_IN addr; socklen_t addrlen = sizeof(struct SOCKADDR_IN); int fd; assert(0 != listener); listener->last_accept = CurrentTime; /* * 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 (-1 == (fd = accept(listener->fd, (struct sockaddr*) &addr, &addrlen))) { if (EAGAIN == errno) return; /* * slow down the whining to opers bit */ if((last_oper_notice + 20) <= CurrentTime) { report_error("Error accepting connection %s:%s", listener->name, errno); last_oper_notice = CurrentTime; } return; } /* * check for connection limit */ if ((MAXCONNECTIONS - 10) < fd) { ++ServerStats->is_ref; /* * slow down the whining to opers bit */ if((last_oper_notice + 20) <= CurrentTime) { sendto_realops("All connections in use. (%s)", get_listener_name(listener)); last_oper_notice = CurrentTime; } send(fd, "ERROR :All connections in use\r\n", 32, 0); CLOSE(fd); return; } /* * check to see if listener is shutting down */ if (!listener->active) { ++ServerStats->is_ref; send(fd, "ERROR :Use another port\r\n", 25, 0); CLOSE(fd); return; } /* * check conf for ip address access */ if (!conf_connect_allowed(addr.SIN_ADDR)) { ServerStats->is_ref++; #ifdef REPORT_DLINE_TO_USER send(fd, "NOTICE DLINE :*** You have been D-lined\r\n", 41, 0); #endif CLOSE(fd); return; } ServerStats->is_ac++; nextping = CurrentTime; add_connection(listener, fd); }
static int inetport(struct Listener* listener) { struct SOCKADDR_IN port_sin; int fd; int opt = 1; /* * At first, open a new socket */ fd = socket(AFINET, SOCK_STREAM, 0); if (-1 == fd) { report_error("opening listener socket %s:%s", get_listener_name(listener), errno); return 0; } else if ((HARD_FDLIMIT - 10) < fd) { report_error("no more connections left for listener %s:%s", get_listener_name(listener), errno); CLOSE(fd); 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), errno); 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. */ memset(&port_sin, 0, sizeof(port_sin)); port_sin.SIN_FAMILY = AFINET; port_sin.SIN_PORT = htons(listener->port); #ifdef __CYGWIN__ port_sin.sin_addr = listener->addr; if (INADDR_ANY != listener->addr.S_ADDR) { strncpy_irc(listener->vhost, inetntoa((char *)&listener->addr), HOSTLEN); listener->name = listener->vhost; } #else #ifdef IPV6 bcopy((const char*)listener->addr.S_ADDR, (char*)port_sin.SIN_ADDR.S_ADDR, sizeof(struct IN_ADDR)); if ( bcmp((char*)listener->addr.S_ADDR, &INADDRANY, sizeof(struct IN_ADDR)) == 0 ) #else port_sin.sin_addr = listener->addr; if (INADDRANY != listener->addr.s_addr) #endif { struct addrinfo *ans; int ret; char port[5]; char tmp[HOSTLEN]; /* * XXX - blocking call to getaddrinfo */ sprintf( port, "%d", listener->port); #ifdef IPV6 inetntop(AFINET, &listener->addr, tmp, HOSTLEN); #else inet_ntop(AF_INET, &listener->addr, tmp, HOSTLEN); #endif ret = getaddrinfo(tmp, port, NULL, &ans ); if( ret == 0 && ans->ai_canonname) strncpy_irc(listener->vhost, ans->ai_canonname, HOSTLEN); } #endif if (bind(fd, (struct sockaddr*) &port_sin, sizeof(port_sin))) { report_error("binding listener socket %s:%s", get_listener_name(listener), errno); CLOSE(fd); return 0; } if (listen(fd, HYBRID_SOMAXCONN)) { report_error("listen failed for %s:%s", get_listener_name(listener), errno); CLOSE(fd); return 0; } /* * XXX - this should always work, performance will suck if it doesn't */ if (!set_non_blocking(fd)) report_error(NONB_ERROR_MSG, get_listener_name(listener), errno); listener->fd = fd; return 1; }