/*! \brief SVSNICK command handler * * \param source_p Pointer to allocated Client struct from which the message * originally comes from. This can be a local or remote client. * \param parc Integer holding the number of supplied arguments. * \param parv Argument vector where parv[0] .. parv[parc-1] are non-NULL * pointers. * \note Valid arguments for this command are: * - parv[0] = command * - parv[1] = old nickname * - parv[2] = new nickname * - parv[3] = timestamp */ static int ms_svsnick(struct Client *source_p, int parc, char *parv[]) { struct Client *target_p = NULL, *exists_p = NULL; if (!HasFlag(source_p, FLAGS_SERVICE) || !valid_nickname(parv[2], 1)) return 0; if ((target_p = find_person(source_p, parv[1])) == NULL) return 0; if (!MyConnect(target_p)) { if (target_p->from == source_p->from) { sendto_realops_flags(UMODE_DEBUG, L_ALL, SEND_NOTICE, "Received wrong-direction SVSNICK " "for %s (behind %s) from %s", target_p->name, source_p->from->name, get_client_name(source_p, HIDE_IP)); return 0; } sendto_one(target_p, ":%s SVSNICK %s %s %s", source_p->id, target_p->id, parv[2], parv[3]); return 0; } if ((exists_p = hash_find_client(parv[2]))) { if (target_p == exists_p) { if (!strcmp(target_p->name, parv[2])) return 0; } else if (IsUnknown(exists_p)) exit_client(exists_p, "SVSNICK Override"); else { exit_client(target_p, "SVSNICK Collide"); return 0; } } target_p->tsinfo = strtoimax(parv[3], NULL, 10); clear_ban_cache_client(target_p); watch_check_hash(target_p, RPL_LOGOFF); if (HasUMode(target_p, UMODE_REGISTERED)) { const unsigned int oldmodes = target_p->umodes; char modebuf[IRCD_BUFSIZE] = ""; DelUMode(target_p, UMODE_REGISTERED); send_umode(target_p, target_p, oldmodes, modebuf); } sendto_common_channels_local(target_p, 1, 0, 0, ":%s!%s@%s NICK :%s", target_p->name, target_p->username, target_p->host, parv[2]); whowas_add_history(target_p, 1); sendto_server(NULL, 0, 0, ":%s NICK %s :%ju", target_p->id, parv[2], target_p->tsinfo); hash_del_client(target_p); strlcpy(target_p->name, parv[2], sizeof(target_p->name)); hash_add_client(target_p); watch_check_hash(target_p, RPL_LOGON); fd_note(&target_p->connection->fd, "Nick: %s", target_p->name); return 0; }
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; }
static int do_server_estab(aClient *cptr) { aClient *acptr; aConnect *aconn; aChannel *chptr; int i; /* "refresh" inpath with host */ char *inpath = get_client_name(cptr, HIDEME); SetServer(cptr); Count.unknown--; Count.server++; Count.myserver++; if(IsZipCapable(cptr) && DoZipThis(cptr)) { sendto_one(cptr, "SVINFO ZIP"); SetZipOut(cptr); cptr->serv->zip_out = zip_create_output_session(); } #ifdef MAXBUFFERS /* let's try to bump up server sock_opts... -Taner */ reset_sock_opts(cptr->fd, 1); #endif /* adds to server list */ add_to_list(&server_list, cptr); set_effective_class(cptr); /* Check one more time for good measure... is it there? */ if ((acptr = find_name(cptr->name, NULL))) { char nbuf[HOSTLEN * 2 + USERLEN + 5]; aClient *bcptr; /* * While negotiating stuff, another copy of this server appeared. * * 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", cptr->name); if (bcptr == cptr) { sendto_gnotice("from %s: Link %s cancelled, server %s already " "exists (final phase)", me.name, get_client_name(bcptr, HIDEME), cptr->name); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, " "server %s already exists (final phase)", me.name, get_client_name(bcptr, HIDEME), cptr->name); return exit_client(bcptr, bcptr, &me, "Server Exists (final phase)"); } /* 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 (final phase)", me.name, nbuf, cptr->name, get_client_name(cptr, HIDEME)); sendto_serv_butone(bcptr, ":%s GNOTICE :Link %s cancelled, server %s " "reintroduced by %s (final phase)", me.name, nbuf, cptr->name, get_client_name(cptr, HIDEME)); exit_client(bcptr, bcptr, &me, "Server Exists (final phase)"); } /* error, error, error! if a server is U:'d, and it connects to us, * we need to figure that out! So, do it here. - lucas */ if (find_aUserver(cptr->name)) { Count.myulined++; cptr->flags |= FLAGS_ULINE; /* If the server has special u:line flags, let's set them.. */ cptr->serv->uflags = cptr->serv->aconn->uflags; } fakelinkserver_update(cptr->name, cptr->info); sendto_gnotice("from %s: Link with %s established, states:%s%s%s%s", me.name, inpath, ZipOut(cptr) ? " Output-compressed" : "", RC4EncLink(cptr) ? " encrypted" : "", IsULine(cptr) ? " ULined" : "", DoesTS(cptr) ? " TS" : " Non-TS"); /* * Notify everyone of the fact that this has just linked: the entire * network should get two of these, one explaining the link between * me->serv and the other between serv->me */ sendto_serv_butone(NULL, ":%s GNOTICE :Link with %s established: %s", me.name, inpath, DoesTS(cptr) ? "TS link" : "Non-TS link!"); add_to_client_hash_table(cptr->name, cptr); /* add it to scache */ find_or_add(cptr->name); /* * Old sendto_serv_but_one() call removed because we now need to * send different names to different servers (domain name * matching) Send new server to other servers. */ for (i = 0; i <= highest_fd; i++) { if (!(acptr = local[i]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) continue; if ((aconn = acptr->serv->aconn) && !match(my_name_for_link(me.name, aconn), cptr->name)) continue; sendto_one(acptr, ":%s SERVER %s 2 :%s", me.name, cptr->name, cptr->info); } /* * Pass on my client information to the new server * * First, pass only servers (idea is that if the link gets * cancelled beacause the server was already there, there are no * NICK's to be cancelled...). Of course, if cancellation occurs, * all this info is sent anyway, and I guess the link dies when a * read is attempted...? --msa * * Note: Link cancellation to occur at this point means that at * least two servers from my fragment are building up connection * this other fragment at the same time, it's a race condition, * not the normal way of operation... * * ALSO NOTE: using the get_client_name for server names-- see * previous *WARNING*!!! (Also, original inpath is * destroyed...) */ aconn = cptr->serv->aconn; for (acptr = &me; acptr; acptr = acptr->prev) { if (acptr->from == cptr) continue; if (IsServer(acptr)) { if (match(my_name_for_link(me.name, aconn), acptr->name) == 0) continue; sendto_one(cptr, ":%s SERVER %s %d :%s", acptr->serv->up, acptr->name, acptr->hopcount + 1, acptr->info); } } /* send out our SQLINES and SGLINES too */ send_simbans(cptr, SBAN_CHAN|SBAN_NETWORK); send_simbans(cptr, SBAN_NICK|SBAN_NETWORK); send_simbans(cptr, SBAN_GCOS|SBAN_NETWORK); /* Send out fake server list and other 'fake' stuff */ fakeserver_sendserver(cptr); /* Send UHM (user host-masking) type */ if(confopts & FLAGS_HUB) sendto_one(cptr, "SVSUHM %d", uhm_type); /* send clone list */ clones_send(cptr); /* Bursts are about to start.. send a BURST */ if (IsBurst(cptr)) sendto_one(cptr, "BURST"); /* * * Send it in the shortened format with the TS, if it's a TS * server; walk the list of channels, sending all the nicks that * haven't been sent yet for each channel, then send the channel * itself -- it's less obvious than sending all nicks first, but * on the receiving side memory will be allocated more nicely * saving a few seconds in the handling of a split -orabidoo */ { chanMember *cm; static char nickissent = 1; nickissent = 3 - nickissent; /* * flag used for each nick to check if we've sent it yet - must * be different each time and !=0, so we alternate between 1 and * 2 -orabidoo */ for (chptr = channel; chptr; chptr = chptr->nextch) { for (cm = chptr->members; cm; cm = cm->next) { acptr = cm->cptr; if (acptr->nicksent != nickissent) { acptr->nicksent = nickissent; if (acptr->from != cptr) sendnick_TS(cptr, acptr); } } send_channel_modes(cptr, chptr); } /* also send out those that are not on any channel */ for (acptr = &me; acptr; acptr = acptr->prev) if (acptr->nicksent != nickissent) { acptr->nicksent = nickissent; if (acptr->from != cptr) sendnick_TS(cptr, acptr); } } if(confopts & FLAGS_HUB) fakelusers_sendlock(cptr); if(ZipOut(cptr)) { unsigned long inb, outb; double rat; zip_out_get_stats(cptr->serv->zip_out, &inb, &outb, &rat); if(inb) { sendto_gnotice("from %s: Connect burst to %s: %lu bytes normal, " "%lu compressed (%3.2f%%)", me.name, get_client_name(cptr, HIDEME), inb, outb, rat); sendto_serv_butone(cptr, ":%s GNOTICE :Connect burst to %s: %lu " "bytes normal, %lu compressed (%3.2f%%)", me.name, get_client_name(cptr, HIDEME), inb, outb, rat); } } /* stuff a PING at the end of this burst so we can figure out when the other side has finished processing it. */ cptr->flags |= FLAGS_BURST|FLAGS_PINGSENT; if (IsBurst(cptr)) cptr->flags |= FLAGS_SOBSENT; sendto_one(cptr, "PING :%s", me.name); return 0; }
int parse(aClient *cptr, char *buffer, char *bufend) { aClient *from = cptr; char *ch, *s; int i, numeric = 0, paramcount; struct Message *mptr; #ifdef DUMP_DEBUG if(dumpfp!=NULL) { fprintf(dumpfp, "<- %s: %s\n", (cptr->name ? cptr->name : "*"), buffer); fflush(dumpfp); } #endif Debug((DEBUG_DEBUG, "Parsing %s: %s", get_client_name(cptr, TRUE), buffer)); if (IsDead(cptr)) return -1; s = sender; *s = '\0'; for (ch = buffer; *ch == ' '; ch++); /* skip spaces */ para[0] = from->name; if (*ch == ':') { /* * Copy the prefix to 'sender' assuming it terminates with * SPACE (or NULL, which is an error, though). */ for (++ch; *ch && *ch != ' '; ++ch) if (s < (sender + HOSTLEN)) *s++ = *ch; *s = '\0'; /* * Actually, only messages coming from servers can have the * prefix--prefix silently ignored, if coming from a user * client... * * ...sigh, the current release "v2.2PL1" generates also null * prefixes, at least to NOTIFY messages (e.g. it puts * "sptr->nickname" as prefix from server structures where it's * null--the following will handle this case as "no prefix" at * all --msa (": NOTICE nick ...") */ if (*sender && IsServer(cptr)) { from = find_client(sender, (aClient *) NULL); /* * okay, this doesn't seem to do much here. * from->name _MUST_ be equal to sender. * That's what find_client does. * find_client will find servers too, and since we don't use server * masking, the find server call is useless (and very wasteful). * now, there HAS to be a from and from->name and * sender have to be the same * for us to get to the next if. but the next if * starts out with if(!from) * so this is UNREACHABLE CODE! AGH! - lucas * * if (!from || mycmp(from->name, sender)) * from = find_server(sender, (aClient *) NULL); * else if (!from && strchr(sender, '@')) * from = find_nickserv(sender, (aClient *) NULL); */ para[0] = sender; /* * Hmm! If the client corresponding to the prefix is not * found--what is the correct action??? Now, I will ignore the * message (old IRC just let it through as if the prefix just * wasn't there...) --msa */ if (!from) { Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)", sender, buffer, cptr->name)); ircstp->is_unpf++; remove_unknown(cptr, sender, buffer); return -1; } if (from->from != cptr) { ircstp->is_wrdi++; Debug((DEBUG_ERROR, "Message (%s) coming from (%s)", buffer, cptr->name)); return cancel_clients(cptr, from, buffer); } } while (*ch == ' ') ch++; } if (*ch == '\0') { ircstp->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cptr->name, from->name)); return (-1); } /* * Extract the command code from the packet. Point s to the end * of the command code and calculate the length using pointer * arithmetic. Note: only need length for numerics and *all* * numerics must have parameters and thus a space after the command * code. -avalon * * ummm???? - Dianora */ /* check for numeric */ if (*(ch + 3) == ' ' && IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2))) { mptr = (struct Message *) NULL; numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = MAXPARA; ircstp->is_num++; s = ch + 3; *s++ = '\0'; } else { s = strchr(ch, ' '); if (s) *s++ = '\0'; char *t = ch; for (; *t; *t++) *t = ToUpper(*t); #ifndef USE_NEW_COMMAND_SYSTEM mptr = tree_parse(ch); #else HASH_FIND_STR(msgtab, ch, mptr); #endif if (!mptr || !mptr->cmd) { /* * only send error messages to things that actually sent * buffers to us and only people, too. */ if (buffer[0] != '\0') { if (IsPerson(from)) sendto_one(&me, from, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, TRUE))); } ircstp->is_unco++; return -1; } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; /* * Allow only 1 msg per 2 seconds (on average) to prevent * dumping. to keep the response rate up, bursts of up to 5 msgs * are allowed -SRB Opers can send 1 msg per second, burst of ~20 * -Taner */ if (!IsServer(cptr) || (*(int **)(&cptr->cb) == NULL)) { if (!NoMsgThrottle(cptr)) { #ifdef NO_OPER_FLOOD if (IsAnOper(cptr)) /* "randomly" (weighted) increase the since */ cptr->since += (cptr->receiveM % 10) ? 1 : 0; else #endif cptr->since += (2 + i / 120); } } } /* * Must the following loop really be so devious? On surface it * splits the message to parameters from blank spaces. But, if * paramcount has been reached, the rest of the message goes into * this last parameter (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ i = 1; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* The rest is a single parameter */ para[i++] = s + 1; break; } para[i++] = s; if (i >= paramcount) { if(paramcount == MAXPARA && strchr(s, ' ')) { sendto_realops_lev(DEBUG_LEV, "Overflowed MAXPARA on %s from %s", mptr ? mptr->cmd : "numeric", get_client_name(cptr, (IsServer(cptr) ? HIDEME : FALSE))); } break; } while(*s && *s != ' ') s++; } } para[i] = NULL; if (mptr == (struct Message *) NULL) return (do_numeric(numeric, cptr, from, i, para)); mptr->count++; /* patch to avoid server flooding from unregistered connects */ if (!IsRegistered(cptr) && !(mptr->flags & MF_UNREG)) { sendto_one(&me, from, err_str(ERR_NOTREGISTERED), me.name, *para[0] ? para[0] : "*", ch); return -1; } if (IsRegisteredUser(cptr) && (mptr->flags & MF_RIDLE)) from->user->last = timeofday; if (mptr->flags & MF_ALIAS) return mptr->func(cptr, from, i, para, &aliastab[mptr->aliasidx]); return (*mptr->func) (cptr, from, i, para); }
/* * Restore files */ int restore_cmd(UAContext *ua, const char *cmd) { RESTORE_CTX rx; /* restore context */ POOL_MEM buf; JOBRES *job; int i; JCR *jcr = ua->jcr; char *escaped_bsr_name = NULL; char *escaped_where_name = NULL; char *strip_prefix, *add_prefix, *add_suffix, *regexp; strip_prefix = add_prefix = add_suffix = regexp = NULL; memset(&rx, 0, sizeof(rx)); rx.path = get_pool_memory(PM_FNAME); rx.fname = get_pool_memory(PM_FNAME); rx.JobIds = get_pool_memory(PM_FNAME); rx.JobIds[0] = 0; rx.BaseJobIds = get_pool_memory(PM_FNAME); rx.query = get_pool_memory(PM_FNAME); rx.bsr = new_bsr(); i = find_arg_with_value(ua, "comment"); if (i >= 0) { rx.comment = ua->argv[i]; if (!is_comment_legal(ua, rx.comment)) { goto bail_out; } } i = find_arg_with_value(ua, "backupformat"); if (i >= 0) { rx.backup_format = ua->argv[i]; } i = find_arg_with_value(ua, "where"); if (i >= 0) { rx.where = ua->argv[i]; } i = find_arg_with_value(ua, "replace"); if (i >= 0) { rx.replace = ua->argv[i]; } i = find_arg_with_value(ua, "pluginoptions"); if (i >= 0) { rx.plugin_options = ua->argv[i]; } i = find_arg_with_value(ua, "strip_prefix"); if (i >= 0) { strip_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_prefix"); if (i >= 0) { add_prefix = ua->argv[i]; } i = find_arg_with_value(ua, "add_suffix"); if (i >= 0) { add_suffix = ua->argv[i]; } i = find_arg_with_value(ua, "regexwhere"); if (i >= 0) { rx.RegexWhere = ua->argv[i]; } if (strip_prefix || add_suffix || add_prefix) { int len = bregexp_get_build_where_size(strip_prefix, add_prefix, add_suffix); regexp = (char *)bmalloc(len * sizeof(char)); bregexp_build_where(regexp, len, strip_prefix, add_prefix, add_suffix); rx.RegexWhere = regexp; } /* TODO: add acl for regexwhere ? */ if (rx.RegexWhere) { if (!acl_access_ok(ua, Where_ACL, rx.RegexWhere, true)) { ua->error_msg(_("\"RegexWhere\" specification not authorized.\n")); goto bail_out; } } if (rx.where) { if (!acl_access_ok(ua, Where_ACL, rx.where, true)) { ua->error_msg(_("\"where\" specification not authorized.\n")); goto bail_out; } } if (!open_client_db(ua, true)) { goto bail_out; } /* Ensure there is at least one Restore Job */ LockRes(); foreach_res(job, R_JOB) { if (job->JobType == JT_RESTORE) { if (!rx.restore_job) { rx.restore_job = job; } rx.restore_jobs++; } } UnlockRes(); if (!rx.restore_jobs) { ua->error_msg(_( "No Restore Job Resource found in bareos-dir.conf.\n" "You must create at least one before running this command.\n")); goto bail_out; } /* * Request user to select JobIds or files by various different methods * last 20 jobs, where File saved, most recent backup, ... * In the end, a list of files are pumped into * add_findex() */ switch (user_select_jobids_or_files(ua, &rx)) { case 0: /* error */ goto bail_out; case 1: /* selected by jobid */ get_and_display_basejobs(ua, &rx); if (!build_directory_tree(ua, &rx)) { ua->send_msg(_("Restore not done.\n")); goto bail_out; } break; case 2: /* selected by filename, no tree needed */ break; } if (rx.bsr->JobId) { char ed1[50]; if (!complete_bsr(ua, rx.bsr)) { /* find Vol, SessId, SessTime from JobIds */ ua->error_msg(_("Unable to construct a valid BSR. Cannot continue.\n")); goto bail_out; } if (!(rx.selected_files = write_bsr_file(ua, rx))) { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } display_bsr_info(ua, rx); /* display vols needed, etc */ if (rx.selected_files==1) { ua->info_msg(_("\n1 file selected to be restored.\n\n")); } else { ua->info_msg(_("\n%s files selected to be restored.\n\n"), edit_uint64_with_commas(rx.selected_files, ed1)); } } else { ua->warning_msg(_("No files selected to be restored.\n")); goto bail_out; } if (rx.restore_jobs == 1) { job = rx.restore_job; } else { job = get_restore_job(ua); } if (!job) { goto bail_out; } get_client_name(ua, &rx); if (!rx.ClientName) { ua->error_msg(_("No Client resource found!\n")); goto bail_out; } get_restore_client_name(ua, rx); escaped_bsr_name = escape_filename(jcr->RestoreBootstrap); Mmsg(ua->cmd, "run job=\"%s\" client=\"%s\" restoreclient=\"%s\" storage=\"%s\"" " bootstrap=\"%s\" files=%u catalog=\"%s\"", job->name(), rx.ClientName, rx.RestoreClientName, rx.store?rx.store->name():"", escaped_bsr_name ? escaped_bsr_name : jcr->RestoreBootstrap, rx.selected_files, ua->catalog->name()); /* * Build run command */ if (rx.backup_format) { Mmsg(buf, " backupformat=%s", rx.backup_format); pm_strcat(ua->cmd, buf); } pm_strcpy(buf, ""); if (rx.RegexWhere) { escaped_where_name = escape_filename(rx.RegexWhere); Mmsg(buf, " regexwhere=\"%s\"", escaped_where_name ? escaped_where_name : rx.RegexWhere); } else if (rx.where) { escaped_where_name = escape_filename(rx.where); Mmsg(buf," where=\"%s\"", escaped_where_name ? escaped_where_name : rx.where); } pm_strcat(ua->cmd, buf); if (rx.replace) { Mmsg(buf, " replace=%s", rx.replace); pm_strcat(ua->cmd, buf); } if (rx.plugin_options) { Mmsg(buf, " pluginoptions=%s", rx.plugin_options); pm_strcat(ua->cmd, buf); } if (rx.comment) { Mmsg(buf, " comment=\"%s\"", rx.comment); pm_strcat(ua->cmd, buf); } if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } if (find_arg(ua, NT_("yes")) > 0) { pm_strcat(ua->cmd, " yes"); /* pass it on to the run command */ } Dmsg1(200, "Submitting: %s\n", ua->cmd); /* * Transfer jobids to jcr to for picking up restore objects */ jcr->JobIds = rx.JobIds; rx.JobIds = NULL; parse_ua_args(ua); run_cmd(ua, ua->cmd); free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return 1; bail_out: if (escaped_bsr_name != NULL) { bfree(escaped_bsr_name); } if (escaped_where_name != NULL) { bfree(escaped_where_name); } if (regexp) { bfree(regexp); } free_rx(&rx); garbage_collect_memory(); /* release unused memory */ return 0; }
/** Parse a line of data from a server. * @param[in] cptr Client that sent the data. * @param[in] buffer Start of input line. * @param[in] bufend End of input line. * @return 0 on success, -1 on parse error, or CPTR_KILLED if message * handler returns it. */ int parse_server(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch = buffer; char* s; int len; int i; int numeric = 0; int paramcount; struct Message* mptr; Debug((DEBUG_DEBUG, "Server Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); /* * A server ALWAYS sends a prefix. When it starts with a ':' it's the * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric * nick or server */ if (*ch == ':') { /* Let para[0] point to the name of the sender */ para[0] = ch + 1; if (!(ch = strchr(ch, ' '))) return -1; *ch++ = '\0'; /* And let `from' point to its client structure, opps.. a server is _also_ a client --Nem */ from = FindClient(para[0]); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message traveling * upstream a SQUIT that removed the client * --Run */ if (from == NULL) { Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)", para[0], buffer, cli_name(cptr))); ++ServerStats->is_unpf; while (*ch == ' ') ch++; /* * However, the only thing that MUST be * allowed to travel upstream against an * squit, is an SQUIT itself (the timestamp * protects us from being used wrong) */ if (ch[1] == 'Q') { para[0] = cli_name(cptr); from = cptr; } else return 0; } else if (cli_from(from) != cptr) { ++ServerStats->is_wrdi; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } else { char numeric_prefix[6]; int i; for (i = 0; i < 5; ++i) { if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i])) { break; } } numeric_prefix[i] = '\0'; /* * We got a numeric nick as prefix * 1 or 2 character prefixes are from servers * 3 or 5 chars are from clients */ if (0 == i) { protocol_violation(cptr,"Missing Prefix"); from = cptr; } else if (' ' == ch[1] || ' ' == ch[2]) from = FindNServer(numeric_prefix); else from = findNUser(numeric_prefix); do { ++ch; } while (*ch != ' ' && *ch); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message traveling * upstream a SQUIT that removed the client * --Run * There turned out to be other reasons that * a prefix is unknown, needing an upstream * KILL. Also, next to an SQUIT we better * allow a KILL to pass too. * --Run */ if (from == NULL) { ServerStats->is_unpf++; while (*ch == ' ') ch++; if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I')) /* Only sent a KILL for a nick change */ { struct Client *server; /* Kill the unknown numeric prefix upstream if * it's server still exists: */ if ((server = FindNServer(numeric_prefix)) && cli_from(server) == cptr) sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (Unknown numeric nick)", numeric_prefix, cli_name(&me)); } /* * Things that must be allowed to travel * upstream against an squit: */ if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') || (*ch == 'K' && ch[2] == 'L')) from = cptr; else return 0; } /* Let para[0] point to the name of the sender */ para[0] = cli_name(from); if (cli_from(from) != cptr) { ServerStats->is_wrdi++; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } while (*ch == ' ') ch++; if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } /* * Extract the command code from the packet. Point s to the end * of the command code and calculate the length using pointer * arithmetic. Note: only need length for numerics and *all* * numerics must have parameters and thus a space after the command * code. -avalon */ s = strchr(ch, ' '); /* s -> End of the command code */ len = (s) ? (s - ch) : 0; if (len == 3 && IsDigit(*ch)) { numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = 2; /* destination, and the rest of it */ ServerStats->is_num++; mptr = NULL; /* Init. to avoid stupid compiler warning :/ */ } else { if (s) *s++ = '\0'; /* Version Receive Send * 2.9 Long Long * 2.10.0 Tkn/Long Long * 2.10.10 Tkn/Long Tkn * 2.10.20 Tkn Tkn * * Clients/unreg servers always receive/ * send long commands -record * * And for the record, this trie parser really does not care. - Dianora */ mptr = msg_tree_parse(ch, &tok_tree); if (mptr == NULL) { mptr = msg_tree_parse(ch, &msg_tree); } if (mptr == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ #ifdef DEBUGMODE if (buffer[0] != '\0') { Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } #endif ServerStats->is_unco++; return (-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; } /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ i = 0; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ if (numeric) para[++i] = s; /* preserve the colon to make do_numeric happy */ else para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; if (numeric) return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para)); mptr->count++; return (*mptr->handlers[cli_handler(cptr)]) (cptr, from, i, para); }
/* * 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 (!(IsServer(sptr) || HasUmode(sptr,UMODE_REMOTE))) { if (SeesOperMessages(sptr)) sendto_one(sptr,":%s NOTICE %s :You have no R umode", me.name, parv[0]); else 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 (MyClient(sptr) && !HasUmode(sptr,UMODE_REMOTE) && !MyConnect(acptr)) { sendto_one(sptr,":%s NOTICE %s :You have no R umode",me.name,parv[0]); return 0; } /* ** Notify all opers, if my local link is remotely squitted */ if (MyConnect(acptr) && IsServer(cptr)) { sendto_ops_flag(UMODE_AUSPEX, "%s received SQUIT %s from %s (%s)", me.name, server, get_client_name(sptr,FALSE), comment); logprintf(L_TRACE, "SQUIT From %s : %s (%s)", parv[0], server, comment); } else if (MyConnect(acptr)) sendto_ops_flag(UMODE_SERVNOTICE, "Received SQUIT %s from %s (%s)", acptr->name, get_client_name(sptr,FALSE), comment); return exit_client(cptr, acptr, sptr, comment); }
int ssl_handshake(struct Client *cptr) { char *str; int err; cptr->ssl = (struct SSL*) SSL_new (ctx); // cptr->use_ssl=1; CHK_NULL (cptr->ssl); SSL_set_fd ((SSL *)cptr->ssl, cptr->fd); set_non_blocking(cptr->fd); err = ircd_SSL_accept (cptr, cptr->fd); if ((err)==-1) { irclog(L_ERROR,"Lost connection to %s:Error in SSL_accept()", get_client_name(cptr, TRUE)); SSL_shutdown((SSL *)cptr->ssl); SSL_free((SSL *)cptr->ssl); cptr->ssl = NULL; return 0; } /* Get the cipher - opt */ SetSecure(cptr); irclog (L_DEBUG, "SSL connection using %s", SSL_get_cipher ((SSL *)cptr->ssl)); /* Get client's certificate (note: beware of dynamic * allocation) - opt */ cptr->client_cert = (struct X509*)SSL_get_peer_certificate ((SSL *)cptr->ssl); if (cptr->client_cert != NULL) { irclog (L_DEBUG,"Client certificate:"); str = X509_NAME_oneline (X509_get_subject_name ((X509*)cptr->client_cert), 0, 0); CHK_NULL (str); irclog (L_DEBUG, "\t subject: %s", str); // Bejvavalo // Free (str); free(str); str = X509_NAME_oneline (X509_get_issuer_name ((X509*)cptr->client_cert), 0, 0); CHK_NULL (str); irclog (L_DEBUG, "\t issuer: %s", str); // Bejvavalo // Free (str); free(str); /* We could do all sorts of certificate * verification stuff here before * deallocating the certificate. */ X509_free ((X509*)cptr->client_cert); } else irclog (L_DEBUG, "Client does not have certificate."); return 1; }
/** * Exits a client of *any* type (user, server, etc) * from this server. Also, this generates all necessary prototol * messages that this exit may cause. * * This function implicitly exits all other clients depending on * this connection. * * For convenience, this function returns a suitable value for * m_function return value: * * CPTR_KILLED if (cptr == bcptr) * 0 if (cptr != bcptr) * * This function can be called in two ways: * 1) From before or in parse(), exiting the 'cptr', in which case it was * invoked as exit_client(cptr, cptr, &me,...), causing it to always * return CPTR_KILLED. * 2) Via parse from a m_function call, in which case it was invoked as * exit_client(cptr, acptr, sptr, ...). Here 'sptr' is known; the client * that generated the message in a way that we can assume he already * did remove acptr from memory himself (or in other cases we don't mind * because he will be delinked.) Or invoked as: * exit_client(cptr, acptr/sptr, &me, ...) when WE decide this one should * be removed. * In general: No generated SQUIT or QUIT should be sent to source link * sptr->from. And CPTR_KILLED should be returned if cptr got removed (too). * * --Run * @param cptr Connection currently being handled by read_message. * @param victim Client being killed. * @param killer Client that made the decision to remove \a victim. * @param comment Reason for the exit. * @return CPTR_KILLED if cptr == bcptr, else 0. */ int exit_client(struct Client *cptr, struct Client* victim, struct Client* killer, const char* comment) { struct Client* acptr = 0; struct DLink *dlp; time_t on_for; char comment1[HOSTLEN + HOSTLEN + 2]; assert(killer); if (MyConnect(victim)) { SetFlag(victim, FLAG_CLOSING); if (feature_bool(FEAT_CONNEXIT_NOTICES) && IsUser(victim)) sendto_opmask_butone(0, SNO_CONNEXIT, "Client exiting: %s (%s@%s) [%s] [%s] <%s%s>", cli_name(victim), cli_user(victim)->username, cli_user(victim)->host, comment, ircd_ntoa(&cli_ip(victim)), NumNick(victim) /* two %s's */); update_load(); on_for = CurrentTime - cli_firsttime(victim); if (IsUser(victim) || IsUserPort(victim)) auth_send_exit(victim); if (IsUser(victim)) log_write(LS_USER, L_TRACE, 0, "%Tu %i %s@%s %s %s %s%s %s :%s", cli_firsttime(victim), on_for, cli_user(victim)->username, cli_sockhost(victim), ircd_ntoa(&cli_ip(victim)), IsAccount(victim) ? cli_username(victim) : "0", NumNick(victim), /* two %s's */ cli_name(victim), cli_info(victim)); if (victim != cli_from(killer) /* The source knows already */ && IsClient(victim)) /* Not a Ping struct or Log file */ { if (IsServer(victim) || IsHandshake(victim)) sendcmdto_one(killer, CMD_SQUIT, victim, "%s 0 :%s", cli_name(&me), comment); else if (!IsConnecting(victim)) { if (!IsDead(victim)) { if (IsServer(victim)) sendcmdto_one(killer, CMD_ERROR, victim, ":Closing Link: %s by %s (%s)", cli_name(victim), cli_name(killer), comment); else sendrawto_one(victim, MSG_ERROR " :Closing Link: %s by %s (%s)", cli_name(victim), cli_name(IsServer(killer) ? &his : killer), comment); } } if ((IsServer(victim) || IsHandshake(victim) || IsConnecting(victim)) && (killer == &me || (IsServer(killer) && (strncmp(comment, "Leaf-only link", 14) || strncmp(comment, "Non-Hub link", 12))))) { /* * Note: check user == user needed to make sure we have the same * client */ if (cli_serv(victim)->user && *(cli_serv(victim))->by && (acptr = findNUser(cli_serv(victim)->by))) { if (cli_user(acptr) == cli_serv(victim)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s canceled: %s", acptr, cli_name(victim), comment); } else { /* * not right client, set by to empty string */ acptr = 0; *(cli_serv(victim))->by = '\0'; } } if (killer == &me) sendto_opmask_butone(acptr, SNO_OLDSNO, "Link with %s canceled: %s", cli_name(victim), comment); } } /* * Close the Client connection first. */ close_connection(victim); } if (IsServer(victim)) { if (feature_bool(FEAT_HIS_NETSPLIT)) strcpy(comment1, "*.net *.split"); else { strcpy(comment1, cli_name(cli_serv(victim)->up)); strcat(comment1, " "); strcat(comment1, cli_name(victim)); } if (IsUser(killer)) sendto_opmask_butone(killer, SNO_OLDSNO, "%s SQUIT by %s [%s]:", (cli_user(killer)->server == victim || cli_user(killer)->server == cli_serv(victim)->up) ? "Local" : "Remote", get_client_name(killer, HIDE_IP), cli_name(cli_user(killer)->server)); else if (killer != &me && cli_serv(victim)->up != killer) sendto_opmask_butone(0, SNO_OLDSNO, "Received SQUIT %s from %s :", cli_name(victim), IsServer(killer) ? cli_name(killer) : get_client_name(killer, HIDE_IP)); sendto_opmask_butone(0, SNO_NETWORK, "Net break: %C %C (%s)", cli_serv(victim)->up, victim, comment); } /* * First generate the needed protocol for the other server links * except the source: */ for (dlp = cli_serv(&me)->down; dlp; dlp = dlp->next) { if (dlp->value.cptr != cli_from(killer) && dlp->value.cptr != victim) { if (IsServer(victim)) sendcmdto_one(killer, CMD_SQUIT, dlp->value.cptr, "%s %Tu :%s", cli_name(victim), cli_serv(victim)->timestamp, comment); else if (IsUser(victim) && !HasFlag(victim, FLAG_KILLED)) sendcmdto_one(victim, CMD_QUIT, dlp->value.cptr, ":%s", comment); } } /* Then remove the client structures */ if (IsServer(victim)) exit_downlinks(victim, killer, comment1); exit_one_client(victim, comment); /* * cptr can only have been killed if it was cptr itself that got killed here, * because cptr can never have been a dependent of victim --Run */ return (cptr == victim) ? CPTR_KILLED : 0; }
/* * Check_pings_list() * * inputs - pointer to list to check * output - NONE * side effects - */ static void check_pings_list(dlink_list *list) { char scratch[32]; /* way too generous but... */ struct Client *client_p; /* current local client_p being examined */ int ping = 0; /* ping time value from client */ dlink_node *ptr, *next_ptr; for (ptr = list->head; ptr; ptr = next_ptr) { next_ptr = ptr->next; client_p = ptr->data; /* ** Note: No need to notify opers here. It's ** already done when "FLAGS_DEADSOCKET" is set. */ if (client_p->flags & FLAGS_DEADSOCKET) { /* Ignore it, its been exited already */ continue; } if (IsPerson(client_p)) { if(!IsExemptKline(client_p) && GlobalSetOptions.idletime && !IsOper(client_p) && !IsIdlelined(client_p) && ((CurrentTime - client_p->user->last) > GlobalSetOptions.idletime)) { struct ConfItem *aconf; aconf = make_conf(); aconf->status = CONF_KILL; DupString(aconf->host, client_p->host); DupString(aconf->passwd, "idle exceeder"); DupString(aconf->name, client_p->username); aconf->port = 0; aconf->hold = CurrentTime + 60; add_temp_kline(aconf); sendto_realops_flags(FLAGS_ALL, L_ALL, "Idle time limit exceeded for %s - temp k-lining", get_client_name(client_p, HIDE_IP)); (void)exit_client(client_p, client_p, &me, aconf->passwd); continue; } } if (!IsRegistered(client_p)) ping = CONNECTTIMEOUT; else ping = get_client_ping(client_p); if (ping < (CurrentTime - client_p->lasttime)) { /* * If the client/server hasnt talked to us in 2*ping seconds * and it has a ping time, then close its connection. */ if (((CurrentTime - client_p->lasttime) >= (2 * ping) && (client_p->flags & FLAGS_PINGSENT))) { if (IsServer(client_p) || IsConnecting(client_p) || IsHandshake(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ADMIN, "No response from %s, closing link", get_client_name(client_p, HIDE_IP)); sendto_realops_flags(FLAGS_ALL, L_OPER, "No response from %s, closing link", get_client_name(client_p, MASK_IP)); ilog(L_NOTICE, "No response from %s, closing link", get_client_name(client_p, HIDE_IP)); } (void)ircsprintf(scratch, "Ping timeout: %d seconds", (int)(CurrentTime - client_p->lasttime)); (void)exit_client(client_p, client_p, &me, scratch); continue; } else if ((client_p->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. */ client_p->flags |= FLAGS_PINGSENT; /* not nice but does the job */ client_p->lasttime = CurrentTime - ping; sendto_one(client_p, "PING :%s", me.name); } } /* ping_timeout: */ } }
/* * check_klines * inputs - NONE * output - NONE * side effects - Check all connections for a pending kline against the * client, exit the client if a kline matches. */ void check_klines(void) { struct Client *client_p; /* current local client_p being examined */ struct ConfItem *aconf = (struct ConfItem *)NULL; char *reason; /* pointer to reason string */ dlink_node *ptr, *next_ptr; for (ptr = lclient_list.head; ptr; ptr = next_ptr) { next_ptr = ptr->next; client_p = ptr->data; if (IsMe(client_p)) continue; /* if there is a returned struct ConfItem then kill it */ if ((aconf = find_dline(&client_p->localClient->ip, client_p->localClient->aftype))) { if (aconf->status & CONF_EXEMPTDLINE) continue; sendto_realops_flags(FLAGS_ALL, L_ALL,"DLINE active for %s", get_client_name(client_p, HIDE_IP)); if (ConfigFileEntry.kline_with_connection_closed && ConfigFileEntry.kline_with_reason) { reason = "Connection closed"; if(IsPerson(client_p)) sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, aconf->passwd ? aconf->passwd : "D-lined"); else sendto_one(client_p, "NOTICE DLINE :*** You have been D-lined"); } else { if(ConfigFileEntry.kline_with_connection_closed) reason = "Connection closed"; else if(ConfigFileEntry.kline_with_reason && aconf->passwd) reason = aconf->passwd; else reason = "D-lined"; if(IsPerson(client_p)) sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, reason); else sendto_one(client_p, "NOTICE DLINE :*** You have been D-lined"); } (void)exit_client(client_p, client_p, &me, reason); continue; /* and go examine next fd/client_p */ } if (IsPerson(client_p)) { if (ConfigFileEntry.glines && (aconf = find_gkill(client_p, client_p->username))) { if (IsExemptKline(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "GLINE over-ruled for %s, client is kline_exempt", get_client_name(client_p, HIDE_IP)); continue; } if (IsExemptGline(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "GLINE over-ruled for %s, client is gline_exempt", get_client_name(client_p, HIDE_IP)); continue; } sendto_realops_flags(FLAGS_ALL, L_ALL, "GLINE active for %s", get_client_name(client_p, HIDE_IP)); if(ConfigFileEntry.kline_with_connection_closed && ConfigFileEntry.kline_with_reason) { reason = "Connection closed"; sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, aconf->passwd ? aconf->passwd : "G-lined"); } else { if(ConfigFileEntry.kline_with_connection_closed) reason = "Connection closed"; else if(ConfigFileEntry.kline_with_reason && aconf->passwd) reason = aconf->passwd; else reason = "G-lined"; sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, reason); } (void)exit_client(client_p, client_p, &me, reason); /* and go examine next fd/client_p */ continue; } else if((aconf = find_kill(client_p))) { /* if there is a returned struct ConfItem.. then kill it */ if (IsExemptKline(client_p)) { sendto_realops_flags(FLAGS_ALL, L_ALL, "KLINE over-ruled for %s, client is kline_exempt", get_client_name(client_p, HIDE_IP)); continue; } sendto_realops_flags(FLAGS_ALL, L_ALL, "KLINE active for %s", get_client_name(client_p, HIDE_IP)); if(ConfigFileEntry.kline_with_connection_closed && ConfigFileEntry.kline_with_reason) { reason = "Connection closed"; sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, aconf->passwd ? aconf->passwd : "K-lined"); } else { if(ConfigFileEntry.kline_with_connection_closed) reason = "Connection closed"; else if(ConfigFileEntry.kline_with_reason && aconf->passwd) reason = aconf->passwd; else reason = "K-lined"; sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), me.name, client_p->name, reason); } (void)exit_client(client_p, client_p, &me, reason); continue; } } } /* also check the unknowns list for new dlines */ for (ptr = unknown_list.head; ptr; ptr = next_ptr) { next_ptr = ptr->next; client_p = ptr->data; if((aconf = find_dline(&client_p->localClient->ip, client_p->localClient->aftype))) { if(aconf->status & CONF_EXEMPTDLINE) continue; sendto_one(client_p, "NOTICE DLINE :*** You have been D-lined"); exit_client(client_p, client_p, &me, "D-lined"); } } }
/* * m_svinfo - SVINFO message handler * parv[0] = sender prefix * parv[1] = TS_CURRENT for the server * parv[2] = TS_MIN for the server * parv[3] = server is standalone or connected to non-TS only * parv[4] = server's idea of UTC time */ int m_svinfo(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { time_t deltat; time_t theirtime; if (MyConnect(sptr) && IsUnknown(sptr)) return exit_client(sptr, sptr, sptr, "Need SERVER before SVINFO"); if (!IsServer(sptr) || !MyConnect(sptr) || parc < 5) return 0; if (TS_CURRENT < atoi(parv[2]) || atoi(parv[1]) < TS_MIN) { /* * a server with the wrong TS version connected; since we're * TS_ONLY we can't fall back to the non-TS protocol so * we drop the link -orabidoo */ #ifdef HIDE_SERVERS_IPS sendto_realops("Link %s dropped, wrong TS protocol version (%s,%s)", get_client_name(sptr, MASK_IP), parv[1], parv[2]); #else sendto_realops("Link %s dropped, wrong TS protocol version (%s,%s)", get_client_name(sptr, TRUE), parv[1], parv[2]); #endif return exit_client(sptr, sptr, sptr, "Incompatible TS version"); } sptr->serv->tsversion = atoi(parv[1]); /* * since we're here, might as well set CurrentTime while we're at it */ CurrentTime = time(0); theirtime = atol(parv[4]); deltat = abs(theirtime - CurrentTime); if (deltat > TS_MAX_DELTA) { #ifdef HIDE_SERVERS_IPS sendto_realops( "Link %s dropped, excessive TS delta (my TS=%d, their TS=%d, delta=%d)", get_client_name(sptr, MASK_IP), CurrentTime, theirtime,deltat); #else sendto_realops( "Link %s dropped, excessive TS delta (my TS=%d, their TS=%d, delta=%d)", get_client_name(sptr, TRUE), CurrentTime, theirtime,deltat); #endif return exit_client(sptr, sptr, sptr, "Excessive TS delta"); } if (deltat > TS_WARN_DELTA) { #ifdef HIDE_SERVERS_IPS sendto_realops( "Link %s notable TS delta (my TS=%d, their TS=%d, delta=%d)", get_client_name(sptr, MASK_IP), CurrentTime, theirtime, deltat); #else sendto_realops( "Link %s notable TS delta (my TS=%d, their TS=%d, delta=%d)", get_client_name(sptr, TRUE), CurrentTime, theirtime, deltat); #endif } return 0; }
/* ** m_ltrace - LimitedTRACE... like m_trace() but doesn't return TRACEUSER, TRACEUNKNOWN, or TRACECLASS ** parv[0] = sender prefix ** parv[1] = servername */ int m_ltrace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr = NULL; char *tname; int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS]; int cnt = 0, wilds, dow; static time_t now; if (check_registered(sptr)) return 0; #ifdef SERVERHIDE if (!IsAnOper(sptr)) return 0; #endif if (parc > 2) if (hunt_server(cptr, sptr, ":%s LTRACE %s :%s", 2, parc, parv)) return 0; if (parc > 1) tname = parv[1]; else tname = me.name; switch (hunt_server(cptr, sptr, ":%s LTRACE :%s", 1, parc, parv)) { case HUNTED_PASS: /* note: gets here only if parv[1] exists */ { struct Client *ac2ptr; ac2ptr = next_client(GlobalClientList, tname); if (ac2ptr) sendto_one(sptr, form_str(RPL_TRACELINK), me.name, parv[0], ircd_version, debugmode, tname, ac2ptr->from->name); else sendto_one(sptr, form_str(RPL_TRACELINK), me.name, parv[0], ircd_version, debugmode, tname, "ac2ptr_is_NULL!!"); return 0; } case HUNTED_ISME: break; default: return 0; } if(MyClient(sptr)) sendto_realops_flags(FLAGS_SPY, "ltrace requested by %s (%s@%s) [%s]", sptr->name, sptr->username, sptr->host, sptr->user->server); doall = (parv[1] && (parc > 1)) ? match(tname, me.name): TRUE; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; if(!IsAnOper(sptr) || !dow) /* non-oper traces must be full nicks */ /* lets also do this for opers tracing nicks */ { const char* name; const char* ip; int c_class; acptr = hash_find_client(tname,(struct Client *)NULL); if(!acptr || !IsPerson(acptr)) { /* this should only be reached if the matching target is this server */ sendto_one(sptr, form_str(RPL_ENDOFTRACE),me.name, parv[0], tname); return 0; } name = get_client_name(acptr, FALSE); ip = inetntoa((char*) &acptr->ip); c_class = get_client_class(acptr); if (IsAnOper(acptr)) { sendto_one(sptr, form_str(RPL_TRACEOPERATOR), me.name, parv[0], c_class, name, IsAnOper(sptr)?ip:(IsIPHidden(acptr)?"255.255.255.255":ip), now - acptr->lasttime, (acptr->user)?(now - acptr->user->last):0); } sendto_one(sptr, form_str(RPL_ENDOFTRACE),me.name, parv[0], tname); return 0; } for (i = 0; i < MAXCONNECTIONS; i++) link_s[i] = 0, link_u[i] = 0; if (dow && LIFESUX && !IsOper(sptr)) { sendto_one(sptr,form_str(RPL_LOAD2HI),me.name,parv[0]); return 0; } /* * Count up all the servers and clients in a downlink. */ if (doall) { for (acptr = GlobalClientList; acptr; acptr = acptr->next) { if (IsServer(acptr)) ++link_s[acptr->from->fd]; } } /* report all direct connections */ now = time(NULL); for (i = 0; i <= highest_fd; i++) { const char* name; const char* ip; int c_class; if (!(acptr = local[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsAnOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && !match(tname, acptr->name)) continue; if (!dow && irccmp(tname, acptr->name)) continue; name = get_client_name(acptr, FALSE); ip = inetntoa((const char*) &acptr->ip); c_class = get_client_class(acptr); switch(acptr->status) { case STAT_HANDSHAKE: #ifdef HIDE_SERVERS_IPS name=get_client_name(acptr, MASK_IP); #endif sendto_one(sptr, form_str(RPL_TRACEHANDSHAKE), me.name, parv[0], c_class, name); cnt++; break; case STAT_CONNECTING: #ifdef HIDE_SERVERS_IPS name=get_client_name(acptr, MASK_IP); #endif sendto_one(sptr, form_str(RPL_TRACECONNECTING), me.name, parv[0], c_class, name); cnt++; break; case STAT_ME: break; case STAT_CLIENT: /* Well, most servers don't have a LOT of OPERs... let's show them too */ if ((IsAnOper(sptr) && (MyClient(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) sendto_one(sptr, form_str(RPL_TRACEOPERATOR), me.name, parv[0], c_class, name, IsAnOper(sptr)?ip:(IsIPHidden(acptr)?"255.255.255.255":ip), now - acptr->lasttime, (acptr->user)?(now - acptr->user->last):0); cnt++; } break; case STAT_SERVER: #if 0 if (acptr->serv->user) sendto_one(sptr, form_str(RPL_TRACESERVER), me.name, parv[0], c_class, link_s[i], link_u[i], name, acptr->serv->by, acptr->serv->user->username, acptr->serv->user->host, now - acptr->lasttime); else #endif #ifdef HIDE_SERVERS_IPS name=get_client_name(acptr, MASK_IP); #endif sendto_one(sptr, form_str(RPL_TRACESERVER), me.name, parv[0], c_class, link_s[i], link_u[i], name, *(acptr->serv->by) ? acptr->serv->by : "*", "*", me.name, now - acptr->lasttime); cnt++; break; default: /* ...we actually shouldn't come here... --msa */ sendto_one(sptr, form_str(RPL_TRACENEWTYPE), me.name, parv[0], name); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (!IsAnOper(sptr) || !cnt) { if (cnt) return 0; /* let the user have some idea that its at the end of the * trace */ sendto_one(sptr, form_str(RPL_TRACESERVER), me.name, parv[0], 0, link_s[me.fd], link_u[me.fd], me.name, "*", "*", me.name); sendto_one(sptr, form_str(RPL_ENDOFTRACE),me.name, parv[0],tname); return 0; } sendto_one(sptr, form_str(RPL_ENDOFTRACE),me.name, parv[0],tname); return 0; }
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); }
static void do_trace(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { int i; struct Client *acptr; struct Client *acptr2; const struct ConnectionClass* cl; char* tname; int doall; int *link_s; int *link_u; int cnt = 0; int wilds; int dow; if (parc < 2 || BadPtr(parv[1])) { /* just "TRACE" without parameters. Must be from local client */ parc = 1; acptr = &me; tname = cli_name(&me); i = HUNTED_ISME; } else if (parc < 3 || BadPtr(parv[2])) { /* No target specified. Make one before propagating. */ parc = 2; tname = parv[1]; if ((acptr = find_match_server(parv[1])) || ((acptr = FindClient(parv[1])) && !MyUser(acptr))) { if (IsUser(acptr)) parv[2] = cli_name(cli_user(acptr)->server); else parv[2] = cli_name(acptr); parc = 3; parv[3] = 0; if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, IsServer(acptr), "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; } else i = HUNTED_ISME; } else { /* Got "TRACE <tname> :<target>" */ parc = 3; if (MyUser(sptr) || Protocol(cptr) < 10) acptr = find_match_server(parv[2]); else acptr = FindNServer(parv[2]); if ((i = hunt_server_cmd(sptr, CMD_TRACE, cptr, 0, "%s :%C", 2, parc, parv)) == HUNTED_NOSUCH) return; tname = parv[1]; } if (i == HUNTED_PASS) { if (!acptr) acptr = next_client(GlobalClientList, tname); else acptr = cli_from(acptr); send_reply(sptr, RPL_TRACELINK, version, debugmode, tname, acptr ? cli_name(cli_from(acptr)) : "<No_match>"); return; } doall = (parv[1] && (parc > 1)) ? !match(tname, cli_name(&me)) : 1; wilds = !parv[1] || strchr(tname, '*') || strchr(tname, '?'); dow = wilds || doall; /* Don't give (long) remote listings to lusers */ if (dow && !MyConnect(sptr) && !IsAnOper(sptr)) { send_reply(sptr, RPL_TRACEEND); return; } link_s = MyCalloc(2 * maxconnections, sizeof(link_s[0])); link_u = link_s + maxconnections; if (doall) { for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { if (IsUser(acptr)) link_u[cli_fd(cli_from(acptr))]++; else if (IsServer(acptr)) link_s[cli_fd(cli_from(acptr))]++; } } /* report all direct connections */ for (i = 0; i <= HighestFd; i++) { const char *conClass; if (!(acptr = LocalClientArray[i])) /* Local Connection? */ continue; if (IsInvisible(acptr) && dow && !(MyConnect(sptr) && IsOper(sptr)) && !IsAnOper(acptr) && (acptr != sptr)) continue; if (!doall && wilds && match(tname, cli_name(acptr))) continue; if (!dow && 0 != ircd_strcmp(tname, cli_name(acptr))) continue; conClass = get_client_class(acptr); switch (cli_status(acptr)) { case STAT_CONNECTING: send_reply(sptr, RPL_TRACECONNECTING, conClass, cli_name(acptr)); cnt++; break; case STAT_HANDSHAKE: send_reply(sptr, RPL_TRACEHANDSHAKE, conClass, cli_name(acptr)); cnt++; break; case STAT_ME: break; case STAT_UNKNOWN: case STAT_UNKNOWN_USER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, get_client_name(acptr, HIDE_IP)); cnt++; break; case STAT_UNKNOWN_SERVER: send_reply(sptr, RPL_TRACEUNKNOWN, conClass, "Unknown Server"); cnt++; break; case STAT_USER: /* Only opers see users if there is a wildcard but anyone can see all the opers. */ if ((IsAnOper(sptr) && (MyUser(sptr) || !(dow && IsInvisible(acptr)))) || !dow || IsAnOper(acptr)) { if (IsAnOper(acptr)) send_reply(sptr, RPL_TRACEOPERATOR, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); else send_reply(sptr, RPL_TRACEUSER, conClass, get_client_name(acptr, SHOW_IP), CurrentTime - cli_lasttime(acptr)); cnt++; } break; /* * Connection is a server * * Serv <class> <nS> <nC> <name> <ConnBy> <last> <age> * * class Class the server is in * nS Number of servers reached via this link * nC Number of clients reached via this link * name Name of the server linked * ConnBy Who established this link * last Seconds since we got something from this link * age Seconds this link has been alive * * Additional comments etc...... -Cym-<*****@*****.**> */ case STAT_SERVER: if (cli_serv(acptr)->user) { if (!cli_serv(acptr)->by[0] || !(acptr2 = findNUser(cli_serv(acptr)->by)) || (cli_user(acptr2) != cli_serv(acptr)->user)) acptr2 = NULL; send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), acptr2 ? cli_name(acptr2) : "*", cli_serv(acptr)->user->username, cli_serv(acptr)->user->host, CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); } else send_reply(sptr, RPL_TRACESERVER, conClass, link_s[i], link_u[i], cli_name(acptr), (*(cli_serv(acptr))->by) ? cli_serv(acptr)->by : "*", "*", cli_name(&me), CurrentTime - cli_lasttime(acptr), CurrentTime - cli_serv(acptr)->timestamp); cnt++; break; default: /* We actually shouldn't come here, -msa */ send_reply(sptr, RPL_TRACENEWTYPE, get_client_name(acptr, HIDE_IP)); cnt++; break; } } /* * Add these lines to summarize the above which can get rather long * and messy when done remotely - Avalon */ if (IsAnOper(sptr) && doall) { for (cl = get_class_list(); cl; cl = cl->next) { if (Links(cl) > 1) send_reply(sptr, RPL_TRACECLASS, ConClass(cl), Links(cl) - 1); } } send_reply(sptr, RPL_TRACEEND); MyFree(link_s); }
/* * 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) { #ifndef NO_IDENT struct SOCKADDR_IN us, them; SOCK_LEN_TYPE ulen, tlen; # if defined(USE_IAUTH) if ((iauth_options & XOPT_REQUIRED) && adfd < 0) return; # endif Debug((DEBUG_NOTICE,"start_auth(%x) fd %d status %d", cptr, cptr->fd, cptr->status)); if ((cptr->authfd = socket(AFINET, SOCK_STREAM, 0)) == -1) { # ifdef USE_SYSLOG syslog(LOG_ERR, "Unable to create auth socket for %s:%m", get_client_name(cptr,TRUE)); # endif Debug((DEBUG_ERROR, "Unable to create auth socket for %s:%s", get_client_name(cptr, TRUE), strerror(get_sockerr(cptr)))); ircstp->is_abad++; return; } if (cptr->authfd >= (MAXCONNECTIONS - 2)) { sendto_flag(SCH_ERROR, "Can't allocate fd for auth on %s", get_client_name(cptr, TRUE)); (void)close(cptr->authfd); return; } set_non_blocking(cptr->authfd, cptr); /* get remote host peer - so that we get right interface -- jrg */ tlen = ulen = sizeof(us); if (getpeername(cptr->fd, (struct sockaddr *)&them, &tlen) < 0) { /* we probably don't need this error message -kalt */ report_error("getpeername for auth request %s:%s", cptr); close(cptr->authfd); cptr->authfd = -1; return; } them.SIN_FAMILY = AFINET; /* We must bind the local end to the interface that they connected to: The local system might have more than one network address, and RFC931 check only sends port numbers: server takes IP addresses from query socket -- jrg */ (void)getsockname(cptr->fd, (struct sockaddr *)&us, &ulen); us.SIN_FAMILY = AFINET; # if defined(USE_IAUTH) if (adfd >= 0) { char abuf[BUFSIZ]; # ifdef INET6 sprintf(abuf, "%d C %s %u ", cptr->fd, inetntop(AF_INET6, (char *)&them.sin6_addr, ipv6string, sizeof(ipv6string)), ntohs(them.SIN_PORT)); sprintf(abuf+strlen(abuf), "%s %u", inetntop(AF_INET6, (char *)&us.sin6_addr, ipv6string, sizeof(ipv6string)), ntohs(us.SIN_PORT)); # else sprintf(abuf, "%d C %s %u ", cptr->fd, inetntoa((char *)&them.sin_addr),ntohs(them.SIN_PORT)); sprintf(abuf+strlen(abuf), "%s %u", inetntoa((char *)&us.sin_addr), ntohs(us.SIN_PORT)); # endif if (sendto_iauth(abuf) == 0) { close(cptr->authfd); cptr->authfd = -1; cptr->flags |= FLAGS_XAUTH; return; } } # endif # ifdef INET6 Debug((DEBUG_NOTICE,"auth(%x) from %s %x %x", cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr, ipv6string, sizeof(ipv6string)), us.sin6_addr.s6_addr[14], us.sin6_addr.s6_addr[15])); # else Debug((DEBUG_NOTICE,"auth(%x) from %s", cptr, inetntoa((char *)&us.sin_addr))); # endif them.SIN_PORT = htons(113); us.SIN_PORT = htons(0); /* bind assigns us a port */ if (bind(cptr->authfd, (struct SOCKADDR *)&us, ulen) >= 0) { (void)getsockname(cptr->fd, (struct SOCKADDR *)&us, &ulen); # ifdef INET6 Debug((DEBUG_NOTICE,"auth(%x) to %s", cptr, inet_ntop(AF_INET6, (char *)&them.sin6_addr, ipv6string, sizeof(ipv6string)))); # else Debug((DEBUG_NOTICE,"auth(%x) to %s", cptr, inetntoa((char *)&them.sin_addr))); # endif (void)alarm((unsigned)4); if (connect(cptr->authfd, (struct SOCKADDR *)&them, tlen) == -1 && errno != EINPROGRESS) { # ifdef INET6 Debug((DEBUG_ERROR, "auth(%x) connect failed to %s - %d", cptr, inet_ntop(AF_INET6, (char *)&them.sin6_addr, ipv6string, sizeof(ipv6string)), errno)); # else Debug((DEBUG_ERROR, "auth(%x) connect failed to %s - %d", cptr, inetntoa((char *)&them.sin_addr), errno)); # endif ircstp->is_abad++; /* * No error report from this... */ (void)alarm((unsigned)0); (void)close(cptr->authfd); cptr->authfd = -1; return; } (void)alarm((unsigned)0); } else { report_error("binding stream socket for auth request %s:%s", cptr); # ifdef INET6 Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d", cptr, inet_ntop(AF_INET6, (char *)&us.sin6_addr, ipv6string, sizeof(ipv6string)), ntohs(us.SIN_PORT), errno)); # else Debug((DEBUG_ERROR,"auth(%x) bind failed on %s port %d - %d", cptr, inetntoa((char *)&us.sin_addr), ntohs(us.SIN_PORT), errno)); # endif } cptr->flags |= (FLAGS_WRAUTH|FLAGS_AUTH); if (cptr->authfd > highest_fd) highest_fd = cptr->authfd; #endif return; }
/** Parse a line of data from a user. * NOTE: parse_*() should not be called recursively by any other * functions! * @param[in] cptr Client that sent the data. * @param[in] buffer Start of input line. * @param[in] bufend End of input line. * @return 0 on success, -1 on parse error, or CPTR_KILLED if message * handler returns it. */ int parse_client(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch; char* s; int i; int paramcount; int isshun = 0; int lagmin = -1; int lagfactor = -1; struct Message* mptr; MessageHandler handler = 0; Debug((DEBUG_DEBUG, "Client Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */ if (*ch == ':') /* Is any client doing this ? */ { for (++ch; *ch && *ch != ' '; ++ch) ; /* Ignore sender prefix from client */ while (*ch == ' ') ch++; /* Advance to command */ } if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } if ((s = strchr(ch, ' '))) *s++ = '\0'; expire_shuns(); if (IsRegistered(cptr)) { if (cli_user(cptr)->username && cli_user(cptr)->host) { if (shun_lookup(cptr, 0)) isshun = 1; } } if ((mptr = msg_tree_parse(ch, &msg_tree)) == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ if (buffer[0] != '\0' && !isshun) { if (IsUser(from)) send_reply(from, ERR_UNKNOWNCOMMAND, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } ServerStats->is_unco++; return (-1); } if (isshun && !(mptr->flags & MFLG_NOSHUN)) return 0; paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; lagmin = get_lag_min(cptr); lagfactor = get_lag_factor(cptr); if (lagmin < 0) lagmin = 2; if (lagfactor < 0) lagfactor = 120; if (((mptr->flags & MFLG_SLOW) || !IsAnOper(cptr)) && lagfactor > 0) cli_since(cptr) += (lagmin + i / lagfactor); /* * Allow only 1 msg per 2 seconds * (on average) to prevent dumping. * to keep the response rate up, * bursts of up to 5 msgs are allowed * -SRB */ /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ if (mptr->flags & MFLG_EXTRA) { /* This is a horrid kludge to avoid changing the command handler * argument list. */ para[1] = (char*)mptr->extra; i = 1; } else { i = 0; } if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; ++mptr->count; handler = mptr->handlers[cli_handler(cptr)]; assert(0 != handler); if (!feature_bool(FEAT_IDLE_FROM_MSG) && IsUser(cptr) && handler != m_ping && handler != m_ignore) cli_user(from)->last = CurrentTime; return (*handler) (cptr, from, i, para); }
/* ** 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_funtion return value: ** ** FLUSH_BUFFER if (cptr == sptr) ** 0 if (cptr != sptr) ** ** Parameters: ** ** aClient *cptr ** The local client originating the exit or NULL, if this ** exit is generated by this server for internal reasons. ** This will not get any of the generated messages. ** aClient *sptr ** Client exiting ** aClient *from ** Client firing off this Exit, never NULL! ** char *comment ** Reason for the exit */ int exit_client(aClient *cptr, aClient *sptr, aClient *from, const char *comment) { char comment1[HOSTLEN + HOSTLEN + 2]; if (MyConnect(sptr)) { if (sptr->flags & FLAGS_KILLED) { sendto_flag(SCH_NOTICE, "Killed: %s.", get_client_name(sptr, TRUE)); sptr->exitc = EXITC_KILL; } sptr->flags |= FLAGS_CLOSING; #if (defined(FNAME_USERLOG) || defined(FNAME_CONNLOG) \ || defined(USE_SERVICES)) \ || (defined(USE_SYSLOG) && (defined(SYSLOG_USERS) || defined(SYSLOG_CONN))) if (IsPerson(sptr)) { # if defined(FNAME_USERLOG) || defined(USE_SERVICES) || \ (defined(USE_SYSLOG) && defined(SYSLOG_USERS)) sendto_flog(sptr, EXITC_REG, sptr->user->username, sptr->user->host); # endif # if defined(CLIENTS_CHANNEL) && (CLIENTS_CHANNEL_LEVEL & CCL_QUIT) sendto_flag(SCH_CLIENT, "%s %s %s %s QUIT %c" # if (CLIENTS_CHANNEL_LEVEL & CCL_QUITINFO) " :%s" # endif , sptr->user->uid, sptr->name, sptr->user->username, sptr->user->host, sptr->exitc # if (CLIENTS_CHANNEL_LEVEL & CCL_QUITINFO) , comment # endif ); # endif } else if (!IsService(sptr)) { # if defined(FNAME_CONNLOG) || defined(USE_SERVICES) || \ (defined(USE_SYSLOG) && defined(SYSLOG_CONN)) if (sptr->exitc == '\0' || sptr->exitc == EXITC_REG) { sptr->exitc = EXITC_UNDEF; } sendto_flog(sptr, sptr->exitc, sptr->user && sptr->user->username ? sptr->user->username : "", #ifdef UNIXPORT (IsUnixSocket(sptr)) ? me.sockhost : #endif ((sptr->hostp) ? sptr->hostp->h_name : sptr->sockhost)); # endif } #endif if (MyConnect(sptr)) { if (IsPerson(sptr)) { istat.is_myclnt--; } else if (IsServer(sptr)) { istat.is_myserv--; } else if (IsService(sptr)) { istat.is_myservice--; } else { istat.is_unknown--; } if (istat.is_myclnt % CLCHNO == 0 && istat.is_myclnt != istat.is_l_myclnt) { sendto_flag(SCH_NOTICE, "Local %screase from %d to %d clients " "in %d seconds", istat.is_myclnt>istat.is_l_myclnt?"in":"de", istat.is_l_myclnt, istat.is_myclnt, timeofday - istat.is_l_myclnt_t); istat.is_l_myclnt_t = timeofday; istat.is_l_myclnt = istat.is_myclnt; } /* Send SQUIT message to 2.11 servers to tell them * the squit reason for rebroadcast on the other side * - jv */ if (IsServer(sptr)) { sendto_one(sptr, ":%s SQUIT %s :%s", me.serv->sid, sptr->serv->sid, comment); } if (cptr != NULL && sptr != cptr) { sendto_one(sptr, "ERROR :Closing Link: " "%s %s (%s)", get_client_name(sptr,FALSE), cptr->name, comment); } else { sendto_one(sptr, "ERROR :Closing Link: %s (%s)", get_client_name(sptr,FALSE), comment); } if (sptr->auth != sptr->username) { istat.is_authmem -= strlen(sptr->auth) + 1; istat.is_auth -= 1; MyFree(sptr->auth); sptr->auth = sptr->username; } } /* ** 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... ** now, I think it harms big client servers... - krys ** ** 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. */ close_connection(sptr); } /* if (MyConnect(sptr) */ if (IsServer(sptr)) { /* Remove all dependent servers and clients. */ if (!IsMasked(sptr)) { sprintf(comment1, "%s %s", sptr->serv->up->name, sptr->name); } else { /* It was a masked server, the squit reason should ** give the right quit reason for clients. */ strncpyzt(comment1, comment, sizeof(comment1)); } /* cptr != sptr means non-local server */ if (cptr != sptr && nextconnect == 0 && find_conf_name(sptr->name, (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER))) { /* try AC */ nextconnect = timeofday + HANGONRETRYDELAY; } exit_server(sptr, sptr, from, comment, comment1); check_split(); if ((cptr == sptr)) { /* It serves no purpose. --B. sendto_flag(SCH_SERVER, "Sending SQUIT %s (%s)", cptr->name, comment); */ return FLUSH_BUFFER; } return 0; } /* ** Try to guess from comment if the client is exiting ** normally (KILL or issued QUIT), or if it is splitting ** It requires comment for splitting users to be ** "server.some.where splitting.some.where" */ comment1[0] = '\0'; if ((sptr->flags & FLAGS_KILLED) == 0) { if (comment[0] == '"') { /* definitely user quit, see m_quit */ sptr->flags |= FLAGS_QUIT; } else { const char *c = comment; int i = 0; while (*c && *c != ' ') if (*c++ == '.') i++; if (*c++ && i) { i = 0; while (*c && *c != ' ') if (*c++ == '.') i++; if (!i || *c) sptr->flags |= FLAGS_QUIT; } else { sptr->flags |= FLAGS_QUIT; } } if (sptr == cptr && !(sptr->flags & FLAGS_QUIT)) { /* ** This will avoid nick delay to be abused by ** letting local users put a comment looking ** like a server split. */ strncpyzt(comment1, comment, HOSTLEN + HOSTLEN); strcat(comment1, " "); sptr->flags |= FLAGS_QUIT; } } exit_one_client(cptr, sptr, from, (*comment1) ? comment1 : comment); /* XXX: we probably should not call it every client exit */ /* checking every server quit should suffice --B. */ /* check_split(); */ return cptr == sptr ? FLUSH_BUFFER : 0; }
int m_chall(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { static char blank[] = ""; char* salt = parc > 4 ? parv[4] : blank; char* chall = parc > 3 ? parv[3] : blank; char* remote_host = parc > 2 ? parv[2] : blank; char* host = parc > 1 ? parv[1] : blank; struct ConfItem *n_conf, *c_conf; struct Client* acptr; if (IsPerson(sptr)) { if (IsServer(cptr)) { sendto_ops_flag(UMODE_SERVNOTICE,"CHALL command from remote user %s -- %s is a hacked server", get_client_name(sptr,HIDE_IP), get_client_name(cptr,HIDE_IP)); } else { sendto_one(sptr, form_str(ERR_UNKNOWNCOMMAND), me.name, parv[0], "CHALL"); } return 0; } /* If we know this, we shouldn't be getting a chall */ if (IsPerson(cptr) || IsServer(cptr)) return 0; /* What are they trying to auth against, is it me? */ if (irccmp(remote_host, me.name) != 0) { sendto_one(cptr, "ERROR :I am %s, not %s", me.name, remote_host); sendto_ops_flag(UMODE_SERVCONNECT, "Server claiming to be %s challenged thinking I was %s, rejected", host, remote_host); return exit_client(cptr, cptr, &me, "Sorry, wrong number"); } if ((acptr = find_server(host))) { sendto_one(cptr,"ERROR :Server %s already exists", host); if (!(acptr->from->caps & CAP_SERVICES)) /* If it's connected to services, it's actually a server jupe. Silently ignore. */ sendto_ops_flag(UMODE_EXTERNAL, "Challenge for %s rejected, server already exists", host); return exit_client(cptr, cptr, &me, "Server already exists"); } sendto_ops_flag(UMODE_SERVCONNECT, "CHALL received, trying to use N:line for %s", host); if (parc < 5) { sendto_one(cptr, "ERROR: Invalid challenge"); sendto_ops_flag(UMODE_SERVCONNECT, "Invalid CHALL from remote host for %s", host); return 0; } /* See if we got c and n lines. */ if (!(n_conf = find_conf_by_name(host, CONF_NOCONNECT_SERVER))) { sendto_ops_flag(UMODE_SERVCONNECT, "Challenge rejected. No N line for server %s", host); logprintf(L_NOTICE, "Challenge rejected. No N line for server %s", host); sendto_one(cptr, "ERROR :Access denied. No N line"); return exit_client(cptr, cptr, cptr, "Access denied. No N line"); } if (!(c_conf = find_conf_by_name(host, CONF_CONNECT_SERVER))) { sendto_ops_flag(UMODE_SERVCONNECT, "Challenge rejected. No C line for server %s", host); logprintf(L_NOTICE, "Challenge rejected. No C line for server %s", host); sendto_one(cptr, "ERROR :Access denied. No C line"); return exit_client(cptr, cptr, cptr, "Access denied. No C line"); } /* Check if the ip matches the c/n IPs */ #ifdef IPV6 if((memcmp((char*)cptr->ip.S_ADDR, (char*)c_conf->ipnum.S_ADDR, sizeof(struct IN_ADDR) != 0)) || (memcmp((char*)cptr->ip.S_ADDR, (char*)n_conf->ipnum.S_ADDR, sizeof(struct IN_ADDR)) != 0)) { #else if ( (cptr->ip.s_addr != c_conf->ipnum.s_addr) || (cptr->ip.s_addr != n_conf->ipnum.s_addr)) { #endif sendto_ops_flag(UMODE_SERVCONNECT, "Challenge rejected. C/N line IP doesn't match connection"); logprintf(L_NOTICE, "Challenge rejected. C/N line IP doesn't match connection"); sendto_one(cptr, "ERROR :Access denied. C/N line don't match you"); return exit_client(cptr, cptr, cptr, "Access denied. C/N line don't match"); } /* Ok, it got c/n lines and correct ip, lets challenge it back */ if (!Challenged(cptr)) cr_sendchallenge(cptr, n_conf); /* And send a response. */ cr_sendresponse(cptr, c_conf, chall, salt); /* check for TS as in m_pass */ if (parc > 5) { if (0 == irccmp(parv[5], "TS")) cptr->tsinfo = TS_DOESTS; } return 0; }
/* ** 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, const char *comment) { Reg aClient *acptr; Reg int i; Reg Link *lp; invLink *ilp; /* ** For a server or user quitting, propagage the information to ** other servers (except to the one where is came from (cptr)) */ if (IsMe(sptr)) { sendto_flag(SCH_ERROR, "ERROR: tried to exit me! : %s", comment); return; /* ...must *never* exit self!! */ } else if (IsServer(sptr)) { /* ** Old sendto_serv_but_one() call removed because we now ** need to send different names to different servers ** (domain name matching) */ if (!IsMasked(sptr)) { istat.is_serv--; } if (!IsBursting(sptr)) { istat.is_eobservers--; } for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (!(sptr->flags & FLAGS_SQUIT)) { /* Make sure we only send the last SQUIT ** to a 2.11. */ continue; } if ((acptr->flags & FLAGS_HIDDEN) && !IsMasked(sptr)) { /* We need a special SQUIT reason, so ** the remote server can send the ** right quit message. */ sendto_one(acptr, ":%s SQUIT %s :%s %s", sptr->serv->up->serv->sid, sptr->serv->sid, sptr->serv->up->name, sptr->name); } else { sendto_one(acptr, ":%s SQUIT %s :%s", sptr->serv->up->serv->sid, sptr->serv->sid, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SQUIT, sptr->serv, sptr, ":%s SQUIT %s :%s", from->name, sptr->name, comment); #endif del_from_sid_hash_table(sptr->serv); remove_server_from_tree(sptr); /* remove server from svrtop */ unregister_server(sptr); } else if (!IsPerson(sptr) && !IsService(sptr)) { /* ...this test is *dubious*, would need ** some thougth.. but for now it plugs a ** nasty hole in the server... --msa */ ; /* Nothing */ } else if (sptr->name[0] && !IsService(sptr)) /* clean 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) { if ((sptr->flags & FLAGS_SPLIT) == 0) { sendto_serv_butone(cptr, ":%s QUIT :%s", sptr->user->uid, comment); #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT| SERVICE_WANT_RQUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } else { if (sptr->flags & FLAGS_HIDDEN) /* joys of hostmasking */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr =local[fdas.fd[i]]) || acptr == cptr || IsMe(acptr)) continue; if (acptr->flags & FLAGS_HIDDEN) sendto_one(acptr, ":%s QUIT :%s", sptr->user->uid, comment); } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); #endif } } #ifdef USE_SERVICES else { /* Send QUIT to services which desire such as well. ** Services with both _QUIT and _KILL will get both ** for now --jv */ check_services_butone(SERVICE_WANT_QUIT, (sptr->user) ? sptr->user->servp : NULL, cptr, ":%s QUIT :%s", sptr->name, comment); } #endif /* ** 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) { if (IsInvisible(sptr)) { istat.is_user[1]--; sptr->user->servp->usercnt[1]--; } else { istat.is_user[0]--; sptr->user->servp->usercnt[0]--; } if (IsAnOper(sptr)) { sptr->user->servp->usercnt[2]--; istat.is_oper--; } sendto_common_channels(sptr, ":%s QUIT :%s", sptr->name, comment); if (!(acptr = cptr ? cptr : sptr->from)) acptr = sptr; while ((lp = sptr->user->channel)) { /* ** Mark channels from where remote chop left, ** this will eventually lock the channel. ** close_connection() has already been called, ** it makes MyConnect == False - krys */ if (sptr != cptr) { if (*lp->value.chptr->chname == '!') { if (!(sptr->flags &FLAGS_QUIT)) lp->value.chptr->history = timeofday + LDELAYCHASETIMELIMIT; } else if ( #ifndef BETTER_CDELAY !(sptr->flags & FLAGS_QUIT) && #endif is_chan_op(sptr, lp->value.chptr)) { lp->value.chptr->history = timeofday + DELAYCHASETIMELIMIT; } } if (IsAnonymous(lp->value.chptr) && !IsQuiet(lp->value.chptr)) { sendto_channel_butserv(lp->value.chptr, sptr, ":%s PART %s :None", sptr->name, lp->value.chptr->chname); } remove_user_from_channel(sptr,lp->value.chptr); } /* Clean up invitefield */ while ((ilp = sptr->user->invited)) { del_invite(sptr, ilp->chptr); /* again, this is all that is needed */ } /* remove from uid hash table */ if (sptr->user) { del_from_uid_hash_table(sptr->user->uid, sptr); } /* Add user to history */ #ifndef BETTER_NDELAY add_history(sptr, (sptr->flags & FLAGS_QUIT) ? &me : NULL); #else add_history(sptr, (sptr == cptr) ? &me : NULL); #endif off_history(sptr); #ifdef USE_HOSTHASH del_from_hostname_hash_table(sptr->user->host, sptr->user); #endif #ifdef USE_IPHASH del_from_ip_hash_table(sptr->user->sip, sptr->user); #endif } } else if (sptr->name[0] && IsService(sptr)) { /* ** 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) { /* ** A service quitting is annoying, It has to be sent ** to connected servers depending on ** sptr->service->dist */ for (i = fdas.highest; i >= 0; i--) { if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) { continue; } if (match(sptr->service->dist, acptr->name) && match(sptr->service->dist, acptr->serv->sid)) { continue; } sendto_one(acptr, ":%s QUIT :%s", sptr->name, comment); } } #ifdef USE_SERVICES check_services_butone(SERVICE_WANT_SERVICE, NULL, NULL, ":%s QUIT :%s", sptr->name, comment); #endif /* MyConnect(sptr) is always FALSE here */ if (cptr == sptr) { sendto_flag(SCH_NOTICE, "Service %s disconnected", get_client_name(sptr, TRUE)); } sendto_flag(SCH_SERVICE, "Received QUIT %s from %s (%s)", sptr->name, from->name, comment); istat.is_service--; } /* 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_client_from_list(sptr); return; }
/* * ms_svinfo - SVINFO message handler * parv[0] = sender prefix * parv[1] = TS_CURRENT for the server * parv[2] = TS_MIN for the server * parv[3] = server is standalone or connected to non-TS only * parv[4] = server's idea of UTC time */ static void ms_svinfo(struct Client *client_p, struct Client *source_p, int parc, char *parv[]) { time_t deltat; time_t theirtime; if (MyConnect(source_p) && IsUnknown(source_p)) { exit_client(source_p, source_p, source_p, "Need SERVER before SVINFO"); return; } if (!IsServer(source_p) || !MyConnect(source_p) || parc < 5) return; if (TS_CURRENT < atoi(parv[2]) || atoi(parv[1]) < TS_MIN) { /* * a server with the wrong TS version connected; since we're * TS_ONLY we can't fall back to the non-TS protocol so * we drop the link -orabidoo */ sendto_realops_flags(UMODE_ALL, L_ADMIN, "Link %s dropped, wrong TS protocol version (%s,%s)", get_client_name(source_p, SHOW_IP), parv[1], parv[2]); sendto_realops_flags(UMODE_ALL, L_OPER, "Link %s dropped, wrong TS protocol version (%s,%s)", get_client_name(source_p, MASK_IP), parv[1], parv[2]); exit_client(source_p, source_p, source_p, "Incompatible TS version"); return; } /* * since we're here, might as well set CurrentTime while we're at it */ set_time(); theirtime = atol(parv[4]); deltat = abs(theirtime - CurrentTime); if (deltat > ConfigFileEntry.ts_max_delta) { sendto_realops_flags(UMODE_ALL, L_ADMIN, "Link %s dropped, excessive TS delta (my TS=%lu, their TS=%lu, delta=%d)", get_client_name(source_p, SHOW_IP), (unsigned long) CurrentTime, (unsigned long) theirtime, (int) deltat); sendto_realops_flags(UMODE_ALL, L_OPER, "Link %s dropped, excessive TS delta (my TS=%lu, their TS=%lu, delta=%d)", get_client_name(source_p, MASK_IP), (unsigned long) CurrentTime, (unsigned long) theirtime, (int) deltat); ilog(L_NOTICE, "Link %s dropped, excessive TS delta (my TS=%lu, their TS=%lu, delta=%d)", get_client_name(source_p, SHOW_IP), (unsigned long) CurrentTime, (unsigned long) theirtime, (int) deltat); exit_client(source_p, source_p, source_p, "Excessive TS delta"); return; } if (deltat > ConfigFileEntry.ts_warn_delta) { sendto_realops_flags(UMODE_ALL, L_ALL, "Link %s notable TS delta (my TS=%lu, their TS=%lu, delta=%d)", source_p->name, (unsigned long) CurrentTime, (unsigned long) theirtime, (int) deltat); } }
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; }
/* * The first step in the restore process is for the user to * select a list of JobIds from which he will subsequently * select which files are to be restored. * * Returns: 2 if filename list made * 1 if jobid list made * 0 on error */ static int user_select_jobids_or_files(UAContext *ua, RESTORE_CTX *rx) { char *p; char date[MAX_TIME_LENGTH]; bool have_date = false; /* Include current second if using current time */ utime_t now = time(NULL) + 1; JobId_t JobId; JOB_DBR jr = { (JobId_t)-1 }; bool done = false; int i, j; const char *list[] = { _("List last 20 Jobs run"), _("List Jobs where a given File is saved"), _("Enter list of comma separated JobIds to select"), _("Enter SQL list command"), _("Select the most recent backup for a client"), _("Select backup for a client before a specified time"), _("Enter a list of files to restore"), _("Enter a list of files to restore before a specified time"), _("Find the JobIds of the most recent backup for a client"), _("Find the JobIds for a backup for a client before a specified time"), _("Enter a list of directories to restore for found JobIds"), _("Select full restore to a specified Job date"), _("Cancel"), NULL }; const char *kw[] = { /* * These keywords are handled in a for loop */ "jobid", /* 0 */ "current", /* 1 */ "before", /* 2 */ "file", /* 3 */ "directory", /* 4 */ "select", /* 5 */ "pool", /* 6 */ "all", /* 7 */ /* * The keyword below are handled by individual arg lookups */ "client", /* 8 */ "storage", /* 9 */ "fileset", /* 10 */ "where", /* 11 */ "yes", /* 12 */ "bootstrap", /* 13 */ "done", /* 14 */ "strip_prefix", /* 15 */ "add_prefix", /* 16 */ "add_suffix", /* 17 */ "regexwhere", /* 18 */ "restoreclient", /* 19 */ "copies", /* 20 */ "comment", /* 21 */ "restorejob", /* 22 */ "replace", /* 23 */ "pluginoptions", /* 24 */ NULL }; rx->JobIds[0] = 0; for (i = 1; i<ua->argc; i++) { /* loop through arguments */ bool found_kw = false; for (j = 0; kw[j]; j++) { /* loop through keywords */ if (bstrcasecmp(kw[j], ua->argk[i])) { found_kw = true; break; } } if (!found_kw) { ua->error_msg(_("Unknown keyword: %s\n"), ua->argk[i]); return 0; } /* Found keyword in kw[] list, process it */ switch (j) { case 0: /* jobid */ if (!has_value(ua, i)) { return 0; } if (*rx->JobIds != 0) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->argv[i]); done = true; break; case 1: /* current */ /* * Note, we add one second here just to include any job * that may have finished within the current second, * which happens a lot in scripting small jobs. */ bstrutime(date, sizeof(date), now); have_date = true; break; case 2: /* before */ if (have_date || !has_value(ua, i)) { return 0; } if (str_to_utime(ua->argv[i]) == 0) { ua->error_msg(_("Improper date format: %s\n"), ua->argv[i]); return 0; } bstrncpy(date, ua->argv[i], sizeof(date)); have_date = true; break; case 3: /* file */ case 4: /* dir */ if (!has_value(ua, i)) { return 0; } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } pm_strcpy(ua->cmd, ua->argv[i]); insert_one_file_or_dir(ua, rx, date, j==4); return 2; case 5: /* select */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = true; break; case 6: /* pool specified */ if (!has_value(ua, i)) { return 0; } rx->pool = (POOLRES *)GetResWithName(R_POOL, ua->argv[i]); if (!rx->pool) { ua->error_msg(_("Error: Pool resource \"%s\" does not exist.\n"), ua->argv[i]); return 0; } if (!acl_access_ok(ua, Pool_ACL, ua->argv[i], true)) { rx->pool = NULL; ua->error_msg(_("Error: Pool resource \"%s\" access not allowed.\n"), ua->argv[i]); return 0; } break; case 7: /* all specified */ rx->all = true; break; default: /* * All keywords 7 or greater are ignored or handled by a select prompt */ break; } } if (!done) { ua->send_msg(_("\nFirst you select one or more JobIds that contain files\n" "to be restored. You will be presented several methods\n" "of specifying the JobIds. Then you will be allowed to\n" "select which files from those JobIds are to be restored.\n\n")); } /* If choice not already made above, prompt */ for ( ; !done; ) { char *fname; int len; bool gui_save; db_list_ctx jobids; start_prompt(ua, _("To select the JobIds, you have the following choices:\n")); for (int i=0; list[i]; i++) { add_prompt(ua, list[i]); } done = true; switch (do_prompt(ua, "", _("Select item: "), NULL, 0)) { case -1: /* error or cancel */ return 0; case 0: /* list last 20 Jobs run */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, uar_list_jobs, prtit, ua, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 1: /* list where a file is saved */ if (!get_client_name(ua, rx)) { return 0; } if (!get_cmd(ua, _("Enter Filename (no path):"))) { return 0; } len = strlen(ua->cmd); fname = (char *)malloc(len * 2 + 1); db_escape_string(ua->jcr, ua->db, fname, ua->cmd, len); Mmsg(rx->query, uar_file[db_get_type_index(ua->db)], rx->ClientName, fname); free(fname); gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, rx->query, prtit, ua, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 2: /* enter a list of JobIds */ if (!get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { return 0; } pm_strcpy(rx->JobIds, ua->cmd); break; case 3: /* Enter an SQL list command */ if (!acl_access_ok(ua, Command_ACL, NT_("sqlquery"), true)) { ua->error_msg(_("SQL query not authorized.\n")); return 0; } if (!get_cmd(ua, _("Enter SQL list command: "))) { return 0; } gui_save = ua->jcr->gui; ua->jcr->gui = true; db_list_sql_query(ua->jcr, ua->db, ua->cmd, prtit, ua, true, HORZ_LIST); ua->jcr->gui = gui_save; done = false; break; case 4: /* Select the most recent backups */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 5: /* select backup at specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } break; case 6: /* Enter files */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 7: /* enter files backed up before specified time */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter file names with paths, or < to enter a filename\n" "containing a list of file names with paths, and terminate\n" "them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter full filename: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } insert_one_file_or_dir(ua, rx, date, false); } return 2; case 8: /* Find JobIds for current backup */ if (!have_date) { bstrutime(date, sizeof(date), now); } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 9: /* Find JobIds for give date */ if (!have_date) { if (!get_date(ua, date, sizeof(date))) { return 0; } } if (!select_backups_before_date(ua, rx, date)) { return 0; } done = false; break; case 10: /* Enter directories */ if (*rx->JobIds != 0) { ua->send_msg(_("You have already selected the following JobIds: %s\n"), rx->JobIds); } else if (get_cmd(ua, _("Enter JobId(s), comma separated, to restore: "))) { if (*rx->JobIds != 0 && *ua->cmd) { pm_strcat(rx->JobIds, ","); } pm_strcat(rx->JobIds, ua->cmd); } if (*rx->JobIds == 0 || *rx->JobIds == '.') { *rx->JobIds = 0; return 0; /* nothing entered, return */ } if (!have_date) { bstrutime(date, sizeof(date), now); } if (!get_client_name(ua, rx)) { return 0; } ua->send_msg(_("Enter full directory names or start the name\n" "with a < to indicate it is a filename containing a list\n" "of directories and terminate them with a blank line.\n")); for ( ;; ) { if (!get_cmd(ua, _("Enter directory name: "))) { return 0; } len = strlen(ua->cmd); if (len == 0) { break; } /* Add trailing slash to end of directory names */ if (ua->cmd[0] != '<' && !IsPathSeparator(ua->cmd[len-1])) { strcat(ua->cmd, "/"); } insert_one_file_or_dir(ua, rx, date, true); } return 2; case 11: /* Choose a jobid and select jobs */ if (!get_cmd(ua, _("Enter JobId to get the state to restore: ")) || !is_an_integer(ua->cmd)) { return 0; } memset(&jr, 0, sizeof(jr)); jr.JobId = str_to_int64(ua->cmd); if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), ua->cmd, db_strerror(ua->db)); return 0; } ua->send_msg(_("Selecting jobs to build the Full state at %s\n"), jr.cStartTime); jr.JobLevel = L_INCREMENTAL; /* Take Full+Diff+Incr */ if (!db_accurate_get_jobids(ua->jcr, ua->db, &jr, &jobids)) { return 0; } pm_strcpy(rx->JobIds, jobids.list); Dmsg1(30, "Item 12: jobids = %s\n", rx->JobIds); break; case 12: /* Cancel or quit */ return 0; } } memset(&jr, 0, sizeof(jr)); POOLMEM *JobIds = get_pool_memory(PM_FNAME); *JobIds = 0; rx->TotalFiles = 0; /* * Find total number of files to be restored, and filter the JobId * list to contain only ones permitted by the ACL conditions. */ for (p=rx->JobIds; ; ) { char ed1[50]; int status = get_next_jobid_from_list(&p, &JobId); if (status < 0) { ua->error_msg(_("Invalid JobId in list.\n")); free_pool_memory(JobIds); return 0; } if (status == 0) { break; } if (jr.JobId == JobId) { continue; /* duplicate of last JobId */ } memset(&jr, 0, sizeof(jr)); jr.JobId = JobId; if (!db_get_job_record(ua->jcr, ua->db, &jr)) { ua->error_msg(_("Unable to get Job record for JobId=%s: ERR=%s\n"), edit_int64(JobId, ed1), db_strerror(ua->db)); free_pool_memory(JobIds); return 0; } if (!acl_access_ok(ua, Job_ACL, jr.Name, true)) { ua->error_msg(_("Access to JobId=%s (Job \"%s\") not authorized. Not selected.\n"), edit_int64(JobId, ed1), jr.Name); continue; } if (*JobIds != 0) { pm_strcat(JobIds, ","); } pm_strcat(JobIds, edit_int64(JobId, ed1)); rx->TotalFiles += jr.JobFiles; } free_pool_memory(rx->JobIds); rx->JobIds = JobIds; /* Set ACL filtered list */ if (*rx->JobIds == 0) { ua->warning_msg(_("No Jobs selected.\n")); return 0; } if (strchr(rx->JobIds,',')) { ua->info_msg(_("You have selected the following JobIds: %s\n"), rx->JobIds); } else { ua->info_msg(_("You have selected the following JobId: %s\n"), rx->JobIds); } return 1; }
/** Handle a CONNECT message from a server. * * \a parv has the following elements: * \li \a parv[1] is the server that should initiate the connection * \li \a parv[2] is the port number to connect on (zero for the default) * \li \a parv[3] is the server to connect to * * See @ref m_functions for discussion of the arguments. * @param[in] cptr Client that sent us the message. * @param[in] sptr Original source of message. * @param[in] parc Number of arguments. * @param[in] parv Argument vector. */ int ms_connect(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { unsigned short port; unsigned short tmpport; const char* rule; struct ConfItem* aconf; struct Client* acptr; struct Jupe* ajupe; assert(0 != cptr); assert(0 != sptr); if (!IsPrivileged(sptr)) return send_reply(sptr, ERR_NOPRIVILEGES); if (parc < 4) { /* * this is coming from a server which should have already * checked it's args, if we don't have parc == 4, something * isn't right. */ protocol_violation(sptr, "Too few parameters to connect"); return need_more_params(sptr, "CONNECT"); } if (hunt_server_cmd(sptr, CMD_CONNECT, cptr, 1, "%s %s :%C", 3, parc, parv) != HUNTED_ISME) return 0; /* * need to find the conf entry first so we can use the server name from * the conf entry instead of parv[1] to find out if the server is already * present below. --Bleep */ if (0 == (aconf = conf_find_server(parv[1]))) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Connect: Host %s not listed " "in ircd.conf", sptr, parv[1]); return 0; } /* * use aconf->name to look up the server */ if ((acptr = FindServer(aconf->name))) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Connect: Server %s already " "exists from %s", sptr, parv[1], cli_name(cli_from(acptr))); return 0; } /* * Evaluate connection rules... If no rules found, allow the * connect. Otherwise stop with the first true rule (ie: rules * are ored together. Oper connects are effected only by D * lines (CRULEALL) not d lines (CRULEAUTO). */ if ((rule = conf_eval_crule(aconf->name, CRULE_ALL))) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Connect: Disallowed by rule: %s", sptr, rule); return 0; } /* * Check to see if the server is juped; if it is, disallow the connect */ if ((ajupe = jupe_find(aconf->name)) && JupeIsActive(ajupe)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :Connect: Server %s is juped: %s", sptr, JupeServer(ajupe), JupeReason(ajupe)); return 0; } /* * Allow opers to /connect foo.* 0 bah.* to connect foo and bah * using the conf's configured port */ port = atoi(parv[2]); /* * save the old port */ tmpport = aconf->address.port; if (port) aconf->address.port = port; else port = aconf->address.port; /* * Notify all operators about remote connect requests */ sendwallto_group(&me, WALL_WALLOPS, 0, "Remote CONNECT %s %s from %s", parv[1], parv[2] ? parv[2] : "", get_client_name(sptr, HIDE_IP)); log_write(LS_NETWORK, L_INFO, 0, "CONNECT From %C : %s %s", sptr, parv[1], parv[2] ? parv[2] : ""); if (connect_server(aconf, sptr)) { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Connecting to %s.", sptr, aconf->name); } else { sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :*** Connection to %s failed", sptr, aconf->name); } aconf->address.port = tmpport; return 0; }
/* ** m_kill ** parv[0] = sender prefix ** parv[1] = kill victim(s) - comma separated list ** parv[2] = kill path */ DLLFUNC int m_kill(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; anUser *auser; char inpath[HOSTLEN * 2 + USERLEN + 5]; char *oinpath = get_client_name(cptr, FALSE); char *user, *path, *killer, *nick, *p, *s; int chasing = 0, kcount = 0; if (parc < 2 || *parv[1] == '\0') { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL"); return 0; } user = parv[1]; path = parv[2]; /* Either defined or NULL (parc >= 2!!) */ strlcpy(inpath, oinpath, sizeof inpath); #ifndef ROXnet if (IsServer(cptr) && (s = (char *)index(inpath, '.')) != NULL) *s = '\0'; /* Truncate at first "." */ #endif if (!IsPrivileged(cptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (IsAnOper(cptr)) { if (BadPtr(path)) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "KILL"); return 0; } if (strlen(path) > (size_t)TOPICLEN) path[TOPICLEN] = '\0'; } if (MyClient(sptr)) user = (char *)canonize(user); for (p = NULL, nick = strtoken(&p, user, ","); nick; nick = strtoken(&p, NULL, ",")) { chasing = 0; if (!(acptr = find_client(nick, NULL))) { /* ** If the user has recently changed nick, we automaticly ** rewrite the KILL for this new nickname--this keeps ** servers in synch when nick change and kill collide */ if (!(acptr = get_history(nick, (long)KILLCHASETIMELIMIT))) { sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); continue; } sendto_one(sptr, ":%s %s %s :*** KILL changed from %s to %s", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], nick, acptr->name); chasing = 1; } if ((!MyConnect(acptr) && MyClient(cptr) && !OPCanGKill(cptr)) || (MyConnect(acptr) && MyClient(cptr) && !OPCanLKill(cptr))) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); continue; } if (IsServer(acptr) || IsMe(acptr)) { sendto_one(sptr, err_str(ERR_CANTKILLSERVER), me.name, parv[0]); continue; } if (!IsPerson(acptr)) { /* Nick exists but user is not registered yet: IOTW "doesn't exist". -- Syzop */ sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], nick); continue; } if (IsServices(acptr) && !(IsNetAdmin(sptr) || IsULine(sptr))) { sendto_one(sptr, err_str(ERR_KILLDENY), me.name, parv[0], parv[1]); return 0; } /* From here on, the kill is probably going to be successful. */ kcount++; if (!IsServer(sptr) && (kcount > MAXKILLS)) { sendto_one(sptr, ":%s %s %s :*** Too many targets, kill list was truncated. Maximum is %d.", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], MAXKILLS); break; } if (!IsServer(cptr)) { /* ** The kill originates from this server, initialize path. ** (In which case the 'path' may contain user suplied ** explanation ...or some nasty comment, sigh... >;-) ** ** ...!operhost!oper ** ...!operhost!oper (comment) */ strlcpy(inpath, GetHost(cptr), sizeof inpath); if (kcount < 2) { /* Only check the path the first time around, or it gets appended to itself. */ if (!BadPtr(path)) { (void)ircsprintf(buf, "%s%s (%s)", cptr->name, IsOper(sptr) ? "" : "(L)", path); path = buf; } else path = cptr->name; } } else if (BadPtr(path)) path = "*no-path*"; /* Bogus server sending??? */ /* ** Notify all *local* opers about the KILL (this includes the one ** originating the kill, if from this server--the special numeric ** reply message is not generated anymore). ** ** Note: "acptr->name" is used instead of "user" because we may ** have changed the target because of the nickname change. */ auser = acptr->user; sendto_snomask_normal(SNO_KILLS, "*** Notice -- Received KILL message for %s!%s@%s from %s Path: %s!%s", acptr->name, auser->username, IsHidden(acptr) ? auser->virthost : auser->realhost, parv[0], inpath, path); #if defined(USE_SYSLOG) && defined(SYSLOG_KILL) if (IsOper(sptr)) syslog(LOG_DEBUG, "KILL From %s For %s Path %s!%s", parv[0], acptr->name, inpath, path); #endif /* * By otherguy */ ircd_log (LOG_KILL, "KILL (%s) by %s(%s!%s)", make_nick_user_host (acptr->name, acptr->user->username, GetHost(acptr)), parv[0], inpath, path); /* ** And pass on the message to other servers. Note, that if KILL ** was changed, the message has to be sent to all links, also ** back. ** Suicide kills are NOT passed on --SRB */ if (!MyConnect(acptr) || !MyConnect(sptr) || !IsAnOper(sptr)) { sendto_serv_butone(cptr, ":%s KILL %s :%s!%s", parv[0], acptr->name, inpath, path); if (chasing && IsServer(cptr)) sendto_one(cptr, ":%s KILL %s :%s!%s", me.name, acptr->name, inpath, path); acptr->flags |= FLAGS_KILLED; } /* ** Tell the victim she/he has been zapped, but *only* if ** the victim is on current server--no sense in sending the ** notification chasing the above kill, it won't get far ** anyway (as this user don't exist there any more either) */ if (MyConnect(acptr)) sendto_prefix_one(acptr, sptr, ":%s KILL %s :%s!%s", parv[0], acptr->name, inpath, path); /* ** Set FLAGS_KILLED. This prevents exit_one_client from sending ** the unnecessary QUIT for this. (This flag should never be ** set in any other place) */ if (MyConnect(acptr) && MyConnect(sptr) && IsAnOper(sptr)) (void)ircsprintf(buf2, "[%s] Local kill by %s (%s)", me.name, sptr->name, BadPtr(parv[2]) ? sptr->name : parv[2]); else { if ((killer = index(path, ' '))) { while ((killer >= path) && *killer && *killer != '!') killer--; if (!*killer) killer = path; else killer++; } else killer = path; (void)ircsprintf(buf2, "Killed (%s)", killer); } if (MyClient(sptr)) RunHook3(HOOKTYPE_LOCAL_KILL, sptr, acptr, parv[2]); if (exit_client(cptr, acptr, sptr, buf2) == FLUSH_BUFFER) return FLUSH_BUFFER; } return 0; }
DLLFUNC CMD_FUNC(m_names) { int uhnames = (MyConnect(sptr) && SupportUHNAMES(sptr)); // cache UHNAMES support int bufLen = NICKLEN + (!uhnames ? 0 : (1 + USERLEN + 1 + HOSTLEN)); int mlen = strlen(me.name) + bufLen + 7; aChannel *chptr; aClient *acptr; int member; Member *cm; int idx, flag = 1, spos; char *s, *para = parv[1]; char nuhBuffer[NICKLEN+USERLEN+HOSTLEN+3]; if (parc < 2 || !MyConnect(sptr)) { sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], "*"); return 0; } for (s = para; *s; s++) { if (*s == ',') { if (strlen(para) > TRUNCATED_NAMES) para[TRUNCATED_NAMES] = '\0'; sendto_realops("names abuser %s %s", get_client_name(sptr, FALSE), para); sendto_one(sptr, err_str(ERR_TOOMANYTARGETS), me.name, sptr->name, "NAMES"); return 0; } } chptr = find_channel(para, (aChannel *)NULL); if (!chptr || (!ShowChannel(sptr, chptr) && !OPCanSeeSecret(sptr))) { sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para); return 0; } /* cache whether this user is a member of this channel or not */ member = IsMember(sptr, chptr); if (PubChannel(chptr)) buf[0] = '='; else if (SecretChannel(chptr)) buf[0] = '@'; else buf[0] = '*'; idx = 1; buf[idx++] = ' '; for (s = chptr->chname; *s; s++) buf[idx++] = *s; buf[idx++] = ' '; buf[idx++] = ':'; /* If we go through the following loop and never add anything, we need this to be empty, otherwise spurious things from the LAST /names call get stuck in there.. - lucas */ buf[idx] = '\0'; spos = idx; /* starting point in buffer for names! */ for (cm = chptr->members; cm; cm = cm->next) { acptr = cm->cptr; if (IsInvisible(acptr) && !member && !IsNetAdmin(sptr)) continue; if (chptr->mode.mode & MODE_AUDITORIUM) if (!is_chan_op(sptr, chptr) && !is_chanprot(sptr, chptr) && !is_chanowner(sptr, chptr)) if (!(cm-> flags & (CHFL_CHANOP | CHFL_CHANPROT | CHFL_CHANOWNER)) && acptr != sptr) continue; if (!SupportNAMESX(sptr)) { /* Standard NAMES reply */ #ifdef PREFIX_AQ if (cm->flags & CHFL_CHANOWNER) buf[idx++] = '~'; else if (cm->flags & CHFL_CHANPROT) buf[idx++] = '&'; else #endif if (cm->flags & CHFL_CHANOP) buf[idx++] = '@'; else if (cm->flags & CHFL_HALFOP) buf[idx++] = '%'; else if (cm->flags & CHFL_VOICE) buf[idx++] = '+'; } else { /* NAMES reply with all rights included (NAMESX) */ #ifdef PREFIX_AQ if (cm->flags & CHFL_CHANOWNER) buf[idx++] = '~'; if (cm->flags & CHFL_CHANPROT) buf[idx++] = '&'; #endif if (cm->flags & CHFL_CHANOP) buf[idx++] = '@'; if (cm->flags & CHFL_HALFOP) buf[idx++] = '%'; if (cm->flags & CHFL_VOICE) buf[idx++] = '+'; } if (!uhnames) { s = acptr->name; } else { strlcpy(nuhBuffer, make_nick_user_host(acptr->name, acptr->user->username, GetHost(acptr)), bufLen + 1); s = nuhBuffer; } /* 's' is intialized above to point to either acptr->name (normal), * or to nuhBuffer (for UHNAMES). */ for (; *s; s++) buf[idx++] = *s; buf[idx++] = ' '; buf[idx] = '\0'; flag = 1; if (mlen + idx + bufLen > BUFSIZE - 7) { sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); idx = spos; flag = 0; } } if (flag) sendto_one(sptr, rpl_str(RPL_NAMREPLY), me.name, parv[0], buf); sendto_one(sptr, rpl_str(RPL_ENDOFNAMES), me.name, parv[0], para); return 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; }
/* ** 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; }
/* * parse a buffer. * * NOTE: parse() should not be called recusively by any other functions! */ int parse(aClient *cptr, char *pbuffer, char *bufend) { aClient *from = cptr; char *ch; char *s; char parsebuf[2048]; int i; char* numeric = 0; int paramcount; struct Message *mptr; Debug((DEBUG_DEBUG, "Parsing %s: %s", get_client_name(cptr, TRUE), pbuffer)); if (IsDead(cptr)) return -1; if (MyClient(cptr) && cptr->codepage) { char *trans = codepage_to_unicode(pbuffer, cptr->codepage - 1); if (trans != pbuffer) { strncpy(parsebuf, trans, sizeof(parsebuf)); pbuffer = parsebuf; bufend = parsebuf + strlen(parsebuf) - 1; } } s = sender; *s = '\0'; for (ch = pbuffer; *ch == ' '; ch++) /* skip spaces */ /* null statement */ ; para[0] = from->name; if (*ch == ':') { ch++; /* ** Copy the prefix to 'sender' assuming it terminates ** with SPACE (or NULL, which is an error, though). */ for (i = 0; *ch && *ch != ' '; i++ ) { if (i < (sizeof(sender)-1)) *s++ = *ch; /* leave room for NULL */ ch++; } *s = '\0'; i = 0; /* ** Actually, only messages coming from servers can have ** the prefix--prefix silently ignored, if coming from ** a user client... ** ** ...sigh, the current release "v2.2PL1" generates also ** null prefixes, at least to NOTIFY messages (e.g. it ** puts "sptr->nickname" as prefix from server structures ** where it's null--the following will handle this case ** as "no prefix" at all --msa (": NOTICE nick ...") */ if (*sender && IsServer(cptr)) { from = find_client(sender, (aClient *) NULL); if (!from || !match(from->name, sender)) from = find_server(sender); para[0] = sender; /* Hmm! If the client corresponding to the * prefix is not found--what is the correct * action??? Now, I will ignore the message * (old IRC just let it through as if the * prefix just wasn't there...) --msa */ if (!from) { Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)", sender, pbuffer, cptr->name)); ServerStats->is_unpf++; remove_unknown(cptr, sender, pbuffer); return -1; } if (from->from != cptr) { ServerStats->is_wrdi++; Debug((DEBUG_ERROR, "Message (%s) coming from (%s)", pbuffer, cptr->name)); return cancel_clients(cptr, from, pbuffer); } } while (*ch == ' ') ch++; } if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cptr->name, from->name)); return(-1); } /* ** Extract the command code from the packet. Point s to the end ** of the command code and calculate the length using pointer ** arithmetic. Note: only need length for numerics and *all* ** numerics must have parameters and thus a space after the command ** code. -avalon * * ummm???? - Dianora */ /* Lets log any message we receive except nickserv/chanserv and privmsgs/notices - Lamego */ if(debug_opt) { if(strncasecmp(ch,"PRIVMSG",7)==0 || strncasecmp(ch,"NOTICE",6)==0) irclog(L_NOTICE,"Recv: (%s) PRIVMSG/NOTICE ...", from->name); else if(strncasecmp(ch,"NICKSERV",8)==0 || strncasecmp(ch,"CHANSERV",8)==0) irclog(L_NOTICE,"Recv: (%s) NICKSERV/CHANSERV ...", from->name); else irclog(L_NOTICE,"Recv: (%s) %s", from->name, ch); } if( *(ch + 3) == ' ' && /* ok, lets see if its a possible numeric.. */ IsDigit(*ch) && IsDigit(*(ch + 1)) && IsDigit(*(ch + 2)) ) { mptr = (struct Message *)NULL; numeric = ch; paramcount = MAXPARA; ServerStats->is_num++; s = ch + 3; /* I know this is ' ' from above if */ *s++ = '\0'; /* blow away the ' ', and point s to next part */ } else { s = strchr(ch, ' '); /* moved from above,now need it here */ if (s) *s++ = '\0'; mptr = tree_parse(ch); if (!mptr || !mptr->cmd) { /* ** Note: Give error message *only* to recognized ** persons. It's a nightmare situation to have ** two programs sending "Unknown command"'s or ** equivalent to each other at full blast.... ** If it has got to person state, it at least ** seems to be well behaving. Perhaps this message ** should never be generated, though... --msa ** Hm, when is the buffer empty -- if a command ** code has been found ?? -Armin */ if (pbuffer[0] != '\0') { if (IsPerson(from)) sendto_one(from, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); Debug((DEBUG_ERROR,"Unknown (%s) from %s", ch, get_client_name(cptr, TRUE))); } ServerStats->is_unco++; return(-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; #ifdef FLOOD_DELAY /* Allow only 1 msg per 2 seconds * (on average) to prevent dumping. * to keep the response rate up, * bursts of up to 5 msgs are allowed * -SRB * Opers can send 1 msg per second, burst of ~20 * -Taner */ if ((mptr->flags & 1) && !(IsServer(cptr)) && !IsFloodEx(cptr) ) { #ifdef NO_OPER_FLOOD #ifndef TRUE_NO_OPER_FLOOD /* note: both have to be defined for the real no-flood */ if (IsAnOper(cptr)) /* "randomly" (weighted) increase the since */ cptr->since += (cptr->receiveM % 5) ? 1 : 0; else #else if (!IsAnOper(cptr)) #endif #endif cptr->since += (2 + i / 120); } #endif /* FLOOD_DELAY */ } /* ** Must the following loop really be so devious? On ** surface it splits the message to parameters from ** blank spaces. But, if paramcount has been reached, ** the rest of the message goes into this last parameter ** (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ /* ZZZ hmmmmmmmm whats this then? */ #if 0 if (me.user) para[0] = sender; #endif i = 1; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { while(*s == ' ') /* tabs are not considered space */ *s++ = '\0'; if(!*s) break; if (*s == ':') { /* ** The rest is single parameter--can ** include blanks also. */ para[i++] = s + 1; break; } else { para[i++] = s; if (i >= paramcount) { break; } /* scan for end of string, either ' ' or '\0' */ while (IsNonEOS(*s)) s++; } } } para[i] = NULL; if (mptr == (struct Message *)NULL) return (do_numeric(numeric, cptr, from, i, para)); mptr->count++; /* patch to avoid server flooding from unregistered connects */ /* check allow_unregistered_use flag I've set up instead of function comparing *yech* - Dianora */ if ((!IsRegistered(cptr) || (hvc_enabled && cptr->hvc)) && !mptr->allow_unregistered_use) { /* if its from a possible server connection * ignore it.. more than likely its a header thats sneaked through */ if(IsHandshake(cptr) || IsConnecting(cptr) || IsServer(cptr)) return -1; sendto_one(from, ":%s %d %s %s :Register first.", me.name, ERR_NOTREGISTERED, BadPtr(from->name) ? "*" : from->name, ch); return -1; } /* Again, instead of function address comparing, see if * this function resets idle time as given from mptr * if IDLE_FROM_MSG is undefined, the sense of the flag is reversed. * i.e. if the flag is 0, then reset idle time, otherwise don't reset it. * * - Dianora */ #ifdef IDLE_FROM_MSG if (IsRegisteredUser(cptr) && mptr->reset_idle) from->user->last = CurrentTime; #else if (IsRegisteredUser(cptr) && !mptr->reset_idle) from->user->last = CurrentTime; #endif /* don't allow other commands while a list is blocked. since we treat them specially with respect to sendq. */ if ((IsDoingList(cptr)) && (*mptr->func != m_list)) return -1; return (*mptr->func)(cptr, from, i, para); }
/* * m_notice - generic message handler */ int m_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* name; char* server; int ret = 0; int i; int j; int fd = 0; int count; char *clean; char* vector[MAXTARGETS]; char* temp; /* added by Vadtec 02/25/2008 */ char* parv_temp; /* added by Vadtec 02/26/2008 */ int found_g = 0; /* added by Vadtec 02/26/2008 */ int sent = 0; /* added by Vadtec 03/13/2008 */ struct Client* acptr; /* added by Vadtec 02/26/2008 */ struct Channel* chptr; /* added by Vadtec 02/27/2008 */ int isdcc = 0; assert(0 != cptr); assert(cptr == sptr); ClrFlag(sptr, FLAG_TS8); if (parc < 2 || EmptyString(parv[1])) return send_reply(sptr, ERR_NORECIPIENT, MSG_NOTICE); if (parc < 3 || EmptyString(parv[parc - 1])) return send_reply(sptr, ERR_NOTEXTTOSEND); if (parv[1][0] == '@' && IsChannelPrefix(parv[1][1])) { parv[1]++; /* Get rid of '@' */ return m_wallchops(cptr, sptr, parc, parv); } count = unique_name_vector(parv[1], ',', vector, MAXTARGETS); /* Check here to make sure that the client is ours so we dont respond to NOTICES from other server's users. - Vadtec 02/25/2008 */ /* Also, check to make sure that the notice is actually destined for the *server* and not another user. That way we don't process some user saying "what version do you use" to another user via notice. - Vadtec 03/13/2008 */ if (feature_bool(FEAT_CTCP_VERSIONING) && MyConnect(sptr) && !strcmp(parv[1], cli_name(&me))) { /* Added by Vadtec 02/25/2008. This is so that we can do version checking (and banning) of connecting clients. Rules: Only one really. CTCP VERSION is not part of the RFC and therefore clients are not required to respond to a request for their version. NOTE: If we are lucky enough to have _GNU_SOURCE, we will use it over the standard strstr because its case insensetive. This should help against clients that like to send lower case CTCPs from slipping through as easily with only one function call. */ for (fd = HighestFd; fd >= 0 && !sent; --fd) { /* Added the "sent" check here so that if we have already sent the notice we don't needlessly loop through *all* the users - Vadtec 03/13/2008 */ if ((acptr = LocalClientArray[fd])) { if (!cli_user(acptr)) continue; #ifdef _GNU_SOURCE if ((temp = strcasestr(parv[2], "\x01VERSION"))) { /* added \x01 to the string so that it will *only* respond to CTCP version replies. Seems redundant, but we dont want the users doing /notice <server> version (and getting away with it) - Vadtec 03/13/2008 */ temp = strchrnul(parv[2], ' '); /* Moved this here to take advantage of strchrnul - added by Vadtec 03/13/2008 */ #else if ((temp = strstr(parv[2], "\x01VERSION")) || (temp = strstr(parv[2], "\x01version"))) { /* See above comment about \x01 - Vadtec */ temp = strchr(parv[2], ' '); /* Moved this here to take advantage of strchrnul - added by Vadtec 03/13/2008 */ if (temp == 0) temp = parv[2] + strlen(parv[2]); /* This does the same thing as strchrnul - Vadtec */ #endif parv_temp = parv[2]; j = 0; while (j <= (temp - parv[2])) { parv_temp++; j++; } clean = normalizeBuffer(parv_temp); doCleanBuffer((char *) clean); ircd_strncpy(cli_version(sptr), normalizeBuffer(clean), VERSIONLEN); sendcmdto_serv_butone(&me, CMD_MARK, cptr, "%s %s :%s", cli_name(sptr), MARK_CVERSION, cli_version(sptr)); /* Moved here to solve duplicate MARK's if any of the CTCP_* conditions were false 05/13/2009 */ sent = 1; if (feature_bool(FEAT_CTCP_VERSIONING_CHAN)) { sprintf(temp, "%s has version \002%s\002", cli_name(sptr), cli_version(sptr)); /* Announce to channel. */ if ((chptr = FindChannel(feature_str(FEAT_CTCP_VERSIONING_CHANNAME)))) { if (feature_bool(FEAT_CTCP_VERSIONING_USEMSG)) sendcmdto_channel_butone(&me, CMD_PRIVATE, chptr, cptr, SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, temp); else sendcmdto_channel_butone(&me, CMD_NOTICE, chptr, cptr, SKIP_DEAF | SKIP_BURST, '\0', "%H :%s", chptr, temp); /* Removed sent=1 from here because it caused the MARK above to be sent more then once if any of the conditions leading here are false 05/13/2009 */ } } if (feature_bool(FEAT_CTCP_VERSIONING_KILL)) { if ((found_g = find_kill(acptr))) { sendto_opmask_butone(0, found_g == -2 ? SNO_GLINE : SNO_OPERKILL, found_g == -2 ? "G-line active for %s%s" : "K-line active for %s%s", IsUnknown(sptr) ? "Unregistered Client ":"", get_client_name(sptr, SHOW_IP)); return exit_client_msg(cptr, acptr, &me, "Banned Client: %s", cli_version(acptr)); } } else return 0; } } } } for (i = 0; i < count; ++i) { name = vector[i]; if (IsChannelPrefix(*name)) { ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_CHANNOTICE, name); if (ret != 0) { if (ret == 2) return CPTR_KILLED; else return 0; } } else { #ifdef _GNU_SOURCE if ((temp = strcasestr(parv[2], "\001DCC"))) { temp = strchrnul(parv[2], ' '); #else if ((temp = strstr(parv[2], "\001DCC")) || (temp = strstr(parv[2], "\001dcc"))) { temp = strchr(parv[2], ' '); #endif isdcc = 1; ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_DCC, name); if (ret != 0) { if (ret == 2) return CPTR_KILLED; else return 0; } } if (!isdcc) { ret = find_fline(cptr, sptr, parv[parc-1], WFFLAG_NOTICE, name); if (ret != 0) { if (ret == 2) return CPTR_KILLED; else return 0; } } } } i = 0; for (i = 0; i < count; ++i) { name = vector[i]; /* * channel msg? */ if (IsChannelPrefix(*name)) { relay_channel_notice(sptr, name, parv[parc - 1], count); } /* * we have to check for the '@' at least once no matter what we do * handle it first so we don't have to do it twice */ else if ((server = strchr(name, '@'))) relay_directed_notice(sptr, name, server, parv[parc - 1]); else relay_private_notice(sptr, name, parv[parc - 1]); } return 0; } /* * ms_notice - server message handler */ int ms_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* name; char* server; ClrFlag(sptr, FLAG_TS8); if (parc < 3) { /* * we can't deliver it, sending an error back is pointless */ return protocol_violation(sptr,"Not enough params for NOTICE"); } name = parv[1]; /* * channel msg? */ if (IsChannelPrefix(*name)) { server_relay_channel_notice(sptr, name, parv[parc - 1]); } /* * coming from another server, we have to check this here */ else if ('$' == *name && IsOper(sptr)) { server_relay_masked_notice(sptr, name, parv[parc - 1]); } else if ((server = strchr(name, '@'))) { /* * XXX - can't get away with not doing everything * relay_directed_notice has to do */ relay_directed_notice(sptr, name, server, parv[parc - 1]); } else { server_relay_private_notice(sptr, name, parv[parc - 1]); } return 0; } /* * mo_notice - oper message handler */ int mo_notice(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { char* name; char* server; int i; int count; char* vector[MAXTARGETS]; assert(0 != cptr); assert(cptr == sptr); ClrFlag(sptr, FLAG_TS8); if (parc < 2 || EmptyString(parv[1])) return send_reply(sptr, ERR_NORECIPIENT, MSG_NOTICE); if (parc < 3 || EmptyString(parv[parc - 1])) return send_reply(sptr, ERR_NOTEXTTOSEND); if (parv[1][0] == '@' && IsChannelPrefix(parv[1][1])) { parv[1]++; /* Get rid of '@' */ return m_wallchops(cptr, sptr, parc, parv); } count = unique_name_vector(parv[1], ',', vector, MAXTARGETS); for (i = 0; i < count; ++i) { name = vector[i]; /* * channel msg? */ if (IsChannelPrefix(*name)) relay_channel_notice(sptr, name, parv[parc - 1], count); else if (*name == '$') relay_masked_notice(sptr, name, parv[parc - 1]); else if ((server = strchr(name, '@'))) relay_directed_notice(sptr, name, server, parv[parc - 1]); else relay_private_notice(sptr, name, parv[parc - 1]); } return 0; }