X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_bsd.c;h=c63f5654d947983684e94d23c1aa959c369ca64b;hb=refs%2Fheads%2Fupstream;hp=28e078a1ad45a4d7a79ada7e5cc1186e51632593;hpb=6978c1326bbe28f7dac9ef26bfd9995c32113e51;p=ircu2.10.12-pk.git diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c index 28e078a..c63f565 100644 --- a/ircd/s_bsd.c +++ b/ircd/s_bsd.c @@ -53,13 +53,13 @@ #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 -#include +/* #include -- Now using assert in ircd_log.h */ #include #include #include @@ -72,16 +72,14 @@ #include #include -#ifdef USE_POLL -#include -#endif /* USE_POLL */ - /** 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 connections. */ -struct irc_sockaddr VirtualHost; +/** 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]; @@ -107,24 +105,6 @@ 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); -#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) - */ -#error "FD_SETSIZE is too small or MAXCONNECTIONS too large." -#endif -#endif - /* * Cannot use perror() within daemon. stderr is closed in @@ -153,13 +133,7 @@ void report_error(const char* text, const char* who, int err) if (EmptyString(who)) who = "unknown"; - if (last_notice + 20 < CurrentTime) { - /* - * pace error messages so opers don't get flooded by transients - */ - sendto_opmask_butone(0, SNO_OLDSNO, text, who, errmsg); - last_notice = CurrentTime; - } + sendto_opmask_butone_ratelimited(0, SNO_OLDSNO, &last_notice, text, who, errmsg); log_write(LS_SOCKET, L_ERROR, 0, text, who, errmsg); errno = errtmp; } @@ -171,14 +145,13 @@ void report_error(const char* text, const char* who, int err) * @param vptr The struct ConfItem representing the Connect block. * @param hp A pointer to the DNS lookup results (NULL on failure). */ -static void connect_dns_callback(void* vptr, struct DNSReply* hp) +static void connect_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) { struct ConfItem* aconf = (struct ConfItem*) vptr; assert(aconf); aconf->dns_pending = 0; - if (hp) { - memcpy(&aconf->address, &hp->addr, sizeof(aconf->address)); - MyFree(hp); + if (addr) { + memcpy(&aconf->address, addr, sizeof(aconf->address)); connect_server(aconf, 0); } else @@ -192,10 +165,12 @@ static void connect_dns_callback(void* vptr, struct DNSReply* hp) void close_connections(int close_stderr) { int i; - close(0); - close(1); if (close_stderr) + { + close(0); + close(1); close(2); + } for (i = 3; i < MAXCONNECTIONS; ++i) close(i); } @@ -208,7 +183,7 @@ int init_connection_limits(void) if (0 == limit) return 1; if (limit < 0) { - fprintf(stderr, "error setting max fd's to %d\n", limit); + fprintf(stderr, "error setting max fds to %d: %s\n", limit, strerror(errno)); } else if (limit > 0) { fprintf(stderr, "ircd fd table too big\nHard Limit: %d IRC max: %d\n", @@ -227,6 +202,8 @@ 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); /* @@ -235,9 +212,12 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr) */ if (irc_in_addr_valid(&aconf->origin.addr)) local = &aconf->origin; - else - local = &VirtualHost; - cli_fd(cptr) = os_socket(local, SOCK_STREAM, cli_name(cptr)); + 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; @@ -256,6 +236,12 @@ static int connect_inet(struct ConfItem* aconf, struct Client* 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); @@ -263,6 +249,7 @@ static int connect_inet(struct ConfItem* aconf, struct Client* 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, @@ -273,6 +260,21 @@ static int connect_inet(struct ConfItem* aconf, struct Client* cptr) cli_fd(cptr) = -1; return 0; } + + 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; } @@ -289,26 +291,22 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf) { unsigned int bytes_written = 0; unsigned int bytes_count = 0; + IOResult result; assert(0 != cptr); - switch (os_sendv_nonb(cli_fd(cptr), buf, &bytes_count, &bytes_written)) { + 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; - if (cli_sendB(cptr) > 1023) { - cli_sendK(cptr) += (cli_sendB(cptr) >> 10); - cli_sendB(cptr) &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ - } - if (cli_sendB(&me) > 1023) { - cli_sendK(&me) += (cli_sendB(&me) >> 10); - cli_sendB(&me) &= 0x03ff; - } - /* - * XXX - hrmm.. set blocked here? the socket didn't - * say it was blocked - */ + /* A partial write implies that future writes will block. */ if (bytes_written < bytes_count) SetFlag(cptr, FLAG_BLOCKED); break; @@ -323,27 +321,12 @@ unsigned int deliver_it(struct Client *cptr, struct MsgQ *buf) return bytes_written; } -/** Free the client's DNS reply, if any. - * @param cptr Client to operate on. - */ -void release_dns_reply(struct Client* cptr) -{ - assert(0 != cptr); - assert(MyConnect(cptr)); - - if (cli_dns_reply(cptr)) { - MyFree(cli_dns_reply(cptr)->h_name); - MyFree(cli_dns_reply(cptr)); - cli_dns_reply(cptr) = 0; - } -} - /** 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. + * @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. */ -static int completed_connection(struct Client* cptr) +int completed_connection(struct Client* cptr) { struct ConfItem *aconf; time_t newts; @@ -393,9 +376,9 @@ static int completed_connection(struct Client* cptr) * Make us timeout after twice the timeout for DNS look ups */ cli_lasttime(cptr) = CurrentTime; - SetFlag(cptr, FLAG_PINGSENT); + ClearPingSent(cptr); - sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s :%s", + 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)); @@ -415,24 +398,14 @@ void close_connection(struct Client *cptr) ServerStats->is_sv++; ServerStats->is_sbs += cli_sendB(cptr); ServerStats->is_sbr += cli_receiveB(cptr); - ServerStats->is_sks += cli_sendK(cptr); - ServerStats->is_skr += cli_receiveK(cptr); ServerStats->is_sti += CurrentTime - cli_firsttime(cptr); - if (ServerStats->is_sbs > 1023) { - ServerStats->is_sks += (ServerStats->is_sbs >> 10); - ServerStats->is_sbs &= 0x3ff; - } - if (ServerStats->is_sbr > 1023) { - ServerStats->is_skr += (ServerStats->is_sbr >> 10); - ServerStats->is_sbr &= 0x3ff; - } /* * If the connection has been up for a long amount of time, schedule * a 'quick' reconnect, else reset the next-connect cycle. */ - if ((aconf = find_conf_exact(cli_name(cptr), 0, cli_sockhost(cptr), CONF_SERVER))) { + if ((aconf = find_conf_exact(cli_name(cptr), cptr, CONF_SERVER))) { /* - * Reschedule a faster reconnect, if this was a automaticly + * 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. @@ -449,21 +422,16 @@ void close_connection(struct Client *cptr) ServerStats->is_cl++; ServerStats->is_cbs += cli_sendB(cptr); ServerStats->is_cbr += cli_receiveB(cptr); - ServerStats->is_cks += cli_sendK(cptr); - ServerStats->is_ckr += cli_receiveK(cptr); ServerStats->is_cti += CurrentTime - cli_firsttime(cptr); - if (ServerStats->is_cbs > 1023) { - ServerStats->is_cks += (ServerStats->is_cbs >> 10); - ServerStats->is_cbs &= 0x3ff; - } - if (ServerStats->is_cbr > 1023) { - ServerStats->is_ckr += (ServerStats->is_cbr >> 10); - ServerStats->is_cbr &= 0x3ff; - } } else 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; @@ -554,27 +522,32 @@ void add_connection(struct Listener* listener, int fd) { */ os_disable_options(fd); - /* - * 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) && !listener->server) + if (listener_server(listener)) { - ++ServerStats->is_ref; - write(fd, throttle_message, strlen(throttle_message)); - close(fd); - return; + 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); } - - new_client = make_client(0, ((listener->server) ? - STAT_UNKNOWN_SERVER : STAT_UNKNOWN_USER)); /* * Copy ascii address to 'sockhost' just in case. Then we have something * valid to put into error messages... */ - SetIPChecked(new_client); 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))); @@ -596,8 +569,20 @@ void add_connection(struct Listener* listener, int fd) { ++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); + + 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); + } } /** Determines whether to tell the events engine we're interested in @@ -633,27 +618,35 @@ static int read_packet(struct Client *cptr, int socket_ready) if (socket_ready && !(IsUser(cptr) && DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_CLIENT_FLOOD))) { - switch (os_recv_nonb(cli_fd(cptr), readbuf, sizeof(readbuf), &length)) { + + /* 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) { - if (!IsServer(cptr)) - cli_lasttime(cptr) = CurrentTime; + cli_lasttime(cptr) = CurrentTime; + ClearPingSent(cptr); + ClrFlag(cptr, FLAG_NONL); if (cli_lasttime(cptr) > cli_since(cptr)) cli_since(cptr) = cli_lasttime(cptr); - ClrFlag(cptr, FLAG_PINGSENT); - ClrFlag(cptr, FLAG_NONL); } break; case IO_BLOCKED: break; case IO_FAILURE: cli_error(cptr) = errno; - /* SetFlag(cpt, FLAG_DEADSOCKET); */ + /* 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 :) @@ -672,11 +665,13 @@ static int read_packet(struct Client *cptr, int socket_ready) if (length > 0 && dbuf_put(&(cli_recvQ(cptr)), readbuf, length) == 0) return exit_client(cptr, cptr, &me, "dbuf_put fail"); - if (DBufLength(&(cli_recvQ(cptr))) > feature_int(FEAT_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"); while (DBufLength(&(cli_recvQ(cptr))) && !NoNewLine(cptr) && - (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10)) + (IsTrusted(cptr) || cli_since(cptr) - CurrentTime < 10 || HasUnlimitFlood)) { dolen = dbuf_getmsg(&(cli_recvQ(cptr)), cli_buffer(cptr), BUFSIZE); /* @@ -692,7 +687,13 @@ static int read_packet(struct Client *cptr, int socket_ready) 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); + } } else if (client_dopacket(cptr, dolen) == CPTR_KILLED) return CPTR_KILLED; @@ -773,20 +774,15 @@ int connect_server(struct ConfItem* aconf, struct Client* by) } } /* - * If we dont know the IP# for this host and it is 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 (!irc_in_addr_valid(&aconf->address.addr) && !ircd_aton(&aconf->address.addr, aconf->host)) { char buf[HOSTLEN + 1]; - struct DNSQuery query; - query.vptr = aconf; - query.callback = connect_dns_callback; host_from_uh(buf, aconf->host, HOSTLEN); - buf[HOSTLEN] = '\0'; - - gethost_byname(buf, &query); + gethost_byname(buf, connect_dns_callback, aconf); aconf->dns_pending = 1; return 0; } @@ -806,10 +802,10 @@ int connect_server(struct ConfItem* aconf, struct Client* by) 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 C-line", aconf->name); + "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 " - "C-line", by, aconf->name); + "Connect block", by, aconf->name); } det_confs_butmask(cptr, 0); free_client(cptr); @@ -911,15 +907,28 @@ static void client_sock_callback(struct Event* ev) break; case ET_CONNECT: /* socket connection completed */ - if (!completed_connection(cptr) || IsDead(cptr)) + 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; 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*/