/************************************************************************
* IRC - Internet Relay Chat, ircd/s_conf.c
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Computing Center
*
* 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.
*/
/* -- avalon -- 20 Feb 1992
* Reversed the order of the params for attach_conf().
* detach_conf() and attach_conf() are now the same:
* function_conf(aClient *, aConfItem *)
*/
/* -- Jto -- 20 Jun 1990
* Added gruner's overnight fix..
*/
/* -- Jto -- 16 Jun 1990
* Moved matches to ../common/match.c
*/
/* -- Jto -- 03 Jun 1990
* Added Kill fixes from gruner@lan.informatik.tu-muenchen.de
* Added jarlek's msgbase fix (I still don't understand it... -- Jto)
*/
/* -- Jto -- 13 May 1990
* Added fixes from msa:
* Comments and return value to init_conf()
*/
/*
* -- Jto -- 12 May 1990
* Added close() into configuration file (was forgotten...)
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: s_conf.c,v 1.13 2004/09/08 17:51:19 skold Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define S_CONF_C
#include "s_externs.h"
#undef S_CONF_C
#ifdef RUSNET_IRCD
extern char *rusnetfile;
#endif
static int check_time_interval __P((char *, char *));
static int lookup_confhost __P((aConfItem *));
aConfItem *conf = NULL;
aConfItem *kconf = NULL;
#ifdef RUSNET_IRCD
aConfItem *econf = NULL;
#endif
/*
* remove all conf entries from the client except those which match
* the status field mask.
*/
void det_confs_butmask(cptr, mask)
aClient *cptr;
int mask;
{
Reg Link *tmp, *tmp2;
for (tmp = cptr->confs; tmp; tmp = tmp2)
{
tmp2 = tmp->next;
if ((tmp->value.aconf->status & mask) == 0)
(void)detach_conf(cptr, tmp->value.aconf);
}
}
/*
* Match address by #IP bitmask (10.11.12.128/27)
* Now should work for IPv6 too.
* returns -1 on error, 0 on match, 1 when NO match.
*/
int match_ipmask(mask, cptr)
char *mask;
aClient *cptr;
{
int m;
char *p;
struct IN_ADDR addr;
char dummy[128];
u_long lmask;
#ifdef INET6
int j;
#endif
strncpyzt(dummy, mask, sizeof(dummy));
mask = dummy;
if ((p = index(mask, '@')))
{
*p = '\0';
if (match(mask, cptr->username))
return 1;
mask = p + 1;
}
if (!(p = index(mask, '/')))
goto badmask;
*p = '\0';
if (sscanf(p + 1, "%d", &m) != 1)
{
goto badmask;
}
#ifndef INET6
if (m < 0 || m > 32)
goto badmask;
if (!m)
return 0; /* x.x.x.x/0 always matches */
lmask = htonl((u_long)0xffffffffL << (32 - m));
addr.s_addr = inetaddr(mask);
return ((addr.s_addr ^ cptr->ip.s_addr) & lmask) ? 1 : 0;
#else
if (m < 0 || m > 128)
goto badmask;
if (inetpton(AF_INET6, mask, (void *)addr.s6_addr) != 1)
{
return -1;
}
/* Make sure that the ipv4 notation still works. */
if (IN6_IS_ADDR_V4MAPPED(&addr) && m < 96)
{
m += 96;
}
j = m & 0x1F; /* number not mutliple of 32 bits */
m >>= 5; /* number of 32 bits */
if (m && memcmp((void *)(addr.s6_addr),
(void *)(cptr->ip.s6_addr), m << 2))
return 1;
if (j)
{
lmask = htonl((u_long)0xffffffffL << (32 - j));
if ((((u_int32_t *)(addr.s6_addr))[m] ^
((u_int32_t *)(cptr->ip.s6_addr))[m]) & lmask)
return 1;
}
return 0;
#endif
badmask:
sendto_flag(SCH_ERROR, "Ignoring bad mask: %s", mask);
return -1;
}
/*
* find the first (best) I line to attach.
*/
int attach_Iline(cptr, hp, sockhost)
aClient *cptr;
Reg struct hostent *hp;
char *sockhost;
{
Reg aConfItem *aconf;
Reg char *hname;
Reg int i;
static char uhost[HOSTLEN+USERLEN+3];
static char fullname[HOSTLEN+1];
for (aconf = conf; aconf; aconf = aconf->next)
{
if ((aconf->status != CONF_CLIENT) &&
(aconf->status != CONF_RCLIENT))
continue;
if (aconf->port && aconf->port != cptr->acpt->port)
continue;
if (!aconf->host || !aconf->name)
goto attach_iline;
if (hp)
for (i = 0, hname = hp->h_name; hname;
hname = hp->h_aliases[i++])
{
strncpyzt(fullname, hname,
sizeof(fullname));
add_local_domain(fullname,
HOSTLEN - strlen(fullname));
Debug((DEBUG_DNS, "a_il: %s->%s",
sockhost, fullname));
if (index(aconf->name, '@'))
{
(void)strcpy(uhost, cptr->username);
(void)strcat(uhost, "@");
}
else
*uhost = '\0';
(void)strncat(uhost, fullname,
sizeof(uhost) - strlen(uhost));
if (!match(aconf->name, uhost))
goto attach_iline;
}
if (index(aconf->host, '@'))
{
strncpyzt(uhost, cptr->username, sizeof(uhost));
(void)strcat(uhost, "@");
}
else
*uhost = '\0';
(void)strncat(uhost, sockhost, sizeof(uhost) - strlen(uhost));
if (strchr(aconf->host, '/')) /* 1.2.3.0/24 */
{
if (match_ipmask(aconf->host, cptr))
continue;
} else if (match(aconf->host, uhost)) /* 1.2.3.* */
continue;
if (*aconf->name == '\0' && hp)
{
strncpyzt(uhost, hp->h_name, sizeof(uhost));
add_local_domain(uhost, sizeof(uhost) - strlen(uhost));
}
attach_iline:
if (aconf->status & CONF_RCLIENT)
SetRestricted(cptr);
#ifdef RUSNET_IRCD
cptr->flood = aconf->localpref;
#endif
get_sockhost(cptr, uhost);
if ((i = attach_conf(cptr, aconf)) < -1)
find_bounce(cptr, ConfClass(aconf), -1);
return i;
}
find_bounce(cptr, 0, -2);
return -2; /* used in register_user() */
}
/*
* Find the single N line and return pointer to it (from list).
* If more than one then return NULL pointer.
*/
aConfItem *count_cnlines(lp)
Reg Link *lp;
{
Reg aConfItem *aconf, *cline = NULL, *nline = NULL;
for (; lp; lp = lp->next)
{
aconf = lp->value.aconf;
if (!(aconf->status & CONF_SERVER_MASK))
continue;
if ((aconf->status == CONF_CONNECT_SERVER ||
aconf->status == CONF_ZCONNECT_SERVER) && !cline)
cline = aconf;
else if (aconf->status == CONF_NOCONNECT_SERVER && !nline)
nline = aconf;
}
return nline;
}
/*
** detach_conf
** Disassociate configuration from the client.
** Also removes a class from the list if marked for deleting.
*/
int detach_conf(cptr, aconf)
aClient *cptr;
aConfItem *aconf;
{
Reg Link **lp, *tmp;
aConfItem **aconf2,*aconf3;
lp = &(cptr->confs);
while (*lp)
{
if ((*lp)->value.aconf == aconf)
{
if ((aconf) && (Class(aconf)))
{
if (aconf->status & CONF_CLIENT_MASK)
if (ConfLinks(aconf) > 0)
--ConfLinks(aconf);
if (ConfMaxLinks(aconf) == -1 &&
ConfLinks(aconf) == 0)
{
free_class(Class(aconf));
Class(aconf) = NULL;
}
}
if (aconf && !--aconf->clients && IsIllegal(aconf))
{
/* Remove the conf entry from the Conf linked list */
for (aconf2 = &conf; (aconf3 = *aconf2); )
{
if (aconf3 == aconf)
{
*aconf2 = aconf3->next;
aconf3->next = NULL;
free_conf(aconf);
}
else
{
aconf2 = &aconf3->next;
}
}
}
tmp = *lp;
*lp = tmp->next;
free_link(tmp);
istat.is_conflink--;
return 0;
}
else
lp = &((*lp)->next);
}
return -1;
}
static int is_attached(aconf, cptr)
aConfItem *aconf;
aClient *cptr;
{
Reg Link *lp;
for (lp = cptr->confs; lp; lp = lp->next)
if (lp->value.aconf == aconf)
break;
return (lp) ? 1 : 0;
}
/*
** attach_conf
** Associate a specific configuration entry to a *local*
** client (this is the one which used in accepting the
** connection). Note, that this automaticly changes the
** attachment if there was an old one...
*/
int attach_conf(cptr, aconf)
aConfItem *aconf;
aClient *cptr;
{
Reg Link *lp;
if (is_attached(aconf, cptr))
return 1;
if (IsIllegal(aconf))
return -1;
if ((aconf->status & (CONF_LOCOP | CONF_OPERATOR | CONF_CLIENT |
CONF_RCLIENT)))
{
if (aconf->clients >= ConfMaxLinks(aconf) &&
ConfMaxLinks(aconf) > 0)
return -3; /* Use this for printing error message */
}
if ((aconf->status & (CONF_CLIENT | CONF_RCLIENT)))
{
int hcnt = 0, ucnt = 0;
/* check on local/global limits per host and per user@host */
/*
** local limits first to save CPU if any is hit.
** host check is done on the IP address.
** user check is done on the IDENT reply.
*/
if (ConfMaxHLocal(aconf) > 0 || ConfMaxUHLocal(aconf) > 0) {
Reg aClient *acptr;
Reg int i;
for (i = highest_fd; i >= 0; i--)
if ((acptr = local[i]) && (cptr != acptr) &&
!IsListening(acptr) &&
!bcmp((char *)&cptr->ip,(char *)&acptr->ip,
sizeof(cptr->ip)))
{
hcnt++;
if (!strncasecmp(acptr->auth,
cptr->auth, USERLEN))
ucnt++;
}
if (ConfMaxHLocal(aconf) > 0 &&
hcnt >= ConfMaxHLocal(aconf))
return -4; /* for error message */
if (ConfMaxUHLocal(aconf) > 0 &&
ucnt >= ConfMaxUHLocal(aconf))
return -5; /* for error message */
}
/*
** Global limits
** host check is done on the hostname (IP if unresolved)
** user check is done on username
*/
if (ConfMaxHGlobal(aconf) > 0 || ConfMaxUHGlobal(aconf) > 0)
{
Reg aClient *acptr;
Reg int ghcnt = hcnt, gucnt = ucnt;
for (acptr = client; acptr; acptr = acptr->next)
{
if (!IsPerson(acptr))
continue;
if (MyConnect(acptr) &&
(ConfMaxHLocal(aconf) > 0 ||
ConfMaxUHLocal(aconf) > 0))
continue;
#ifdef RUSNET_IRCD
if (!strcmp(cptr->sockhost, acptr->sockhost))
#else
if (!strcmp(cptr->sockhost, acptr->user->host))
#endif
{
if (ConfMaxHGlobal(aconf) > 0 &&
++ghcnt >= ConfMaxHGlobal(aconf))
return -6;
if (ConfMaxUHGlobal(aconf) > 0 &&
!strcmp(cptr->user->username,
acptr->user->username) &&
(++gucnt >=ConfMaxUHGlobal(aconf)))
return -7;
}
}
}
}
lp = make_link();
istat.is_conflink++;
lp->next = cptr->confs;
lp->value.aconf = aconf;
cptr->confs = lp;
aconf->clients++;
if (aconf->status & CONF_CLIENT_MASK)
ConfLinks(aconf)++;
return 0;
}
aConfItem *find_admin()
{
Reg aConfItem *aconf;
for (aconf = conf; aconf; aconf = aconf->next)
if (aconf->status & CONF_ADMIN)
break;
return (aconf);
}
aConfItem *find_me()
{
Reg aConfItem *aconf;
for (aconf = conf; aconf; aconf = aconf->next)
if (aconf->status & CONF_ME)
break;
return (aconf);
}
/*
* attach_confs
* Attach a CONF line to a client if the name passed matches that for
* the conf file (for non-C/N lines) or is an exact match (C/N lines
* only). The difference in behaviour is to stop C:*::* and N:*::*.
*/
aConfItem *attach_confs(cptr, name, statmask)
aClient *cptr;
char *name;
int statmask;
{
Reg aConfItem *tmp;
aConfItem *first = NULL;
int len = strlen(name);
if (!name || len > HOSTLEN)
return NULL;
for (tmp = conf; tmp; tmp = tmp->next)
{
if ((tmp->status & statmask) && !IsIllegal(tmp) &&
((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0) &&
tmp->name && !match(tmp->name, name))
{
if (!attach_conf(cptr, tmp) && !first)
first = tmp;
}
else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
(tmp->status & (CONF_SERVER_MASK|CONF_HUB)) &&
tmp->name && !strcasecmp(tmp->name, name))
{
if (!attach_conf(cptr, tmp) && !first)
first = tmp;
}
}
return (first);
}
/*
* Added for new access check meLazy
*/
aConfItem *attach_confs_host(cptr, host, statmask)
aClient *cptr;
char *host;
int statmask;
{
Reg aConfItem *tmp;
aConfItem *first = NULL;
int len = strlen(host);
if (!host || len > HOSTLEN)
return NULL;
for (tmp = conf; tmp; tmp = tmp->next)
{
if ((tmp->status & statmask) && !IsIllegal(tmp) &&
(tmp->status & CONF_SERVER_MASK) == 0 &&
(!tmp->host || match(tmp->host, host) == 0))
{
if (!attach_conf(cptr, tmp) && !first)
first = tmp;
}
else if ((tmp->status & statmask) && !IsIllegal(tmp) &&
(tmp->status & CONF_SERVER_MASK) &&
(tmp->host && strcasecmp(tmp->host, host) == 0))
{
if (!attach_conf(cptr, tmp) && !first)
first = tmp;
}
}
return (first);
}
/*
* find a conf entry which matches the hostname and has the same name.
*/
aConfItem *find_conf_exact(name, user, host, statmask)
char *name, *host, *user;
int statmask;
{
Reg aConfItem *tmp;
char userhost[USERLEN+HOSTLEN+3];
SPRINTF(userhost, "%s@%s", user, host);
for (tmp = conf; tmp; tmp = tmp->next)
{
if (!(tmp->status & statmask) || !tmp->name || !tmp->host ||
strcasecmp(tmp->name, name))
continue;
/*
** Accept if the *real* hostname (usually sockecthost)
** socket host) matches *either* host or name field
** of the configuration.
*/
if (match(tmp->host, userhost))
continue;
if (tmp->status & (CONF_OPERATOR|CONF_LOCOP))
{
if (tmp->clients < MaxLinks(Class(tmp)))
return tmp;
else
continue;
}
else
return tmp;
}
return NULL;
}
/*
* find an O-line which matches the hostname and has the same "name".
*/
aConfItem *find_Oline(name, cptr)
char *name;
aClient *cptr;
{
Reg aConfItem *tmp;
char userhost[USERLEN+HOSTLEN+3];
char userip[USERLEN+HOSTLEN+3];
SPRINTF(userhost, "%s@%s", cptr->username, cptr->sockhost);
SPRINTF(userip, "%s@%s", cptr->username,
#ifdef INET6
(char *)inetntop(AF_INET6, (char *)&cptr->ip, mydummy,
MYDUMMY_SIZE)
#else
(char *)inetntoa((char *)&cptr->ip)
#endif
);
for (tmp = conf; tmp; tmp = tmp->next)
{
if (!(tmp->status & (CONF_OPS)) || !tmp->name || !tmp->host ||
strcasecmp(tmp->name, name))
continue;
/*
** Accept if the *real* hostname matches the host field or
** the ip does.
*/
if (match(tmp->host, userhost) && match(tmp->host, userip) &&
(!strchr(tmp->host, '/')
|| match_ipmask(tmp->host, cptr)))
continue;
if (tmp->clients < MaxLinks(Class(tmp)))
return tmp;
}
return NULL;
}
aConfItem *find_conf_name(name, statmask)
char *name;
int statmask;
{
Reg aConfItem *tmp;
for (tmp = conf; tmp; tmp = tmp->next)
{
/*
** Accept if the *real* hostname (usually sockecthost)
** matches *either* host or name field of the configuration.
*/
if ((tmp->status & statmask) &&
(!tmp->name || match(tmp->name, name) == 0))
return tmp;
}
return NULL;
}
aConfItem *find_conf(lp, name, statmask)
char *name;
Link *lp;
int statmask;
{
Reg aConfItem *tmp;
int namelen = name ? strlen(name) : 0;
if (namelen > HOSTLEN)
return (aConfItem *) 0;
for (; lp; lp = lp->next)
{
tmp = lp->value.aconf;
if ((tmp->status & statmask) &&
(((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) &&
tmp->name && !strcasecmp(tmp->name, name)) ||
((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0 &&
tmp->name && !match(tmp->name, name))))
return tmp;
}
return NULL;
}
/*
* Added for new access check meLazy
*/
aConfItem *find_conf_host(lp, host, statmask)
Reg Link *lp;
char *host;
Reg int statmask;
{
Reg aConfItem *tmp;
int hostlen = host ? strlen(host) : 0;
if (hostlen > HOSTLEN || BadPtr(host))
return (aConfItem *)NULL;
for (; lp; lp = lp->next)
{
tmp = lp->value.aconf;
if (tmp->status & statmask &&
(!(tmp->status & CONF_SERVER_MASK || tmp->host) ||
(tmp->host && !match(tmp->host, host))))
return tmp;
}
return NULL;
}
/*
* find_conf_ip
*
* Find a conf line using the IP# stored in it to search upon.
* Added 1/8/92 by Avalon.
*/
aConfItem *find_conf_ip(lp, ip, user, statmask)
char *ip, *user;
Link *lp;
int statmask;
{
Reg aConfItem *tmp;
Reg char *s;
for (; lp; lp = lp->next)
{
tmp = lp->value.aconf;
if (!(tmp->status & statmask))
continue;
s = index(tmp->host, '@');
*s = '\0';
if (match(tmp->host, user))
{
*s = '@';
continue;
}
*s = '@';
if (!bcmp((char *)&tmp->ipnum, ip, sizeof(struct IN_ADDR)))
return tmp;
}
return NULL;
}
/*
* find_conf_entry
*
* - looks for a match on all given fields.
*/
aConfItem *find_conf_entry(aconf, mask)
aConfItem *aconf;
u_int mask;
{
Reg aConfItem *bconf;
for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next)
{
if (!(bconf->status & mask) || (bconf->port != aconf->port))
continue;
if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) ||
(BadPtr(aconf->host) && !BadPtr(bconf->host)))
continue;
if (!BadPtr(bconf->host) && strcasecmp(bconf->host, aconf->host))
continue;
if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) ||
(BadPtr(aconf->passwd) && !BadPtr(bconf->passwd)))
continue;
if (!BadPtr(bconf->passwd) &&
strcasecmp(bconf->passwd, aconf->passwd))
continue;
if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) ||
(BadPtr(aconf->name) && !BadPtr(bconf->name)))
continue;
if (!BadPtr(bconf->name) && strcasecmp(bconf->name, aconf->name))
continue;
break;
}
return bconf;
}
/*
* rehash
*
* Actual REHASH service routine. Called with sig == 0 if it has been called
* as a result of an operator issuing this command, else assume it has been
* called as a result of the server receiving a HUP signal.
*/
int rehash(cptr, sptr, sig)
aClient *cptr, *sptr;
int sig;
{
Reg aConfItem **tmp = &conf, *tmp2 = NULL;
Reg aClass *cltmp;
Reg aClient *acptr;
Reg int i;
int ret = 0;
if (sig == 1)
{
sendto_flag(SCH_NOTICE,
"Got signal SIGHUP, reloading ircd.conf file");
logfiles_close();
logfiles_open();
#ifdef ULTRIX
if (fork() > 0)
exit(0);
write_pidfile();
#endif
}
for (i = 0; i <= highest_fd; i++)
if ((acptr = local[i]) && !IsMe(acptr))
{
/*
* Nullify any references from client structures to
* this host structure which is about to be freed.
* Could always keep reference counts instead of
* this....-avalon
*/
acptr->hostp = NULL;
#if defined(R_LINES) && ( defined(R_LINES_REHASH) && !defined(R_LINES_OFTEN) )
if (find_restrict(acptr))
{
sendto_flag(SCH_NOTICE,
"Restricting %s, closing lp",
get_client_name(acptr,FALSE));
acptr->exitc = EXITC_RLINE;
if (exit_client(cptr,acptr,&me,"R-lined") ==
FLUSH_BUFFER)
ret = FLUSH_BUFFER;
}
#endif
}
while ((tmp2 = *tmp))
if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT)
{
/*
** Configuration entry is still in use by some
** local clients, cannot delete it--mark it so
** that it will be deleted when the last client
** exits...
*/
if (!(tmp2->status & (CONF_LISTEN_PORT|CONF_CLIENT)))
{
*tmp = tmp2->next;
tmp2->next = NULL;
}
else
tmp = &tmp2->next;
tmp2->status |= CONF_ILLEGAL;
}
else
{
*tmp = tmp2->next;
free_conf(tmp2);
}
tmp = &kconf;
while ((tmp2 = *tmp))
{
*tmp = tmp2->next;
free_conf(tmp2);
}
#ifdef RUSNET_IRCD
tmp = &econf;
while ((tmp2 = *tmp))
{
*tmp = tmp2->next;
free_conf(tmp2);
}
#endif
/*
* We don't delete the class table, rather mark all entries
* for deletion. The table is cleaned up by check_class. - avalon
*/
for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp))
MaxLinks(cltmp) = -1;
if (sig == 2)
flush_cache();
#ifdef RUSNET_IRCD
rusnet_free_routes();
#endif
(void) initconf(0, configfile);
close_listeners();
/*
* flush out deleted I and P lines although still in use.
*/
for (tmp = &conf; (tmp2 = *tmp); )
if (!(tmp2->status & CONF_ILLEGAL))
tmp = &tmp2->next;
else
{
*tmp = tmp2->next;
tmp2->next = NULL;
if (!tmp2->clients)
free_conf(tmp2);
}
#ifdef CACHED_MOTD
read_motd(IRCDMOTD_PATH);
#endif
rehashed = 1;
return ret;
}
/*
* openconf
*
* returns -1 on any error or else the fd opened from which to read the
* configuration file from. This may either be the file direct or one end
* of a pipe from m4.
*/
int openconf(conf_file)
char *conf_file;
{
#ifdef M4_PREPROC
int pi[2], i;
if (pipe(pi) == -1)
return -1;
switch(vfork())
{
case -1 :
return -1;
case 0 :
(void)close(pi[0]);
if (pi[1] != 1)
{
(void)dup2(pi[1], 1);
(void)close(pi[1]);
}
(void)dup2(1,2);
for (i = 3; i < MAXCONNECTIONS; i++)
if (local[i])
(void) close(i);
/*
* m4 maybe anywhere, use execvp to find it. Any error
* goes out with report_error. Could be dangerous,
* two servers running with the same fd's >:-) -avalon
*/
(void)execlp("m4", "m4", IRCDM4_PATH, conf, 0);
report_error("Error executing m4 %s:%s", &me);
_exit(-1);
default :
(void)close(pi[1]);
return pi[0];
}
#else
return open(conf_file, O_RDONLY);
#endif
}
/*
** char *ipv6_convert(char *orig)
** converts the original ip address to an standard form
** returns a pointer to a string.
*/
#ifdef INET6
char *ipv6_convert(orig)
char *orig;
{
char *s, *t, *buf = NULL;
int i, j;
int len = 1; /* for the '\0' in case of no @ */
struct in6_addr addr;
char dummy[MYDUMMY_SIZE];
if ((s = strchr(orig, '@')))
{
*s = '\0';
len = strlen(orig) + 2; /* +2 for '@' and '\0' */
buf = (char *)MyMalloc(len);
(void *)strcpy(buf, orig);
buf[len - 2] = '@';
buf[len - 1] = '\0';
*s = '@';
orig = s + 1;
}
if ((s = strchr(orig, '/')))
{
*s = '\0';
s++;
}
i = inetpton(AF_INET6, orig, addr.s6_addr);
if (i > 0)
{
t = inetntop(AF_INET6, addr.s6_addr, dummy, MYDUMMY_SIZE);
}
j = len - 1;
if (!((i > 0) && t))
t = orig;
len += strlen(t);
buf = (char *)MyRealloc(buf, len);
strcpy(buf + j, t);
if (s)
{
*(s-1) = '/'; /* put the '/' back, not sure it's needed tho */
j = len;
len += strlen(s) + 1;
buf = (char *)MyRealloc(buf, len);
buf[j - 1] = '/';
strcpy(buf + j, s);
}
return buf;
}
#endif
/*
** initconf()
** Read configuration file.
**
** returns -1, if file cannot be opened
** 0, if file opened
*/
#define MAXCONFLINKS 150
int initconf(opt, conf_file)
int opt;
char *conf_file;
{
static char quotes[9][2] = {{'b', '\b'}, {'f', '\f'}, {'n', '\n'},
{'r', '\r'}, {'t', '\t'}, {'v', '\v'},
{'\\', '\\'}, { 0, 0}};
Reg char *tmp, *s;
int fd, i;
char line[4096], *head, *tail;
char *tmp2 = NULL, *tmp3 = NULL, *tmp4 = NULL;
int ccount = 0, ncount = 0;
aConfItem *aconf = NULL;
Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", conf_file));
if ((fd = openconf(conf_file)) == -1)
{
#if defined(M4_PREPROC) && !defined(USE_IAUTH)
(void)wait(0);
#endif
return -1;
}
(void)dgets(-1, line, 0, &head, &tail); /* initialize line */
while ((i = dgets(fd, line, sizeof(line) - 1, &head, &tail)) > 0)
{
if ((tmp = (char *)index(line, '\n')))
*tmp = 0;
#ifdef RUSNET_IRCD
if (*line == '#') /* handle inclusions */
{
if (!strncasecmp(line + 1, "include", 7))
{
s = strtok(line + 8, " \t");
if (s)
{
strtok(NULL, " \t#"); /* rest of the line */
if (*s == '/') /* absolute path */
initconf(0, s);
else
{
char *path = MyMalloc( strlen(IRCDCONF_DIR) +
strlen(s) + 3 );
if (!path)
{
Debug((DEBUG_DEBUG, "initconf(): "
"out of memory"));
return -1;
}
strcpy(path, IRCDCONF_DIR "/");
strcat(path, s);
initconf(0, path);
MyFree( path );
}
}
else
Debug((DEBUG_DEBUG, "initconf(): bad include: %s",
line));
}
*line = '\0'; /* nullify it */
continue;
}
#endif
/*
* Do quoting of characters and # detection.
*/
for (tmp = line; *tmp; tmp++)
{
if (*tmp == '\\')
{
for (i = 0; quotes[i][0]; i++)
if (quotes[i][0] == *(tmp + 1))
{
*tmp = quotes[i][1];
break;
}
if (!quotes[i][0])
*tmp = *(tmp + 1);
if (!*(tmp+1))
break;
else
for (s = tmp; (*s = *(s + 1)); s++)
;
}
else if (*tmp == '#')
{
*tmp = '\0';
break; /* Ignore the rest of the line */
}
}
if (!*line || line[0] == '#' || line[0] == '\n' ||
line[0] == ' ' || line[0] == '\t')
continue;
/* Could we test if it's conf line at all? -Vesa */
if (line[1] != IRCDCONF_DELIMITER)
{
Debug((DEBUG_ERROR, "Bad config line: %s", line));
continue;
}
if (aconf)
free_conf(aconf);
aconf = make_conf();
if (tmp2)
{
MyFree(tmp2);
tmp2 = NULL;
}
tmp3 = tmp4 = NULL;
tmp = getfield(line);
if (!tmp)
continue;
switch (*tmp)
{
case 'A': /* Name, e-mail address of administrator */
case 'a': /* of this server. */
aconf->status = CONF_ADMIN;
break;
case 'B': /* Name of alternate servers */
case 'b':
aconf->status = CONF_BOUNCE;
break;
case 'C': /* Server where I should try to connect */
/* in case of lp failures */
ccount++;
aconf->status = CONF_CONNECT_SERVER;
break;
case 'c':
ccount++;
aconf->status = CONF_ZCONNECT_SERVER;
break;
case 'D': /* auto connect restrictions */
case 'd':
aconf->status = CONF_DENY;
break;
# ifdef RUSNET_IRCD
case 'E': /* Exempt from kill user line */
case 'e':
aconf->status = CONF_EXEMPT;
break;
case 'F': /* virtual interface to fasten to when */
case 'f': /* connecting to the server mentioned */
aconf->status = CONF_INTERFACE;
break;
#endif
case 'H': /* Hub server line */
case 'h':
aconf->status = CONF_HUB;
break;
case 'I': /* Just plain normal irc client trying */
/* to connect me */
aconf->status = CONF_CLIENT;
break;
case 'i' : /* Restricted client */
aconf->status = CONF_RCLIENT;
break;
case 'K': /* Kill user line on irc.conf */
aconf->status = CONF_KILL;
break;
case 'k':
aconf->status = CONF_OTHERKILL;
break;
/* Operator. Line should contain at least */
/* password and host where connection is */
case 'L': /* guaranteed leaf server */
case 'l':
aconf->status = CONF_LEAF;
break;
/* Me. Host field is name used for this host */
/* and port number is the number of the port */
case 'M':
case 'm':
aconf->status = CONF_ME;
break;
case 'N': /* Server where I should NOT try to */
case 'n': /* connect in case of lp failures */
/* but which tries to connect ME */
++ncount;
aconf->status = CONF_NOCONNECT_SERVER;
break;
case 'O':
aconf->status = CONF_OPERATOR;
break;
/* Local Operator, (limited privs --SRB) */
case 'o':
aconf->status = CONF_LOCOP;
break;
case 'P': /* listen port line */
case 'p':
aconf->status = CONF_LISTEN_PORT;
break;
case 'Q': /* a server that you don't want in your */
case 'q': /* network. USE WITH CAUTION! */
aconf->status = CONF_QUARANTINED_SERVER;
break;
#ifdef R_LINES
case 'R': /* extended K line */
case 'r': /* Offers more options of how to restrict */
aconf->status = CONF_RESTRICT;
break;
#endif
case 'S': /* Service. Same semantics as */
case 's': /* CONF_OPERATOR */
aconf->status = CONF_SERVICE;
break;
#if 0
case 'U': /* Uphost, ie. host where client reading */
case 'u': /* this should connect. */
/* This is for client only, I must ignore this */
/* ...U-line should be removed... --msa */
break;
#endif
case 'V': /* Server link version requirements */
aconf->status = CONF_VER;
break;
case 'Y':
case 'y':
aconf->status = CONF_CLASS;
break;
default:
Debug((DEBUG_ERROR, "Error in config file: %s", line));
break;
}
if (IsIllegal(aconf))
continue;
for (;;) /* Fake loop, that I can use break here --msa */
{
if ((tmp = getfield(NULL)) == NULL)
break;
#ifdef INET6
if (aconf->status &
(CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER
|CONF_CLIENT|CONF_RCLIENT|CONF_KILL
|CONF_OTHERKILL|CONF_NOCONNECT_SERVER
|CONF_OPERATOR|CONF_LOCOP|CONF_LISTEN_PORT
# ifdef R_LINES
|CONF_RESTRICT
# endif
# ifdef RUSNET_IRCD
|CONF_EXEMPT|CONF_INTERFACE
# endif
|CONF_SERVICE))
aconf->host = ipv6_convert(tmp);
else
#endif
DupString(aconf->host, tmp);
if ((tmp = getfield(NULL)) == NULL)
break;
DupString(aconf->passwd, tmp);
if ((tmp = getfield(NULL)) == NULL)
break;
#if defined( RUSNET_IRCD ) && defined( INET6 )
if (aconf->status & CONF_INTERFACE)
aconf->name = ipv6_convert(tmp);
else
#endif
DupString(aconf->name, tmp);
if ((tmp = getfield(NULL)) == NULL)
break;
aconf->port = 0;
if (sscanf(tmp, "0x%x", &aconf->port) != 1 ||
aconf->port == 0)
aconf->port = atoi(tmp);
if (aconf->status == CONF_CONNECT_SERVER)
DupString(tmp2, tmp);
if (aconf->status == CONF_ZCONNECT_SERVER)
DupString(tmp2, tmp);
if ((tmp = getfield(NULL)) == NULL)
break;
Class(aconf) = find_class(atoi(tmp));
/* the following are only used for Y: */
if ((tmp3 = getfield(NULL)) == NULL)
break;
tmp4 = getfield(NULL);
break;
}
istat.is_confmem += aconf->host ? strlen(aconf->host)+1 : 0;
istat.is_confmem += aconf->passwd ? strlen(aconf->passwd)+1 :0;
istat.is_confmem += aconf->name ? strlen(aconf->name)+1 : 0;
/*
** Bounce line fields are mandatory
*/
#ifndef RUSNET_IRCD
if (aconf->status == CONF_BOUNCE && aconf->port == 0)
continue;
#endif
/*
** If conf line is a class definition, create a class entry
** for it and make the conf_line illegal and delete it.
*/
if (aconf->status & CONF_CLASS)
{
if (atoi(aconf->host) >= 0)
add_class(atoi(aconf->host),
atoi(aconf->passwd),
atoi(aconf->name), aconf->port,
tmp ? atoi(tmp) : 0,
tmp3 ? atoi(tmp3) : 1,
(tmp3 && index(tmp3, '.')) ?
atoi(index(tmp3, '.') + 1) : 1,
tmp4 ? atoi(tmp4) : 1,
(tmp4 && index(tmp4, '.')) ?
atoi(index(tmp4, '.') + 1) : 1);
continue;
}
/*
** associate each conf line with a class by using a pointer
** to the correct class record. -avalon
*/
if (aconf->status & (CONF_CLIENT_MASK|CONF_LISTEN_PORT))
{
if (Class(aconf) == 0)
Class(aconf) = find_class(0);
if (MaxLinks(Class(aconf)) < 0)
Class(aconf) = find_class(0);
}
if (aconf->status & (CONF_LISTEN_PORT|CONF_CLIENT|CONF_RCLIENT))
{
aConfItem *bconf;
if ((bconf = find_conf_entry(aconf, aconf->status)))
{
delist_conf(bconf);
bconf->status &= ~CONF_ILLEGAL;
if (aconf->status == CONF_CLIENT)
{
bconf->class->links -= bconf->clients;
bconf->class = aconf->class;
bconf->class->links += bconf->clients;
}
free_conf(aconf);
aconf = bconf;
}
else if (aconf->host &&
aconf->status == CONF_LISTEN_PORT)
(void)add_listener(aconf);
}
if (aconf->status & CONF_SERVICE)
aconf->port &= SERVICE_MASK_ALL;
if (aconf->status & (CONF_SERVER_MASK|CONF_SERVICE))
if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS ||
!aconf->host || index(aconf->host, '*') ||
index(aconf->host,'?') || !aconf->name)
continue;
if (aconf->status &
(CONF_SERVER_MASK|CONF_LOCOP|CONF_OPERATOR|CONF_SERVICE))
if (!index(aconf->host, '@') && *aconf->host != '/')
{
char *newhost;
int len = 3; /* *@\0 = 3 */
len += strlen(aconf->host);
newhost = (char *)MyMalloc(len);
SPRINTF(newhost, "*@%s", aconf->host);
MyFree(aconf->host);
aconf->host = newhost;
istat.is_confmem += 2;
}
if (aconf->status & CONF_SERVER_MASK)
{
if (BadPtr(aconf->passwd))
continue;
else if (!(opt & BOOT_QUICK))
(void)lookup_confhost(aconf);
}
if (aconf->status &
(CONF_CONNECT_SERVER | CONF_ZCONNECT_SERVER))
{
aconf->ping = (aCPing *)MyMalloc(sizeof(aCPing));
bzero((char *)aconf->ping, sizeof(*aconf->ping));
istat.is_confmem += sizeof(*aconf->ping);
if (tmp2)
{
char *x = index(tmp2, '.');
aconf->ping->port = (x) ? atoi(++x) :
aconf->port;
MyFree(tmp2);
tmp2 = NULL;
}
else
aconf->ping->port = aconf->port;
}
#ifdef RUSNET_IRCD
/* we want autoconnect links prioritized */
if (aconf->status & (CONF_CLIENT | CONF_RCLIENT |
CONF_CONNECT_SERVER | CONF_ZCONNECT_SERVER))
aconf->localpref = (tmp3) ? atoi(tmp3) : 0;
if (aconf->status & CONF_INTERFACE &&
aconf->host && aconf->name)
{
rusnet_add_route(aconf->host, aconf->name,
(BadPtr(aconf->passwd)) ?
"[Unnamed]" : aconf->passwd);
}
#endif
/*
** Name cannot be changed after the startup.
** (or could be allowed, but only if all links are closed
** first).
** Configuration info does not override the name and port
** if previously defined. Note, that "info"-field can be
** changed by "/rehash".
*/
if (aconf->status == CONF_ME)
{
if (me.info != DefInfo)
MyFree(me.info);
me.info = MyMalloc(REALLEN+1);
strncpyzt(me.info, aconf->name, REALLEN+1);
if (ME[0] == '\0' && aconf->host[0])
strncpyzt(ME, aconf->host,
sizeof(ME));
if (aconf->port)
setup_ping(aconf);
}
(void)collapse(aconf->host);
(void)collapse(aconf->name);
Debug((DEBUG_NOTICE,
"Read Init: (%d) (%s) (%s) (%s) (%d) (%d)",
aconf->status, aconf->host, aconf->passwd,
aconf->name, aconf->port,
aconf->class ? ConfClass(aconf) : 0));
if (aconf->status & (CONF_KILL|CONF_OTHERKILL))
{
aconf->next = kconf;
kconf = aconf;
}
#ifdef RUSNET_IRCD
else if (aconf->status & CONF_EXEMPT)
{
aconf->next = econf;
econf = aconf;
}
#endif
else
{
aconf->next = conf;
conf = aconf;
}
aconf = NULL;
}
if (aconf)
free_conf(aconf);
(void)close(fd);
#if defined(M4_PREPROC) && !defined(USE_IAUTH)
(void)wait(0);
#endif
check_class();
nextping = nextconnect = timeofday;
return 0;
}
/*
* lookup_confhost
* Do (start) DNS lookups of all hostnames in the conf line and convert
* an IP addresses in a.b.c.d number for to IP#s.
*/
static int lookup_confhost(aconf)
Reg aConfItem *aconf;
{
Reg char *s;
Reg struct hostent *hp;
Link ln;
if (BadPtr(aconf->host) || BadPtr(aconf->name))
goto badlookup;
if ((s = index(aconf->host, '@')))
s++;
else
s = aconf->host;
/*
** Do name lookup now on hostnames given and store the
** ip numbers in conf structure.
*/
if (!isalpha(*s) && !isdigit(*s))
goto badlookup;
/*
** Prepare structure in case we have to wait for a
** reply which we get later and store away.
*/
ln.value.aconf = aconf;
ln.flags = ASYNC_CONF;
#ifdef INET6
if(inetpton(AF_INET6, s, aconf->ipnum.s6_addr))
;
#else
if (isdigit(*s))
aconf->ipnum.s_addr = inetaddr(s);
#endif
else if ((hp = gethost_byname(s, &ln)))
bcopy(hp->h_addr, (char *)&(aconf->ipnum),
sizeof(struct IN_ADDR));
#ifdef INET6
else
{
bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ);
goto badlookup;
}
#else
if (aconf->ipnum.s_addr == -1)
goto badlookup;
#endif
return 0;
badlookup:
#ifdef INET6
if (AND16(aconf->ipnum.s6_addr) == 255)
#else
if (aconf->ipnum.s_addr == -1)
#endif
bzero((char *)&aconf->ipnum, sizeof(struct IN_ADDR));
Debug((DEBUG_ERROR,"Host/server name error: (%s) (%s)",
aconf->host, aconf->name));
return -1;
}
int find_kill(cptr, doall, comment
#ifdef RUSNET_IRCD
, nick
#endif
)
aClient *cptr;
int doall;
char **comment;
#ifdef RUSNET_IRCD
char *nick;
#endif
{
static char reply[256];
char *host, *ip, *name, *ident, *check;
aConfItem *tmp;
int now;
if (!cptr->user)
return 0;
host = cptr->sockhost;
#ifdef INET6
ip = (char *) inetntop(AF_INET6, (char *)&cptr->ip, mydummy,
MYDUMMY_SIZE);
#else
ip = (char *) inetntoa((char *)&cptr->ip);
#endif
if (!strcmp(host, ip))
ip = NULL; /* we don't have a name for the ip# */
name = cptr->user->username;
if (IsRestricted(cptr) && name[0] == '+')
{
/*
** since we added '+' at the begining of valid
** ident response, remove it here for kline
** comparison --Beeth
*/
name++;
}
ident = cptr->auth;
if (strlen(host) > (size_t) HOSTLEN ||
(name ? strlen(name) : 0) > (size_t) HOSTLEN)
return (0);
*reply = '\0';
#ifdef RUSNET_IRCD
for (tmp = econf; tmp; tmp = tmp->next)
{
if (!doall && (BadPtr(tmp->passwd) || !isdigit(*tmp->passwd)))
continue;
if (!(tmp->status & CONF_EXEMPT))
continue; /* should never happen with econf */
if (!tmp->host || !tmp->name)
continue;
/* host & IP matching.. */
if (!ip) /* unresolved */
{
if (strchr(tmp->host, '/'))
{
if (match_ipmask((*tmp->host == '=') ?
tmp->host+1: tmp->host, cptr))
continue;
}
else
if (match((*tmp->host == '=') ? tmp->host+1 :
tmp->host, host))
continue;
}
else if (*tmp->host == '=') /* numeric only */
continue;
else /* resolved */
if (strchr(tmp->host, '/'))
{
if (match_ipmask(tmp->host, cptr))
continue;
}
else
if (match(tmp->host, ip) &&
match(tmp->host, host))
continue;
/* user & port matching */
if ((!name || match(tmp->name, name) == 0) &&
(!tmp->port || (tmp->port == cptr->acpt->port)))
{
now = 0;
if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) &&
!(now = check_time_interval(tmp->passwd, reply)))
continue;
if (now != ERR_YOUWILLBEBANNED)
return 0; /* exempted */
#if 0 /* should send some notice, but leave it for later --erra */
if (now == ERR_YOUWILLBEBANNED)
tmp = NULL;
break;
#endif
}
}
#endif
for (tmp = kconf; tmp; tmp = tmp->next)
{
if (!doall && (BadPtr(tmp->passwd) || !isdigit(*tmp->passwd)))
continue;
if (!(tmp->status & (CONF_KILL | CONF_OTHERKILL)))
continue; /* should never happen with kconf */
if (!tmp->host || !tmp->name)
continue;
check = (tmp->status == CONF_KILL) ? name : ident;
/* host & IP matching.. */
if (!ip) /* unresolved */
{
if (strchr(tmp->host, '/'))
{
if (match_ipmask((*tmp->host == '=') ?
tmp->host+1: tmp->host, cptr))
continue;
}
else
if (match((*tmp->host == '=') ? tmp->host+1 :
tmp->host, host))
continue;
}
else if (*tmp->host == '=') /* numeric only */
continue;
else /* resolved */
if (strchr(tmp->host, '/'))
{
if (match_ipmask(tmp->host, cptr))
continue;
}
else
if (match(tmp->host, ip) &&
match(tmp->host, host))
continue;
/* user & port matching */
if ((!check || match(tmp->name, check) == 0) &&
(!tmp->port || (tmp->port == cptr->acpt->port)))
{
now = 0;
if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) &&
!(now = check_time_interval(tmp->passwd, reply)))
continue;
if (now == ERR_YOUWILLBEBANNED)
tmp = NULL;
break;
}
#ifdef RUSNET_IRCD
/* nick matching */
if ((!nick || match(tmp->name, nick) == 0) &&
(!tmp->port || (tmp->port == cptr->acpt->port)))
{
now = 0;
if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) &&
!(now = check_time_interval(tmp->passwd, reply)))
continue;
if (now == ERR_YOUWILLBEBANNED)
tmp = NULL;
break;
}
#endif
}
if (*reply)
sendto_one(cptr, reply, ME, now, cptr->name);
else if (tmp)
sendto_one(cptr, ":%s %d %s :%s%s", ME,
ERR_YOUREBANNEDCREEP, cptr->name,
BadPtr(tmp->passwd) ?
"You are not welcome to this server" :
"You are not welcome to this server: ",
BadPtr(tmp->passwd) ? "" : tmp->passwd);
if (tmp && !BadPtr(tmp->passwd))
*comment = tmp->passwd;
else
*comment = NULL;
return (tmp ? -1 : 0);
}
/*
* For type stat, check if both name and host masks match.
* Return -1 for match, 0 for no-match.
*/
int find_two_masks(name, host, stat)
char *name, *host;
int stat;
{
aConfItem *tmp;
for (tmp = conf; tmp; tmp = tmp->next)
if ((tmp->status == stat) && tmp->host && tmp->name &&
(match(tmp->host, host) == 0) &&
(match(tmp->name, name) == 0))
break;
return (tmp ? -1 : 0);
}
/*
* For type stat, check if name matches and any char from key matches
* to chars in passwd field.
* Return -1 for match, 0 for no-match.
*/
int find_conf_flags(name, key, stat)
char *name, *key;
int stat;
{
aConfItem *tmp;
int l;
if (index(key, '/') == NULL)
return 0;
l = ((char *)index(key, '/') - key) + 1;
for (tmp = conf; tmp; tmp = tmp->next)
if ((tmp->status == stat) && tmp->passwd && tmp->name &&
(strncasecmp(key, tmp->passwd, l) == 0) &&
(match(tmp->name, name) == 0) &&
(strpbrk(key + l, tmp->passwd + l)))
break;
return (tmp ? -1 : 0);
}
#ifdef R_LINES
/* find_restrict works against host/name and calls an outside program
* to determine whether a client is allowed to connect. This allows
* more freedom to determine who is legal and who isn't, for example
* machine load considerations. The outside program is expected to
* return a reply line where the first word is either 'Y' or 'N' meaning
* "Yes Let them in" or "No don't let them in." If the first word
* begins with neither 'Y' or 'N' the default is to let the person on.
* It returns a value of 0 if the user is to be let through -Hoppie
*/
int find_restrict(cptr)
aClient *cptr;
{
aConfItem *tmp;
char reply[80], temprpl[80], *head, *tail;
char *rplhold = reply, *host, *name, *s;
char rplchar = 'Y';
int pi[2], rc = 0, n;
if (!cptr->user)
return 0;
name = cptr->user->username;
host = cptr->sockhost;
Debug((DEBUG_INFO, "R-line check for %s[%s]", name, host));
for (tmp = conf; tmp; tmp = tmp->next)
{
if (tmp->status != CONF_RESTRICT ||
(tmp->host && host && match(tmp->host, host)) ||
(tmp->name && name && match(tmp->name, name)))
continue;
if (BadPtr(tmp->passwd))
continue;
if (pipe(pi) == -1)
{
report_error("Error creating pipe for R-line %s:%s",
&me);
return 0;
}
switch (rc = vfork())
{
case -1 :
report_error("Error forking for R-line %s:%s", &me);
return 0;
case 0 :
{
Reg int i;
(void)close(pi[0]);
for (i = 2; i < MAXCONNECTIONS; i++)
if (i != pi[1])
(void)close(i);
if (pi[1] != 2)
(void)dup2(pi[1], 2);
(void)dup2(2, 1);
if (pi[1] != 2 && pi[1] != 1)
(void)close(pi[1]);
(void)execlp(tmp->passwd, tmp->passwd, name, host,
cptr->username, 0);
_exit(-1);
}
default :
(void)close(pi[1]);
break;
}
*reply = '\0';
(void)dgets(-1, NULL, 0, &head, &tail); /* make sure buffer marked empty */
while ((n = dgets(pi[0], temprpl,
sizeof(temprpl) - 1), &head, &tail) > 0)
{
temprpl[n] = '\0';
if ((s = (char *)index(temprpl, '\n')))
*s = '\0';
if (strlen(temprpl) + strlen(reply) < sizeof(reply)-2)
SPRINTF(rplhold,"%s %s", rplhold, temprpl);
else
{
sendto_flag(SCH_ERROR,
"R-line %s/%s: reply too long!",
name, host);
break;
}
}
(void)dgets(-1, NULL, 0, &head, &tail); /* make sure buffer marked empty */
(void)close(pi[0]);
(void)kill(rc, SIGKILL); /* cleanup time */
#if !defined(USE_IAUTH)
(void)wait(0);
#endif
rc = 0;
while (*rplhold == ' ')
rplhold++;
rplchar = *rplhold; /* Pull out the yes or no */
while (*rplhold != ' ')
rplhold++;
while (*rplhold == ' ')
rplhold++;
(void)strcpy(reply,rplhold);
rplhold = reply;
if ((rc = (rplchar == 'n' || rplchar == 'N')))
break;
}
if (rc)
{
sendto_one(cptr, ":%s %d %s :Restriction: %s",
ME, ERR_YOUREBANNEDCREEP, cptr->name, reply);
return -1;
}
return 0;
}
#endif
/*
** check against a set of time intervals
*/
static int check_time_interval(interval, reply)
char *interval, *reply;
{
struct tm *tptr;
char *p;
int perm_min_hours, perm_min_minutes,
perm_max_hours, perm_max_minutes;
int now, perm_min, perm_max;
tptr = localtime(&timeofday);
now = tptr->tm_hour * 60 + tptr->tm_min;
while (interval)
{
p = (char *)index(interval, ',');
if (p)
*p = '\0';
if (sscanf(interval, "%2d%2d-%2d%2d",
&perm_min_hours, &perm_min_minutes,
&perm_max_hours, &perm_max_minutes) != 4)
{
if (p)
*p = ',';
return(0);
}
if (p)
*(p++) = ',';
perm_min = 60 * perm_min_hours + perm_min_minutes;
perm_max = 60 * perm_max_hours + perm_max_minutes;
/*
** The following check allows intervals over midnight ...
*/
if ((perm_min < perm_max)
? (perm_min <= now && now <= perm_max)
: (perm_min <= now || now <= perm_max))
{
(void)sprintf(reply,
":%%s %%d %%s :%s %d:%02d to %d:%02d.",
"You are not allowed to connect from",
perm_min_hours, perm_min_minutes,
perm_max_hours, perm_max_minutes);
return(ERR_YOUREBANNEDCREEP);
}
if ((perm_min < perm_max)
? (perm_min <= now + 5 && now + 5 <= perm_max)
: (perm_min <= now + 5 || now + 5 <= perm_max))
{
(void)sprintf(reply, ":%%s %%d %%s :%d minute%s%s",
perm_min-now,(perm_min-now)>1?"s ":" ",
"and you will be denied for further access");
return(ERR_YOUWILLBEBANNED);
}
interval = p;
}
return(0);
}
/*
** find_bounce
** send a bounce numeric to a client.
** fd is optional, and only makes sense if positive and when cptr is NULL
** fd == -1 : not fd, class is a class number.
** fd == -2 : not fd, class isn't a class number.
*/
void find_bounce(cptr, class, fd)
aClient *cptr;
int class, fd;
{
Reg aConfItem *aconf;
for (aconf = conf; aconf; aconf = aconf->next)
{
if (aconf->status != CONF_BOUNCE)
continue;
if (fd >= 0)
/*
** early rejection,
** connection class and hostname are unknown
*/
if (*aconf->host == '\0')
{
char rpl[BUFSIZE];
#ifdef RUSNET_IRCD
SPRINTF(rpl, rpl_str(RPL_BOUNCE,"unknown"),
aconf->name, (aconf->port) ?
aconf->port : rusnet_getclientport(fd));
#else
SPRINTF(rpl, rpl_str(RPL_BOUNCE,"unknown"),
aconf->name, aconf->port);
#endif
strcat(rpl, "\r\n");
#ifdef INET6
sendto(fd, rpl, strlen(rpl), 0, 0, 0);
#else
send(fd, rpl, strlen(rpl), 0);
#endif
return;
}
else
continue;
/* fd < 0 */
/*
** "too many" type rejection, class is known.
** check if B line is for a class #,
** and if it is for a hostname.
*/
if (fd != -2 &&
!strchr(aconf->host, '.') && isdigit(*aconf->host))
{
if (class != atoi(aconf->host))
continue;
}
else
if (strchr(aconf->host, '/'))
{
if (match_ipmask(aconf->host, cptr))
continue;
}
else if (match(aconf->host, cptr->sockhost))
continue;
sendto_one(cptr, rpl_str(RPL_BOUNCE, cptr->name), aconf->name,
#ifdef RUSNET_IRCD
(!aconf->port) ?
rusnet_getclientport(cptr->acpt->fd) :
#endif
aconf->port);
return;
}
}
/*
** find_denied
** for a given server name, make sure no D line matches any of the
** servers currently present on the net.
*/
aConfItem *
find_denied(name, class)
char *name;
int class;
{
aConfItem *aconf;
for (aconf = conf; aconf; aconf = aconf->next)
{
if (aconf->status != CONF_DENY)
continue;
if (!aconf->name)
continue;
if (match(aconf->name, name) && aconf->port != class)
continue;
if (isdigit(*aconf->passwd))
{
aConfItem *aconf2;
int ck = atoi(aconf->passwd);
for (aconf2 = conf; aconf2; aconf2 = aconf2->next)
{
if (aconf2->status != CONF_NOCONNECT_SERVER)
continue;
if (!aconf2->class || ConfClass(aconf2) != ck)
continue;
if (find_client(aconf2->host, NULL))
return aconf2;
}
}
if (aconf->host)
{
aServer *asptr;
for (asptr = svrtop; asptr; asptr = asptr->nexts)
if (aconf->host &&
!match(aconf->host, asptr->bcptr->name))
return aconf;
}
}
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1