diff options
author | Jonas Gunz <himself@jonasgunz.de> | 2020-05-25 20:09:04 +0200 |
---|---|---|
committer | Jonas Gunz <himself@jonasgunz.de> | 2020-05-25 20:09:04 +0200 |
commit | 4440a86cfa359b8e40a484a2cd46d33db5455d8a (patch) | |
tree | f5c0c59aebf0058ae97e7ef8b5fb8017f459a05a /iauth | |
download | ircd-4440a86cfa359b8e40a484a2cd46d33db5455d8a.tar.gz |
Initial
Diffstat (limited to 'iauth')
-rw-r--r-- | iauth/a_conf.c | 548 | ||||
-rw-r--r-- | iauth/a_conf_def.h | 56 | ||||
-rw-r--r-- | iauth/a_conf_ext.h | 43 | ||||
-rw-r--r-- | iauth/a_defines.h | 39 | ||||
-rw-r--r-- | iauth/a_externs.h | 34 | ||||
-rw-r--r-- | iauth/a_io.c | 831 | ||||
-rw-r--r-- | iauth/a_io_ext.h | 50 | ||||
-rw-r--r-- | iauth/a_log.c | 123 | ||||
-rw-r--r-- | iauth/a_log_def.h | 41 | ||||
-rw-r--r-- | iauth/a_log_ext.h | 46 | ||||
-rw-r--r-- | iauth/a_struct_def.h | 72 | ||||
-rw-r--r-- | iauth/iauth.c | 226 | ||||
-rw-r--r-- | iauth/mod_lhex.c | 318 | ||||
-rw-r--r-- | iauth/mod_lhex_ext.h | 28 | ||||
-rw-r--r-- | iauth/mod_pipe.c | 217 | ||||
-rw-r--r-- | iauth/mod_pipe_ext.h | 28 | ||||
-rw-r--r-- | iauth/mod_rfc931.c | 368 | ||||
-rw-r--r-- | iauth/mod_rfc931_ext.h | 45 | ||||
-rw-r--r-- | iauth/mod_socks.c | 632 | ||||
-rw-r--r-- | iauth/mod_socks_ext.h | 28 |
20 files changed, 3773 insertions, 0 deletions
diff --git a/iauth/a_conf.c b/iauth/a_conf.c new file mode 100644 index 0000000..602ab53 --- /dev/null +++ b/iauth/a_conf.c @@ -0,0 +1,548 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_conf.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: a_conf.c,v 1.21 1999/07/11 22:11:33 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define A_CONF_C +#include "a_externs.h" +#undef A_CONF_C + +static aModule *Mlist[16]; + +#define DEFAULT_TIMEOUT 30 + +u_int debuglevel = 0; + +AnInstance *instances = NULL; + +static void +conf_err(nb, msg, chk) +u_int nb; +char *msg, *chk; +{ + if (chk) + printf("line %d: %s\n", nb, msg); + else + sendto_log(ALOG_IRCD|ALOG_DCONF, LOG_ERR, + "Configuration error line %d: %s", nb, msg); +} + +/* + * Match address by #IP bitmask (10.11.12.128/27) + */ +static int +match_ipmask(mask, ipaddr) +aTarget *mask; +char *ipaddr; +{ +#ifdef INET6 + return 1; +#else + int i1, i2, i3, i4; + u_long iptested; + + if (sscanf(ipaddr, "%d.%d.%d.%d", &i1, &i2, &i3, &i4) != 4) + return -1; + iptested = htonl(i1 * 0x1000000 + i2 * 0x10000 + i3 * 0x100 + i4); + return ((iptested & mask->lmask) == mask->baseip) ? 0 : 1; +#endif +} + +/* conf_read: read the configuration file, instanciate modules */ +char * +conf_read(cfile) +char *cfile; +{ + AnInstance *ident = NULL; /* make sure this module is used */ + u_char needh = 0; /* do we need hostname information for any host? */ + u_char o_req = 0, o_dto = 0, o_wup = 0; + static char o_all[5]; + u_int timeout = DEFAULT_TIMEOUT, totto = 0; + u_int lnnb = 0, i; + u_char icount = 0, Mcnt = 0; + char buffer[160], *ch; + AnInstance **last = &instances, *itmp; + FILE *cfh; + + Mlist[Mcnt++] = &Module_rfc931; + Mlist[Mcnt++] = &Module_socks; + Mlist[Mcnt++] = &Module_pipe; + Mlist[Mcnt++] = &Module_lhex; + Mlist[Mcnt] = NULL; + + cfh = fopen((cfile) ? cfile : IAUTHCONF_PATH, "r"); + if (cfh) + { + while (fgets(buffer, 160, cfh)) + { + if (ch = index(buffer, '\n')) + lnnb += 1; + else + { + conf_err(lnnb, "line too long, ignoring.", + cfile); + /* now skip what's left */ + while (fgets(buffer, 160, cfh)) + if (index(buffer, '\n')) + break; + continue; + } + if (buffer[0] == '#' || buffer[0] == '\n') + continue; + *ch = '\0'; + if (ch = index(buffer, '#')) + *ch = '\0'; + if (!strncmp("required", buffer, 8)) + { + o_req = 1; + continue; + } + if (!strncmp("notimeout", buffer, 9)) + { + o_dto = 1; + continue; + } + if (!strncmp("extinfo", buffer, 7)) + { + o_wup = 1; + continue; + } + if (!strncmp("timeout = ", buffer, 10)) + { + if (sscanf(buffer, "timeout = %u", + &timeout) != 1) + conf_err(lnnb, "Invalid setting.", + cfile); + continue; + } + /* debugmode setting */ + if (!strncmp("debuglvl = 0x", buffer, 13)) + { + if (sscanf(buffer, "debuglvl = %x", + &debuglevel) != 1) + conf_err(lnnb, "Invalid setting.", + cfile); + else if (!cfile) + sendto_log(ALOG_DCONF, LOG_DEBUG, + "debuglevel = %X", + debuglevel); + continue; + } +#if defined(USE_DSM) + if (!strncmp("shared ", buffer, 7)) + { + char lfname[80]; + void *mod_handle; + aModule *(*load_func)(); + + ch = index(buffer+7, ' '); + if (ch == NULL) + { + conf_err(lnnb, "Syntax error.", cfile); + continue; + } + *ch++ = '\0'; + mod_handle = dlopen(ch, RTLD_NOW); + if (mod_handle == NULL) + { + conf_err(lnnb, dlerror(), cfile); + continue; + } +# if defined(DLSYM_NEEDS_UNDERSCORE) + sprintf(lfname, "_%s_load", buffer+7); +# else + sprintf(lfname, "%s_load", buffer+7); +# endif + load_func = (aModule *(*)())dlsym(mod_handle, + lfname); + if (load_func == NULL) + { + conf_err(lnnb,"Invalid shared object.", + cfile); + dlclose(mod_handle); + continue; + } + Mlist[Mcnt] = load_func(); + if (Mlist[Mcnt]) + { + Mcnt += 1; + Mlist[Mcnt] = NULL; + } + else + { + conf_err(lnnb, "Failed.", cfile); + dlclose(mod_handle); + } + continue; + } +#endif + if (buffer[0] == '\t') + { + conf_err(lnnb, "Ignoring unexpected property.", + cfile); + continue; + } + /* at this point, it has to be the following */ + if (strncasecmp("module ", buffer, 7)) + { + conf_err(lnnb, + "Unexpected line: not a module.", + cfile); + continue; + } + for (i = 0; Mlist[i] != NULL; i++) + if (!strcasecmp(buffer+7, Mlist[i]->name)) + break; + if (Mlist[i] == NULL) + { + conf_err(lnnb, "Unknown module name.", cfile); + continue; + } + if (Mlist[i] == &Module_rfc931 && ident) + { + conf_err(lnnb, + "This module can only be loaded once.", + cfile); + continue; + } + *last = (AnInstance *) malloc(sizeof(AnInstance)); + (*last)->nexti = NULL; + (*last)->in = icount++; + (*last)->mod = Mlist[i]; + (*last)->opt = NULL; + (*last)->popt = NULL; + (*last)->data = NULL; + (*last)->hostname = NULL; + (*last)->address = NULL; + (*last)->timeout = timeout; + if (Mlist[i] == &Module_rfc931) + ident = *last; + + while (fgets(buffer, 160, cfh)) + { + aTarget **ttmp; + u_long baseip = 0, lmask = 0; + + if (ch = index(buffer, '\n')) + lnnb += 1; + else + { + conf_err(lnnb, + "line too long, ignoring.", + cfile); + /* now skip what's left */ + while (fgets(buffer, 160, cfh)) + if (index(buffer,'\n')) + break; + continue; + } + if (buffer[0] == '#') + continue; + if (buffer[0] == '\n') + break; + if (buffer[0] != '\t') + { + conf_err(lnnb, "Invalid syntax.", + cfile); + continue; + } + *ch = '\0'; + if (!strncasecmp(buffer+1, "option = ", 9)) + { + if ((*last)->opt) + conf_err(lnnb, + "Duplicate option keyword: ignored.", + cfile); + else + (*last)->opt = + mystrdup(buffer + 10); + continue; + } + if (!strncasecmp(buffer+1, "host = ", 7)) + { + needh = 1; + ttmp = &((*last)->hostname); + ch = buffer + 8; + } + else if (!strncasecmp(buffer+1, "ip = ", 5)) + { + ttmp = &((*last)->address); + ch = buffer + 6; + if (strchr(ch, '/')) + { + int i1, i2, i3, i4, m; + + if (sscanf(ch,"%d.%d.%d.%d/%d", + &i1, &i2, &i3, &i4, + &m) != 5 || + m < 1 || m > 31) + { + conf_err(lnnb, + "Bad mask.", + cfile); + continue; + } + lmask = htonl((u_long)0xffffffffL << (32 - m)); + baseip = htonl(i1 * 0x1000000 + + i2 * 0x10000 + + i3 * 0x100 + + i4); + } + else + { + lmask = 0; + baseip = 0; + } + } + else if (!strncmp(buffer+1, "timeout = ", 10)) + { + u_int local_timeout; + if (sscanf(buffer+1, "timeout = %u", + &local_timeout) != 1) + conf_err(lnnb, + "Invalid setting.", + cfile); + (*last)->timeout = local_timeout; + continue; + } + else + { + conf_err(lnnb, "Invalid keyword.", + cfile); + continue; + } + if (Mlist[i] == &Module_rfc931) + continue; + while (*ttmp) + ttmp = &((*ttmp)->nextt); + *ttmp = (aTarget *) malloc(sizeof(aTarget)); + if (*ch == '!') + { + (*ttmp)->yes = -1; + ch++; + } + else + (*ttmp)->yes = 0; + (*ttmp)->value = mystrdup(ch); + if ((*ttmp)->baseip) + { + (*ttmp)->lmask = lmask; + (*ttmp)->baseip = baseip; + } + (*ttmp)->nextt = NULL; + } + + last = &((*last)->nexti); + } + } + else if (cfile) + { + perror("fopen"); + exit(0); + } + if (ident == NULL) + { + ident = *last = (AnInstance *) malloc(sizeof(AnInstance)); + (*last)->nexti = NULL; + (*last)->opt = NULL; + (*last)->mod = &Module_rfc931; + (*last)->hostname = NULL; + (*last)->address = NULL; + (*last)->timeout = DEFAULT_TIMEOUT; + } + ident->timeout = MAX(DEFAULT_TIMEOUT, ident->timeout); + + itmp = instances; + while (itmp) + { + totto += itmp->timeout; + itmp = itmp->nexti; + } + if (totto > ACCEPTTIMEOUT) + { + if (cfile) + printf("Warning: sum of timeouts exceeds ACCEPTTIMEOUT!\n"); + else + sendto_log(ALOG_IRCD|ALOG_DCONF, LOG_ERR, + "Warning: sum of timeouts exceeds ACCEPTTIMEOUT!"); + if (o_dto) + if (cfile) + printf("Error: \"notimeout\" is set!\n"); + else + sendto_log(ALOG_IRCD|ALOG_DCONF, LOG_ERR, + "Error: \"notimeout\" is set!"); + } + + itmp = instances; + if (cfile) + { + aTarget *ttmp; + char *err; + + printf("\nModule(s) loaded:\n"); + while (itmp) + { + printf("\t%s\t%s\n", itmp->mod->name, + (itmp->opt) ? itmp->opt : ""); + if (ttmp = itmp->hostname) + { + printf("\t\tHost = %s%s", + (ttmp->yes == 0) ? "" : "!", + ttmp->value); + while (ttmp = ttmp->nextt) + printf(",%s%s", + (ttmp->yes == 0) ? "" : "!", + ttmp->value); + printf("\n"); + } + if (ttmp = itmp->address) + { + printf("\t\tIP = %s", + (ttmp->yes == 0) ? "" : "!", + ttmp->value); + while (ttmp = ttmp->nextt) + printf(",%s%s", + (ttmp->yes == 0) ? "" : "!", + ttmp->value); + printf("\n"); + } + if (itmp->timeout != DEFAULT_TIMEOUT) + printf("\t\ttimeout: %u seconds\n", + itmp->timeout); + if (itmp->mod->init) + { + err = itmp->mod->init(itmp); + printf("\t\tInitialization: %s\n", + (err) ? err : "Successful"); + } + itmp = itmp->nexti; + } + } + else + while (itmp) + { + if (itmp->mod->init) + itmp->mod->init(itmp); + itmp = itmp->nexti; + } + + ch = o_all; + if (o_req) *ch++ = 'R'; + if (o_dto) *ch++ = 'T'; + if (o_wup) *ch++ = 'A'; + if (needh) *ch++ = 'W'; + *ch++ = '\0'; + return o_all; +} + +/* conf_match: check if an instance is to be applied to a connection + Returns -1: no match, and never will + 0: got a match, doIt[tm] + 1: no match, but might be later so ask again */ +int +conf_match(cl, inst) +u_int cl; +AnInstance *inst; +{ + aTarget *ttmp; + + /* general case, always matches */ + if (inst->address == NULL && inst->hostname == NULL) + return 0; + /* feature case, "host = *" to force to wait for DNS info */ + if ((cldata[cl].state & A_NOH) && inst->hostname && + !strcmp(inst->hostname->value, "*")) + return 0; + /* check matches on IP addresses */ + if (ttmp = inst->address) + while (ttmp) + { + if (ttmp->baseip) + { + if (match_ipmask(ttmp, cldata[cl].itsip) == 0) + return ttmp->yes; + } + else + if (match(ttmp->value, cldata[cl].itsip) == 0) + return ttmp->yes; + ttmp = ttmp->nextt; + } + /* check matches on hostnames */ + if (ttmp = inst->hostname) + { + if (cldata[cl].state & A_GOTH) + { + while (ttmp) + { + if (match(ttmp->value, cldata[cl].host) == 0) + return ttmp->yes; + ttmp = ttmp->nextt; + } + /* no match, will never match */ + return -1; + } + else if (cldata[cl].state & A_NOH) + return -1; + else + /* may be later, once we have DNS information */ + return 1; + } + /* fall through, no match, will never match */ + return -1; +} + +/* conf_ircd: send the configuration to the ircd daemon */ +void +conf_ircd() +{ + AnInstance *itmp = instances; + aTarget *ttmp; + + sendto_ircd("a"); + while (itmp) + { + if (itmp->address == NULL && itmp->hostname == NULL) + sendto_ircd("A * %s %s", itmp->mod->name, + (itmp->popt) ? itmp->popt : ""); + else + { + ttmp = itmp->address; + while (ttmp) + { + sendto_ircd("A %s %s %s", ttmp->value, + itmp->mod->name, + (itmp->popt) ? itmp->popt : ""); + ttmp = ttmp->nextt; + } + ttmp = itmp->hostname; + while (ttmp) + { + sendto_ircd("A %s %s %s", ttmp->value, + itmp->mod->name, + (itmp->popt) ? itmp->popt : ""); + ttmp = ttmp->nextt; + } + } + itmp = itmp->nexti; + } +} diff --git a/iauth/a_conf_def.h b/iauth/a_conf_def.h new file mode 100644 index 0000000..078a9a0 --- /dev/null +++ b/iauth/a_conf_def.h @@ -0,0 +1,56 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_conf_def.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +typedef struct Module aModule; +typedef struct Instance AnInstance; +typedef struct Target aTarget; + +struct Module +{ + char *name; /* module name */ + char *(*init)(AnInstance *); /* instance initialization */ + void (*release)(AnInstance *);/* instance releasing >UNUSED< */ + void (*stats)(AnInstance *); /* send instance stats to ircd */ + int (*start)(u_int); /* start authentication */ + int (*work)(u_int); /* called whenever something has to be + * done (incoming data, timeout..) */ + int (*timeout)(u_int); /* called when timeout is reached */ + void (*clean)(u_int); /* finish/abort: cleanup*/ +}; + +struct Instance +{ + AnInstance *nexti; + u_char in; /* instance number */ + aModule *mod; /* module */ + char *opt; /* options read from file */ + char *popt; /* options to send to ircd */ + void *data; /* private data: stats, ... */ + aTarget *address; + aTarget *hostname; + u_int timeout; +}; + +struct Target +{ + char *value; + u_long baseip, lmask; /* a.b.c.d/z */ + char yes; + aTarget *nextt; +}; diff --git a/iauth/a_conf_ext.h b/iauth/a_conf_ext.h new file mode 100644 index 0000000..f0416be --- /dev/null +++ b/iauth/a_conf_ext.h @@ -0,0 +1,43 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_conf_ext.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/a_conf.c. + */ + +/* External definitions for global variables. + */ +#ifndef A_CONF_C +extern u_int debuglevel; +extern AnInstance *instances; +#endif /* A_CONF_C */ + +/* External definitions for global functions. + */ +#ifndef A_CONF_C +# define EXTERN extern +#else /* A_CONF_C */ +# define EXTERN +#endif /* A_CONF_C */ + +EXTERN char *conf_read __P((char *)); +EXTERN int conf_match __P((u_int, AnInstance *)); +EXTERN void conf_ircd(); + +#undef EXTERN diff --git a/iauth/a_defines.h b/iauth/a_defines.h new file mode 100644 index 0000000..aff3038 --- /dev/null +++ b/iauth/a_defines.h @@ -0,0 +1,39 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_defines.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file includes all files defining constants, macros and types + definitions used by the authentication process. + */ + +#define IAUTH_DEBUG 1 + +#include "config.h" +#include "patchlevel.h" + +#include "dbuf_def.h" /* needed for struct_def.h, sigh */ +#include "class_def.h" /* needed for struct_def.h, sigh */ +#include "struct_def.h" +#if INET6 +# include "../ircd/nameser_def.h" +#endif +#include "support_def.h" + +#include "a_conf_def.h" +#include "a_struct_def.h" +#include "a_log_def.h" diff --git a/iauth/a_externs.h b/iauth/a_externs.h new file mode 100644 index 0000000..5108b56 --- /dev/null +++ b/iauth/a_externs.h @@ -0,0 +1,34 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_externs.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file includes all *_ext.h files containing external declarations + * for the authentication process. + */ + +#include "match_ext.h" +#include "support_ext.h" + +#include "a_conf_ext.h" +#include "a_io_ext.h" +#include "a_log_ext.h" + +#include "mod_rfc931_ext.h" +#include "mod_socks_ext.h" +#include "mod_pipe_ext.h" +#include "mod_lhex_ext.h" diff --git a/iauth/a_io.c b/iauth/a_io.c new file mode 100644 index 0000000..a4bd57e --- /dev/null +++ b/iauth/a_io.c @@ -0,0 +1,831 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_io.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: a_io.c,v 1.22 1999/07/11 22:09:59 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define A_IO_C +#include "a_externs.h" +#undef A_IO_C + +anAuthData cldata[MAXCONNECTIONS]; /* index == ircd fd */ +static int cl_highest = -1; +#if defined(USE_POLL) +static int fd2cl[MAXCONNECTIONS]; /* fd -> cl mapping */ +#endif + +#define IOBUFSIZE 4096 +static char iobuf[IOBUFSIZE+1]; +static char rbuf[IOBUFSIZE+1]; /* incoming ircd stream */ +static int iob_len = 0, rb_len = 0; + +void +init_io() +{ + bzero((char *) cldata, sizeof(cldata)); +} + +/* sendto_ircd() functions */ +#if ! USE_STDARG +void +sendto_ircd(pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) +char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10; +#else +void +vsendto_ircd(char *pattern, va_list va) +#endif +{ + char ibuf[4096]; + +#if ! USE_STDARG + sprintf(ibuf, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +#else + vsprintf(ibuf, pattern, va); +#endif + DebugLog((ALOG_DSPY, 0, "To ircd: [%s]", ibuf)); + strcat(ibuf, "\n"); + if (write(0, ibuf, strlen(ibuf)) != strlen(ibuf)) + { + sendto_log(ALOG_DMISC, LOG_NOTICE, "Daemon exiting. [w %s]", + strerror(errno)); + exit(0); + } +} + +#if USE_STDARG +void +sendto_ircd(char *pattern, ...) +{ + va_list va; + va_start(va, pattern); + vsendto_ircd(pattern, va); + va_end(va); +} +#endif + +/* + * next_io + * + * given an entry, look for the next module instance to start + */ +static void +next_io(cl, last) +int cl; +AnInstance *last; +{ + DebugLog((ALOG_DIO, 0, "next_io(#%d, %x): last=%s state=0x%X", cl, last, + (last) ? last->mod->name : "", cldata[cl].state)); + + /* first, bail out immediately if the entry is flagged A_DONE */ + if (cldata[cl].state & A_DONE) + return; + + /* second, make sure the last instance which ran cleaned up */ + if (cldata[cl].rfd > 0 || cldata[cl].wfd > 0) + { + /* last is defined here */ + sendto_log(ALOG_IRCD|ALOG_DMISC, LOG_ERR, + "module \"%s\" didn't clean up fd's! (%d %d)", + last->mod->name, cldata[cl].rfd, cldata[cl].wfd); + if (cldata[cl].rfd > 0) + close(cldata[cl].rfd); + if (cldata[cl].wfd > 0 && cldata[cl].rfd != cldata[cl].wfd) + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; + } + + cldata[cl].buflen = 0; + cldata[cl].mod_status = 0; + cldata[cl].instance = NULL; + cldata[cl].timeout = 0; + + /* third, if A_START is set, a new pass has to be started */ + if (cldata[cl].state & A_START) + { + cldata[cl].state ^= A_START; + DebugLog((ALOG_DIO, 0, "next_io(#%d, %x): Starting again", + cl, last)); + last = NULL; /* start from beginning */ + } + + /* fourth, find next instance to be ran */ + if (last == NULL) + { + cldata[cl].instance = instances; + cldata[cl].ileft = 0; + } + else + cldata[cl].instance = last->nexti; + + while (cldata[cl].instance) + { + int cm; + + if (CheckBit(cldata[cl].idone, cldata[cl].instance->in)) + { + DebugLog((ALOG_DIO, 0, + "conf_match(#%d, %x, goth=%d, noh=%d) skipped %x (%s)", + cl, last, (cldata[cl].state & A_GOTH) == A_GOTH, + (cldata[cl].state & A_NOH) == A_NOH, + cldata[cl].instance, + cldata[cl].instance->mod->name)); + cldata[cl].instance = cldata[cl].instance->nexti; + continue; + } + cm = conf_match(cl, cldata[cl].instance); + DebugLog((ALOG_DIO, 0, + "conf_match(#%d, %x, goth=%d, noh=%d) said \"%s\" for %x (%s)", + cl, last, (cldata[cl].state & A_GOTH) == A_GOTH, + (cldata[cl].state & A_NOH) == A_NOH, + (cm==-1) ? "no match" : (cm==0) ? "match" : "try again", + cldata[cl].instance, cldata[cl].instance->mod->name)); + if (cm == 0) + break; + if (cm == -1) + SetBit(cldata[cl].idone, cldata[cl].instance->in); + else /* cm == 1 */ + cldata[cl].ileft += 1; + cldata[cl].instance = cldata[cl].instance->nexti; + } + + if (cldata[cl].instance == NULL) + /* fifth, when there's no instance to try.. */ + { + DebugLog((ALOG_DIO, 0, + "next_io(#%d, %x): no more instances to try (%d)", + cl, last, cldata[cl].ileft)); + if (cldata[cl].ileft == 0) + { + /* we are done */ + sendto_ircd("D %d %s %u ", cl, cldata[cl].itsip, + cldata[cl].itsport); + cldata[cl].state |= A_DONE; + free(cldata[cl].inbuffer); + cldata[cl].inbuffer = NULL; + } + return; + } + else + /* sixth, we've got an instance to try */ + { + int r; + + cldata[cl].timeout = time(NULL) + cldata[cl].instance->timeout; + r = cldata[cl].instance->mod->start(cl); + DebugLog((ALOG_DIO, 0, + "next_io(#%d, %x): %s->start() returned %d", + cl, last, cldata[cl].instance->mod->name, r)); + if (r != 1) + /* started, or nothing to do or failed: don't try again */ + SetBit(cldata[cl].idone, cldata[cl].instance->in); + if (r == 1) + cldata[cl].ileft += 1; + if (r != 0) + /* start() didn't start something */ + next_io(cl, cldata[cl].instance); + } +} + +/* + * parse_ircd + * + * parses data coming from ircd (doh ;-) + */ +static void +parse_ircd() +{ + char *ch, *chp, *buf = iobuf; + int cl = -1, ncl; + + iobuf[iob_len] = '\0'; + while (ch = index(buf, '\n')) + { + *ch = '\0'; + DebugLog((ALOG_DSPY, 0, "parse_ircd(): got [%s]", buf)); + + cl = atoi(chp = buf); + while (*chp++ != ' '); + switch (chp[0]) + { + case 'C': /* new connection */ + case 'O': /* old connection: do nothing, just update data */ + if (cldata[cl].state & A_ACTIVE) + { + /* this is not supposed to happen!!! */ + sendto_log(ALOG_IRCD, LOG_CRIT, + "Entry %d [%c] is already active (fatal)!", + cl, chp[0]); + exit(1); + } + if (cldata[cl].instance || cldata[cl].rfd > 0 || + cldata[cl].wfd > 0) + { + sendto_log(ALOG_IRCD, LOG_CRIT, + "Entry %d [%c] is already active! (fatal)", + cl, chp[0]); + exit(1); + } + if (cldata[cl].authuser) + { + /* shouldn't be here - hmmpf */ + sendto_log(ALOG_IRCD|ALOG_DIO, LOG_WARNING, + "Unreleased data [%c %d]!", chp[0], + cl); + free(cldata[cl].authuser); + cldata[cl].authuser = NULL; + } + if (cldata[cl].inbuffer) + { + /* shouldn't be here - hmmpf */ + sendto_log(ALOG_IRCD|ALOG_DIO, LOG_WARNING, + "Unreleased buffer [%c %d]!", + chp[0], cl); + free(cldata[cl].inbuffer); + cldata[cl].inbuffer = NULL; + } + cldata[cl].user[0] = '\0'; + cldata[cl].passwd[0] = '\0'; + cldata[cl].host[0] = '\0'; + bzero(cldata[cl].idone, BDSIZE); + cldata[cl].buflen = 0; + if (chp[0] == 'C') + cldata[cl].state = A_ACTIVE; + else + { + cldata[cl].state = A_ACTIVE|A_IGNORE; + break; + } + if (sscanf(chp+2, "%[^ ] %hu %[^ ] %hu", + cldata[cl].itsip, &cldata[cl].itsport, + cldata[cl].ourip, &cldata[cl].ourport) != 4) + { + sendto_log(ALOG_IRCD, LOG_CRIT, + "Bad data from ircd [%s] (fatal)", + chp); + exit(1); + } + /* we should really be using a pool of buffer here */ + cldata[cl].inbuffer = malloc(INBUFSIZE+1); + if (cl > cl_highest) + cl_highest = cl; + next_io(cl, NULL); /* get started */ + break; + case 'D': /* client disconnect */ + if (!(cldata[cl].state & A_ACTIVE)) + /* + ** this is not fatal, it happens with servers + ** we connected to (and more?). + ** It's better/safer to ignore here rather + ** than try to filter in ircd. -kalt + */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [D] is not active.", cl); + cldata[cl].state = 0; + if (cldata[cl].rfd > 0 || cldata[cl].wfd > 0) + cldata[cl].instance->mod->clean(cl); + cldata[cl].instance = NULL; + /* log something here? hmmpf */ + if (cldata[cl].authuser) + free(cldata[cl].authuser); + cldata[cl].authuser = NULL; + if (cldata[cl].inbuffer) + free(cldata[cl].inbuffer); + cldata[cl].inbuffer = NULL; + break; + case 'R': /* fd remap */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* this should really not happen */ + sendto_log(ALOG_IRCD, LOG_CRIT, + "Entry %d [R] is not active!", cl); + break; + } + ncl = atoi(chp+2); + if (cldata[ncl].state & A_ACTIVE) + { + /* this is not supposed to happen!!! */ + sendto_log(ALOG_IRCD, LOG_CRIT, + "Entry %d [R] is already active (fatal)!", ncl); + exit(1); + } + if (cldata[ncl].instance || cldata[ncl].rfd > 0 || + cldata[ncl].wfd > 0) + { + sendto_log(ALOG_IRCD, LOG_CRIT, + "Entry %d is already active! (fatal)", + ncl); + exit(1); + } + if (cldata[ncl].authuser) + { + /* shouldn't be here - hmmpf */ + sendto_log(ALOG_IRCD|ALOG_DIO, LOG_WARNING, + "Unreleased data [%d]!", ncl); + free(cldata[ncl].authuser); + cldata[ncl].authuser = NULL; + } + if (cldata[ncl].inbuffer) + { + /* shouldn't be here - hmmpf */ + sendto_log(ALOG_IRCD|ALOG_DIO, LOG_WARNING, + "Unreleased buffer [%c %d]!", + chp[0], ncl); + free(cldata[ncl].inbuffer); + cldata[ncl].inbuffer = NULL; + } + bcopy(cldata+cl, cldata+ncl, sizeof(anAuthData)); + + cldata[cl].state = 0; + cldata[cl].rfd = cldata[cl].wfd = 0; + cldata[cl].instance = NULL; + cldata[cl].authuser = NULL; + cldata[cl].inbuffer = NULL; + /* + ** this is the ugly part of having a slave (considering + ** that ircd remaps fd's: there is lag between the + ** server and the slave. + ** I can't think of any better way to handle this at + ** the moment -kalt + */ + if (cldata[ncl].state & A_IGNORE) + break; + if (cldata[ncl].state & A_LATE) + /* pointless 99.9% of the time */ + break; + if (cldata[ncl].authuser) + sendto_ircd("%c %d %s %u %s", + (cldata[ncl].state&A_UNIX)?'U':'u', + ncl, cldata[ncl].itsip, + cldata[ncl].itsport, + cldata[ncl].authuser); + if (cldata[ncl].state & A_DENY) + sendto_ircd("K %d %s %u ", ncl, + cldata[ncl].itsip, + cldata[ncl].itsport, + cldata[ncl].authuser); + if (cldata[ncl].state & A_DONE) + sendto_ircd("D %d %s %u ", ncl, + cldata[ncl].itsip, + cldata[ncl].itsport, + cldata[ncl].authuser); + break; + case 'N': /* hostname */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* let's be conservative and just ignore */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [N] is not active.", cl); + break; + } + if (cldata[cl].state & A_IGNORE) + break; + strcpy(cldata[cl].host, chp+2); + cldata[cl].state |= A_GOTH|A_START; + if (cldata[cl].instance == NULL) + next_io(cl, NULL); + break; + case 'A': /* host alias */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* let's be conservative and just ignore */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [A] is not active.", cl); + break; + } + if (cldata[cl].state & A_IGNORE) + break; + /* hmmpf */ + break; + case 'U': /* user provided username */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* let's be conservative and just ignore */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [U] is not active.", cl); + break; + } + if (cldata[cl].state & A_IGNORE) + break; + strcpy(cldata[cl].user, chp+2); + cldata[cl].state |= A_GOTU|A_START; + if (cldata[cl].instance == NULL) + next_io(cl, NULL); + break; + case 'P': /* user provided password */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* let's be conservative and just ignore */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [P] is not active.", cl); + break; + } + if (cldata[cl].state & A_IGNORE) + break; + strcpy(cldata[cl].passwd, chp+2); + cldata[cl].state |= A_GOTP; + /* + ** U message will follow immediately, + ** no need to do any thing else here + */ + break; + case 'T': /* ircd is registering the client */ + /* what to do with this? abort/continue? */ + cldata[cl].state |= A_LATE; + break; + case 'd': /* DNS timeout */ + case 'n': /* No hostname information, but no timeout either */ + if (!(cldata[cl].state & A_ACTIVE)) + { + /* let's be conservative and just ignore */ + sendto_log(ALOG_IRCD, LOG_WARNING, + "Warning: Entry %d [%c] is not active.", + cl, chp[0]); + break; + } + cldata[cl].state |= A_NOH|A_START; + if (cldata[cl].instance == NULL) + next_io(cl, NULL); + break; + case 'E': /* error message from ircd */ + sendto_log(ALOG_DIRCD, LOG_DEBUG, + "Error from ircd: %s", chp); + break; + default: + sendto_log(ALOG_IRCD, LOG_ERR, "Unexpected data [%s]", + chp); + break; + } + + buf = ch+1; + } + rb_len = 0; iob_len = 0; + if (strlen(buf)) + bcopy(buf, rbuf, rb_len = strlen(buf)); +} + +/* + * loop_io + * + * select()/poll() loop + */ +void +loop_io() +{ + /* the following is from ircd/s_bsd.c */ +#if ! USE_POLL +# define SET_READ_EVENT( thisfd ) FD_SET( thisfd, &read_set) +# define SET_WRITE_EVENT( thisfd ) FD_SET( thisfd, &write_set) +# define CLR_READ_EVENT( thisfd ) FD_CLR( thisfd, &read_set) +# define CLR_WRITE_EVENT( thisfd ) FD_CLR( thisfd, &write_set) +# define TST_READ_EVENT( thisfd ) FD_ISSET( thisfd, &read_set) +# define TST_WRITE_EVENT( thisfd ) FD_ISSET( thisfd, &write_set) + + fd_set read_set, write_set; + int highfd = -1; +#else +/* most of the following use pfd */ +# define POLLSETREADFLAGS (POLLIN|POLLRDNORM) +# define POLLREADFLAGS (POLLSETREADFLAGS|POLLHUP|POLLERR) +# define POLLSETWRITEFLAGS (POLLOUT|POLLWRNORM) +# define POLLWRITEFLAGS (POLLOUT|POLLWRNORM|POLLHUP|POLLERR) + +# define SET_READ_EVENT( thisfd ){ CHECK_PFD( thisfd );\ + pfd->events |= POLLSETREADFLAGS;} +# define SET_WRITE_EVENT( thisfd ){ CHECK_PFD( thisfd );\ + pfd->events |= POLLSETWRITEFLAGS;} + +# define CLR_READ_EVENT( thisfd ) pfd->revents &= ~POLLSETREADFLAGS +# define CLR_WRITE_EVENT( thisfd ) pfd->revents &= ~POLLSETWRITEFLAGS +# define TST_READ_EVENT( thisfd ) pfd->revents & POLLREADFLAGS +# define TST_WRITE_EVENT( thisfd ) pfd->revents & POLLWRITEFLAGS + +# define CHECK_PFD( thisfd ) \ + if ( pfd->fd != thisfd ) { \ + pfd = &poll_fdarray[nbr_pfds++];\ + pfd->fd = thisfd; \ + pfd->events = 0; \ + } + + struct pollfd poll_fdarray[MAXCONNECTIONS]; + struct pollfd * pfd = poll_fdarray; + int nbr_pfds = 0; +#endif + + int i, nfds = 0; + struct timeval wait; + time_t now = time(NULL); + +#if !defined(USE_POLL) + FD_ZERO(&read_set); + FD_ZERO(&write_set); + highfd = 0; +#else + /* set up such that CHECK_FD works */ + nbr_pfds = 0; + pfd = poll_fdarray; + pfd->fd = -1; +#endif /* USE_POLL */ + + SET_READ_EVENT(0); nfds = 1; /* ircd stream */ +#if defined(USE_POLL) && defined(IAUTH_DEBUG) + for (i = 0; i < MAXCONNECTIONS; i++) + fd2cl[i] = -1; /* sanity */ +#endif + for (i = 0; i <= cl_highest; i++) + { + if (cldata[i].timeout && cldata[i].timeout < now && + cldata[i].instance /* shouldn't be needed.. but it is */) + { + DebugLog((ALOG_DIO, 0, + "io_loop(): module %s timeout [%d]", + cldata[i].instance->mod->name, i)); + if (cldata[i].instance->mod->timeout(i) != 0) + next_io(i, cldata[i].instance); + } + if (cldata[i].rfd > 0) + { + SET_READ_EVENT(cldata[i].rfd); +#if !defined(USE_POLL) + if (cldata[i].rfd > highfd) + highfd = cldata[i].rfd; +#else + fd2cl[cldata[i].rfd] = i; +#endif + nfds++; + } + else if (cldata[i].wfd > 0) + { + SET_WRITE_EVENT(cldata[i].wfd); +#if ! USE_POLL + if (cldata[i].wfd > highfd) + highfd = cldata[i].wfd; +#else + fd2cl[cldata[i].wfd] = i; +#endif + nfds++; + } + } + + DebugLog((ALOG_DIO, 0, "io_loop(): checking for %d fd's", nfds)); + wait.tv_sec = 5; wait.tv_usec = 0; +#if ! USE_POLL + nfds = select(highfd + 1, (SELECT_FDSET_TYPE *)&read_set, + (SELECT_FDSET_TYPE *)&write_set, 0, &wait); + DebugLog((ALOG_DIO, 0, "io_loop(): select() returned %d, errno = %d", + nfds, errno)); +#else + nfds = poll(poll_fdarray, nbr_pfds, + wait.tv_sec * 1000 + wait.tv_usec/1000 ); + DebugLog((ALOG_DIO, 0, "io_loop(): poll() returned %d, errno = %d", + nfds, errno)); + pfd = poll_fdarray; +#endif + if (nfds == -1) + if (errno == EINTR) + return; + else + { + sendto_log(ALOG_IRCD, LOG_CRIT, + "fatal select/poll error: %s", + strerror(errno)); + exit(1); + } + if (nfds == 0) /* end of timeout */ + return; + + /* no matter select() or poll() this is also fd # 0 */ + if (TST_READ_EVENT(0)) + nfds--; + +#if !defined(USE_POLL) + for (i = 0; i <= cl_highest && nfds; i++) +#else + for (pfd = poll_fdarray+1; pfd != poll_fdarray+nbr_pfds && nfds; pfd++) +#endif + { +#if defined(USE_POLL) + i = fd2cl[pfd->fd]; +# if defined(IAUTH_DEBUG) + if (i == -1) + { + sendto_log(ALOG_DALL, LOG_CRIT,"io_loop(): fatal bug"); + exit(1); + } +# endif +#endif + if (cldata[i].rfd <= 0 && cldata[i].wfd <= 0) + { +#if defined(USE_POLL) + sendto_log(ALOG_IRCD, LOG_CRIT, + "io_loop(): fatal data inconsistency #%d (%d, %d)", + i, cldata[i].rfd, cldata[i].wfd); + exit(1); +#else + continue; +#endif + } + if (cldata[i].rfd > 0 && TST_READ_EVENT(cldata[i].rfd)) + { + int len; + + len = recv(cldata[i].rfd, + cldata[i].inbuffer + cldata[i].buflen, + INBUFSIZE - cldata[i].buflen, 0); + DebugLog((ALOG_DIO, 0, "io_loop(): i = #%d: recv(%d) returned %d, errno = %d", i, cldata[i].rfd, len, errno)); + if (len < 0) + { + cldata[i].instance->mod->clean(i); + next_io(i, cldata[i].instance); + } + else + { + cldata[i].buflen += len; + if (cldata[i].instance->mod->work(i) != 0) + next_io(i, cldata[i].instance); + else if (len == 0) + { + cldata[i].instance->mod->clean(i); + next_io(i, cldata[i].instance); + } + } + nfds--; + } + else if (cldata[i].wfd > 0 && TST_WRITE_EVENT(cldata[i].wfd)) + { + if (cldata[i].instance->mod->work(i) != 0) + next_io(i, cldata[i].instance); + + nfds--; + } + } + + /* + ** no matter select() or poll() this is also fd # 0 + ** this has to be done last (for the USE_POLL version) because + ** of R messages we may get from the server :/ + */ +#if defined(USE_POLL) + pfd = poll_fdarray; +#endif + if (TST_READ_EVENT(0)) + { + /* data from the ircd.. */ + while (1) + { + if (rb_len) + bcopy(rbuf, iobuf, iob_len = rb_len); + if ((i=recv(0,iobuf+iob_len,IOBUFSIZE-iob_len,0)) <= 0) + { + DebugLog((ALOG_DIO, 0, "io_loop(): recv(0) returned %d, errno = %d", i, errno)); + break; + } + iob_len += i; + DebugLog((ALOG_DIO, 0, + "io_loop(): got %d bytes from ircd [%d]", i, + iob_len)); + parse_ircd(); + } + if (i == 0) + { + sendto_log(ALOG_DMISC, LOG_NOTICE, + "Daemon exiting. [r]"); + exit(0); + } + } + +#if defined(IAUTH_DEBUG) + if (nfds > 0) + sendto_log(ALOG_DIO, 0, "io_loop(): nfds = %d !!!", nfds); +# if !defined(USE_POLL) + /* the equivalent should be written for poll() */ + if (nfds == 0) + while (i <= cl_highest) + { + if (cldata[i].rfd > 0 && TST_READ_EVENT(cldata[i].rfd)) + { + /* this should not happen! */ + /* hmmpf */ + } + i++; + } +# endif +#endif +} + +/* + * set_non_blocking (ripped from ircd/s_bsd.c) + */ +static void +set_non_blocking(fd, ip, port) +int fd; +char *ip; +u_short port; +{ + int res, nonb = 0; + +#if NBLOCK_POSIX + nonb |= O_NONBLOCK; +#endif +#if NBLOCK_BSD + nonb |= O_NDELAY; +#endif +#if NBLOCK_SYSV + /* This portion of code might also apply to NeXT. -LynX */ + res = 1; + + if (ioctl (fd, FIONBIO, &res) < 0) + sendto_log(ALOG_IRCD, 0, "ioctl(fd,FIONBIO) failed for %s:%u", + ip, port); +#else + if ((res = fcntl(fd, F_GETFL, 0)) == -1) + sendto_log(ALOG_IRCD, 0, "fcntl(fd, F_GETFL) failed for %s:%u", + ip, port); + else if (fcntl(fd, F_SETFL, res | nonb) == -1) + sendto_log(ALOG_IRCD, 0, + "fcntl(fd, F_SETL, nonb) failed for %s:%u", + ip, port); +#endif +} + +/* + * tcp_connect + * + * utility function for use in modules, creates a socket and connects + * it to an IP/port + * + * Returns the fd + */ +int +tcp_connect(ourIP, theirIP, port, error) +char *ourIP, *theirIP, **error; +u_short port; +{ + int fd; + static char errbuf[BUFSIZ]; + struct SOCKADDR_IN sk; + + fd = socket(AFINET, SOCK_STREAM, 0); + if (fd < 0) + { + sprintf(errbuf, "socket() failed: %s", strerror(errno)); + *error = errbuf; + return -1; + } + /* + * this bzero() shouldn't be needed.. should it? + * AIX 4.1.5 doesn't like not having it tho.. I have no clue why -kalt + */ + bzero((char *)&sk, sizeof(sk)); + sk.SIN_FAMILY = AFINET; +#if defined(INET6) + if(!inet_pton(AF_INET6, ourIP, sk.sin6_addr.s6_addr)) + bcopy(minus_one, sk.sin6_addr.s6_addr, IN6ADDRSZ); +#else + sk.sin_addr.s_addr = inetaddr(ourIP); +#endif + sk.SIN_PORT = htons(0); + if (bind(fd, (SAP)&sk, sizeof(sk)) < 0) + { + sprintf(errbuf, "bind() failed: %s", strerror(errno)); + *error = errbuf; + close(fd); + return -1; + } + set_non_blocking(fd, theirIP, port); +#if defined(INET6) + if(!inet_pton(AF_INET6, theirIP, sk.sin6_addr.s6_addr)) + bcopy(minus_one, sk.sin6_addr.s6_addr, IN6ADDRSZ); +#else + sk.sin_addr.s_addr = inetaddr(theirIP); +#endif + sk.SIN_PORT = htons(port); + if (connect(fd, (SAP)&sk, sizeof(sk)) < 0 && errno != EINPROGRESS) + { + sprintf(errbuf, "connect() to %s %u failed: %s", theirIP, port, + strerror(errno)); + *error = errbuf; + close(fd); + return -1; + } + *error = NULL; + return fd; +} diff --git a/iauth/a_io_ext.h b/iauth/a_io_ext.h new file mode 100644 index 0000000..12ec257 --- /dev/null +++ b/iauth/a_io_ext.h @@ -0,0 +1,50 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_io_ext.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/a_io.c. + */ + +/* External definitions for global variables. + */ +#ifndef A_IO_C +extern anAuthData cldata[MAXCONNECTIONS]; +#endif /* A_IO_C */ + +/* External definitions for global functions. + */ +#ifndef A_IO_C +#define EXTERN extern +#else /* A_IO_C */ +#define EXTERN +#endif /* A_IO_C */ + +EXTERN void io_init(); +#if ! USE_STDARG +EXTERN void sendto_ircd(); +#else /* USE_STDARG */ +EXTERN void vsendto_ircd (char *, va_list); +EXTERN void sendto_ircd (char *, ...); +#endif +EXTERN void init_io __P(()); +EXTERN void loop_io __P(()); +EXTERN int tcp_connect __P((char *, char *, u_short, char **)); + +/* __P(()) */ +#undef EXTERN diff --git a/iauth/a_log.c b/iauth/a_log.c new file mode 100644 index 0000000..5307cfe --- /dev/null +++ b/iauth/a_log.c @@ -0,0 +1,123 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_log.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: a_log.c,v 1.6 1999/02/21 00:33:45 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define A_LOG_C +#include "a_externs.h" +#undef A_LOG_C + +static FILE *debug = NULL, *authlog = NULL; + +void +init_filelogs() +{ +#if defined(IAUTH_DEBUG) + if (debug) + fclose(debug); + debug = fopen(IAUTHDBG_PATH, "w"); +# if defined(USE_SYSLOG) + if (!debug) + syslog(LOG_ERR, "Failed to open \"%s\" for writing", + IAUTHDBG_PATH); +# endif +#endif /* IAUTH_DEBUG */ + if (authlog) + fclose(authlog); + authlog = fopen(FNAME_AUTHLOG, "a"); +#if defined(USE_SYSLOG) + if (!authlog) + syslog(LOG_NOTICE, "Failed to open \"%s\" for writing", + FNAME_AUTHLOG); +#endif +} + +void +init_syslog() +{ +#if defined(USE_SYSLOG) + openlog("iauth", LOG_PID|LOG_NDELAY, LOG_FACILITY); +#endif +} + +#if ! USE_STDARG +void +sendto_log(flags, slflag, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10) +int flags, slflag; +char *pattern, *p1, *p2, *p3, *p4, *p5, *p6, *p7, *p8, *p9, *p10; +#else +void +vsendto_log(int flags, int slflag, char *pattern, va_list va) +#endif +{ + char logbuf[4096]; + + logbuf[0] = '>'; +#if ! USE_STDARG + sprintf(logbuf+1, pattern, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10); +#else + vsprintf(logbuf+1, pattern, va); +#endif + +#if defined(USE_SYSLOG) + if (slflag) + syslog(slflag, logbuf+1); +#endif + + strcat(logbuf, "\n"); + +#if defined(IAUTH_DEBUG) + if ((flags & ALOG_DALL) && (flags & debuglevel) && debug) + { + fprintf(debug, logbuf+1); + fflush(debug); + } +#endif + if (authlog && (flags & ALOG_FLOG)) + { + fprintf(authlog, "%s: %s", myctime(time(NULL)), logbuf+1); + fflush(authlog); + } + if (flags & ALOG_IRCD) + { + write(0, logbuf, strlen(logbuf)); +#if defined(IAUTH_DEBUG) + if ((ALOG_DSPY & debuglevel) && debug) + { + fprintf(debug, "To ircd: %s", logbuf+1); + fflush(debug); + } +#endif + } +} + +#if USE_STDARG +void +sendto_log(int flags, int slflag, char *pattern, ...) +{ + va_list va; + va_start(va, pattern); + vsendto_log(flags, slflag, pattern, va); + va_end(va); +} +#endif diff --git a/iauth/a_log_def.h b/iauth/a_log_def.h new file mode 100644 index 0000000..e50df0b --- /dev/null +++ b/iauth/a_log_def.h @@ -0,0 +1,41 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_log_def.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +#if defined(IAUTH_DEBUG) +# define DebugLog(x) sendto_log x +#else +# define DebugLog(x) ; +#endif + +#define ALOG_FLOG 0x01 /* file log */ +#define ALOG_IRCD 0x02 /* notice sent to ircd (then sent to &AUTH) */ + +#define ALOG_DCONF 0x000100 /* debug: configuration file */ +#define ALOG_DMISC 0x000200 /* debug: misc stuff */ +#define ALOG_DIO 0x000400 /* debug: IO stuff */ +#define ALOG_DSPY 0x001000 /* debug: show ircd stream */ +#define ALOG_DIRCD 0x002000 /* debug: errors reported by ircd */ + +#define ALOG_D931 0x010000 /* debug: module rfc931 */ +#define ALOG_DSOCKS 0x020000 /* debug: module socks */ +#define ALOG_DSOCKSC 0x040000 /* debug: module socks cache */ +#define ALOG_DPIPE 0x080000 /* debug: module pipe */ +#define ALOG_DLHEX 0x100000 /* debug: module pipe */ + +#define ALOG_DALL 0x1F3700 /* any debug flag */ diff --git a/iauth/a_log_ext.h b/iauth/a_log_ext.h new file mode 100644 index 0000000..d5887a0 --- /dev/null +++ b/iauth/a_log_ext.h @@ -0,0 +1,46 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_log_ext.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/a_log.c. + */ + +/* External definitions for global variables. + */ +#ifndef A_LOG_C +#endif /* A_LOG_C */ + +/* External definitions for global functions. + */ +#ifndef A_LOG_C +# define EXTERN extern +#else /* A_LOG_C */ +# define EXTERN +#endif /* A_LOG_C */ + +EXTERN void init_filelogs(); +EXTERN void init_syslog(); +#if ! USE_STDARG +EXTERN void sendto_log(); +#else /* USE_STDARG */ +EXTERN void vsendto_log (int, int, char *, va_list); +EXTERN void sendto_log (int, int, char *, ...); +#endif /* USE_STDARG */ + +#undef EXTERN diff --git a/iauth/a_struct_def.h b/iauth/a_struct_def.h new file mode 100644 index 0000000..d71b166 --- /dev/null +++ b/iauth/a_struct_def.h @@ -0,0 +1,72 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/a_struct_def.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + + +typedef struct AuthData anAuthData; + +#define INBUFSIZE 4096 /* I/O buffer size */ +#define MAXI 16 /* maximum number of instances */ +#define BDSIZE ((MAXI + 7) / 8) /* bit data size */ + +struct AuthData +{ + /* the following are set by a_io.c and may be read by modules */ + char user[USERLEN+1]; /* username */ + char passwd[PASSWDLEN+1]; /* password */ + char host[HOSTLEN+1]; /* hostname */ + char itsip[HOSTLEN+1]; /* client ip */ + u_short itsport; /* client port */ + char ourip[HOSTLEN+1]; /* our ip */ + u_short ourport; /* our port */ + u_int state; /* state (general) */ + + /* the following are set by modules */ + char *authuser; /* authenticated username */ + u_char authfrom; /* where we got authuser from */ + + /* the following are for use by a_io.c only */ + char idone[BDSIZE]; /* keeping track of instances' work */ + u_char ileft; /* time saver, anything left? */ + + /* the following are shared by a_io.c & modules */ + char *inbuffer; /* input buffer */ + u_int buflen; /* length of data in buffer */ + int rfd, wfd; /* fd's */ + AnInstance *instance; /* the module instanciation working */ + u_int mod_status; /* used by the module only! */ + time_t timeout; /* timeout */ +}; + +#define A_ACTIVE 0x0001 /* entry is active */ +#define A_START 0x0002 /* go through modules from beginning */ +#define A_DONE 0x0004 /* nothing left to be done */ +#define A_IGNORE 0x0010 /* ignore subsequent messages from ircd */ +#define A_LATE 0x0080 /* ircd is no longer waiting for a reply */ + +#define A_GOTU 0x0100 /* got username (from ircd) */ +#define A_GOTP 0x0200 /* got password (from ircd) */ +#define A_GOTH 0x0400 /* got hostname (from ircd) */ +#define A_NOH 0x0800 /* no hostname available */ + +#define A_UNIX 0x1000 /* authuser is suitable for use by ircd */ +#define A_DENY 0x8000 /* connection should be denied access */ + +#define SetBit(v,n) v[n/8] |= (1 << (n % 8)) +#define UnsetBit(v,n) v[n/8] &= ~(1 << (n % 8)) +#define CheckBit(v,n) (v[n/8] & (1 << (n % 8))) diff --git a/iauth/iauth.c b/iauth/iauth.c new file mode 100644 index 0000000..395be72 --- /dev/null +++ b/iauth/iauth.c @@ -0,0 +1,226 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/iauthd.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: iauth.c,v 1.11 1999/06/17 01:22:20 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define IAUTH_C +#include "a_externs.h" +#undef IAUTH_C + +static int do_log = 0; + +RETSIGTYPE dummy(s) +int s; +{ + /* from common/bsd.c */ +#ifndef HAVE_RELIABLE_SIGNALS + (void)signal(SIGALRM, dummy); + (void)signal(SIGPIPE, dummy); +# ifndef HPUX /* Only 9k/800 series require this, but don't know how to.. */ +# ifdef SIGWINCH + (void)signal(SIGWINCH, dummy); +# endif +# endif +#else +# if POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = dummy; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGALRM); + (void)sigaddset(&act.sa_mask, SIGPIPE); +# ifdef SIGWINCH + (void)sigaddset(&act.sa_mask, SIGWINCH); +# endif + (void)sigaction(SIGALRM, &act, (struct sigaction *)NULL); + (void)sigaction(SIGPIPE, &act, (struct sigaction *)NULL); +# ifdef SIGWINCH + (void)sigaction(SIGWINCH, &act, (struct sigaction *)NULL); +# endif +# endif +#endif +} + +RETSIGTYPE s_log(s) +int s; +{ +# if POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = s_log; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGUSR2); + (void)sigaction(SIGUSR2, &act, NULL); +# else + (void)signal(SIGUSR2, s_log); +# endif + do_log = 1; +} + +void +init_signals() +{ + /* from ircd/ircd.c setup_signals() */ +#if POSIX_SIGNALS + struct sigaction act; + + act.sa_handler = SIG_IGN; + act.sa_flags = 0; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGPIPE); + (void)sigaddset(&act.sa_mask, SIGALRM); +# ifdef SIGWINCH + (void)sigaddset(&act.sa_mask, SIGWINCH); + (void)sigaction(SIGWINCH, &act, NULL); +# endif + (void)sigaction(SIGPIPE, &act, NULL); + act.sa_handler = dummy; + (void)sigaction(SIGALRM, &act, NULL); +/* + act.sa_handler = s_rehash; + (void)sigemptyset(&act.sa_mask); + (void)sigaddset(&act.sa_mask, SIGHUP); + (void)sigaction(SIGHUP, &act, NULL); + act.sa_handler = s_restart; + (void)sigaddset(&act.sa_mask, SIGINT); + (void)sigaction(SIGINT, &act, NULL); + act.sa_handler = s_die; + (void)sigaddset(&act.sa_mask, SIGTERM); + (void)sigaction(SIGTERM, &act, NULL); +*/ + act.sa_handler = s_log; + (void)sigaddset(&act.sa_mask, SIGUSR2); + (void)sigaction(SIGUSR2, &act, NULL); +#else +# ifndef HAVE_RELIABLE_SIGNALS + (void)signal(SIGPIPE, dummy); +# ifdef SIGWINCH + (void)signal(SIGWINCH, dummy); +# endif +# else +# ifdef SIGWINCH + (void)signal(SIGWINCH, SIG_IGN); +# endif + (void)signal(SIGPIPE, SIG_IGN); +# endif + (void)signal(SIGALRM, dummy); +/* + (void)signal(SIGHUP, s_rehash); + (void)signal(SIGTERM, s_die); + (void)signal(SIGINT, s_restart); +*/ + (void)signal(SIGUSR2, s_log); +#endif +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + time_t nextst = time(NULL) + 90; + char *xopt; + + if (argc == 2 && !strcmp(argv[1], "-X")) + exit(0); + + if (isatty(0)) + { + (void)printf("iauth %s", make_version()); +#if defined(USE_DSM) + (void)printf(" (with DSM support)\n"); +#else + (void)printf("\n"); +#endif + if (argc == 3 && !strcmp(argv[1], "-c")) + { + (void)printf("\nReading \"%s\"\n\n", argv[2]); + conf_read(argv[2]); + } + else + { +#if defined(INET6) + (void)printf("\t+INET6\n"); +#endif +#if defined(IAUTH_DEBUG) + (void)printf("\t+IAUTH_DEBUG\n"); +#endif +#if defined(USE_POLL) + (void)printf("\t+USE_POLL\n"); +#endif + } + exit(0); + } + + init_signals(); + init_syslog(); + init_filelogs(); + sendto_log(ALOG_DMISC, LOG_NOTICE, "Daemon starting (%s%s).", + make_version(), +#if defined(IAUTH_DEBUG) + "+debug" +#else + "" +#endif + ); + init_io(); + xopt = conf_read(NULL); + sendto_ircd("V %s", make_version()); + sendto_ircd("O %s", xopt); + conf_ircd(); + +#if defined(IAUTH_DEBUG) + if (debuglevel & ALOG_DIRCD) + sendto_ircd("G 1"); + else +#endif + sendto_ircd("G 0"); + + while (1) + { + loop_io(); + + if (do_log) + { + sendto_log(ALOG_IRCD|ALOG_DMISC, LOG_INFO, + "Got SIGUSR2, reinitializing log file(s)."); + init_filelogs(); + do_log = 0; + } + + if (time(NULL) > nextst) + { + AnInstance *itmp = instances; + + sendto_ircd("s"); + while (itmp) + { + if (itmp->mod->stats) + itmp->mod->stats(itmp); + itmp = itmp->nexti; + } + nextst = time(NULL) + 60; + } + } +} diff --git a/iauth/mod_lhex.c b/iauth/mod_lhex.c new file mode 100644 index 0000000..07e7e88 --- /dev/null +++ b/iauth/mod_lhex.c @@ -0,0 +1,318 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_lhex.c + * Copyright (C) 1998-1999 Christophe Kalt and Andrew Snare + * + * 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 mod_lhex.c,v 1.12 1999/02/06 21:43:52 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define MOD_LHEX_C +#include "a_externs.h" +#undef MOD_LHEX_C + +/****************************** PRIVATE *************************************/ +#define LHEXPORT 9674 + +struct lhex_private +{ + /* stats */ + u_int ok, banned; + u_int tried, clean, timeout; +}; + +/******************************** PUBLIC ************************************/ + +/* + * lhex_init + * + * This procedure is called when a particular module is loaded. + * Returns NULL if everything went fine, + * an error message otherwise. + */ +char * +lhex_init(self) +AnInstance *self; +{ + struct lhex_private *mydata; + +#if defined(INET6) + return "IPv6 unsupported."; +#endif + if(self->opt == NULL) + return "Aie! no option(s): no LHEx server to connect to!"; + if(!inetaton(self->opt,NULL)) + return "Aie! Option wasn't a valid IP address!"; + + /* Allocate the module data */ + mydata = (struct lhex_private *) malloc(sizeof(struct lhex_private)); + bzero((char *) mydata, sizeof(struct lhex_private)); + + self->popt = mystrdup(self->opt); + self->data = mydata; + return NULL; +} + +/* + * lhex_release + * + * This procedure is called when a particular module is unloaded. + */ +void +lhex_release(self) +AnInstance *self; +{ + struct lhex_private *mydata = self->data; + free(mydata); + free(self->popt); +} + +/* + * lhex_stats + * + * This procedure is called regularly to update statistics sent to ircd. + */ +void +lhex_stats(self) +AnInstance *self; +{ + struct lhex_private *mydata = self->data; + + sendto_ircd("S lhex ok %u banned %u", mydata->ok, mydata->banned); + sendto_ircd("S lhex tried %u aborted %u / %u", + mydata->tried, mydata->clean, mydata->timeout); +} + +/* + * lhex_start + * + * This procedure is called to start the LHEx check procedure. + * Returns 0 if everything went fine, + * anything else otherwise (nothing to be done, or failure) + * + * It is responsible for sending error messages where appropriate. + * In case of failure, it's responsible for cleaning up (e.g. lhex_clean + * will NOT be called) + */ +int +lhex_start(cl) +u_int cl; +{ + char *error; + int fd; + struct lhex_private *mydata = cldata[cl].instance->data; + + if (cldata[cl].state & A_DENY) + { + /* no point of doing anything */ + DebugLog((ALOG_DLHEX, 0, + "lhex_start(%d): A_DENY already set ", cl)); + return -1; + } + + DebugLog((ALOG_DLHEX, 0, "lhex_start(%d): Connecting to %s", cl, + cldata[cl].instance->opt)); + mydata->tried += 1; + fd= tcp_connect(cldata[cl].ourip, cldata[cl].instance->opt, + LHEXPORT, &error); + if (fd < 0) + { + DebugLog((ALOG_DLHEX, 0, + "lhex_start(%d): tcp_connect() reported %s", + cl, error)); + return -1; + } + + cldata[cl].wfd = fd; /*so that lhex_work() is called when connected*/ + return 0; +} + +/* + * lhex_work + * + * This procedure is called whenever there's new data in the buffer. + * Returns 0 if everything went fine, and there is more work to be done, + * Returns -1 if the module has finished its work (and cleaned up). + * + * It is responsible for sending error messages where appropriate. + */ +int +lhex_work(cl) +u_int cl; +{ + DebugLog((ALOG_DLHEX, 0, "lhex_work(%d): %d %d buflen=%d", cl, + cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen)); + if (cldata[cl].wfd > 0) + { + /* + ** We haven't sent the query yet, the connection was just + ** established. + */ + char query[3+7+6+4+USERLEN+2*HOSTLEN+8+3];/*strlen(atoi(cl))<=8*/ + char *ident = cldata[cl].authuser; + + /* This is part of every request */ + sprintf(query, "id:%u ip:%s", cl, cldata[cl].itsip); + + /* These bits are optional, depending on what's known */ + if (ident) + { + strcat(query, " ident:"); + strcat(query, ident); + } + if (cldata[cl].state & A_GOTH) + { + strcat(query, " host:"); + strcat(query, cldata[cl].host); + } + /* Terminate the request */ + strcat(query, "\r\n"); + + DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): Sending query [%s]", + cl, query)); + if (write(cldata[cl].wfd, query, strlen(query)) < 0) + { + /* most likely the connection failed */ + DebugLog((ALOG_DLHEX, 0, + "lhex_work(%u): write() failed: %s", + cl, strerror(errno))); + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; + return 1; + } + cldata[cl].rfd = cldata[cl].wfd; + cldata[cl].wfd = 0; + } + else + { + /* data's in from the other end */ + char *ch, *nch; + u_int id; + int retval = 0; + + cldata[cl].inbuffer[cldata[cl].buflen] = '\0'; + nch = cldata[cl].inbuffer; + while((nch < (cldata[cl].inbuffer + cldata[cl].buflen)) && + (ch = index(nch, '\r')) && !retval) + { + char *och = nch; + nch = ch+2; /* Skip the \r\n */ + *ch = '\0'; + DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): Got [%s]", + cl, och)); + + /* Have a go at parsing the return info */ + if(sscanf(och, "%u", &id) != 1) + DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): " + "Malformed data!", cl)); + else + { + struct lhex_private *d=cldata[cl].instance->data; + ch = index(och, ':'); + while(isspace(*(++ch))); + if(!strcmp(ch,"OK")) + { + d->ok++; + DebugLog((ALOG_DLHEX, 0, + "lhex_work(%u): OK", id)); + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + retval = -1; + } + else if(!strcmp(ch,"Not OK")) + { + d->banned++; + DebugLog((ALOG_DLHEX, 0, + "lhex_work(%u): Not OK", id)); + cldata[cl].state |= A_DENY; + /* I really wish we could send the + client a "reason" here :P */ + sendto_ircd("K %d %s %u ", cl, + cldata[cl].itsip, + cldata[cl].itsport); + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + retval = -1; + } + else + { +#if 0 + /* Call this info for the client */ + sendto_ircd("I %d %s %u NOTICE AUTH :%s", + cl, cldata[cl].itsip, + cldata[cl].itsport, ch); +#endif + retval = 0; + } + } + } + return retval; + } + return 0; +} + +/* + * lhex_clean + * + * This procedure is called whenever the module should interrupt its work. + * It is responsible for cleaning up any allocated data, and in particular + * closing file descriptors. + */ +void +lhex_clean(cl) +u_int cl; +{ + struct lhex_private *mydata = cldata[cl].instance->data; + + mydata->clean += 1; + DebugLog((ALOG_DLHEX, 0, "lhex_clean(%d): cleaning up", cl)); + /* + ** only one of rfd and wfd may be set at the same time, + ** in any case, they would be the same fd, so only close() once + */ + if (cldata[cl].rfd) + close(cldata[cl].rfd); + else if (cldata[cl].wfd) + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; +} + +/* + * lhex_timeout + * + * This procedure is called whenever the timeout set by the module is + * reached. + * + * Returns 0 if things are okay, -1 if check was aborted. + */ +int +lhex_timeout(cl) +u_int cl; +{ + struct lhex_private *mydata = cldata[cl].instance->data; + + mydata->timeout += 1; + DebugLog((ALOG_DLHEX, 0, "lhex_timeout(%d): calling lhex_clean ", cl)); + lhex_clean(cl); + return -1; +} + +aModule Module_lhex = + { "lhex", lhex_init, lhex_release, lhex_stats, + lhex_start, lhex_work, lhex_timeout, lhex_clean }; diff --git a/iauth/mod_lhex_ext.h b/iauth/mod_lhex_ext.h new file mode 100644 index 0000000..7777ca1 --- /dev/null +++ b/iauth/mod_lhex_ext.h @@ -0,0 +1,28 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_lhex_ext.h + * Copyright (C) 1998-1999 Christophe Kalt and Andrew Snare + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/mod_lhex.c. + */ + +/* External definitions for global variables. + */ +#ifndef MOD_LHEX_C +extern aModule Module_lhex; +#endif /* MOD_LHEX_C */ diff --git a/iauth/mod_pipe.c b/iauth/mod_pipe.c new file mode 100644 index 0000000..df41157 --- /dev/null +++ b/iauth/mod_pipe.c @@ -0,0 +1,217 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_pipe.c + * Copyright (C) 1999 Christophe Kalt + * + * 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: mod_pipe.c,v 1.3 1999/03/11 19:53:20 kalt Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define MOD_PIPE_C +#include "a_externs.h" +#undef MOD_PIPE_C + +/* + * pipe_init + * + * This procedure is called when a particular module is loaded. + * Returns NULL if everything went fine, + * an error message otherwise. + */ +char * +pipe_init(self) +AnInstance *self; +{ + if (self->opt == NULL) + return "Aie! no option(s): nothing to be done!"; + if (strncasecmp(self->opt, "prog=", 5)) + return "Aie! unknown option(s): nothing to be done!"; + self->popt = self->opt + 5; + return self->popt; +} + +/* + * pipe_release + * + * This procedure is called when a particular module is unloaded. +void +pipe_release(self) +AnInstance *self; +{ +} + */ + +/* + * pipe_stats + * + * This procedure is called regularly to update statistics sent to ircd. +void +pipe_stats(self) +AnInstance *self; +{ +} + */ + +/* + * pipe_start + * + * This procedure is called to start an authentication. + * Returns 0 if everything went fine, + * -1 otherwise (nothing to be done, or failure) + * + * It is responsible for sending error messages where appropriate. + * In case of failure, it's responsible for cleaning up (e.g. pipe_clean + * will NOT be called) + */ +int +pipe_start(cl) +u_int cl; +{ + int pp[2], rc; + + DebugLog((ALOG_DPIPE, 0, "pipe_start(%d): Forking for %s %u", cl, + cldata[cl].itsip, cldata[cl].itsport)); + if (pipe(pp) == -1) + { + DebugLog((ALOG_DPIPE, 0, + "pipe_start(%d): Error creating pipe: %s", + cl, strerror(errno))); + return -1; + } + switch (rc = vfork()) + { + case -1 : + DebugLog((ALOG_DPIPE, 0, + "pipe_start(%d): Error forking: %s", + cl, strerror(errno))); + return -1; + case 0 : + { + (void)close(pp[0]); + for (rc = 2; rc < MAXCONNECTIONS; rc++) + if (rc != pp[1]) + (void)close(rc); + if (pp[1] != 2) + (void)dup2(pp[1], 2); + (void)dup2(2, 1); + if (pp[1] != 2 && pp[1] != 1) + (void)close(pp[1]); + (void)execlp(cldata[cl].instance->popt, + cldata[cl].instance->popt, + cldata[cl].itsip, cldata[cl].itsport); + _exit(-1); + } + default : + (void)close(pp[1]); + break; + } + + cldata[cl].rfd = pp[0]; + return 0; +} + +/* + * pipe_work + * + * This procedure is called whenever there's new data in the buffer. + * Returns 0 if everything went fine, and there is more work to be done, + * Returns -1 if the module has finished its work (and cleaned up). + * + * It is responsible for sending error messages where appropriate. + */ +int +pipe_work(cl) +u_int cl; +{ + DebugLog((ALOG_DPIPE, 0, "pipe_work(%d): %d %d buflen=%d %c", cl, + cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen, + cldata[cl].inbuffer[0])); + + switch (cldata[cl].inbuffer[0]) + { + case 'Y': + break; + case 'N': + cldata[cl].state |= A_DENY; + sendto_ircd("K %d %s %u ", cl, cldata[cl].itsip, + cldata[cl].itsport); + break; +#if 0 + /* hm.. need deeper mods to ircd */ + case 'y': + /* restricted connection only */ + cldata[cl].state |= A_RESTRICT; + sendto_ircd("k %d %s %u ", cl, cldata[cl].itsip, + cldata[cl].itsport); + break; +#endif + default : + /* error */ + sendto_log(ALOG_FLOG|ALOG_IRCD, LOG_WARNING, + "pipe: unexpected %c for %s[%s]", + cldata[cl].inbuffer[0], + cldata[cl].host, + cldata[cl].itsip); + break; + } + + /* We're done */ + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + return -1; +} + +/* + * pipe_clean + * + * This procedure is called whenever the module should interrupt its work. + * It is responsible for cleaning up any allocated data, and in particular + * closing file descriptors. + */ +void +pipe_clean(cl) +u_int cl; +{ + DebugLog((ALOG_DPIPE, 0, "pipe_clean(%d): cleaning up", cl)); + if (cldata[cl].rfd) + close(cldata[cl].rfd); + cldata[cl].rfd = 0; +} + +/* + * pipe_timeout + * + * This procedure is called whenever the timeout set by the module is + * reached. + * + * Returns 0 if things are okay, -1 if authentication was aborted. + */ +int +pipe_timeout(cl) +u_int cl; +{ + DebugLog((ALOG_DPIPE, 0, "pipe_timeout(%d): calling pipe_clean ", + cl)); + pipe_clean(cl); + return -1; +} + +aModule Module_pipe = + { "pipe", pipe_init, NULL, NULL, + pipe_start, pipe_work, pipe_timeout, pipe_clean }; diff --git a/iauth/mod_pipe_ext.h b/iauth/mod_pipe_ext.h new file mode 100644 index 0000000..9012250 --- /dev/null +++ b/iauth/mod_pipe_ext.h @@ -0,0 +1,28 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_pipe_ext.h + * Copyright (C) 1999 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/mod_pipe.c. + */ + +/* External definitions for global variables. + */ +#ifndef MOD_PIPE_C +extern aModule Module_pipe; +#endif /* MOD_PIPE_C */ diff --git a/iauth/mod_rfc931.c b/iauth/mod_rfc931.c new file mode 100644 index 0000000..80e5292 --- /dev/null +++ b/iauth/mod_rfc931.c @@ -0,0 +1,368 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_rfc931.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: mod_rfc931.c,v 1.16 1999/07/11 20:56:25 chopin Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define MOD_RFC931_C +#include "a_externs.h" +#undef MOD_RFC931_C + +#define OPT_PROTOCOL 0x1 +#define OPT_LAZY 0x2 + +struct _data +{ + u_char options; + u_int tried; + u_int connected; + u_int unx; + u_int other; + u_int bad; + u_int skipped; + u_int clean, timeout; +}; + +/* + * rfc931_init + * + * This procedure is called when a particular module is loaded. + * Returns NULL if everything went fine, + * an error message otherwise. + */ +char * +rfc931_init(self) +AnInstance *self; +{ + struct _data *dt; + + dt = (struct _data *) malloc(sizeof(struct _data)); + bzero((char *) dt, sizeof(struct _data)); + self->data = (void *) dt; + + /* undocumented option */ + if (self->opt && strstr(self->opt, "protocol")) + dt->options |= OPT_PROTOCOL; + if (self->opt && strstr(self->opt, "lazy")) + dt->options |= OPT_LAZY; + + if (dt->options & (OPT_LAZY|OPT_PROTOCOL)) + self->popt = "protocol,lazy"; + else if (dt->options & OPT_LAZY) + self->popt = "lazy"; + else if (dt->options & OPT_PROTOCOL) + self->popt = "protocol"; + else + return NULL; + return self->popt; +} + +/* + * rfc931_release + * + * This procedure is called when a particular module is unloaded. + */ +void +rfc931_release(self) +AnInstance *self; +{ + struct _data *st = self->data; + free(st); +} + +/* + * rfc931_stats + * + * This procedure is called regularly to update statistics sent to ircd. + */ +void +rfc931_stats(self) +AnInstance *self; +{ + struct _data *st = self->data; + + sendto_ircd("S rfc931 connected %u unix %u other %u bad %u out of %u", + st->connected, st->unx, st->other, st->bad, st->tried); + sendto_ircd("S rfc931 skipped %u aborted %u / %u", + st->skipped, st->clean, st->timeout); +} + +/* + * rfc931_start + * + * This procedure is called to start an authentication. + * Returns 0 if everything went fine, + * -1 else otherwise (nothing to be done, or failure) + * + * It is responsible for sending error messages where appropriate. + * In case of failure, it's responsible for cleaning up (e.g. rfc931_clean + * will NOT be called) + */ +int +rfc931_start(cl) +u_int cl; +{ + char *error; + int fd; + struct _data *st = cldata[cl].instance->data; + + if (st->options & OPT_LAZY && cldata[cl].state & A_DENY) + { + DebugLog((ALOG_D931, 0, "rfc931_start(%d): Lazy.", cl)); + return -1; + } + if (cldata[cl].authuser && + cldata[cl].authfrom < cldata[cl].instance->in) + { + DebugLog((ALOG_D931, 0, + "rfc931_start(%d): Instance %d already got the info", + cl, cldata[cl].authfrom)); + return -1; + } + DebugLog((ALOG_D931, 0, "rfc931_start(%d): Connecting to %s %u", cl, + cldata[cl].itsip, 113)); + st->tried += 1; + fd = tcp_connect(cldata[cl].ourip, cldata[cl].itsip, 113, &error); + if (fd < 0) + { + DebugLog((ALOG_D931, 0, + "rfc931_start(%d): tcp_connect() reported %s", + cl, error)); + return -1; + } + + cldata[cl].wfd = fd; /*so that rfc931_work() is called when connected*/ + return 0; +} + +/* + * rfc931_work + * + * This procedure is called whenever there's new data in the buffer. + * Returns 0 if everything went fine, and there is more work to be done, + * Returns -1 if the module has finished its work (and cleaned up). + * + * It is responsible for sending error messages where appropriate. + */ +int +rfc931_work(cl) +u_int cl; +{ + struct _data *st = cldata[cl].instance->data; + + DebugLog((ALOG_D931, 0, "rfc931_work(%d): %d %d buflen=%d", cl, + cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen)); + if (cldata[cl].wfd > 0) + { + /* + ** We haven't sent the query yet, the connection was just + ** established. + */ + char query[32]; + + sprintf(query, "%u , %u\r\n", cldata[cl].itsport, + cldata[cl].ourport); + if (write(cldata[cl].wfd, query, strlen(query)) < 0) + { + /* most likely the connection failed */ + DebugLog((ALOG_D931, 0, + "rfc931_work(%d): write() failed: %s", cl, + strerror(errno))); + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; + return 1; + } + else + st->connected += 1; + cldata[cl].rfd = cldata[cl].wfd; + cldata[cl].wfd = 0; + } + else + { + /* data's in from the ident server */ + char *ch; + u_char bad = 0; + + cldata[cl].inbuffer[cldata[cl].buflen] = '\0'; + ch = index(cldata[cl].inbuffer, '\r'); + if (ch) + { + /* got all of it! */ + *ch = '\0'; + DebugLog((ALOG_D931, 0, "rfc931_work(%d): Got [%s]", + cl, cldata[cl].inbuffer)); + if (cldata[cl].buflen > 1024) + cldata[cl].inbuffer[1024] = '\0'; + if (ch = index(cldata[cl].inbuffer, '\n')) + /* delimiter for ircd<->iauth messages. */ + *ch = '\0'; + ch = cldata[cl].inbuffer; + while (*ch && !isdigit(*ch)) ch++; + if (!*ch || atoi(ch) != cldata[cl].itsport) + { + DebugLog((ALOG_D931, 0, + "remote port mismatch.")); + ch = NULL; + } + while (ch && *ch && *ch != ',') ch++; + while (ch && *ch && !isdigit(*ch)) ch++; + if (ch && (!*ch || atoi(ch) != cldata[cl].ourport)) + { + DebugLog((ALOG_D931, 0, + "local port mismatch.")); + ch = NULL; + } + if (ch) ch = index(ch, ':'); + if (ch) ch += 1; + while (ch && *ch && *ch == ' ') ch++; + if (ch && strncmp(ch, "USERID", 6)) + { + DebugLog((ALOG_D931, 0, "No USERID.")); + ch = NULL; + } + if (ch) ch = index(ch, ':'); + if (ch) ch += 1; + while (ch && *ch && *ch == ' ') ch++; + if (ch) + { + int other = 0; + + if (!strncmp(ch, "OTHER", 5)) + other = 1; + ch = rindex(ch, ':'); + if (ch) ch += 1; + while (ch && *ch && *ch == ' ') ch++; + if (ch && *ch) + { + if (cldata[cl].authuser) + free(cldata[cl].authuser); + cldata[cl].authuser = mystrdup(ch); + cldata[cl].authfrom = + cldata[cl].instance->in; + if (other) + st->other += 1; + else + { + st->unx += 1; + cldata[cl].state |= A_UNIX; + } + sendto_ircd("%c %d %s %u %s", + (other) ? 'u' : 'U', cl, + cldata[cl].itsip, + cldata[cl].itsport, + cldata[cl].authuser); + } + else + bad = 1; + } + else + bad = 1; + if (bad) + { + st->bad += 1; + + if (st->options & OPT_PROTOCOL) + { + ch = cldata[cl].inbuffer; + while (*ch) + { + if (!(isalnum(*ch) || + ispunct(*ch) || + isspace(*ch))) + break; + ch += 1; + } + *ch = '\0'; + sendto_log(ALOG_IRCD|ALOG_FLOG, + LOG_WARNING, + "rfc931: bad reply from %s[%s] to \"%u, %u\": %u, \"%s\"", + cldata[cl].host, + cldata[cl].itsip, + cldata[cl].itsport, + cldata[cl].ourport, + cldata[cl].buflen, + cldata[cl].inbuffer); + } + } + /* + ** In any case, our job is done, let's cleanup. + */ + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + return -1; + } + else + return 0; + } + return 0; +} + +/* + * rfc931_clean + * + * This procedure is called whenever the module should interrupt its work. + * It is responsible for cleaning up any allocated data, and in particular + * closing file descriptors. + */ +void +rfc931_clean(cl) +u_int cl; +{ + struct _data *st = cldata[cl].instance->data; + + st->clean += 1; + DebugLog((ALOG_D931, 0, "rfc931_clean(%d): cleaning up", cl)); + /* + ** only one of rfd and wfd may be set at the same time, + ** in any case, they would be the same fd, so only close() once + */ + if (cldata[cl].rfd) + close(cldata[cl].rfd); + else if (cldata[cl].wfd) + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; +} + +/* + * rfc931_timeout + * + * This procedure is called whenever the timeout set by the module is + * reached. + * + * Returns 0 if things are okay, -1 if authentication was aborted. + */ +int +rfc931_timeout(cl) +u_int cl; +{ + struct _data *st = cldata[cl].instance->data; + + st->timeout += 1; + DebugLog((ALOG_D931, 0, "rfc931_timeout(%d): calling rfc931_clean ", + cl)); + rfc931_clean(cl); + return -1; +} + +aModule Module_rfc931 = + { "rfc931", rfc931_init, rfc931_release, rfc931_stats, + rfc931_start, rfc931_work, rfc931_timeout, rfc931_clean }; diff --git a/iauth/mod_rfc931_ext.h b/iauth/mod_rfc931_ext.h new file mode 100644 index 0000000..b37892e --- /dev/null +++ b/iauth/mod_rfc931_ext.h @@ -0,0 +1,45 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_rfc931_ext.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/mod_rfc931.c. + */ + +/* External definitions for global variables. + */ +#ifndef MOD_RFC931_C +extern aModule Module_rfc931; +#endif /* MOD_RFC931_C */ + +/* External definitions for global functions. + */ +#ifndef MOD_RFC931_C +# define EXTERN extern +#else /* MOD_RFC931_C */ +# define EXTERN +#endif /* MOD_RFC931_C */ + +/* +EXTERN int rfc931_start __P((u_int)); +EXTERN int rfc931_work __P((u_int)); +EXTERN int rfc931_timeout __P((u_int)); +EXTERN void rfc931_clean __P((u_int)); +*/ + +#undef EXTERN diff --git a/iauth/mod_socks.c b/iauth/mod_socks.c new file mode 100644 index 0000000..94742c4 --- /dev/null +++ b/iauth/mod_socks.c @@ -0,0 +1,632 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_socks.c + * Copyright (C) 1998 Christophe Kalt + * + * 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: mod_socks.c,v 1.25 1999/08/13 21:06:30 chopin Exp $"; +#endif + +#include "os.h" +#include "a_defines.h" +#define MOD_SOCKS_C +#include "a_externs.h" +#undef MOD_SOCKS_C + +/****************************** PRIVATE *************************************/ + +#define CACHETIME 30 + +#define SOCKSPORT 1080 + +struct proxylog +{ + struct proxylog *next; + char ip[HOSTLEN+1]; + u_char state; /* 0 = no proxy, 1 = open proxy, 2 = closed proxy */ + time_t expire; +}; + +#define OPT_LOG 0x001 +#define OPT_DENY 0x002 +#define OPT_PARANOID 0x004 +#define OPT_CAREFUL 0x008 +#define OPT_V4ONLY 0x010 +#define OPT_V5ONLY 0x020 +#define OPT_PROTOCOL 0x100 + +#define PROXY_NONE 0 +#define PROXY_OPEN 1 +#define PROXY_CLOSE 2 +#define PROXY_UNEXPECTED 3 +#define PROXY_BADPROTO 4 + +#define ST_V4 0x01 +#define ST_V5 0x02 +#define ST_V5b 0x04 + +struct socks_private +{ + struct proxylog *cache; + u_int lifetime; + u_char options; + /* stats */ + u_int chitc, chito, chitn, cmiss, cnow, cmax; + u_int closed4, closed5, open4, open5, noprox; + u_int closed5b, open5b; +}; + +/* + * socks_open_proxy + * + * Found an open proxy for cl: deal with it! + */ +static void +socks_open_proxy(cl, strver) +int cl; +char *strver; +{ + struct socks_private *mydata = cldata[cl].instance->data; + + /* open proxy */ + if (mydata->options & OPT_DENY) + { + cldata[cl].state |= A_DENY; + sendto_ircd("k %d %s %u ", cl, cldata[cl].itsip, + cldata[cl].itsport); + } + if (mydata->options & OPT_LOG) + sendto_log(ALOG_FLOG, LOG_INFO, "socks%s: open proxy: %s[%s]", + strver, cldata[cl].host, cldata[cl].itsip); +} + +/* + * socks_add_cache + * + * Add an entry to the cache. + */ +static void +socks_add_cache(cl, state) +int cl, state; +{ + struct socks_private *mydata = cldata[cl].instance->data; + struct proxylog *next; + + if (state == PROXY_OPEN) + if (cldata[cl].mod_status == ST_V4) + mydata->open4 += 1; + else if (cldata[cl].mod_status == ST_V5) + mydata->open5 += 1; + else + mydata->open5b += 1; + else if (state == PROXY_NONE) + mydata->noprox += 1; + else /* state == PROXY_CLOSE|PROXY_UNEXPECTED|PROXY_BADPROTO */ + if (cldata[cl].mod_status == ST_V4) + mydata->closed4 += 1; + else if (cldata[cl].mod_status == ST_V5) + mydata->closed5 += 1; + else + mydata->closed5b += 1; + + if (mydata->lifetime == 0) + return; + + mydata->cnow += 1; + if (mydata->cnow > mydata->cmax) + mydata->cmax = mydata->cnow; + + next = mydata->cache; + mydata->cache = (struct proxylog *)malloc(sizeof(struct proxylog)); + mydata->cache->expire = time(NULL) + mydata->lifetime; + strcpy(mydata->cache->ip, cldata[cl].itsip); + mydata->cache->state = state; + mydata->cache->next = next; + DebugLog((ALOG_DSOCKSC, 0, "socks_add_cache(%d): new cache %s, open=%d", + cl, mydata->cache->ip, state)); +} + +/* + * socks_check_cache + * + * Check cache for an entry. + */ +static int +socks_check_cache(cl) +{ + struct socks_private *mydata = cldata[cl].instance->data; + struct proxylog **last, *pl; + time_t now = time(NULL); + + if (mydata->lifetime == 0) + return 0; + + DebugLog((ALOG_DSOCKSC, 0, "socks_check_cache(%d): Checking cache for %s", + cl, cldata[cl].itsip)); + + last = &(mydata->cache); + while (pl = *last) + { + DebugLog((ALOG_DSOCKSC, 0, "socks_check_cache(%d): cache %s", + cl, pl->ip)); + if (pl->expire < now) + { + DebugLog((ALOG_DSOCKSC, 0, + "socks_check_cache(%d): free %s (%d < %d)", + cl, pl->ip, pl->expire, now)); + *last = pl->next; + free(pl); + mydata->cnow -= 1; + continue; + } + if (!strcasecmp(pl->ip, cldata[cl].itsip)) + { + DebugLog((ALOG_DSOCKSC, 0, + "socks_check_cache(%d): match (%u)", + cl, pl->state)); + pl->expire = now + mydata->lifetime; /* dubious */ + if (pl->state == 1) + { + socks_open_proxy(cl, "C"); + mydata->chito += 1; + } + else if (pl->state == 0) + mydata->chitn += 1; + else + mydata->chitc += 1; + return -1; + } + last = &(pl->next); + } + mydata->cmiss += 1; + return 0; +} + +static int +socks_write(cl, strver) +u_int cl; +char *strver; +{ + u_char query[13]; /* big enough to hold all queries */ + int query_len = 13; /* lenght of socks4 query */ + u_int a, b, c, d; + + if (sscanf(cldata[cl].ourip, "%u.%u.%u.%u", &a,&b,&c,&d) != 4) + { + sendto_log(ALOG_DSOCKS|ALOG_IRCD, LOG_ERR, + "socks_write%s(%d): sscanf(\"%s\") failed", + strver, cl, cldata[cl].ourip); + close(cldata[cl].wfd); + cldata[cl].wfd = 0; + return -1; + } + if (cldata[cl].mod_status == ST_V4) + { + query[0] = 4; query[1] = 1; + query[2] = ((cldata[cl].ourport & 0xff00) >> 8); + query[3] = (cldata[cl].ourport & 0x00ff); + query[4] = a; query[5] = b; query[6] = c; query[7] = d; + query[8] = 'u'; query[9] = 's'; query[10] = 'e'; query[11] = 'r'; + query[12] = 0; + } + else + { + query[0] = 5; query[1] = 1; query[2] = 0; + query_len = 3; + if (cldata[cl].mod_status == ST_V5b) + { + query_len = 10; + query[3] = 1; + query[4] = a; query[5] = b; query[6] = c; query[7] = d; + query[8] = ((cldata[cl].ourport & 0xff00) >>8); + query[9] = (cldata[cl].ourport & 0x00ff); + } + } + + DebugLog((ALOG_DSOCKS, 0, "socks%s_write(%d): Checking %s %u", + strver, cl, cldata[cl].ourip, SOCKSPORT)); + if (write(cldata[cl].wfd, query, query_len) != query_len) + { + /* most likely the connection failed */ + DebugLog((ALOG_DSOCKS, 0, "socks%s_write(%d): write() failed: %s", + strver, cl, strerror(errno))); + socks_add_cache(cl, PROXY_NONE, 0); + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; + return 1; + } + cldata[cl].rfd = cldata[cl].wfd; + cldata[cl].wfd = 0; + return 0; +} + +static int +socks_read(cl, strver) +u_int cl; +char *strver; +{ + struct socks_private *mydata = cldata[cl].instance->data; + int again = 0; + u_char state = PROXY_CLOSE; + + /* data's in from the other end */ + if (cldata[cl].buflen < 2) + return 0; + + /* got all we need */ + DebugLog((ALOG_DSOCKS, 0, "socks%s_read(%d): Got [%d %d]", strver, cl, + cldata[cl].inbuffer[0], cldata[cl].inbuffer[1])); + + if (cldata[cl].mod_status == ST_V4) + { + if (cldata[cl].inbuffer[0] == 0) + { + if (cldata[cl].inbuffer[1] < 90 || + cldata[cl].inbuffer[1] > 93) + state = PROXY_UNEXPECTED; + else + { + if (cldata[cl].inbuffer[1] == 90) + state = PROXY_OPEN; + else if ((mydata->options & OPT_PARANOID) && + cldata[cl].inbuffer[1] != 91) + state = PROXY_OPEN; + } + } + else + state = PROXY_BADPROTO; + } + else /* ST_V5 or ST_V5b */ + { + if (cldata[cl].inbuffer[0] == 5) + if (cldata[cl].inbuffer[1] == 0) + state = PROXY_OPEN; + else + { + if (cldata[cl].mod_status == ST_V5) + { + if ((u_char)cldata[cl].inbuffer[1] == 4 || + ((u_char)cldata[cl].inbuffer[1] > 9 && + (u_char)cldata[cl].inbuffer[1] != 255)) + state = PROXY_UNEXPECTED; + } + else /* ST_V5b */ + if ((u_char) cldata[cl].inbuffer[1] > 8) + state = PROXY_UNEXPECTED; + else if ((mydata->options&OPT_PARANOID) && + cldata[cl].inbuffer[1] != 2) + state = PROXY_OPEN; + } + else + state = PROXY_BADPROTO; + } + + if (cldata[cl].mod_status == ST_V4 && state != PROXY_OPEN && + !(mydata->options & OPT_V4ONLY)) + { + cldata[cl].mod_status = ST_V5; + cldata[cl].buflen=0; + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + again = 1; + } + + if (cldata[cl].mod_status == ST_V5 && state == PROXY_OPEN && + (mydata->options & OPT_CAREFUL)) + { + cldata[cl].mod_status = ST_V5b; + cldata[cl].buflen=0; + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + again = 1; + } + + if (state == PROXY_OPEN && again == 0) + socks_open_proxy(cl, strver); + + if (state == PROXY_UNEXPECTED) + { + sendto_log(ALOG_FLOG, LOG_WARNING, + "socks%s: unexpected reply: %u,%u %s[%s]", strver, + cldata[cl].inbuffer[0], cldata[cl].inbuffer[1], + cldata[cl].host, cldata[cl].itsip); + sendto_log(ALOG_IRCD, 0, "socks%s: unexpected reply: %u,%u", + strver, cldata[cl].inbuffer[0], + cldata[cl].inbuffer[1]); + state = PROXY_CLOSE; + } + + if (state == PROXY_BADPROTO && (mydata->options & OPT_PROTOCOL)) + { + sendto_log(ALOG_FLOG, LOG_WARNING, + "socks%s: protocol error: %u,%u %s[%s]", strver, + cldata[cl].inbuffer[0], cldata[cl].inbuffer[1], + cldata[cl].host, cldata[cl].itsip); + sendto_log(ALOG_IRCD, 0, "socks%s: protocol error: %u,%u", + strver, cldata[cl].inbuffer[0], + cldata[cl].inbuffer[1]); + state = PROXY_CLOSE; + } + + if (again == 1) + return socks_start(cl); + else + { + socks_add_cache(cl, state); + close(cldata[cl].rfd); + cldata[cl].rfd = 0; + return -1; + } + return 0; +} + +/******************************** PUBLIC ************************************/ + +/* + * socks_init + * + * This procedure is called when a particular module is loaded. + * Returns NULL if everything went fine, + * an error message otherwise. + */ +char * +socks_init(self) +AnInstance *self; +{ + struct socks_private *mydata; + char tmpbuf[80], cbuf[32]; + static char txtbuf[80]; + +#if defined(INET6) + return "IPv6 unsupported."; +#endif + if (self->opt == NULL) + return "Aie! no option(s): nothing to be done!"; + + mydata = (struct socks_private *) malloc(sizeof(struct socks_private)); + bzero((char *) mydata, sizeof(struct socks_private)); + mydata->cache = NULL; + mydata->lifetime = CACHETIME; + + tmpbuf[0] = txtbuf[0] = '\0'; + if (strstr(self->opt, "log")) + { + mydata->options |= OPT_LOG; + strcat(tmpbuf, ",log"); + strcat(txtbuf, ", Log"); + } + if (strstr(self->opt, "reject")) + { + mydata->options |= OPT_DENY; + strcat(tmpbuf, ",reject"); + strcat(txtbuf, ", Reject"); + } + if (strstr(self->opt, "paranoid")) + { + mydata->options |= OPT_PARANOID; + strcat(tmpbuf, ",paranoid"); + strcat(txtbuf, ", Paranoid"); + } + if (strstr(self->opt, "careful")) + { + mydata->options |= OPT_CAREFUL; + strcat(tmpbuf, ",careful"); + strcat(txtbuf, ", Careful"); + } + if (strstr(self->opt, "v4only")) + { + mydata->options |= OPT_V4ONLY; + strcat(tmpbuf, ",v4only"); + strcat(txtbuf, ", V4only"); + } + if (strstr(self->opt, "v5only")) + { + mydata->options |= OPT_V5ONLY; + strcat(tmpbuf, ",v5only"); + strcat(txtbuf, ", V5only"); + } + if (strstr(self->opt, "protocol")) + { + mydata->options |= OPT_PROTOCOL; + strcat(tmpbuf, ",protocol"); + strcat(txtbuf, ", Protocol"); + } + + if (mydata->options == 0) + return "Aie! unknown option(s): nothing to be done!"; + + if (strstr(self->opt, "cache")) + { + char *ch = index(self->opt, '='); + + if (ch) + mydata->lifetime = atoi(ch+1); + } + sprintf(cbuf, ",cache=%d", mydata->lifetime); + strcat(tmpbuf, cbuf); + sprintf(cbuf, ", Cache %d (min)", mydata->lifetime); + strcat(txtbuf, cbuf); + mydata->lifetime *= 60; + + self->popt = mystrdup(tmpbuf+1); + self->data = mydata; + return txtbuf+2; +} + +/* + * socks_release + * + * This procedure is called when a particular module is unloaded. + */ +void +socks_release(self) +AnInstance *self; +{ + struct sock_private *mydata = self->data; + free(mydata); + free(self->popt); +} + +/* + * socks_stats + * + * This procedure is called regularly to update statistics sent to ircd. + */ +void +socks_stats(self) +AnInstance *self; +{ + struct socks_private *mydata = self->data; + + sendto_ircd("S socks open %u/%u/%u closed %u/%u/%u noproxy %u", + mydata->open4, mydata->open5, mydata->open5b, + mydata->closed4, mydata->closed5, mydata->closed5b, + mydata->noprox); + sendto_ircd("S socks cache open %u closed %u noproxy %u miss %u (%u <= %u)", + mydata->chito, mydata->chitc, mydata->chitn, + mydata->cmiss, mydata->cnow, mydata->cmax); +} + +/* + * socks_start + * + * This procedure is called to start the socks check procedure. + * Returns 0 if everything went fine, + * -1 otherwise (nothing to be done, or failure) + * + * It is responsible for sending error messages where appropriate. + * In case of failure, it's responsible for cleaning up (e.g. socks_clean + * will NOT be called) + */ +int +socks_start(cl) +u_int cl; +{ + struct socks_private *mydata = cldata[cl].instance->data; + char *error; + int fd; + + if (cldata[cl].state & A_DENY) + { + /* no point of doing anything */ + DebugLog((ALOG_DSOCKS, 0, + "socks_start(%d): A_DENY alredy set ", cl)); + return -1; + } + + if (socks_check_cache(cl)) + return -1; + + DebugLog((ALOG_DSOCKS, 0, "socks_start(%d): Connecting to %s", cl, + cldata[cl].itsip)); + fd= tcp_connect(cldata[cl].ourip, cldata[cl].itsip, SOCKSPORT, &error); + if (fd < 0) + { + DebugLog((ALOG_DSOCKS, 0, + "socks_start(%d): tcp_connect() reported %s", cl,error)); + socks_add_cache(cl, PROXY_NONE, 0); + return -1; + } + + cldata[cl].wfd = fd; /*so that socks_work() is called when connected*/ + + return 0; +} + +/* + * socks_work + * + * This procedure is called whenever there's new data in the buffer. + * Returns 0 if everything went fine, and there is more work to be done, + * Returns -1 if the module has finished its work (and cleaned up). + * + * It is responsible for sending error messages where appropriate. + */ +int +socks_work(cl) +u_int cl; +{ + char *strver = "4"; + struct socks_private *mydata = cldata[cl].instance->data; + + if (cldata[cl].mod_status == 0) + if (mydata->options & OPT_V5ONLY) + cldata[cl].mod_status = ST_V5; + else + cldata[cl].mod_status = ST_V4; + + if (cldata[cl].mod_status & ST_V5) + strver = "5"; + else if (cldata[cl].mod_status & ST_V5b) + strver = "5b"; + + DebugLog((ALOG_DSOCKS, 0, "socks%s_work(%d): %d %d buflen=%d", strver, + cl, cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen)); + + if (cldata[cl].wfd > 0) + /* + ** We haven't sent the query yet, the connection was just + ** established. + */ + return socks_write(cl, strver); + else + return socks_read(cl, strver); +} + +/* + * socks_clean + * + * This procedure is called whenever the module should interrupt its work. + * It is responsible for cleaning up any allocated data, and in particular + * closing file descriptors. + */ +void +socks_clean(cl) +u_int cl; +{ + DebugLog((ALOG_DSOCKS, 0, "socks_clean(%d): cleaning up", cl)); + /* + ** only one of rfd and wfd may be set at the same time, + ** in any case, they would be the same fd, so only close() once + */ + if (cldata[cl].rfd) + close(cldata[cl].rfd); + else if (cldata[cl].wfd) + close(cldata[cl].wfd); + cldata[cl].rfd = cldata[cl].wfd = 0; +} + +/* + * socks_timeout + * + * This procedure is called whenever the timeout set by the module is + * reached. + * + * Returns 0 if things are okay, -1 if check was aborted. + */ +int +socks_timeout(cl) +u_int cl; +{ + DebugLog((ALOG_DSOCKS, 0, "socks_timeout(%d): calling socks_clean ", cl)); + socks_clean(cl); + return -1; +} + +aModule Module_socks = + { "socks", socks_init, socks_release, socks_stats, + socks_start, socks_work, socks_timeout, socks_clean }; diff --git a/iauth/mod_socks_ext.h b/iauth/mod_socks_ext.h new file mode 100644 index 0000000..9f4df51 --- /dev/null +++ b/iauth/mod_socks_ext.h @@ -0,0 +1,28 @@ +/************************************************************************ + * IRC - Internet Relay Chat, iauth/mod_socks_ext.h + * Copyright (C) 1998 Christophe Kalt + * + * 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. + */ + +/* This file contains external definitions for global variables and functions + defined in iauth/mod_socks.c. + */ + +/* External definitions for global variables. + */ +#ifndef MOD_SOCKS_C +extern aModule Module_socks; +#endif /* MOD_SOCKS_C */ |