src/sockets.c
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following functions.
- nonblock
- block
- server_up
- server_down
- scfg_connect
- detachServer
- attachServer
- Sread
- Stest
- Sfree
- Smore
- Salloc
- Cfget
- getArt
- getHostAddr
- logFromClient
- logToClient
- logFromServer
- logToServer
- writeClient
- fwriteClient
- emit
- emitrn
- emitf
- Cemitf
- Cfemitf
- flush
- Cfflush
- Cflush
- Cfemit
- Cfemitrn
- Cemit
- Cemitrn
- Cget
- Get
/* $Id: sockets.c,v 1.12 2002/04/04 11:14:54 proff Exp $
* $Copyright$
*/
#include "nglobal.h"
#include "network.h"
#include "group.h"
#include "sockets.h"
EXPORT FILE *clientin;
EXPORT FILE *clientout;
#if defined(F_GETFL) && defined(O_NONBLOCK)
EXPORT int nonblock (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
int flags = O_NONBLOCK | fcntl (fd, F_GETFL);
return fcntl (fd, F_SETFL, flags);
}
EXPORT int block (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
int flags = (~O_NONBLOCK) & fcntl (fd, F_GETFL);
return fcntl (fd, F_SETFL, flags);
}
#else
# error gee. get a real system. this one doesnt have O_NONBLOCK
#endif
static void server_up (struct server_cfg *scfg)
/* [<][>][^][v][top][bottom][index][help] */
{
if (scfg->share->server_up <= scfg->share->server_down)
{
time_t now = time(NULL);
if (scfg->share->server_down != 0)
scfg->share->server_down_time += now - scfg->share->server_down;
scfg->share->server_up = now;
}
}
static void server_down (struct server_cfg *scfg)
/* [<][>][^][v][top][bottom][index][help] */
{
if (scfg->share->server_down <= scfg->share->server_up)
{
time_t now = time(NULL);
if (scfg->share->server_up != 0)
scfg->share->server_up_time += now - scfg->share->server_up;
scfg->share->server_down = now;
}
}
static int scfg_connect (struct server_cfg *scfg)
/* [<][>][^][v][top][bottom][index][help] */
{
struct sockaddr_in *in;
struct sockaddr_in *in_us;
int sock;
int yes = 1;
sock = socket (AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
loge (("socket() failed"));
return -1;
}
if (!(in_us = getHostAddr (scfg->us)))
{
loge (("could not convert '%s' to an address", scfg->us));
return -1;
}
if (bind (sock, (struct sockaddr *) in_us, sizeof *in_us) == -1)
loge (("couldn't bind to %s", scfg->us));
if (!(in = getHostAddr (scfg->host)))
{
loge (("could not convert '%s' to an address", scfg->host));
return -1;
}
if (in->sin_port == 0)
in->sin_port = htons(119);
if (connect (sock, (struct sockaddr *) in, sizeof *in) == -1)
{
logw (("could not connect to %s as %s", scfg->host, scfg->us));
close (sock);
return -1;
}
#ifdef SO_KEEPALIVE
if (setsockopt (sock, SOL_SOCKET, SO_KEEPALIVE, (char*) &yes, sizeof yes))
logd(("keepalive setsockopt failed on %s", scfg->host));
#endif
return sock;
}
EXPORT void detachServer (struct server_cfg *scfg)
/* [<][>][^][v][top][bottom][index][help] */
{
if (scfg->fd >= 0)
{
fclose(scfg->fh);
scfg->fd = -1;
if (scfg->group_actual)
{
free(scfg->group_actual);
scfg->group_actual = NULL;
}
if (scfg->group)
{
free(scfg->group);
scfg->group= NULL;
}
}
}
EXPORT struct server_cfg *attachServer (struct server_cfg *scfg)
/* [<][>][^][v][top][bottom][index][help] */
{
int res;
char buf[MAX_LINE];
time_t now;
assert(scfg);
/*
* we do not use Cfemitf as it may call this routine!
*/
/*
* see if we are already attached
*/
if (scfg->fd >= 0)
{
struct sockaddr_in sa;
int slen = sizeof sa;
fd_set rdfs;
struct timeval tv;
FD_ZERO (&rdfs);
FD_SET (scfg->fd, &rdfs);
tv.tv_sec = 0;
tv.tv_usec = 0;
if ((select (scfg->fd + 1, &rdfs, NULL, NULL, &tv) == 0) &&
(getpeername (scfg->fd, (struct sockaddr *) (struct sockaddr *)&sa, &slen) == 0))
{
/* we have delayed group binding. make sure group is bound, but only
* if this server is the current server. non-current servers do not
* need group binding, however one day they may, and this routine will
* need to be de-optimised accordingly
*/
if (CurrentGroupScfg == scfg && (scfg->group && (!scfg->group_actual || !strEq(scfg->group, scfg->group_actual))))
goto dogroup;
return scfg;
}
}
if (scfg->fh)
fclose (scfg->fh);
scfg->fd = -1;
now = time (NULL);
if (now - scfg->share->server_down < con->serverDownRecheck)
{
logd (("server attach bypassed (server tagged down) %d seconds remaining before retry",
(int)(con->serverDownRecheck - (now - scfg->share->server_down))));
return NULL;
}
scfg->share->server_check = now;
if ((scfg->fd = scfg_connect (scfg)) < 0)
{
logw (("couldn't connect to %s as %s", scfg->host, scfg->us));
bad:
scfg->fd = -1;
server_down(scfg);
scfg->share->connect_fail++;
if (con->statistics)
Stats->serverConnectsFailed++;
return NULL;
}
scfg->fh = fdopen (scfg->fd, "r+");
logd (("connected to NNTP server %s as %s", scfg->host, scfg->us));
if (!Cfget (scfg, buf, sizeof buf))
{
dropped:
logw (("server %s dropped during initial connect phase", scfg->host));
fclose (scfg->fh);
goto bad;
}
strStripEOL (buf);
res = strToi (buf);
if (res != NNTP_POSTOK_VAL &&
res != NNTP_NOPOSTOK_VAL)
{
logw (("<- %.128s", buf));
goto dropped;
}
scfg->post_ok = (res==NNTP_POSTOK_VAL)? TRUE: FALSE;
/* use "authinfo" login here -bradf */
if (scfg->user && scfg-> pass)
{
fprintf (scfg->fh, "authinfo user %s\r\n", scfg->user);
fflush (scfg->fh);
if (!Cfget(scfg, buf, sizeof buf))
goto dropped;
fprintf (scfg->fh, "authinfo pass %s\r\n", scfg->pass);
fflush (scfg->fh);
if (!Cfget(scfg, buf, sizeof buf))
goto dropped;
}
if (ModeReader)
{
fprintf (scfg->fh, "mode reader\r\n");
fflush (scfg->fh);
if (!Cfget (scfg, buf, sizeof buf))
goto dropped;
}
if (scfg->group_actual)
{
free(scfg->group_actual);
scfg->group_actual = NULL;
}
if (scfg->group)
{
dogroup:
if (!attachGroupTalk (scfg->group, scfg, FALSE))
goto dropped;
/* set pointer back to article we were looking at -an */
if (strEq(scfg->group, CurrentGroup) && CurrentGroupArtNum != scfg->artno) {
fprintf (scfg->fh, "stat %d\r\n", CurrentGroupArtNum);
if (fflush(scfg->fh) || !Cfget(scfg, buf, sizeof buf))
goto dropped;
if (strToi (buf) == NNTP_NOTHING_FOLLOWS_VAL) {
scfg->artno = CurrentGroupArtNum;
}
}
}
server_up(scfg);
scfg->share->connect_good++;
if (con->statistics)
Stats->serverConnects++;
return scfg;
}
EXPORT int Sread (struct server_cfg *scfg, char *buf, int len)
/* [<][>][^][v][top][bottom][index][help] */
{
fd_set rdfs;
struct timeval tv;
int cc;
int fd = scfg->fd;
FD_ZERO (&rdfs);
FD_SET (fd, &rdfs);
tv.tv_sec = con->networkTimeout;
tv.tv_usec = 0;
if (select (fd + 1, &rdfs, NULL, NULL, &tv) == 0)
{
logw (("news server read timed out"));
return 0;
}
redo:
errno = 0;
if ((cc = read (fd, buf, len - 1)) < 1)
{
if (cc<0 && errno == EINTR)
goto redo;
logw (("error reading from news server"));
return 0;
}
buf[cc] = '\0';
server_up (scfg);
scfg->share->bytes_from += cc;
if (con->statistics)
{
Stats->serverFromBytes += cc;
CS->serverFromBytes+=len;
}
return cc;
}
/*
* consider turning this into a ll. I vote against for performance reasons at the moment.
*/
static int Spos[FD_HIGH];
static char *Sbuf[FD_HIGH];
static bool Sinitalised = FALSE;
inline static int Stest (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
assert (fd<FD_HIGH);
if (fd<0)
{
logw (("Stest(%d) -- server down?", fd));
return FALSE;
}
if (!Sinitalised) /* apparently some sun4.1.3 GCC -O system breaks static arrays */
{
memset (Spos, 0, FD_HIGH * (sizeof *Spos));
memset (Sbuf, 0, FD_HIGH * (sizeof *Sbuf));
}
Sinitalised = TRUE;
return TRUE;
}
EXPORT bool Sfree (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
if (!Stest (fd))
return FALSE;
if (Sbuf[fd])
free (Sbuf[fd]);
Sbuf[fd] = NULL;
Spos[fd] = 0;
return TRUE;
}
EXPORT bool Smore (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
if (fd<0) /* not even connected yet */
return FALSE;
if (!Stest (fd))
return FALSE;
if (Spos[fd])
return TRUE;
{
fd_set fs;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&fs);
FD_SET (fd, &fs);
if (select (fd + 1, &fs, NULL, NULL, &tv) == 1 &&
FD_ISSET (fd, &fs))
return TRUE;
else
return FALSE;
}
}
inline static char *Salloc (int fd)
/* [<][>][^][v][top][bottom][index][help] */
{
Sbuf[fd] = Smalloc (MAX_BFR);
Spos[fd] = 0;
return Sbuf[fd];
}
EXPORT int Cfget (struct server_cfg *scfg, char *line, int len)
/* [<][>][^][v][top][bottom][index][help] */
{
char *buf;
int pos;
int got_r = 0;
int n;
assert (scfg);
if (!Stest (scfg->fd))
return 0;
if (!(buf = Sbuf[scfg->fd]))
buf = Salloc (scfg->fd);
pos = Spos[scfg->fd];
for (n = 0;;)
{
char c;
if (!pos && Sread (scfg, buf + pos, MAX_BFR - pos) < 1)
return 0;
for (; n < len - 1; line[n++] = c, pos++)
{
c = buf[pos];
if (c == '\n')
{
#if 0 /* lots of lame servers do this */
if (!got_r)
logw (("saw '\\n' before '\\r'"));
#endif
line[n++] = c;
if (!buf[++pos])
Spos[scfg->fd] = 0;
else
Spos[scfg->fd] = pos;
goto ret;
}
if (c == '\r')
{
#if 0 /* lots of lame servers do this */
if (got_r++)
logw (("saw more than one '\\r'"));
#endif
continue;
}
if (c == '\0')
{
pos = Spos[scfg->fd] = 0;
break;
}
}
if (n >= len - 1)
goto ret;
}
ret:
line[n] = '\0';
if (con->logFromServer && *line)
logFromServer (scfg->host, line);
return n;
}
EXPORT bool getArt (struct server_cfg *srvr, FILE *fout)
/* [<][>][^][v][top][bottom][index][help] */
{
char line[MAX_LINE]="";
while (Cfget (srvr, line, sizeof line) && !EL (line))
fputs (line, fout);
fputs (".\r\n", fout);
return EL(line);
}
EXPORT struct sockaddr_in *getHostAddr (char *hostname)
/* [<][>][^][v][top][bottom][index][help] */
{
struct hostent *host;
static struct sockaddr_in in;
char *p=strchr(hostname, ':');
bzero(&in, sizeof in);
in.sin_family = AF_INET;
if (p)
{
*p='\0';
in.sin_port = htons(strToi(p+1));
} else
in.sin_port = 0;
if (strEq(hostname, "DEFAULT"))
{
in.sin_addr.s_addr=0;
} else
{
if ((in.sin_addr.s_addr = inet_addr (hostname)) == (unsigned int)-1)
{
if (!(host = gethostbyname (hostname)))
{
if (p)
*p=':';
return NULL;
}
memcpy (&in.sin_addr, host->h_addr, host->h_length);
}
}
if (p)
*p=':';
return ∈
}
EXPORT void logFromClient (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf[MAX_SYSLOG]="";
if (!con->logFromClient)
return;
strncpy (buf, s, MAX_SYSLOG-1);
buf[MAX_SYSLOG-1]='\0';
strStripEOL(buf);
if (*buf)
logt (("<- %s", buf));
}
EXPORT void logToClient (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf[MAX_SYSLOG]="";
if (!con->logToClient)
return;
strncpy (buf, s, MAX_SYSLOG-1);
buf[MAX_SYSLOG-1]='\0';
strStripEOL(buf);
if (*buf)
logt (("-> %s", buf));
}
EXPORT void logFromServer (char *host, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf[MAX_SYSLOG]="";
if (!con->logFromServer)
return;
strncpy (buf, s, MAX_SYSLOG-1);
buf[MAX_SYSLOG-1]='\0';
strStripEOL(buf);
if (*buf)
logt (("<= [%s] %s", host, buf));
}
EXPORT void logToServer (char *host, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
char buf[MAX_SYSLOG]="";
if (!con->logToServer)
return;
strncpy (buf, s, MAX_SYSLOG-1);
buf[MAX_SYSLOG-1]='\0';
strStripEOL(buf);
if (*buf)
logt (("=> [%s] %s", host, buf));
}
EXPORT int writeClient (char *s, int len)
/* [<][>][^][v][top][bottom][index][help] */
{
if (con->logToClient)
{
char *p;
for (p = s; p - s < len; )
{
char *p2;
for (p2 = p; *p2 != '\n' && *p2 && p2-s < len; p2++) {}
logt (("-> %.*s", (int)MIN(p2-p, MAX_SYSLOG-1), p));
p = p2+1;
}
}
if (slaveClient)
{
strnStackAdd(slaveClient, s, len);
return len;
}
if (con->statistics)
{
Stats->clientToBytes +=len;
CS->clientToBytes+=len;
ClientBytes += len;
}
return write (fileno(clientout), s, len);
}
EXPORT int fwriteClient (char *s, int len)
/* [<][>][^][v][top][bottom][index][help] */
{
if (con->logToClient)
{
char *p;
for (p = s; p - s < len; )
{
char *p2;
for (p2 = p; *p2 != '\n' && *p2 && p2-s < len; p2++) {}
logt (("-> %.*s", (int)MIN(p2-p, MAX_SYSLOG-1), p));
p = p2+1;
}
}
if (slaveClient)
{
strnStackAdd(slaveClient, s, len);
return len;
}
if (con->statistics)
{
Stats->clientToBytes +=len;
CS->clientToBytes+=len;
ClientBytes += len;
}
return fwrite (s, len, 1, clientout);
}
EXPORT bool emit (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
int i = 0;
logToClient (s);
if (con->statistics || slaveClient)
i = strlen(s);
if (con->statistics)
{
Stats->clientToBytes += i;
CS->clientToBytes+=i;
ClientBytes += i;
}
if (slaveClient)
{
strnStackAdd(slaveClient, s, i);
return TRUE;
}
return fputs (s, clientout)!=EOF;
}
EXPORT int emitrn (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
logToClient (s);
if (con->statistics || slaveClient)
{
int i = strlen(s);
if (slaveClient)
{
strnStackAdd(slaveClient, s, i);
strnStackAdd(slaveClient, "\r\n", 2);
return TRUE;
}
fputs (s, clientout);
Stats->clientToBytes += i + 2;
CS->clientToBytes += i + 2;
ClientBytes += i + 2;
}
else
fputs (s, clientout);
return fputs ("\r\n", clientout)!=EOF;
}
EXPORT int emitf (char *fmt, ...) EXP_(GNUC_EXT(__attribute__ ((format (printf, 1, 2)))))
/* [<][>][^][v][top][bottom][index][help] */
{
int i;
va_list ap;
va_start(ap, fmt);
if (slaveClient)
{
char buf[MAX_BFR];
i = vsnprintf(buf, sizeof buf, fmt, ap);
strnStackAdd(slaveClient, buf, i);
}
else
{
if (con->logToClient)
{
char buf[MAX_SYSLOG]; /* should be more than enough */
vsnprintf(buf, MAX_SYSLOG-1, fmt, ap);
logToClient (buf);
}
i=vfprintf(clientout, fmt, ap);
}
va_end(ap);
if (con->statistics)
{
Stats->clientToBytes += i;
CS->clientToBytes+=i;
ClientBytes += i;
}
return i;
}
EXPORT int Cemitf (char *fmt, ...) EXP_(GNUC_EXT(__attribute__ ((format (printf, 1, 2)))))
/* [<][>][^][v][top][bottom][index][help] */
{
int i;
struct server_cfg *scfg;
va_list ap;
va_start(ap, fmt);
scfg=attachServer(CurrentScfg);
if (!scfg)
return 0;
if (con->logToServer)
{
char buf[MAX_SYSLOG];
vsnprintf(buf, MAX_SYSLOG-1, fmt, ap);
logToServer (scfg->host, buf);
}
i=vfprintf(scfg->fh, fmt, ap);
va_end(ap);
scfg->share->bytes_to+=i;
if (con->statistics)
{
CS->serverToBytes+=i;
Stats->serverToBytes += i;
}
return i;
}
EXPORT int Cfemitf (struct server_cfg *scfg, char *fmt, ...) EXP_(GNUC_EXT(__attribute__ ((format (printf, 2, 3)))))
/* [<][>][^][v][top][bottom][index][help] */
{
int i;
va_list ap;
va_start(ap, fmt);
scfg=attachServer(scfg);
if (!scfg)
return 0;
if (con->logToServer)
{
char buf[MAX_SYSLOG];
vsnprintf(buf, MAX_SYSLOG-1, fmt, ap);
logToServer (scfg->host, buf);
}
i=vfprintf(scfg->fh, fmt, ap);
va_end(ap);
scfg->share->bytes_to+=i;
if (con->statistics)
{
Stats->serverToBytes += i;
CS->serverToBytes+=i;
}
return i;
}
EXPORT int flush ()
/* [<][>][^][v][top][bottom][index][help] */
{
return fflush (clientout);
}
EXPORT int Cfflush (struct server_cfg *cf)
/* [<][>][^][v][top][bottom][index][help] */
{
return fflush(cf->fh);
}
EXPORT int Cflush ()
/* [<][>][^][v][top][bottom][index][help] */
{
return Cfflush(CurrentScfg);
}
EXPORT int Cfemit (struct server_cfg *cf, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
int i;
cf=attachServer(cf);
if (!cf)
return 0;
logToServer (cf->host, s);
i = strlen(s);
cf->share->bytes_to += i;
if (con->statistics)
{
Stats->serverToBytes += i;
CS->serverToBytes+=i;
}
return fwrite(s, 1, i, cf->fh);
}
EXPORT int Cfemitrn (struct server_cfg *cf, char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
return Cfemitf(cf, "%s\r\n", s);
}
EXPORT int Cemit (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
return Cfemit (CurrentScfg, s);
}
EXPORT int Cemitrn (char *s)
/* [<][>][^][v][top][bottom][index][help] */
{
return Cfemitrn (CurrentScfg, s);
}
EXPORT int Cget (char *s, int n)
/* [<][>][^][v][top][bottom][index][help] */
{
return Cfget (CurrentScfg, s, n);
}
EXPORT int Get (char *s, int n)
/* [<][>][^][v][top][bottom][index][help] */
{
char *p=fgets (s, n, clientin);
int len;
if (!p)
return 0;
logFromClient (s);
if (con->statistics)
{
len = strlen(p);
Stats->clientFromBytes += len;
CS->clientFromBytes +=len;
}
else
len = 1;
return len;
}