void cli_connect(int sd) { int cli_fd; struct sockaddr_in srv; socklen_t len = sizeof(srv); while (1) { cli_fd = accept(sd, (struct sockaddr *)&srv, &len); if (cli_fd == -1) continue; cli_handler(cli_fd, srv); close(cli_fd); } }
int m_challenge(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { #ifdef USE_SSL struct ConfItem *aconf; RSA *rsa_public_key; BIO *file = NULL; char *challenge = NULL; char *name; char *tmpname; char chan[CHANNELLEN-1]; char* join[2]; int nl; struct Flags old_mode = cli_flags(sptr); if (!MyUser(sptr)) return 0; if (parc < 2) return need_more_params(sptr, "CHALLENGE"); if (parc > 2) { /* This is a remote OPER Request */ struct Client *srv; if (!string_has_wildcards(parv[1])) srv = FindServer(parv[1]); else srv = find_match_server(parv[1]); if (!feature_bool(FEAT_REMOTE_OPER)) return send_reply(sptr, ERR_NOOPERHOST); if (!srv) return send_reply(sptr, ERR_NOOPERHOST); if (IsMe(srv)) { parv[1] = parv[2]; } else { sendcmdto_one(sptr, CMD_CHALLENGE, srv, "%C %s", srv, parv[2]); return 0; } } /* if theyre an oper, reprint oper motd and ignore */ if (IsOper(sptr)) { send_reply(sptr, RPL_YOUREOPER); if (feature_bool(FEAT_OPERMOTD)) m_opermotd(sptr, sptr, 1, parv); } if (*parv[1] == '+') { /* Ignore it if we aren't expecting this... -A1kmm */ if (cli_user(sptr)->response == NULL) return 0; if (ircd_strcmp(cli_user(sptr)->response, ++parv[1])) { send_reply(sptr, ERR_PASSWDMISMATCH); sendto_allops(&me, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s) (Password Incorrect)", parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); tmpname = strdup(cli_user(sptr)->auth_oper); failed_challenge_notice(sptr, tmpname, "challenge failed"); return 0; } name = strdup(cli_user(sptr)->auth_oper); aconf = find_conf_exact(cli_user(sptr)->auth_oper, cli_user(sptr)->realusername, MyUser(sptr) ? cli_sockhost(sptr) : cli_user(sptr)->realhost, CONF_OPS); if (!aconf) aconf = find_conf_exact(cli_user(sptr)->auth_oper, cli_user(sptr)->realusername, ircd_ntoa((const char*) &(cli_ip(sptr))), CONF_OPS); if (!aconf) aconf = find_conf_cidr(cli_user(sptr)->auth_oper, cli_user(sptr)->realusername, cli_ip(sptr), CONF_OPS); if (!aconf) { send_reply(sptr, ERR_NOOPERHOST); sendto_allops(&me, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s) (No O:line)", parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); return 0; } if (CONF_LOCOP == aconf->status) { ClearOper(sptr); SetLocOp(sptr); } else { /* * prevent someone from being both oper and local oper */ ClearLocOp(sptr); if (!feature_bool(FEAT_OPERFLAGS) || !(aconf->port & OFLAG_ADMIN)) { /* Global Oper */ SetOper(sptr); ClearAdmin(sptr); } else { /* Admin */ SetOper(sptr); OSetGlobal(sptr); SetAdmin(sptr); } ++UserStats.opers; } cli_handler(cptr) = OPER_HANDLER; if (!feature_bool(FEAT_OPERFLAGS) || (aconf->port & OFLAG_WHOIS)) { OSetWhois(sptr); } if (!feature_bool(FEAT_OPERFLAGS) || (aconf->port & OFLAG_IDLE)) { OSetIdle(sptr); } if (!feature_bool(FEAT_OPERFLAGS) || (aconf->port & OFLAG_XTRAOP)) { OSetXtraop(sptr); } if (!feature_bool(FEAT_OPERFLAGS) || (aconf->port & OFLAG_HIDECHANS)) { OSetHideChans(sptr); } SetFlag(sptr, FLAG_WALLOP); SetFlag(sptr, FLAG_SERVNOTICE); SetFlag(sptr, FLAG_DEBUG); if (!IsAdmin(sptr)) cli_oflags(sptr) = aconf->port; set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD); client_set_privs(sptr, aconf); cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */ send_umode_out(cptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); send_reply(sptr, RPL_YOUREOPER); if (IsAdmin(sptr)) { sendto_allops(&me, SNO_OLDSNO, "%s (%s@%s) is now an IRC Administrator", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); /* Autojoin admins to admin channel and oper channel (if enabled) */ if (feature_bool(FEAT_AUTOJOIN_ADMIN)) { if (feature_bool(FEAT_AUTOJOIN_ADMIN_NOTICE)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, feature_str(FEAT_AUTOJOIN_ADMIN_NOTICE_VALUE)); ircd_strncpy(chan, feature_str(FEAT_AUTOJOIN_ADMIN_CHANNEL), CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; m_join(sptr, sptr, 2, join); } if (feature_bool(FEAT_AUTOJOIN_OPER) && IsOper(sptr)) { if (feature_bool(FEAT_AUTOJOIN_OPER_NOTICE)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, feature_str(FEAT_AUTOJOIN_OPER_NOTICE_VALUE)); ircd_strncpy(chan, feature_str(FEAT_AUTOJOIN_OPER_CHANNEL), CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; m_join(sptr, sptr, 2, join); } } else { sendto_allops(&me, SNO_OLDSNO, "%s (%s@%s) is now an IRC Operator (%c)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr), IsOper(sptr) ? 'O' : 'o'); if (feature_bool(FEAT_AUTOJOIN_OPER) && IsOper(sptr)) { if (feature_bool(FEAT_AUTOJOIN_OPER_NOTICE)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, feature_str(FEAT_AUTOJOIN_OPER_NOTICE_VALUE)); ircd_strncpy(chan, feature_str(FEAT_AUTOJOIN_OPER_CHANNEL), CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; m_join(sptr, sptr, 2, join); } } if (feature_bool(FEAT_OPERMOTD)) m_opermotd(sptr, sptr, 1, parv); log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", name, sptr); ircd_snprintf(0, cli_user(sptr)->response, BUFSIZE+1, "%s", ""); return 0; } ircd_snprintf(0, cli_user(sptr)->response, BUFSIZE+1, "%s", ""); ircd_snprintf(0, cli_user(sptr)->auth_oper, NICKLEN+1, "%s", ""); aconf = find_conf_exact(parv[1], cli_user(sptr)->realusername, cli_user(sptr)->realhost, CONF_OPS); if (!aconf) aconf = find_conf_exact(parv[1], cli_user(sptr)->realusername, ircd_ntoa((const char*) &(cli_ip(sptr))), CONF_OPS); if (!aconf) aconf = find_conf_cidr(parv[1], cli_user(sptr)->realusername, cli_ip(sptr), CONF_OPS); if (aconf == NULL) { send_reply(sptr, ERR_NOOPERHOST); failed_challenge_notice(sptr, parv[1], "No o:line"); sendto_allops(&me, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s) (No O:line)", parv[0], cli_user(sptr)->realusername, cli_sockhost(sptr)); return 0; } if (!(aconf->port & OFLAG_RSA)) { send_reply(sptr, RPL_NO_CHALL); return 0; } if (!verify_sslclifp(sptr, aconf)) { sendto_allops(&me, SNO_OLDREALOP, "Failed OPER attempt by %s (%s@%s) (SSL Fingerprint Missmatch)", parv[0], cli_user(sptr)->realusername, cli_user(sptr)->realhost); send_reply(sptr, ERR_SSLCLIFP); return 0; } if ((file = BIO_new_file(aconf->passwd, "r")) == NULL) { send_reply(sptr, RPL_NO_KEY); return 0; } rsa_public_key = (RSA *)PEM_read_bio_RSA_PUBKEY(file, NULL, 0, NULL); if (rsa_public_key == NULL) return send_reply(sptr, RPL_INVALID_KEY); if (!generate_challenge(&challenge, rsa_public_key, sptr)) { Debug((DEBUG_DEBUG, "generating challenge sum (%s)", challenge)); send_reply(sptr, RPL_RSACHALLENGE, challenge); ircd_snprintf(0, cli_user(sptr)->auth_oper, NICKLEN + 1, "%s", aconf->name); } nl = BIO_set_close(file, BIO_CLOSE); BIO_free(file); return 1; #else return 1; #endif }
/** Handle a connection that has sent a valid PASS and SERVER. * @param cptr New peer server. * @param aconf Connect block for \a cptr. * @return Zero. */ int server_estab(struct Client *cptr, struct ConfItem *aconf) { struct Client* acptr = 0; const char* inpath; int i; assert(0 != cptr); assert(0 != cli_local(cptr)); inpath = cli_name(cptr); if (IsUnknown(cptr)) { if (aconf->passwd[0]) sendrawto_one(cptr, MSG_PASS " :%s", aconf->passwd); /* * Pass my info to the new server */ sendrawto_one(cptr, MSG_SERVER " %s 1 %Tu %Tu J%s %s%s +%s6 :%s", cli_name(&me), cli_serv(&me)->timestamp, cli_serv(cptr)->timestamp, MAJOR_PROTOCOL, NumServCap(&me), feature_bool(FEAT_HUB) ? "h" : "", *(cli_info(&me)) ? cli_info(&me) : "IRCers United"); } det_confs_butmask(cptr, CONF_SERVER); if (!IsHandshake(cptr)) hAddClient(cptr); SetServer(cptr); cli_handler(cptr) = SERVER_HANDLER; Count_unknownbecomesserver(UserStats); SetBurst(cptr); /* nextping = CurrentTime; */ /* * NOTE: check for acptr->user == cptr->serv->user is necessary to insure * that we got the same one... bleah */ if (cli_serv(cptr)->user && *(cli_serv(cptr))->by && (acptr = findNUser(cli_serv(cptr)->by))) { if (cli_user(acptr) == cli_serv(cptr)->user) { sendcmdto_one(&me, CMD_NOTICE, acptr, "%C :Link with %s established.", acptr, inpath); } else { /* * if not the same client, set by to empty string */ acptr = 0; *(cli_serv(cptr))->by = '\0'; } } sendto_opmask(acptr, SNO_OLDSNO, "Link with %s established.", inpath); cli_serv(cptr)->up = &me; cli_serv(cptr)->updown = add_dlink(&(cli_serv(&me))->down, cptr); sendto_opmask(0, SNO_NETWORK, "Net junction: %s %s", cli_name(&me), cli_name(cptr)); SetJunction(cptr); /* * 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 <= HighestFd; i++) { if (!(acptr = LocalClientArray[i]) || !IsServer(acptr) || acptr == cptr || IsMe(acptr)) continue; if (!match(cli_name(&me), cli_name(cptr))) continue; sendcmdto_one(&me, CMD_SERVER, acptr, "%s 2 0 %Tu J%02u %s%s +%s%s%s :%s", cli_name(cptr), cli_serv(cptr)->timestamp, Protocol(cptr), NumServCap(cptr), IsHub(cptr) ? "h" : "", IsService(cptr) ? "s" : "", IsIPv6(cptr) ? "6" : "", cli_info(cptr)); } /* Send these as early as possible so that glined users/juped servers can * be removed from the network while the remote server is still chewing * our burst. */ gline_burst(cptr); jupe_burst(cptr); /* * Pass on my client information to the new server * * First, pass only servers (idea is that if the link gets * canceled because the server was already there, * there are no NICK's to be canceled...). 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... */ for (acptr = &me; acptr; acptr = cli_prev(acptr)) { /* acptr->from == acptr for acptr == cptr */ if (cli_from(acptr) == cptr) continue; if (IsServer(acptr)) { const char* protocol_str; if (Protocol(acptr) > 9) protocol_str = IsBurst(acptr) ? "J" : "P"; else protocol_str = IsBurst(acptr) ? "J0" : "P0"; if (0 == match(cli_name(&me), cli_name(acptr))) continue; sendcmdto_one(cli_serv(acptr)->up, CMD_SERVER, cptr, "%s %d 0 %Tu %s%u %s%s +%s%s%s :%s", cli_name(acptr), cli_hopcount(acptr) + 1, cli_serv(acptr)->timestamp, protocol_str, Protocol(acptr), NumServCap(acptr), IsHub(acptr) ? "h" : "", IsService(acptr) ? "s" : "", IsIPv6(acptr) ? "6" : "", cli_info(acptr)); } } for (acptr = &me; acptr; acptr = cli_prev(acptr)) { /* acptr->from == acptr for acptr == cptr */ if (cli_from(acptr) == cptr) continue; if (IsUser(acptr)) { char xxx_buf[25]; char *s = umode_str(acptr); sendcmdto_one(cli_user(acptr)->server, CMD_NICK, cptr, "%s %d %Tu %s %s %s%s%s%s %s%s :%s", cli_name(acptr), cli_hopcount(acptr) + 1, cli_lastnick(acptr), cli_user(acptr)->username, cli_user(acptr)->realhost, *s ? "+" : "", s, *s ? " " : "", iptobase64(xxx_buf, &cli_ip(acptr), sizeof(xxx_buf), IsIPv6(cptr)), NumNick(acptr), cli_info(acptr)); } } /* * Last, send the BURST. * (Or for 2.9 servers: pass all channels plus statuses) */ { struct Channel *chptr; for (chptr = GlobalChannelList; chptr; chptr = chptr->next) send_channel_modes(cptr, chptr); } sendcmdto_one(&me, CMD_END_OF_BURST, cptr, ""); return 0; }
/* * m_oper - generic message handler */ int m_oper(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { struct ConfItem* aconf; char* name; char* password; assert(0 != cptr); assert(cptr == sptr); name = parc > 1 ? parv[1] : 0; password = parc > 2 ? parv[2] : 0; if (EmptyString(name) || EmptyString(password)) return need_more_params(sptr, "OPER"); aconf = find_conf_exact(name, sptr, CONF_OPERATOR); if (!aconf || IsIllegal(aconf)) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s (%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); return 0; } assert(0 != (aconf->status & CONF_OPERATOR)); if (oper_password_match(password, aconf->passwd)) { struct Flags old_mode = cli_flags(sptr); if (ACR_OK != attach_conf(sptr, aconf)) { send_reply(sptr, ERR_NOOPERHOST); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s " "(%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); return 0; } SetLocOp(sptr); client_set_privs(sptr, aconf); if (HasPriv(sptr, PRIV_PROPAGATE)) { ClearLocOp(sptr); SetOper(sptr); ++UserStats.opers; } cli_handler(cptr) = OPER_HANDLER; SetFlag(sptr, FLAG_WALLOP); SetFlag(sptr, FLAG_SERVNOTICE); SetFlag(sptr, FLAG_DEBUG); set_snomask(sptr, SNO_OPERDEFAULT, SNO_ADD); cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */ send_umode_out(cptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); send_reply(sptr, RPL_YOUREOPER); sendto_opmask_butone(0, SNO_OLDSNO, "%s (%s@%s) has authenticated as staff", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", name, sptr); } else { send_reply(sptr, ERR_PASSWDMISMATCH); sendto_opmask_butone(0, SNO_OLDREALOP, "Failed staff authentication attempt by %s (%s@%s)", parv[0], cli_user(sptr)->username, cli_sockhost(sptr)); } 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); }
/** 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); }
/** Run the daemon. * @param[in] argc Number of arguments in \a argv. * @param[in] argv Arguments to program execution. */ int main(int argc, char **argv) { CurrentTime = time(NULL); thisServer.argc = argc; thisServer.argv = argv; thisServer.uid = getuid(); thisServer.euid = geteuid(); #ifdef MDEBUG mem_dbg_initialise(); #endif #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_CORE) set_core_limit(); #endif umask(077); /* better safe than sorry --SRB */ memset(&me, 0, sizeof(me)); memset(&me_con, 0, sizeof(me_con)); cli_connect(&me) = &me_con; cli_fd(&me) = -1; parse_command_line(argc, argv); if (chdir(dpath)) { fprintf(stderr, "Fail: Cannot chdir(%s): %s, check DPATH\n", dpath, strerror(errno)); return 2; } if (!set_userid_if_needed()) return 3; /* Check paths for accessibility */ if (!check_file_access(SPATH, 'S', X_OK) || !check_file_access(configfile, 'C', R_OK)) return 4; if (!init_connection_limits()) return 9; close_connections(!(thisServer.bootopt & (BOOT_DEBUG | BOOT_TTY | BOOT_CHKCONF))); /* daemon_init() must be before event_init() because kqueue() FDs * are, perversely, not inherited across fork(). */ daemon_init(thisServer.bootopt & BOOT_TTY); #ifdef DEBUGMODE /* Must reserve fd 2... */ if (debuglevel >= 0 && !(thisServer.bootopt & BOOT_TTY)) { int fd; if ((fd = open("/dev/null", O_WRONLY)) < 0) { fprintf(stderr, "Unable to open /dev/null (to reserve fd 2): %s\n", strerror(errno)); return 8; } if (fd != 2 && dup2(fd, 2) < 0) { fprintf(stderr, "Unable to reserve fd 2; dup2 said: %s\n", strerror(errno)); return 8; } } #endif event_init(MAXCONNECTIONS); setup_signals(); feature_init(); /* initialize features... */ log_init(*argv); set_nomem_handler(outofmemory); initload(); init_list(); init_hash(); init_class(); initwhowas(); initmsgtree(); initstats(); /* we need this for now, when we're modular this should be removed -- hikari */ ircd_crypt_init(); motd_init(); if (!init_conf()) { log_write(LS_SYSTEM, L_CRIT, 0, "Failed to read configuration file %s", configfile); return 7; } if (thisServer.bootopt & BOOT_CHKCONF) { if (dbg_client) conf_debug_iline(dbg_client); fprintf(stderr, "Configuration file %s checked okay.\n", configfile); return 0; } debug_init(thisServer.bootopt & BOOT_TTY); if (check_pid()) { Debug((DEBUG_FATAL, "Failed to acquire PID file lock after fork")); exit(2); } init_server_identity(); uping_init(); stats_init(); IPcheck_init(); timer_add(timer_init(&connect_timer), try_connections, 0, TT_RELATIVE, 1); timer_add(timer_init(&ping_timer), check_pings, 0, TT_RELATIVE, 1); timer_add(timer_init(&destruct_event_timer), exec_expired_destruct_events, 0, TT_PERIODIC, 60); timer_add(timer_init(&mute_timer), check_expired_mutes, 0, TT_PERIODIC, 30); CurrentTime = time(NULL); SetMe(&me); cli_magic(&me) = CLIENT_MAGIC; cli_from(&me) = &me; make_server(&me); cli_serv(&me)->timestamp = TStime(); /* Abuse own link timestamp as start TS */ cli_serv(&me)->prot = atoi(MAJOR_PROTOCOL); cli_serv(&me)->up = &me; cli_serv(&me)->down = NULL; cli_handler(&me) = SERVER_HANDLER; SetYXXCapacity(&me, MAXCLIENTS); cli_lasttime(&me) = cli_since(&me) = cli_firsttime(&me) = CurrentTime; hAddClient(&me); write_pidfile(); init_counters(); Debug((DEBUG_NOTICE, "Server ready...")); log_write(LS_SYSTEM, L_NOTICE, 0, "Server Ready"); event_loop(); return 0; }
void do_oper(struct Client* cptr, struct Client* sptr, struct ConfItem* aconf) { struct Flags old_mode = cli_flags(sptr); char* modes; char* parv[2]; char* join[3]; char chan[CHANNELLEN-1]; char* ajoinchan; char* ajoinnotice; unsigned int snomask = 0; parv[0] = cli_name(sptr); parv[1] = NULL; SetOper(sptr); client_set_privs(sptr, aconf); ClearOper(sptr); snomask = ConfSnoMask(aconf) & SNO_ALL; snomask |= aconf->snomask & SNO_ALL; ajoinchan = ConfAjoinChan(aconf); ajoinnotice = ConfAjoinNotice(aconf); if (MyUser(sptr)) { SetLocOp(sptr); if (HasPriv(sptr, PRIV_PROPAGATE)) { ClearLocOp(sptr); SetOper(sptr); if (HasPriv(sptr, PRIV_ADMIN)) SetAdmin(sptr); if (!IsHideOper(sptr) && !IsChannelService(sptr) && !IsBot(sptr)) ++UserStats.opers; } cli_handler(sptr) = OPER_HANDLER; SetFlag(sptr, FLAG_WALLOP); SetFlag(sptr, FLAG_SERVNOTICE); SetFlag(sptr, FLAG_DEBUG); if (snomask) set_snomask(sptr, snomask, SNO_ADD); else set_snomask(sptr, feature_int(FEAT_SNOMASK_OPERDEFAULT), SNO_ADD); cli_max_sendq(sptr) = 0; /* Get the sendq from the oper's class */ cli_max_recvq(sptr) = 0; /* Get the recvq from the oper's class */ cli_lag_min(sptr) = -2; /* Get the fake lag minimum from the oper's class */ cli_lag_factor(sptr) = -2; /* Get the fake lag factor from the oper's class */ send_umode_out(sptr, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); } else { client_send_privs(&me, sptr, sptr); if (HasPriv(sptr, PRIV_PROPAGATE)) { modes = (HasPriv(sptr, PRIV_ADMIN) ? "aowsg" : "owsg"); } else { modes = "Owsg"; } sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes); } modes = ConfUmode(aconf); if (modes) { if (MyUser(sptr)) { char *umodev[] = { NULL, NULL, NULL, NULL }; umodev[1] = cli_name(sptr); umodev[2] = modes; old_mode = cli_flags(sptr); set_user_mode(sptr, sptr, 3, umodev, ALLOWMODES_ANY); send_umode(NULL, sptr, &old_mode, HasPriv(sptr, PRIV_PROPAGATE)); if ((cli_snomask(sptr) != feature_int(FEAT_SNOMASK_OPERDEFAULT)) && HasFlag(sptr, FLAG_SERVNOTICE)) send_reply(sptr, RPL_SNOMASK, cli_snomask(sptr), cli_snomask(sptr)); } else { if (snomask) sendcmdto_one(&me, CMD_MODE, sptr, "%s %s+s +%d", cli_name(sptr), modes, snomask); else sendcmdto_one(&me, CMD_MODE, sptr, "%s %s", cli_name(sptr), modes); } } send_reply(sptr, RPL_YOUREOPER); if ((feature_int(FEAT_HOST_HIDING_STYLE) == 1) || (feature_int(FEAT_HOST_HIDING_STYLE) == 3)) hide_hostmask(sptr); if (!EmptyString(ajoinchan)) { if (!EmptyString(ajoinnotice)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, ajoinnotice); ircd_strncpy(chan, ajoinchan, CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; join[2] = NULL; m_join(sptr, sptr, 2, join); } if (!EmptyString(aconf->autojoinchan)) { if (!EmptyString(aconf->autojoinnotice)) sendcmdto_one(&me, CMD_NOTICE, sptr, "%C :%s", sptr, aconf->autojoinnotice); ircd_strncpy(chan, aconf->autojoinchan, CHANNELLEN-1); join[0] = cli_name(sptr); join[1] = chan; join[2] = NULL; m_join(sptr, sptr, 2, join); } sendto_opmask_butone_global((MyUser(sptr) ? &me : NULL), SNO_OLDSNO, "%s (%s@%s) is now operator (%c)", cli_name(sptr), cli_user(sptr)->username, cli_user(sptr)->realhost, IsOper(sptr) ? 'O' : 'o'); if (feature_bool(FEAT_OPERMOTD)) m_opermotd(sptr, sptr, 1, parv); log_write(LS_OPER, L_INFO, 0, "OPER (%s) by (%#C)", aconf->name, sptr); }