"Fossies" - the Fresh Open Source Software Archive

Member "leafnode-1.12.0/nntputil.c" (28 Dec 2021, 15513 Bytes) of package /linux/misc/leafnode-1.12.0.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "nntputil.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.11.12_vs_1.12.0.

    1 /*
    2 nntputil.c -- misc nntp-related stuff
    3 
    4 Written by Arnt Gulbrandsen <agulbra@troll.no> and copyright 1995
    5 Troll Tech AS, Postboks 6133 Etterstad, 0602 Oslo, Norway, fax +47
    6 22646949.
    7 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>
    8 and Randolf Skerka <Randolf.Skerka@gmx.de>.
    9 Copyright of the modifications 1997.
   10 Modified by Kent Robotti <robotti@erols.com>. Copyright of the
   11 modifications 1998.
   12 Modified by Markus Enzenberger <enz@cip.physik.uni-muenchen.de>.
   13 Copyright of the modifications 1998.
   14 Modified by Cornelius Krasel <krasel@wpxx02.toxi.uni-wuerzburg.de>.
   15 Copyright of the modifications 1998, 1999.
   16 Modified by Ralf Wildenhues <ralf.wildenhues@gmx.de>.
   17 Copyright of the modifications 2002.
   18 Modified by Matthias Andree <matthias.andree@gmx.de>.
   19 Copyright of the modifications 2000 - 2013.
   20 
   21 See file COPYING for restrictions on the use of this software.
   22 */
   23 
   24 #include "leafnode.h"
   25 #include "system.h"
   26 #include "mysigact.h"
   27 
   28 #include <fcntl.h>
   29 #include <sys/uio.h>
   30 #include <sys/param.h>
   31 #include <sys/socket.h>
   32 #include <netinet/in.h>
   33 #ifndef __LCLINT__
   34 #include <arpa/inet.h>
   35 #endif              /* not __LCLINT__ */
   36 #include <ctype.h>
   37 #include <errno.h>
   38 #include <limits.h>
   39 #include <stdlib.h>
   40 #include <netdb.h>
   41 #include <setjmp.h>
   42 #include <signal.h>
   43 #include <stdio.h>
   44 #include <string.h>
   45 #include <syslog.h>
   46 #include <sys/stat.h>
   47 #include <sys/types.h>
   48 #include <unistd.h>
   49 #include <dirent.h>
   50 
   51 #include "ln_log.h"
   52 
   53 char last_command[SIZE_lineout + 1];
   54 char lineout[SIZE_lineout + 1];
   55 
   56 /*@relnull@*//*@dependent@*/ FILE *nntpin = NULL;
   57 /*@relnull@*//*@dependent@*/ FILE *nntpout = NULL;
   58 
   59 int stat_is_evil = 0;
   60 int date_is_evil = 0;
   61 
   62 static int xnntpreply(const struct server *, int);
   63 
   64 static void authsucc(const struct server *current_server) {
   65     if (verbose) {
   66     printf("%s: authenticated as %s\n", current_server->name, current_server->username);
   67     syslog(LOG_INFO, "%s: authenticated as %s", current_server->name, current_server->username);
   68     }
   69 }
   70 
   71 /*
   72 05/26/97 - T. Sweeney - Send a string out, keeping a copy in reserve.
   73 */
   74 void
   75 putaline(void)
   76 {
   77     if (debug >= 1) {
   78     char y, *x = lineout + strcspn(lineout, "\r\n");
   79 
   80     y = *x; *x = '\0';
   81     syslog(LOG_DEBUG, ">%s", lineout);
   82     *x = y;
   83     }
   84     strcpy(last_command, lineout);  /* RATS: ignore */
   85     fputs(lineout, nntpout);
   86     fflush(nntpout);
   87 }
   88 
   89 /*
   90  * Authenticate ourselves at a remote server.
   91  * Returns TRUE if authentication succeeds, FALSE if it does not.
   92  * Error will have been logged in case of a FALSE return,
   93  * no log output if TRUE returned.
   94  */
   95 int
   96 authenticate(const struct server *current_server)
   97 {
   98     int reply;
   99 
  100     if (!current_server) {
  101     ln_log(LNLOG_SERR, LNLOG_CTOP,
  102         "authenticate: internal error: current_server is NULL, aborting");
  103     abort();
  104     }
  105 
  106     if (!current_server->username) {
  107     ln_log(LNLOG_SERR, LNLOG_CTOP, "error: %s: username needed for authentication",
  108            current_server->name);
  109     return FALSE;
  110     }
  111 
  112     fprintf(nntpout, "AUTHINFO USER %s\r\n", current_server->username);
  113     fflush(nntpout);
  114     if (debugmode)
  115     syslog(LOG_DEBUG, ">AUTHINFO USER %s", current_server->username);
  116 
  117     reply = xnntpreply(current_server, 0);
  118     if (reply == 281) {
  119     authsucc(current_server);
  120     return TRUE;
  121     } else if (reply != 381) {
  122     ln_log(LNLOG_SERR, LNLOG_CSERVER, "error: %s: AUTHINFO USER rejected: %03d",
  123         current_server->name, reply);
  124     return FALSE;
  125     }
  126 
  127     if (!current_server->password) {
  128     ln_log(LNLOG_SERR, LNLOG_CTOP, "error: %s: password needed for authentication",
  129            current_server->name);
  130     return FALSE;
  131     }
  132     /* DO NOT LOG THIS: */
  133     fprintf(nntpout, "AUTHINFO PASS %s\r\n", current_server->password);
  134     fflush(nntpout);
  135     if (debugmode)
  136     syslog(LOG_DEBUG, ">AUTHINFO PASS <password not shown>");
  137 
  138     reply = xnntpreply(current_server, 0);
  139 
  140     if (reply != 281) {
  141     ln_log(LNLOG_SERR, LNLOG_CTOP, "error: AUTHINFO PASS failed: %03d", reply);
  142     return FALSE;
  143     }
  144     authsucc(current_server);
  145     return TRUE;
  146 }
  147 
  148 static size_t lllen = 0;
  149 static
  150                     /*@null@*/
  151  /*@owned@*/
  152 char *llstr = NULL;
  153 
  154 void freelastreply(void)
  155 {
  156     if (!llstr) return; 
  157     free(llstr);
  158     llstr = NULL;
  159 }
  160 
  161 /*@dependent@*//*@null@*/ char *
  162 lastreply(void)
  163 {
  164     return llstr;
  165 }
  166 
  167 /**
  168  * decode an NNTP reply number
  169  * reads a line from the server and returns an integer
  170  *
  171  * 498 is used to mean "protocol error", like smail,
  172  * and includes timeout and "server disconnect" (EOF)
  173  * conditions
  174  *
  175  * the text returned is stored in lllen/llstr
  176  * for later retrieval by lastreply()
  177  *
  178  * from Tim Sweeney: retry in case of AUTHINFO failure.
  179  */
  180 static int
  181 xnntpreply(const struct server *current_server,
  182     /** set this to true to enable authentication after 480 reply */
  183     int may_auth)
  184 {
  185     char *response;
  186     int r = 0;
  187     int c;
  188 
  189     do {
  190     response = mgetaline(nntpin);
  191     if (!response) {
  192         if (llstr)
  193         free(llstr);
  194         llstr = NULL;
  195         ln_log(LNLOG_SERR, LNLOG_CTOP, "error: NNTP server went away (server disconnect or timeout)");
  196         return 498;
  197     }
  198     if (debug == 1) syslog(LOG_DEBUG, "<%s", response);
  199 
  200     /* cache line */
  201     if (strlen(response) > lllen || !llstr) {
  202         if (llstr)
  203         free(llstr);
  204         lllen = strlen(response);
  205         llstr = critmalloc(lllen + 1, "nntpreply");
  206     }
  207     strcpy(llstr, response);    /* RATS: ignore */
  208     if (strlen(response) > 2
  209         && isdigit((unsigned char)response[0])
  210         && isdigit((unsigned char)response[1])
  211         && isdigit((unsigned char)response[2])
  212         && ((response[3] == ' ')
  213             || (response[3] == '\0')
  214             || (response[3] == '-'))) {
  215         int rl;
  216         rl = atoi(response);
  217         if (r > 0 && r != rl) {
  218         ln_log(LNLOG_SERR, LNLOG_CTOP, "error: multiline reply with variant error code (%d vs. %d), last line: %s", r, rl, response);
  219         r = 498;    /* protocol error */
  220         } else
  221         r = rl;
  222         c = (response[3] == '-');
  223     } else {
  224         ln_log(LNLOG_SERR, LNLOG_CTOP, "error: syntax error in reply \"%s\"", response);
  225         c = 0;
  226         r = 498;        /* protocol error */
  227     }
  228     } while(c);
  229 
  230     if (r == 480 && may_auth) { /* need to authenticate */
  231     char *x = critstrdup(last_command, "xnntpreply");
  232     x[strcspn(last_command, "\r\n")] = '\0';
  233     if (debugmode)
  234         syslog(LOG_DEBUG, "%s: requested authentication for command \"%s\"",
  235             current_server->name, x);
  236     if (verbose)
  237         printf("%s: requested authentication for command \"%s\"\n",
  238             current_server->name, x);
  239     free(x);
  240     if (authenticate(current_server)) {
  241         strcpy(lineout, last_command);
  242         putaline();
  243         r = xnntpreply(current_server, 0);
  244     }
  245     }
  246     return r;
  247 }
  248 
  249 int nntpreply(const struct server *s) {
  250     return xnntpreply(s, 1);
  251 }
  252 
  253 struct versions {
  254     const char *name;
  255     int is_evil;
  256 };
  257 
  258 /*
  259  * NewsCache 0.99.17 and previous versions always
  260  * reply with 223 0 <MID> when asked "STAT <MID>". This
  261  * is a violation of RFC 977 and breaks posting.
  262  */
  263 static struct versions stat_versions[] = {
  264     { "NewsCache 0.99.2 ", 1 },
  265     { "NewsCache 0.99.2", 0 },
  266     { "NewsCache 0.99.19", 0 },
  267     { "NewsCache 0.99.18", 0 },
  268     { "NewsCache 1.0.", 1 },
  269     { "NewsCache 1", 0 },
  270     { "NewsCache", 1 },
  271     /* reported to be necessary by 
  272      * Robert Marshall <robert@chezmarshall.freeserve.co.uk>:
  273      * nc news.cache.ntlworld.com 119
  274      * 200 ntl NNTP news cache. posting ok (feedback to nntptrial-feedback@ntli.net)
  275      * quit
  276      * 205 NNTP Service closing connection - goodbye!
  277      */
  278     { "NNTP news cache", 1 },
  279 };
  280 static const int stat_count = sizeof(stat_versions)/sizeof(stat_versions[0]);
  281 
  282 static struct versions date_versions[] = {
  283     { "NewsCache 0.99.22p", 0 },
  284     { "NewsCache 0.99.2 ", 1 },
  285     { "NewsCache 0.99.20", 1 },
  286     { "NewsCache 0.99.21", 1 },
  287     { "NewsCache 0.99.22", 1 },
  288     { "NewsCache 0.99.2", 0 },
  289     { "NewsCache 1.1.10 ", 1 },
  290     { "NewsCache 1.1.11 ", 1 },
  291     { "NewsCache 1.1.1 ", 1 },
  292     { "NewsCache 1.1.0", 1},
  293     { "NewsCache 1", 0},
  294     { "NewsCache", 1 }
  295 };
  296 static const int date_count = sizeof(date_versions)/sizeof(date_versions[0]);
  297 
  298 static int check_linlist(const char *s, const struct versions *list, int count) {
  299     int i;
  300 
  301     for (i = 0; i < count; i++) {
  302     if (strstr(s, list[i].name)) {
  303         return list[i].is_evil;
  304     }
  305     }
  306     return 0;
  307 }
  308 
  309 #define incopy(a)       (*((struct in_addr *)a))
  310 
  311 /*
  312  * connect to upstream nntp server
  313  *
  314  * returns 200 for posting allowed, 201 for read-only;
  315  * if connection failed, return 0
  316  */
  317 int
  318 nntpconnect(const struct server *upstream)
  319 {
  320     static /*@observer@*/ struct servent *sp;
  321     struct servent sp_def;
  322 #ifdef HAVE_IPV6
  323     struct addrinfo hints, *ai;
  324     struct addrinfo *volatile aii;
  325     char buf[INET6_ADDRSTRLEN+1];
  326 #else
  327     struct sockaddr_in s_in;
  328     struct hostent *hp;
  329     char buf[16];
  330     volatile int i;
  331 #endif
  332     int sock, reply, e, ds;
  333     char *line;
  334 
  335     if (upstream->port == 0) {
  336     sp = getservbyname("nntp", "tcp");
  337     if (sp == NULL) {
  338         ln_log(LNLOG_SERR, LNLOG_CTOP, "error: unable to find service name nntp/tcp");
  339         return FALSE;
  340     }
  341     } else {
  342     sp = &sp_def;
  343     sp->s_port = htons(upstream->port);
  344     }
  345 
  346     sprintf(buf, "%u", (unsigned int)ntohs(sp->s_port));
  347 
  348     /* Fetch the ip addresses of the given host. */
  349 #ifdef HAVE_IPV6
  350     memset((void *)&hints, 0, sizeof(hints));
  351     hints.ai_family = AF_UNSPEC;
  352     hints.ai_socktype = SOCK_STREAM;
  353     hints.ai_protocol = IPPROTO_TCP;
  354     hints.ai_flags = AI_CANONNAME|AI_ADDRCONFIG;
  355 
  356     e = getaddrinfo(upstream->name, buf, &hints, &ai);
  357     if (e == 0) {
  358     for (aii = ai; aii; aii = aii->ai_next) {
  359         sock = socket(aii->ai_family, SOCK_STREAM, 0);
  360         if (sock < 0) {
  361         ln_log(LNLOG_SERR, LNLOG_CTOP,
  362             "error: cannot create inet/stream socket: %m");
  363         break;
  364         }
  365         switch(aii->ai_family) {
  366         case AF_INET6:
  367             inet_ntop(aii->ai_family, &((const struct sockaddr_in6 *)aii->ai_addr)->sin6_addr, buf, sizeof(buf));
  368             break;
  369         case AF_INET:
  370             inet_ntop(aii->ai_family, &((const struct sockaddr_in *)aii->ai_addr)->sin_addr, buf, sizeof(buf));
  371             break;
  372         default:
  373             strcpy(buf, "UNKNOWN");
  374         }
  375 #else
  376     hp = gethostbyname(upstream->name);
  377     if (hp) {
  378     /* Try to make connection to each of the addresses in turn. */
  379     for (i = 0; (int *)(hp->h_addr_list)[i]; i++) {
  380         sock = socket(AF_INET, SOCK_STREAM, 0);
  381         if (sock < 0) {
  382         ln_log(LNLOG_SERR, LNLOG_CTOP,
  383             "error: cannot create inet/stream socket: %m");
  384         break;
  385         }
  386         memset((void *)&s_in, 0, sizeof(s_in));
  387         s_in.sin_family = hp->h_addrtype;
  388         s_in.sin_port = sp->s_port;
  389         s_in.sin_addr = incopy(hp->h_addr_list[i]);
  390 
  391         strcpy(buf, inet_ntoa(s_in.sin_addr));
  392 #endif
  393 
  394         if (sigsetjmp(timeout,1) != 0) {
  395         ln_log(LNLOG_SWARNING, LNLOG_CTOP,
  396             "warning: %s: connection to %s timed out",
  397             upstream->name, buf);
  398         (void)close(sock);
  399         continue;
  400         }
  401 
  402         (void)mysigact(SIGALRM, SA_RESETHAND, timer, 0);
  403         (void)alarm((unsigned)upstream->timeout);
  404 
  405 #ifdef HAVE_IPV6
  406         e = connect(sock, aii->ai_addr, aii->ai_addrlen);
  407         if (e) {
  408         ln_log(LNLOG_SWARNING, LNLOG_CTOP,
  409             "warning: %s: connection to %s failed: %m",
  410             upstream->name, buf);
  411         }
  412 #else
  413         e = connect(sock, (struct sockaddr *)&s_in, sizeof(s_in));
  414         if (e)
  415         ln_log(LNLOG_SWARNING, LNLOG_CTOP,
  416             "warning: %s: connection to %s failed: %m", 
  417             upstream->name, inet_ntoa(s_in.sin_addr));
  418 #endif
  419         (void)alarm(0U);
  420         (void)mysigact(SIGALRM, 0, SIG_DFL, 0);
  421         if (e)
  422         continue;
  423 
  424         nntpout = fdopen(sock, "w");
  425         if (nntpout == NULL) {
  426         ln_log(LNLOG_SERR, LNLOG_CTOP,
  427             "error: %s: fdopen(%d, \"w\") failed: %m",
  428             upstream->name, sock);
  429         break;
  430         }
  431 
  432         if ((ds = dup(sock)) < 0) {
  433         ln_log(LNLOG_SERR, LNLOG_CTOP,
  434             "error: %s: dup(%d) failed returning %d: %m",
  435             upstream->name, sock, ds);
  436         break;
  437         }
  438 
  439         nntpin = fdopen(ds, "r");
  440         if (nntpin == NULL) {
  441         ln_log(LNLOG_SERR, LNLOG_CTOP,
  442             "error: %s: fdopen(%d, \"r\") failed: %m",
  443             upstream->name, sock);
  444         break;
  445         }
  446 
  447         reply = nntpreply(upstream);
  448         if (reply == 200 || reply == 201) {
  449         ln_log(LNLOG_SINFO, LNLOG_CSERVER, "%s: connected to %s:%u, reply: %d",
  450             upstream->name, buf, (unsigned int)ntohs(sp->s_port), reply);
  451         line = lastreply();
  452         if (line == NULL) {
  453             ln_log(LNLOG_SWARNING, LNLOG_CTOP,
  454                 "warning: %s: server disconnect or timeout before sending the greeting",
  455                 upstream->name);
  456             nntpdisconnect();
  457             continue;
  458         }
  459 
  460         if (strstr(line, "NNTPcache server V2.3")) {
  461             /* NNTPcache 2.3.3 is still in widespread use, but it
  462              * has Y2k bugs which have only been fixed in a beta
  463              * version as of 2001-12-24. This 2.3 version is
  464              * unsuitable for any use since 2000-01-01. */
  465             static const char msg[] =
  466             "error: %s: Server greeting \"%s\" hints to "
  467             "NNTPcache v2.3.x. "
  468             "This server has severe (Year 2000) bugs which make it "
  469             "unsuitable for use with leafnode. "
  470             "Ask the news server administrator to update to "
  471             "NNTPcache v3.0.x or newer.";
  472             ln_log(LNLOG_SERR, LNLOG_CTOP, msg, upstream->name, line);
  473             nntpquit();
  474             continue;
  475         }
  476         stat_is_evil = check_linlist(lastreply(), stat_versions,
  477             stat_count);
  478         date_is_evil = check_linlist(lastreply(), date_versions,
  479             date_count);
  480         if (stat_is_evil) {
  481             syslog(LOG_WARNING, "warning: server \"%s\" greeting "
  482                 "\"%s\" hints to "
  483                 "an outdated version with broken "
  484                 "STAT command handling. Please ask the upstream "
  485                 "maintainer to update. "
  486                 "Emulating STAT with HEAD at the expense of bandwidth.",
  487                 upstream->name, line);
  488         }
  489 #ifdef HAVE_IPV6
  490         if (ai)
  491             freeaddrinfo(ai);
  492 #endif
  493         return reply;
  494         } else { /* reply not 200 and not 201 */
  495         char *ll = lastreply();
  496         ln_log(LNLOG_SERR, LNLOG_CTOP, "error: %s: received bogus greeting (%d): %s",
  497             upstream->name, reply, ll ? ll : "(nil)");
  498         nntpquit();
  499         }
  500     } /* end of IP-addresses for loop */
  501 #ifdef HAVE_IPV6
  502     if (!aii)
  503 #else
  504     if (!(int *)(hp->h_addr_list)[i])
  505 #endif
  506         ln_log(LNLOG_SNOTICE, LNLOG_CTOP,
  507             "%s: address list exhausted without establishing connection.",
  508             upstream->name);
  509 #ifdef HAVE_IPV6
  510     if (ai)
  511         freeaddrinfo(ai);
  512 #endif
  513     } else {
  514     /* gethostbyname or getaddrinfo returned error */
  515     const char *er;
  516 #ifdef HAVE_IPV6
  517     er = gai_strerror(e);
  518 #else
  519     switch(h_errno) {
  520         case HOST_NOT_FOUND:
  521         er = "No such host.";
  522         break;
  523         case NO_DATA:
  524         er = "Name exists in DNS, but has no associated address (\"A\"-type DNS resource record).";
  525         break;
  526         case NO_RECOVERY:
  527         er = "Unexpected permanent server failure.";
  528         break;
  529         case TRY_AGAIN:
  530         er = "Temporary DNS error, please try again later.";
  531         break;
  532         default:
  533         er = "Unknown h_errno value.";
  534         break;
  535     }
  536 #endif
  537     ln_log(LNLOG_SWARNING, LNLOG_CTOP,
  538         "warning: %s: cannot resolve host name: %s", upstream->name, er);
  539     }
  540     return FALSE;
  541 }               /* end of connect function */
  542 
  543 /*
  544  * disconnect from upstream server
  545  */
  546 void
  547 nntpdisconnect(void)
  548 {
  549     if (nntpin) {
  550     fclose(nntpin);
  551     nntpin = NULL;
  552     }
  553     if (nntpout) {
  554     fclose(nntpout);
  555     nntpout = NULL;
  556     }
  557 }
  558 
  559 void
  560 nntpquit(void)
  561 {
  562     xsnprintf(lineout, SIZE_lineout, "QUIT\r\n");   /* say it, then just exit :) */
  563     putaline();
  564     nntpdisconnect();
  565 }
  566 
  567 #ifdef MAIN
  568 int verbose=0;
  569 int debug=0;
  570 
  571 int main(int argc, char **argv) {
  572     int i = 1;
  573     while (i < argc) {
  574     int stat_is_evil;
  575     int date_is_evil;
  576 
  577     stat_is_evil = check_linlist(argv[i], stat_versions,
  578         stat_count);
  579     date_is_evil = check_linlist(argv[i], date_versions,
  580         date_count);
  581 
  582     printf("%s: stat_evil=%d, date_evil=%d\n",
  583         argv[i], stat_is_evil, date_is_evil);
  584 
  585     i++;
  586     }
  587     return 0;
  588 }
  589 #endif