/************************************************************************ * IRC - Internet Relay Chat, src/s_bsd.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "struct.h" #include "common.h" #include "sys.h" #include "res.h" #include "numeric.h" #include "patchlevel.h" #include "zlink.h" #include "throttle.h" #include "userban.h" #include #include #include #include #if defined(SOL20) #include #include #include #endif #include "inet.h" #include #include #include #include #include #include "hooks.h" #ifdef AIX #include #include #else #include "nameser.h" #endif #include "resolv.h" #include "memcount.h" /* If FD_ZERO isn't define up to this point, * define it (BSD4.2 needs this) */ #include "h.h" #include "fdlist.h" #include "fds.h" extern void engine_init(); extern fdlist default_fdlist; extern int forked; extern void free_port(aPort *); #ifndef IN_LOOPBACKNET #define IN_LOOPBACKNET 0x7f #endif #if defined(MAXBUFFERS) int rcvbufmax = 0, sndbufmax = 0; #endif #ifdef MAXBUFFERS void reset_sock_opts(int, int); #endif static void set_listener_sock_opts(int, aListener *); static void set_listener_non_blocking(int, aListener *); /* listener list, count */ aListener *listen_list = NULL; int listen_count = 0; aClient *local[MAXCONNECTIONS]; int highest_fd = 0, resfd = -1; time_t timeofday; static struct sockaddr_in mysk; static struct sockaddr *connect_inet(aConnect *, aClient *, int *); static int check_init(aClient *, char *); static void set_sock_opts(int, aClient *); #if defined(MAXBUFFERS) static char *readbuf; #else static char readbuf[8192]; #endif /* Silly macro to ignore certain report error statements */ #define silent_report_error(x,y) /* * Try and find the correct name to use with getrlimit() for setting * the max. number of files allowed to be open by this process. */ #ifdef RLIMIT_FDMAX #define RLIMIT_FD_MAX RLIMIT_FDMAX #else #ifdef RLIMIT_NOFILE #define RLIMIT_FD_MAX RLIMIT_NOFILE #else #ifdef RLIMIT_OPEN_MAX #define RLIMIT_FD_MAX RLIMIT_OPEN_MAX #else #undef RLIMIT_FD_MAX #endif #endif #endif /* * add_local_domain() * Add the domain to hostname, if it is missing * (as suggested by eps@TOASTER.SFSU.EDU) */ void add_local_domain(char *hname, int size) { #ifdef RES_INIT /* try to fix up unqualified name */ if (!strchr(hname, '.')) { if (size < 3) return; if (!(_res.options & RES_INIT)) { Debug((DEBUG_DNS, "res_init()")); res_init(); } if (_res.defdname[0]) { strncat(hname, ".", size - 1); strncat(hname, _res.defdname, size - 2); } } #endif return; } /* * Cannot use perror() within daemon. stderr is closed in * ircd and cannot be used. And, worse yet, it might have * been reassigned to a normal connection... */ /* * report_error * This a replacement for perror(). Record error to log and * also send a copy to all *LOCAL* opers online. * text is a *format* string for outputting error. It must * contain only two '%s', the first will be replaced by the * sockhost from the cptr, and the latter will be taken from * sys_errlist[errno]. * * cptr, if not NULL, is the *LOCAL* client associated with * the error. */ void report_error(char *text, aClient * cptr) { int errtmp = errno; /* debug may change 'errno' */ char *host; int err; socklen_t len = sizeof(err); extern char *strerror(); host = (cptr) ? get_client_name(cptr, (IsServer(cptr) ? HIDEME : FALSE)) : ""; Debug((DEBUG_ERROR, text, host, strerror(errtmp))); /* * Get the *real* error from the socket (well try to anyway..). This * may only work when SO_DEBUG is enabled but its worth the gamble * anyway. */ #ifdef SO_ERROR if (!IsMe(cptr) && cptr->fd >= 0) if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len)) if (err) errtmp = err; #endif sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp)); #ifdef USE_SYSLOG syslog(LOG_WARNING, text, host, strerror(errtmp)); if (!forked) { fprintf(stderr, text, host, strerror(errtmp)); fprintf(stderr, "\n"); } #endif return; } void report_listener_error(char *text, aListener *lptr) { int errtmp = errno; /* debug may change 'errno' */ char *host; int err; socklen_t len = sizeof(err); extern char *strerror(); host = get_listener_name(lptr); Debug((DEBUG_ERROR, text, host, strerror(errtmp))); #ifdef SO_ERROR if (lptr->fd >= 0) if (!getsockopt(lptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len)) if (err) errtmp = err; #endif sendto_realops_lev(DEBUG_LEV, text, host, strerror(errtmp)); #ifdef USE_SYSLOG syslog(LOG_WARNING, text, host, strerror(errtmp)); #endif if (!forked) { fprintf(stderr, text, host, strerror(errtmp)); fprintf(stderr, "\n"); } return; } /* * open_listeners() * * cycle through our entire ports list and open them if they * arent already open. * Added Feb/04 -epi */ void open_listeners() { aPort *tmp; if(!ports) sendto_realops("Lost all port configurations!"); for(tmp = ports; tmp; tmp = tmp->next) { if(tmp->lstn) continue; add_listener(tmp); } return; } /* * add_listener * * Create a new client which is essentially the stub like 'me' to be used * for a socket that is passive (listen'ing for connections to be * accepted). * Backported from defunct 1.6 and updated for aPort structure in Feb04. * I'm assuming lucas rewrote this originally. -epi */ int add_listener(aPort *aport) { aListener *lptr; aListener lstn; struct sockaddr_in server; int ad[4]; socklen_t len = sizeof(server); char ipname[20]; memset(&lstn, 0, sizeof(aListener)); ad[0] = ad[1] = ad[2] = ad[3] = 0; if(!BadPtr(aport->allow)) { strncpyzt(lstn.allow_string, aport->allow, sizeof(lstn.allow_string)); sscanf(lstn.allow_string, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]); ircsprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]); lstn.allow_ip.s_addr = inet_addr(ipname); } if (!BadPtr(aport->address) && (*aport->address != '*')) { lstn.vhost_ip.s_addr = inet_addr(aport->address); strncpyzt(lstn.vhost_string, aport->address, sizeof(lstn.vhost_string)); } lstn.port = aport->port; if(lstn.port <= 0) /* stop stupidity cold */ return -1; lstn.fd = socket(AF_INET, SOCK_STREAM, 0); if (lstn.fd < 0) { report_listener_error("opening stream socket %s:%s", &lstn); return -1; } set_listener_sock_opts(lstn.fd, &lstn); memset(&server, '\0', sizeof(server)); server.sin_family = AF_INET; if (lstn.vhost_ip.s_addr) server.sin_addr.s_addr = lstn.vhost_ip.s_addr; else server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(lstn.port); if (bind(lstn.fd, (struct sockaddr *) &server, sizeof(struct sockaddr_in))) { report_listener_error("binding stream socket %s:%s", &lstn); close(lstn.fd); return -1; } if (getsockname(lstn.fd, (struct sockaddr *) &server, &len)) { report_listener_error("getsockname failed for %s:%s", &lstn); close(lstn.fd); return -1; } if (lstn.fd > highest_fd) highest_fd = lstn.fd; #ifdef SOMAXCONN if(listen(lstn.fd, SOMAXCONN)) #else if(listen(lstn.fd, HYBRID_SOMAXCONN)) #endif { report_listener_error("error listening on FD %s:%s", &lstn); close(lstn.fd); return -1; } lptr = (aListener *) MyMalloc(sizeof(aListener)); memcpy(lptr, &lstn, sizeof(aListener)); if(local[lptr->fd]) { report_listener_error("!!!! listener fd is held by client" " in local[] array %s:%s", &lstn); abort(); } lptr->aport = aport; aport->lstn = lptr; set_listener_non_blocking(lptr->fd, lptr); #ifdef HAVE_SSL if ((aport->ptype!=NULL) && (mycmp("SSL", aport->ptype)==0)) { SetSSL(lptr); lptr->ssl = NULL; lptr->client_cert = NULL; } #endif add_fd(lptr->fd, FDT_LISTENER, lptr); set_fd_flags(lptr->fd, FDF_WANTREAD); listen_count++; lptr->next = listen_list; listen_list = lptr; lptr->lasttime = timeofday; return 0; } void close_listener(aListener *lptr) { aListener *alptr, *alptrprev = NULL; aPort *aport, *aportl, *aportn = NULL; del_fd(lptr->fd); close(lptr->fd); /* drop our conf link */ aport = lptr->aport; aport->lstn = NULL; /* and now drop the conf itself */ for(aportl = ports ; aportl ; aportl = aportl->next) { if(aportl == aport) { if(aportn) aportn->next = aportl->next; else ports = aportl->next; free_port(aportl); break; } aportn = aportl; } /* now drop the listener */ for(alptr = listen_list; alptr; alptr = alptr->next) { if(alptr == lptr) { if(alptrprev) alptrprev->next = alptr->next; else listen_list = alptr->next; MyFree(lptr); listen_count--; break; } alptrprev = alptr; } } /* * close_listeners * * Close and free all clients which are marked as having their socket open * and in a state where they can accept connections. Unix sockets have * the path to the socket unlinked for cleanliness. */ void close_listeners() { aListener *lptr, *lptrnext; lptr = listen_list; while(lptr) { lptrnext = lptr->next; if(lptr->clients <= 0) close_listener(lptr); else /* if we cant close it, mark it for closing * when we loose all of our connections */ lptr->aport->legal = -1; lptr = lptrnext; } } /* init_sys */ void init_sys() { int fd; #ifdef RLIMIT_FD_MAX struct rlimit limit; if (!getrlimit(RLIMIT_FD_MAX, &limit)) { if (limit.rlim_max < MAXCONNECTIONS) { printf("FATAL: System is only allowing %ld open files.\n", (long)limit.rlim_max); printf("ircd requires at least %ld.\n", (long)MAXCONNECTIONS); printf("Fix the system account limits or recompile ircd.\n"); printf("Aborting...\n"); exit(-1); } /* set limit to exactly what we can handle */ limit.rlim_cur = MAXCONNECTIONS; if (setrlimit(RLIMIT_FD_MAX, &limit) == -1) { printf("FATAL: Unable to set open file limit to %ld:\n%s\n", (long)limit.rlim_cur, strerror(errno)); printf("Aborting...\n"); exit(-1); } } #endif printf("\nIrcd is now becoming a daemon.\n"); #if !defined(SOL20) setlinebuf(stderr); #endif for (fd = 3; fd < MAXCONNECTIONS; fd++) { close(fd); local[fd] = NULL; } local[1] = NULL; if (bootopt & BOOT_TTY) { engine_init(); /* debugging is going to a tty */ resfd = init_resolver(0x1f); add_fd(resfd, FDT_RESOLVER, NULL); set_fd_flags(resfd, FDF_WANTREAD); return; } close(1); if (!(bootopt & BOOT_DEBUG) && !(bootopt & BOOT_STDERR)) close(2); if ((isatty(0)) && !(bootopt & BOOT_OPER) && !(bootopt & BOOT_STDERR)) { int pid; if ((pid = fork()) < 0) { if ((fd = open("/dev/tty", O_RDWR)) >= 0) write(fd, "Couldn't fork!\n", 15); /* crude, but effective */ exit(0); } else if (pid > 0) exit(0); setsid(); close(0); /* fd 0 opened by inetd */ local[0] = NULL; } engine_init(); resfd = init_resolver(0x1f); add_fd(resfd, FDT_RESOLVER, NULL); set_fd_flags(resfd, FDF_WANTREAD); return; } void write_pidfile() { #ifdef IRCD_PIDFILE int fd; char buff[20]; if ((fd = open(IRCD_PIDFILE, O_CREAT | O_WRONLY, 0600)) >= 0) { ircsprintf(buff, "%5d\n", (int) getpid()); if (write(fd, buff, strlen(buff)) == -1) Debug((DEBUG_NOTICE, "Error writing to pid file %s", IRCD_PIDFILE)); close(fd); return; } #ifdef DEBUGMODE else Debug((DEBUG_NOTICE, "Error opening pid file %s", IRCD_PIDFILE)); #endif #endif } /* * Initialize the various name strings used to store hostnames. This is * set from either the server's sockhost (if client fd is a tty or * localhost) or from the ip# converted into a string. 0 = success, -1 * = fail. */ static int check_init(aClient * cptr, char *sockn) { struct sockaddr_in sk; socklen_t len = sizeof(struct sockaddr_in); /* If descriptor is a tty, special checking... * IT can't EVER be a tty */ if (getpeername(cptr->fd, (struct sockaddr *) &sk, &len) == -1) return -1; strcpy(sockn, (char *) inetntoa((char *) &sk.sin_addr)); if (inet_netof(sk.sin_addr) == IN_LOOPBACKNET) { cptr->hostp = NULL; strncpyzt(sockn, me.sockhost, HOSTLEN); } memcpy((char *) &cptr->ip, (char *) &sk.sin_addr, sizeof(struct in_addr)); cptr->port = (int) (ntohs(sk.sin_port)); return 0; } /* * Ordinary client access check. Look for conf lines which have the * same status as the flags passed. 0 = Success -1 = Access denied -2 = * Bad socket. */ int check_client(aClient *cptr) { static char sockname[HOSTLEN + 1]; struct hostent *hp = NULL; int i; Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]", cptr->name, inetntoa((char *) &cptr->ip))); if (check_init(cptr, sockname)) return -2; hp = cptr->hostp; /* * Verify that the host to ip mapping is correct both ways and that * the ip#(s) for the socket is listed for the host. */ if (hp) { for (i = 0; hp->h_addr_list[i]; i++) if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip, sizeof(struct in_addr))) break; if (!hp->h_addr_list[i]) { sendto_one(cptr, "NOTICE AUTH :*** Your forward and reverse" " DNS do not match, ignoring hostname."); hp = NULL; } } if ((i = attach_Iline(cptr, hp, sockname))) { Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cptr->name, sockname)); return i; } Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]", cptr->name, sockname)); if (inet_netof(cptr->ip) == IN_LOOPBACKNET || inet_netof(cptr->ip) == inet_netof(mysk.sin_addr)) { ircstp->is_loc++; cptr->flags |= FLAGS_LOCAL; } return 0; } #define CFLAG CONF_CONNECT_SERVER #define NFLAG CONF_NOCONNECT_SERVER /* * check_server_init(), check_server() check access for a server given * its name (passed in cptr struct). Must check for all C/N lines which * have a name which matches the name given and a host which matches. A * host alias which is the same as the server name is also acceptable * in the host field of a C/N line. 0 = Success -1 = Access denied -2 = * Bad socket. * * This was terrible code. Terrible! Almost fucking scary! Rewritten into * a single function, much prettier. Feb04 -epi */ int check_server_init(aClient * cptr) { aConnect *aconn = NULL; struct hostent *hp = NULL; char sockname[HOSTLEN + 1], fullname[HOSTLEN + 1]; char abuff[HOSTLEN + USERLEN + 2]; int i = 0, ok = 0; if (check_init(cptr, sockname)) return -2; if (!(aconn = find_aConnect(cptr->name))) { Debug((DEBUG_DNS, "No Connect block for %s", cptr->name)); return -1; } /* * * If the servername is a hostname, either an alias (CNAME) or * real name, then check with it as the host. Use gethostbyname() * to check for servername as hostname. */ if (!cptr->hostp) { char *s; Link lin; /* * * Do a lookup for the CONF line *only* and not the server * connection else we get stuck in a nasty state since it * takes a SERVER message to get us here and we cant * interrupt that very well. */ lin.value.aconn = aconn; lin.flags = ASYNC_CONF; nextdnscheck = 1; if ((s = strchr(aconn->host, '@'))) s++; else s = aconn->host; Debug((DEBUG_DNS, "sv_ci:cache lookup (%s)", s)); if((hp = gethost_byname(s, &lin))) { for (i = 0; hp->h_addr_list[i]; i++) if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip, sizeof(struct in_addr))) break; if (!hp->h_addr_list[i]) { sendto_realops_lev(ADMIN_LEV, "Server IP# Mismatch: %s != %s[%08x]", inetntoa((char *) &cptr->ip), hp->h_name, *((unsigned long *) hp->h_addr)); hp = NULL; } } } else { hp = cptr->hostp; for (i = 0; hp->h_addr_list[i]; i++) if (!memcmp(hp->h_addr_list[i], (char *) &cptr->ip, sizeof(struct in_addr))) break; } if(hp) { strncpyzt(fullname, cptr->name, sizeof(fullname)); add_local_domain(fullname, HOSTLEN - strlen(fullname)); Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s", sockname, fullname)); ircsprintf(abuff, "%s@%s", cptr->username, fullname); get_sockhost(cptr, fullname); for (i = 0; hp->h_addr_list[i]; i++) { if(!memcmp((char *) &aconn->ipnum, (char *) hp->h_addr_list[i], sizeof(struct in_addr))) ok = 1; else ok = 0; } } else { /* having no luck finding a host.. check against IP */ if(!memcmp((char *) &aconn->ipnum, (char *) &cptr->ip, sizeof(struct in_addr))) ok = 1; else ok = 0; } /* if they dont match up, then away with them */ if (!ok) { get_sockhost(cptr, sockname); return -1; } /* check for Ulined access and link it if nessecary */ if(find_aUserver(cptr->name)) cptr->flags |= FLAGS_ULINE; make_server(cptr); cptr->serv->aconn = aconn; aconn->acpt = cptr; set_effective_class(cptr); if ((aconn->ipnum.s_addr == -1)) memcpy((char *) &aconn->ipnum, (char *) &cptr->ip, sizeof(struct in_addr)); get_sockhost(cptr, aconn->host); Debug((DEBUG_DNS, "sv_cl: access ok: %s[%s]", cptr->name, cptr->sockhost)); return 0; } /* * completed_connection * Complete non-blocking * connect()-sequence. Check access and terminate connection, * if trouble detected. * * Return TRUE if successfully completed - FALSE if failed and ClientExit */ int completed_connection(aClient * cptr) { aConnect *aconn; if(!(cptr->flags & FLAGS_BLOCKED)) unset_fd_flags(cptr->fd, FDF_WANTWRITE); unset_fd_flags(cptr->fd, FDF_WANTREAD); SetHandshake(cptr); if (!(aconn = find_aConnect(cptr->name))) { sendto_realops("Lost Config for %s", get_client_name(cptr, HIDEME)); return -1; } if (!BadPtr(aconn->cpasswd)) sendto_one(cptr, "PASS %s :TS", aconn->cpasswd); /* pass on our capabilities to the server we /connect'd */ #ifdef HAVE_ENCRYPTION_ON if(!(aconn->flags & CONN_DKEY)) sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP" " NICKIP TSMODE"); else { sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY" " ZIP NICKIP TSMODE"); SetWantDKEY(cptr); } #else sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP TSMODE"); #endif if(aconn->flags & CONN_ZIP) cptr->capabilities |= CAPAB_DOZIP; sendto_one(cptr, "SERVER %s 1 :%s", my_name_for_link(me.name, aconn), me.info); #ifdef DO_IDENTD /* Is this the right place to do this? dunno... -Taner */ if (!IsDead(cptr)) start_auth(cptr); #endif check_client_fd(cptr); return (IsDead(cptr)) ? -1 : 0; } /* * close_connection * Close the physical connection. This function must make * MyConnect(cptr) == FALSE, and set cptr->from == NULL. */ void close_connection(aClient *cptr) { aConnect *aconn; if (IsServer(cptr)) { ircstp->is_sv++; ircstp->is_sbs += cptr->sendB; ircstp->is_sbr += cptr->receiveB; ircstp->is_sks += cptr->sendK; ircstp->is_skr += cptr->receiveK; ircstp->is_sti += timeofday - cptr->firsttime; if (ircstp->is_sbs > 2047) { ircstp->is_sks += (ircstp->is_sbs >> 10); ircstp->is_sbs &= 0x3ff; } if (ircstp->is_sbr > 2047) { ircstp->is_skr += (ircstp->is_sbr >> 10); ircstp->is_sbr &= 0x3ff; } /* schedule a quick reconnect if we've been connected a long time */ if((aconn = find_aConnect_match(cptr->name, cptr->username, cptr->sockhost))) { aconn->hold = time(NULL); aconn->hold += (aconn->hold - cptr->since > HANGONGOODLINK) ? HANGONRETRYDELAY : aconn->class->connfreq; if (nextconnect > aconn->hold) nextconnect = aconn->hold; } } else if (IsClient(cptr)) { ircstp->is_cl++; ircstp->is_cbs += cptr->sendB; ircstp->is_cbr += cptr->receiveB; ircstp->is_cks += cptr->sendK; ircstp->is_ckr += cptr->receiveK; ircstp->is_cti += timeofday - cptr->firsttime; if (ircstp->is_cbs > 2047) { ircstp->is_cks += (ircstp->is_cbs >> 10); ircstp->is_cbs &= 0x3ff; } if (ircstp->is_cbr > 2047) { ircstp->is_ckr += (ircstp->is_cbr >> 10); ircstp->is_cbr &= 0x3ff; } } else ircstp->is_ni++; /* remove outstanding DNS queries. */ del_queries((char *) cptr); if (cptr->authfd >= 0) { del_fd(cptr->authfd); close(cptr->authfd); cptr->authfd = -1; } if (cptr->fd >= 0) { dump_connections(cptr->fd); local[cptr->fd] = NULL; #ifdef HAVE_SSL if(IsSSL(cptr) && cptr->ssl) { SSL_set_shutdown(cptr->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(cptr->ssl); SSL_free(cptr->ssl); cptr->ssl = NULL; } #endif del_fd(cptr->fd); close(cptr->fd); cptr->fd = -2; SBufClear(&cptr->sendQ); SBufClear(&cptr->recvQ); memset(cptr->passwd, '\0', sizeof(cptr->passwd)); if(cptr->lstn) cptr->lstn->clients--; } for (; highest_fd > 0; highest_fd--) if (local[highest_fd]) break; clear_conflinks(cptr); cptr->from = NULL; /* ...this should catch them! >:) --msa */ /* if we're the last socket open on this listener, * check to make sure the listener is even supposed to be * open, and close it if its not -epi */ if (cptr->lstn && (cptr->lstn->clients <= 0) && (cptr->lstn->aport->legal == -1)) close_listener(cptr->lstn); return; } #ifdef MAXBUFFERS /* reset_sock_opts type = 0 = client, 1 = server */ void reset_sock_opts(int fd, int type) { #define CLIENT_BUFFER_SIZE 4096 #define SEND_BUF_SIZE 2920 int opt; opt = type ? rcvbufmax : CLIENT_BUFFER_SIZE; if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) sendto_realops("REsetsockopt(SO_RCVBUF) for fd %d (%s) failed", fd, type ? "server" : "client"); opt = type ? sndbufmax : SEND_BUF_SIZE; if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) sendto_realops("REsetsockopt(SO_SNDBUF) for fd %d (%s) failed", fd, type ? "server" : "client"); } #endif /* MAXBUFFERS */ /* set_sock_opts */ static void set_sock_opts(int fd, aClient * cptr) { socklen_t opt; #ifdef SO_REUSEADDR opt = 1; if (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) silent_report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr); #endif #if defined(SO_DEBUG) && defined(DEBUGMODE) && 0 /* Solaris with SO_DEBUG writes to syslog by default */ #if !defined(SOL20) || defined(USE_SYSLOG) opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0) silent_report_error("setsockopt(SO_DEBUG) %s:%s", cptr); #endif /* SOL20 */ #endif #ifdef SO_USELOOPBACK opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt, sizeof(opt)) < 0) silent_report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr); #endif #ifdef SO_RCVBUF #if defined(MAXBUFFERS) if (rcvbufmax == 0) { socklen_t optlen; optlen = sizeof(rcvbufmax); getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen); while ((rcvbufmax < 16385) && (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) (char *) &rcvbufmax,optlen) >= 0)) rcvbufmax += 1024; getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen); readbuf = (char *) MyMalloc(rcvbufmax * sizeof(char)); } if (IsServer(cptr)) opt = rcvbufmax; else opt = 4096; #else opt = 8192; #endif if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) silent_report_error("setsockopt(SO_RCVBUF) %s:%s", cptr); #endif #ifdef SO_SNDBUF #if defined(MAXBUFFERS) if (sndbufmax == 0) { socklen_t optlen; optlen = sizeof(sndbufmax); getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen); while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, optlen) >= 0)) sndbufmax += 1024; getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen); } if (IsServer(cptr)) opt = sndbufmax; else opt = 4096; #else opt = 8192; #endif if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) silent_report_error("setsockopt(SO_SNDBUF) %s:%s", cptr); #endif #if defined(IP_OPTIONS) && defined(IPPROTO_IP) { #if defined(MAXBUFFERS) char *s = readbuf, *t = readbuf + (rcvbufmax * sizeof(char)) / 2; opt = (rcvbufmax * sizeof(char)) / 8; #else char *s = readbuf, *t = readbuf + sizeof(readbuf) / 2; opt = sizeof(readbuf) / 8; #endif if (getsockopt(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) < 0) silent_report_error("getsockopt(IP_OPTIONS) %s:%s", cptr); else if (opt > 0) { for (*readbuf = '\0'; opt > 0; opt--, s += 3) ircsprintf(s, "%02.2x:", *t++); *s = '\0'; sendto_realops("Connection %s using IP opts: (%s)", get_client_name(cptr, HIDEME), readbuf); } if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, (char *) NULL, 0) < 0) silent_report_error("setsockopt(IP_OPTIONS) %s:%s", cptr); } #endif } static void set_listener_sock_opts(int fd, aListener *lptr) { int opt; #ifdef SO_REUSEADDR opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) report_listener_error("setsockopt(SO_REUSEADDR) %s:%s", lptr); #endif #if defined(SO_DEBUG) && defined(DEBUGMODE) && 0 /* * Solaris with SO_DEBUG writes to syslog by default */ #if !defined(SOL20) || defined(USE_SYSLOG) opt = 1; if (setsockopt(fd, SOL_SOCKET, SO_DEBUG, (char *) &opt, sizeof(opt)) < 0) report_listener_error("setsockopt(SO_DEBUG) %s:%s", lptr); #endif /* SOL20 */ #endif #ifdef SO_USELOOPBACK opt = 1; if (setsockopt (fd, SOL_SOCKET, SO_USELOOPBACK, (char *) &opt, sizeof(opt)) < 0) report_listener_error("setsockopt(SO_USELOOPBACK) %s:%s", lptr); #endif #ifdef SO_RCVBUF # if defined(MAXBUFFERS) if (rcvbufmax == 0) { socklen_t optlen; optlen = sizeof(rcvbufmax); getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen); while ((rcvbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_RCVBUF, (char *) (char *) &rcvbufmax, optlen) >= 0)) rcvbufmax += 1024; getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &rcvbufmax, &optlen); readbuf = (char *) MyMalloc(rcvbufmax * sizeof(char)); } opt = 4096; # else opt = 8192; # endif if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) report_listener_error("setsockopt(SO_RCVBUF) %s:%s", lptr); #endif #ifdef SO_SNDBUF #if defined(MAXBUFFERS) if (sndbufmax == 0) { socklen_t optlen; optlen = sizeof(sndbufmax); getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen); while ((sndbufmax < 16385) && (setsockopt (fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, optlen) >= 0)) sndbufmax += 1024; getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &sndbufmax, &optlen); } opt = 4096; #else opt = 8192; #endif if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) report_listener_error("setsockopt(SO_SNDBUF) %s:%s", lptr); #endif } int get_sockerr(aClient * cptr) { int errtmp = errno, err = 0; socklen_t len = sizeof(err); #ifdef SO_ERROR if (cptr->fd >= 0) if (!getsockopt(cptr->fd, SOL_SOCKET, SO_ERROR, (char *) &err, &len)) if (err) errtmp = err; #endif return errtmp; } char *irc_get_sockerr(aClient *cptr) { if(cptr->sockerr == 0) return "No error"; if(cptr->sockerr > 0) return strerror(cptr->sockerr); switch(cptr->sockerr) { case -1: /* this is the default */ return "Unset error message [this is a bug!]"; case IRCERR_BUFALLOC: return "dbuf allocation error"; case IRCERR_ZIP: return "compression general failure"; default: return "Unknown error!"; } /* unreachable code, but the compiler is complaining.. */ return NULL; } /* * * set_non_blocking * Set the client connection into non-blocking mode. */ void set_non_blocking(int fd, aClient * cptr) { int res, nonb = 0; nonb |= O_NONBLOCK; if ((res = fcntl(fd, F_GETFL, 0)) == -1) silent_report_error("fcntl(fd, F_GETFL) failed for %s:%s", cptr); else if (fcntl(fd, F_SETFL, res | nonb) == -1) silent_report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", cptr); return; } void set_listener_non_blocking(int fd, aListener *lptr) { int res, nonb = 0; nonb |= O_NONBLOCK; if ((res = fcntl(fd, F_GETFL, 0)) == -1) report_listener_error("fcntl(fd, F_GETFL) failed for %s:%s", lptr); else if (fcntl(fd, F_SETFL, res | nonb) == -1) report_listener_error("fcntl(fd, F_SETL, nonb) failed for %s:%s", lptr); return; } /* * 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 added to the linked list of clients but isnt added to any hash * tables yuet since it doesnt have a name. */ aClient *add_connection(aListener *lptr, int fd) { Link lin; aClient *acptr = NULL; char *s, *t; struct sockaddr_in addr; socklen_t len = sizeof(struct sockaddr_in); struct userBan *ban; if (getpeername(fd, (struct sockaddr *) &addr, &len) == -1) { ircstp->is_ref++; close(fd); return NULL; } acptr = make_client(NULL, &me); /* * Copy ascii address to 'sockhost' just in case. Then we have * something valid to put into error messages... */ get_sockhost(acptr, (char *) inetntoa((char *) &addr.sin_addr)); memcpy((char *) &acptr->ip, (char *) &addr.sin_addr, sizeof(struct in_addr)); acptr->port = ntohs(addr.sin_port); /* * Check that this socket (client) is allowed to accept * connections from this IP#. */ for (s = (char *) &lptr->allow_ip, t = (char *) &acptr->ip, len = 4; len > 0; len--, s++, t++) { if (!*s) continue; if (*s != *t) break; } if (len) { ircstp->is_ref++; acptr->fd = -2; free_client(acptr); close(fd); return NULL; } lptr->ccount++; lptr->clients++; Count.unknown++; add_fd(fd, FDT_CLIENT, acptr); local[fd] = acptr; acptr->fd = fd; if (fd > highest_fd) highest_fd = fd; /* sockets inherit the options of their parents.. do we need these? */ set_non_blocking(acptr->fd, acptr); set_sock_opts(acptr->fd, acptr); acptr->lstn = lptr; add_client_to_list(acptr); ban = check_userbanned(acptr, UBAN_IP|UBAN_CIDR4|UBAN_WILDUSER, 0); if(ban) { char *reason, *ktype; int local; local = (ban->flags & UBAN_LOCAL) ? 1 : 0; ktype = local ? LOCAL_BANNED_NAME : NETWORK_BANNED_NAME; reason = ban->reason ? ban->reason : ktype; sendto_one(acptr, err_str(ERR_YOUREBANNEDCREEP), me.name, "*", ktype); sendto_one(acptr, ":%s NOTICE * :*** You are not welcome on this %s.", me.name, local ? "server" : "network"); sendto_one(acptr, ":%s NOTICE * :*** %s for %s", me.name, ktype, reason); sendto_one(acptr, ":%s NOTICE * :*** Your IP is %s", me.name, inetntoa((char *)&acptr->ip.s_addr)); sendto_one(acptr, ":%s NOTICE * :*** For assistance, please email %s" " and include everything shown here.", me.name, local ? Local_Kline_Address : Network_Kline_Address); ircstp->is_ref++; ircstp->is_ref_1++; throttle_force(inetntoa((char *)&acptr->ip.s_addr)); exit_client(acptr, acptr, &me, reason); return NULL; } if(call_hooks(CHOOK_PREACCESS, acptr) == FLUSH_BUFFER) return NULL; #ifdef SHOW_HEADERS sendto_one(acptr, REPORT_DO_DNS); #endif lin.flags = ASYNC_CLIENT; lin.value.cptr = acptr; Debug((DEBUG_DNS, "lookup %s", inetntoa((char *) &addr.sin_addr))); acptr->hostp = gethost_byaddr((char *) &acptr->ip, &lin); if (!acptr->hostp) SetDNS(acptr); #ifdef SHOW_HEADERS else sendto_one(acptr, REPORT_FIN_DNSC); #endif nextdnscheck = 1; #ifdef DO_IDENTD start_auth(acptr); #endif check_client_fd(acptr); #ifdef HAVE_SSL if (IsSSL(lptr)) { acptr->ssl = NULL; /* If for some reason we fail to create a new SSL object, * notify the debug ops and reject the client. */ if((acptr->ssl = SSL_new(CTX_Server)) == NULL) { sendto_realops_lev(DEBUG_LEV, "SSL_new() object failed [client %s]", acptr->sockhost); ircstp->is_ref++; acptr->fd = -2; free_client(acptr); return NULL ; } SetSSL(acptr); SSL_set_fd((SSL *)acptr->ssl, fd); set_non_blocking(fd, acptr); set_sock_opts(fd, acptr); if (!safe_SSL_accept(acptr, fd)) { SSL_set_shutdown(acptr->ssl, SSL_RECEIVED_SHUTDOWN); SSL_smart_shutdown(acptr->ssl); SSL_free(acptr->ssl); ircstp->is_ref++; acptr->fd = -2; free_client(acptr); (void) close(fd); return NULL; } } #endif return acptr; } /* handle taking care of the client's recvq here */ int do_client_queue(aClient *cptr) { int dolen = 0, done; while (SBufLength(&cptr->recvQ) && !NoNewLine(cptr) && ((cptr->status < STAT_UNKNOWN) || (cptr->since - timeofday < 10) || IsNegoServer(cptr))) { /* If it's become registered as a server, just parse the whole block */ if (IsServer(cptr) || IsNegoServer(cptr)) { #if defined(MAXBUFFERS) dolen = sbuf_get(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char)); #else dolen = sbuf_get(&cptr->recvQ, readbuf, sizeof(readbuf)); #endif if (dolen <= 0) break; if ((done = dopacket(cptr, readbuf, dolen))) return done; break; } #if defined(MAXBUFFERS) dolen = sbuf_getmsg(&cptr->recvQ, readbuf, rcvbufmax * sizeof(char)); #else dolen = sbuf_getmsg(&cptr->recvQ, readbuf, sizeof(readbuf)); #endif if (dolen <= 0) { if (dolen < 0) return exit_client(cptr, cptr, cptr, "sbuf_getmsg fail"); if (SBufLength(&cptr->recvQ) < 510) { cptr->flags |= FLAGS_NONL; break; } /* The buffer is full (more than 512 bytes) and it has no \n * Some user is trying to trick us. Kill their recvq. */ SBufClear(&cptr->recvQ); break; } else if(client_dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER) return FLUSH_BUFFER; } if(!(cptr->flags & FLAGS_HAVERECVQ) && SBufLength(&cptr->recvQ) && !NoNewLine(cptr)) { add_to_list(&recvq_clients, cptr); cptr->flags |= FLAGS_HAVERECVQ; } return 1; } /* * read_packet * * Read a 'packet' of data from a connection and process it. Read in 8k * chunks to give a better performance rating (for server connections). * Do some tricky stuff for client connections to make sure they don't * do any flooding >:-) -avalon */ #define MAX_CLIENT_RECVQ 8192 int read_packet(aClient * cptr) { int length = 0, done; /* If data is ready, and the user is either not a person or * is a person and has a recvq of less than MAX_CLIENT_RECVQ, * read from this client */ if (!(IsPerson(cptr) && SBufLength(&cptr->recvQ) > MAX_CLIENT_RECVQ)) { errno = 0; #ifdef HAVE_SSL #if defined ( MAXBUFFERS ) if (IsPerson(cptr)) length = RECV_CHECK_SSL(cptr, readbuf, 8192 * sizeof(char)); else length = RECV_CHECK_SSL(cptr, readbuf, rcvbufmax * sizeof(char)); #else length = RECV_CHECK_SSL(cptr, readbuf, sizeof(readbuf)); #endif #else #if defined(MAXBUFFERS) if (IsPerson(cptr)) length = recv(cptr->fd, readbuf, 8192 * sizeof(char), 0); else length = recv(cptr->fd, readbuf, rcvbufmax * sizeof(char), 0); #else length = recv(cptr->fd, readbuf, sizeof(readbuf), 0); #endif #endif cptr->lasttime = timeofday; if (cptr->lasttime > cptr->since) cptr->since = cptr->lasttime; cptr->flags &= ~(FLAGS_PINGSENT | FLAGS_NONL); /* If not ready, fake it so it isnt closed */ if (length == -1 && ((errno == EWOULDBLOCK) || (errno == EAGAIN))) return 1; if (length <= 0) { cptr->sockerr = length ? errno : 0; return length; } } /* * For server connections, we process as many as we can without * worrying about the time of day or anything :) */ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr) || IsNegoServer(cptr)) { if (length > 0) if ((done = dopacket(cptr, readbuf, length))) return done; } else { /* * Before we even think of parsing what we just read, stick * it on the end of the receive queue and do it when its turn * comes around. */ if (sbuf_put(&cptr->recvQ, readbuf, length) < 0) return exit_client(cptr, cptr, cptr, "sbuf_put fail"); if (IsPerson(cptr) && #ifdef NO_OPER_FLOOD !IsAnOper(cptr) && #endif SBufLength(&cptr->recvQ) > CLIENT_FLOOD) { sendto_realops_lev(FLOOD_LEV, "Flood -- %s!%s@%s (%d) Exceeds %d" " RecvQ", cptr->name[0] ? cptr->name : "*", cptr->user ? cptr->user->username : "*", cptr->user ? cptr->user->host : "*", SBufLength(&cptr->recvQ), CLIENT_FLOOD); return exit_client(cptr, cptr, cptr, "Excess Flood"); } return do_client_queue(cptr); } return 1; } void read_error_exit(aClient *cptr, int length, int err) { char fbuf[512]; char errmsg[512]; if (IsServer(cptr) || IsHandshake(cptr) || IsConnecting(cptr)) { if (length == 0) { char *errtxt = "Server %s closed the connection"; ircsprintf(fbuf, "from %s: %s", me.name, errtxt); sendto_gnotice(fbuf, get_client_name(cptr, HIDEME)); ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt); sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME)); } else { char *errtxt = (IsConnecting(cptr) || IsHandshake(cptr)) ? "Connect error to %s (%s)" : "Read error from %s, closing link (%s)"; ircsprintf(fbuf, "from %s: %s", me.name, errtxt); sendto_gnotice(fbuf, get_client_name(cptr, HIDEME), strerror(err)); ircsprintf(fbuf, ":%s GNOTICE :%s", me.name, errtxt); sendto_serv_butone(cptr, fbuf, get_client_name(cptr, HIDEME), strerror(err)); } } if (err) ircsprintf(errmsg, "Read error: %s", strerror(err)); else ircsprintf(errmsg, "Client closed connection"); exit_client(cptr, cptr, &me, errmsg); } void accept_connection(aListener *lptr) { static struct sockaddr_in addr; socklen_t addrlen = sizeof(struct sockaddr_in); char host[HOSTLEN + 2]; int newfd; int i; lptr->lasttime = timeofday; for (i = 0; i < 100; i++) /* accept up to 100 times per call * to deal with high connect rates */ { if((newfd = accept(lptr->fd, (struct sockaddr *) &addr, &addrlen)) < 0) { switch(errno) { #ifdef EMFILE case EMFILE: report_listener_error("Cannot accept connections %s:%s", lptr); break; #endif #ifdef ENFILE case ENFILE: report_listener_error("Cannot accept connections %s:%s", lptr); break; #endif } return; } strncpyzt(host, (char *) inetntoa((char *) &addr.sin_addr), sizeof(host)); /* if they are throttled, drop them silently. */ if (throttle_check(host, newfd, NOW) == 0) { ircstp->is_ref++; ircstp->is_throt++; close(newfd); return; } if (newfd >= MAX_ACTIVECONN) { ircstp->is_ref++; sendto_realops_lev(CCONN_LEV,"All connections in use. fd: %d (%s)", newfd,get_listener_name(lptr)); send(newfd, "ERROR :All connections in use\r\n", 32, 0); close(newfd); return; } if((lptr->aport->legal == -1)) { ircstp->is_ref++; send(newfd, "ERROR :This port is closed\r\n", 29, 0); close(newfd); return; } ircstp->is_ac++; add_connection(lptr, newfd); #ifdef PINGNAZI nextping = timeofday; #endif } } int readwrite_client(aClient *cptr, int isread, int iswrite) { /* * NOTE * We now do this in a more logical way. * We request a write poll on a socket for two reasons * - the socket is waiting for a connect() call * - the socket is blocked */ #ifdef HAVE_SSL if (cptr->ssl != NULL && IsSSL(cptr) && !SSL_is_init_finished(cptr->ssl)) { if(IsDead(cptr) || (!safe_SSL_accept(cptr, cptr->fd))) close_connection(cptr); return 1; } #endif if(iswrite) { if (IsConnecting(cptr) && completed_connection(cptr)) { char errmsg[512]; ircsprintf(errmsg, "Connect Error: %s", irc_get_sockerr(cptr)); return exit_client(cptr, cptr, &me, errmsg); } if(cptr->flags & FLAGS_BLOCKED) { cptr->flags &= ~FLAGS_BLOCKED; unset_fd_flags(cptr->fd, FDF_WANTWRITE); } else { /* this may be our problem with occational 100% cpu looping * we've experienced. jason suggested this, here we will try * this and see if it happens at all -epi */ sendto_realops_lev(DEBUG_LEV, "Removing socket %d: reported ready" " for write, but not blocking", cptr->fd); /* This unset_fd_flags() does not appear to make any difference * to the write set. The socket appears stuck, and there has * to be a reason for it. Since we're experiencing a very low * number of these, simply drop the client entirely, and treat * this as a socket handling error. This is essentially a kludge * however tracking down this bug will take a serious amount of * time and testing - since its not easily reproducable. This * will in the meantime prevent maxing the CPU. -epi * * unset_fd_flags(cptr->fd, FDF_WANTWRITE); */ exit_client(cptr, cptr, &me, "Socket error (write)"); return FLUSH_BUFFER; } } if (isread) { int length = read_packet(cptr); if(length == FLUSH_BUFFER) return length; if(length <= 0) { read_error_exit(cptr, length, cptr->sockerr); return FLUSH_BUFFER; } } if (IsDead(cptr)) { char errmsg[512]; ircsprintf(errmsg, "Write Error: %s", (cptr->flags & FLAGS_SENDQEX) ? "SendQ Exceeded" : irc_get_sockerr(cptr)); return exit_client(cptr, cptr, &me, errmsg); } return 1; } /* connect_server */ int connect_server(aConnect *aconn, aClient * by, struct hostent *hp) { struct sockaddr *svp; aClient *cptr, *c2ptr; char *s; int errtmp, len; Debug((DEBUG_NOTICE, "Connect to %s[%s] @%s", aconn->name, aconn->host, inetntoa((char *) &aconn->ipnum))); if ((c2ptr = find_server(aconn->name, NULL))) { sendto_ops("Server %s already present from %s", aconn->name, get_client_name(c2ptr, HIDEME)); if (by && IsPerson(by) && !MyClient(by)) sendto_one(by, ":%s NOTICE %s :Server %s already present from %s", me.name, by->name, aconn->name, get_client_name(c2ptr, HIDEME)); return -1; } /* * If we dont know the IP# for this host and itis a hostname and not * a ip# string, then try and find the appropriate host record. */ if ((!aconn->ipnum.s_addr)) { Link lin; lin.flags = ASYNC_CONNECT; lin.value.aconn = aconn; nextdnscheck = 1; s = (char *) strchr(aconn->host, '@'); s++; /* should NEVER be NULL */ if ((aconn->ipnum.s_addr = inet_addr(s)) == -1) { aconn->ipnum.s_addr = 0; hp = gethost_byname(s, &lin); Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s", hp, aconn, aconn->name, s)); if (!hp) return 0; memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr)); } } cptr = make_client(NULL, &me); cptr->hostp = hp; /* Copy these in so we have something for error detection. */ strncpyzt(cptr->name, aconn->name, sizeof(cptr->name)); strncpyzt(cptr->sockhost, aconn->host, HOSTLEN + 1); svp = connect_inet(aconn, cptr, &len); if (!svp) { if (cptr->fd >= 0) close(cptr->fd); cptr->fd = -2; free_client(cptr); return -1; } set_non_blocking(cptr->fd, cptr); set_sock_opts(cptr->fd, cptr); signal(SIGALRM, dummy); if (connect(cptr->fd, svp, len) < 0 && errno != EINPROGRESS) { errtmp = errno; /* other system calls may eat errno */ report_error("Connect to host %s failed: %s", cptr); if (by && IsPerson(by) && !MyClient(by)) sendto_one(by, ":%s NOTICE %s :Connect to server %s failed.", me.name, by->name, cptr->name); close(cptr->fd); cptr->fd = -2; free_client(cptr); errno = errtmp; if (errno == EINTR) errno = ETIMEDOUT; return -1; } make_server(cptr); cptr->serv->aconn = aconn; /* The socket has been connected or connect is in progress. */ if (by && IsPerson(by)) { strcpy(cptr->serv->bynick, by->name); strcpy(cptr->serv->byuser, by->user->username); strcpy(cptr->serv->byhost, by->user->host); } else { strcpy(cptr->serv->bynick, "AutoConn."); *cptr->serv->byuser = '\0'; *cptr->serv->byhost = '\0'; } cptr->serv->up = me.name; if (cptr->fd > highest_fd) highest_fd = cptr->fd; local[cptr->fd] = cptr; SetConnecting(cptr); get_sockhost(cptr, aconn->host); add_client_to_list(cptr); #ifdef PINGNAZI nextping = timeofday; #endif add_fd(cptr->fd, FDT_CLIENT, cptr); cptr->flags |= FLAGS_BLOCKED; set_fd_flags(cptr->fd, FDF_WANTREAD|FDF_WANTWRITE); return 0; } static struct sockaddr * connect_inet(aConnect *aconn, aClient *cptr, int *lenp) { static struct sockaddr_in server; struct hostent *hp; struct sockaddr_in sin; /* * Might as well get sockhost from here, the connection is attempted * with it so if it fails its useless. */ cptr->fd = socket(AF_INET, SOCK_STREAM, 0); if (cptr->fd >= MAX_ACTIVECONN) { sendto_realops("No more connections allowed (%s)", cptr->name); return NULL; } memset((char *) &server, '\0', sizeof(server)); memset((char *) &sin, '\0', sizeof(sin)); server.sin_family = sin.sin_family = AF_INET; get_sockhost(cptr, aconn->host); if (aconn->source) sin.sin_addr.s_addr = inet_addr(aconn->source); if (cptr->fd < 0) { report_error("opening stream socket to server %s:%s", cptr); cptr->fd = -2; return NULL; } /* * Bind to a local IP# (with unknown port - let unix decide) so * * we have some chance of knowing the IP# that gets used for a host * * with more than one IP#. * * No we don't bind it, not all OS's can handle connecting with an * already bound socket, different ip# might occur anyway leading to * a freezing select() on this side for some time. */ if (aconn->source) { /* * * No, we do bind it if we have virtual host support. If we * don't explicitly bind it, it will default to IN_ADDR_ANY and * we lose due to the other server not allowing our base IP * --smg */ if (bind(cptr->fd, (struct sockaddr *) &sin, sizeof(sin)) == -1) { report_error("error binding to local port for %s:%s", cptr); close(cptr->fd); return NULL; } } /* * By this point we should know the IP# of the host listed in the * conf line, whether as a result of the hostname lookup or the ip# * being present instead. If we dont know it, then the connect * fails. */ if (IsDigit(*aconn->host) && (aconn->ipnum.s_addr == -1)) aconn->ipnum.s_addr = inet_addr(aconn->host); if (aconn->ipnum.s_addr == -1) { hp = cptr->hostp; if (!hp) { Debug((DEBUG_FATAL, "%s: unknown host", aconn->host)); return NULL; } memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr)); } memcpy((char *) &server.sin_addr, (char *) &aconn->ipnum, sizeof(struct in_addr)); memcpy((char *) &cptr->ip, (char *) &aconn->ipnum, sizeof(struct in_addr)); server.sin_port = htons((aconn->port > 0) ? aconn->port : PORTNUM); *lenp = sizeof(server); return (struct sockaddr *) &server; } /* * find the real hostname for the host running the server (or one * which matches the server's name) and its primary IP#. Hostname is * stored in the client structure passed as a pointer. */ void get_my_name(aClient * cptr, char *name, int len) { static char tmp[HOSTLEN + 1]; struct hostent *hp; /* * The following conflicts with both AIX and linux prototypes oh * well, we can put up with the errors from other systems -Dianora */ char *cname = cptr->name; /* Setup local socket structure to use for binding to. */ memset((char *) &mysk, '\0', sizeof(mysk)); mysk.sin_family = AF_INET; if (gethostname(name, len) == -1) return; name[len] = '\0'; /* assume that a name containing '.' is a FQDN */ if (!strchr(name, '.')) add_local_domain(name, len - strlen(name)); /* * If hostname gives another name than cname, then check if there * is a CNAME record for cname pointing to hostname. If so accept * cname as our name. meLazy */ if (BadPtr(cname)) return; if ((hp = gethostbyname(cname)) || (hp = gethostbyname(name))) { char *hname; int i = 0; for (hname = hp->h_name; hname; hname = hp->h_aliases[i++]) { strncpyzt(tmp, hname, sizeof(tmp)); add_local_domain(tmp, sizeof(tmp) - strlen(tmp)); /* * Copy the matching name over and store the 'primary' IP# * as 'myip' which is used later for making the right one is * used for connecting to other hosts. */ if (!mycmp(me.name, tmp)) break; } if (mycmp(me.name, tmp)) strncpyzt(name, hp->h_name, len); else strncpyzt(name, tmp, len); memcpy((char *) &mysk.sin_addr, hp->h_addr, sizeof(struct in_addr)); Debug((DEBUG_DEBUG, "local name is %s", get_client_name(&me, TRUE))); } return; } /* * do_dns_async * * Called when the fd returned from init_resolver() has been selected for * reading. */ void do_dns_async() { static Link ln; aClient *cptr; aConnect *aconn; struct hostent *hp; int bytes, packets = 0; do { ln.flags = -1; hp = get_res((char *) &ln); Debug((DEBUG_DNS, "%#x = get_res(%d,%#x)", hp, ln.flags, ln.value.cptr)); switch (ln.flags) { case ASYNC_NONE: /* * no reply was processed that was outstanding or had * a client still waiting. */ break; case ASYNC_CLIENT: if ((cptr = ln.value.cptr)) { del_queries((char *) cptr); #ifdef SHOW_HEADERS sendto_one(cptr, REPORT_FIN_DNS); #endif ClearDNS(cptr); cptr->hostp = hp; check_client_fd(cptr); } break; case ASYNC_CONNECT: aconn = ln.value.aconn; if (hp && aconn) { memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr)); connect_server(aconn, NULL, hp); } else sendto_ops("Connect to %s failed: host lookup", (aconn) ? aconn->host : "unknown"); break; case ASYNC_CONF: aconn = ln.value.aconn; if (hp && aconn) memcpy((char *) &aconn->ipnum, hp->h_addr, sizeof(struct in_addr)); break; default: break; } if (ioctl(resfd, FIONREAD, &bytes) == -1) bytes = 0; packets++; } while ((bytes > 0) && (packets < 512)); } u_long memcount_s_bsd(MCs_bsd *mc) { aListener *lptr; mc->file = __FILE__; for (lptr = listen_list; lptr; lptr = lptr->next) { mc->listeners.c++; mc->listeners.m += sizeof(*lptr); } mc->total.c += mc->listeners.c; mc->total.m += mc->listeners.m; mc->s_local.c = sizeof(local)/sizeof(local[0]); mc->s_local.m = sizeof(local); mc->s_readbuf.c = 1; #ifndef MAXBUFFERS mc->s_readbuf.m = sizeof(readbuf); #else mc->s_readbuf.m = rcvbufmax; mc->total.c += mc->s_readbuf.c; mc->total.m += mc->s_readbuf.m; #endif return mc->total.m; }