/* * Unreal Internet Relay Chat Daemon, src/user.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* s_user.c 2.74 2/8/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ #include "unrealircd.h" void send_umode_out(aClient *, aClient *, long); void send_umode(aClient *, aClient *, long, long, char *); void set_snomask(aClient *, char *); extern int short_motd(aClient *sptr); extern aChannel *get_channel(aClient *cptr, char *chname, int flag); /* static Link *is_banned(aClient *, aChannel *); */ int dontspread = 0; extern char *me_hash; extern char backupbuf[]; static char buf[BUFSIZE]; int labeled_response_inhibit = 0; void iNAH_host(aClient *sptr, char *host) { if (!sptr->user) return; userhost_save_current(sptr); if (sptr->user->virthost) { MyFree(sptr->user->virthost); sptr->user->virthost = NULL; } sptr->user->virthost = strdup(host); if (MyConnect(sptr)) sendto_server(&me, 0, 0, NULL, ":%s SETHOST :%s", sptr->name, sptr->user->virthost); sptr->umodes |= UMODE_SETHOST; userhost_changed(sptr); sendnumeric(sptr, RPL_HOSTHIDDEN, sptr->user->virthost); } long set_usermode(char *umode) { int newumode; int what; char *m; int i; newumode = 0; what = MODE_ADD; for (m = umode; *m; m++) switch (*m) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; case ' ': case '\n': case '\r': case '\t': break; default: for (i = 0; i <= Usermode_highest; i++) { if (!Usermode_Table[i].flag) continue; if (*m == Usermode_Table[i].flag) { if (what == MODE_ADD) newumode |= Usermode_Table[i].mode; else newumode &= ~Usermode_Table[i].mode; } } } return (newumode); } /* ** m_functions execute protocol messages on this server: ** ** cptr is always NON-NULL, pointing to a *LOCAL* client ** structure (with an open socket connected!). This ** identifies the physical socket where the message ** originated (or which caused the m_function to be ** executed--some m_functions may call others...). ** ** sptr is the source of the message, defined by the ** prefix part of the message if present. If not ** or prefix not found, then sptr==cptr. ** ** (!IsServer(cptr)) => (cptr == sptr), because ** prefixes are taken *only* from servers... ** ** (IsServer(cptr)) ** (sptr == cptr) => the message didn't ** have the prefix. ** ** (sptr != cptr && IsServer(sptr) means ** the prefix specified servername. (?) ** ** (sptr != cptr && !IsServer(sptr) means ** that message originated from a remote ** user (not local). ** ** combining ** ** (!IsServer(sptr)) means that, sptr can safely ** taken as defining the target structure of the ** message in this server. ** ** *Always* true (if 'parse' and others are working correct): ** ** 1) sptr->from == cptr (note: cptr->from == cptr) ** ** 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr ** *cannot* be a local connection, unless it's ** actually cptr!). [MyConnect(x) should probably ** be defined as (x == x->from) --msa ] ** ** parc number of variable parameter strings (if zero, ** parv is allowed to be NULL) ** ** parv a NULL terminated list of parameter pointers, ** ** parv[1]...parv[parc-1] ** pointers to additional parameters ** parv[parc] == NULL, *always* ** ** note: it is guaranteed that parv[1]..parv[parc-1] are all ** non-NULL pointers. */ /* ** hunt_server ** ** Do the basic thing in delivering the message (command) ** across the relays to the specific server (server) for ** actions. ** ** Note: The command is a format string and *MUST* be ** of prefixed style (e.g. ":%s COMMAND %s ..."). ** Command can have only max 8 parameters. ** ** server parv[server] is the parameter identifying the ** target server. ** ** *WARNING* ** parv[server] is replaced with the pointer to the ** real servername from the matched client (I'm lazy ** now --msa). ** ** returns: (see #defines) ** ** Rewritten by Syzop / Oct 2015. This function was rather ** complex and no longer understandable. It also was responsible ** for mysterious issues and crashes. Hence rewritten. */ int hunt_server(aClient *cptr, aClient *sptr, MessageTag *mtags, char *command, int server, int parc, char *parv[]) { aClient *acptr; char *saved; /* This would be strange and bad. Previous version assumed "it's for me". Hmm.. okay. */ if (parc <= server || BadPtr(parv[server])) return HUNTED_ISME; acptr = find_client(parv[server], NULL); /* find_client() may find a variety of clients. Only servers/persons please, no 'unknowns'. */ if (acptr && MyConnect(acptr) && !IsMe(acptr) && !IsPerson(acptr) && !IsServer(acptr)) acptr = NULL; if (!acptr) { sendnumeric(sptr, ERR_NOSUCHSERVER, parv[server]); return HUNTED_NOSUCH; } if (IsMe(acptr) || MyClient(acptr)) return HUNTED_ISME; /* Never send the message back from where it came from */ if (acptr->from == sptr->from) { sendnumeric(sptr, ERR_NOSUCHSERVER, parv[server]); return HUNTED_NOSUCH; } /* Replace "server" part with actual servername (eg: 'User' -> 'x.y.net') * Ugly. Previous version didn't even restore the state, now we do. */ saved = parv[server]; parv[server] = acptr->name; sendto_one(acptr, mtags, command, sptr->name, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6], parv[7], parv[8]); parv[server] = saved; return HUNTED_PASS; } /** Convert a target pointer to an 8 bit hash, used for target limiting. */ unsigned char hash_target(void *target) { unsigned long long v = (unsigned long long)target; /* ircu does >> 16 and 8 but since our sizeof(aClient) is * towards 512 (and hence the alignment), that bit is useless. * So we do >> 17 and 9. */ return (unsigned char)((v >> 17) ^ (v >> 9)); } /** check_for_target_limit * @param sptr The client. * @param target The target client * @param name The name of the target client (used in the error message) * @retval Returns 1 if too many targets were addressed (do not send!), 0 if ok to send. */ int check_for_target_limit(aClient *sptr, void *target, const char *name) { u_char *p; u_char hash = hash_target(target); int i; if (ValidatePermissionsForPath("immune:target-limit",sptr,NULL,NULL,NULL)) return 0; if (sptr->local->targets[0] == hash) return 0; for (i = 1; i < iConf.max_concurrent_conversations_users; i++) { if (sptr->local->targets[i] == hash) { /* Move this target hash to the first position */ memmove(&sptr->local->targets[1], &sptr->local->targets[0], i); sptr->local->targets[0] = hash; return 0; } } if (TStime() < sptr->local->nexttarget) { /* Target limit reached */ sptr->local->nexttarget += 2; /* punish them some more */ sptr->local->since += 2; /* lag them up as well */ sendnumeric(sptr, ERR_TARGETTOOFAST, name, sptr->local->nexttarget - TStime()); return 1; } /* If not set yet or in the very past, then adjust it. * This is so sptr->local->nexttarget=0 will become sptr->local->nexttarget=currenttime-... */ if (TStime() > sptr->local->nexttarget + (iConf.max_concurrent_conversations_users * iConf.max_concurrent_conversations_new_user_every)) { sptr->local->nexttarget = TStime() - ((iConf.max_concurrent_conversations_users-1) * iConf.max_concurrent_conversations_new_user_every); } sptr->local->nexttarget += iConf.max_concurrent_conversations_new_user_every; /* Add the new target (first move the rest, then add us at position 0 */ memmove(&sptr->local->targets[1], &sptr->local->targets[0], iConf.max_concurrent_conversations_users - 1); sptr->local->targets[0] = hash; return 0; } /* ** canonize ** ** reduce a string of duplicate list entries to contain only the unique ** items. Unavoidably O(n^2). */ char *canonize(char *buffer) { static char cbuf[2048]; char *s, *t, *cp = cbuf; int l = 0; char *p = NULL, *p2; *cp = '\0'; if (!buffer) return NULL; /* Ohh.. so lazy. But then again, this should never happen with a 2K buffer anyway. */ if (strlen(buffer) >= sizeof(cbuf)) buffer[sizeof(cbuf)-1] = '\0'; for (s = strtoken(&p, buffer, ","); s; s = strtoken(&p, NULL, ",")) { if (l) { for (p2 = NULL, t = strtoken(&p2, cbuf, ","); t; t = strtoken(&p2, NULL, ",")) if (!mycmp(s, t)) break; else if (p2) p2[-1] = ','; } else t = NULL; if (!t) { if (l) *(cp - 1) = ','; else l = 1; (void)strcpy(cp, s); if (p) cp += (p - s); } else if (p2) p2[-1] = ','; } return cbuf; } /* ** get_mode_str ** by vmlinuz ** returns an ascii string of modes */ char *get_sno_str(aClient *sptr) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) if (Snomask_Table[i].flag && sptr->user->snomask & Snomask_Table[i].mode) *m++ = Snomask_Table[i].flag; *m = 0; return buf; } char *get_mode_str(aClient *acptr) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) if (Usermode_Table[i].flag && (acptr->umodes & Usermode_Table[i].mode)) *m++ = Usermode_Table[i].flag; *m = '\0'; return buf; } char *get_modestr(long umodes) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; (i <= Usermode_highest) && (m - buf < BUFSIZE - 4); i++) if (Usermode_Table[i].flag && (umodes & Usermode_Table[i].mode)) *m++ = Usermode_Table[i].flag; *m = '\0'; return buf; } char *get_snostr(long sno) { int i; char *m; m = buf; *m++ = '+'; for (i = 0; i <= Snomask_highest && (m - buf < BUFSIZE - 4); i++) if (Snomask_Table[i].flag && sno & Snomask_Table[i].mode) *m++ = Snomask_Table[i].flag; *m = 0; return buf; } void set_snomask(aClient *sptr, char *snomask) { int what = MODE_ADD; /* keep this an int. -- Syzop */ char *p; int i; if (snomask == NULL) { sptr->user->snomask = 0; return; } for (p = snomask; p && *p; p++) { switch (*p) { case '+': what = MODE_ADD; break; case '-': what = MODE_DEL; break; default: for (i = 0; i <= Snomask_highest; i++) { if (!Snomask_Table[i].flag) continue; if (*p == Snomask_Table[i].flag) { if (Snomask_Table[i].allowed && !Snomask_Table[i].allowed(sptr,what)) continue; if (what == MODE_ADD) sptr->user->snomask |= Snomask_Table[i].mode; else sptr->user->snomask &= ~Snomask_Table[i].mode; } } } } } /* * send the MODE string for user (user) to connection cptr * -avalon */ void send_umode(aClient *cptr, aClient *sptr, long old, long sendmask, char *umode_buf) { int i; long flag; char *m; int what = MODE_NULL; /* * build a string in umode_buf to represent the change in the user's * mode between the new (sptr->flag) and 'old'. */ m = umode_buf; *m = '\0'; for (i = 0; i <= Usermode_highest; i++) { if (!Usermode_Table[i].flag) continue; flag = Usermode_Table[i].mode; if (MyClient(sptr) && !(flag & sendmask)) continue; if ((flag & old) && !(sptr->umodes & flag)) { if (what == MODE_DEL) *m++ = Usermode_Table[i].flag; else { what = MODE_DEL; *m++ = '-'; *m++ = Usermode_Table[i].flag; } } else if (!(flag & old) && (sptr->umodes & flag)) { if (what == MODE_ADD) *m++ = Usermode_Table[i].flag; else { what = MODE_ADD; *m++ = '+'; *m++ = Usermode_Table[i].flag; } } } *m = '\0'; if (*umode_buf && cptr) sendto_one(cptr, NULL, ":%s MODE %s :%s", sptr->name, sptr->name, umode_buf); } /* * added Sat Jul 25 07:30:42 EST 1992 */ void send_umode_out(aClient *cptr, aClient *sptr, long old) { aClient *acptr; send_umode(NULL, sptr, old, SEND_UMODES, buf); list_for_each_entry(acptr, &server_list, special_node) { if ((acptr != cptr) && (acptr != sptr) && *buf) { sendto_one(acptr, NULL, ":%s UMODE2 %s", sptr->name, buf); } } if (cptr && MyClient(cptr)) send_umode(cptr, sptr, old, ALL_UMODES, buf); } int del_silence(aClient *sptr, char *mask) { Link **lp; Link *tmp; for (lp = &(sptr->user->silence); *lp; lp = &((*lp)->next)) if (mycmp(mask, (*lp)->value.cp) == 0) { tmp = *lp; *lp = tmp->next; MyFree(tmp->value.cp); free_link(tmp); return 0; } return -1; } int add_silence(aClient *sptr, char *mask, int senderr) { Link *lp; int cnt = 0; for (lp = sptr->user->silence; lp; lp = lp->next) { if (MyClient(sptr)) if ((strlen(lp->value.cp) > MAXSILELENGTH) || (++cnt >= SILENCE_LIMIT)) { if (senderr) sendnumeric(sptr, ERR_SILELISTFULL, mask); return -1; } else { if (match_simple(lp->value.cp, mask)) return -1; } else if (!mycmp(lp->value.cp, mask)) return -1; } lp = make_link(); bzero((char *)lp, sizeof(Link)); lp->next = sptr->user->silence; lp->value.cp = strdup(mask); sptr->user->silence = lp; return 0; } static MaxTarget *maxtargets = NULL; /**< For set::max-targets-per-command configuration */ static void maxtarget_add_sorted(MaxTarget *n) { MaxTarget *e; if (!maxtargets) { maxtargets = n; return; } for (e = maxtargets; e; e = e->next) { if (strcmp(n->cmd, e->cmd) < 0) { /* Insert us before */ if (e->prev) e->prev->next = n; else maxtargets = n; /* new head */ n->prev = e->prev; n->next = e; e->prev = n; return; } if (!e->next) { /* Append us at end */ e->next = n; n->prev = e; return; } } } /** Find a maxtarget structure for a cmd (internal) */ MaxTarget *findmaxtarget(char *cmd) { MaxTarget *m; for (m = maxtargets; m; m = m->next) if (!strcasecmp(m->cmd, cmd)) return m; return NULL; } /** Set a maximum targets per command restriction */ void setmaxtargets(char *cmd, int limit) { MaxTarget *m = findmaxtarget(cmd); if (!m) { char cmdupper[64], *i, *o; if (strlen(cmd) > 63) cmd[63] = '\0'; for (i=cmd,o=cmdupper; *i; i++) *o++ = toupper(*i); *o = '\0'; m = MyMallocEx(sizeof(MaxTarget)); m->cmd = strdup(cmdupper); maxtarget_add_sorted(m); } m->limit = limit; } /** Free all set::max-targets-per-command configuration (internal) */ void freemaxtargets(void) { MaxTarget *m, *m_next; for (m = maxtargets; m; m = m_next) { m_next = m->next; safefree(m->cmd); MyFree(m); } maxtargets = NULL; } /** Return the maximum number of targets permitted for a command */ int max_targets_for_command(char *cmd) { MaxTarget *m = findmaxtarget(cmd); if (m) return m->limit; return 1; /* default to 1 */ } void set_isupport_targmax(void) { char buf[512], tbuf[64]; MaxTarget *m; *buf = '\0'; for (m = maxtargets; m; m = m->next) { if (m->limit == MAXTARGETS_MAX) snprintf(tbuf, sizeof(tbuf), "%s:", m->cmd); else snprintf(tbuf, sizeof(tbuf), "%s:%d", m->cmd, m->limit); if (*buf) strlcat(buf, ",", sizeof(buf)); strlcat(buf, tbuf, sizeof(buf)); } IsupportSet(NULL, "TARGMAX", buf); } /** Called between config test and config run */ void set_targmax_defaults(void) { /* Free existing... */ freemaxtargets(); /* Set the defaults */ setmaxtargets("PRIVMSG", 4); setmaxtargets("NOTICE", 1); setmaxtargets("NAMES", 1); // >1 is not supported setmaxtargets("WHOIS", 1); setmaxtargets("WHOWAS", 1); // >1 is not supported setmaxtargets("KICK", 4); setmaxtargets("LIST", MAXTARGETS_MAX); setmaxtargets("JOIN", MAXTARGETS_MAX); setmaxtargets("PART", MAXTARGETS_MAX); setmaxtargets("SAJOIN", MAXTARGETS_MAX); setmaxtargets("SAPART", MAXTARGETS_MAX); setmaxtargets("KILL", MAXTARGETS_MAX); setmaxtargets("DCCALLOW", MAXTARGETS_MAX); /* The following 3 are space-separated (and actually the previous * mentioned DCCALLOW is both space-and-comma separated). * It seems most IRCd's don't list space-separated targets list * in TARGMAX... On the other hand, why not? It says nowhere in * the TARGMAX specification that it's only for comma-separated * commands. So let's be nice and consistent and inform the * clients about the limits for such commands as well: */ setmaxtargets("USERHOST", MAXTARGETS_MAX); // not configurable setmaxtargets("USERIP", MAXTARGETS_MAX); // not configurable setmaxtargets("ISON", MAXTARGETS_MAX); // not configurable setmaxtargets("WATCH", MAXTARGETS_MAX); // not configurable } /** Is the user handshake finished and can register_user() be called? * This checks things like: do we have a NICK, USER, nospoof, * and any other things modules may add: * eg: the cap module checks if client capability negotiation * is in progress */ int is_handshake_finished(aClient *sptr) { Hook *h; int n; for (h = Hooks[HOOKTYPE_IS_HANDSHAKE_FINISHED]; h; h = h->next) { n = (*(h->func.intfunc))(sptr); if (n == 0) return 0; /* We can stop already */ } /* I figured these can be here, in the core: */ if (sptr->user && *sptr->user->username && sptr->name[0] && IsNotSpoof(sptr)) return 1; return 0; }