/************************************************************************
* 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.9 2003/11/19 09:22:13 gvs 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(serv);
cptr->service = NULL;
}
}
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) &&
!strncasecmp(name, bcptr->name, len))
{
if (!acptr || bcptr->hopcount < acptr->hopcount)
{
acptr = bcptr;
}
}
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 aService *sp;
*nbuf = '\0';
for (sp = svctop; sp; sp = sp->nexts)
{
if (!MyConnect(sp->bcptr) ||
(cptr && sp->bcptr == 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 ((sp->wants & action)
&& (!server || !match(sp->dist, server)))
if ((sp->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,
#ifdef RUSNET_IRCD
cptr->sockhost
#else
cptr->user->host
#endif
);
#if ! USE_STDARG
sendto_one(sp->bcptr, fmt, nbuf, p2, p3, p4, p5,
p6, p7, p8);
#else
sendto_one(sp->bcptr, ":%s%s", nbuf, buf);
#endif
}
else
{
#if ! USE_STDARG
sendto_one(sp->bcptr, fmt, p1, p2, p3, p4, p5,
p6, p7, p8);
#else
va_list va;
va_start(va, fmt);
vsendto_one(sp->bcptr, 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
: ".",
#ifdef RUSNET_IRCD
(wants & SERVICE_WANT_USER) ? sptr->sockhost :".",
#else
(wants & SERVICE_WANT_USER) ? sptr->user->host :".",
#endif
(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,
#ifdef RUSNET_IRCD
sptr->sockhost
#else
sptr->user->host
#endif
);
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,
#ifdef RUSNET_IRCD
sptr->sockhost,
#else
sptr->user->host,
#endif
(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 aService *sp;
for (sp = svctop; sp; sp = sp->nexts)
{
if (!MyConnect(sp->bcptr))
continue;
/*
** found a (local) service, check if action matches what's
** wanted AND if it comes from a server matching the dist
*/
if ((sp->wants & SERVICE_MASK_NUM)
&& !match(sp->dist, sptr->user->server))
sendnum_toone(sp->bcptr, sp->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) ||
strcasecmp(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))
{
int l = do_nick_name(parv[1], 0);
metric = 0;
server = ME;
sp = me.serv;
#if defined(RUSNET_IRCD) && !defined(NO_DIRECT_VHOST)
/*
** wrap around lose check in do_nick_name -erra
** there is something else you lose here :) -skold
*/
if (l && parv[1][l - 1] == '!')
{
l--;
parv[1][l] = 0;
}
#endif
if (!l)
{
sendto_one(sptr, err_str(ERR_ERRONEOUSNICKNAME,
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++;
#ifdef EXTRA_STATISTICS
/*
** Keep track of maximum number of simultanious
** services to this server.
*/
if (istat.is_myservice > istat.is_m_myservice)
istat.is_m_myservice = istat.is_myservice;
#endif
}
#endif
istat.is_service++;
#ifdef EXTRA_STATISTICS
/*
** Keep track of maximum number of services
** simultaniously connected to this network.
*/
if (istat.is_service > istat.is_m_service)
istat.is_m_service = istat.is_service;
#endif
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) &&
strcasecmp(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,
#ifdef RUSNET_IRCD
sptr->sockhost,
#else
sptr->user->host,
#endif
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;
}
syntax highlighted by Code2HTML, v. 0.9.1