/** Apply GeoIP country data to a client. * @param[in] cptr Client to apply GeoIP country data to. */ void geoip_apply(struct Client* cptr) { #ifdef USE_GEOIP int gcid = 0; #endif /* USE_GEOIP */ #ifdef USE_GEOIP_GL GeoIPLookup gl; #endif /* USE_GEOIP_GL */ if (!feature_bool(FEAT_GEOIP_ENABLE)) return; if (!(cptr)) return; #ifdef USE_GEOIP if (irc_in_addr_is_ipv4(&cli_ip(cptr))) { /* User is IPv4 so use gi4. */ if (gi4 != NULL) #ifdef USE_GEOIP_GL gcid = GeoIP_id_by_addr_gl(gi4, cli_sock_ip(cptr), &gl); #else gcid = GeoIP_id_by_addr(gi4, cli_sock_ip(cptr)); #endif /* USE_GEOIP_GL */ } else { /* User is IPv6 so use gi6. */ if (gi6 != NULL) #ifdef USE_GEOIP_GL gcid = GeoIP_id_by_addr_v6_gl(gi6, cli_sock_ip(cptr), &gl); #else gcid = GeoIP_id_by_addr_v6(gi6, cli_sock_ip(cptr)); #endif /* USE_GEOIP_GL */ } #endif /* USE_GEOIP */ #ifdef USE_GEOIP if (gcid == 0) { #endif /* USE_GEOIP */ ircd_strncpy((char *)&cli_countrycode(cptr), "--", 3); ircd_strncpy((char *)&cli_countryname(cptr), "Unknown", 8); ircd_strncpy((char *)&cli_continentcode(cptr), "--", 3); ircd_strncpy((char *)&cli_continentname(cptr), "Unknown", 8); #ifdef USE_GEOIP } else { ircd_strncpy((char *)&cli_countrycode(cptr), GeoIP_code_by_id(gcid), 3); ircd_strncpy((char *)&cli_countryname(cptr), GeoIP_name_by_id(gcid), 256); ircd_strncpy((char *)&cli_continentcode(cptr), GeoIP_continent_by_id(gcid), 3); ircd_strncpy((char *)&cli_continentname(cptr), geoip_continent_name_by_code(GeoIP_continent_by_id(gcid)), 256); } #endif /* USE_GEOIP */ SetGeoIP(cptr); }
/** Find a matching Z-line for a user. * @param[in] cptr Client to compare against. * @param[in] flags Bitwise combination of ZLINE_GLOBAL and/or * ZLINE_LASTMOD to limit matches. * @return Matching Z-line, or NULL if none are found. */ struct Zline * zline_lookup(struct Client *cptr, unsigned int flags) { struct Zline *zline; struct Zline *szline; if (find_except_conf(cptr, EFLAG_ZLINE)) return 0; zliter(GlobalZlineList, zline, szline) { if ((flags & ZLINE_GLOBAL && zline->zl_flags & ZLINE_LOCAL) || (flags & ZLINE_LASTMOD && !zline->zl_lastmod)) continue; if (ZlineIsIpMask(zline)) { if (!irc_in_addr_type_cmp(&cli_ip(cptr), &zline->zl_addr) && zline->zl_bits) continue; if (!ipmask_check(&cli_ip(cptr), &zline->zl_addr, zline->zl_bits)) continue; } else { if (match(zline->zl_mask, cli_sock_ip(cptr)) != 0) continue; } if (ZlineIsActive(zline)) return zline; } /* * No Zlines matched */ return 0; }
/** Return the name of the client for various tracking and admin * purposes. The main purpose of this function is to return the * "socket host" name of the client, if that differs from the * advertised name (other than case). But, this can be used on any * client structure. * @param sptr Client to operate on. * @param showip If non-zero, append [username\@text-ip] to name. * @return Either cli_name(\a sptr) or a static buffer. */ const char* get_client_name(const struct Client* sptr, int showip) { static char nbuf[HOSTLEN * 2 + USERLEN + 5]; if (!MyConnect(sptr) || !showip) return cli_name(sptr); ircd_snprintf(0, nbuf, sizeof(nbuf), "%s[%s@%s]", cli_name(sptr), IsIdented(sptr) ? cli_username(sptr) : "", cli_sock_ip(sptr)); return nbuf; }
/** Check local clients against a new Z-line. * If the Z-line is inactive, return immediately. * Otherwise, if any users match it, disconnect them. * @param[in] cptr Peer connect that sent the Z-line. * @param[in] sptr Client that originated the Z-line. * @param[in] zline New Z-line to check. * @return Zero, unless \a sptr Z-lined himself, in which case CPTR_KILLED. */ static int do_zline(struct Client *cptr, struct Client *sptr, struct Zline *zline) { struct Client *acptr; int fd, retval = 0, tval; if (feature_bool(FEAT_DISABLE_ZLINES)) return 0; /* Z-lines are disabled */ if (!ZlineIsActive(zline)) /* no action taken on inactive zlines */ return 0; for (fd = HighestFd; fd >= 0; --fd) { /* * get the users! */ if ((acptr = LocalClientArray[fd])) { if (!cli_user(acptr)) continue; if (find_except_conf(acptr, EFLAG_ZLINE)) continue; /* IP zline */ if (ZlineIsIpMask(zline)) { if (!irc_in_addr_type_cmp(&cli_ip(acptr), &zline->zl_addr) && zline->zl_bits) continue; if (!ipmask_check(&cli_ip(acptr), &zline->zl_addr, zline->zl_bits)) continue; } else { if (match(zline->zl_mask, cli_sock_ip(acptr)) != 0) continue; } /* ok, here's one that got Z-lined */ send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s", zline->zl_reason); /* let the ops know about it */ sendto_opmask_butone_global(&me, SNO_GLINE, "Z-line active for %s", get_client_name(acptr, SHOW_IP)); /* and get rid of him */ if ((tval = exit_client_msg(cptr, acptr, &me, "Z-lined%s%s%s", (!feature_bool(FEAT_HIS_ZLINE_REASON) ? " (" : ""), (!feature_bool(FEAT_HIS_ZLINE_REASON) ? zline->zl_reason : ""), (!feature_bool(FEAT_HIS_ZLINE_REASON) ? ")" : "")))) retval = tval; /* retain killed status */ } } return retval; }
/* * release_auth_client - release auth client from auth system * this adds the client into the local client lists so it can be read by * the main io processing loop */ static void release_auth_client(struct Client* client) { assert(0 != client); cli_auth(client) = 0; cli_lasttime(client) = cli_since(client) = CurrentTime; if (cli_fd(client) > HighestFd) HighestFd = cli_fd(client); LocalClientArray[cli_fd(client)] = client; add_client_to_list(client); socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE); Debug((DEBUG_INFO, "Auth: release_auth_client %s@%s[%s]", cli_username(client), cli_sockhost(client), cli_sock_ip(client))); }
/** Handle a SERVER message from an unregistered connection. * * \a parv has the following elements: * \li \a parv[1] is the server name * \li \a parv[2] is the hop count to the server * \li \a parv[3] is the start timestamp for the server * \li \a parv[4] is the link timestamp * \li \a parv[5] is the protocol version (P10 or J10) * \li \a parv[6] is the numnick mask for the server * \li \a parv[7] is a string of flags like +hs to mark hubs and services * \li \a parv[\a parc - 1] is the server description * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int mr_server(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* host; struct ConfItem* aconf; struct Jupe* ajupe; unsigned int hop; int ret; unsigned short prot; time_t start_timestamp; time_t timestamp; time_t recv_time; time_t ghost; if (IsUserPort(cptr)) return exit_client_msg(cptr, cptr, &me, "Cannot connect a server to a user port"); if (parc < 8) { need_more_params(sptr, "SERVER"); return exit_client(cptr, cptr, &me, "Need more parameters"); } host = clean_servername(parv[1]); if (!host) { sendto_opmask(0, SNO_OLDSNO, "Bogus server name (%s) from %s", host, cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Bogus server name (%s)", host); } if ((ajupe = jupe_find(host)) && JupeIsActive(ajupe)) return exit_client_msg(cptr, sptr, &me, "Juped: %s", JupeReason(ajupe)); /* check connection rules */ if (0 != conf_eval_crule(host, CRULE_ALL)) { ServerStats->is_ref++; sendto_opmask(0, SNO_OLDSNO, "Refused connection from %s.", cli_name(cptr)); return exit_client(cptr, cptr, &me, "Disallowed by connection rule"); } log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "SERVER: %s %s[%s]", host, cli_sockhost(cptr), cli_sock_ip(cptr)); /* * Detect protocol */ hop = atoi(parv[2]); start_timestamp = atoi(parv[3]); timestamp = atoi(parv[4]); prot = parse_protocol(parv[5]); if (!prot) return exit_client_msg(cptr, sptr, &me, "Bogus protocol (%s)", parv[5]); else if (prot < atoi(MINOR_PROTOCOL)) return exit_new_server(cptr, sptr, host, timestamp, "Incompatible protocol: %s", parv[5]); Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %Tu (%Tu)", host, parv[4], start_timestamp, cli_serv(&me)->timestamp)); if (timestamp < OLDEST_TS || start_timestamp < OLDEST_TS) return exit_client_msg(cptr, sptr, &me, "Bogus timestamps (%s %s)", parv[3], parv[4]); /* If the server had a different name before, change it. */ if (!EmptyString(cli_name(cptr)) && (IsUnknown(cptr) || IsHandshake(cptr)) && 0 != ircd_strcmp(cli_name(cptr), host)) hChangeClient(cptr, host); ircd_strncpy(cli_name(cptr), host, HOSTLEN); ircd_strncpy(cli_info(cptr), parv[parc-1][0] ? parv[parc-1] : cli_name(&me), REALLEN); cli_hopcount(cptr) = hop; if (conf_check_server(cptr)) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Received unauthorized connection from %s", cli_name(cptr)); log_write(LS_NETWORK, L_NOTICE, LOG_NOSNOTICE, "Received unauthorized " "connection from %C [%s]", cptr, ircd_ntoa(&cli_ip(cptr))); return exit_client(cptr, cptr, &me, "No Connect block"); } host = cli_name(cptr); update_load(); if (!(aconf = find_conf_byname(cli_confs(cptr), host, CONF_SERVER))) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied. No conf line for server %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "Access denied. No conf line for server %s", cli_name(cptr)); } #if defined(USE_SSL) if (!verify_sslclifp(cptr, aconf)) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied (SSL fingerprint mismatch) %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "No Access (SSL fingerprint mismatch) %s", cli_name(cptr)); } #endif if (*aconf->passwd && !!strcmp(aconf->passwd, cli_passwd(cptr))) { ++ServerStats->is_ref; sendto_opmask(0, SNO_OLDSNO, "Access denied (passwd mismatch) %s", cli_name(cptr)); return exit_client_msg(cptr, cptr, &me, "No Access (passwd mismatch) %s", cli_name(cptr)); } memset(cli_passwd(cptr), 0, sizeof(cli_passwd(cptr))); ret = check_loop_and_lh(cptr, sptr, &ghost, host, (parc > 7 ? parv[6] : NULL), timestamp, hop, 1); if (ret != 1) return ret; make_server(cptr); cli_serv(cptr)->timestamp = timestamp; cli_serv(cptr)->prot = prot; cli_serv(cptr)->ghost = ghost; memset(cli_privs(cptr), 255, sizeof(struct Privs)); ClrPriv(cptr, PRIV_SET); SetServerYXX(cptr, cptr, parv[6]); update_uworld_flags(cptr); if (*parv[7] == '+') set_server_flags(cptr, parv[7] + 1); recv_time = TStime(); check_start_timestamp(cptr, timestamp, start_timestamp, recv_time); ret = server_estab(cptr, aconf); if (feature_bool(FEAT_RELIABLE_CLOCK) && abs(cli_serv(cptr)->timestamp - recv_time) > 30) { sendto_opmask(0, SNO_OLDSNO, "Connected to a net with a " "timestamp-clock difference of %Td seconds! " "Used SETTIME to correct this.", timestamp - recv_time); sendcmdto_prio_one(&me, CMD_SETTIME, cptr, "%Tu :%s", TStime(), cli_name(&me)); } return ret; }
/* * auth_dns_callback - called when resolver query finishes * if the query resulted in a successful search, hp will contain * a non-null pointer, otherwise hp will be null. * set the client on it's way to a connection completion, regardless * of success of failure */ static void auth_dns_callback(void* vptr, struct DNSReply* reply) { struct AuthRequest* auth = (struct AuthRequest*) vptr; assert(0 != auth); /* * need to do this here so auth_kill_client doesn't * try have the resolver delete the query it's about * to delete anyways. --Bleep */ ClearDNSPending(auth); if (reply) { const struct hostent* hp = reply->hp; int i; assert(0 != hp); /* * Verify that the host to ip mapping is correct both ways and that * the ip#(s) for the socket is listed for the host. */ for (i = 0; hp->h_addr_list[i]; ++i) { if (0 == memcmp(hp->h_addr_list[i], &(cli_ip(auth->client)), sizeof(struct in_addr))) break; } if (!hp->h_addr_list[i]) { if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_IP_MISMATCH); sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]", cli_sock_ip(auth->client), hp->h_name, ircd_ntoa(hp->h_addr_list[0])); if (feature_bool(FEAT_KILL_IPMISMATCH)) { auth_kill_client(auth); return; } } else if (!auth_verify_hostname(hp->h_name, HOSTLEN)) { if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_INVAL_DNS); } else { ++reply->ref_count; cli_dns_reply(auth->client) = reply; ircd_strncpy(cli_sockhost(auth->client), hp->h_name, HOSTLEN); if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FIN_DNS); } } else { /* * this should have already been done by s_bsd.c in add_connection * * strcpy(auth->client->sockhost, auth->client->sock_ip); */ if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_DNS); } if (!IsDoingAuth(auth)) { release_auth_client(auth->client); unlink_auth_request(auth, &AuthIncompleteList); free_auth_request(auth); } }
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); }
/** 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. */ static int connect_inet(struct ConfItem* aconf, struct Client* cptr) { const struct irc_sockaddr *local; IOResult result; int family = 0; assert(0 != aconf); assert(0 != cptr); /* * Might as well get sockhost from here, the connection is attempted * with it so if it fails its useless. */ 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; /* * 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; } /* * 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); } 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; } 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; } cli_freeflag(cptr) |= FREEFLAG_SOCKET; return 1; }
/** 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); }