/*
* Copyright (c) 1998,1999,2000 Kazushi (Jam) Marukawa
* All rights of my changes are reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice in the documentation and/or other materials provided with
* the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* $Orig-Id: util.c,v 1.22 1997/07/23 18:35:18 agulbra Exp $ */
/*
Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
22646949.
Use, modification and distribution is allowed without limitation,
warranty, or liability of any kind. */
/*
This code is derived from only leafnode+ by using same structure
of Cornelius's leafnode to prepare for merging with Cornelius's code.
*/
#ifdef SOCKS
#include <socks.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <dirent.h>
#include "leafnode.h"
FILE *nntpin = NULL;
FILE *nntpout = NULL;
static jmp_buf timeout;
char last_command[1025];
char lineout[1025];
int authenticated;
static int authenticate(void);
static void timer(int sig)
{
longjmp(timeout, 1);
exit(sig);
}
static void sigint(int sig)
{
fprintf(stderr, "I got SIGINT. I'm closing nntp connection.\n");
nntpclose();
}
static void sigpipe(int sig)
{
fprintf(stderr, "I got SIGPIPE. I'm closing nntp connection.\n");
nntpclose();
}
/*
05/26/97 - T. Sweeney - Send a string out, keeping a copy in reserve.
*/
void putaline(void)
{
strcpy(last_command, lineout);
fprintf(nntpout, "%s", lineout);
fflush(nntpout);
if (verbose > 5) {
printf("%s", lineout);
}
}
/* originally from Tim Sweeney
*
* Returns TRUE if authentication succeeds, FALSE if it does not.
*
* Precondition: servers->username != 0.
*/
static int authenticate(void)
{
int reply;
fprintf(nntpout, "authinfo user %s\r\n", servers->username);
fflush(nntpout);
reply = nntpreply();
if (reply == 281) {
return TRUE;
} else if (reply != 381) {
mysyslog(LOG_ERR, "Username rejected: %03d", reply);
return FALSE;
}
if (servers->password == NULL) {
mysyslog(LOG_ERR, "Password needed for authentication");
return FALSE;
}
fprintf(nntpout, "authinfo pass %s\r\n", servers->password);
fflush(nntpout);
reply = nntpreply();
if ( reply != 281) {
mysyslog(LOG_ERR, "Password failed: %03d", reply);
return FALSE;
}
return TRUE;
}
/*
* decode an NNTP reply number
* reads a line from the server and returns an integer
*
* 498 is used to mean "protocol error", like smail
*
* the text returned is discarded
*
* from Tim Sweeney: retry in case of authinfo failure.
*/
int nntpreply(void)
{
char* response;
int r = 0;
int c = 1;
while (c) {
response=getaline(nntpin);
if (!response) {
mysyslog(LOG_ERR, "NNTP server went away");
return 498;
}
if (strlen(response) > 2 &&
isdigit(response[0]) && isdigit(response[1]) &&
isdigit(response[2]) &&
(response[3] == ' ' || response[3] == '\0' ||
response[3] == '-')) {
int rl;
rl = atoi(response);
if (r > 0 && r != rl)
r = 498; /* protocol error */
else
r = rl;
c = (response[3] == '-');
} else {
c = 0;
r = 498; /* protocol error */
}
}
return r;
}
extern int h_errno;
extern struct state _res;
#ifndef INET6
#define incopy(a) *((struct in_addr*)a)
#endif
/*
* Connect to a server.
* Return TRUE if succeeds. Otherwise, return FALSE.
*/
int nntpconnect(void)
{
#ifdef INET6
struct addrinfo hints, *res0, *res;
char serv[NI_MAXSERV];
int gai;
#else
struct hostent* hp;
struct servent* sp;
struct servent sp_def;
struct sockaddr_in s_in;
#endif
int sock;
int i;
#ifdef INET6
if (servers->port != 0)
snprintf(serv, sizeof(serv), "%d", servers->port);
else
strcpy(serv, "nntp");
#else
memset((void*)&s_in, 0, sizeof(s_in));
if (servers->port == 0) {
sp = getservbyname("nntp", "tcp");
if (sp == NULL) {
mysyslog(LOG_ERR, "Unable to find service NNTP");
return FALSE;
}
} else {
sp = &sp_def;
sp->s_port = htons(servers->port);
}
#endif
/* Fetch the ip addresses of the given host. */
#ifdef INET6
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
res = res0 = NULL;
gai = getaddrinfo(servers->viahost ? servers->viahost : servers->servername,
serv, &hints, &res0);
if (gai || !res0){
mysyslog(LOG_ERR, "getaddrinfo() failed: %s", gai_strerror(gai));
return FALSE;
}
#else
if (servers->viahost)
hp = gethostbyname(servers->viahost);
else
hp = gethostbyname(servers->servername);
#endif
#ifdef INET6
if (res0) {
#else
if (hp) {
#endif
/* Execute preconnect */
if (servers->preconnect) {
uid_t euid = geteuid();
gid_t egid = getegid();
int ret;
if (setreuid(-1, 0) != 0)
perror("setreuid");
if (setregid(-1, 0) != 0)
perror("setregid");
ret = system(servers->preconnect);
if (setregid(-1, egid) != 0)
perror("setregid");
if (setreuid(-1, euid) != 0)
perror("setreuid");
if (ret != 0) {
mysyslog(LOG_ERR, "System(%s) failed: %s",
servers->preconnect, strerror(errno));
return FALSE;
}
}
/* Try to make connection to each of the addresses in turn. */
#ifdef INET6
for (res=res0; res; res=res->ai_next) {
#else
for (i = 0; (int*)(hp->h_addr_list)[i]; i++) {
#endif
#ifdef INET6
sock = socket(res->ai_family, res->ai_socktype, 0);
#else
s_in.sin_family = hp->h_addrtype;
s_in.sin_port = sp->s_port;
s_in.sin_addr = incopy(hp->h_addr_list[i]);
sock = socket(AF_INET, SOCK_STREAM, 0);
#endif
if (sock < 0)
break;
if (setjmp(timeout) != 0) {
(void)close(sock);
continue;
}
(void)signal(SIGINT, sigint);
(void)signal(SIGPIPE, sigpipe);
(void)signal(SIGALRM, timer);
(void)alarm(servers ? servers->timeout_open : TIMEOUT_OPEN);
#ifdef INET6
if (connect(sock, res->ai_addr, res->ai_addrlen) < 0)
#else
if (connect(sock, (struct sockaddr*)&s_in, sizeof(s_in)) < 0)
#endif
break;
(void)alarm(0U);
nntpout = fdopen(sock, "w");
if (nntpout == NULL)
break;
nntpin = fdopen(dup(sock), "r");
if (nntpin == NULL)
break;
switch(nntpreply()) {
case 200:
case 201:
return TRUE;
}
shutdown(fileno(nntpout), 0);
}/* end of IP-addresses for loop */
#ifdef INET6
if (res0)
freeaddrinfo(res0);
#endif
}
return FALSE;
}/* end of connect function */
/*
* connect to a server with authentification if it is needed.
* login server with username and password if those are specified.
*/
int nntpconnectauth(void)
{
if (nntpconnect()) {
if (servers->username && !authenticated) { /* need to authenticate */
authenticated = TRUE;
if (authenticate()) {
return TRUE;
} else {
nntpclose();
}
} else {
return TRUE;
}
}
return FALSE;
}
/*
* Close nntp connection.
* - close nntpin and set NULL nntpin because it is accessed by only sfgets()
* and guarded by sfgets() also.
* - don't close nntpout because it may be accessed by fprintf().
*/
void nntpclose(void)
{
int fd = -1;
if (nntpout) {
fd = fileno(nntpout);
}
if (nntpin) {
fclose(nntpin);
}
if (fd >= 0)
shutdown(fd, 0);
nntpin = NULL;
authenticated = FALSE;
nntpout = fopen("/dev/null", "w");
nntpin = fopen("/dev/null", "r");
}
int nntpreconnect(void)
{
int fd = -1;
if (nntpout) {
fd = fileno(nntpout);
fclose(nntpout);
}
if (nntpin) {
fclose(nntpin);
}
if (fd >= 0)
shutdown(fd, 0);
nntpout = nntpin = NULL;
authenticated = FALSE;
return nntpconnectauth();
}
/*
* read size bytes into buf from file fp,
* with four hours timeout
* return a pointer to buf, or NULL in case of error
*/
static char* sfgets(char* buf, size_t size, FILE* fp)
{
char* p;
if (fp == NULL)
return NULL;
if (setjmp(timeout)) {
nntpclose();
return(NULL);
}
(void) signal(SIGALRM, timer);
(void) alarm(servers ? servers->timeout_read : TIMEOUT_READ);
p = fgets(buf, size, fp);
if (errno == EINTR)
errno = ETIMEDOUT;
(void) alarm(0U);
return p;
}
/*
* Lars Wirzenius: read a line into memory, with no max length
* return a pointer to the line, or NULL in case of error
*
* strip \r at EOL
*/
char* getaline(FILE* f)
{
static char* buf; /* buffer for line */
static size_t size; /* size of buffer */
size_t len; /* # of chars stored into buf before '\0' */
char* p;
len = 0;
if (!size)
size = 256;
if (!buf)
buf = critmalloc(size, "reading line");
while ((p = sfgets(buf + len, size - len, f)) != NULL) {
len += strlen(buf + len);
if (len > 0 && buf[len-1] == '\n')
break; /* the whole line has been read */
if (len + 5 > size) {
size += size + 100;
buf = critrealloc(buf, size, "reading line");
}
}
if (len == 0)
return NULL;
if (len && (buf[len - 1] == '\n')) { /* go back on top of the newline */
--len;
if (len && (buf[len - 1] == '\r')) /* also delete CR */
--len;
}
buf[len] = '\0'; /* unconditionally terminate string,
possibly overwriting newline */
if (verbose > 5) {
printf("%s\n", buf);
}
return buf;
}
syntax highlighted by Code2HTML, v. 0.9.1