/* ************************************************************************ */ int BlockHeapFree(BlockHeap *bh, void *ptr) { Block *walker; unsigned long ctr; unsigned long bitmask; if (bh == NULL) { #if defined(SYSLOG_BLOCK_ALLOCATOR) log(L_NOTICE,"blalloc.c bh == NULL"); #endif return 1; } for (walker = bh->base; walker != NULL; walker = walker->next) { if ((ptr >= walker->elems) && (ptr <= walker->endElem)) { ctr = ((unsigned long) ptr - (unsigned long) (walker->elems)) / (unsigned long )bh->elemSize; bitmask = 1L << (ctr % (sizeof(long) * 8)); ctr = ctr / (sizeof(long) * 8); /* Flip the right allocation bit */ /* Complain if the bit is already clear, something is wrong * (typically, someone freed the same block twice) */ if( (walker->allocMap[ctr] & bitmask) == 0 ) { #ifdef DEBUG_BLOCK_ALLOCATOR log(L_WARN, "blalloc.c bit already clear in map caller %s %d", BH_CurrentFile, BH_CurrentLine); sendto_ops("blalloc.c bit already clear in map elemSize %d caller %s %d", bh->elemSize, BH_CurrentFile, BH_CurrentLine); sendto_ops("Please report to the hybrid team! [email protected]"); #endif /* DEBUG_BLOCK_ALLOCATOR */ } else { walker->allocMap[ctr] = walker->allocMap[ctr] & ~bitmask; walker->freeElems++; bh->freeElems++; } return 0; } } return 1; }
/* * m_lnotice (send notice to all local users) * parv[0] = sender prefix * parv[1] = message text */ int m_lnotice(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { char* message; struct Client *acptr; message = parc > 1 ? parv[1] : NULL; if (EmptyString(message)) { sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "LNOTICE"); return 0; } if (MyClient(sptr) && !IsAnOper(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return(0); } for(acptr = local_cptr_list; acptr; acptr = acptr->next_local_client) { sendto_one(acptr,":%s NOTICE %s :%s", me.name, acptr->name, parv[1]); } sendto_ops("LNOTICE sent by \2%s\2",parv[0]); sendto_serv_butone(&me, ":%s GLOBOPS :LNOTICE on %s :%s", me.name, sptr->name, parv[1]); return 0; }
/* * Attempt to send a sequence of bytes to the connection. * Returns * * < 0 Some fatal error occurred, (but not EWOULDBLOCK). * This return is a request to close the socket and * clean up the link. * * >= 0 No real error occurred, returns the number of * bytes actually transferred. EWOULDBLOCK and other * possibly similar conditions should be mapped to * zero return. Upper level routine will have to * decide what to do with those unwritten bytes... * * *NOTE* alarm calls have been preserved, so this should * work equally well whether blocking or non-blocking * mode is used... */ int deliver_it(aClient *cptr, char *str, int len) { int retval; aClient *acpt = cptr->acpt; #ifdef DEBUGMODE writecalls++; #endif if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) && !IsUnknown(cptr))) { str[len]='\0'; sendto_ops("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr)?"DEAD":"", str); return -1; } retval = socket_write(cptr->sock, str, len); /* * Convert WOULDBLOCK to a return of "0 bytes moved". This * should occur only if socket was non-blocking. Note, that * all is Ok, if the 'write' just returns '0' instead of an * error and errno=EWOULDBLOCK. */ #ifdef DEBUGMODE if (retval < 0) { writeb[0]++; Debug((DEBUG_ERROR,"write error (%s) to %s", strerror(errno), cptr->name)); } else if (retval == 0) writeb[1]++; else if (retval < 16) writeb[2]++; else if (retval < 32) writeb[3]++; else if (retval < 64) writeb[4]++; else if (retval < 128) writeb[5]++; else if (retval < 256) writeb[6]++; else if (retval < 512) writeb[7]++; else if (retval < 1024) writeb[8]++; else writeb[9]++; #endif if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
static void gs_error(char *msg) { if (!forked) puts(msg); else sendto_ops("%s", msg); }
int m_svsnoop(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; if (!(IsULine(sptr) && parc > 2)) return 0; /* svsnoop bugfix --binary */ if (hunt_server_token(cptr, sptr, MSG_SVSNOOP, TOK_SVSNOOP, "%s :%s", 1, parc, parv) == HUNTED_ISME) { if (parv[2][0] == '+') { SVSNOOP = 1; sendto_ops("This server has been placed in NOOP mode"); for (acptr = &me; acptr; acptr = acptr->prev) { if (MyClient(acptr) && IsAnOper(acptr)) { if (IsOper(acptr)) { IRCstats.operators--; VERIFY_OPERCOUNT(acptr, "svsnoop"); } if (IsAnOper(acptr)) delfrom_fdlist(acptr->slot, &oper_fdlist); if (IsTotalInvis(acptr)) { sendto_channels_inviso_join(acptr); } acptr->umodes &= ~(UMODE_OPER|UMODE_HELPOP|UMODE_SADMIN|UMODE_ADMIN|UMODE_LOCOP|UMODE_SERVICES|UMODE_NETADMIN); acptr->umodes &= ~(UMODE_WHOIS|UMODE_KIX|UMODE_HIDEOPER|UMODE_HIDEWHOIS|UMODE_TOTALINVIS|UMODE_MODEWALK|UMODE_NOFAKELAG); acptr->oflag = 0; remove_oper_snomasks(acptr); sendto_one(acptr, ":%s NOTICE %s :*** Your OFLAGS have been cleared, because this server has been placed in NOOP mode", me.name, acptr->name); RunHook2(HOOKTYPE_LOCAL_OPER, acptr, 0); } } } else { SVSNOOP = 0; sendto_ops("This server is no longer in NOOP mode"); } } return 0; }
int BlockHeapFree (BlockHeap * bh, void *ptr) { Block *walker; unsigned long ctr; unsigned long bitmask; if (bh == NULL) { #if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR) syslog (LOG_DEBUG, "blalloc.c bh == NULL"); #endif return 1; } for (walker = bh->base; walker != NULL; walker = walker->next) { if ((ptr >= walker->elems) && (ptr <= walker->endElem)) { ctr = ((unsigned long) ptr - (unsigned long) (walker->elems)) / (unsigned long) bh->elemSize; bitmask = 1L << (ctr % (sizeof (long) * 8)); ctr = ctr / (sizeof (long) * 8); /* Flip the right allocation bit * * Complain if the bit is already clear, something is wrong * (typically, someone freed the same block twice) */ if ((walker->allocMap[ctr] & bitmask) == 0) { #if defined(USE_SYSLOG) && defined(SYSLOG_BLOCK_ALLOCATOR) syslog (LOG_DEBUG, "blalloc.c bit already clear in map!"); #endif sendto_ops ("blalloc.c bit already clear in map!"); sendto_ops ("Please report to the development team! [email protected]"); } else { walker->allocMap[ctr] = walker->allocMap[ctr] & ~bitmask; walker->freeElems++; bh->freeElems++; } return 0; } } return 1; }
int m_sdesc(aClient *cptr, aClient *sptr, int parc, char *parv[]) { if (!IsAdmin(sptr) && !IsCoAdmin(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name); return 0; } if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, sptr->name, "SDESC"); return 0; } if (strlen(parv[1]) < 1) if (MyConnect(sptr)) { sendto_one(sptr, ":%s NOTICE %s :*** Nothing to change to (SDESC)", me.name, sptr->name); return 0; } if (strlen(parv[1]) > (REALLEN)) { if (MyConnect(sptr)) { sendto_one(sptr, ":%s NOTICE %s :*** /SDESC Error: \"Server info\" may maximum be %i characters of length", me.name, sptr->name, REALLEN); } return 0; } ircsprintf(sptr->srvptr->info, "%s", parv[1]); sendto_serv_butone_token(cptr, sptr->name, MSG_SDESC, TOK_SDESC, ":%s", parv[1]); if (MyConnect(sptr)) { sendto_one(sptr, ":%s NOTICE %s :Your \"server description\" is now set to be %s - you have to set it manually to undo it", me.name, parv[0], parv[1]); return 0; } sendto_ops("Server description for %s is now '%s' changed by %s", sptr->srvptr->name, sptr->srvptr->info, parv[0]); return 0; }
/* Remove a temporary dccdeny line * parv[0] - sender * parv[1] - file/mask */ DLLFUNC CMD_FUNC(m_undccdeny) { ConfigItem_deny_dcc *p; if (!MyClient(sptr)) return 0; if (!IsAnOper(sptr) || !OPCanDCCDeny(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UNDCCDENY"); return 0; } if (BadPtr(parv[1])) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UNDCCDENY"); return 0; } /* If we find an exact match even if it is a wild card only remove the exact match -- codemastr */ if ((p = Find_deny_dcc(parv[1])) && p->flag.type2 == CONF_BAN_TYPE_TEMPORARY) { sendto_ops("%s removed a temp dccdeny for %s", parv[0], parv[1]); DCCdeny_del(p); return 1; } /* Next search using the wild card -- codemastr */ /* Uncommented by Stskeeps: else if (dcc_del_wild_match(parv[1]) == 1) sendto_ops ("%s removed a temp dccdeny for all dccdenys matching %s", parv[0], parv[1]); */ /* If still no match, give an error */ else sendto_one(sptr, "NOTICE %s :*** Unable to find a temp dccdeny matching %s", parv[0], parv[1]); return 0; }
void server_reboot() { int i; sendto_ops("Aieeeee!!! Restarting server... sbrk(0)-etext: %d", (u_int) sbrk((size_t) 0) - (u_int) sbrk0); Debug((DEBUG_NOTICE, "Restarting server...")); dump_connections(me.fd); /* * fd 0 must be 'preserved' if either the -d or -i options have * been passed to us before restarting. */ #ifdef USE_SYSLOG (void) closelog(); #endif for (i = 3; i < MAXCONNECTIONS; i++) (void) close(i); if (!(bootopt & (BOOT_TTY | BOOT_DEBUG))) (void) close(2); (void) close(1); if ((bootopt & BOOT_CONSOLE)) (void) close(0); if (!(bootopt & (BOOT_INETD))) (void) execv(MYNAME, myargv); #ifdef USE_SYSLOG /* Have to reopen since it has been closed above */ openlog(myargv[0], LOG_PID | LOG_NDELAY, LOG_FACILITY); syslog(LOG_CRIT, "execv(%s,%s) failed: %m\n", MYNAME, myargv[0]); closelog(); #endif Debug((DEBUG_FATAL, "Couldn't restart server: %s", strerror(errno))); exit(-1); }
/* * m_rnotice (send notice to all users on a given server) * parv[0] = sender prefix * parv[1] = target server * parv[2] = message */ int m_rnotice(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { char* message; struct Client *acptr; /* firs check if user has enough privileges */ if (MyClient(sptr) && !IsAnOper(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return(0); } message = parv[2]; /* now check if sufficiente parameters */ if (EmptyString(message) || parc<3 ) { if(MyClient(sptr)) sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RNOTICE"); return 0; } /* check we are the hunted server (send to server if not) */ if(hunt_server (cptr, sptr, ":%s RNOTICE %s :%s", 1, parc, parv) != HUNTED_ISME) return 0; for(acptr = local_cptr_list; acptr; acptr = acptr->next_local_client) { sendto_one(acptr,":%s NOTICE %s :%s", me.name, acptr->name, message); } sendto_ops("RNOTICE sent by \2%s\2",parv[0]); sendto_serv_butone(&me, ":%s GLOBOPS :LNOTICE to %s :%s", sptr->name, parv[1], parv[2]); return 0; }
static void disable_ssl(int do_errors) { if(do_errors) { char buf[384]; unsigned long e; while((e = ERR_get_error())) { ERR_error_string_n(e, buf, sizeof(buf) - 1); sendto_realops("SSL ERROR: %s", buf); } } if(ircdssl_ctx) { SSL_CTX_free(ircdssl_ctx); } sendto_ops("Disabling SSL support due to unrecoverable SSL errors. /REHASH again to retry."); ssl_capable = 0; return; }
/* Add a temporary dccdeny line * * parv[0] - sender * parv[1] - file * parv[2] - reason */ DLLFUNC CMD_FUNC(m_dccdeny) { if (!MyClient(sptr)) return 0; if (!IsAnOper(sptr) || !OPCanDCCDeny(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } /* fixup --Stskeeps */ if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "DCCDENY"); return 0; } if (BadPtr(parv[2])) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "DCCDENY"); return 0; } if (!Find_deny_dcc(parv[1])) { sendto_ops("%s added a temp dccdeny for %s (%s)", parv[0], parv[1], parv[2]); DCCdeny_add(parv[1], parv[2], DCCDENY_HARD, CONF_BAN_TYPE_TEMPORARY); return 0; } else sendto_one(sptr, "NOTICE %s :*** %s already has a dccdeny", parv[0], parv[1]); return 0; }
/* ** deliver_it ** Attempt to send a sequence of bytes to the connection. ** Returns ** ** < 0 Some fatal error occurred, (but not EWOULDBLOCK). ** This return is a request to close the socket and ** clean up the link. ** ** >= 0 No real error occurred, returns the number of ** bytes actually transferred. EWOULDBLOCK and other ** possibly similar conditions should be mapped to ** zero return. Upper level routine will have to ** decide what to do with those unwritten bytes... ** ** *NOTE* alarm calls have been preserved, so this should ** work equally well whether blocking or non-blocking ** mode is used... ** ** *NOTE* I nuked 'em. At the load of current ircd servers ** you can't run with stuff that blocks. And we don't. */ int deliver_it(aClient *cptr, char *str, int len) { int retval; aClient *acpt = cptr->listener; #ifdef DEBUGMODE writecalls++; #endif #ifdef VMS retval = netwrite(cptr->fd, str, len); #else if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) #ifdef USE_SSL && !IsSSLHandshake(cptr) #endif && !IsUnknown(cptr))) { str[len] = '\0'; sendto_ops ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str); return -1; } #ifdef USE_SSL if (cptr->flags & FLAGS_SSL) retval = ircd_SSL_write(cptr, str, len); else #endif retval = send(cptr->fd, str, len, 0); /* ** Convert WOULDBLOCK to a return of "0 bytes moved". This ** should occur only if socket was non-blocking. Note, that ** all is Ok, if the 'write' just returns '0' instead of an ** error and errno=EWOULDBLOCK. ** ** ...now, would this work on VMS too? --msa */ # ifndef _WIN32 if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS)) # else if (retval < 0 && (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAENOBUFS)) # endif { retval = 0; SetBlocked(cptr); } else if (retval > 0) { ClearBlocked(cptr); } #endif #ifdef DEBUGMODE if (retval < 0) { writeb[0]++; Debug((DEBUG_ERROR, "write error (%s) to %s", STRERROR(ERRNO), cptr->name)); } else if (retval == 0) writeb[1]++; else if (retval < 16) writeb[2]++; else if (retval < 32) writeb[3]++; else if (retval < 64) writeb[4]++; else if (retval < 128) writeb[5]++; else if (retval < 256) writeb[6]++; else if (retval < 512) writeb[7]++; else if (retval < 1024) writeb[8]++; else writeb[9]++; #endif if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
/* * start_auth * * 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". */ void start_auth(aClient *cptr) { struct sockaddr_in sock; struct sockaddr_in localaddr; int locallen; Debug((DEBUG_NOTICE,"start_auth(%x) fd %d status %d", cptr, cptr->fd, cptr->status)); if ((cptr->authfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { #ifdef USE_SYSLOG syslog(LOG_ERR, "Unable to create auth socket for %s:%m", get_client_name(cptr,TRUE)); #endif if (!DoingDNS(cptr)) SetAccess(cptr); ircstp->is_abad++; return; } if (cptr->authfd >= MAXCONNECTIONS) { sendto_ops("Can't allocate fd for auth on %s", get_client_name(cptr, TRUE)); (void)close(cptr->authfd); return; } #ifdef SHOW_HEADERS send(cptr->fd, REPORT_DO_ID, R_do_id, 0); #endif set_non_blocking(cptr->authfd, cptr); /* 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 */ locallen = sizeof(struct sockaddr_in); bzero(&localaddr, locallen); getsockname(cptr->fd, (struct sockaddr *)&localaddr, &locallen); localaddr.sin_port = htons(0); if (bind(cptr->authfd, (struct sockaddr *)&localaddr, sizeof(localaddr)) == -1) { report_error("binding auth stream socket %s:%s", cptr); (void)close(cptr->fd); return; } bcopy((char *)&cptr->ip, (char *)&sock.sin_addr, sizeof(struct in_addr)); sock.sin_port = htons(113); sock.sin_family = AF_INET; /* (void)alarm((unsigned)4);*/ if (connect(cptr->authfd, (struct sockaddr *)&sock, sizeof(sock)) == -1 && errno != EINPROGRESS) { ircstp->is_abad++; /* * No error report from this... */ /* (void)alarm((unsigned)0);*/ (void)close(cptr->authfd); cptr->authfd = -1; if (!DoingDNS(cptr)) SetAccess(cptr); #ifdef SHOW_HEADERS send(cptr->fd, REPORT_FAIL_ID, R_fail_id, 0); #endif return; } /* (void)alarm((unsigned)0);*/ cptr->flags |= (FLAGS_WRAUTH|FLAGS_AUTH); if (cptr->authfd > highest_fd) highest_fd = cptr->authfd; return; }
/* * Exit one client, local or remote. Assuming all dependants have * been already removed, and socket closed for local client. */ static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, char *comment) { Link *lp; /* * For a server or user quitting, propogate the information to * other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_ops("ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = YES; #endif exit_server(cptr, sptr, from, comment); #ifdef ALWAYS_SEND_DURING_SPLIT currently_processing_netsplit = NO; #endif return; } else if (!(IsPerson(sptr))) /* * ...this test is *dubious*, would need * some thought.. but for * now it plugs a * nasty hole in the server... --msa */ ; /* Nothing */ else if (sptr->name[0]) { /* ...just clean all others with QUIT... */ /* * If this exit is generated from "m_kill", then there is no * sense in sending the QUIT--KILL's have been sent instead. */ if ((sptr->flags & FLAGS_KILLED) == 0) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->name, comment); } /* * * If a person is on a channel, send a QUIT notice * to every * client (person) on the same channel (so * that the client can * show the "**signoff" message). * (Note: The notice is to the * local clients *only*) */ if (sptr->user) { send_part_to_common_channels(sptr, comment); send_quit_to_common_channels(sptr, comment); while ((lp = sptr->user->channel)) remove_user_from_channel(sptr, lp->value.chptr); clones_remove(sptr); #ifdef RWHO_PROBABILITY probability_remove(sptr); #endif /* Clean up invitefield */ while ((lp = sptr->user->invited)) del_invite(sptr, lp->value.chptr); /* Clean up silences */ while ((lp = sptr->user->silence)) del_silence(sptr, lp->value.cp); remove_dcc_references(sptr); /* again, this is all that is needed */ } } /* Remove sptr from the client list */ if (del_from_client_hash_table(sptr->name, sptr) != 1) { Debug((DEBUG_ERROR, "%#x !in tab %s[%s] %#x %#x %#x %d %d %#x", sptr, sptr->name, sptr->from ? sptr->from->sockhost : "??host", sptr->from, sptr->next, sptr->prev, sptr->fd, sptr->status, sptr->user)); } /* remove user from watchlists */ if(IsRegistered(sptr)) hash_check_watch(sptr, RPL_LOGOFF); remove_client_from_list(sptr); return; }
/* * exit_client * This is old "m_bye". Name changed, because this is not a * protocol function, but a general server utility function. * * This function exits a client of *any* type (user, server, etc) * from this server. Also, this generates all necessary prototol * messages that this exit may cause. * * 1) If the client is a local client, then this implicitly exits * all other clients depending on this connection (e.g. remote * clients having 'from'-field that points to this. * * 2) If the client is a remote client, then only this is exited. * * For convenience, this function returns a suitable value for * m_function return value: * * FLUSH_BUFFER if (cptr == sptr) * 0 if (cptr != sptr) */ int exit_client(aClient *cptr, aClient *sptr, aClient *from, char *comment) { #ifdef FNAME_USERLOG time_t on_for; #endif if (MyConnect(sptr)) { call_hooks(CHOOK_SIGNOFF, sptr); if (IsUnknown(sptr)) Count.unknown--; if (IsAnOper(sptr)) remove_from_list(&oper_list, sptr, NULL); if (sptr->flags & FLAGS_HAVERECVQ) { /* mark invalid, will be deleted in do_recvqs() */ DLink *lp = find_dlink(recvq_clients, sptr); if (lp) lp->flags = -1; } if (IsClient(sptr)) Count.local--; if (IsNegoServer(sptr)) sendto_realops("Lost server %s during negotiation: %s", sptr->name, comment); if (IsServer(sptr)) { Count.myserver--; if (IsULine(sptr)) Count.myulined--; remove_from_list(&server_list, sptr, NULL); if (server_list == NULL) server_was_split = YES; } sptr->flags |= FLAGS_CLOSING; if (IsPerson(sptr)) { Link *lp, *next; LOpts *lopt = sptr->user->lopt; /* poof goes their watchlist! */ hash_del_watch_list(sptr); /* if they have listopts, axe those, too */ if(lopt != NULL) { remove_from_list(&listing_clients, sptr, NULL); for (lp = lopt->yeslist; lp; lp = next) { next = lp->next; MyFree(lp->value.cp); free_link(lp); } for (lp = lopt->nolist; lp; lp = next) { next = lp->next; MyFree(lp->value.cp); free_link(lp); } MyFree(sptr->user->lopt); sptr->user->lopt = NULL; } sendto_realops_lev(CCONN_LEV, "Client exiting: %s (%s@%s) [%s] [%s]", sptr->name, sptr->user->username, sptr->user->host, (sptr->flags & FLAGS_NORMALEX) ? "Client Quit" : comment, sptr->hostip); } #ifdef FNAME_USERLOG on_for = timeofday - sptr->firsttime; #endif #if defined(USE_SYSLOG) && defined(SYSLOG_USERS) if (IsPerson(sptr)) syslog(LOG_NOTICE, "%s (%3d:%02d:%02d): %s!%s@%s %d/%d\n", myctime(sptr->firsttime), on_for / 3600, (on_for % 3600) / 60, on_for % 60, sptr->name, sptr->user->username, sptr->user->host, sptr->sendK, sptr->receiveK); #endif #if defined(FNAME_USERLOG) { char linebuf[300]; static int logfile = -1; static long lasttime; /* * This conditional makes the logfile active only after it's * been created - thus logging can be turned off by removing * the file. * * stop NFS hangs...most systems should be able to open a file in * 3 seconds. -avalon (curtesy of wumpus) * * Keep the logfile open, syncing it every 10 seconds -Taner */ if (IsPerson(sptr)) { if (logfile == -1) { alarm(3); logfile = open(FNAME_USERLOG, O_WRONLY | O_APPEND); alarm(0); } ircsprintf(linebuf, "%s (%3d:%02d:%02d): %s!%s@%s %d/%d\n", myctime(sptr->firsttime), on_for / 3600, (on_for % 3600) / 60, on_for % 60, sptr->name, sptr->user->username, sptr->user->host, sptr->sendK, sptr->receiveK); alarm(3); write(logfile, linebuf, strlen(linebuf)); alarm(0); /* Resync the file evey 10 seconds*/ if (timeofday - lasttime > 10) { alarm(3); close(logfile); alarm(0); logfile = -1; lasttime = timeofday; } } } #endif if (sptr->fd >= 0) { if (cptr != NULL && sptr != cptr) sendto_one(&me, sptr, "ERROR :Closing Link: %s %s (%s)", IsPerson(sptr) ? sptr->sockhost : "0.0.0.0", sptr->name, comment); else sendto_one(&me, sptr, "ERROR :Closing Link: %s (%s)", IsPerson(sptr) ? sptr->sockhost : "0.0.0.0", comment); } /* * * Currently only server connections can have * depending * remote clients here, but it does no * harm to check for all * local clients. In * future some other clients than servers * might * have remotes too... * * * Close the Client connection first and mark it * so that no * messages are attempted to send to it. *, The following *must* * make MyConnect(sptr) == FALSE!). * It also makes sptr->from == * NULL, thus it's unnecessary * to test whether "sptr != acptr" * in the following loops. */ if (IsServer(sptr)) { sendto_ops("%s was connected for %lu seconds. %lu/%lu " "sendK/recvK.", sptr->name, (long)(timeofday - sptr->firsttime), sptr->sendK, sptr->receiveK); #ifdef USE_SYSLOG syslog(LOG_NOTICE, "%s was connected for %lu seconds. %lu/%lu " "sendK/recvK.", sptr->name, (u_long) timeofday - sptr->firsttime, sptr->sendK, sptr->receiveK); #endif close_connection(sptr); sptr->sockerr = 0; sptr->flags |= FLAGS_DEADSOCKET; } else { close_connection(sptr); sptr->sockerr = 0; sptr->flags |= FLAGS_DEADSOCKET; } } exit_one_client(cptr, sptr, from, comment); return cptr == sptr ? FLUSH_BUFFER : 0; }
/* * m_server * parv[0] = sender prefix * parv[1] = servername * parv[2] = serverinfo/hopcount * parv[3] = serverinfo */ int m_server(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int i; char info[REALLEN + 1], *host; aClient *acptr, *bcptr; aConnect *aconn; int hop; char nbuf[HOSTLEN * 2 + USERLEN + 5]; /* same size as in s_misc.c */ info[0] = '\0'; if (parc < 2 || *parv[1] == '\0') { sendto_one(cptr, "ERROR :No servername"); return 0; } hop = 0; host = parv[1]; if (parc > 3 && atoi(parv[2])) { hop = atoi(parv[2]); strncpyzt(info, parv[3], REALLEN + 1); } else if (parc > 2) { strncpyzt(info, parv[2], REALLEN + 1); if ((parc > 3) && ((i = strlen(info)) < (REALLEN - 2))) { strcat(info, " "); strncat(info, parv[3], REALLEN - i - 2); info[REALLEN] = '\0'; } } /* * July 5, 1997 * Rewritten to throw away server cruft from users, * combined the hostname validity test with cleanup of host name, * so a cleaned up hostname can be returned as an error if * necessary. - Dianora */ /* yes, the if(strlen) below is really needed!! */ if (strlen(host) > HOSTLEN) host[HOSTLEN] = '\0'; if (IsPerson(cptr)) { /* A local link that has been identified as a USER tries * something fishy... ;-) */ sendto_one(cptr, err_str(ERR_UNKNOWNCOMMAND), me.name, parv[0], "SERVER"); return 0; } else /* hostile servername check */ { /* * Lets check for bogus names and clean them up we don't bother * cleaning up ones from users, becasuse we will never see them * any more - Dianora */ int bogus_server = 0; int found_dot = 0; char clean_host[(2 * HOSTLEN) + 1]; char *s; char *d; int n; s = host; d = clean_host; n = (2 * HOSTLEN) - 2; while (*s && n > 0) { if ((unsigned char) *s < (unsigned char) ' ') /* Is it a control character? */ { bogus_server = 1; *d++ = '^'; *d++ = (char) ((unsigned char) *s + 0x40); /* turn it into a printable */ n -= 2; } else if ((unsigned char) *s > (unsigned char) '~') { bogus_server = 1; *d++ = '.'; n--; } else { if (*s == '.') found_dot = 1; *d++ = *s; n--; } s++; } *d = '\0'; if ((!found_dot) || bogus_server) { sendto_one(sptr, "ERROR :Bogus server name (%s)", clean_host); return exit_client(cptr, cptr, cptr, "Bogus server name"); } } /* new connection */ if (IsUnknown(cptr) || IsHandshake(cptr)) { strncpyzt(cptr->name, host, sizeof(cptr->name)); strncpyzt(cptr->info, info[0] ? info : me.name, REALLEN + 1); cptr->hopcount = hop; switch (check_server_init(cptr)) { case 0: return m_server_estab(cptr); case 1: sendto_ops("Access check for %s in progress", get_client_name(cptr, HIDEME)); return 1; default: ircstp->is_ref++; sendto_ops_lev(ADMIN_LEV, "Link %s dropped, no Connect block", get_client_name(cptr, TRUE)); return exit_client(cptr, cptr, cptr, "No Connect block"); } } /* already linked server */ if (!IsServer(cptr)) return 0; if ((acptr = find_name(host, NULL))) { /* * * This link is trying feed me a server that I already have * access through another path -- multiple paths not accepted * currently, kill this link immediately!! * * Rather than KILL the link which introduced it, KILL the * youngest of the two links. -avalon */ bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr : acptr->from; sendto_one(bcptr, "ERROR :Server %s already exists", host); if (bcptr == cptr) { /* Don't complain for servers that are juped */ /* (don't complain if the server that already exists is U: lined, unless I actually have a .conf U: line for it */ if(!IsULine(acptr) || find_aUserver(acptr->name)) { sendto_gnotice("from %s: Link %s cancelled, server %s already " "exists", me.name, get_client_name(bcptr, HIDEME), host); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, " "server %s already exists", me.name, get_client_name(bcptr, HIDEME), host); } return exit_client(bcptr, bcptr, &me, "Server Exists"); } /* inform all those who care (set +n) -epi */ strcpy(nbuf, get_client_name(bcptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, server %s reintroduced " "by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s " "reintroduced by %s", me.name, nbuf, host, get_client_name(cptr, HIDEME)); exit_client(bcptr, bcptr, &me, "Server Exists"); } /* * The following if statement would be nice to remove since user * nicks never have '.' in them and servers must always have '.' in * them. There should never be a server/nick name collision, but it * is possible a capricious server admin could deliberately do * something strange. * * -Dianora */ if ((acptr = find_client(host, NULL)) && acptr != cptr) { /* * * Server trying to use the same name as a person. Would * cause a fair bit of confusion. Enough to make it hellish for * a while and servers to send stuff to the wrong place. */ sendto_one(cptr, "ERROR :Nickname %s already exists!", host); strcpy(nbuf, get_client_name(cptr, HIDEME)); sendto_gnotice("from %s: Link %s cancelled, servername/nick collision", me.name, nbuf); sendto_serv_butone(cptr, ":%s GNOTICE :Link %s cancelled, " "servername/nick collision", me.name, nbuf); return exit_client(cptr, cptr, cptr, "Nick as Server"); } if (IsServer(cptr)) { /* * * Server is informing about a new server behind this link. * Create REMOTE server structure, add it to list and propagate * word to my other server links... */ if (parc == 1 || info[0] == '\0') { sendto_one(cptr, "ERROR :No server info specified for %s", host); return 0; } /* * * See if the newly found server is behind a guaranteed leaf * (L-line). If so, close the link. * * Depreciated. Kinda redundant with Hlines. -epi */ if (!(cptr->serv->aconn->flags & CONN_HUB)) { aconn = cptr->serv->aconn; sendto_gnotice("from %s: Non-Hub link %s introduced %s", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr,":%s GNOTICE :Non-Hub link %s introduced " "%s", me.name, get_client_name(cptr, HIDEME), host); sendto_one(cptr, "ERROR :You're not a hub (introducing %s)", host); return exit_client(cptr, cptr, cptr, "Too many servers"); } acptr = make_client(cptr, sptr); make_server(acptr); acptr->hopcount = hop; strncpyzt(acptr->name, host, sizeof(acptr->name)); strncpyzt(acptr->info, info, REALLEN + 1); acptr->serv->up = find_or_add(parv[0]); fakelinkserver_update(acptr->name, acptr->info); SetServer(acptr); /* * if this server is behind a U-lined server, make it U-lined as * well. - lucas */ if (IsULine(sptr) || find_aUserver(acptr->name)) { acptr->flags |= FLAGS_ULINE; sendto_realops_lev(DEBUG_LEV, "%s introducing super server %s", cptr->name, acptr->name); } Count.server++; add_client_to_list(acptr); add_to_client_hash_table(acptr->name, acptr); /* * Old sendto_serv_but_one() call removed because we now need * to send different names to different servers (domain name matching) */ for (i = 0; i <= highest_fd; i++) { if (!(bcptr = local[i]) || !IsServer(bcptr) || bcptr == cptr || IsMe(bcptr)) continue; if (!(aconn = bcptr->serv->aconn)) { sendto_gnotice("from %s: Lost Connect block for %s on %s." " Closing", me.name, get_client_name(cptr, HIDEME), host); sendto_serv_butone(cptr, ":%s GNOTICE :Lost Connect block for" " %s on %s. Closing", me.name, get_client_name(cptr, HIDEME), host); return exit_client(cptr, cptr, cptr, "Lost Connect block"); } if (match(my_name_for_link(me.name, aconn), acptr->name) == 0) continue; sendto_one(bcptr, ":%s SERVER %s %d :%s", parv[0], acptr->name, hop + 1, acptr->info); } return 0; } return 0; }
static time_t io_loop(time_t delay) { static char to_send[200]; static time_t lasttime = 0; static long lastrecvK = 0; static int lrv = 0; time_t lasttimeofday; lasttimeofday = CurrentTime; if (CurrentTime < lasttimeofday) { ircsprintf(to_send, "System clock is running backwards - (%d < %d)", CurrentTime, lasttimeofday); report_error(to_send, me.name, 0); } if(!next_gc) { next_gc = CurrentTime + 600; } /* * This chunk of code determines whether or not * "life sucks", that is to say if the traffic * level is so high that standard server * commands should be restricted * * Changed by Taner so that it tells you what's going on * as well as allows forced on (long LCF), etc... */ if ((CurrentTime - lasttime) >= LCF) { lrv = LRV * LCF; lasttime = CurrentTime; currlife = (float)((long)me.receiveK - lastrecvK)/(float)LCF; if (((long)me.receiveK - lrv) > lastrecvK ) { if (!LIFESUX) { LIFESUX = 1; if (NOISYHTM) { sprintf(to_send, "Entering high-traffic mode - (%.1fk/s > %dk/s)", (float)currlife, LRV); sendto_ops("%s", to_send); } } else { LIFESUX++; /* Ok, life really sucks! */ LCF += 2; /* Wait even longer */ if (NOISYHTM) { sprintf(to_send, "Still high-traffic mode %d%s (%d delay): %.1fk/s", LIFESUX, (LIFESUX & 0x04) ? " (TURBO)" : "", (int)LCF, (float)currlife); sendto_ops("%s", to_send); } } } else { LCF = LOADCFREQ; if (LIFESUX) { LIFESUX = 0; if (NOISYHTM) sendto_ops("Resuming standard operation . . . ."); } } lastrecvK = (long)me.receiveK; } /* ** We only want to connect if a connection is due, ** not every time through. Note, if there are no ** active C lines, this call to Tryconnections is ** made once only; it will return 0. - avalon */ if (nextconnect && CurrentTime >= nextconnect) nextconnect = try_connections(CurrentTime); /* ** take the smaller of the two 'timed' event times as ** the time of next event (stops us being late :) - avalon ** WARNING - nextconnect can return 0! */ if (nextconnect) delay = IRCD_MIN(nextping, nextconnect); delay -= CurrentTime; /* ** Adjust delay to something reasonable [ad hoc values] ** (one might think something more clever here... --msa) ** We don't really need to check that often and as long ** as we don't delay too long, everything should be ok. ** waiting too long can cause things to timeout... ** i.e. PINGS -> a disconnection :( ** - avalon */ if (delay < 1) delay = 1; else delay = IRCD_MIN(delay, TIMESEC); /* * We want to read servers on every io_loop, as well * as "busy" clients (which again, includes servers. * If "lifesux", then we read servers AGAIN, and then * flush any data to servers. * -Taner */ #ifndef NO_PRIORITY read_message(0, FDL_SERVER); read_message(0, FDL_BUSY); if (LIFESUX) { read_message(0, FDL_SERVER); if (LIFESUX & 0x4) { /* life really sucks */ read_message(0, FDL_BUSY); read_message(0, FDL_SERVER); } flush_server_connections(); } /* * CLIENT_SERVER = TRUE: * If we're in normal mode, or if "lifesux" and a few * seconds have passed, then read everything. * CLIENT_SERVER = FALSE: * If it's been more than lifesux*2 seconds (that is, * at most 1 second, or at least 2s when lifesux is * != 0) check everything. * -Taner */ { static time_t lslasttime=0; #ifdef CLIENT_SERVER if (!LIFESUX || (lslasttime + LIFESUX) < CurrentTime) { #else if ((lslasttime + (LIFESUX + 1)) < CurrentTime) { #endif read_message(0, FDL_ALL); /* check everything! */ lslasttime = CurrentTime; } } #else read_message(0, FDL_ALL); /* check everything! */ flush_server_connections(); #endif /* ** ...perhaps should not do these loops every time, ** but only if there is some chance of something ** happening (but, note that conf->hold times may ** be changed elsewhere--so precomputed next event ** time might be too far away... (similarly with ** ping times) --msa */ if (CurrentTime >= nextping) { nextping = check_pings(CurrentTime); timeout_auth_queries(CurrentTime); } if (dorehash && !LIFESUX) { rehash(&me, &me, 1); dorehash = 0; } /* ** Flush output buffers on all connections now if they ** have data in them (or at least try to flush) ** -avalon */ flush_connections(0); #ifndef NO_PRIORITY fdlist_check(CurrentTime); #endif if(CurrentTime >= next_gc) { block_garbage_collect(); next_gc = CurrentTime + 600; } return delay; } /* * initalialize_global_set_options * * inputs - none * output - none * side effects - This sets all global set options needed */ static void initialize_global_set_options(void) { memset( &GlobalSetOptions, 0, sizeof(GlobalSetOptions)); MAXCLIENTS = MAX_CLIENTS; NOISYHTM = NOISY_HTM; GlobalSetOptions.autoconn = 1; #ifdef FLUD FLUDNUM = FLUD_NUM; FLUDTIME = FLUD_TIME; FLUDBLOCK = FLUD_BLOCK; #endif #ifdef IDLE_CHECK IDLETIME = MIN_IDLETIME; #endif #ifdef ANTI_SPAMBOT SPAMTIME = MIN_JOIN_LEAVE_TIME; SPAMNUM = MAX_JOIN_LEAVE_COUNT; #endif #ifdef ANTI_DRONE_FLOOD DRONETIME = DEFAULT_DRONE_TIME; DRONECOUNT = DEFAULT_DRONE_COUNT; #endif #ifdef NEED_SPLITCODE SPLITDELAY = (DEFAULT_SERVER_SPLIT_RECOVERY_TIME * 60); SPLITNUM = SPLIT_SMALLNET_SIZE; SPLITUSERS = SPLIT_SMALLNET_USER_SIZE; server_split_time = CurrentTime; #endif /* End of global set options */ }
/* * m_ungline * Remove a local user@host ban. * * parv[0] = sender * parv[1] = user@host mask */ int m_ungline(aClient *cptr, aClient *sptr, int parc, char *parv[]) { char hbuf[512]; char *user; char *host; struct userBan *ban; struct userBan *existing; if (!IsPrivileged(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (parc < 2) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "UNGline"); return 0; } if ((host = strchr(parv[1], '@'))) { *host++ = 0; user = parv[1]; } else { user = "******"; host = parv[1]; } if (!(ban = make_hostbased_ban(user, host))) { sendto_one(sptr, ":%s NOTICE %s :UNGline: No such ban %s@%s", me.name, parv[0], user, host); return 0; } ban->flags |= UBAN_GLINE; existing = find_userban_exact(ban, UBAN_GLINE); host = get_userban_host(ban, hbuf, sizeof(hbuf)); userban_free(ban); if (!existing) { sendto_one(sptr, ":%s NOTICE %s :UNGLINE: No such ban %s@%s", me.name, parv[0], user, host); return 0; } if (existing->flags & UBAN_CONF) { sendto_one(sptr, ":%s NOTICE %s :UNGLINE: %s@%s is specified in the" " configuration file and cannot be removed online", me.name, parv[0], user, host); return 0; } remove_userban(existing); glinestore_remove(existing); userban_free(existing); sendto_ops("%s has removed the G-Line for: [%s@%s]", sptr->name, user, host); sendto_serv_butone(MyConnect(sptr) ? NULL : cptr, ":%s UNGLINE %s@%s", sptr->name, user, host); return 0; }
/* * m_htm - HTM command handler * high traffic mode info */ int m_htm(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { char *command; if (!MyClient(sptr) || !IsOper(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } sendto_one(sptr, ":%s NOTICE %s :HTM is %s(%d), %s. Max rate = %dk/s. Current = %.1fk/s", me.name, parv[0], LIFESUX ? "ON" : "OFF", LIFESUX, NOISYHTM ? "NOISY" : "QUIET", LRV, currlife); if (parc > 1) { command = parv[1]; if (!irccmp(command,"TO")) { if (parc > 2) { int new_value = atoi(parv[2]); if (new_value < 10) { sendto_one(sptr, ":%s NOTICE %s :Cannot set LRV < 10!", me.name, parv[0]); } else LRV = new_value; sendto_one(sptr, ":%s NOTICE %s :NEW Max rate = %dk/s. Current = %.1fk/s", me.name, parv[0], LRV, currlife); sendto_realops("%s!%s@%s set new HTM rate to %dk/s (%.1fk/s current)", parv[0], sptr->username, sptr->host, LRV, currlife); } else sendto_one(sptr, ":%s NOTICE %s :LRV command needs an integer parameter",me.name, parv[0]); } else { if (!irccmp(command,"ON")) { LIFESUX = 1; sendto_one(sptr, ":%s NOTICE %s :HTM is now ON.", me.name, parv[0]); sendto_ops("Entering high-traffic mode: Forced by %s!%s@%s", parv[0], sptr->username, sptr->host); LCF = 30; /* 30s */ } else if (!irccmp(command,"OFF")) { LIFESUX = 0; LCF = LOADCFREQ; sendto_one(sptr, ":%s NOTICE %s :HTM is now OFF.", me.name, parv[0]); sendto_ops("Resuming standard operation: Forced by %s!%s@%s", parv[0], sptr->username, sptr->host); } else if (!irccmp(command,"QUIET")) { sendto_ops("HTM is now QUIET"); NOISYHTM = NO; } else if (!irccmp(command,"NOISY")) { sendto_ops("HTM is now NOISY"); NOISYHTM = YES; } else sendto_one(sptr, ":%s NOTICE %s :Commands are:HTM [ON] [OFF] [TO int] [QUIET] [NOISY]", me.name, parv[0]); } } return 0; }
int main(int argc, char *argv[]) { uid_t uid, euid; int portarg = 0, fd; #ifdef SAVE_MAXCLIENT_STATS FILE *mcsfp; #endif memset(&me, 0, sizeof(aClient)); if ((timeofday = time(NULL)) == -1) { (void) fprintf(stderr, "ERROR: Clock Failure (%d)\n", errno); exit(errno); } build_version(); Count.server = 1; /* us */ Count.oper = 0; Count.chan = 0; Count.local = 0; Count.total = 0; Count.invisi = 0; Count.unknown = 0; Count.max_loc = 0; Count.max_tot = 0; Count.today = 0; Count.weekly = 0; Count.monthly = 0; Count.yearly = 0; Count.start = NOW; Count.day = NOW; Count.week = NOW; Count.month = NOW; Count.year = NOW; #ifdef SAVE_MAXCLIENT_STATS mcsfp=fopen(DPATH "/.maxclients", "r"); if(mcsfp!=NULL) { fscanf(mcsfp, "%d %d %li %li %li %ld %ld %ld %ld", &Count.max_loc, &Count.max_tot, &Count.weekly, &Count.monthly, &Count.yearly, &Count.start, &Count.week, &Count.month, &Count.year); fclose(mcsfp); } #endif /* * this code by [email protected] * it is intended to keep the ircd from being swapped out. BSD * swapping criteria do not match the requirements of ircd */ #ifdef INITIAL_DBUFS dbuf_init(); /* set up some dbuf stuff to control paging */ #endif sbrk0 = (char *) sbrk((size_t) 0); uid = getuid(); euid = geteuid(); #ifdef PROFIL (void) monstartup(0, etext); (void) moncontrol(1); (void) signal(SIGUSR1, s_monitor); #endif myargv = argv; (void) umask(077); /* better safe than sorry --SRB */ memset((char *) &me, '\0', sizeof(me)); setup_signals(); /* * * All command line parameters have the syntax "-fstring" or "-f * string" (e.g. the space is optional). String may be empty. Flag * characters cannot be concatenated (like "-fxyz"), it would * conflict with the form "-fstring". */ while (--argc > 0 && (*++argv)[0] == '-') { char *p = argv[0] + 1; int flag = *p++; if (flag == '\0' || *p == '\0') { if (argc > 1 && argv[1][0] != '-') { p = *++argv; argc -= 1; } else p = ""; } switch (flag) { case 'a': bootopt |= BOOT_AUTODIE; break; case 'c': bootopt |= BOOT_CONSOLE; break; case 'q': bootopt |= BOOT_QUICK; break; case 'd': (void) setuid((uid_t) uid); dpath = p; break; case 'o': /* Per user local daemon... */ (void) setuid((uid_t) uid); bootopt |= BOOT_OPER; break; #ifdef CMDLINE_CONFIG case 'f': (void) setuid((uid_t) uid); configfile = p; break; # ifdef KPATH case 'k': (void) setuid((uid_t) uid); klinefile = p; break; # endif #endif case 'h': strncpyzt(me.name, p, sizeof(me.name)); break; case 'i': bootopt |= BOOT_INETD | BOOT_AUTODIE; break; case 'p': if ((portarg = atoi(p)) > 0) portnum = portarg; break; case 's': bootopt |= BOOT_STDERR; break; case 't': (void) setuid((uid_t) uid); bootopt |= BOOT_TTY; break; case 'v': (void) printf("ircd %s\n", version); exit(0); case 'x': #ifdef DEBUGMODE (void) setuid((uid_t) uid); debuglevel = atoi(p); debugmode = *p ? p : "0"; bootopt |= BOOT_DEBUG; break; #else (void) fprintf(stderr, "%s: DEBUGMODE must be defined for -x y\n", myargv[0]); exit(0); #endif default: bad_command(); break; } } if (chdir(dpath)) { perror("chdir"); exit(-1); } if ((uid != euid) && !euid) { (void) fprintf(stderr, "ERROR: do not run ircd setuid root. Make it setuid a normal user.\n"); exit(-1); } if (argc > 0) return bad_command(); /* This should exit out */ initialize_ssl(); motd = (aMotd *) NULL; helpfile = (aMotd *) NULL; motd_tm = NULL; #ifdef SHORT_MOTD shortmotd = NULL; #endif read_motd(MOTD); read_help(HELPFILE); #ifdef SHORT_MOTD read_shortmotd(SHORTMOTD); #endif clear_client_hash_table(); clear_channel_hash_table(); clear_scache_hash_table(); /* server cache name table */ clear_ip_hash_table(); /* client host ip hash table */ initlists(); initclass(); initwhowas(); initstats(); init_tree_parse(msgtab); init_send(); NOW = time(NULL); open_debugfile(); NOW = time(NULL); init_fdlist(&serv_fdlist); init_fdlist(&oper_fdlist); init_fdlist(&listen_fdlist); #ifndef NO_PRIORITY init_fdlist(&busycli_fdlist); #endif init_fdlist(&default_fdlist); { int i; for (i = MAXCONNECTIONS + 1; i > 0; i--) { default_fdlist.entry[i] = i - 1; } } if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } #ifdef WINGATE_NOTICE strcpy(ProxyMonURL, "http://"); strncpyzt((ProxyMonURL + 7), DEFAULT_PROXY_INFO_URL, (TOPICLEN + 1) - 7); strncpyzt(ProxyMonHost, MONITOR_HOST, (HOSTLEN + 1)); #endif if (portnum < 0) portnum = PORTNUM; me.port = portnum; (void) init_sys(); me.flags = FLAGS_LISTEN; #ifndef _WIN32 if (bootopt & BOOT_INETD) { me.fd = 0; local[0] = &me; me.flags = FLAGS_LISTEN; } else #endif me.fd = -1; #ifdef USE_SYSLOG # define SYSLOG_ME "ircd" openlog(SYSLOG_ME, LOG_PID | LOG_NDELAY, LOG_FACILITY); #endif if ((fd = openconf(configfile)) == -1) { Debug((DEBUG_FATAL, "Failed in reading configuration file %s", configfile)); (void) printf("Couldn't open configuration file %s\n", configfile); exit(-1); } (void) initconf(bootopt, fd); /* comstuds SEPARATE_QUOTE_KLINES_BY_DATE code */ #ifdef SEPARATE_QUOTE_KLINES_BY_DATE { struct tm *tmptr; char timebuffer[20], filename[200]; tmptr = localtime(&NOW); (void) strftime(timebuffer, 20, "%y%m%d", tmptr); ircsprintf(filename, "%s.%s", klinefile, timebuffer); if ((fd = openconf(filename)) == -1) { Debug((DEBUG_ERROR, "Failed reading kline file %s", filename)); (void) printf("Couldn't open kline file %s\n", filename); } else (void) initconf(0, fd); } #else # ifdef KPATH if ((fd = openconf(klinefile)) == -1) { Debug((DEBUG_ERROR, "Failed reading kline file %s", klinefile)); (void) printf("Couldn't open kline file %s\n", klinefile); } else (void) initconf(0, fd); # endif #endif if (!(bootopt & BOOT_INETD)) { static char star[] = "*"; aConfItem *aconf; u_long vaddr; if ((aconf = find_me()) && portarg <= 0 && aconf->port > 0) portnum = aconf->port; Debug((DEBUG_ERROR, "Port = %d", portnum)); if ((aconf->passwd[0] != '\0') && (aconf->passwd[0] != '*')) vaddr = inet_addr(aconf->passwd); else vaddr = (u_long) NULL; if (inetport(&me, star, portnum, vaddr)) { if (bootopt & BOOT_STDERR) fprintf(stderr, "Couldn't bind to primary port %d\n", portnum); #ifdef USE_SYSLOG (void) syslog(LOG_CRIT, "Couldn't bind to primary port %d\n", portnum); #endif exit(1); } } else if (inetport(&me, "*", 0, 0)) { if (bootopt & BOOT_STDERR) fprintf(stderr, "Couldn't bind to port passed from inetd\n"); #ifdef USE_SYSLOG (void) syslog(LOG_CRIT, "Couldn't bind to port passed from inetd\n"); #endif exit(1); } (void) get_my_name(&me, me.sockhost, sizeof(me.sockhost) - 1); if (me.name[0] == '\0') strncpyzt(me.name, me.sockhost, sizeof(me.name)); me.hopcount = 0; me.authfd = -1; me.confs = NULL; me.next = NULL; me.user = NULL; me.from = &me; SetMe(&me); make_server(&me); me.serv->up = me.name; me.lasttime = me.since = me.firsttime = NOW; (void) add_to_client_hash_table(me.name, &me); /* We don't want to calculate these every time they are used :) */ sprintf(REPORT_DO_DNS, REPORT_DO_DNS_, me.name); sprintf(REPORT_FIN_DNS, REPORT_FIN_DNS_, me.name); sprintf(REPORT_FIN_DNSC, REPORT_FIN_DNSC_, me.name); sprintf(REPORT_FAIL_DNS, REPORT_FAIL_DNS_, me.name); sprintf(REPORT_DO_ID, REPORT_DO_ID_, me.name); sprintf(REPORT_FIN_ID, REPORT_FIN_ID_, me.name); sprintf(REPORT_FAIL_ID, REPORT_FAIL_ID_, me.name); R_do_dns = strlen(REPORT_DO_DNS); R_fin_dns = strlen(REPORT_FIN_DNS); R_fin_dnsc = strlen(REPORT_FIN_DNSC); R_fail_dns = strlen(REPORT_FAIL_DNS); R_do_id = strlen(REPORT_DO_ID); R_fin_id = strlen(REPORT_FIN_ID); R_fail_id = strlen(REPORT_FAIL_ID); check_class(); if (bootopt & BOOT_OPER) { aClient *tmp = add_connection(&me, 0); if (!tmp) exit(1); SetMaster(tmp); } else write_pidfile(); Debug((DEBUG_NOTICE, "Server ready...")); #ifdef USE_SYSLOG syslog(LOG_NOTICE, "Server Ready"); #endif NOW = time(NULL); #ifndef NO_PRIORITY check_fdlists(); #endif if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } #ifdef DUMP_DEBUG dumpfp=fopen("dump.log", "w"); #endif io_loop(); return 0; }
static time_t check_pings(time_t currenttime) { aClient *cptr; aConfItem *aconf = (aConfItem *) NULL; int killflag, zkillflag, ping = 0, i; time_t oldest = 0; /* timeout removed, see EXPLANATION below */ char *reason, *ktype, fbuf[512]; char *errtxt = "No response from %s, closing link"; for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr)) continue; /* Note: No need to notify opers here. It's * already done when "FLAGS_DEADSOCKET" is set. */ if (cptr->flags & FLAGS_DEADSOCKET) { (void) exit_client(cptr, cptr, &me, (cptr->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket"); i--; continue; } killflag = NO; zkillflag = NO; if (rehashed) { if (zline_in_progress) { if (IsPerson(cptr)) { if ((aconf = find_zkill(cptr))) zkillflag = YES; } } else { if(IsPerson(cptr)) { if((aconf = find_kill(cptr))) killflag = YES; } } } /* Added a bit of code here to differentiate * between K and Z-lines. -ThemBones */ if (zkillflag || killflag) { ktype = zkillflag ? "Z-lined" : ((aconf->status == CONF_KILL) ? "K-lined" : "Autokilled"); if (killflag) { sendto_ops("%s active for %s", (aconf->status == CONF_KILL) ? "K-line" : "Autokill", get_client_name(cptr, FALSE)); reason = aconf->passwd ? aconf->passwd : ktype; } else { /* its a Z line */ sendto_ops("Z-line active for %s", get_client_name(cptr, FALSE)); reason = aconf->passwd ? aconf->passwd : "Z-lined"; } sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, ktype); ircsprintf(fbuf, "%s: %s", ktype, reason); (void) exit_client(cptr, cptr, &me, fbuf); i--; /* subtract out this fd so we check it again.. */ continue; } if (IsRegistered(cptr)) ping = cptr->pingval; else ping = CONNECTTIMEOUT; /* * Ok, so goto's are ugly and can be avoided here but this code * is already indented enough so I think its justified. -avalon * * justified by what? laziness? <g> * If the client pingtime is fine (ie, not larger than the client ping) * skip over all the checks below. - lucas */ if (ping < (currenttime - cptr->lasttime)) { /* * If the server hasnt talked to us in 2*ping seconds and it has * a ping time, then close its connection. If the client is a * user and a KILL line was found to be active, close this * connection too. */ if (((cptr->flags & FLAGS_PINGSENT) && ((currenttime - cptr->lasttime) >= (2 * ping))) || ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping))) { if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr))) { if (cptr->authfd >= 0) { (void) close(cptr->authfd); cptr->authfd = -1; cptr->count = 0; *cptr->buffer = '\0'; } #ifdef SHOW_HEADERS if (DoingDNS(cptr)) ssl_send(cptr, REPORT_FAIL_DNS, R_fail_dns, 0); if (DoingAuth(cptr)) ssl_send(cptr, REPORT_FAIL_ID, R_fail_id, 0); #endif Debug((DEBUG_NOTICE, "DNS/AUTH timeout %s", get_client_name(cptr, TRUE))); del_queries((char *) cptr); ClearAuth(cptr); ClearDNS(cptr); SetAccess(cptr); cptr->since = currenttime; continue; } if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) { 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)); } (void) exit_client(cptr, cptr, &me, "Ping timeout"); i--; /* subtract out this fd so we check it again.. */ continue; } /* don't send pings during a burst, as we send them already. */ else if (!(cptr->flags & (FLAGS_PINGSENT|FLAGS_BURST))) { /* * if we havent PINGed the connection and we havent heard from * it in a while, PING it to make sure it is still alive. */ cptr->flags |= FLAGS_PINGSENT; /* * not nice but does the job */ cptr->lasttime = currenttime - ping; sendto_one(cptr, "PING :%s", me.name); } } /* see EXPLANATION below * * timeout = cptr->lasttime + ping; * while (timeout <= currenttime) * timeout += ping; * if (timeout < oldest || !oldest) * oldest = timeout; */ /* * Check UNKNOWN connections - if they have been in this state * for > 100s, close them. */ if (IsUnknown(cptr)) if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) (void) exit_client(cptr, cptr, &me, "Connection Timed Out"); } rehashed = 0; zline_in_progress = 0; /* EXPLANATION * on a server with a large volume of clients, at any given point * there may be a client which needs to be pinged the next second, * or even right away (a second may have passed while running * check_pings). Preserving CPU time is more important than * pinging clients out at exact times, IMO. Therefore, I am going to make * check_pings always return currenttime + 9. This means that it may take * a user up to 9 seconds more than pingfreq to timeout. Oh well. * Plus, the number is 9 to 'stagger' our check_pings calls out over * time, to avoid doing it and the other tasks ircd does at the same time * all the time (which are usually done on intervals of 5 seconds or so). * - lucas * * if (!oldest || oldest < currenttime) * oldest = currenttime + PINGFREQUENCY; */ oldest = currenttime + 9; Debug((DEBUG_NOTICE, "Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return oldest; }
/* ** deliver_it ** Attempt to send a sequence of bytes to the connection. ** Returns ** ** < 0 Some fatal error occurred, (but not EWOULDBLOCK). ** This return is a request to close the socket and ** clean up the link. ** ** >= 0 No real error occurred, returns the number of ** bytes actually transferred. EWOULDBLOCK and other ** possibly similar conditions should be mapped to ** zero return. Upper level routine will have to ** decide what to do with those unwritten bytes... ** ** *NOTE* alarm calls have been preserved, so this should ** work equally well whether blocking or non-blocking ** mode is used... ** ** *NOTE* I nuked 'em. At the load of current ircd servers ** you can't run with stuff that blocks. And we don't. */ int deliver_it(aClient *cptr, char *str, int len) { int retval; if (IsDead(cptr) || (!IsServer(cptr) && !IsPerson(cptr) && !IsHandshake(cptr) #ifdef USE_SSL && !IsSSLHandshake(cptr) #endif && !IsUnknown(cptr))) { str[len] = '\0'; sendto_ops ("* * * DEBUG ERROR * * * !!! Calling deliver_it() for %s, status %d %s, with message: %s", cptr->name, cptr->status, IsDead(cptr) ? "DEAD" : "", str); return -1; } #ifdef USE_SSL if (IsSSL(cptr) && cptr->ssl != NULL) { retval = SSL_write(cptr->ssl, str, len); if (retval < 0) { switch (SSL_get_error(cptr->ssl, retval)) { case SSL_ERROR_WANT_READ: /* retry later */ return 0; case SSL_ERROR_WANT_WRITE: SET_ERRNO(P_EWOULDBLOCK); break; case SSL_ERROR_SYSCALL: break; case SSL_ERROR_SSL: if (ERRNO == P_EAGAIN) break; default: return 0; } } } else #endif retval = send(cptr->fd, str, len, 0); /* ** Convert WOULDBLOCK to a return of "0 bytes moved". This ** should occur only if socket was non-blocking. Note, that ** all is Ok, if the 'write' just returns '0' instead of an ** error and errno=EWOULDBLOCK. ** ** ...now, would this work on VMS too? --msa */ if (retval < 0 && (errno == EWOULDBLOCK || errno == EAGAIN || errno == ENOBUFS)) retval = 0; if (retval > 0) { cptr->sendB += retval; me.sendB += retval; if (cptr->sendB > 1023) { cptr->sendK += (cptr->sendB >> 10); cptr->sendB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ }
static time_t check_pings(time_t currenttime) { register aClient *cptr; /* current local cptr being examined */ aConfItem *aconf = (aConfItem *)NULL; int ping = 0; /* ping time value from client */ int i; /* used to index through fd/cptr's */ time_t oldest = 0; /* next ping time */ time_t timeout; /* found necessary ping time */ char *reason; /* pointer to reason string */ int die_index=0; /* index into list */ char ping_time_out_buffer[64]; /* blech that should be a define */ /* of dying clients */ dying_clients[0] = (aClient *)NULL; /* mark first one empty */ /* * I re-wrote the way klines are handled. Instead of rescanning * the local[] array and calling exit_client() right away, I * mark the client thats dying by placing a pointer to its aClient * into dying_clients[]. When I have examined all in local[], * I then examine the dying_clients[] for aClient's to exit. * This saves the rescan on k-lines, also greatly simplifies the code, * * Jan 28, 1998 * -Dianora */ for (i = 0; i <= highest_fd; i++) { if (!(cptr = local[i]) || IsMe(cptr) || IsLog(cptr)) continue; /* and go examine next fd/cptr */ /* ** Note: No need to notify opers here. It's ** already done when "FLAGS_DEADSOCKET" is set. */ if (cptr->flags & FLAGS_DEADSOCKET) { /* N.B. EVERY single time dying_clients[] is set * it must be followed by an immediate continue, * to prevent this cptr from being marked again for exit. * If you don't, you could cause exit_client() to be called twice * for the same cptr. i.e. bad news * -Dianora */ dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = ((cptr->flags & FLAGS_SENDQEX) ? "SendQ exceeded" : "Dead socket"); dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } if (rehashed) { if(dline_in_progress) { if(IsPerson(cptr)) { if( (aconf = find_dkill(cptr)) ) /* if there is a returned aConfItem then kill it */ { sendto_ops("D-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON reason = aconf->passwd ? aconf->passwd : "D-lined"; dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "D-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } } } else { if(IsPerson(cptr)) { #ifdef GLINES if( (aconf = find_gkill(cptr)) ) { sendto_ops("G-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON reason = aconf->passwd ? aconf->passwd : "G-lined"; dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "G-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } else #endif if((aconf = find_kill(cptr))) /* if there is a returned aConfItem.. then kill it */ { sendto_ops("K-line active for %s", get_client_name(cptr, FALSE)); dying_clients[die_index] = cptr; #ifdef KLINE_WITH_REASON #ifdef K_COMMENT_ONLY reason = aconf->passwd ? aconf->passwd : "K-lined"; #else reason = (BadPtr(aconf->passwd) || !is_comment(aconf->passwd)) ? "K-lined" : aconf->passwd; #endif dying_clients_reason[die_index++] = reason; #else dying_clients_reason[die_index++] = "K-lined"; #endif dying_clients[die_index] = (aClient *)NULL; sendto_one(cptr, err_str(ERR_YOUREBANNEDCREEP), me.name, cptr->name, reason); continue; /* and go examine next fd/cptr */ } } } } #ifdef IDLE_CHECK if (IsPerson(cptr)) { if( !IsElined(cptr) && ((timeofday - cptr->user->last) > idle_time)) { aConfItem *aconf; dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "idle exceeder"; dying_clients[die_index] = (aClient *)NULL; aconf = make_conf(); aconf->status = CONF_KILL; DupString(aconf->host, cptr->user->host); DupString(aconf->passwd, "idle exceeder" ); DupString(aconf->name, cptr->user->username); aconf->port = 0; aconf->hold = timeofday + 60; add_temp_kline(aconf); sendto_ops("Idle exceeder %s temp k-lining", get_client_name(cptr,FALSE)); continue; /* and go examine next fd/cptr */ } } #endif #ifdef REJECT_HOLD if (IsRejectHeld(cptr)) { if( timeofday > (cptr->firsttime + REJECT_HOLD_TIME) ) { dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "reject held client"; dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } } #endif #if defined(R_LINES) && defined(R_LINES_OFTEN) /* * this is used for KILL lines with time restrictions * on them - send a message to the user being killed * first. * *** Moved up above -taner *** * * Moved here, no more rflag -Dianora */ if (IsPerson(cptr) && find_restrict(cptr)) { sendto_ops("Restricting %s, closing link.", get_client_name(cptr,FALSE)); dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "you have been R-lined"; dying_clients[die_index] = (aClient *)NULL; continue; /* and go examine next fd/cptr */ } #endif if (!IsRegistered(cptr)) ping = CONNECTTIMEOUT; else ping = get_client_ping(cptr); /* * Ok, so goto's are ugly and can be avoided here but this code * is already indented enough so I think its justified. -avalon */ /* if (!rflag && (ping >= currenttime - cptr->lasttime)) goto ping_timeout; */ /* * *sigh* I think not -Dianora */ if (ping < (currenttime - cptr->lasttime)) { /* * If the server hasnt talked to us in 2*ping seconds * and it has a ping time, then close its connection. * If the client is a user and a KILL line was found * to be active, close this connection too. */ if (((currenttime - cptr->lasttime) >= (2 * ping) && (cptr->flags & FLAGS_PINGSENT)) || ((!IsRegistered(cptr) && (currenttime - cptr->since) >= ping))) { if (!IsRegistered(cptr) && (DoingDNS(cptr) || DoingAuth(cptr))) { if (cptr->authfd >= 0) { (void)close(cptr->authfd); cptr->authfd = -1; cptr->count = 0; *cptr->buffer = '\0'; } #ifdef SHOW_HEADERS if (DoingDNS(cptr)) send(cptr->fd, REPORT_FAIL_DNS, R_fail_dns, 0); else send(cptr->fd, REPORT_FAIL_ID, R_fail_id, 0); #endif Debug((DEBUG_NOTICE,"DNS/AUTH timeout %s", get_client_name(cptr,TRUE))); del_queries((char *)cptr); ClearAuth(cptr); ClearDNS(cptr); SetAccess(cptr); cptr->since = currenttime; continue; } if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr)) { sendto_ops("No response from %s, closing link", get_client_name(cptr, FALSE)); } /* * this is used for KILL lines with time restrictions * on them - send a messgae to the user being killed * first. * *** Moved up above -taner *** */ cptr->flags2 |= FLAGS2_PING_TIMEOUT; dying_clients[die_index++] = cptr; /* the reason is taken care of at exit time */ /* dying_clients_reason[die_index++] = "Ping timeout"; */ dying_clients[die_index] = (aClient *)NULL; /* * need to start loop over because the close can * affect the ordering of the local[] array.- avalon * ** Not if you do it right - Dianora */ continue; } else if ((cptr->flags & FLAGS_PINGSENT) == 0) { /* * if we havent PINGed the connection and we havent * heard from it in a while, PING it to make sure * it is still alive. */ cptr->flags |= FLAGS_PINGSENT; /* not nice but does the job */ cptr->lasttime = currenttime - ping; sendto_one(cptr, "PING :%s", me.name); } } /* ping_timeout: */ timeout = cptr->lasttime + ping; while (timeout <= currenttime) timeout += ping; if (timeout < oldest || !oldest) oldest = timeout; /* * Check UNKNOWN connections - if they have been in this state * for > 100s, close them. */ if (IsUnknown(cptr)) { if (cptr->firsttime ? ((timeofday - cptr->firsttime) > 100) : 0) { dying_clients[die_index] = cptr; dying_clients_reason[die_index++] = "Connection Timed Out"; dying_clients[die_index] = (aClient *)NULL; continue; } } } /* Now exit clients marked for exit above. * it doesn't matter if local[] gets re-arranged now * * -Dianora */ for(die_index = 0; (cptr = dying_clients[die_index]); die_index++) { if(cptr->flags2 & FLAGS2_PING_TIMEOUT) { (void)ircsprintf(ping_time_out_buffer, "Ping timeout: %d seconds", currenttime - cptr->lasttime); (void)exit_client(cptr, cptr, &me, ping_time_out_buffer ); } else (void)exit_client(cptr, cptr, &me, dying_clients_reason[die_index]); } rehashed = 0; dline_in_progress = 0; if (!oldest || oldest < currenttime) oldest = currenttime + PINGFREQUENCY; Debug((DEBUG_NOTICE,"Next check_ping() call at: %s, %d %d %d", myctime(oldest), ping, oldest, currenttime)); return (oldest); }
/* ** try_connections ** ** Scan through configuration and try new connections. ** Returns the calendar time when the next call to this ** function should be made latest. (No harm done if this ** is called earlier or later...) */ static time_t try_connections(time_t currenttime) { Reg aConfItem *aconf; Reg aClient *cptr; aConfItem **pconf; int connecting, confrq; time_t next = 0; aClass *cltmp; aConfItem *con_conf = (aConfItem *)NULL; int con_class = 0; connecting = FALSE; Debug((DEBUG_NOTICE,"Connection check at : %s", myctime(currenttime))); for (aconf = conf; aconf; aconf = aconf->next ) { /* Also when already connecting! (update holdtimes) --SRB */ if (!(aconf->status & CONF_CONNECT_SERVER) || aconf->port <= 0) continue; cltmp = Class(aconf); /* ** Skip this entry if the use of it is still on hold until ** future. Otherwise handle this entry (and set it on hold ** until next time). Will reset only hold times, if already ** made one successfull connection... [this algorithm is ** a bit fuzzy... -- msa >;) ] */ if ((aconf->hold > currenttime)) { if ((next > aconf->hold) || (next == 0)) next = aconf->hold; continue; } confrq = get_con_freq(cltmp); aconf->hold = currenttime + confrq; /* ** Found a CONNECT config with port specified, scan clients ** and see if this server is already connected? */ cptr = find_name(aconf->name, (aClient *)NULL); if (!cptr && (Links(cltmp) < MaxLinks(cltmp)) && (!connecting || (Class(cltmp) > con_class))) { con_class = Class(cltmp); con_conf = aconf; /* We connect only one at time... */ connecting = TRUE; } if ((next > aconf->hold) || (next == 0)) next = aconf->hold; } if (connecting) { if (con_conf->next) /* are we already last? */ { for (pconf = &conf; (aconf = *pconf); pconf = &(aconf->next)) /* put the current one at the end and * make sure we try all connections */ if (aconf == con_conf) *pconf = aconf->next; (*pconf = con_conf)->next = 0; } if (connect_server(con_conf, (aClient *)NULL, (struct hostent *)NULL) == 0) sendto_ops("Connection to %s[%s] activated.", con_conf->name, con_conf->host); } Debug((DEBUG_NOTICE,"Next connection check : %s", myctime(next))); return (next); }
static int m_server_estab(aClient *cptr) { aConnect *aconn; aClient *acptr; char *inpath, *host, *s, *encr; inpath = get_client_name(cptr, HIDEME); /* "refresh" inpath with host */ host = cptr->name; if (!(aconn = cptr->serv->aconn)) { ircstp->is_ref++; sendto_one(cptr, "ERROR :Lost Connect block"); sendto_ops_lev(ADMIN_LEV, "Lost Connect block for server %s", get_client_name(cptr, TRUE)); return exit_client(cptr, cptr, cptr, "Lost Connect block"); } encr = cptr->passwd; if (*aconn->apasswd && !StrEq(aconn->apasswd, encr)) { ircstp->is_ref++; sendto_one(cptr, "ERROR :Wrong link password"); sendto_ops("Link %s dropped, wrong password", inpath); return exit_client(cptr, cptr, cptr, "Bad Password"); } memset(cptr->passwd, '\0', sizeof(cptr->passwd)); if ((acptr = find_client(host, NULL))) { /* Don't complain about juped servers */ if(!IsULine(acptr) || find_aUserver(acptr->name)) { sendto_gnotice("from %s: Link %s dropped, server already exists", me.name, inpath); sendto_serv_butone(cptr, ":%s GNOTICE :Link %s dropped, server already" " exists", me.name, inpath); } return exit_client(cptr, cptr, cptr, "Server Exists"); } if(!(confopts & FLAGS_HUB)) { int i; for (i = 0; i <= highest_fd; i++) if (local[i] && IsServer(local[i])) { ircstp->is_ref++; sendto_one(cptr, "ERROR :I'm a leaf not a hub"); return exit_client(cptr, cptr, cptr, "I'm a leaf"); } } /* aconf->port is a CAPAB field, kind-of. kludge. mm, mm. */ /* no longer! this should still get better though */ if((aconn->flags & CONN_ZIP)) SetZipCapable(cptr); if((aconn->flags & CONN_DKEY)) SetWantDKEY(cptr); if (IsUnknown(cptr)) { if (aconn->cpasswd[0]) sendto_one(cptr, "PASS %s :TS", aconn->cpasswd); /* Pass my info to the new server */ #ifdef HAVE_ENCRYPTION_ON if(!WantDKEY(cptr)) sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP " "NICKIP NICKIPSTR TSMODE"); else sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT DKEY " "ZIP NICKIP NICKIPSTR TSMODE"); #else sendto_one(cptr, "CAPAB SSJOIN NOQUIT BURST UNCONNECT ZIP NICKIP NICKIPSTR TSMODE"); #endif sendto_one(cptr, "SERVER %s 1 :%s", my_name_for_link(me.name, aconn), (me.info[0]) ? (me.info) : "IRCers United"); } else { s = (char *) strchr(aconn->host, '@'); *s = '\0'; /* should never be NULL -- wanna bet? -Dianora */ Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]", aconn->host, cptr->username)); if (match(aconn->host, cptr->username)) { *s = '@'; ircstp->is_ref++; sendto_ops("Username mismatch [%s]v[%s] : %s", aconn->host, cptr->username, get_client_name(cptr, HIDEME)); sendto_one(cptr, "ERROR :No Username Match"); return exit_client(cptr, cptr, cptr, "Bad User"); } *s = '@'; } /* send routing notice, this should never happen anymore */ if (!DoesTS(cptr)) { sendto_gnotice("from %s: Warning: %s linked, non-TS server", me.name, get_client_name(cptr, HIDEME)); sendto_serv_butone(cptr, ":%s GNOTICE :Warning: %s linked, non-TS server", me.name, get_client_name(cptr, HIDEME)); } sendto_one(cptr, "SVINFO %d %d 0 :%ld", TS_CURRENT, TS_MIN, (ts_val) timeofday); /* sendto one(cptr, "CAPAB ...."); moved to after PASS but before SERVER * now in two places.. up above and in s_bsd.c. - lucas * This is to make sure we pass on our capabilities before we establish * a server connection */ /* * *WARNING* * In the following code in place of plain * server's name we send what is returned by * get_client_name which may add the "sockhost" after the name. * It's *very* *important* that there is a SPACE between * the name and sockhost (if present). The receiving server * will start the information field from this first blank and * thus puts the sockhost into info. ...a bit tricky, but * you have been warned, besides code is more neat this way... * --msa */ cptr->serv->up = me.name; cptr->serv->aconn = aconn; throttle_remove(cipntoa(cptr)); #ifdef HAVE_ENCRYPTION_ON if(!CanDoDKEY(cptr) || !WantDKEY(cptr)) return do_server_estab(cptr); else { SetNegoServer(cptr); /* VERY IMPORTANT THAT THIS IS HERE */ sendto_one(cptr, "DKEY START"); } #else return do_server_estab(cptr); #endif return 0; }
/* * m_squit - SQUIT message handler * parv[0] = sender prefix * parv[1] = server name * parv[2] = comment */ int m_squit(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct ConfItem* aconf; char* server; struct Client* acptr; char *comment = (parc > 2 && parv[2]) ? parv[2] : cptr->name; if (!IsPrivileged(sptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (parc > 1) { server = parv[1]; /* ** To accomodate host masking, a squit for a masked server ** name is expanded if the incoming mask is the same as ** the server name for that link to the name of link. */ while ((*server == '*') && IsServer(cptr)) { aconf = cptr->serv->nline; if (!aconf) break; if (!irccmp(server, my_name_for_link(me.name, aconf))) server = cptr->name; break; /* WARNING is normal here */ /* NOTREACHED */ } /* ** The following allows wild cards in SQUIT. Only useful ** when the command is issued by an oper. */ for (acptr = GlobalClientList; (acptr = next_client(acptr, server)); acptr = acptr->next) if (IsServer(acptr) || IsMe(acptr)) break; if (acptr && IsMe(acptr)) { acptr = cptr; server = cptr->name; } } else { /* ** This is actually protocol error. But, well, closing ** the link is very proper answer to that... ** ** Closing the client's connection probably wouldn't do much ** good.. any oper out there should know that the proper way ** to disconnect is /QUIT :) ** ** its still valid if its not a local client, its then ** a protocol error for sure -Dianora */ if(MyClient(sptr)) { sendto_one(sptr, form_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SQUIT"); return 0; } else { server = cptr->host; acptr = cptr; } } /* ** SQUIT semantics is tricky, be careful... ** ** The old (irc2.2PL1 and earlier) code just cleans away the ** server client from the links (because it is never true ** "cptr == acptr". ** ** This logic here works the same way until "SQUIT host" hits ** the server having the target "host" as local link. Then it ** will do a real cleanup spewing SQUIT's and QUIT's to all ** directions, also to the link from which the orinal SQUIT ** came, generating one unnecessary "SQUIT host" back to that ** link. ** ** One may think that this could be implemented like ** "hunt_server" (e.g. just pass on "SQUIT" without doing ** nothing until the server having the link as local is ** reached). Unfortunately this wouldn't work in the real life, ** because either target may be unreachable or may not comply ** with the request. In either case it would leave target in ** links--no command to clear it away. So, it's better just ** clean out while going forward, just to be sure. ** ** ...of course, even better cleanout would be to QUIT/SQUIT ** dependant users/servers already on the way out, but ** currently there is not enough information about remote ** clients to do this... --msa */ if (!acptr) { sendto_one(sptr, form_str(ERR_NOSUCHSERVER), me.name, parv[0], server); return 0; } if (IsLocOp(sptr) && !MyConnect(acptr)) { sendto_one(sptr, form_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (MyClient(sptr) && !IsOperRemote(sptr) && !MyConnect(acptr)) { sendto_one(sptr,":%s NOTICE %s :You have no R flag",me.name,parv[0]); return 0; } /* ** Notify all opers, if my local link is remotely squitted */ if (MyConnect(acptr) && !IsAnOper(cptr)) { sendto_ops_butone(NULL, &me, ":%s WALLOPS :Received SQUIT %s from %s (%s)", me.name, server, get_client_name(sptr,FALSE), comment); log(L_TRACE, "SQUIT From %s : %s (%s)", parv[0], server, comment); } else if (MyConnect(acptr)) sendto_ops("Received SQUIT %s from %s (%s)", acptr->name, get_client_name(sptr,FALSE), comment); return exit_client(cptr, acptr, sptr, comment); }
void io_loop() { char to_send[200]; time_t lasttime = 0; long lastrecvK = 0; int lrv = 0; time_t lasttimeofday; int delay = 0; while(1) { lasttimeofday = timeofday; if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } if (timeofday < lasttimeofday) { ircsprintf(to_send, "System clock is running backwards - (%d < %d)", timeofday, lasttimeofday); report_error(to_send, &me); } NOW = timeofday; /* * This chunk of code determines whether or not "life sucks", that * is to say if the traffic level is so high that standard server * commands should be restricted * * Changed by Taner so that it tells you what's going on as well as * allows forced on (long LCF), etc... */ if ((timeofday - lasttime) >= LCF) { lrv = LRV * LCF; lasttime = timeofday; currlife = (me.receiveK - lastrecvK) / LCF; if ((me.receiveK - lrv) > lastrecvK || HTMLOCK == YES) { if (!lifesux) { /* * In the original +th code Taner had * * LCF << 1; / * add hysteresis * / * * which does nothing... so, the hybrid team changed it to * * LCF <<= 1; / * add hysteresis * / * * suddenly, there were reports of clients mysteriously just * dropping off... Neither rodder or I can see why it makes * a difference, but lets try it this way... * * The original dog3 code, does not have an LCF variable * * -Dianora * */ lifesux = 1; if (noisy_htm) sendto_ops("Entering high-traffic mode - (%dk/s > %dk/s)", currlife, LRV); } else { lifesux++; /* Ok, life really sucks! */ LCF += 2; /* Wait even longer */ if (noisy_htm) sendto_ops("Still high-traffic mode %d%s (%d delay): %dk/s", lifesux, (lifesux > 9) ? " (TURBO)" : "", (int) LCF, currlife); /* Reset htm here, because its been on a little too long. * Bad Things(tm) tend to happen with HTM on too long -epi */ if (lifesux>15) { if (noisy_htm) sendto_ops("Resetting HTM and raising limit to: %dk/s\n", LRV + 5); LCF=LOADCFREQ; lifesux=0; LRV+=5; } } } else { LCF = LOADCFREQ; if (lifesux) { lifesux = 0; if (noisy_htm) sendto_ops("Resuming standard operation . . . ."); } } lastrecvK = me.receiveK; } /* * * We only want to connect if a connection is due, not every * time through. Note, if there are no active C lines, this call * to Tryconnections is made once only; it will return 0. - avalon */ if (nextconnect && timeofday >= nextconnect) nextconnect = try_connections(timeofday); /* DNS checks. One to timeout queries, one for cache expiries.*/ if (timeofday >= nextdnscheck) nextdnscheck = timeout_query_list(timeofday); if (timeofday >= nextexpire) nextexpire = expire_cache(timeofday); /* * * take the smaller of the two 'timed' event times as the time * of next event (stops us being late :) - avalon WARNING - * nextconnect can return 0! */ if (nextconnect) delay = MIN(nextping, nextconnect); else delay = nextping; delay = MIN(nextdnscheck, delay); delay = MIN(nextexpire, delay); delay -= timeofday; /* * * Adjust delay to something reasonable [ad hoc values] (one * might think something more clever here... --msa) * We don't really need to check that often and as long * as we don't delay too long, everything should be ok. * waiting too long can cause things to timeout... * i.e. PINGS -> a disconnection :( * - avalon */ if (delay < 1) delay = 1; else delay = MIN(delay, TIMESEC); /* * We want to read servers on every io_loop, as well as "busy" * clients (which again, includes servers. If "lifesux", then we * read servers AGAIN, and then flush any data to servers. -Taner */ #ifndef NO_PRIORITY read_message(0, &serv_fdlist); read_message(1, &busycli_fdlist); if (lifesux) { (void) read_message(1, &serv_fdlist); if (lifesux > 9) /* life really sucks */ { (void) read_message(1, &busycli_fdlist); (void) read_message(1, &serv_fdlist); } flush_fdlist_connections(&serv_fdlist); } if ((timeofday = time(NULL)) == -1) { #ifdef USE_SYSLOG syslog(LOG_WARNING, "Clock Failure (%d), TS can be corrupted", errno); #endif sendto_ops("Clock Failure (%d), TS can be corrupted", errno); } /* * CLIENT_SERVER = TRUE: If we're in normal mode, or if "lifesux" * and a few seconds have passed, then read everything. * CLIENT_SERVER = FALSE: If it's been more than lifesux*2 seconds * (that is, at most 1 second, or at least 2s when lifesux is != 0) * check everything. -Taner */ { static time_t lasttime = 0; # ifdef CLIENT_SERVER if (!lifesux || (lasttime + lifesux) < timeofday) { # else if ((lasttime + (lifesux + 1)) < timeofday) { # endif (void) read_message(delay ? delay : 1, NULL); /* check everything! */ lasttime = timeofday; } } #else (void) read_message(delay, NULL); /* check everything! */ #endif /* * * ...perhaps should not do these loops every time, but only if * there is some chance of something happening (but, note that * conf->hold times may be changed elsewhere--so precomputed next * event time might be too far away... (similarly with ping * times) --msa */ if ((timeofday >= nextping)) nextping = check_pings(timeofday); if (dorehash && !lifesux) { (void) rehash(&me, &me, 1); dorehash = 0; } /* * * Flush output buffers on all connections now if they * have data in them (or at least try to flush) -avalon * * flush_connections(me.fd); * * avalon, what kind of crack have you been smoking? why * on earth would we flush_connections blindly when * we already check to see if we can write (and do) * in read_message? There is no point, as this causes * lots and lots of unnecessary sendto's which * 99% of the time will fail because if we couldn't * empty them in read_message we can't empty them here. * one effect: during htm, output to normal lusers * will lag. */ /* Now we've made this call a bit smarter. */ /* Only flush non-blocked sockets. */ flush_connections(me.fd); #ifndef NO_PRIORITY check_fdlists(); #endif #ifdef LOCKFILE /* * * If we have pending klines and CHECK_PENDING_KLINES minutes * have passed, try writing them out. -ThemBones */ if ((pending_klines) && ((timeofday - pending_kline_time) >= (CHECK_PENDING_KLINES * 60))) do_pending_klines(); #endif } } /* * open_debugfile * * If the -t option is not given on the command line when the server is * started, all debugging output is sent to the file set by LPATH in * config.h Here we just open that file and make sure it is opened to * fd 2 so that any fprintf's to stderr also goto the logfile. If the * debuglevel is not set from the command line by -x, use /dev/null as * the dummy logfile as long as DEBUGMODE has been defined, else dont * waste the fd. */ static void open_debugfile() { #ifdef DEBUGMODE int fd; aClient *cptr; if (debuglevel >= 0) { cptr = make_client(NULL, NULL); cptr->fd = 2; SetLog(cptr); cptr->port = debuglevel; cptr->flags = 0; cptr->acpt = cptr; local[2] = cptr; (void) strcpy(cptr->sockhost, me.sockhost); (void) printf("isatty = %d ttyname = %#x\n", isatty(2), (u_int) ttyname(2)); if (!(bootopt & BOOT_TTY)) /*) leave debugging output on fd */ { (void) truncate(LOGFILE, 0); if ((fd = open(LOGFILE, O_WRONLY | O_CREAT, 0600)) < 0) if ((fd = open("/dev/null", O_WRONLY)) < 0) exit(-1); if (fd != 2) { (void) dup2(fd, 2); (void) close(fd); } strncpyzt(cptr->name, LOGFILE, sizeof(cptr->name)); } else if (isatty(2) && ttyname(2)) strncpyzt(cptr->name, ttyname(2), sizeof(cptr->name)); else (void) strcpy(cptr->name, "FD2-Pipe"); Debug((DEBUG_FATAL, "Debug: File <%s> Level: %d at %s", cptr->name, cptr->port, myctime(time(NULL)))); } else local[2] = NULL; #endif return; }
DLLFUNC int m_htm(aClient *cptr, aClient *sptr, int parc, char *parv[]) { int x = HUNTED_NOSUCH; char *command, *param; if (!IsOper(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name); return 0; } switch(parc) { case 1: break; case 2: x = hunt_server_token_quiet(cptr, sptr, MSG_HTM, TOK_HTM, "%s", 1, parc, parv); break; case 3: x = hunt_server_token_quiet(cptr, sptr, MSG_HTM, TOK_HTM, "%s %s", 1, parc, parv); break; default: x = hunt_server_token_quiet(cptr, sptr, MSG_HTM, TOK_HTM, "%s %s %s", 1, parc, parv); } switch (x) { case HUNTED_NOSUCH: command = (parv[1]); param = (parv[2]); break; case HUNTED_ISME: command = (parv[2]); param = (parv[3]); break; default: return 0; } #ifndef NO_FDLIST if (!command) { sendto_one(sptr, ":%s NOTICE %s :*** Current incoming rate: %0.2f kb/s", me.name, parv[0], currentrate); sendto_one(sptr, ":%s NOTICE %s :*** Current outgoing rate: %0.2f kb/s", me.name, parv[0], currentrate2); sendto_one(sptr, ":%s NOTICE %s :*** Highest incoming rate: %0.2f kb/s", me.name, parv[0], highest_rate); sendto_one(sptr, ":%s NOTICE %s :*** Highest outgoing rate: %0.2f kb/s", me.name, parv[0], highest_rate2); sendto_one(sptr, ":%s NOTICE %s :*** High traffic mode is currently \2%s\2", me.name, parv[0], (lifesux ? "ON" : "OFF")); sendto_one(sptr, ":%s NOTICE %s :*** High traffic mode is currently in \2%s\2 mode", me.name, parv[0], (noisy_htm ? "NOISY" : "QUIET")); sendto_one(sptr, ":%s NOTICE %s :*** HTM will be activated if incoming > %i kb/s", me.name, parv[0], LRV); } else { if (!stricmp(command, "ON")) { EventInfo mod; lifesux = 1; sendto_one(sptr, ":%s NOTICE %s :High traffic mode is now ON.", me.name, parv[0]); sendto_ops ("%s (%s@%s) forced High traffic mode to activate", parv[0], sptr->user->username, GetHost(sptr)); LCF = 60; /* 60 seconds */ mod.flags = EMOD_EVERY; mod.every = LCF; LockEventSystem(); EventMod(e_lcf, &mod); UnlockEventSystem(); } else if (!stricmp(command, "OFF")) { EventInfo mod; lifesux = 0; LCF = LOADCFREQ; mod.flags = EMOD_EVERY; mod.every = LCF; LockEventSystem(); EventMod(e_lcf, &mod); UnlockEventSystem(); sendto_one(sptr, ":%s NOTICE %s :High traffic mode is now OFF.", me.name, parv[0]); sendto_ops ("%s (%s@%s) forced High traffic mode to deactivate", parv[0], sptr->user->username, GetHost(sptr)); } else if (!stricmp(command, "TO")) { if (!param) sendto_one(sptr, ":%s NOTICE %s :You must specify an integer value", me.name, parv[0]); else { int new_val = atoi(param); if (new_val < 10) sendto_one(sptr, ":%s NOTICE %s :New value must be > 10", me.name, parv[0]); else { LRV = new_val; sendto_one(sptr, ":%s NOTICE %s :New max rate is %dkb/s", me.name, parv[0], LRV); sendto_ops ("%s (%s@%s) changed the High traffic mode max rate to %dkb/s", parv[0], sptr->user->username, GetHost(sptr), LRV); } } } else if (!stricmp(command, "QUIET")) { noisy_htm = 0; sendto_one(sptr, ":%s NOTICE %s :High traffic mode is now QUIET", me.name, parv[0]); sendto_ops("%s (%s@%s) set High traffic mode to QUIET", parv[0], sptr->user->username, GetHost(sptr)); } else if (!stricmp(command, "NOISY")) { noisy_htm = 1; sendto_one(sptr, ":%s NOTICE %s :High traffic mode is now NOISY", me.name, parv[0]); sendto_ops("%s (%s@%s) set High traffic mode to NOISY", parv[0], sptr->user->username, GetHost(sptr)); } else sendto_one(sptr, ":%s NOTICE %s :Unknown option: %s", me.name, parv[0], command); } #else sendto_one(sptr, ":%s NOTICE %s :*** High traffic mode and fdlists are not enabled on this server", me.name, sptr->name); #endif return 0; }
/* ** m_user ** parv[0] = sender prefix ** parv[1] = username (login name, account) ** parv[2] = client host name (used only from other servers) ** parv[3] = server host name (used only from other servers) ** parv[4] = users real name info ** ** NOTE: Be advised that multiple USER messages are possible, ** hence, always check if a certain struct is already allocated... -- Syzop */ DLLFUNC CMD_FUNC(m_user) { #define UFLAGS (UMODE_INVISIBLE|UMODE_WALLOP|UMODE_SERVNOTICE) char *username, *host, *server, *realname, *umodex = NULL, *virthost = NULL, *ip = NULL; u_int32_t sstamp = 0; anUser *user; aClient *acptr; if (IsServer(cptr) && !IsUnknown(sptr)) return 0; if (MyConnect(sptr) && (sptr->listener->umodes & LISTENER_SERVERSONLY)) { return exit_client(cptr, sptr, sptr, "This port is for servers only"); } if (parc > 2 && (username = (char *)index(parv[1], '@'))) *username = '******'; if (parc < 5 || *parv[1] == '\0' || *parv[2] == '\0' || *parv[3] == '\0' || *parv[4] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "USER"); if (IsServer(cptr)) sendto_ops("bad USER param count for %s from %s", parv[0], get_client_name(cptr, FALSE)); else return 0; } /* Copy parameters into better documenting variables */ username = (parc < 2 || BadPtr(parv[1])) ? "<bad-boy>" : parv[1]; host = (parc < 3 || BadPtr(parv[2])) ? "<nohost>" : parv[2]; server = (parc < 4 || BadPtr(parv[3])) ? "<noserver>" : parv[3]; /* This we can remove as soon as all servers have upgraded. */ if (parc == 6 && IsServer(cptr)) { if (isdigit(*parv[4])) sstamp = strtoul(parv[4], NULL, 10); realname = (BadPtr(parv[5])) ? "<bad-realname>" : parv[5]; umodex = NULL; } else if (parc == 8 && IsServer(cptr)) { if (isdigit(*parv[4])) sstamp = strtoul(parv[4], NULL, 10); realname = (BadPtr(parv[7])) ? "<bad-realname>" : parv[7]; umodex = parv[5]; virthost = parv[6]; } else if (parc == 9 && IsServer(cptr)) { if (isdigit(*parv[4])) sstamp = strtoul(parv[4], NULL, 10); realname = (BadPtr(parv[8])) ? "<bad-realname>" : parv[8]; umodex = parv[5]; virthost = parv[6]; ip = parv[7]; } else { realname = (BadPtr(parv[4])) ? "<bad-realname>" : parv[4]; } user = make_user(sptr); if (!MyConnect(sptr)) { if (sptr->srvptr == NULL) sendto_ops("WARNING, User %s introduced as being " "on non-existant server %s.", sptr->name, server); if (SupportNS(cptr)) { acptr = (aClient *)find_server_b64_or_real(server); if (acptr) user->server = find_or_add(acptr->name); else user->server = find_or_add(server); } else user->server = find_or_add(server); strlcpy(user->realhost, host, sizeof(user->realhost)); goto user_finish; } if (!IsUnknown(sptr)) { sendto_one(sptr, err_str(ERR_ALREADYREGISTRED), me.name, parv[0]); return 0; } if (!IsServer(cptr)) { sptr->umodes |= CONN_MODES; if (CONNECT_SNOMASK) { sptr->umodes |= UMODE_SERVNOTICE; create_snomask(sptr, user, CONNECT_SNOMASK); } } /* Set it temporarely to at least something trusted, * this was copying user supplied data directly into user->realhost * which seemed bad. Not to say this is much better ;p. -- Syzop */ strncpyzt(user->realhost, Inet_ia2p(&sptr->ip), sizeof(user->realhost)); if (!user->ip_str) user->ip_str = strdup(Inet_ia2p(&sptr->ip)); user->server = me_hash; user_finish: user->servicestamp = sstamp; strlcpy(sptr->info, realname, sizeof(sptr->info)); if (sptr->name[0] && (IsServer(cptr) ? 1 : IsNotSpoof(sptr))) /* NICK and no-spoof already received, now we have USER... */ { if (USE_BAN_VERSION && MyConnect(sptr)) sendto_one(sptr, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1", me.name, sptr->name); if (strlen(username) > USERLEN) username[USERLEN] = '\0'; /* cut-off */ return( register_user(cptr, sptr, sptr->name, username, umodex, virthost,ip)); } else strncpyzt(sptr->user->username, username, USERLEN + 1); return 0; }