/************************************************************************
* 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.2.3 2001/05/16 10:42:51 chopin 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);
if (cl >= MAXCONNECTIONS)
{
sendto_log(ALOG_IRCD, LOG_CRIT,
"Recompile iauth, (fatal %d>=%d)", cl, MAXCONNECTIONS);
exit(1);
}
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 0
/* stupid code that tries to find a bug, but does nothing --Q */
#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)
{
/* Q got core here, he had i=-1 */
if (cldata[i].rfd > 0 && TST_READ_EVENT(cldata[i].rfd))
{
/* this should not happen! */
/* hmmpf */
}
i++;
}
# endif
#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(!inetpton(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(!inetpton(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;
}
syntax highlighted by Code2HTML, v. 0.9.1