/** Start sending upings to a server. * @param[in] sptr Client requesting the upings. * @param[in] aconf ConfItem containing the address to ping. * @param[in] port Port number to ping. * @param[in] count Number of times to ping (should be at least 20). * @return Zero. */ int uping_server(struct Client* sptr, struct ConfItem* aconf, int port, int count) { int fd; struct UPing* pptr; assert(0 != sptr); assert(0 != aconf); if (INADDR_NONE == aconf->ipnum.s_addr) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Host lookup failed for " "%s", sptr, aconf->name); return 0; } if (IsUPing(sptr)) uping_cancel(sptr, sptr); /* Cancel previous ping request */ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Unable to create udp " "ping socket", sptr); return 0; } if (!os_set_nonblocking(fd)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't set fd non-" "blocking", sptr); close(fd); return 0; } pptr = (struct UPing*) MyMalloc(sizeof(struct UPing)); assert(0 != pptr); memset(pptr, 0, sizeof(struct UPing)); if (!socket_add(&pptr->socket, uping_read_callback, (void*) pptr, SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :UPING: Can't queue fd for " "reading", sptr); close(fd); MyFree(pptr); return 0; } pptr->fd = fd; pptr->sin.sin_port = htons(port); pptr->sin.sin_addr.s_addr = aconf->ipnum.s_addr; pptr->sin.sin_family = AF_INET; pptr->count = IRCD_MIN(20, count); pptr->client = sptr; pptr->index = -1; pptr->freeable = UPING_PENDING_SOCKET; strcpy(pptr->name, aconf->name); pptr->next = pingList; pingList = pptr; SetUPing(sptr); uping_start(pptr); return 0; }
/* * @returns 0 on success, -1 on error. */ int uping_init(void) { struct sockaddr_in from = { 0 }; int fd; memset(&from, 0, sizeof(from)); from.sin_addr = VirtualHost.sin_addr; from.sin_port = htons(atoi(UDP_PORT)); from.sin_family = AF_INET; if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { Debug((DEBUG_ERROR, "UPING: UDP listener socket call failed: %s", (strerror(errno)) ? strerror(errno) : "Unknown error")); return -1; } if (!os_set_reuseaddr(fd)) { log_write(LS_SOCKET, L_ERROR, 0, "UPING: set reuseaddr on UDP listener failed: %m (fd %d)", fd); Debug((DEBUG_ERROR, "UPING: set reuseaddr on UDP listener failed: %s", (strerror(errno)) ? strerror(errno) : "Unknown error")); close(fd); return -1; } if (bind(fd, (struct sockaddr*) &from, sizeof(from)) == -1) { log_write(LS_SOCKET, L_ERROR, 0, "UPING: bind on UDP listener (%d fd %d) failed: %m", htons(from.sin_port), fd); Debug((DEBUG_ERROR, "UPING: bind on UDP listener failed : %s", (strerror(errno)) ? strerror(errno) : "Unknown error")); close(fd); return -1; } if (!os_set_nonblocking(fd)) { Debug((DEBUG_ERROR, "UPING: set non-blocking: %s", (strerror(errno)) ? strerror(errno) : "Unknown error")); close(fd); return -1; } if (!socket_add(&upingSock, uping_echo_callback, 0, SS_DATAGRAM, SOCK_EVENT_READABLE, fd)) { Debug((DEBUG_ERROR, "UPING: Unable to queue fd to event system")); close(fd); return -1; } UPingFileDescriptor = fd; return fd; }
/** Open a TCP or UDP socket on a particular address. * @param[in] local Local address to bind to. * @param[in] type SOCK_STREAM or SOCK_DGRAM. * @param[in] port_name Port name (used in error diagnostics). * @param[in] family A specific address family to use, or 0 for automatic. * @return Bound descriptor, or -1 on error. */ int os_socket(const struct irc_sockaddr* local, int type, const char* port_name, int family) { struct sockaddr_native addr; int size, fd; assert(local != 0); size = sockaddr_from_irc(&addr, local, -1, family); fd = socket(addr.sn_family, type, 0); if (fd < 0) { report_error(SOCKET_ERROR_MSG, port_name, errno); return -1; } if (fd > MAXCLIENTS - 1) { report_error(CONNLIMIT_ERROR_MSG, port_name, 0); close(fd); return -1; } if (!os_set_reuseaddr(fd)) { report_error(REUSEADDR_ERROR_MSG, port_name, errno); close(fd); return -1; } if (!os_set_nonblocking(fd)) { report_error(NONB_ERROR_MSG, port_name, errno); close(fd); return -1; } if (local) { #if defined(IPV6_V6ONLY) int on = 1; if (family == AF_INET6 && irc_in_addr_unspec(&local->addr)) setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); #endif if (bind(fd, (struct sockaddr*)&addr, size)) { report_error(BIND_ERROR_MSG, port_name, errno); close(fd); return -1; } } return fd; }
/* * start_auth_query - Flag the client to show that an attempt to * contact the ident server on the client's host. The connect and * subsequently the socket are all put into 'non-blocking' mode. * Should the connect or any later phase of the identifing process fail, * it is aborted and the user is given a username of "unknown". */ static int start_auth_query(struct AuthRequest* auth) { struct sockaddr_in remote_addr; struct sockaddr_in local_addr; int fd; IOResult result; assert(0 != auth); assert(0 != auth->client); if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { ++ServerStats->is_abad; return 0; } if ((MAXCONNECTIONS - 10) < fd) { close(fd); return 0; } if (!os_set_nonblocking(fd)) { close(fd); return 0; } if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_DO_ID); /* * get the local address of the client and bind to that to * make the auth request. This used to be done only for * ifdef VIRTTUAL_HOST, but needs to be done for all clients * since the ident request must originate from that same address-- * and machines with multiple IP addresses are common now */ memset(&local_addr, 0, sizeof(struct sockaddr_in)); os_get_sockname(cli_fd(auth->client), &local_addr); local_addr.sin_port = htons(0); if (bind(fd, (struct sockaddr*) &local_addr, sizeof(struct sockaddr_in))) { close(fd); return 0; } remote_addr.sin_addr.s_addr = (cli_ip(auth->client)).s_addr; remote_addr.sin_port = htons(113); remote_addr.sin_family = AF_INET; if ((result = os_connect_nonb(fd, &remote_addr)) == IO_FAILURE || !socket_add(&auth->socket, auth_sock_callback, (void*) auth, result == IO_SUCCESS ? SS_CONNECTED : SS_CONNECTING, SOCK_EVENT_READABLE, fd)) { ServerStats->is_abad++; /* * No error report from this... */ close(fd); if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_ID); return 0; } auth->flags |= AM_SOCKET; auth->fd = fd; SetAuthConnect(auth); if (result == IO_SUCCESS) send_auth_query(auth); /* this does a SetAuthPending(auth) for us */ return 1; }
void add_connection(struct Listener* listener, int fd, void *ssl) { #else void add_connection(struct Listener* listener, int fd) { #endif struct irc_sockaddr addr; struct Client *new_client; time_t next_target = 0; #if defined(USE_SSL) char *sslfp; #endif 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"; assert(0 != listener); /* * 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 (!os_get_peername(fd, &addr) || !os_set_nonblocking(fd)) { ++ServerStats->is_ref; #if defined(USE_SSL) ssl_murder(ssl, fd, NULL); #else close(fd); #endif return; } /* * 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. */ os_disable_options(fd); if (listener_server(listener)) { new_client = make_client(0, STAT_UNKNOWN_SERVER); } else { /* * Add this local client to the IPcheck registry. * * If they're throttled, murder them, but tell them why first. */ if (!IPcheck_local_connect(&addr.addr, &next_target)) { ++ServerStats->is_ref; #if defined(USE_SSL) ssl_murder(ssl, fd, throttle_message); #else write(fd, throttle_message, strlen(throttle_message)); close(fd); #endif return; } new_client = make_client(0, STAT_UNKNOWN_USER); SetIPChecked(new_client); } /* * Copy ascii address to 'sockhost' just in case. Then we have something * valid to put into error messages... */ 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))); if (next_target) cli_nexttarget(new_client) = next_target; 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; #if defined(USE_SSL) ssl_murder(ssl, fd, register_message); #else write(fd, register_message, strlen(register_message)); close(fd); #endif cli_fd(new_client) = -1; return; } #if defined(USE_SSL) if (ssl) { cli_socket(new_client).s_ssl = ssl; sslfp = ssl_get_fingerprint(ssl); if (sslfp) ircd_strncpy(cli_sslclifp(new_client), sslfp, BUFSIZE+1); } #endif cli_freeflag(new_client) |= FREEFLAG_SOCKET; cli_listener(new_client) = listener; ++listener->ref_count; Count_newunknown(UserStats); /* if we've made it this far we can put the client on the auth query pile */ start_auth(new_client); } /** Determines whether to tell the events engine we're interested in * writable events. * @param cptr Client for which to decide this. */ 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); }
/** 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; 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"; assert(0 != listener); /* * 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 (!os_get_peername(fd, &addr) || !os_set_nonblocking(fd)) { ++ServerStats->is_ref; close(fd); return; } /* * 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. */ os_disable_options(fd); if (listener_server(listener)) { new_client = make_client(0, STAT_UNKNOWN_SERVER); } else { /* * Add this local client to the IPcheck registry. * * If they're throttled, murder them, but tell them why first. */ if (!IPcheck_local_connect(&addr.addr, &next_target)) { ++ServerStats->is_ref; write(fd, throttle_message, strlen(throttle_message)); close(fd); return; } new_client = make_client(0, STAT_UNKNOWN_USER); SetIPChecked(new_client); } /* * Copy ascii address to 'sockhost' just in case. Then we have something * valid to put into error messages... */ 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))); if (next_target) cli_nexttarget(new_client) = next_target; 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 we've made it this far we can put the client on the auth query pile */ start_auth(new_client); }