"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