aboutsummaryrefslogtreecommitdiff
path: root/ircd/s_bsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'ircd/s_bsd.c')
-rw-r--r--ircd/s_bsd.c3243
1 files changed, 3243 insertions, 0 deletions
diff --git a/ircd/s_bsd.c b/ircd/s_bsd.c
new file mode 100644
index 0000000..f10e459
--- /dev/null
+++ b/ircd/s_bsd.c
@@ -0,0 +1,3243 @@
+/************************************************************************
+ * IRC - Internet Relay Chat, ircd/s_bsd.c
+ * Copyright (C) 1990 Jarkko Oikarinen and
+ * University of Oulu, Computing Center
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 1, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* -- Jto -- 07 Jul 1990
+ * Added jlp@hamblin.byu.edu's debugtty fix
+ */
+
+/* -- Armin -- Jun 18 1990
+ * Added setdtablesize() for more socket connections
+ * (sequent OS Dynix only) -- maybe select()-call must be changed ...
+ */
+
+/* -- Jto -- 13 May 1990
+ * Added several fixes from msa:
+ * Better error messages
+ * Changes in check_access
+ * Added SO_REUSEADDR fix from zessel@informatik.uni-kl.de
+ */
+
+#ifndef lint
+static char rcsid[] = "@(#)$Id: s_bsd.c,v 1.73 1999/07/23 17:15:14 kalt Exp $";
+#endif
+
+#include "os.h"
+#include "s_defines.h"
+#define S_BSD_C
+#include "s_externs.h"
+#undef S_BSD_C
+
+#ifndef IN_LOOPBACKNET
+#define IN_LOOPBACKNET 0x7f
+#endif
+
+aClient *local[MAXCONNECTIONS];
+FdAry fdas, fdall;
+int highest_fd = 0, readcalls = 0, udpfd = -1, resfd = -1, adfd = -1;
+time_t timeofday;
+static struct SOCKADDR_IN mysk;
+static void polludp();
+
+static struct SOCKADDR *connect_inet __P((aConfItem *, aClient *, int *));
+static int completed_connection __P((aClient *));
+static int check_init __P((aClient *, char *));
+static int check_ping __P((char *, int));
+static void do_dns_async __P(());
+static int set_sock_opts __P((int, aClient *));
+#ifdef UNIXPORT
+static struct SOCKADDR *connect_unix __P((aConfItem *, aClient *, int *));
+static void add_unixconnection __P((aClient *, int));
+static char unixpath[256];
+#endif
+static char readbuf[READBUF_SIZE];
+
+#define CFLAG (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER)
+#define NFLAG CONF_NOCONNECT_SERVER
+
+/*
+ * Try and find the correct name to use with getrlimit() for setting the max.
+ * number of files allowed to be open by this process.
+ */
+#ifdef RLIMIT_FDMAX
+# define RLIMIT_FD_MAX RLIMIT_FDMAX
+#else
+# ifdef RLIMIT_NOFILE
+# define RLIMIT_FD_MAX RLIMIT_NOFILE
+# else
+# ifdef RLIMIT_OPEN_MAX
+# define RLIMIT_FD_MAX RLIMIT_OPEN_MAX
+# else
+# undef RLIMIT_FD_MAX
+# endif
+# endif
+#endif
+
+/*
+** add_local_domain()
+** Add the domain to hostname, if it is missing
+** (as suggested by eps@TOASTER.SFSU.EDU)
+*/
+
+void add_local_domain(hname, size)
+char *hname;
+int size;
+{
+#ifdef RES_INIT
+ /* try to fix up unqualified names */
+ if (!index(hname, '.'))
+ {
+ if (!(ircd_res.options & RES_INIT))
+ {
+ Debug((DEBUG_DNS,"ircd_res_init()"));
+ ircd_res_init();
+ }
+ if (ircd_res.defdname[0])
+ {
+ (void)strncat(hname, ".", size-1);
+ (void)strncat(hname, ircd_res.defdname, size-2);
+ }
+ }
+#endif
+ return;
+}
+
+/*
+** Cannot use perror() within daemon. stderr is closed in
+** ircd and cannot be used. And, worse yet, it might have
+** been reassigned to a normal connection...
+*/
+
+/*
+** report_error
+** This a replacement for perror(). Record error to log and
+** also send a copy to all *LOCAL* opers online.
+**
+** text is a *format* string for outputting error. It must
+** contain only two '%s', the first will be replaced
+** by the sockhost from the cptr, and the latter will
+** be taken from sys_errlist[errno].
+**
+** cptr if not NULL, is the *LOCAL* client associated with
+** the error.
+*/
+void report_error(text, cptr)
+char *text;
+aClient *cptr;
+{
+ Reg int errtmp = errno; /* debug may change 'errno' */
+ Reg char *host;
+ int err;
+ SOCK_LEN_TYPE len = sizeof(err);
+ extern char *strerror();
+
+ host = (cptr) ? get_client_name(cptr, FALSE) : "";
+
+ Debug((DEBUG_ERROR, text, host, strerror(errtmp)));
+
+ /*
+ * Get the *real* error from the socket (well try to anyway..).
+ * This may only work when SO_DEBUG is enabled but its worth the
+ * gamble anyway.
+ */
+#ifdef SO_ERROR
+ if (!IsMe(cptr) && cptr->fd >= 0)
+ if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &err, &len))
+ if (err)
+ errtmp = err;
+#endif
+ sendto_flag(SCH_ERROR, text, host, strerror(errtmp));
+#ifdef USE_SYSLOG
+ syslog(LOG_WARNING, text, host, strerror(errtmp));
+#endif
+ return;
+}
+
+/*
+ * inetport
+ *
+ * Create a socket in the AF_INET domain, bind it to the port given in
+ * 'port' and listen to it. If 'ip' has a value, use it as vif to listen.
+ * Connections are accepted to this socket depending on the IP# mask given
+ * by 'ipmask'. Returns the fd of the socket created or -1 on error.
+ */
+int inetport(cptr, ip, ipmask, port)
+aClient *cptr;
+char *ipmask, *ip;
+int port;
+{
+ static struct SOCKADDR_IN server;
+ int ad[4];
+ SOCK_LEN_TYPE len = sizeof(server);
+ char ipname[20];
+
+ ad[0] = ad[1] = ad[2] = ad[3] = 0;
+
+ /*
+ * do it this way because building ip# from separate values for each
+ * byte requires endian knowledge or some nasty messing. Also means
+ * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
+ */
+ (void)sscanf(ipmask, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
+ (void)sprintf(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
+
+ (void)sprintf(cptr->sockhost, "%-.42s.%u", ip ? ip : ME,
+ (unsigned int)port);
+ (void)strcpy(cptr->name, ME);
+ DupString(cptr->auth, ipname);
+ /*
+ * At first, open a new socket
+ */
+ if (cptr->fd == -1)
+ cptr->fd = socket(AFINET, SOCK_STREAM, 0);
+ if (cptr->fd < 0)
+ {
+ report_error("opening stream socket %s:%s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_ERROR,
+ "No more connections allowed (%s)", cptr->name);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ (void)set_sock_opts(cptr->fd, cptr);
+ /*
+ * Bind a port to listen for new connections if port is non-null,
+ * else assume it is already open and try get something from it.
+ */
+ if (port)
+ {
+ server.SIN_FAMILY = AFINET;
+#ifdef INET6
+ if (!ip || (!isxdigit(*ip) && *ip != ':'))
+ server.sin6_addr = in6addr_any;
+ else
+ if(!inet_pton(AF_INET6, ip, server.sin6_addr.s6_addr))
+ bcopy(minus_one, server.sin6_addr.s6_addr,
+ IN6ADDRSZ);
+#else
+ if (!ip || !isdigit(*ip))
+ server.sin_addr.s_addr = INADDR_ANY;
+ else
+ server.sin_addr.s_addr = inetaddr(ip);
+#endif
+ server.SIN_PORT = htons(port);
+ /*
+ * Try 10 times to bind the socket with an interval of 20
+ * seconds. Do this so we don't have to keep trying manually
+ * to bind. Why ? Because a port that has closed often lingers
+ * around for a short time.
+ * This used to be the case. Now it no longer is.
+ * Could cause the server to hang for too long - avalon
+ */
+ if (bind(cptr->fd, (SAP)&server, sizeof(server)) == -1)
+ {
+ report_error("binding stream socket %s:%s", cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ }
+
+ if (getsockname(cptr->fd, (struct SOCKADDR *)&server, &len))
+ {
+ report_error("getsockname failed for %s:%s",cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+
+ if (cptr == &me) /* KLUDGE to get it work... */
+ {
+ char buf[1024];
+
+ (void)sprintf(buf, rpl_str(RPL_MYPORTIS, "*"),
+ ntohs(server.SIN_PORT));
+ (void)write(0, buf, strlen(buf));
+ }
+
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+#ifdef INET6
+ bcopy(server.sin6_addr.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ);
+#else
+ cptr->ip.s_addr = server.sin_addr.s_addr; /* broken on linux at least*/
+#endif
+ cptr->port = port;
+ (void)listen(cptr->fd, LISTENQUEUE);
+ local[cptr->fd] = cptr;
+
+ return 0;
+}
+
+/*
+ * add_listener
+ *
+ * Create a new client which is essentially the stub like 'me' to be used
+ * for a socket that is passive (listen'ing for connections to be accepted).
+ */
+int add_listener(aconf)
+aConfItem *aconf;
+{
+ aClient *cptr;
+
+ cptr = make_client(NULL);
+ cptr->flags = FLAGS_LISTEN;
+ cptr->acpt = cptr;
+ cptr->from = cptr;
+ cptr->firsttime = time(NULL);
+ SetMe(cptr);
+#ifdef UNIXPORT
+ if (*aconf->host == '/')
+ {
+ strncpyzt(cptr->name, aconf->host, sizeof(cptr->name));
+ if (unixport(cptr, aconf->host, aconf->port))
+ cptr->fd = -2;
+ }
+ else
+#endif
+ if (inetport(cptr, aconf->host, aconf->name, aconf->port))
+ cptr->fd = -2;
+
+ if (cptr->fd >= 0)
+ {
+ cptr->confs = make_link();
+ cptr->confs->next = NULL;
+ cptr->confs->value.aconf = aconf;
+ add_fd(cptr->fd, &fdas);
+ add_fd(cptr->fd, &fdall);
+ set_non_blocking(cptr->fd, cptr);
+ }
+ else
+ free_client(cptr);
+ return 0;
+}
+
+#ifdef UNIXPORT
+/*
+ * unixport
+ *
+ * Create a socket and bind it to a filename which is comprised of the path
+ * (directory where file is placed) and port (actual filename created).
+ * Set directory permissions as rwxr-xr-x so other users can connect to the
+ * file which is 'forced' to rwxrwxrwx (different OS's have different need of
+ * modes so users can connect to the socket).
+ */
+int unixport(cptr, path, port)
+aClient *cptr;
+char *path;
+int port;
+{
+ struct sockaddr_un un;
+
+ if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ report_error("error opening unix domain socket %s:%s", cptr);
+ return -1;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_ERROR,
+ "No more connections allowed (%s)", cptr->name);
+ (void)close(cptr->fd);
+ return -1;
+ }
+
+ un.sun_family = AF_UNIX;
+ (void)mkdir(path, 0755);
+ SPRINTF(unixpath, "%s/%d", path, port);
+ (void)unlink(unixpath);
+ strncpyzt(un.sun_path, unixpath, sizeof(un.sun_path));
+ (void)strcpy(cptr->name, ME);
+ errno = 0;
+ get_sockhost(cptr, unixpath);
+
+ if (bind(cptr->fd, (SAP)&un, strlen(unixpath)+2) == -1)
+ {
+ report_error("error binding unix socket %s:%s", cptr);
+ (void)close(cptr->fd);
+ return -1;
+ }
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ (void)listen(cptr->fd, LISTENQUEUE);
+ (void)chmod(path, 0755);
+ (void)chmod(unixpath, 0777);
+ cptr->flags |= FLAGS_UNIX;
+ cptr->port = 0;
+ local[cptr->fd] = cptr;
+
+ return 0;
+}
+#endif
+
+/*
+ * close_listeners
+ *
+ * Close and free all clients which are marked as having their socket open
+ * and in a state where they can accept connections. Unix sockets have
+ * the path to the socket unlinked for cleanliness.
+ */
+void close_listeners()
+{
+ Reg aClient *cptr;
+ Reg int i;
+ Reg aConfItem *aconf;
+
+ /*
+ * close all 'extra' listening ports we have and unlink the file
+ * name if it was a unix socket.
+ */
+ for (i = highest_fd; i >= 0; i--)
+ {
+ if (!(cptr = local[i]))
+ continue;
+ if (cptr == &me || !IsListening(cptr))
+ continue;
+ aconf = cptr->confs->value.aconf;
+
+ if (IsIllegal(aconf) && aconf->clients == 0)
+ {
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ SPRINTF(unixpath, "%s/%d",
+ aconf->host, aconf->port);
+ (void)unlink(unixpath);
+ }
+#endif
+ close_connection(cptr);
+ }
+ }
+}
+
+void
+start_iauth(rcvdsig)
+int rcvdsig;
+{
+#if defined(USE_IAUTH)
+ static time_t last = 0;
+ static char first = 1;
+ int sp[2], fd;
+
+ if ((bootopt & BOOT_NOIAUTH) != 0)
+ return;
+ if (adfd >= 0)
+ {
+ if (rcvdsig)
+ sendto_flag(SCH_AUTH,
+ "iauth is already running, restart canceled");
+ return;
+ }
+ if ((time(NULL) - last) > 90 || rcvdsig)
+ {
+ sendto_flag(SCH_AUTH, "Starting iauth...");
+ last = time(NULL);
+ read_iauth(); /* to reset olen */
+ iauth_spawn += 1;
+ }
+ else
+ return;
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0)
+ {
+ sendto_flag(SCH_ERROR, "socketpair() failed!");
+ sendto_flag(SCH_AUTH, "Failed to restart iauth!");
+ }
+ adfd = sp[0];
+ set_non_blocking(sp[0], NULL);
+ set_non_blocking(sp[1], NULL); /* less to worry about in iauth */
+ switch (vfork())
+ {
+ case -1:
+ sendto_flag(SCH_ERROR, "vfork() failed!");
+ sendto_flag(SCH_AUTH, "Failed to restart iauth!");
+ close(sp[0]); close(sp[1]);
+ adfd = -1;
+ return;
+ case 0:
+ for (fd = 0; fd < MAXCONNECTIONS; fd++)
+ if (fd != sp[1])
+ (void)close(fd);
+ if (sp[1] != 0)
+ {
+ (void)dup2(sp[1], 0);
+ close(sp[1]);
+ }
+ if (execl(IAUTH_PATH, IAUTH, NULL) < 0)
+ _exit(-1); /* should really not happen.. */
+ default:
+ close(sp[1]);
+ }
+
+ if (first)
+ first = 0;
+ else
+ {
+ int i;
+ aClient *cptr;
+
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(cptr = local[i]))
+ continue;
+ if (IsServer(cptr) || IsService(cptr))
+ continue;
+ sendto_iauth("%d O", i);
+ }
+ }
+#endif
+}
+
+/*
+ * init_sys
+ */
+void init_sys()
+{
+ Reg int fd;
+
+#ifdef RLIMIT_FD_MAX
+ struct rlimit limit;
+
+ if (!getrlimit(RLIMIT_FD_MAX, &limit))
+ {
+ if (limit.rlim_max < MAXCONNECTIONS)
+ {
+ (void)fprintf(stderr, "ircd fd table is too big\n");
+ (void)fprintf(stderr, "Hard Limit: %d IRC max: %d\n",
+ (int) limit.rlim_max, MAXCONNECTIONS);
+ (void)fprintf(stderr,
+ "Fix MAXCONNECTIONS and recompile.\n");
+ exit(-1);
+ }
+ limit.rlim_cur = limit.rlim_max; /* make soft limit the max */
+ if (setrlimit(RLIMIT_FD_MAX, &limit) == -1)
+ {
+ (void)fprintf(stderr, "error setting max fd's to %d\n",
+ (int) limit.rlim_cur);
+ exit(-1);
+ }
+ }
+#endif
+#if ! USE_POLL
+# ifdef sequent
+# ifndef DYNIXPTX
+ int fd_limit;
+
+ fd_limit = setdtablesize(MAXCONNECTIONS + 1);
+ if (fd_limit < MAXCONNECTIONS)
+ {
+ (void)fprintf(stderr,"ircd fd table too big\n");
+ (void)fprintf(stderr,"Hard Limit: %d IRC max: %d\n",
+ fd_limit, MAXCONNECTIONS);
+ (void)fprintf(stderr,"Fix MAXCONNECTIONS\n");
+ exit(-1);
+ }
+# endif
+# endif
+#endif /* USE_POLL */
+
+#if defined(PCS) || defined(DYNIXPTX) || defined(SVR3)
+ char logbuf[BUFSIZ];
+
+ (void)setvbuf(stderr,logbuf,_IOLBF,sizeof(logbuf));
+#else
+# if defined(HPUX)
+ (void)setvbuf(stderr, NULL, _IOLBF, 0);
+# else
+# if !defined(SVR4)
+ (void)setlinebuf(stderr);
+# endif
+# endif
+#endif
+
+ bzero((char *)&fdas, sizeof(fdas));
+ bzero((char *)&fdall, sizeof(fdall));
+ fdas.highest = fdall.highest = -1;
+
+ for (fd = 3; fd < MAXCONNECTIONS; fd++)
+ {
+ (void)close(fd);
+ local[fd] = NULL;
+ }
+ local[1] = NULL;
+ (void) fclose(stdout);
+ (void)close(1);
+
+ if (bootopt & BOOT_TTY) /* debugging is going to a tty */
+ goto init_dgram;
+ if (!(bootopt & BOOT_DEBUG))
+ (void)close(2);
+
+ if (((bootopt & BOOT_CONSOLE) || isatty(0)) &&
+ !(bootopt & (BOOT_INETD|BOOT_OPER)))
+ {
+#ifndef __CYGWIN32__
+ if (fork())
+ exit(0);
+#endif
+#ifdef TIOCNOTTY
+ if ((fd = open("/dev/tty", O_RDWR)) >= 0)
+ {
+ (void)ioctl(fd, TIOCNOTTY, (char *)NULL);
+ (void)close(fd);
+ }
+#endif
+#if defined(HPUX) || defined(SVR4) || defined(DYNIXPTX) || \
+ defined(_POSIX_SOURCE) || defined(SGI)
+ (void)setsid();
+#else
+ (void)setpgrp(0, (int)getpid());
+#endif
+ (void)close(0); /* fd 0 opened by inetd */
+ local[0] = NULL;
+ }
+init_dgram:
+ resfd = init_resolver(0x1f);
+
+ start_iauth(0);
+}
+
+void write_pidfile()
+{
+ int fd;
+ char buff[20];
+ (void)truncate(IRCDPID_PATH, 0);
+ if ((fd = open(IRCDPID_PATH, O_CREAT|O_WRONLY, 0600))>=0)
+ {
+ bzero(buff, sizeof(buff));
+ (void)sprintf(buff,"%5d\n", (int)getpid());
+ if (write(fd, buff, strlen(buff)) == -1)
+ Debug((DEBUG_NOTICE,"Error writing to pid file %s",
+ IRCDPID_PATH));
+ (void)close(fd);
+ return;
+ }
+# ifdef DEBUGMODE
+ else
+ Debug((DEBUG_NOTICE,"Error opening pid file %s",
+ IRCDPID_PATH));
+# endif
+}
+
+/*
+ * Initialize the various name strings used to store hostnames. This is set
+ * from either the server's sockhost (if client fd is a tty or localhost)
+ * or from the ip# converted into a string. 0 = success, -1 = fail.
+ */
+static int check_init(cptr, sockn)
+Reg aClient *cptr;
+Reg char *sockn;
+{
+ struct SOCKADDR_IN sk;
+ SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);
+
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ strncpyzt(sockn, cptr->acpt->sockhost, HOSTLEN+1);
+ get_sockhost(cptr, sockn);
+ return 0;
+ }
+#endif
+
+ /* If descriptor is a tty, special checking... */
+ if (isatty(cptr->fd))
+ {
+ strncpyzt(sockn, me.sockhost, HOSTLEN);
+ bzero((char *)&sk, sizeof(struct SOCKADDR_IN));
+ }
+ else if (getpeername(cptr->fd, (SAP)&sk, &len) == -1)
+ {
+ report_error("connect failure: %s %s", cptr);
+ return -1;
+ }
+#ifdef INET6
+ inetntop(AF_INET6, (char *)&sk.sin6_addr, sockn, MYDUMMY_SIZE);
+ Debug((DEBUG_DNS,"sockn %x",sockn));
+ Debug((DEBUG_DNS,"sockn %s",sockn));
+#else
+ (void)strcpy(sockn, (char *)inetntoa((char *)&sk.sin_addr));
+#endif
+#ifdef INET6
+ if (IN6_IS_ADDR_LOOPBACK(&sk.SIN_ADDR))
+#else
+ if (inetnetof(sk.SIN_ADDR) == IN_LOOPBACKNET)
+#endif
+ {
+ cptr->hostp = me.hostp;
+ }
+ bcopy((char *)&sk.SIN_ADDR, (char *)&cptr->ip, sizeof(struct IN_ADDR));
+ cptr->port = (int)(ntohs(sk.SIN_PORT));
+
+ return 0;
+}
+
+/*
+ * Ordinary client access check. Look for conf lines which have the same
+ * status as the flags passed.
+ * 0 = Success
+ * -1 = Bad socket.
+ * -2 = Access denied
+ */
+int check_client(cptr)
+Reg aClient *cptr;
+{
+ static char sockname[HOSTLEN+1];
+ Reg struct hostent *hp = NULL;
+ Reg int i;
+
+#ifdef INET6
+ Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+ cptr->name, inet_ntop(AF_INET6, (char *)&cptr->ip, mydummy,
+ MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "ch_cl: check access for %s[%s]",
+ cptr->name, inetntoa((char *)&cptr->ip)));
+#endif
+
+ if (check_init(cptr, sockname))
+ return -1;
+
+ if (!IsUnixSocket(cptr))
+ hp = cptr->hostp;
+ /*
+ * Verify that the host to ip mapping is correct both ways and that
+ * the ip#(s) for the socket is listed for the host.
+ * We shouldn't check it for localhost, because hp is fake in that
+ * case. -Toor
+ */
+ if (hp && (hp != me.hostp))
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
+ sizeof(struct IN_ADDR)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+#ifdef INET6
+ sendto_flag(SCH_ERROR,
+ "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
+ inetntop(AF_INET6, (char *)&cptr->ip,
+ mydummy,MYDUMMY_SIZE),hp->h_name,
+ ((unsigned long *)hp->h_addr)[0],
+ ((unsigned long *)hp->h_addr)[1],
+ ((unsigned long *)hp->h_addr)[2],
+ ((unsigned long *)hp->h_addr)[3]);
+#else
+ sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa((char *)&cptr->ip), hp->h_name,
+ *((unsigned long *)hp->h_addr));
+#endif
+ hp = NULL;
+ }
+ }
+
+ if ((i = attach_Iline(cptr, hp, sockname)))
+ {
+ Debug((DEBUG_DNS,"ch_cl: access denied: %s[%s]",
+ cptr->name, sockname));
+ return i;
+ }
+
+ Debug((DEBUG_DNS, "ch_cl: access ok: %s[%s]",
+ cptr->name, sockname));
+
+#ifdef INET6
+ if (IN6_IS_ADDR_LOOPBACK(&cptr->ip) || IsUnixSocket(cptr) ||
+ (cptr->ip.s6_laddr[0]==mysk.sin6_addr.s6_laddr[0] &&
+ cptr->ip.s6_laddr[1]==mysk.sin6_addr.s6_laddr[1])
+/* ||
+ IN6_ARE_ADDR_SAMEPREFIX(&cptr->ip, &mysk.SIN_ADDR))
+ about the same, I think NOT */
+ )
+#else
+ if (inetnetof(cptr->ip) == IN_LOOPBACKNET || IsUnixSocket(cptr) ||
+ inetnetof(cptr->ip) == inetnetof(mysk.SIN_ADDR))
+#endif
+ {
+
+ ircstp->is_loc++;
+ cptr->flags |= FLAGS_LOCAL;
+ }
+ return 0;
+}
+
+/*
+ * check_server_init(), check_server()
+ * check access for a server given its name (passed in cptr struct).
+ * Must check for all C/N lines which have a name which matches the
+ * name given and a host which matches. A host alias which is the
+ * same as the server name is also acceptable in the host field of a
+ * C/N line.
+ * 0 = Success
+ * -1 = Access denied
+ * -2 = Bad socket.
+ */
+int check_server_init(cptr)
+aClient *cptr;
+{
+ Reg char *name;
+ Reg aConfItem *c_conf = NULL, *n_conf = NULL;
+ struct hostent *hp = NULL;
+ Link *lp;
+
+ name = cptr->name;
+ Debug((DEBUG_DNS, "sv_cl: check access for %s[%s]",
+ name, cptr->sockhost));
+
+ if (IsUnknown(cptr) && !attach_confs(cptr, name, CFLAG|NFLAG))
+ {
+ Debug((DEBUG_DNS,"No C/N lines for %s", name));
+ return -1;
+ }
+ lp = cptr->confs;
+ /*
+ * We initiated this connection so the client should have a C and N
+ * line already attached after passing through the connec_server()
+ * function earlier.
+ */
+ if (IsConnecting(cptr) || IsHandshake(cptr))
+ {
+ c_conf = find_conf(lp, name, CFLAG);
+ n_conf = find_conf(lp, name, NFLAG);
+ if (!c_conf || !n_conf)
+ {
+ sendto_flag(SCH_ERROR, "Connecting Error: %s[%s]",
+ name, cptr->sockhost);
+ det_confs_butmask(cptr, 0);
+ return -1;
+ }
+ }
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ {
+ if (!c_conf)
+ c_conf = find_conf(lp, name, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf(lp, name, NFLAG);
+ }
+#endif
+
+ /*
+ ** If the servername is a hostname, either an alias (CNAME) or
+ ** real name, then check with it as the host. Use gethostbyname()
+ ** to check for servername as hostname.
+ */
+ if (!IsUnixSocket(cptr) && !cptr->hostp)
+ {
+ Reg aConfItem *aconf;
+
+ aconf = count_cnlines(lp);
+ if (aconf)
+ {
+ Reg char *s;
+ Link lin;
+
+ /*
+ ** Do a lookup for the CONF line *only* and not
+ ** the server connection else we get stuck in a
+ ** nasty state since it takes a SERVER message to
+ ** get us here and we can't interrupt that very
+ ** well.
+ */
+ lin.value.aconf = aconf;
+ lin.flags = ASYNC_CONF;
+ nextdnscheck = 1;
+ if ((s = index(aconf->host, '@')))
+ s++;
+ else
+ s = aconf->host;
+ Debug((DEBUG_DNS,"sv_ci:cache lookup (%s)",s));
+ hp = gethost_byname(s, &lin);
+ }
+ }
+ return check_server(cptr, hp, c_conf, n_conf, 0);
+}
+
+int check_server(cptr, hp, c_conf, n_conf, estab)
+aClient *cptr;
+Reg aConfItem *n_conf, *c_conf;
+Reg struct hostent *hp;
+int estab;
+{
+ Reg char *name;
+ char abuff[HOSTLEN+USERLEN+2];
+ char sockname[HOSTLEN+1], fullname[HOSTLEN+1];
+ Link *lp = cptr->confs;
+ int i;
+
+ if (check_init(cptr, sockname))
+ return -2;
+
+check_serverback:
+ if (hp)
+ {
+ for (i = 0; hp->h_addr_list[i]; i++)
+ if (!bcmp(hp->h_addr_list[i], (char *)&cptr->ip,
+ sizeof(struct IN_ADDR)))
+ break;
+ if (!hp->h_addr_list[i])
+ {
+#ifdef INET6
+ sendto_flag(SCH_ERROR,
+ "IP# Mismatch: %s != %s[%08x%08x%08x%08x]",
+ inetntop(AF_INET6, (char *)&cptr->ip,
+ mydummy,MYDUMMY_SIZE),hp->h_name,
+ ((unsigned long *)hp->h_addr)[0],
+ ((unsigned long *)hp->h_addr)[1],
+ ((unsigned long *)hp->h_addr)[2],
+ ((unsigned long *)hp->h_addr)[3]);
+#else
+ sendto_flag(SCH_ERROR, "IP# Mismatch: %s != %s[%08x]",
+ inetntoa((char *)&cptr->ip), hp->h_name,
+ *((unsigned long *)hp->h_addr));
+#endif
+ hp = NULL;
+ }
+ }
+ else if (cptr->hostp)
+ {
+ hp = cptr->hostp;
+ goto check_serverback;
+ }
+
+ if (hp)
+ /*
+ * if we are missing a C or N line from above, search for
+ * it under all known hostnames we have for this ip#.
+ */
+ for (i=0,name = hp->h_name; name ; name = hp->h_aliases[i++])
+ {
+ strncpyzt(fullname, name, sizeof(fullname));
+ add_local_domain(fullname, HOSTLEN-strlen(fullname));
+ Debug((DEBUG_DNS, "sv_cl: gethostbyaddr: %s->%s",
+ sockname, fullname));
+ SPRINTF(abuff, "%s@%s", cptr->username, fullname);
+ if (!c_conf)
+ c_conf = find_conf_host(lp, abuff, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_host(lp, abuff, NFLAG);
+ if (c_conf && n_conf)
+ {
+ get_sockhost(cptr, fullname);
+ break;
+ }
+ }
+ name = cptr->name;
+
+ /*
+ * Check for C and N lines with the hostname portion the ip number
+ * of the host the server runs on. This also checks the case where
+ * there is a server connecting from 'localhost'.
+ */
+ if (IsUnknown(cptr) && (!c_conf || !n_conf))
+ {
+ SPRINTF(abuff, "%s@%s", cptr->username, sockname);
+ if (!c_conf)
+ c_conf = find_conf_host(lp, abuff, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_host(lp, abuff, NFLAG);
+ }
+ /*
+ * Attach by IP# only if all other checks have failed.
+ * It is quite possible to get here with the strange things that can
+ * happen when using DNS in the way the irc server does. -avalon
+ */
+ if (!hp)
+ {
+ if (!c_conf)
+ c_conf = find_conf_ip(lp, (char *)&cptr->ip,
+ cptr->username, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_ip(lp, (char *)&cptr->ip,
+ cptr->username, NFLAG);
+ }
+ else
+ for (i = 0; hp->h_addr_list[i]; i++)
+ {
+ if (!c_conf)
+ c_conf = find_conf_ip(lp, hp->h_addr_list[i],
+ cptr->username, CFLAG);
+ if (!n_conf)
+ n_conf = find_conf_ip(lp, hp->h_addr_list[i],
+ cptr->username, NFLAG);
+ }
+ /*
+ * detach all conf lines that got attached by attach_confs()
+ */
+ det_confs_butmask(cptr, 0);
+ /*
+ * if no C or no N lines, then deny access
+ */
+ if (!c_conf || !n_conf)
+ {
+ get_sockhost(cptr, sockname);
+ Debug((DEBUG_DNS, "sv_cl: access denied: %s[%s@%s] c %x n %x",
+ name, cptr->auth, cptr->sockhost,
+ c_conf, n_conf));
+ return -1;
+ }
+ /*
+ * attach the C and N lines to the client structure for later use.
+ */
+ (void)attach_conf(cptr, n_conf);
+ (void)attach_conf(cptr, c_conf);
+ (void)attach_confs(cptr, name, CONF_HUB|CONF_LEAF);
+
+#ifdef INET6
+ if ((AND16(c_conf->ipnum.s6_addr) == 255) && !IsUnixSocket(cptr))
+#else
+ if ((c_conf->ipnum.s_addr == -1) && !IsUnixSocket(cptr))
+#endif
+ bcopy((char *)&cptr->ip, (char *)&c_conf->ipnum,
+ sizeof(struct IN_ADDR));
+ if (!IsUnixSocket(cptr))
+ get_sockhost(cptr, c_conf->host);
+
+ Debug((DEBUG_DNS,"sv_cl: access ok: %s[%s]",
+ name, cptr->sockhost));
+ if (estab)
+ return m_server_estab(cptr);
+ return 0;
+}
+
+/*
+** completed_connection
+** Complete non-blocking connect()-sequence. Check access and
+** terminate connection, if trouble detected.
+**
+** Return TRUE, if successfully completed
+** FALSE, if failed and ClientExit
+*/
+static int completed_connection(cptr)
+aClient *cptr;
+{
+ aConfItem *aconf;
+
+ SetHandshake(cptr);
+
+ aconf = find_conf(cptr->confs, cptr->name, CFLAG);
+ if (!aconf)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Lost C-Line for %s", get_client_name(cptr,FALSE));
+ return -1;
+ }
+ if (!BadPtr(aconf->passwd))
+#ifndef ZIP_LINKS
+ sendto_one(cptr, "PASS %s %s IRC|%s %s", aconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#else
+ sendto_one(cptr, "PASS %s %s IRC|%s %s%s", aconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "",
+ (aconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "");
+#endif
+
+ aconf = find_conf(cptr->confs, cptr->name, CONF_NOCONNECT_SERVER);
+ if (!aconf)
+ {
+ sendto_flag(SCH_NOTICE,
+ "Lost N-Line for %s", get_client_name(cptr,FALSE));
+ return -1;
+ }
+ sendto_one(cptr, "SERVER %s 1 :%s",
+ my_name_for_link(ME, aconf->port), me.info);
+ if (!IsDead(cptr))
+ {
+ start_auth(cptr);
+#if defined(USE_IAUTH)
+ /*
+ ** This could become a bug.. but I don't think iauth needs the
+ ** hostname/aliases in this case. -kalt
+ */
+ sendto_iauth("%d d", cptr->fd);
+#endif
+ }
+
+ return (IsDead(cptr)) ? -1 : 0;
+}
+
+int hold_server(cptr)
+aClient *cptr;
+{
+ return -1; /* needs to be fixed, don't forget virtual hosts */
+
+#if 0 /* code and variables declarations are removed, this
+ avoids compiler warnings */
+
+ struct SOCKADDR_IN sin;
+ aConfItem *aconf;
+ aClient *acptr;
+ int fd;
+
+#ifdef ZIP_LINKS
+ /*
+ * reconnecting will not work with compressed links,
+ * unless someones fixes reconnect and implements what's needed
+ * to have it work for compressed links. -krys
+ */
+ return -1;
+#else
+ if (!IsServer(cptr) ||
+ !(aconf = find_conf_name(cptr->name, CFLAG)))
+ return -1;
+
+ if (!aconf->port)
+ return -1;
+
+ fd = socket(AFINET, SOCK_STREAM, 0);
+
+ if (fd >= MAXCLIENTS)
+ {
+ (void)close(fd);
+ sendto_flag(SCH_ERROR,
+ "Can't reconnect - all connections in use");
+ return -1;
+ }
+
+ cptr->flags |= FLAGS_HELD;
+ (void)close(cptr->fd);
+ del_fd(cptr->fd, &fdall);
+ del_fd(cptr->fd, &fdas);
+ cptr->fd = -2;
+
+ acptr = make_client(NULL);
+ acptr->fd = fd;
+ acptr->port = aconf->port;
+ set_non_blocking(acptr->fd, acptr);
+ (void)set_sock_opts(acptr->fd, acptr);
+ bzero((char *)&sin, sizeof(sin));
+ sin.SIN_FAMILY = AFINET;
+ sin.SIN_PORT = htons(aconf->port);
+ bcopy((char *)&cptr->ip, (char *)&sin.SIN_ADDR, sizeof(cptr->ip));
+ bcopy((char *)&cptr->ip, (char *)&acptr->ip, sizeof(cptr->ip));
+
+ if (connect(acptr->fd, (SAP)&sin, sizeof(sin)) < 0 &&
+ errno != EINPROGRESS)
+ {
+ report_error("Connect to host %s failed: %s", acptr); /*buggy*/
+ (void)close(acptr->fd);
+ MyFree((char *)acptr);
+ return -1;
+ }
+
+ acptr->status = STAT_RECONNECT;
+ if (acptr->fd > highest_fd)
+ highest_fd = acptr->fd;
+ add_fd(acptr->fd, &fdall);
+ local[acptr->fd] = acptr;
+ acptr->acpt = &me;
+ add_client_to_list(acptr);
+ (void)strcpy(acptr->name, cptr->name);
+ /* broken syntax
+ sendto_one(acptr, "PASS %s %s", aconf->passwd, pass_version);
+ */
+ sendto_one(acptr, "RECONNECT %s %d", acptr->name, cptr->sendM);
+ sendto_flag(SCH_NOTICE, "Reconnecting to %s", acptr->name);
+ Debug((DEBUG_NOTICE, "Reconnect %s %#x via %#x %d", cptr->name, cptr,
+ acptr, acptr->fd));
+ return 0;
+#endif
+#endif
+}
+
+/*
+** close_connection
+** Close the physical connection. This function must make
+** MyConnect(cptr) == FALSE, and set cptr->from == NULL.
+*/
+void close_connection(cptr)
+aClient *cptr;
+{
+ Reg aConfItem *aconf;
+ Reg int i,j;
+#ifdef SO_LINGER
+ struct linger sockling;
+
+ sockling.l_onoff = 0;
+#endif
+
+ if (IsServer(cptr))
+ {
+ ircstp->is_sv++;
+ ircstp->is_sbs += cptr->sendB;
+ ircstp->is_sbr += cptr->receiveB;
+ ircstp->is_sks += cptr->sendK;
+ ircstp->is_skr += cptr->receiveK;
+ ircstp->is_sti += timeofday - cptr->firsttime;
+ if (ircstp->is_sbs > 1023)
+ {
+ ircstp->is_sks += (ircstp->is_sbs >> 10);
+ ircstp->is_sbs &= 0x3ff;
+ }
+ if (ircstp->is_sbr > 1023)
+ {
+ ircstp->is_skr += (ircstp->is_sbr >> 10);
+ ircstp->is_sbr &= 0x3ff;
+ }
+ }
+ else if (IsClient(cptr))
+ {
+ ircstp->is_cl++;
+ ircstp->is_cbs += cptr->sendB;
+ ircstp->is_cbr += cptr->receiveB;
+ ircstp->is_cks += cptr->sendK;
+ ircstp->is_ckr += cptr->receiveK;
+ ircstp->is_cti += timeofday - cptr->firsttime;
+ if (ircstp->is_cbs > 1023)
+ {
+ ircstp->is_cks += (ircstp->is_cbs >> 10);
+ ircstp->is_cbs &= 0x3ff;
+ }
+ if (ircstp->is_cbr > 1023)
+ {
+ ircstp->is_ckr += (ircstp->is_cbr >> 10);
+ ircstp->is_cbr &= 0x3ff;
+ }
+ }
+ else
+ ircstp->is_ni++;
+
+ /*
+ * remove outstanding DNS queries.
+ */
+ del_queries((char *)cptr);
+ /*
+ * If the connection has been up for a long amount of time, schedule
+ * a 'quick' reconnect, else reset the next-connect cycle.
+ */
+ if ((aconf = find_conf_exact(cptr->name, cptr->username,
+ cptr->sockhost, CFLAG)))
+ {
+ /*
+ * Reschedule a faster reconnect, if this was a automaticly
+ * connected configuration entry. (Note that if we have had
+ * a rehash in between, the status has been changed to
+ * CONF_ILLEGAL). But only do this if it was a "good" link.
+ */
+ aconf->hold = timeofday;
+ aconf->hold += (aconf->hold - cptr->since > HANGONGOODLINK) ?
+ HANGONRETRYDELAY : ConfConFreq(aconf);
+ if (nextconnect > aconf->hold)
+ nextconnect = aconf->hold;
+ }
+
+ if (cptr->authfd >= 0)
+ {
+#ifdef SO_LINGER
+ if (cptr->exitc == EXITC_PING)
+ if (SETSOCKOPT(cptr->authfd, SOL_SOCKET, SO_LINGER,
+ &sockling, sockling))
+ report_error("setsockopt(SO_LINGER) %s:%s",
+ cptr);
+#endif
+ (void)close(cptr->authfd);
+ }
+
+ if ((i = cptr->fd) >= 0)
+ {
+#if defined(USE_IAUTH)
+ sendto_iauth("%d D", cptr->fd);
+#endif
+ flush_connections(i);
+ if (IsServer(cptr) || IsListening(cptr))
+ {
+ del_fd(i, &fdas);
+#ifdef ZIP_LINKS
+ /*
+ ** the connection might have zip data (even if
+ ** FLAGS_ZIP is not set)
+ */
+ zip_free(cptr);
+#endif
+ }
+ else if (IsClient(cptr))
+ {
+#ifdef SO_LINGER
+ if (cptr->exitc == EXITC_PING)
+ if (SETSOCKOPT(i, SOL_SOCKET, SO_LINGER,
+ &sockling, sockling))
+ report_error("setsockopt(SO_LINGER) %s:%s",
+ cptr);
+#endif
+ }
+ del_fd(i, &fdall);
+ local[i] = NULL;
+ (void)close(i);
+
+ /*
+ * fd remap to keep local[i] filled at the bottom.
+ * don't *ever* move descriptors for
+ * + log file
+ * + sockets bound to listen() ports
+ * --Yegg
+ */
+ if (i >= 0 && (j = highest_fd) > i)
+ {
+ while (!local[j])
+ j--;
+ if (j > i && local[j] &&
+ !(IsLog(local[j]) || IsMe(local[j])))
+ {
+ if (dup2(j,i) == -1)
+ return;
+ local[i] = local[j];
+ local[i]->fd = i;
+ local[j] = NULL;
+ (void)close(j);
+ del_fd(j, &fdall);
+ add_fd(i, &fdall);
+ if (IsServer(local[i]) || IsMe(local[i]))
+ {
+ del_fd(j, &fdas);
+ add_fd(i, &fdas);
+ }
+ while (!local[highest_fd])
+ highest_fd--;
+#if defined(USE_IAUTH)
+ sendto_iauth("%d R %d", j, i);
+#endif
+ }
+ }
+ cptr->fd = -2;
+ DBufClear(&cptr->sendQ);
+ DBufClear(&cptr->recvQ);
+ bzero(cptr->passwd, sizeof(cptr->passwd));
+ /*
+ * clean up extra sockets from P-lines which have been
+ * discarded.
+ */
+ if (cptr->acpt != &me)
+ {
+ aconf = cptr->acpt->confs->value.aconf;
+ if (aconf->clients > 0)
+ aconf->clients--;
+ if (!aconf->clients && IsIllegal(aconf))
+ close_connection(cptr->acpt);
+ }
+ }
+
+ det_confs_butmask(cptr, 0);
+ cptr->from = NULL; /* ...this should catch them! >:) --msa */
+ return;
+}
+
+/*
+** set_sock_opts
+*/
+static int set_sock_opts(fd, cptr)
+int fd;
+aClient *cptr;
+{
+ int opt, ret = 0;
+#ifdef SO_REUSEADDR
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_REUSEADDR, &opt, opt) < 0)
+ report_error("setsockopt(SO_REUSEADDR) %s:%s", cptr);
+#endif
+#if defined(SO_DEBUG) && defined(DEBUGMODE) && 0
+/* Solaris 2.x with SO_DEBUG writes to syslog by default */
+#if ! SOLARIS_2 || defined(USE_SYSLOG)
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_DEBUG, &opt, opt) < 0)
+ report_error("setsockopt(SO_DEBUG) %s:%s", cptr);
+#endif /* SOLARIS_2 */
+#endif
+#ifdef SO_USELOOPBACK
+ opt = 1;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_USELOOPBACK, &opt, opt) < 0)
+ report_error("setsockopt(SO_USELOOPBACK) %s:%s", cptr);
+#endif
+#ifdef SO_RCVBUF
+ opt = 8192;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_RCVBUF, &opt, opt) < 0)
+ report_error("setsockopt(SO_RCVBUF) %s:%s", cptr);
+#endif
+#ifdef SO_SNDBUF
+# ifdef _SEQUENT_
+/* seems that Sequent freezes up if the receving buffer is a different size
+ * to the sending buffer (maybe a tcp window problem too).
+ */
+# endif
+ opt = 8192;
+ if (SETSOCKOPT(fd, SOL_SOCKET, SO_SNDBUF, &opt, opt) < 0)
+ report_error("setsockopt(SO_SNDBUF) %s:%s", cptr);
+# ifdef SO_SNDLOWAT
+ /*
+ * Setting the low water mark should improve performence by avoiding
+ * early returns from select()/poll(). It shouldn't delay sending
+ * data, provided that io_loop() combines read_message() and
+ * flush_fdary/connections() calls properly. -kalt
+ * This call isn't always implemented, even when defined.. so be quiet
+ * about errors. -kalt
+ */
+ opt = 8192;
+ SETSOCKOPT(fd, SOL_SOCKET, SO_SNDLOWAT, &opt, opt);
+# endif
+#endif
+#if defined(IP_OPTIONS) && defined(IPPROTO_IP) && !defined(AIX) && \
+ !defined(SUN_GSO_BUG) && !defined(INET6)
+ /*
+ * Mainly to turn off and alert us to source routing, here.
+ * Method borrowed from Wietse Venema's TCP wrapper.
+ */
+ {
+ if (!IsUnixSocket(cptr) && !IsListening(cptr))
+ {
+ u_char opbuf[256], *t = opbuf;
+ char *s = readbuf;
+
+ opt = sizeof(opbuf);
+ if (GETSOCKOPT(fd, IPPROTO_IP, IP_OPTIONS, t, &opt) == -1)
+ report_error("getsockopt(IP_OPTIONS) %s:%s", cptr);
+ else if (opt > 0)
+ {
+ for (; opt > 0; opt--, s+= 3)
+ (void)sprintf(s, " %02x", *t++);
+ *s = '\0';
+ sendto_flag(SCH_NOTICE,
+ "Connection %s with IP opts%s",
+ get_client_name(cptr, TRUE), readbuf);
+ Debug((DEBUG_NOTICE,
+ "Connection %s with IP opts%s",
+ get_client_name(cptr, TRUE), readbuf));
+ ret = -1;
+ }
+ }
+ }
+#endif
+ return ret;
+}
+
+int get_sockerr(cptr)
+aClient *cptr;
+{
+ int errtmp = errno, err = 0;
+ SOCK_LEN_TYPE len = sizeof(err);
+
+#ifdef SO_ERROR
+ if (cptr->fd >= 0)
+ if (!GETSOCKOPT(cptr->fd, SOL_SOCKET, SO_ERROR, &err, &len))
+ if (err)
+ errtmp = err;
+#endif
+ return errtmp;
+}
+
+/*
+** set_non_blocking
+** Set the client connection into non-blocking mode. If your
+** system doesn't support this, you can make this a dummy
+** function (and get all the old problems that plagued the
+** blocking version of IRC--not a problem if you are a
+** lightly loaded node...)
+*/
+void set_non_blocking(fd, cptr)
+int fd;
+aClient *cptr;
+{
+ int res, nonb = 0;
+
+ /*
+ ** NOTE: consult ALL your relevant manual pages *BEFORE* changing
+ ** these ioctl's. There are quite a few variations on them,
+ ** as can be seen by the PCS one. They are *NOT* all the same.
+ ** Heed this well. - Avalon.
+ */
+#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)
+ report_error("ioctl(fd,FIONBIO) failed for %s:%s", cptr);
+#else
+ if ((res = fcntl(fd, F_GETFL, 0)) == -1)
+ report_error("fcntl(fd, F_GETFL) failed for %s:%s",cptr);
+ else if (fcntl(fd, F_SETFL, res | nonb) == -1)
+ report_error("fcntl(fd, F_SETL, nonb) failed for %s:%s",cptr);
+#endif
+ return;
+}
+
+#ifdef CLONE_CHECK
+/*
+ * check_clones
+ * adapted by jecete 4 IRC Ptnet
+ */
+static int check_clones(cptr)
+aClient *cptr;
+{
+ struct abacklog {
+ struct IN_ADDR ip;
+ time_t PT;
+ struct abacklog *next;
+ };
+ static struct abacklog *backlog = NULL;
+ register struct abacklog **blscn = &backlog,
+ *blptr;
+ register int count = 0;
+
+ /* First, ditch old entries */
+ while (*blscn != NULL)
+ {
+ if ((*blscn)->PT+CLONE_PERIOD < timeofday)
+ {
+ blptr= *blscn;
+ *blscn=blptr->next;
+ MyFree((char *)blptr);
+ }
+ else
+ blscn = &(*blscn)->next;
+ }
+ /* Now add new item to the list */
+ blptr = (struct abacklog *) MyMalloc(sizeof(struct abacklog));
+#ifdef INET6
+ bcopy(cptr->ip.s6_addr, blptr->ip.s6_addr, IN6ADDRSZ);
+#else
+ blptr->ip.s_addr = cptr->ip.s_addr;
+#endif
+ blptr->PT = timeofday;
+ blptr->next = backlog;
+ backlog = blptr;
+
+ /* Count the number of entries from the same host */
+ blptr = backlog;
+ while (blptr != NULL)
+ {
+#ifdef INET6
+ if (bcmp(blptr->ip.s6_addr, cptr->ip.s6_addr, IN6ADDRSZ) == 0)
+#else
+ if (blptr->ip.s_addr == cptr->ip.s_addr)
+#endif
+ count++;
+ blptr = blptr->next;
+ }
+ return (count);
+}
+#endif
+
+/*
+ * Creates a client which has just connected to us on the given fd.
+ * The sockhost field is initialized with the ip# of the host.
+ * The client is added to the linked list of clients but isnt added to any
+ * hash tables yet since it doesnt have a name.
+ */
+aClient *add_connection(cptr, fd)
+aClient *cptr;
+int fd;
+{
+ Link lin;
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+ acptr = make_client(NULL);
+
+ aconf = cptr->confs->value.aconf;
+ /* Removed preliminary access check. Full check is performed in
+ * m_server and m_user instead. Also connection time out help to
+ * get rid of unwanted connections.
+ */
+ if (isatty(fd)) /* If descriptor is a tty, special checking... */
+ get_sockhost(acptr, cptr->sockhost);
+ else
+ {
+ struct SOCKADDR_IN addr;
+ SOCK_LEN_TYPE len = sizeof(struct SOCKADDR_IN);
+
+ if (getpeername(fd, (SAP)&addr, &len) == -1)
+ {
+#if defined(linux)
+ if (errno != ENOTCONN)
+#endif
+ report_error("Failed in connecting to %s :%s",
+ cptr);
+add_con_refuse:
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ (void)close(fd);
+ return NULL;
+ }
+ /* don't want to add "Failed in connecting to" here.. */
+ if (aconf && IsIllegal(aconf))
+ goto add_con_refuse;
+ /* Copy ascii address to 'sockhost' just in case. Then we
+ * have something valid to put into error messages...
+ */
+#ifdef INET6
+ inetntop(AF_INET6, (char *)&addr.sin6_addr, mydummy,
+ MYDUMMY_SIZE);
+ get_sockhost(acptr, (char *)mydummy);
+#else
+ get_sockhost(acptr, (char *)inetntoa((char *)&addr.sin_addr));
+#endif
+ bcopy ((char *)&addr.SIN_ADDR, (char *)&acptr->ip,
+ sizeof(struct IN_ADDR));
+ acptr->port = ntohs(addr.SIN_PORT);
+
+ lin.flags = ASYNC_CLIENT;
+ lin.value.cptr = acptr;
+#ifdef INET6
+ Debug((DEBUG_DNS, "lookup %s",
+ inet_ntop(AF_INET6, (char *)&addr.sin6_addr,
+ mydummy, MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_DNS, "lookup %s",
+ inetntoa((char *)&addr.sin_addr)));
+#endif
+ acptr->hostp = gethost_byaddr((char *)&acptr->ip, &lin);
+ if (!acptr->hostp)
+ SetDNS(acptr);
+ nextdnscheck = 1;
+ }
+
+#ifdef CLONE_CHECK
+ if (check_clones(acptr) > CLONE_MAX)
+ {
+ sendto_flag(SCH_LOCAL, "Rejecting connection from %s[%s].",
+ (acptr->hostp) ? acptr->hostp->h_name : "",
+ acptr->sockhost);
+ sendto_flog(acptr, " ?Clone? ", 0, "<none>",
+ (acptr->hostp) ? acptr->hostp->h_name :
+ acptr->sockhost);
+ del_queries((char *)acptr);
+# ifdef INET6
+ (void)sendto(acptr->fd,
+ "ERROR :Too rapid connections from your host\r\n",
+ 46, 0, 0, 0);
+# else
+ (void)send(acptr->fd,
+ "ERROR :Too rapid connections from your host\r\n",
+ 46, 0);
+# endif
+ goto add_con_refuse;
+ }
+#endif
+ acptr->fd = fd;
+ set_non_blocking(acptr->fd, acptr);
+ if (set_sock_opts(acptr->fd, acptr) == -1)
+ goto add_con_refuse;
+ if (aconf)
+ aconf->clients++;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ local[fd] = acptr;
+ add_fd(fd, &fdall);
+ acptr->acpt = cptr;
+ add_client_to_list(acptr);
+ start_auth(acptr);
+#if defined(USE_IAUTH)
+ if (!isatty(fd) && !DoingDNS(acptr))
+ {
+ int i = 0;
+
+ while (acptr->hostp->h_aliases[i])
+ sendto_iauth("%d A %s", acptr->fd,
+ acptr->hostp->h_aliases[i++]);
+ if (acptr->hostp->h_name)
+ sendto_iauth("%d N %s",acptr->fd,acptr->hostp->h_name);
+ else if (acptr->hostp->h_aliases[0])
+ sendto_iauth("%d n", acptr->fd);
+ }
+#endif
+ return acptr;
+}
+
+#ifdef UNIXPORT
+static void add_unixconnection(cptr, fd)
+aClient *cptr;
+int fd;
+{
+ aClient *acptr;
+ aConfItem *aconf = NULL;
+
+ acptr = make_client(NULL);
+
+ /* Copy ascii address to 'sockhost' just in case. Then we
+ * have something valid to put into error messages...
+ */
+ get_sockhost(acptr, me.sockhost);
+ aconf = cptr->confs->value.aconf;
+ if (aconf)
+ {
+ if (IsIllegal(aconf))
+ {
+ ircstp->is_ref++;
+ acptr->fd = -2;
+ free_client(acptr);
+ (void)close(fd);
+ return;
+ }
+ else
+ aconf->clients++;
+ }
+ acptr->fd = fd;
+ if (fd > highest_fd)
+ highest_fd = fd;
+ local[fd] = acptr;
+ add_fd(fd, &fdall);
+ acptr->acpt = cptr;
+ SetUnixSock(acptr);
+ bcopy((char *)&me.ip, (char *)&acptr->ip, sizeof(struct IN_ADDR));
+
+ add_client_to_list(acptr);
+ set_non_blocking(acptr->fd, acptr);
+ (void)set_sock_opts(acptr->fd, acptr);
+ return;
+}
+#endif
+
+/*
+** read_listener
+**
+** Accept incoming connections, extracted from read_message() 98/12 -kalt
+** Up to 10 connections will be accepted, unless SLOW_ACCEPT is defined.
+*/
+static void
+read_listener(cptr)
+aClient *cptr;
+{
+ int fdnew, max = 10;
+
+#if defined(SLOW_ACCEPT)
+ max = 1;
+#endif
+ while (max--)
+ {
+ /*
+ ** There may be many reasons for error return, but in otherwise
+ ** correctly working environment the probable cause is running
+ ** out of file descriptors (EMFILE, ENFILE or others?). The
+ ** man pages for accept don't seem to list these as possible,
+ ** although it's obvious that it may happen here.
+ ** Thus no specific errors are tested at this point, just
+ ** assume that connections cannot be accepted until some old
+ ** is closed first.
+ */
+ if ((fdnew = accept(cptr->fd, NULL, NULL)) < 0)
+ {
+ if (errno != EWOULDBLOCK)
+ report_error("Cannot accept connections %s:%s",
+ cptr);
+ break;
+ }
+ ircstp->is_ac++;
+ if (fdnew >= MAXCLIENTS)
+ {
+ ircstp->is_ref++;
+ sendto_flag(SCH_ERROR, "All connections in use. (%s)",
+ get_client_name(cptr, TRUE));
+ find_bounce(NULL, 0, fdnew);
+#ifdef INET6
+ (void)sendto(fdnew,
+ "ERROR :All connections in use\r\n",
+ 32, 0, 0, 0);
+#else
+ (void)send(fdnew, "ERROR :All connections in use\r\n",
+ 32, 0);
+#endif
+ (void)close(fdnew);
+ continue;
+ }
+ /*
+ * Use of add_connection (which never fails :) meLazy
+ * Never say never. MrMurphy visited here. -Vesa
+ */
+#ifdef UNIXPORT
+ if (IsUnixSocket(cptr))
+ add_unixconnection(cptr, fdnew);
+ else
+#endif
+ if (!add_connection(cptr, fdnew))
+ continue;
+ nextping = timeofday; /* isn't this abusive? -kalt */
+ istat.is_unknown++;
+ }
+}
+
+/*
+** client_packet
+**
+** Process data from receive buffer to client.
+** Extracted from read_packet() 960804/291p3/Vesa
+*/
+static int client_packet(cptr)
+Reg aClient *cptr;
+{
+ Reg int dolen = 0;
+
+ while (DBufLength(&cptr->recvQ) && !NoNewLine(cptr) &&
+ ((cptr->status < STAT_UNKNOWN) ||
+ (cptr->since - timeofday < MAXPENALTY)))
+ {
+ /*
+ ** If it has become registered as a Service or Server
+ ** then skip the per-message parsing below.
+ */
+ if (IsService(cptr) || IsServer(cptr))
+ {
+ dolen = dbuf_get(&cptr->recvQ, readbuf,
+ sizeof(readbuf));
+ if (dolen <= 0)
+ break;
+ dolen = dopacket(cptr, readbuf, dolen);
+ if (dolen == 2 && cptr->since == cptr->lasttime)
+ cptr->since += 5;
+ if (dolen)
+ return dolen;
+ break;
+ }
+ dolen = dbuf_getmsg(&cptr->recvQ, readbuf,
+ sizeof(readbuf));
+ /*
+ ** Devious looking...whats it do ? well..if a client
+ ** sends a *long* message without any CR or LF, then
+ ** dbuf_getmsg fails and we pull it out using this
+ ** loop which just gets the next 512 bytes and then
+ ** deletes the rest of the buffer contents.
+ ** -avalon
+ */
+ while (dolen <= 0)
+ {
+ if (dolen < 0)
+ return exit_client(cptr, cptr, &me,
+ "dbuf_getmsg fail");
+ if (DBufLength(&cptr->recvQ) < 510)
+ { /* hmm? */
+ cptr->flags |= FLAGS_NONL;
+ break;
+ }
+ dolen = dbuf_get(&cptr->recvQ, readbuf, 511);
+ if (dolen > 0 && DBufLength(&cptr->recvQ))
+ DBufClear(&cptr->recvQ);
+ }
+
+ /* Is it okay not to test for other return values? -krys */
+ if (dolen > 0 &&
+ (dopacket(cptr, readbuf, dolen) == FLUSH_BUFFER))
+ return FLUSH_BUFFER;
+ }
+ return 1;
+}
+
+/*
+** read_packet
+**
+** Read a 'packet' of data from a connection and process it. Read in 8k
+** chunks to give a better performance rating (for server connections).
+** Do some tricky stuff for client connections to make sure they don't do
+** any flooding >:-) -avalon
+*/
+static int read_packet(cptr, msg_ready)
+Reg aClient *cptr;
+int msg_ready;
+{
+ Reg int length = 0, done;
+
+ if (msg_ready &&
+ !(IsPerson(cptr) && DBufLength(&cptr->recvQ) > 6090))
+ {
+ errno = 0;
+#ifdef INET6
+ length = recvfrom(cptr->fd, readbuf, sizeof(readbuf), 0, 0, 0);
+#else
+ length = recv(cptr->fd, readbuf, sizeof(readbuf), 0);
+#endif
+#if defined(DEBUGMODE) && defined(DEBUG_READ)
+ if (length > 0)
+ Debug((DEBUG_READ,
+ "recv = %d bytes from %d[%s]:[%*.*s]\n",
+ length, cptr->fd, cptr->name, length, length,
+ readbuf));
+#endif
+
+ Debug((DEBUG_DEBUG, "Received %d(%d-%s) bytes from %d %s",
+ length, errno, strerror(errno),
+ cptr->fd, get_client_name(cptr, TRUE)));
+ cptr->lasttime = timeofday;
+ if (cptr->lasttime > cptr->since)
+ cptr->since = cptr->lasttime;
+ cptr->flags &= ~(FLAGS_PINGSENT|FLAGS_NONL);
+ /*
+ * If not ready, fake it so it isnt closed
+ */
+ if (length == -1 &&
+ ((errno == EWOULDBLOCK) || (errno == EAGAIN)))
+ return 1;
+ if (length <= 0)
+ return length;
+ }
+ else if (msg_ready)
+ return exit_client(cptr, cptr, &me, "EOF From Client");
+
+ /*
+ ** For server connections, we process as many as we can without
+ ** worrying about the time of day or anything :)
+ */
+ if (IsServer(cptr) || IsConnecting(cptr) || IsHandshake(cptr) ||
+ IsService(cptr))
+ {
+ if (length > 0)
+ {
+ done = dopacket(cptr, readbuf, length);
+ if (done && done != 2)
+ return done;
+ }
+ }
+ else
+ {
+ /*
+ ** Before we even think of parsing what we just read, stick
+ ** it on the end of the receive queue and do it when its
+ ** turn comes around.
+ */
+ if (length && dbuf_put(&cptr->recvQ, readbuf, length) < 0)
+ return exit_client(cptr, cptr, &me, "dbuf_put fail");
+
+ if (IsPerson(cptr) &&
+ DBufLength(&cptr->recvQ) > CLIENT_FLOOD)
+ {
+ cptr->exitc = EXITC_FLOOD;
+ return exit_client(cptr, cptr, &me, "Excess Flood");
+ }
+
+ return client_packet(cptr);
+ }
+ return 1;
+}
+
+
+/*
+ * Check all connections for new connections and input data that is to be
+ * processed. Also check for connections with data queued and whether we can
+ * write it out.
+ */
+int read_message(delay, fdp, ro)
+time_t delay; /* Don't ever use ZERO here, unless you mean to poll and then
+ * you have to have sleep/wait somewhere else in the code.--msa
+ * Actually, ZERO is NOT ZERO anymore.. see below -kalt
+ */
+FdAry *fdp;
+int ro;
+{
+#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;
+ struct pollfd * res_pfd = NULL;
+ struct pollfd * udp_pfd = NULL;
+ struct pollfd * ad_pfd = NULL;
+ aClient * authclnts[MAXCONNECTIONS]; /* mapping of auth fds to client ptrs */
+ int nbr_pfds = 0;
+#endif
+
+ aClient *cptr;
+ int nfds, ret = 0;
+ struct timeval wait;
+ time_t delay2 = delay;
+ int res, length, fd, i;
+ int auth;
+
+ for (res = 0;;)
+ {
+#if ! USE_POLL
+ FD_ZERO(&read_set);
+ FD_ZERO(&write_set);
+#else
+ /* set up such that CHECK_FD works */
+ nbr_pfds = 0;
+ pfd = poll_fdarray;
+ pfd->fd = -1;
+ res_pfd = NULL;
+ udp_pfd = NULL;
+ ad_pfd = NULL;
+#endif /* USE_POLL */
+ auth = 0;
+
+#if USE_POLL
+ if ( auth == 0 )
+ bzero((char *) authclnts, sizeof( authclnts ));
+#endif
+ for (i = fdp->highest; i >= 0; i--)
+ {
+ if (!(cptr = local[fd = fdp->fd[i]]) ||
+ IsLog(cptr) || IsHeld(cptr))
+ continue;
+ Debug((DEBUG_L11, "fd %d cptr %#x %d %#x %s",
+ fd, cptr, cptr->status, cptr->flags,
+ get_client_name(cptr,TRUE)));
+ /* authentication fd's */
+ if (DoingAuth(cptr))
+ {
+ auth++;
+ SET_READ_EVENT(cptr->authfd);
+ Debug((DEBUG_NOTICE,"auth on %x %d", cptr,
+ fd));
+ if (cptr->flags & FLAGS_WRAUTH)
+ SET_WRITE_EVENT(cptr->authfd);
+#if USE_POLL
+ authclnts[cptr->authfd] = cptr;
+#else
+ if (cptr->authfd > highfd)
+ highfd = cptr->authfd;
+#endif
+ }
+ /*
+ ** if any of these is true, data won't be parsed
+ ** so no need to check for anything!
+ */
+#if defined(USE_IAUTH)
+ if (DoingDNS(cptr) || DoingAuth(cptr) ||
+ WaitingXAuth(cptr) ||
+ (DoingXAuth(cptr) &&
+ !(iauth_options & XOPT_EARLYPARSE)))
+#else
+ if (DoingDNS(cptr) || DoingAuth(cptr))
+#endif
+ continue;
+#if ! USE_POLL
+ if (fd > highfd)
+ highfd = fd;
+#endif
+ /*
+ ** Checking for new connections is only done up to
+ ** once per second.
+ */
+ if (IsListening(cptr))
+ {
+ if (timeofday > cptr->lasttime + 1 && ro == 0)
+ {
+ SET_READ_EVENT( fd );
+ }
+ else if (delay2 > 1)
+ delay2 = 1;
+ continue;
+ }
+
+ /*
+ ** This is very approximate, it should take
+ ** cptr->since into account. -kalt
+ */
+ if (DBufLength(&cptr->recvQ) && delay2 > 2)
+ delay2 = 1;
+
+ if (IsRegisteredUser(cptr))
+ {
+ if (cptr->since - timeofday < MAXPENALTY+1)
+ SET_READ_EVENT( fd );
+ }
+ else if (DBufLength(&cptr->recvQ) < 4088)
+ SET_READ_EVENT( fd );
+
+ /*
+ ** If we have anything in the sendQ, check if there is
+ ** room to write data.
+ */
+ if (DBufLength(&cptr->sendQ) || IsConnecting(cptr) ||
+#ifdef ZIP_LINKS
+ ((cptr->flags & FLAGS_ZIP) &&
+ (cptr->zip->outcount > 0)) ||
+#endif
+ IsReconnect(cptr))
+ if (IsServer(cptr) || IsConnecting(cptr) ||
+ ro == 0)
+ SET_WRITE_EVENT( fd );
+ }
+
+ if (udpfd >= 0)
+ {
+ SET_READ_EVENT(udpfd);
+#if ! USE_POLL
+ if (udpfd > highfd)
+ highfd = udpfd;
+#else
+ udp_pfd = pfd;
+#endif
+ }
+ if (resfd >= 0)
+ {
+ SET_READ_EVENT(resfd);
+#if ! USE_POLL
+ if (resfd > highfd)
+ highfd = resfd;
+#else
+ res_pfd = pfd;
+#endif
+ }
+#if defined(USE_IAUTH)
+ if (adfd >= 0)
+ {
+ SET_READ_EVENT(adfd);
+# if ! USE_POLL
+ if (adfd > highfd)
+ highfd = adfd;
+# else
+ ad_pfd = pfd;
+# endif
+ }
+#endif
+ Debug((DEBUG_L11, "udpfd %d resfd %d adfd %d", udpfd, resfd,
+ adfd));
+#if ! USE_POLL
+ Debug((DEBUG_L11, "highfd %d", highfd));
+#endif
+
+ wait.tv_sec = MIN(delay2, delay);
+ wait.tv_usec = (delay == 0) ? 200000 : 0;
+#if ! USE_POLL
+ nfds = select(highfd + 1, (SELECT_FDSET_TYPE *)&read_set,
+ (SELECT_FDSET_TYPE *)&write_set, 0, &wait);
+#else
+ nfds = poll( poll_fdarray, nbr_pfds,
+ wait.tv_sec * 1000 + wait.tv_usec/1000 );
+#endif
+ ret = nfds;
+ if (nfds == -1 && errno == EINTR)
+ return -1;
+ else if (nfds >= 0)
+ break;
+#if ! USE_POLL
+ report_error("select %s:%s", &me);
+#else
+ report_error("poll %s:%s", &me);
+#endif
+ res++;
+ if (res > 5)
+ restart("too many select()/poll() errors");
+ sleep(10);
+ timeofday = time(NULL);
+ } /* for(res=0;;) */
+
+ timeofday = time(NULL);
+ if (nfds > 0 &&
+#if ! USE_POLL
+ resfd >= 0 &&
+#else
+ (pfd = res_pfd) &&
+#endif
+ TST_READ_EVENT(resfd))
+ {
+ CLR_READ_EVENT(resfd);
+ nfds--;
+ do_dns_async();
+ }
+ if (nfds > 0 &&
+#if ! USE_POLL
+ udpfd >= 0 &&
+#else
+ (pfd = udp_pfd) &&
+#endif
+ TST_READ_EVENT(udpfd))
+ {
+ CLR_READ_EVENT(udpfd);
+ nfds--;
+ polludp();
+ }
+#if defined(USE_IAUTH)
+ if (nfds > 0 &&
+# if ! USE_POLL
+ adfd >= 0 &&
+# else
+ (pfd = ad_pfd) &&
+# endif
+ TST_READ_EVENT(adfd))
+ {
+ CLR_READ_EVENT(adfd);
+ nfds--;
+ read_iauth();
+ }
+#endif
+
+#if ! USE_POLL
+ for (i = fdp->highest; i >= 0; i--)
+#else
+ for (pfd = poll_fdarray, i = 0; i < nbr_pfds; i++, pfd++ )
+#endif
+ {
+#if ! USE_POLL
+ if (!(cptr = local[fd = fdp->fd[i]]))
+ continue;
+#else
+ fd = pfd->fd;
+ if ((cptr = authclnts[fd]))
+ {
+#endif
+ /*
+ * check for the auth fd's
+ */
+ if (auth > 0 && nfds > 0
+#if ! USE_POLL
+ && cptr->authfd >= 0
+#endif
+ )
+ {
+ auth--;
+ if (TST_WRITE_EVENT(cptr->authfd))
+ {
+ nfds--;
+ send_authports(cptr);
+ }
+ else if (TST_READ_EVENT(cptr->authfd))
+ {
+ nfds--;
+ read_authports(cptr);
+ }
+ continue;
+ }
+#if USE_POLL
+ }
+ fd = pfd->fd;
+ if (!(cptr = local[fd]))
+ continue;
+#else
+ fd = cptr->fd;
+#endif
+ /*
+ * accept connections
+ */
+ if (TST_READ_EVENT(fd) && IsListening(cptr))
+ {
+ CLR_READ_EVENT(fd);
+ cptr->lasttime = timeofday;
+ read_listener(cptr);
+ continue;
+ }
+ if (IsMe(cptr))
+ continue;
+ if (TST_WRITE_EVENT(fd))
+ {
+ int write_err = 0;
+ /*
+ ** ...room for writing, empty some queue then...
+ */
+ if (IsConnecting(cptr))
+ write_err = completed_connection(cptr);
+ if (!write_err)
+ (void)send_queued(cptr);
+ if (IsDead(cptr) || write_err)
+ {
+deadsocket:
+ if (TST_READ_EVENT(fd))
+ CLR_READ_EVENT(fd);
+ cptr->exitc = EXITC_ERROR;
+ (void)exit_client(cptr, cptr, &me,
+ strerror(get_sockerr(cptr)));
+ continue;
+ }
+ }
+ length = 1; /* for fall through case */
+ if (!NoNewLine(cptr) || TST_READ_EVENT(fd))
+ {
+ if (!DoingAuth(cptr))
+ length = read_packet(cptr, TST_READ_EVENT(fd));
+ }
+ readcalls++;
+ if (length == FLUSH_BUFFER)
+ continue;
+ else if (length > 0)
+ flush_connections(cptr->fd);
+ if (IsDead(cptr))
+ goto deadsocket;
+ if (length > 0)
+ continue;
+
+ /* Ghost! Unknown users are tagged in parse() since 2.9.
+ * Let's not drop the uplink but just the ghost's message.
+ */
+ if (length == -3)
+ continue;
+
+ /*
+ ** NB: This following section has been modified to *expect*
+ ** cptr to be valid (ie if (length == FLUSH_BUFFER) is
+ ** above and stays there). - avalon 24/9/94
+ */
+ /*
+ ** ...hmm, with non-blocking sockets we might get
+ ** here from quite valid reasons, although.. why
+ ** would select report "data available" when there
+ ** wasn't... so, this must be an error anyway... --msa
+ ** actually, EOF occurs when read() returns 0 and
+ ** in due course, select() returns that fd as ready
+ ** for reading even though it ends up being an EOF. -avalon
+ */
+ Debug((DEBUG_ERROR, "READ ERROR: fd = %d %d %d",
+ cptr->fd, errno, length));
+
+ if (IsServer(cptr) || IsHandshake(cptr))
+ {
+ int timeconnected = timeofday - cptr->firsttime;
+
+ if (length == 0)
+ sendto_flag(SCH_NOTICE,
+ "Server %s closed the connection (%d, %2d:%02d:%02d)",
+ get_client_name(cptr, FALSE),
+ timeconnected / 86400,
+ (timeconnected % 86400) / 3600,
+ (timeconnected % 3600)/60,
+ timeconnected % 60);
+ else /* this must be for -1 */
+ {
+ report_error("Lost connection to %s:%s",cptr);
+ sendto_flag(SCH_NOTICE,
+ "%s had been connected for %d, %2d:%02d:%02d",
+ get_client_name(cptr, FALSE),
+ timeconnected / 86400,
+ (timeconnected % 86400) / 3600,
+ (timeconnected % 3600)/60,
+ timeconnected % 60);
+ if (hold_server(cptr) == 0)
+ continue;
+ }
+ }
+ (void)exit_client(cptr, cptr, &me, length >= 0 ?
+ "EOF From client" :
+ strerror(get_sockerr(cptr)));
+ } /* for(i) */
+ return ret;
+}
+
+/*
+ * connect_server
+ */
+int connect_server(aconf, by, hp)
+aConfItem *aconf;
+aClient *by;
+struct hostent *hp;
+{
+ Reg struct SOCKADDR *svp;
+ Reg aClient *cptr, *c2ptr;
+ Reg char *s;
+ int i, len;
+
+#ifdef INET6
+ Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
+ aconf->name, aconf->host,
+ inet_ntop(AF_INET6, (char *)&aconf->ipnum, mydummy,
+ MYDUMMY_SIZE)));
+#else
+ Debug((DEBUG_NOTICE,"Connect to %s[%s] @%s",
+ aconf->name, aconf->host,
+ inetntoa((char *)&aconf->ipnum)));
+#endif
+
+ if ((c2ptr = find_server(aconf->name, NULL)))
+ {
+ sendto_flag(SCH_NOTICE, "Server %s already present from %s",
+ aconf->name, get_client_name(c2ptr, TRUE));
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Server %s already present from %s",
+ ME, by->name, aconf->name,
+ get_client_name(c2ptr, TRUE));
+ return -1;
+ }
+
+ /*
+ * If we don't know the IP# for this host and it is a hostname and
+ * not a ip# string, then try and find the appropriate host record.
+ */
+ if (!aconf->ipnum.S_ADDR && *aconf->host != '/')
+ {
+ Link lin;
+
+ lin.flags = ASYNC_CONNECT;
+ lin.value.aconf = aconf;
+ nextdnscheck = 1;
+ s = (char *)index(aconf->host, '@');
+ s++; /* should NEVER be NULL */
+#ifdef INET6
+ if (!inet_pton(AF_INET6, s, aconf->ipnum.s6_addr))
+#else
+ if ((aconf->ipnum.s_addr = inetaddr(s)) == -1)
+#endif
+ {
+#ifdef INET6
+ bzero(aconf->ipnum.s6_addr, IN6ADDRSZ);
+#else
+ aconf->ipnum.s_addr = 0;
+#endif
+ hp = gethost_byname(s, &lin);
+ Debug((DEBUG_NOTICE, "co_sv: hp %x ac %x na %s ho %s",
+ hp, aconf, aconf->name, s));
+ if (!hp)
+ return 0;
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ }
+ }
+ cptr = make_client(NULL);
+ cptr->hostp = hp;
+ /*
+ * Copy these in so we have something for error detection.
+ */
+ strncpyzt(cptr->name, aconf->name, sizeof(cptr->name));
+ strncpyzt(cptr->sockhost, aconf->host, HOSTLEN+1);
+
+#ifdef UNIXPORT
+ if (*aconf->host == '/') /* (/ starts a 2), Unix domain -- dl*/
+ svp = connect_unix(aconf, cptr, &len);
+ else
+ svp = connect_inet(aconf, cptr, &len);
+#else
+ svp = connect_inet(aconf, cptr, &len);
+#endif
+
+ if (!svp)
+ {
+ if (cptr->fd != -1)
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ return -1;
+ }
+
+ set_non_blocking(cptr->fd, cptr);
+ (void)set_sock_opts(cptr->fd, cptr);
+ (void)signal(SIGALRM, dummy);
+ (void)alarm(4);
+ if (connect(cptr->fd, (SAP)svp, len) < 0 && errno != EINPROGRESS)
+ {
+ i = errno; /* other system calls may eat errno */
+ (void)alarm(0);
+ report_error("Connect to host %s failed: %s",cptr);
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Connect to host %s failed.",
+ ME, by->name, cptr);
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ errno = i;
+ if (errno == EINTR)
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ (void)alarm(0);
+
+ /* Attach config entries to client here rather than in
+ * completed_connection. This to avoid null pointer references
+ * when name returned by gethostbyaddr matches no C lines
+ * (could happen in 2.6.1a when host and servername differ).
+ * No need to check access and do gethostbyaddr calls.
+ * There must at least be one as we got here C line... meLazy
+ */
+ (void)attach_confs_host(cptr, aconf->host, CFLAG|NFLAG);
+
+ if (!find_conf_host(cptr->confs, aconf->host, NFLAG) ||
+ !find_conf_host(cptr->confs, aconf->host, CFLAG))
+ {
+ sendto_flag(SCH_NOTICE,
+ "Host %s is not enabled for connecting:no C/N-line",
+ aconf->host);
+ if (by && IsPerson(by) && !MyClient(by))
+ sendto_one(by,
+ ":%s NOTICE %s :Connect to host %s failed.",
+ ME, by->name, cptr);
+ det_confs_butmask(cptr, 0);
+ (void)close(cptr->fd);
+ cptr->fd = -2;
+ free_client(cptr);
+ return(-1);
+ }
+ /*
+ ** The socket has been connected or connect is in progress.
+ */
+ (void)make_server(cptr);
+ if (by && IsPerson(by))
+ {
+ (void)strcpy(cptr->serv->by, by->name);
+ cptr->serv->user = by->user;
+ by->user->refcnt++;
+ }
+ else
+ (void)strcpy(cptr->serv->by, "AutoConn.");
+ cptr->serv->up = ME;
+ cptr->serv->nline = aconf;
+ if (cptr->fd > highest_fd)
+ highest_fd = cptr->fd;
+ add_fd(cptr->fd, &fdall);
+ local[cptr->fd] = cptr;
+ cptr->acpt = &me;
+ SetConnecting(cptr);
+
+ get_sockhost(cptr, aconf->host);
+ add_client_to_list(cptr);
+ nextping = timeofday;
+ istat.is_unknown++;
+
+ return 0;
+}
+
+static struct SOCKADDR *connect_inet(aconf, cptr, lenp)
+Reg aConfItem *aconf;
+Reg aClient *cptr;
+int *lenp;
+{
+ static struct SOCKADDR_IN server;
+ Reg struct hostent *hp;
+ aClient *acptr;
+ int i;
+
+ /*
+ * Might as well get sockhost from here, the connection is attempted
+ * with it so if it fails its useless.
+ */
+ cptr->fd = socket(AFINET, SOCK_STREAM, 0);
+ if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_NOTICE,
+ "No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+ mysk.SIN_PORT = 0;
+ bzero((char *)&server, sizeof(server));
+ server.SIN_FAMILY = AFINET;
+ get_sockhost(cptr, aconf->host);
+
+ if (cptr->fd == -1)
+ {
+ report_error("opening stream socket to server %s:%s", cptr);
+ return NULL;
+ }
+ /*
+ ** Bind to a local IP# (with unknown port - let unix decide) so
+ ** we have some chance of knowing the IP# that gets used for a host
+ ** with more than one IP#.
+ ** With VIFs, M:line defines outgoing IP# and initialises mysk.
+ */
+ if (bind(cptr->fd, (SAP)&mysk, sizeof(mysk)) == -1)
+ {
+ report_error("error binding to local port for %s:%s", cptr);
+ return NULL;
+ }
+ /*
+ * By this point we should know the IP# of the host listed in the
+ * conf line, whether as a result of the hostname lookup or the ip#
+ * being present instead. If we don't know it, then the connect fails.
+ */
+#ifdef INET6
+ if (isdigit(*aconf->host) && (AND16(aconf->ipnum.s6_addr) == 255))
+ if (!inet_pton(AF_INET6, aconf->host,aconf->ipnum.s6_addr))
+ bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ);
+ if (AND16(aconf->ipnum.s6_addr) == 255)
+#else
+ if (isdigit(*aconf->host) && (aconf->ipnum.s_addr == -1))
+ aconf->ipnum.s_addr = inetaddr(aconf->host);
+ if (aconf->ipnum.s_addr == -1)
+#endif
+ {
+ hp = cptr->hostp;
+ if (!hp)
+ {
+ Debug((DEBUG_FATAL, "%s: unknown host", aconf->host));
+ return NULL;
+ }
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ }
+ bcopy((char *)&aconf->ipnum, (char *)&server.SIN_ADDR,
+ sizeof(struct IN_ADDR));
+ bcopy((char *)&aconf->ipnum, (char *)&cptr->ip,
+ sizeof(struct IN_ADDR));
+ cptr->port = (aconf->port > 0) ? aconf->port : portnum;
+ server.SIN_PORT = htons(cptr->port);
+ /*
+ * Look for a duplicate IP#,port pair among already open connections
+ * (This caters for unestablished connections).
+ */
+ for (i = highest_fd; i >= 0; i--)
+ if ((acptr = local[i]) &&
+ !bcmp((char *)&cptr->ip, (char *)&acptr->ip,
+ sizeof(cptr->ip)) && server.SIN_PORT == acptr->port)
+ return NULL;
+ *lenp = sizeof(server);
+ return (struct SOCKADDR *)&server;
+}
+
+#ifdef UNIXPORT
+/* connect_unix
+ *
+ * Build a socket structure for cptr so that it can connet to the unix
+ * socket defined by the conf structure aconf.
+ */
+static struct SOCKADDR *connect_unix(aconf, cptr, lenp)
+aConfItem *aconf;
+aClient *cptr;
+int *lenp;
+{
+ static struct sockaddr_un sock;
+
+ if ((cptr->fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+ {
+ report_error("Unix domain connect to host %s failed: %s", cptr);
+ return NULL;
+ }
+ else if (cptr->fd >= MAXCLIENTS)
+ {
+ sendto_flag(SCH_NOTICE,
+ "No more connections allowed (%s)", cptr->name);
+ return NULL;
+ }
+
+ get_sockhost(cptr, aconf->host);
+ strncpyzt(sock.sun_path, aconf->host + 2, sizeof(sock.sun_path));
+ sock.sun_family = AF_UNIX;
+ *lenp = strlen(sock.sun_path) + 2;
+
+ SetUnixSock(cptr);
+ return (struct sockaddr *)&sock;
+}
+#endif
+
+/*
+ * The following section of code performs summoning of users to irc.
+ */
+#if defined(ENABLE_SUMMON) || defined(ENABLE_USERS)
+int utmp_open()
+{
+#ifdef O_NOCTTY
+ return (open(UTMP, O_RDONLY|O_NOCTTY));
+#else
+ return (open(UTMP, O_RDONLY));
+#endif
+}
+
+int utmp_read(fd, name, line, host, hlen)
+int fd, hlen;
+char *name, *line, *host;
+{
+ struct utmp ut;
+ while (read(fd, (char *)&ut, sizeof (struct utmp))
+ == sizeof (struct utmp))
+ {
+ strncpyzt(name, ut.ut_name, 9);
+ strncpyzt(line, ut.ut_line, 10);
+#ifdef USER_PROCESS
+# if defined(HPUX) || defined(AIX)
+ strncpyzt(host,(ut.ut_host[0]) ? (ut.ut_host) : ME, 16);
+# else
+ strncpyzt(host, ME, 9);
+# endif
+ if (ut.ut_type == USER_PROCESS)
+ return 0;
+#else
+ strncpyzt(host, (ut.ut_host[0]) ? (ut.ut_host) : ME,
+ hlen);
+ if (ut.ut_name[0])
+ return 0;
+#endif
+ }
+ return -1;
+}
+
+int utmp_close(fd)
+int fd;
+{
+ return(close(fd));
+}
+
+#ifdef ENABLE_SUMMON
+void summon(who, namebuf, linebuf, chname)
+aClient *who;
+char *namebuf, *linebuf, *chname;
+{
+ static char wrerr[] = "NOTICE %s :Write error. Couldn't summon.";
+ int fd;
+ char line[120];
+ struct tm *tp;
+
+ tp = localtime(&timeofday);
+ if (strlen(linebuf) > (size_t) 9)
+ {
+ sendto_one(who,"NOTICE %s :Serious fault in SUMMON.",
+ who->name);
+ sendto_one(who,
+ "NOTICE %s :linebuf too long. Inform Administrator",
+ who->name);
+ return;
+ }
+ /*
+ * Following line added to prevent cracking to e.g. /dev/kmem if
+ * UTMP is for some silly reason writable to everyone...
+ */
+ if ((linebuf[0] != 't' || linebuf[1] != 't' || linebuf[2] != 'y')
+ && (linebuf[0] != 'c' || linebuf[1] != 'o' || linebuf[2] != 'n')
+#ifdef HPUX
+ && (linebuf[0] != 'p' || linebuf[1] != 't' || linebuf[2] != 'y' ||
+ linebuf[3] != '/')
+#endif
+ )
+ {
+ sendto_one(who,
+ "NOTICE %s :Looks like mere mortal souls are trying to",
+ who->name);
+ sendto_one(who,"NOTICE %s :enter the twilight zone... ",
+ who->name);
+ Debug((0, "%s (%s@%s, nick %s, %s)",
+ "FATAL: major security hack. Notify Administrator !",
+ who->username, who->user->host,
+ who->name, who->info));
+ return;
+ }
+
+ SPRINTF(line,"/dev/%s", linebuf);
+ (void)alarm(5);
+#ifdef O_NOCTTY
+ if ((fd = open(line, O_WRONLY | O_NDELAY | O_NOCTTY)) == -1)
+#else
+ if ((fd = open(line, O_WRONLY | O_NDELAY)) == -1)
+#endif
+ {
+ (void)alarm(0);
+ sendto_one(who,
+ "NOTICE %s :%s seems to have disabled summoning...",
+ who->name, namebuf);
+ return;
+ }
+#if !defined(O_NOCTTY) && defined(TIOCNOTTY)
+ (void)ioctl(fd, TIOCNOTTY, NULL);
+#endif
+ (void)alarm(0);
+ (void)sprintf(line,"\n\r\007Message from IRC_Daemon@%s at %d:%02d\n\r",
+ ME, tp->tm_hour, tp->tm_min);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ (void)strcpy(line, "ircd: You are being summoned to Internet Relay \
+Chat on\n\r");
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ SPRINTF(line, "ircd: Channel %s, by %s@%s (%s) %s\n\r", chname,
+ who->user->username, who->user->host, who->name, who->info);
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)alarm(0);
+ (void)strcpy(line,"ircd: Respond with irc\n\r");
+ (void)alarm(5);
+ if (write(fd, line, strlen(line)) != strlen(line))
+ {
+ (void)alarm(0);
+ (void)close(fd);
+ sendto_one(who, wrerr, who->name);
+ return;
+ }
+ (void)close(fd);
+ (void)alarm(0);
+ sendto_one(who, rpl_str(RPL_SUMMONING, who->name), namebuf);
+ return;
+}
+# endif
+#endif /* ENABLE_SUMMON */
+
+/*
+** find the real hostname for the host running the server (or one which
+** matches the server's name) and its primary IP#. Hostname is stored
+** in the client structure passed as a pointer.
+*/
+void get_my_name(cptr, name, len)
+aClient *cptr;
+char *name;
+int len;
+{
+ static char tmp[HOSTLEN+1];
+ struct hostent *hp;
+ char *cname = cptr->name;
+ aConfItem *aconf;
+
+ /*
+ ** Setup local socket structure to use for binding to.
+ */
+ bzero((char *)&mysk, sizeof(mysk));
+ mysk.SIN_FAMILY = AFINET;
+
+ if ((aconf = find_me())->passwd && isdigit(*aconf->passwd))
+#ifdef INET6
+ if(!inet_pton(AF_INET6, aconf->passwd, mysk.sin6_addr.s6_addr))
+ bcopy(minus_one, mysk.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ mysk.sin_addr.s_addr = inetaddr(aconf->passwd);
+#endif
+
+ if (gethostname(name, len) == -1)
+ return;
+ name[len] = '\0';
+
+ /* assume that a name containing '.' is a FQDN */
+ if (!index(name,'.'))
+ add_local_domain(name, len - strlen(name));
+
+ /*
+ ** If hostname gives another name than cname, then check if there is
+ ** a CNAME record for cname pointing to hostname. If so accept
+ ** cname as our name. meLazy
+ */
+ if (BadPtr(cname))
+ return;
+ if ((hp = gethostbyname(cname)) || (hp = gethostbyname(name)))
+ {
+ char *hname;
+ int i = 0;
+
+ for (hname = hp->h_name; hname; hname = hp->h_aliases[i++])
+ {
+ strncpyzt(tmp, hname, sizeof(tmp));
+ add_local_domain(tmp, sizeof(tmp) - strlen(tmp));
+
+ /*
+ ** Copy the matching name over and store the
+ ** 'primary' IP# as 'myip' which is used
+ ** later for making the right one is used
+ ** for connecting to other hosts.
+ */
+ if (!mycmp(ME, tmp))
+ break;
+ }
+ if (mycmp(ME, tmp))
+ strncpyzt(name, hp->h_name, len);
+ else
+ strncpyzt(name, tmp, len);
+ if (!aconf->passwd)
+ bcopy(hp->h_addr, (char *)&mysk.SIN_ADDR,
+ sizeof(struct IN_ADDR));
+ Debug((DEBUG_DEBUG,"local name is %s",
+ get_client_name(&me,TRUE)));
+ }
+ return;
+}
+
+/*
+** setup a UDP socket and listen for incoming packets
+*/
+int setup_ping(aconf)
+aConfItem *aconf;
+{
+ struct SOCKADDR_IN from;
+ int on = 1;
+
+ if (udpfd != -1)
+ return udpfd;
+ bzero((char *)&from, sizeof(from));
+ if (aconf->passwd && isdigit(*aconf->passwd))
+#ifdef INET6
+ if(!inet_pton(AF_INET6, aconf->passwd,from.sin6_addr.s6_addr))
+ bcopy(minus_one, from.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ from.sin_addr.s_addr = inetaddr(aconf->passwd);
+#endif
+ else
+#ifdef INET6
+ from.SIN_ADDR = in6addr_any;
+#else
+ from.sin_addr.s_addr = htonl(INADDR_ANY); /* hmmpf */
+#endif
+ from.SIN_PORT = htons((u_short) aconf->port);
+ from.SIN_FAMILY = AFINET;
+
+ if ((udpfd = socket(AFINET, SOCK_DGRAM, 0)) == -1)
+ {
+ Debug((DEBUG_ERROR, "socket udp : %s", strerror(errno)));
+ return -1;
+ }
+ if (SETSOCKOPT(udpfd, SOL_SOCKET, SO_REUSEADDR, &on, on) == -1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "setsockopt udp fd %d : %m", udpfd);
+#endif
+ Debug((DEBUG_ERROR, "setsockopt so_reuseaddr : %s",
+ strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ on = 0;
+ (void) SETSOCKOPT(udpfd, SOL_SOCKET, SO_BROADCAST, &on, on);
+ if (bind(udpfd, (SAP)&from, sizeof(from))==-1)
+ {
+#ifdef USE_SYSLOG
+ syslog(LOG_ERR, "bind udp.%d fd %d : %m",
+ ntohs(from.SIN_PORT), udpfd);
+#endif
+ Debug((DEBUG_ERROR, "bind : %s", strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ if (fcntl(udpfd, F_SETFL, FNDELAY)==-1)
+ {
+ Debug((DEBUG_ERROR, "fcntl fndelay : %s", strerror(errno)));
+ (void)close(udpfd);
+ return udpfd = -1;
+ }
+ Debug((DEBUG_INFO, "udpfd = %d, port %d", udpfd,ntohs(from.SIN_PORT)));
+ return udpfd;
+}
+
+
+void send_ping(aconf)
+aConfItem *aconf;
+{
+ Ping pi;
+ struct SOCKADDR_IN sin;
+ aCPing *cp = aconf->ping;
+
+#ifdef INET6
+ if (!aconf->ipnum.s6_addr || AND16(aconf->ipnum.s6_addr) == 255 || !cp->port)
+#else
+ if (!aconf->ipnum.s_addr || aconf->ipnum.s_addr == -1 || !cp->port)
+#endif
+ return;
+ if (aconf->class->conFreq == 0) /* avoid flooding */
+ return;
+ pi.pi_cp = aconf;
+ pi.pi_id = htonl(PING_CPING);
+ pi.pi_seq = cp->lseq++;
+ cp->seq++;
+ /*
+ * Only recognise stats from the last 20 minutes as significant...
+ * Try and fake sliding along a "window" here.
+ */
+ if (cp->seq > 1 && cp->seq * aconf->class->conFreq > 1200)
+ {
+ if (cp->recvd)
+ {
+ cp->ping -= (cp->ping / cp->recvd);
+ if (cp->recvd == cp->seq)
+ cp->recvd--;
+ }
+ else
+ cp->ping = 0;
+ cp->seq--;
+ }
+
+ bzero((char *)&sin, sizeof(sin));
+#ifdef INET6
+ bcopy(aconf->ipnum.s6_addr, sin.sin6_addr.s6_addr, IN6ADDRSZ);
+#else
+ sin.sin_addr.s_addr = aconf->ipnum.s_addr;
+#endif
+ sin.SIN_PORT = htons(cp->port);
+ sin.SIN_FAMILY = AFINET;
+ (void)gettimeofday(&pi.pi_tv, NULL);
+#ifdef INET6
+ Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
+ inet_ntop(AF_INET6, (char *)&aconf->ipnum,mydummy,MYDUMMY_SIZE),
+ cp->port, udpfd, sizeof(pi)));
+#else
+ Debug((DEBUG_SEND,"Send ping to %s,%d fd %d, %d bytes",
+ inetntoa((char *)&aconf->ipnum),
+ cp->port, udpfd, sizeof(pi)));
+#endif
+ (void)sendto(udpfd, (char *)&pi, sizeof(pi), 0,(SAP)&sin,sizeof(sin));
+}
+
+static int check_ping(buf, len)
+char *buf;
+int len;
+{
+ Ping pi;
+ aConfItem *aconf;
+ struct timeval tv;
+ double d;
+ aCPing *cp = NULL;
+ u_long rtt;
+
+ (void)gettimeofday(&tv, NULL);
+
+ if (len < sizeof(pi) + 8)
+ return -1;
+
+ bcopy(buf, (char *)&pi, sizeof(pi)); /* ensure nice byte align. */
+
+ for (aconf = conf; aconf; aconf = aconf->next)
+ if (pi.pi_cp == aconf && (cp = aconf->ping))
+ break;
+ if (!aconf || match(aconf->name, buf + sizeof(pi)))
+ return -1;
+
+ cp->recvd++;
+ cp->lrecvd++;
+ rtt = ((tv.tv_sec - pi.pi_tv.tv_sec) * 1000 +
+ (tv.tv_usec - pi.pi_tv.tv_usec) / 1000);
+ cp->ping += rtt;
+ cp->rtt += rtt;
+ if (cp->rtt > 1000000)
+ {
+ cp->ping = (cp->rtt /= cp->lrecvd);
+ cp->recvd = cp->lrecvd = 1;
+ cp->seq = cp->lseq = 1;
+ }
+ d = (double)cp->recvd / (double)cp->seq;
+ d = pow(d, (double)20.0);
+ d = (double)cp->ping / (double)cp->recvd / d;
+ if (d > 10000.0)
+ d = 10000.0;
+ aconf->pref = (int) (d * 100.0);
+
+ return 0;
+}
+
+/*
+ * max # of pings set to 15/sec.
+ */
+static void polludp()
+{
+ static time_t last = 0;
+ static int cnt = 0, mlen = 0, lasterr = 0;
+ Reg char *s;
+ struct SOCKADDR_IN from;
+ Ping pi;
+ int n;
+ SOCK_LEN_TYPE fromlen = sizeof(from);
+
+ /*
+ * find max length of data area of packet.
+ */
+ if (!mlen)
+ {
+ mlen = sizeof(readbuf) - strlen(ME) - strlen(version);
+ mlen -= 6;
+ if (mlen < 0)
+ mlen = 0;
+ }
+ Debug((DEBUG_DEBUG,"udp poll"));
+
+ n = recvfrom(udpfd, readbuf, mlen, 0, (SAP)&from, &fromlen);
+ if (n == -1)
+ {
+ ircstp->is_udperr++;
+ if ((errno == EWOULDBLOCK) || (errno == EAGAIN))
+ return;
+ else
+ {
+ report_error("udp port recvfrom (%s): %s", &me);
+ return;
+ }
+ }
+
+ if (timeofday == last)
+ {
+ if (++cnt > 14)
+ {
+ if (timeofday > lasterr + 30)
+ {
+ sendto_flag(SCH_NOTICE,
+ "udp packet dropped: %d bytes from %s.%d",
+#ifdef INET6
+ n, inetntop(AF_INET6,
+ (char *)&from.sin6_addr, mydummy,
+ MYDUMMY_SIZE),
+#else
+ n,inetntoa((char *)&from.sin_addr),
+#endif
+ ntohs(from.SIN_PORT));
+ lasterr = timeofday;
+ }
+ ircstp->is_udpdrop++;
+ return;
+ }
+ }
+ else
+ cnt = 0, last = timeofday;
+
+#ifdef INET6
+ Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
+ inet_ntop(AF_INET6, (char *)&from.sin6_addr, mydummy,
+ MYDUMMY_SIZE),
+ ntohs(from.SIN_PORT)));
+#else
+ Debug((DEBUG_NOTICE, "udp (%d) %d bytes from %s,%d", cnt, n,
+ inetntoa((char *)&from.sin_addr),
+ ntohs(from.SIN_PORT)));
+#endif
+
+ readbuf[n] = '\0';
+ ircstp->is_udpok++;
+ if (n < 8)
+ return;
+
+ bcopy(s = readbuf, (char *)&pi, MIN(n, sizeof(pi)));
+ pi.pi_id = ntohl(pi.pi_id);
+ Debug((DEBUG_INFO, "\tpi_id %#x pi_seq %d pi_cp %#x",
+ pi.pi_id, pi.pi_seq, pi.pi_cp));
+
+ if ((pi.pi_id == (PING_CPING|PING_REPLY) ||
+ pi.pi_id == (PING_CPING|(PING_REPLY << 24))) && n >= sizeof(pi))
+ {
+ check_ping(s, n);
+ return;
+ }
+ else if (pi.pi_id & PING_REPLY)
+ return;
+ /*
+ * attach my name and version for the reply
+ */
+ pi.pi_id |= PING_REPLY;
+ pi.pi_id = htonl(pi.pi_id);
+ bcopy((char *)&pi, s, MIN(n, sizeof(pi)));
+ s += n;
+ (void)strcpy(s, ME);
+ s += strlen(s)+1;
+ (void)strcpy(s, version);
+ s += strlen(s);
+ (void)sendto(udpfd, readbuf, s-readbuf, 0, (SAP)&from ,sizeof(from));
+ return;
+}
+
+/*
+ * do_dns_async
+ *
+ * Called when the fd returned from init_resolver() has been selected for
+ * reading.
+ */
+static void do_dns_async()
+{
+ static Link ln;
+ aClient *cptr;
+ aConfItem *aconf;
+ struct hostent *hp;
+ int bytes, pkts;
+
+ pkts = 0;
+
+ do {
+ ln.flags = -1;
+ hp = get_res((char *)&ln);
+
+ Debug((DEBUG_DNS,"%#x = get_res(%d,%#x)", hp, ln.flags,
+ ln.value.cptr));
+
+ switch (ln.flags)
+ {
+ case ASYNC_NONE :
+ /*
+ * no reply was processed that was outstanding or
+ * had a client still waiting.
+ */
+ break;
+ case ASYNC_CLIENT :
+ if ((cptr = ln.value.cptr))
+ {
+ del_queries((char *)cptr);
+ ClearDNS(cptr);
+ cptr->hostp = hp;
+#if defined(USE_IAUTH)
+ if (hp)
+ {
+ int i = 0;
+
+ while (hp->h_aliases[i])
+ sendto_iauth("%d A %s",
+ cptr->fd,
+ hp->h_aliases[i++]);
+ if (hp->h_name)
+ sendto_iauth("%d N %s",
+ cptr->fd, hp->h_name);
+ else if (hp->h_aliases[0])
+ sendto_iauth("%d n", cptr->fd);
+ }
+ else
+ sendto_iauth("%d d", cptr->fd);
+ if (iauth_options & XOPT_EXTWAIT)
+ cptr->lasttime = timeofday;
+#endif
+ }
+ break;
+ case ASYNC_CONNECT :
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ {
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ (void)connect_server(aconf, NULL, hp);
+ }
+ else
+ sendto_flag(SCH_ERROR,
+ "Connect to %s failed: host lookup",
+ (aconf) ? aconf->host : "unknown");
+ break;
+ case ASYNC_CONF :
+ aconf = ln.value.aconf;
+ if (hp && aconf)
+ bcopy(hp->h_addr, (char *)&aconf->ipnum,
+ sizeof(struct IN_ADDR));
+ break;
+ default :
+ break;
+ }
+ pkts++;
+ if (ioctl(resfd, FIONREAD, &bytes) == -1)
+ bytes = 0;
+ } while ((bytes > 0) && (pkts < 10));
+}