/* * 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 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 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }