diff options
Diffstat (limited to 'ircd/s_service.c')
-rw-r--r-- | ircd/s_service.c | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/ircd/s_service.c b/ircd/s_service.c new file mode 100644 index 0000000..a86bd02 --- /dev/null +++ b/ircd/s_service.c @@ -0,0 +1,708 @@ +/************************************************************************ + * IRC - Internet Relay Chat, ircd/s_service.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. + */ + +#ifndef lint +static char rcsid[] = "@(#)$Id: s_service.c,v 1.30 1999/07/02 16:49:37 kalt Exp $"; +#endif + +#include "os.h" +#include "s_defines.h" +#define S_SERVICE_C +#include "s_externs.h" +#undef S_SERVICE_C + +aService *svctop = NULL; + +aService *make_service(cptr) +aClient *cptr; +{ + Reg aService *svc = cptr->service; + + if (svc) + return svc; + + cptr->service = svc = (aService *)MyMalloc(sizeof(*svc)); + bzero((char *)svc, sizeof(*svc)); + svc->bcptr = cptr; + if (svctop) + svctop->prevs = svc; + svc->nexts = svctop; + svc->prevs = NULL; /* useless */ + svctop = svc; + return svc; +} + + +void free_service(cptr) +aClient *cptr; +{ + Reg aService *serv; + + if ((serv = cptr->service)) + { + if (serv->nexts) + serv->nexts->prevs = serv->prevs; + if (serv->prevs) + serv->prevs->nexts = serv->nexts; + if (svctop == serv) + svctop = serv->nexts; + if (serv->servp) + free_server(serv->servp, cptr); + if (serv->server) + MyFree(serv->server); + MyFree((char *)serv); + cptr->service = NULL; + } +} + + +static aClient *best_service(name, cptr) +char *name; +aClient *cptr; +{ + Reg aClient *acptr = NULL; + Reg aClient *bcptr; + Reg aService *sp; + int len = strlen(name); + + if (!index(name, '@') || !(acptr = find_service(name, cptr))) + for (sp = svctop; sp; sp = sp->nexts) + if ((bcptr = sp->bcptr) && + !myncmp(name, bcptr->name, len)) + { + acptr = bcptr; + break; + } + return (acptr ? acptr : cptr); +} + + +#ifdef USE_SERVICES +/* +** check_services_butone +** check all local services except `cptr', and send `fmt' according to: +** action type on notice +** server origin +*/ +#if ! USE_STDARG +void check_services_butone(action, server, cptr, fmt, p1, p2, p3, p4, + p5, p6, p7, p8) +long action; +aClient *cptr; /* shouldn't this be named sptr? */ +char *fmt, *server; +void *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8; +#else +void check_services_butone(long action, char *server, aClient *cptr, char *fmt, ...) +#endif +{ + char nbuf[NICKLEN + USERLEN + HOSTLEN + 3]; + Reg aClient *acptr; + Reg int i; + + *nbuf = '\0'; + for (i = 0; i <= highest_fd; i++) + { + if (!(acptr = local[i]) || !IsService(acptr) || + (cptr && acptr == cptr->from)) + continue; + /* + ** found a (local) service, check if action matches what's + ** wanted AND if it comes from a server matching the dist + */ + if ((acptr->service->wants & action) + && (!server || !match(acptr->service->dist, server))) + if ((acptr->service->wants & SERVICE_WANT_PREFIX) && + cptr && IsRegisteredUser(cptr) && + (action & SERVICE_MASK_PREFIX)) + { +#if USE_STDARG + char buf[2048]; + va_list va; + va_start(va, fmt); + va_arg(va, char *); + vsprintf(buf, fmt+3, va); + va_end(va); +#endif + sprintf(nbuf, "%s!%s@%s", cptr->name, + cptr->user->username,cptr->user->host); + +#if ! USE_STDARG + sendto_one(acptr, fmt, nbuf, p2, p3, p4, p5, + p6, p7, p8); +#else + sendto_one(acptr, ":%s%s", nbuf, buf); +#endif + } + else + { +#if ! USE_STDARG + sendto_one(acptr, fmt, p1, p2, p3, p4, p5, + p6, p7, p8); +#else + va_list va; + va_start(va, fmt); + vsendto_one(acptr, fmt, va); + va_end(va); +#endif + } + } + return; +} + +/* +** sendnum_toone +** send the NICK + USER + UMODE for sptr to cptr according to wants +*/ +static void sendnum_toone (cptr, wants, sptr, umode) +aClient *cptr, *sptr; +char *umode; +int wants; +{ + + if (!*umode) + umode = "+"; + + if (wants & SERVICE_WANT_EXTNICK) + /* extended NICK syntax */ + sendto_one(cptr, "NICK %s %d %s %s %s %s :%s", + (wants & SERVICE_WANT_NICK) ? sptr->name : ".", + sptr->hopcount + 1, + (wants & SERVICE_WANT_USER) ? sptr->user->username + : ".", + (wants & SERVICE_WANT_USER) ? sptr->user->host :".", + (wants & SERVICE_WANT_USER) ? + ((wants & SERVICE_WANT_TOKEN) ? + sptr->user->servp->tok : sptr->user->server) : ".", + (wants & SERVICE_WANT_UMODE) ? umode : "+", + (wants & SERVICE_WANT_USER) ? sptr->info : ""); + else + /* old style NICK + USER + UMODE */ + { + char nbuf[NICKLEN + USERLEN + HOSTLEN + 3]; + char *prefix; + + if (wants & SERVICE_WANT_PREFIX) + { + sprintf(nbuf, "%s!%s@%s", sptr->name, + sptr->user->username, sptr->user->host); + prefix = nbuf; + } + else + prefix = sptr->name; + + if (wants & SERVICE_WANT_NICK) + sendto_one(cptr, "NICK %s :%d", sptr->name, + sptr->hopcount+1); + if (wants & SERVICE_WANT_USER) + sendto_one(cptr, ":%s USER %s %s %s :%s", prefix, + sptr->user->username, sptr->user->host, + (wants & SERVICE_WANT_TOKEN)? + sptr->user->servp->tok : sptr->user->server, + sptr->info); + if (wants & SERVICE_WANT_UMODE|SERVICE_WANT_OPER) + sendto_one(cptr, ":%s MODE %s %s", prefix, sptr->name, + umode); + } +} + +/* +** check_services_num +** check all local services to eventually send NICK + USER + UMODE +** for new client sptr +*/ +void check_services_num(sptr, umode) +aClient *sptr; +char *umode; +{ + Reg aClient *acptr; + Reg int i; + + for (i = 0; i <= highest_fd; i++) + { + if (!(acptr = local[i]) || !IsService(acptr)) + continue; + /* + ** found a (local) service, check if action matches what's + ** wanted AND if it comes from a server matching the dist + */ + if ((acptr->service->wants & SERVICE_MASK_NUM) + && !match(acptr->service->dist, sptr->user->server)) + sendnum_toone(acptr, acptr->service->wants, sptr, + umode); + } +} + + +aConfItem *find_conf_service(cptr, type, aconf) +aClient *cptr; +aConfItem *aconf; +int type; +{ + static char uhost[HOSTLEN+USERLEN+3]; + Reg aConfItem *tmp; + char *s; + struct hostent *hp; + int i; + + for (tmp = conf; tmp; tmp = tmp->next) + { + /* + ** Accept if the *real* hostname (usually sockethost) + ** matches host field of the configuration, the name field + ** is the same, the type match is correct and nobody else + ** is using this S-line. + */ + if (!(tmp->status & CONF_SERVICE)) + continue; + Debug((DEBUG_INFO,"service: cl=%d host (%s) name (%s) port=%d", + tmp->clients, tmp->host, tmp->name, tmp->port)); + Debug((DEBUG_INFO,"service: host (%s) name (%s) type=%d", + cptr->sockhost, cptr->name, type)); + if (tmp->clients || (type && tmp->port != type) || + mycmp(tmp->name, cptr->name)) + continue; + if ((hp = cptr->hostp)) + for (s = hp->h_name, i = 0; s; s = hp->h_aliases[i++]) + { + SPRINTF(uhost, "%s@%s", cptr->username, s); + if (match(tmp->host, uhost) == 0) + return tmp; + } + SPRINTF(uhost, "%s@%s", cptr->username, cptr->sockhost); + if (match(tmp->host, uhost) == 0) + return tmp; + } + return aconf; +} +#endif + + +/* +** m_service +** +** parv[0] = sender prefix +** parv[1] = service name +** parv[2] = server token +** parv[3] = distribution code +** parv[4] = service type +** parv[5] = hopcount +** parv[6] = info +*/ +int m_service(cptr, sptr, parc, parv) +aClient *cptr, *sptr; +int parc; +char *parv[]; +{ + Reg aClient *acptr = NULL; + Reg aService *svc; +#ifdef USE_SERVICES + Reg aConfItem *aconf; +#endif + aServer *sp = NULL; + char *dist, *server = NULL, *info, *stok; + int type, metric = 0, i; + char *mlname; + + if (sptr->user) + { + sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0])); + return 1; + } + + if (parc < 7 || *parv[1] == '\0' || *parv[2] == '\0' || + *parv[3] == '\0' || *parv[6] == '\0') + { + sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS, + BadPtr(parv[0]) ? "*" : parv[0]), "SERVICE"); + return 1; + } + + /* Copy parameters into better documenting variables */ + + /* + * Change the sender's origin. + */ + if (IsServer(cptr)) + { + sptr = make_client(cptr); + add_client_to_list(sptr); + strncpyzt(sptr->name, parv[1], sizeof(sptr->name)); + server = parv[2]; + metric = atoi(parv[5]); + sp = find_tokserver(atoi(server), cptr, NULL); + if (!sp) + { + sendto_flag(SCH_ERROR, + "ERROR: SERVICE:%s without SERVER:%s from %s", + sptr->name, server, + get_client_name(cptr, FALSE)); + return exit_client(NULL, sptr, &me, "No Such Server"); + } + if (match(parv[3], ME)) + { + sendto_flag(SCH_ERROR, + "ERROR: SERVICE:%s DIST:%s from %s", sptr->name, + parv[3], get_client_name(cptr, FALSE)); + return exit_client(NULL, sptr, &me, + "Distribution code mismatch"); + } + } +#ifndef USE_SERVICES + else + { + sendto_one(cptr, "ERROR :Server doesn't support services"); + return 1; + } +#endif + + dist = parv[3]; + type = atoi(parv[4]); + info = parv[6]; + +#ifdef USE_SERVICES + if (!IsServer(cptr)) + { + metric = 0; + server = ME; + sp = me.serv; + if (!do_nick_name(parv[1], 0)) + { + sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME, + parv[0]), parv[1]); + return 1; + } + if (strlen(parv[1]) + strlen(server) + 2 >= (size_t) HOSTLEN) + { + sendto_one(acptr, "ERROR :Servicename is too long."); + sendto_flag(SCH_ERROR, + "Access for service %d (%s) denied (%s)", + type, parv[1], "servicename too long"); + return exit_client(cptr, sptr, &me, "Name too long"); + } + + strncpyzt(sptr->name, parv[1], sizeof(sptr->name)); + if (!(aconf = find_conf_service(sptr, type, NULL))) + { + sendto_one(sptr, + "ERROR :Access denied (service %d) %s", + type, get_client_name(sptr, TRUE)); + sendto_flag(SCH_ERROR, + "Access denied (service %d) %s", type, + get_client_name(sptr, TRUE)); + return exit_client(cptr, sptr, &me, "Not enabled"); + } + + if (!BadPtr(aconf->passwd) && + !StrEq(aconf->passwd, sptr->passwd)) + { + sendto_flag(SCH_ERROR, + "Access denied: (passwd mismatch) %s", + get_client_name(sptr, TRUE)); + return exit_client(cptr, sptr, &me, "Bad Password"); + } + + (void)strcat(sptr->name, "@"), strcat(sptr->name, server); + if (find_service(sptr->name, NULL)) + { + sendto_flag(SCH_ERROR, "Service %s already exists", + get_client_name(sptr, TRUE)); + return exit_client(cptr, sptr, &me, "Service Exists"); + } + attach_conf(sptr, aconf); + sendto_one(sptr, rpl_str(RPL_YOURESERVICE, sptr->name), + sptr->name); + sendto_one(sptr, rpl_str(RPL_YOURHOST, sptr->name), + get_client_name(&me, FALSE), version); + sendto_one(sptr, rpl_str(RPL_MYINFO, sptr->name), ME, version); + sendto_flag(SCH_NOTICE, "Service %s connected", + get_client_name(sptr, TRUE)); + istat.is_unknown--; + istat.is_myservice++; + } +#endif + + istat.is_service++; + svc = make_service(sptr); + SetService(sptr); + svc->servp = sp; + sp->refcnt++; + svc->server = mystrdup(sp->bcptr->name); + strncpyzt(svc->dist, dist, HOSTLEN); + if (sptr->info != DefInfo) + MyFree(sptr->info); + if (strlen(info) > REALLEN) info[REALLEN] = '\0'; + sptr->info = mystrdup(info); + svc->wants = 0; + svc->type = type; + sptr->hopcount = metric; + reorder_client_in_list(sptr); + (void)add_to_client_hash_table(sptr->name, sptr); + +#ifdef USE_SERVICES + check_services_butone(SERVICE_WANT_SERVICE, NULL, sptr, + "SERVICE %s %s %s %d %d :%s", sptr->name, + server, dist, type, metric, info); +#endif + sendto_flag(SCH_SERVICE, "Received SERVICE %s from %s (%s %d %s)", + sptr->name, get_client_name(cptr, TRUE), dist, metric, + info); + + for (i = fdas.highest; i >= 0; i--) + { + if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) || + acptr == cptr) + continue; + if (match(dist, acptr->name)) + continue; + mlname = my_name_for_link(ME, acptr->serv->nline->port); + if (*mlname == '*' && match(mlname, sptr->service->server)== 0) + stok = me.serv->tok; + else + stok = sp->tok; + sendto_one(acptr, "SERVICE %s %s %s %d %d :%s", sptr->name, + stok, dist, type, metric+1, info); + } + return 0; +} + + +/* +** Returns list of all matching services. +** parv[1] - string to match names against +** parv[2] - type of service +*/ +int m_servlist(cptr, sptr, parc, parv) +aClient *cptr, *sptr; +int parc; +char *parv[]; +{ + Reg aService *sp; + Reg aClient *acptr; + char *mask = BadPtr(parv[1]) ? "*" : parv[1]; + int type = 0; + + if (parc > 2) + type = BadPtr(parv[2]) ? 0 : atoi(parv[2]); + for (sp = svctop; sp; sp = sp->nexts) + if ((acptr = sp->bcptr) && (!type || type == sp->type) && + (match(mask, acptr->name) == 0)) + sendto_one(sptr, rpl_str(RPL_SERVLIST, parv[0]), + acptr->name, sp->server, sp->dist, + sp->type, acptr->hopcount, acptr->info); + sendto_one(sptr, rpl_str(RPL_SERVLISTEND, parv[0]), mask, type); + return 2; +} + + +#ifdef USE_SERVICES +/* +** m_servset +** +** parv[0] = sender prefix +** parv[1] = data requested +** parv[2] = burst requested (optional) +*/ +int m_servset(cptr, sptr, parc, parv) +aClient *cptr, *sptr; +int parc; +char *parv[]; +{ + aClient *acptr; + int burst = 0; + + if (!MyConnect(sptr)) + { + sendto_flag(SCH_ERROR, "%s issued a SERVSET (from %s)", + sptr->name, get_client_name(cptr, TRUE)); + return 1; + } + if (!IsService(sptr) || (IsService(sptr) && sptr->service->wants)) + { + sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0])); + return 1; + } + if (parc < 2) + { + sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]), + "SERVSET"); + return 1; + } + if (sptr->service->wants) + return 1; + + /* check against configuration */ + sptr->service->wants = atoi(parv[1]) & sptr->service->type; + /* check that service is global for some requests */ + if (strcmp(sptr->service->dist, "*")) + sptr->service->wants &= ~SERVICE_MASK_GLOBAL; + /* allow options */ + sptr->service->wants |= (atoi(parv[1]) & ~SERVICE_MASK_ALL); + /* send accepted SERVSET */ + sendto_one(sptr, ":%s SERVSET %s :%d", sptr->name, sptr->name, + sptr->service->wants); + + if (parc < 3 || + ((burst = sptr->service->wants & atoi(parv[2])) == 0)) + return 0; + + /* + ** services can request a connect burst. + ** it is optional, because most services should not need it, + ** so let's save some bandwidth. + ** + ** tokens are NOT used. (2.8.x like burst) + ** distribution code is respected. + ** service type also respected. + */ + /* cptr->flags |= FLAGS_CBURST; doesn't work.. */ + if (burst & SERVICE_WANT_SERVER) + { + int split; + + for (acptr = &me; acptr; acptr = acptr->prev) + { + if (!IsServer(acptr) && !IsMe(acptr)) + continue; + if (match(sptr->service->dist, acptr->name)) + continue; + split = (MyConnect(acptr) && + mycmp(acptr->name, acptr->sockhost)); + if (split) + sendto_one(sptr,":%s SERVER %s %d %s :[%s] %s", + acptr->serv->up, acptr->name, + acptr->hopcount+1, + acptr->serv->tok, + acptr->sockhost, acptr->info); + else + sendto_one(sptr, ":%s SERVER %s %d %s :%s", + acptr->serv->up, acptr->name, + acptr->hopcount+1, + acptr->serv->tok, + acptr->info); + } + } + + if (burst & (SERVICE_WANT_NICK|SERVICE_WANT_USER|SERVICE_WANT_SERVICE)) + { + char buf[BUFSIZE] = "+"; + + for (acptr = &me; acptr; acptr = acptr->prev) + { + /* acptr->from == acptr for acptr == cptr */ + if (acptr->from == cptr) + continue; + if (IsPerson(acptr)) + { + if (match(sptr->service->dist, + acptr->user->server)) + continue; + if (burst & SERVICE_WANT_UMODE) + send_umode(NULL, acptr, 0, SEND_UMODES, + buf); + else if (burst & SERVICE_WANT_OPER) + send_umode(NULL, acptr, 0, FLAGS_OPER, + buf); + sendnum_toone(sptr, burst, acptr, buf); + } + else if (IsService(acptr)) + { + if (!(burst & SERVICE_WANT_SERVICE)) + continue; + if (match(sptr->service->dist, + acptr->service->server)) + continue; + sendto_one(sptr, "SERVICE %s %s %s %d %d :%s", + acptr->name, acptr->service->server, + acptr->service->dist, + acptr->service->type, + acptr->hopcount + 1, acptr->info); + } + } + } + + if (burst & (SERVICE_WANT_CHANNEL|SERVICE_WANT_VCHANNEL|SERVICE_WANT_MODE|SERVICE_WANT_TOPIC)) + { + char modebuf[MODEBUFLEN], parabuf[MODEBUFLEN]; + aChannel *chptr; + + for (chptr = channel; chptr; chptr = chptr->nextch) + { + if (chptr->users == 0) + continue; + if (burst&(SERVICE_WANT_CHANNEL|SERVICE_WANT_VCHANNEL)) + sendto_one(sptr, "CHANNEL %s %d", + chptr->chname, chptr->users); + if (burst & SERVICE_WANT_MODE) + { + *modebuf = *parabuf = '\0'; + modebuf[1] = '\0'; + channel_modes(&me, modebuf, parabuf, chptr); + sendto_one(sptr, "MODE %s %s", chptr->chname, + modebuf); + } + if ((burst & SERVICE_WANT_TOPIC) && *chptr->topic) + sendto_one(sptr, "TOPIC %s :%s", + chptr->chname, chptr->topic); + } + } + /* cptr->flags ^= FLAGS_CBURST; */ + return 0; +} +#endif + + +/* +** Send query to service. +** parv[1] - string to match name against +** parv[2] - string to send to service +*/ +int m_squery(cptr, sptr, parc, parv) +aClient *cptr, *sptr; +int parc; +char *parv[]; +{ + aClient *acptr; + + if (parc <= 2) + { + if (parc == 1) + sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]), + "SQUERY"); + else if (parc == 2 || BadPtr(parv[1])) + sendto_one(sptr, err_str(ERR_NOTEXTTOSEND, parv[0])); + return 1; + } + + if ((acptr = best_service(parv[1], NULL))) + if (MyConnect(acptr) && + (acptr->service->wants & SERVICE_WANT_PREFIX)) + sendto_one(acptr, ":%s!%s@%s SQUERY %s :%s", parv[0], + sptr->user->username, sptr->user->host, + acptr->name, parv[2]); + else + sendto_one(acptr, ":%s SQUERY %s :%s", + parv[0], acptr->name, parv[2]); + else + sendto_one(sptr, err_str(ERR_NOSUCHSERVICE, parv[0]), parv[1]); + return 2; +} |