1 /* $Id: nnrpd.c 7731 2008-04-06 08:40:29Z iulius $
3 ** NNTP server for readers (NNRP) for InterNetNews.
5 ** This server doesn't do any real load-limiting, except for what has
6 ** proven empirically necesary (i.e., look at GRPscandir).
11 #include "portable/setproctitle.h"
12 #include "portable/wait.h"
22 #include "inn/innconf.h"
23 #include "inn/messages.h"
30 #include "sasl_config.h"
34 int nnrpd_starttls_done = 0;
37 #if NEED_HERRNO_DECLARATION
41 /* If we have getloadavg, include the appropriate header file. Otherwise,
42 just assume that we always have a load of 0. */
44 # if HAVE_SYS_LOADAVG_H
45 # include <sys/loadavg.h>
49 getloadavg(double loadavg[], int nelem)
53 for (i = 0; i < nelem && i < 3; i++)
60 #define MAXPATTERNDEFINE 10
65 typedef struct _CMDENT {
67 void (*Function)(int, char **);
75 char NOACCESS[] = NNTP_ACCESS;
77 char *ACTIVETIMES = NULL;
79 char *NEWSGROUPS = NULL;
80 char *NNRPACCESS = NULL;
82 static char *LocalLogFileName = NULL;
83 static char *LocalLogDirName;
85 struct history *History;
86 static double STATstart;
87 static double STATfinish;
88 static char *PushedBack;
89 static sig_atomic_t ChangeTrace;
90 bool DaemonMode = false;
91 bool ForeGroundMode = false;
93 static const char *ShadowGroup;
95 static const char *HostErrorStr;
96 bool GetHostByAddr = true; /* formerly DO_NNRP_GETHOSTBYADDR */
97 const char *NNRPinstance = "";
100 bool PerlLoaded = false;
104 bool PY_use_dynamic = false;
105 #endif /* DO_PYTHON */
107 static char CMDfetchhelp[] = "[MessageID|Number]";
109 static CMDENT CMDtable[] = {
110 { "authinfo", CMDauthinfo, false, 3, CMDany,
111 "user Name|pass Password|generic <prog> <args>" },
113 { "starttls", CMDstarttls, false, 1, 1,
116 { "article", CMDfetch, true, 1, 2,
118 { "body", CMDfetch, true, 1, 2,
120 { "date", CMDdate, false, 1, 1,
122 { "group", CMDgroup, true, 2, 2,
124 { "head", CMDfetch, true, 1, 2,
126 { "help", CMDhelp, false, 1, CMDany,
128 { "ihave", CMDpost, true, 2, 2,
130 { "last", CMDnextlast, true, 1, 1,
132 { "list", CMDlist, true, 1, 3,
133 "[active|active.times|distrib.pats|distributions|extensions|moderators|motd|newsgroups|overview.fmt|subscriptions]" },
134 { "listgroup", CMDgroup, true, 1, 2,
136 { "mode", CMDmode, false, 2, 2,
138 { "newgroups", CMDnewgroups, true, 3, 5,
139 "[YY]yymmdd hhmmss [\"GMT\"]" },
140 { "newnews", CMDnewnews, true, 4, 5,
141 "newsgroups [YY]yymmdd hhmmss [\"GMT\"]" },
142 { "next", CMDnextlast, true, 1, 1,
144 { "post", CMDpost, true, 1, 1,
146 { "slave", CMD_unimp, false, 1, 1,
148 { "stat", CMDfetch, true, 1, 2,
150 { "xgtitle", CMDxgtitle, true, 1, 2,
152 { "xhdr", CMDpat, true, 2, 3,
153 "header [range|MessageID]" },
154 { "xover", CMDxover, true, 1, 2,
156 { "xpat", CMDpat, true, 4, CMDany,
157 "header range|MessageID pat [morepat...]" },
158 { "xpath", CMDxpath, true, 2, 2,
160 { NULL, CMD_unimp, false, 0, 0,
165 static const char *const timer_name[] = {
175 ** Log a summary status message and exit.
178 ExitWithStats(int x, bool readconf)
183 line_free(&NNTPline);
185 STATfinish = TMRnow_double();
186 if (GetResourceUsage(&usertime, &systime) < 0) {
193 syslog(L_NOTICE, "%s exit articles %ld groups %ld",
194 ClientHost, ARTcount, GRPcount);
195 if (POSTreceived || POSTrejected)
196 syslog(L_NOTICE, "%s posts received %ld rejected %ld",
197 ClientHost, POSTreceived, POSTrejected);
198 syslog(L_NOTICE, "%s times user %.3f system %.3f idle %.3f elapsed %.3f",
199 ClientHost, usertime, systime, IDLEtime, STATfinish - STATstart);
200 /* Tracking code - Make entries in the logfile(s) to show that we have
201 finished with this session */
202 if (!readconf && PERMaccessconf && PERMaccessconf->readertrack) {
203 syslog(L_NOTICE, "%s Tracking Disabled (%s)", ClientHost, Username);
205 fprintf(locallog, "%s Tracking Disabled (%s)\n", ClientHost, Username);
207 syslog(L_NOTICE,"%s Local Logging ends (%s) %s",ClientHost, Username, LocalLogFileName);
211 syslog(L_NOTICE, "%s artstats get %ld time %ld size %ld", ClientHost,
212 ARTget, ARTgettime, ARTgetsize);
213 if (!readconf && PERMaccessconf && PERMaccessconf->nnrpdoverstats && OVERcount)
214 syslog(L_NOTICE, "%s overstats count %ld hit %ld miss %ld time %ld size %ld dbz %ld seek %ld get %ld artcheck %ld", ClientHost,
215 OVERcount, OVERhit, OVERmiss, OVERtime, OVERsize, OVERdbz, OVERseek, OVERget, OVERartcheck);
219 SSL_shutdown(tls_conn);
226 shutdown(STDIN_FILENO, 2);
227 shutdown(STDOUT_FILENO, 2);
228 shutdown(STDERR_FILENO, 2);
230 close(STDOUT_FILENO);
231 close(STDERR_FILENO);
239 #endif /* DO_PYTHON */
244 if (innconf->timer != 0) {
245 TMRsummary(ClientHost, timer_name);
249 if (LocalLogFileName != NULL)
250 free(LocalLogFileName);
257 ** The "help" command.
261 CMDhelp(int ac UNUSED, char *av[] UNUSED)
265 static const char *newsmaster = NEWSMASTER;
267 Reply("%s\r\n", NNTP_HELP_FOLLOWS);
268 for (cp = CMDtable; cp->Name; cp++)
269 if (cp->Help == NULL)
270 Printf(" %s\r\n", cp->Name);
272 Printf(" %s %s\r\n", cp->Name, cp->Help);
273 if (PERMaccessconf && (VirtualPathlen > 0)) {
274 if (PERMaccessconf->newsmaster) {
275 if (strchr(PERMaccessconf->newsmaster, '@') == NULL) {
276 Printf("Report problems to <%s@%s>\r\n",
277 PERMaccessconf->newsmaster, PERMaccessconf->domain);
279 Printf("Report problems to <%s>\r\n",
280 PERMaccessconf->newsmaster);
283 /* sigh, pickup from newsmaster anyway */
284 if ((p = strchr(newsmaster, '@')) == NULL)
285 Printf("Report problems to <%s@%s>\r\n",
286 newsmaster, PERMaccessconf->domain);
288 q = xstrndup(newsmaster, p - newsmaster);
289 Printf("Report problems to <%s@%s>\r\n",
290 q, PERMaccessconf->domain);
295 if (strchr(newsmaster, '@') == NULL)
296 Printf("Report problems to <%s@%s>\r\n",
297 newsmaster, innconf->fromhost);
299 Printf("Report problems to <%s>\r\n",
307 ** Unimplemented catch-all.
315 if (strcasecmp(av[0], "slave") == 0)
316 /* Somebody sends us this? I don't believe it! */
317 Reply("%d Unsupported\r\n", NNTP_SLAVEOK_VAL);
319 Reply("%d %s not implemented; try help\r\n",
320 NNTP_BAD_COMMAND_VAL, av[0]);
324 #ifndef INADDR_LOOPBACK
325 #define INADDR_LOOPBACK 0x7f000001
326 #endif /* INADDR_LOOPBACK */
328 ** Convert an IP address to a hostname. Don't trust the reverse lookup,
329 ** since anyone can fake .in-addr.arpa entries.
332 Address2Name(INADDR *ap, char *hostname, int i)
336 static char mismatch_error[] = "reverse lookup validation failed";
339 /* Get the official hostname, store it away. */
340 if ((hp = gethostbyaddr((char *)ap, sizeof *ap, AF_INET)) == NULL) {
341 HostErrorStr = hstrerror(h_errno);
344 strlcpy(hostname, hp->h_name, i);
346 /* Get addresses for this host. */
347 if ((hp = gethostbyname(hostname)) == NULL) {
348 HostErrorStr = hstrerror(h_errno);
352 /* Make sure one of those addresses is the address we got. */
353 for (pp = hp->h_addr_list; *pp; pp++)
354 if (strncmp((const char *)&ap->s_addr, *pp, hp->h_length) == 0)
358 HostErrorStr = mismatch_error;
362 /* Only needed for misconfigured YP/NIS systems. */
363 if (ap->s_addr != INADDR_LOOPBACK && strchr(hostname, '.') == NULL
364 && (p = innconf->domain) != NULL) {
365 strlcat(hostname, ".", i);
366 strlcat(hostname, p, i);
369 /* Make all lowercase, for wildmat. */
370 for (p = hostname; *p; p++)
371 if (CTYPE(isupper, (int)*p))
377 ** Convert an IPv6 address to a hostname. Don't trust the reverse lookup,
378 ** since anyone can fake .ip6.arpa entries.
382 Address2Name6(struct sockaddr *sa, char *hostname, int i)
384 static char mismatch_error[] = "reverse lookup validation failed";
387 struct addrinfo hints, *res, *res0;
390 /* Get the official hostname, store it away. */
391 ret = getnameinfo( sa, SA_LEN( sa ), hostname, i, NULL, 0, NI_NAMEREQD );
394 HostErrorStr = gai_strerror( ret );
398 /* Get addresses for this host. */
399 memset( &hints, 0, sizeof( hints ) );
400 hints.ai_socktype = SOCK_STREAM;
401 hints.ai_family = AF_INET6;
402 if( ( ret = getaddrinfo( hostname, NULL, &hints, &res0 ) ) != 0 )
404 HostErrorStr = gai_strerror( ret );
408 /* Make sure one of those addresses is the address we got. */
409 for( res = res0; res; res = res->ai_next )
411 #ifdef HAVE_BROKEN_IN6_ARE_ADDR_EQUAL
412 if( ! memcmp( &(((struct sockaddr_in6 *)sa)->sin6_addr),
413 &(((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr),
414 sizeof( struct in6_addr ) ) )
416 if( IN6_ARE_ADDR_EQUAL( &(((struct sockaddr_in6 *)sa)->sin6_addr),
417 &(((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr) ) )
425 freeaddrinfo( res0 );
428 /* Make all lowercase for matching. */
429 for (p = hostname; *p != '\0'; p++)
430 if (CTYPE(isupper, *p))
434 HostErrorStr = mismatch_error;
442 Sock2String( struct sockaddr *sa, char *string, int len, bool lookup )
444 struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
447 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
448 struct sockaddr_in temp;
450 if( sa->sa_family == AF_INET6 )
452 if( ! IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) )
456 return Address2Name6(sa, string, len);
458 strlcpy( string, sprint_sockaddr( sa ), len );
462 temp.sin_family = AF_INET;
463 memcpy( &temp.sin_addr, sin6->sin6_addr.s6_addr + 12, 4 );
464 temp.sin_port = sin6->sin6_port;
466 /* fall through to AF_INET case */
471 return Address2Name(&sin4->sin_addr, string, len);
473 strlcpy( string, inet_ntoa(sin4->sin_addr), len );
479 ** Determine access rights of the client.
481 static void StartConnection(void)
483 struct sockaddr_storage ssc, sss;
485 const char *default_host_error = "unknown error";
488 ClientHost[0] = '\0';
489 ClientIpString[0] = '\0';
491 ServerHost[0] = '\0';
492 ServerIpString[0] = '\0';
495 /* Get the peer's name. */
497 if (getpeername(STDIN_FILENO, (struct sockaddr *)&ssc, &length) < 0) {
498 if (!isatty(STDIN_FILENO)) {
499 syslog(L_TRACE, "%s cant getpeername %m", "?");
500 /* so stats generation looks correct. */
501 strlcpy(ClientHost, "?", sizeof(ClientHost));
502 Printf("%d I can't get your name. Goodbye.\r\n", NNTP_ACCESS_VAL);
503 ExitWithStats(1, true);
505 strlcpy(ClientHost, "stdin", sizeof(ClientHost));
510 if ( ssc.ss_family != AF_INET && ssc.ss_family != AF_INET6) {
512 if ( ssc.ss_family != AF_INET ) {
514 syslog(L_ERROR, "%s bad_address_family %ld",
515 "?", (long)ssc.ss_family);
516 Printf("%d Bad address family. Goodbye.\r\n", NNTP_ACCESS_VAL);
517 ExitWithStats(1, true);
521 if (getsockname(STDIN_FILENO, (struct sockaddr *)&sss, &length) < 0) {
522 syslog(L_NOTICE, "%s can't getsockname %m", ClientHost);
523 Printf("%d Can't figure out where you connected to. Goodbye\r\n", NNTP_ACCESS_VAL);
524 ExitWithStats(1, true);
527 /* figure out client's IP address/hostname */
528 HostErrorStr = default_host_error;
529 if( ! Sock2String( (struct sockaddr *)&ssc, ClientIpString,
530 sizeof( ClientIpString ), false ) ) {
531 syslog(L_NOTICE, "? cant get client numeric address: %s", HostErrorStr);
532 ExitWithStats(1, true);
535 HostErrorStr = default_host_error;
536 if( ! Sock2String( (struct sockaddr *)&ssc, ClientHost,
537 sizeof( ClientHost ), true ) ) {
539 "? reverse lookup for %s failed: %s -- using IP address for access",
540 ClientIpString, HostErrorStr);
541 strlcpy(ClientHost, ClientIpString, sizeof(ClientHost));
544 strlcpy(ClientHost, ClientIpString, sizeof(ClientHost));
547 /* figure out server's IP address/hostname */
548 HostErrorStr = default_host_error;
549 if( ! Sock2String( (struct sockaddr *)&sss, ServerIpString,
550 sizeof( ServerIpString ), false ) ) {
551 syslog(L_NOTICE, "? cant get server numeric address: %s", HostErrorStr);
552 ExitWithStats(1, true);
555 HostErrorStr = default_host_error;
556 if( ! Sock2String( (struct sockaddr *)&sss, ServerHost,
557 sizeof( ServerHost ), true ) ) {
559 "? reverse lookup for %s failed: %s -- using IP address for access",
560 ServerIpString, HostErrorStr);
561 strlcpy(ServerHost, ServerIpString, sizeof(ServerHost));
564 strlcpy(ServerHost, ServerIpString, sizeof(ServerHost));
567 /* get port numbers */
568 switch( ssc.ss_family ) {
570 ClientPort = ntohs( ((struct sockaddr_in *)&ssc)->sin_port );
571 ServerPort = ntohs( ((struct sockaddr_in *)&sss)->sin_port );
575 ClientPort = ntohs( ((struct sockaddr_in6 *)&ssc)->sin6_port );
576 ServerPort = ntohs( ((struct sockaddr_in6 *)&sss)->sin6_port );
582 strlcpy(LogName, ClientHost, sizeof(LogName));
584 syslog(L_NOTICE, "%s (%s) connect", ClientHost, ClientIpString);
586 PERMgetaccess(NNRPACCESS);
587 PERMgetpermissions();
592 ** Send a reply, possibly with debugging output.
595 Reply(const char *fmt, ...)
607 vsnprintf(buff, sizeof(buff), fmt, args);
609 TMRstart(TMR_NNTPWRITE);
611 r = SSL_write(tls_conn, buff, strlen(buff));
612 switch (SSL_get_error(tls_conn, r)) {
614 case SSL_ERROR_SYSCALL:
616 case SSL_ERROR_WANT_WRITE:
620 SSL_shutdown(tls_conn);
624 case SSL_ERROR_ZERO_RETURN:
627 TMRstop(TMR_NNTPWRITE);
630 TMRstart(TMR_NNTPWRITE);
632 TMRstop(TMR_NNTPWRITE);
637 TMRstart(TMR_NNTPWRITE);
639 TMRstop(TMR_NNTPWRITE);
646 /* Copy output, but strip trailing CR-LF. Note we're assuming here
647 that no output line can ever be longer than 2045 characters. */
648 vsnprintf(buff, sizeof(buff), fmt, args);
650 p = buff + strlen(buff) - 1;
651 while (p >= buff && (*p == '\n' || *p == '\r'))
653 syslog(L_TRACE, "%s > %s", ClientHost, buff);
660 Printf(const char *fmt, ...)
670 vsnprintf(buff, sizeof(buff), fmt, args);
672 TMRstart(TMR_NNTPWRITE);
674 r = SSL_write(tls_conn, buff, strlen(buff));
675 switch (SSL_get_error(tls_conn, r)) {
677 case SSL_ERROR_SYSCALL:
679 case SSL_ERROR_WANT_WRITE:
683 SSL_shutdown(tls_conn);
687 case SSL_ERROR_ZERO_RETURN:
690 TMRstop(TMR_NNTPWRITE);
692 #endif /* HAVE_SSL */
694 TMRstart(TMR_NNTPWRITE);
696 TMRstop(TMR_NNTPWRITE);
700 #endif /* HAVE_SSL */
704 #ifdef HAVE_SIGACTION
705 #define NO_SIGACTION_UNUSED UNUSED
707 #define NO_SIGACTION_UNUSED
710 ** Got a signal; toggle tracing.
713 ToggleTrace(int s NO_SIGACTION_UNUSED)
716 #ifndef HAVE_SIGACTION
717 xsignal(s, ToggleTrace);
722 ** Got a SIGPIPE; exit cleanly
725 CatchPipe(int s UNUSED)
727 ExitWithStats(0, false);
731 ** Got a signal; wait for children.
734 WaitChild(int s NO_SIGACTION_UNUSED)
739 pid = waitpid(-1, NULL, WNOHANG);
743 #ifndef HAVE_SIGACTION
744 xsignal(s, WaitChild);
748 static void SetupDaemon(void) {
752 if (SMsetup(SM_PREOPEN, (void *)&val) && !SMinit()) {
753 syslog(L_NOTICE, "cant initialize storage method, %s", SMerrorstr);
754 Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
755 ExitWithStats(1, true);
757 OVextra = overview_extra_fields();
758 if (OVextra == NULL) {
759 /* overview_extra_fields should already have logged something
761 Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
762 ExitWithStats(1, true);
764 overhdr_xref = overview_index("Xref", OVextra);
765 if (!OVopen(OV_READ)) {
766 /* This shouldn't really happen. */
767 syslog(L_NOTICE, "cant open overview %m");
768 Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
769 ExitWithStats(1, true);
771 if (!OVctl(OVCACHEKEEP, &val)) {
772 syslog(L_NOTICE, "cant enable overview cache %m");
773 Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
774 ExitWithStats(1, true);
779 ** Print a usage message and exit.
784 fprintf(stderr, "Usage error.\n");
791 main(int argc, char *argv[])
795 char buff[NNTP_STRLEN];
805 unsigned short ListenPort = NNTP_PORT;
807 char ListenAddr[INET6_ADDRSTRLEN];
814 struct sockaddr_storage ssa, csa;
815 struct sockaddr_in6 *ssa6 = (struct sockaddr_in6 *) &ssa;
817 struct sockaddr_in ssa, csa;
819 struct sockaddr_in *ssa4 = (struct sockaddr_in *) &ssa;
828 char *ConfFile = NULL;
833 #endif /* HAVE_GETSPNAM */
837 setproctitle_init(argc, argv);
839 /* Parse arguments. Must xstrdup() optarg if used because setproctitle may
844 MaxBytesPerSecond = 0;
845 strlcpy(Username, "unknown", sizeof(Username));
847 /* Set up the pathname, first thing, and teach our error handlers about
848 the name of the program. */
850 if (name == NULL || *name == '\0')
855 p = strrchr(name, '/');
859 message_program_name = xstrdup(name);
860 openlog(message_program_name, L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
861 message_handlers_die(1, message_log_syslog_crit);
862 message_handlers_warn(1, message_log_syslog_warning);
863 message_handlers_notice(1, message_log_syslog_notice);
865 if (!innconf_read(NULL))
869 while ((i = getopt(argc, argv, "c:b:Dfi:I:g:nop:P:r:s:tS")) != EOF)
871 while ((i = getopt(argc, argv, "c:b:Dfi:I:g:nop:P:r:s:t")) != EOF)
872 #endif /* HAVE_SSL */
877 case 'c': /* use alternate readers.conf */
878 ConfFile = concatpath(innconf->pathetc, optarg);
880 case 'b': /* bind to a certain address in
882 strlcpy(ListenAddr, optarg, sizeof(ListenAddr));
884 case 'D': /* standalone daemon mode */
887 case 'P': /* prespawn count in daemon mode */
888 respawn = atoi(optarg);
890 case 'f': /* Don't fork on daemon mode */
891 ForeGroundMode = true;
895 ShadowGroup = optarg;
897 #endif /* HAVE_GETSPNAM */
898 case 'i': /* Initial command */
899 PushedBack = xstrdup(optarg);
901 case 'I': /* Instance */
902 NNRPinstance = xstrdup(optarg);
904 case 'n': /* No DNS lookups */
905 GetHostByAddr = false;
908 Offlinepost = true; /* Offline posting only */
910 case 'p': /* tcp port for daemon mode */
911 ListenPort = atoi(optarg);
913 case 'r': /* Reject connection message */
914 Reject = xstrdup(optarg);
916 case 's': /* Unused title string */
918 case 't': /* Tracing */
922 case 'S': /* SSL negotiation as soon as connected */
925 #endif /* HAVE_SSL */
932 * Make other processes happier if someone is reading
933 * This allows other processes like 'overchan' to keep up when
934 * there are lots of readers. Note that this is cumulative with
937 if (innconf->nicennrpd > 0)
938 nice(innconf->nicennrpd);
940 HISTORY = concatpath(innconf->pathdb, _PATH_HISTORY);
941 ACTIVE = concatpath(innconf->pathdb, _PATH_ACTIVE);
942 ACTIVETIMES = concatpath(innconf->pathdb, _PATH_ACTIVETIMES);
943 NEWSGROUPS = concatpath(innconf->pathdb, _PATH_NEWSGROUPS);
945 NNRPACCESS = ConfFile;
947 NNRPACCESS = concatpath(innconf->pathetc,_PATH_NNRPACCESS);
948 SPOOLlen = strlen(innconf->patharticles);
952 memset(&ssa, '\0', sizeof(struct sockaddr_in6));
953 ssa6->sin6_family = AF_INET6;
954 ssa6->sin6_port = htons(ListenPort);
955 if (inet_pton(AF_INET6, ListenAddr, ssa6->sin6_addr.s6_addr) > 0) {
956 if ( (lfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
957 syslog(L_FATAL, "can't open socket (%m)");
963 memset(&ssa, '\0', sizeof(struct sockaddr_in));
964 ssa4->sin_family = AF_INET;
965 ssa4->sin_port = htons(ListenPort);
966 if (inet_aton(ListenAddr, &ssa4->sin_addr) <= 0 )
967 ssa4->sin_addr.s_addr = htonl(INADDR_ANY);
968 if ( (lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
969 syslog(L_FATAL, "can't open socket (%m)");
976 if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
977 (char *)&one, sizeof(one)) < 0) {
978 syslog(L_FATAL, "can't setsockopt(SO_REUSEADDR) (%m)");
982 if (bind(lfd, (struct sockaddr *) &ssa, sizeof(ssa)) < 0) {
983 fprintf(stderr, "%s: can't bind (%s)\n", argv[0], strerror(errno));
984 syslog(L_FATAL, "can't bind local address (%m)");
988 /* If started as root, switch to news uid */
990 if (stat(innconf->pathrun, &Sb) < 0 || !S_ISDIR(Sb.st_mode)) {
991 syslog(L_FATAL, "nnrpd cant stat %s %m", innconf->pathrun);
994 if (Sb.st_uid == 0) {
995 syslog(L_FATAL, "nnrpd %s must not be owned by root", innconf->pathrun);
998 pwd = getpwnam(NEWSUSER);
999 if (pwd == (struct passwd *)NULL) {
1000 syslog(L_FATAL, "nnrpd getpwnam(%s): %s", NEWSUSER, strerror(errno));
1002 } else if (pwd->pw_gid != Sb.st_gid) {
1003 syslog(L_FATAL, "nnrpd %s must have group %s", innconf->pathrun, NEWSGRP);
1005 } else if (pwd->pw_uid != Sb.st_uid) {
1006 syslog(L_FATAL, "nnrpd %s must be owned by %s", innconf->pathrun, NEWSUSER);
1011 shadowgid = (gid_t) -1;
1012 /* Find shadowgroup gid if needed */
1013 if (ShadowGroup != NULL) {
1014 if ((grp = getgrnam(ShadowGroup)) == NULL)
1015 syslog(L_ERROR, "nnrpd cannot find group %s",
1018 shadowgid = grp->gr_gid;
1019 } else if ((grp = getgrnam("shadow")) != NULL) {
1020 /* found default group "shadow" */
1021 shadowgid = grp->gr_gid;
1022 ShadowGroup = "shadow";
1024 /* If we have a shadowgid, try to set it as an extra group. */
1025 if (shadowgid != (gid_t) -1) {
1026 if (setgroups(1, &shadowgid) < 0)
1027 syslog(L_ERROR, "nnrpd cannot set supplementary group %s %m",
1030 syslog(L_NOTICE, "nnrpd added supplementary group %s",
1033 #endif /* HAVE_GETSPNAM */
1035 NewsUID = Sb.st_uid;
1036 NewsGID = Sb.st_gid;
1038 if (getgid() != NewsGID)
1039 syslog(L_ERROR, "nnrpd cant setgid to %d %m", NewsGID);
1041 if (getuid() != NewsUID)
1042 syslog(L_ERROR, "nnrpd cant setuid to %d %m", NewsUID);
1046 if (!ForeGroundMode) {
1050 if (ListenPort == NNTP_PORT)
1051 strlcpy(buff, "nnrpd.pid", sizeof(buff));
1053 snprintf(buff, sizeof(buff), "nnrpd-%d.pid", ListenPort);
1054 path = concatpath(innconf->pathrun, buff);
1055 pidfile = fopen(path, "w");
1057 if (pidfile == NULL) {
1058 syslog(L_ERROR, "cannot write %s %m", buff);
1061 fprintf(pidfile,"%lu\n", (unsigned long) getpid());
1064 /* Set signal handle to care for dead children */
1066 xsignal(SIGCHLD, WaitChild);
1068 /* Arrange to toggle tracing. */
1069 xsignal(SIGHUP, ToggleTrace);
1071 setproctitle("accepting connections");
1076 /* pre-forked mode */
1084 fd = accept(lfd, (struct sockaddr *) &csa, &clen);
1093 pid = waitpid(-1, NULL, WNOHANG);
1100 /* fork on demand */
1103 fd = accept(lfd, (struct sockaddr *) &csa, &clen);
1107 for (i = 0; i <= innconf->maxforks && (pid = fork()) < 0; i++) {
1108 if (i == innconf->maxforks) {
1109 syslog(L_FATAL, "cant fork (dropping connection): %m");
1112 syslog(L_NOTICE, "cant fork (waiting): %m");
1116 Tracing = Tracing ? false : true;
1117 syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
1118 ChangeTrace = false;
1125 /* child process starts here */
1126 setproctitle("connected");
1132 if (innconf->timer != 0)
1134 STATstart = TMRnow_double();
1137 /* if we are a daemon innd didn't make us nice, so be nice kids */
1138 if (innconf->nicekids) {
1139 if (nice(innconf->nicekids) < 0)
1140 syslog(L_ERROR, "Could not nice child to %ld: %m", innconf->nicekids);
1143 /* Only automatically reap children in the listening process */
1144 xsignal(SIGCHLD, SIG_DFL);
1149 STATstart = TMRnow_double();
1151 /* Arrange to toggle tracing. */
1152 xsignal(SIGHUP, ToggleTrace);
1159 if (tls_start_servertls(0, 1) == -1) {
1160 Reply("%d SSL connection failed\r\n", NNTP_STARTTLS_BAD_VAL);
1161 ExitWithStats(1, false);
1163 nnrpd_starttls_done = 1;
1166 #endif /* HAVE_SSL */
1168 /* If requested, check the load average. */
1169 if (innconf->nnrpdloadlimit > 0) {
1172 if (getloadavg(load, 1) < 0)
1173 warn("cannot obtain system load");
1175 if ((int)(load[0] + 0.5) > innconf->nnrpdloadlimit) {
1176 syslog(L_NOTICE, "load %.2f > %ld", load[0], innconf->nnrpdloadlimit);
1177 Reply("%d load at %.2f, try later\r\n", NNTP_GOODBYE_VAL,
1179 ExitWithStats(1, true);
1184 strlcpy(LogName, "?", sizeof(LogName));
1186 /* Catch SIGPIPE so that we can exit out of long write loops */
1187 xsignal(SIGPIPE, CatchPipe);
1189 /* Get permissions and see if we can talk to this client */
1191 if (!PERMcanread && !PERMcanpost && !PERMneedauth) {
1192 syslog(L_NOTICE, "%s no_permission", ClientHost);
1193 Printf("%d You have no permission to talk. Goodbye.\r\n",
1195 ExitWithStats(1, false);
1198 /* Proceed with initialization. */
1199 setproctitle("%s connect", ClientHost);
1201 /* Were we told to reject connections? */
1203 syslog(L_NOTICE, "%s rejected %s", ClientHost, Reject);
1204 Reply("%s %s\r\n", NNTP_GOODBYE, Reject);
1205 ExitWithStats(0, false);
1208 if (PERMaccessconf) {
1209 if (PERMaccessconf->readertrack)
1210 PERMaccessconf->readertrack=TrackClient(ClientHost,Username);
1212 if (innconf->readertrack)
1213 innconf->readertrack=TrackClient(ClientHost,Username);
1216 if ((PERMaccessconf && PERMaccessconf->readertrack)
1217 || (!PERMaccessconf && innconf->readertrack)) {
1219 syslog(L_NOTICE, "%s Tracking Enabled (%s)", ClientHost, Username);
1221 gettimeofday(&tv,NULL);
1223 vid = tv.tv_sec ^ tv.tv_usec ^ pid ^ count;
1224 len = strlen("innconf->pathlog") + strlen("/tracklogs/log-") + BUFSIZ;
1225 LocalLogFileName = xmalloc(len);
1226 sprintf(LocalLogFileName, "%s/tracklogs/log-%d", innconf->pathlog, vid);
1227 if ((locallog = fopen(LocalLogFileName, "w")) == NULL) {
1228 LocalLogDirName = concatpath(innconf->pathlog, "tracklogs");
1229 MakeDirectory(LocalLogDirName, false);
1230 free(LocalLogDirName);
1232 if (locallog == NULL && (locallog = fopen(LocalLogFileName, "w")) == NULL) {
1233 syslog(L_ERROR, "%s Local Logging failed (%s) %s: %m", ClientHost, Username, LocalLogFileName);
1235 syslog(L_NOTICE, "%s Local Logging begins (%s) %s",ClientHost, Username, LocalLogFileName);
1236 fprintf(locallog, "%s Tracking Enabled (%s)\n", ClientHost, Username);
1242 if (PERMaccessconf) {
1243 Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
1244 PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
1245 PERMaccessconf->pathhost, inn_version_string,
1246 PERMcanpost ? "posting ok" : "no posting");
1247 clienttimeout = PERMaccessconf->clienttimeout;
1249 Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
1250 PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
1251 innconf->pathhost, inn_version_string,
1252 PERMcanpost ? "posting ok" : "no posting");
1253 clienttimeout = innconf->clienttimeout;
1256 line_init(&NNTPline);
1258 /* Main dispatch loop. */
1259 for (timeout = innconf->initialtimeout, av = NULL, ac = 0; ;
1260 timeout = clienttimeout) {
1261 TMRstart(TMR_NNTPWRITE);
1263 TMRstop(TMR_NNTPWRITE);
1265 Tracing = Tracing ? false : true;
1266 syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
1267 ChangeTrace = false;
1270 if (PushedBack[0] == '\0')
1273 syslog(L_TRACE, "%s < %s", ClientHost, PushedBack);
1274 ac = Argify(PushedBack, &av);
1281 r = line_read(&NNTPline, timeout, &p, &len);
1284 syslog(L_ERROR, "%s internal %d in main", ClientHost, r);
1287 if (timeout < clienttimeout)
1288 syslog(L_NOTICE, "%s timeout short", ClientHost);
1290 syslog(L_NOTICE, "%s timeout", ClientHost);
1291 ExitWithStats(1, false);
1294 if (len < sizeof(buff)) {
1295 /* line_read guarantees null termination */
1296 memcpy(buff, p, len + 1);
1297 /* Do some input processing, check for blank line. */
1299 syslog(L_TRACE, "%s < %s", ClientHost, buff);
1300 if (buff[0] == '\0')
1302 ac = Argify(buff, &av);
1307 Reply("%d Line too long\r\n", NNTP_BAD_COMMAND_VAL);
1310 /* Handled below. */
1317 if (ac == 0 || strcasecmp(av[0], "quit") == 0)
1321 for (cp = CMDtable; cp->Name; cp++)
1322 if (strcasecmp(cp->Name, av[0]) == 0)
1324 if (cp->Name == NULL) {
1325 if ((int)strlen(buff) > 40)
1326 syslog(L_NOTICE, "%s unrecognized %.40s...", ClientHost, buff);
1328 syslog(L_NOTICE, "%s unrecognized %s", ClientHost, buff);
1329 Reply("%d What?\r\n", NNTP_BAD_COMMAND_VAL);
1334 if ((cp->Minac != CMDany && ac < cp->Minac)
1335 || (cp->Maxac != CMDany && ac > cp->Maxac)) {
1337 NNTP_SYNTAX_VAL, cp->Help ? cp->Help : "Usage error");
1341 /* Check permissions and dispatch. */
1342 if (cp->Needauth && PERMneedauth) {
1343 Reply("%d Authentication required for command\r\n",
1344 NNTP_AUTH_NEEDED_VAL);
1347 setproctitle("%s %s", ClientHost, av[0]);
1348 (*cp->Function)(ac, av);
1352 clienttimeout = PERMaccessconf->clienttimeout;
1354 clienttimeout = innconf->clienttimeout;
1357 Reply("%s\r\n", NNTP_GOODBYE_ACK);
1359 ExitWithStats(0, false);