+++ /dev/null
-/* $Id: nnrpd.c 7731 2008-04-06 08:40:29Z iulius $
-**
-** NNTP server for readers (NNRP) for InterNetNews.
-**
-** This server doesn't do any real load-limiting, except for what has
-** proven empirically necesary (i.e., look at GRPscandir).
-*/
-
-#include "config.h"
-#include "clibrary.h"
-#include "portable/setproctitle.h"
-#include "portable/wait.h"
-#include <grp.h>
-#include <netdb.h>
-#include <pwd.h>
-#include <signal.h>
-
-#if HAVE_GETSPNAM
-# include <shadow.h>
-#endif
-
-#include "inn/innconf.h"
-#include "inn/messages.h"
-#include "libinn.h"
-#include "ov.h"
-#define MAINLINE
-#include "nnrpd.h"
-
-#include "tls.h"
-#include "sasl_config.h"
-
-#ifdef HAVE_SSL
-extern SSL *tls_conn;
-int nnrpd_starttls_done = 0;
-#endif
-
-#if NEED_HERRNO_DECLARATION
-extern int h_errno;
-#endif
-
-/* If we have getloadavg, include the appropriate header file. Otherwise,
- just assume that we always have a load of 0. */
-#if HAVE_GETLOADAVG
-# if HAVE_SYS_LOADAVG_H
-# include <sys/loadavg.h>
-# endif
-#else
-static int
-getloadavg(double loadavg[], int nelem)
-{
- int i;
-
- for (i = 0; i < nelem && i < 3; i++)
- loadavg[i] = 0;
- return i;
-}
-#endif
-
-
-#define MAXPATTERNDEFINE 10
-
-#define CMDany -1
-
-
-typedef struct _CMDENT {
- const char * Name;
- void (*Function)(int, char **);
- bool Needauth;
- int Minac;
- int Maxac;
- const char * Help;
-} CMDENT;
-
-
-char NOACCESS[] = NNTP_ACCESS;
-char *ACTIVE = NULL;
-char *ACTIVETIMES = NULL;
-char *HISTORY = NULL;
-char *NEWSGROUPS = NULL;
-char *NNRPACCESS = NULL;
-
-static char *LocalLogFileName = NULL;
-static char *LocalLogDirName;
-
-struct history *History;
-static double STATstart;
-static double STATfinish;
-static char *PushedBack;
-static sig_atomic_t ChangeTrace;
-bool DaemonMode = false;
-bool ForeGroundMode = false;
-#if HAVE_GETSPNAM
-static const char *ShadowGroup;
-#endif
-static const char *HostErrorStr;
-bool GetHostByAddr = true; /* formerly DO_NNRP_GETHOSTBYADDR */
-const char *NNRPinstance = "";
-
-#ifdef DO_PERL
-bool PerlLoaded = false;
-#endif /* DO_PERL */
-
-#ifdef DO_PYTHON
-bool PY_use_dynamic = false;
-#endif /* DO_PYTHON */
-
-static char CMDfetchhelp[] = "[MessageID|Number]";
-
-static CMDENT CMDtable[] = {
- { "authinfo", CMDauthinfo, false, 3, CMDany,
- "user Name|pass Password|generic <prog> <args>" },
-#ifdef HAVE_SSL
- { "starttls", CMDstarttls, false, 1, 1,
- NULL },
-#endif
- { "article", CMDfetch, true, 1, 2,
- CMDfetchhelp },
- { "body", CMDfetch, true, 1, 2,
- CMDfetchhelp },
- { "date", CMDdate, false, 1, 1,
- NULL },
- { "group", CMDgroup, true, 2, 2,
- "newsgroup" },
- { "head", CMDfetch, true, 1, 2,
- CMDfetchhelp },
- { "help", CMDhelp, false, 1, CMDany,
- NULL },
- { "ihave", CMDpost, true, 2, 2,
- "MessageID" },
- { "last", CMDnextlast, true, 1, 1,
- NULL },
- { "list", CMDlist, true, 1, 3,
- "[active|active.times|distrib.pats|distributions|extensions|moderators|motd|newsgroups|overview.fmt|subscriptions]" },
- { "listgroup", CMDgroup, true, 1, 2,
- "newsgroup" },
- { "mode", CMDmode, false, 2, 2,
- "reader" },
- { "newgroups", CMDnewgroups, true, 3, 5,
- "[YY]yymmdd hhmmss [\"GMT\"]" },
- { "newnews", CMDnewnews, true, 4, 5,
- "newsgroups [YY]yymmdd hhmmss [\"GMT\"]" },
- { "next", CMDnextlast, true, 1, 1,
- NULL },
- { "post", CMDpost, true, 1, 1,
- NULL },
- { "slave", CMD_unimp, false, 1, 1,
- NULL },
- { "stat", CMDfetch, true, 1, 2,
- CMDfetchhelp },
- { "xgtitle", CMDxgtitle, true, 1, 2,
- "[group_pattern]" },
- { "xhdr", CMDpat, true, 2, 3,
- "header [range|MessageID]" },
- { "xover", CMDxover, true, 1, 2,
- "[range]" },
- { "xpat", CMDpat, true, 4, CMDany,
- "header range|MessageID pat [morepat...]" },
- { "xpath", CMDxpath, true, 2, 2,
- "MessageID" },
- { NULL, CMD_unimp, false, 0, 0,
- NULL }
-};
-
-
-static const char *const timer_name[] = {
- "idle",
- "newnews",
- "readart",
- "checkart",
- "nntpread",
- "nntpwrite",
-};
-
-/*
-** Log a summary status message and exit.
-*/
-void
-ExitWithStats(int x, bool readconf)
-{
- double usertime;
- double systime;
-
- line_free(&NNTPline);
- fflush(stdout);
- STATfinish = TMRnow_double();
- if (GetResourceUsage(&usertime, &systime) < 0) {
- usertime = 0;
- systime = 0;
- }
-
- GRPreport();
- if (ARTcount)
- syslog(L_NOTICE, "%s exit articles %ld groups %ld",
- ClientHost, ARTcount, GRPcount);
- if (POSTreceived || POSTrejected)
- syslog(L_NOTICE, "%s posts received %ld rejected %ld",
- ClientHost, POSTreceived, POSTrejected);
- syslog(L_NOTICE, "%s times user %.3f system %.3f idle %.3f elapsed %.3f",
- ClientHost, usertime, systime, IDLEtime, STATfinish - STATstart);
- /* Tracking code - Make entries in the logfile(s) to show that we have
- finished with this session */
- if (!readconf && PERMaccessconf && PERMaccessconf->readertrack) {
- syslog(L_NOTICE, "%s Tracking Disabled (%s)", ClientHost, Username);
- if (LLOGenable) {
- fprintf(locallog, "%s Tracking Disabled (%s)\n", ClientHost, Username);
- fclose(locallog);
- syslog(L_NOTICE,"%s Local Logging ends (%s) %s",ClientHost, Username, LocalLogFileName);
- }
- }
- if (ARTget)
- syslog(L_NOTICE, "%s artstats get %ld time %ld size %ld", ClientHost,
- ARTget, ARTgettime, ARTgetsize);
- if (!readconf && PERMaccessconf && PERMaccessconf->nnrpdoverstats && OVERcount)
- syslog(L_NOTICE, "%s overstats count %ld hit %ld miss %ld time %ld size %ld dbz %ld seek %ld get %ld artcheck %ld", ClientHost,
- OVERcount, OVERhit, OVERmiss, OVERtime, OVERsize, OVERdbz, OVERseek, OVERget, OVERartcheck);
-
-#ifdef HAVE_SSL
- if (tls_conn) {
- SSL_shutdown(tls_conn);
- SSL_free(tls_conn);
- tls_conn = NULL;
- }
-#endif
-
- if (DaemonMode) {
- shutdown(STDIN_FILENO, 2);
- shutdown(STDOUT_FILENO, 2);
- shutdown(STDERR_FILENO, 2);
- close(STDIN_FILENO);
- close(STDOUT_FILENO);
- close(STDERR_FILENO);
- }
-
- OVclose();
- SMshutdown();
-
-#ifdef DO_PYTHON
- PY_close_python();
-#endif /* DO_PYTHON */
-
- if (History)
- HISclose(History);
-
- if (innconf->timer != 0) {
- TMRsummary(ClientHost, timer_name);
- TMRfree();
- }
-
- if (LocalLogFileName != NULL)
- free(LocalLogFileName);
- closelog();
- exit(x);
-}
-
-
-/*
-** The "help" command.
-*/
-/* ARGSUSED0 */
-void
-CMDhelp(int ac UNUSED, char *av[] UNUSED)
-{
- CMDENT *cp;
- char *p, *q;
- static const char *newsmaster = NEWSMASTER;
-
- Reply("%s\r\n", NNTP_HELP_FOLLOWS);
- for (cp = CMDtable; cp->Name; cp++)
- if (cp->Help == NULL)
- Printf(" %s\r\n", cp->Name);
- else
- Printf(" %s %s\r\n", cp->Name, cp->Help);
- if (PERMaccessconf && (VirtualPathlen > 0)) {
- if (PERMaccessconf->newsmaster) {
- if (strchr(PERMaccessconf->newsmaster, '@') == NULL) {
- Printf("Report problems to <%s@%s>\r\n",
- PERMaccessconf->newsmaster, PERMaccessconf->domain);
- } else {
- Printf("Report problems to <%s>\r\n",
- PERMaccessconf->newsmaster);
- }
- } else {
- /* sigh, pickup from newsmaster anyway */
- if ((p = strchr(newsmaster, '@')) == NULL)
- Printf("Report problems to <%s@%s>\r\n",
- newsmaster, PERMaccessconf->domain);
- else {
- q = xstrndup(newsmaster, p - newsmaster);
- Printf("Report problems to <%s@%s>\r\n",
- q, PERMaccessconf->domain);
- free(q);
- }
- }
- } else {
- if (strchr(newsmaster, '@') == NULL)
- Printf("Report problems to <%s@%s>\r\n",
- newsmaster, innconf->fromhost);
- else
- Printf("Report problems to <%s>\r\n",
- newsmaster);
- }
- Reply(".\r\n");
-}
-
-
-/*
-** Unimplemented catch-all.
-*/
-/* ARGSUSED0 */
-void
-CMD_unimp(ac, av)
- int ac UNUSED;
- char *av[];
-{
- if (strcasecmp(av[0], "slave") == 0)
- /* Somebody sends us this? I don't believe it! */
- Reply("%d Unsupported\r\n", NNTP_SLAVEOK_VAL);
- else
- Reply("%d %s not implemented; try help\r\n",
- NNTP_BAD_COMMAND_VAL, av[0]);
-}
-
-
-#ifndef INADDR_LOOPBACK
-#define INADDR_LOOPBACK 0x7f000001
-#endif /* INADDR_LOOPBACK */
-/*
-** Convert an IP address to a hostname. Don't trust the reverse lookup,
-** since anyone can fake .in-addr.arpa entries.
-*/
-static bool
-Address2Name(INADDR *ap, char *hostname, int i)
-{
- char *p;
- struct hostent *hp;
- static char mismatch_error[] = "reverse lookup validation failed";
- char **pp;
-
- /* Get the official hostname, store it away. */
- if ((hp = gethostbyaddr((char *)ap, sizeof *ap, AF_INET)) == NULL) {
- HostErrorStr = hstrerror(h_errno);
- return false;
- }
- strlcpy(hostname, hp->h_name, i);
-
- /* Get addresses for this host. */
- if ((hp = gethostbyname(hostname)) == NULL) {
- HostErrorStr = hstrerror(h_errno);
- return false;
- }
-
- /* Make sure one of those addresses is the address we got. */
- for (pp = hp->h_addr_list; *pp; pp++)
- if (strncmp((const char *)&ap->s_addr, *pp, hp->h_length) == 0)
- break;
- if (*pp == NULL)
- {
- HostErrorStr = mismatch_error;
- return false;
- }
-
- /* Only needed for misconfigured YP/NIS systems. */
- if (ap->s_addr != INADDR_LOOPBACK && strchr(hostname, '.') == NULL
- && (p = innconf->domain) != NULL) {
- strlcat(hostname, ".", i);
- strlcat(hostname, p, i);
- }
-
- /* Make all lowercase, for wildmat. */
- for (p = hostname; *p; p++)
- if (CTYPE(isupper, (int)*p))
- *p = tolower(*p);
- return true;
-}
-
-/*
-** Convert an IPv6 address to a hostname. Don't trust the reverse lookup,
-** since anyone can fake .ip6.arpa entries.
-*/
-#ifdef HAVE_INET6
-static bool
-Address2Name6(struct sockaddr *sa, char *hostname, int i)
-{
- static char mismatch_error[] = "reverse lookup validation failed";
- int ret;
- bool valid = 0;
- struct addrinfo hints, *res, *res0;
- char *p;
-
- /* Get the official hostname, store it away. */
- ret = getnameinfo( sa, SA_LEN( sa ), hostname, i, NULL, 0, NI_NAMEREQD );
- if( ret != 0 )
- {
- HostErrorStr = gai_strerror( ret );
- return false;
- }
-
- /* Get addresses for this host. */
- memset( &hints, 0, sizeof( hints ) );
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_family = AF_INET6;
- if( ( ret = getaddrinfo( hostname, NULL, &hints, &res0 ) ) != 0 )
- {
- HostErrorStr = gai_strerror( ret );
- return false;
- }
-
- /* Make sure one of those addresses is the address we got. */
- for( res = res0; res; res = res->ai_next )
- {
-#ifdef HAVE_BROKEN_IN6_ARE_ADDR_EQUAL
- if( ! memcmp( &(((struct sockaddr_in6 *)sa)->sin6_addr),
- &(((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr),
- sizeof( struct in6_addr ) ) )
-#else
- if( IN6_ARE_ADDR_EQUAL( &(((struct sockaddr_in6 *)sa)->sin6_addr),
- &(((struct sockaddr_in6 *)(res->ai_addr))->sin6_addr) ) )
-#endif
- {
- valid = 1;
- break;
- }
- }
-
- freeaddrinfo( res0 );
-
- if (valid) {
- /* Make all lowercase for matching. */
- for (p = hostname; *p != '\0'; p++)
- if (CTYPE(isupper, *p))
- *p = tolower(*p);
- return true;
- } else {
- HostErrorStr = mismatch_error;
- return false;
- }
-}
-#endif
-
-
-static bool
-Sock2String( struct sockaddr *sa, char *string, int len, bool lookup )
-{
- struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
-
-#ifdef HAVE_INET6
- struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
- struct sockaddr_in temp;
-
- if( sa->sa_family == AF_INET6 )
- {
- if( ! IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) )
- {
- if( lookup )
- {
- return Address2Name6(sa, string, len);
- } else {
- strlcpy( string, sprint_sockaddr( sa ), len );
- return true;
- }
- } else {
- temp.sin_family = AF_INET;
- memcpy( &temp.sin_addr, sin6->sin6_addr.s6_addr + 12, 4 );
- temp.sin_port = sin6->sin6_port;
- sin4 = &temp;
- /* fall through to AF_INET case */
- }
- }
-#endif
- if( lookup ) {
- return Address2Name(&sin4->sin_addr, string, len);
- } else {
- strlcpy( string, inet_ntoa(sin4->sin_addr), len );
- return true;
- }
-}
-
-/*
-** Determine access rights of the client.
-*/
-static void StartConnection(void)
-{
- struct sockaddr_storage ssc, sss;
- socklen_t length;
- const char *default_host_error = "unknown error";
-
- ClientIpAddr = 0L;
- ClientHost[0] = '\0';
- ClientIpString[0] = '\0';
- ClientPort = 0;
- ServerHost[0] = '\0';
- ServerIpString[0] = '\0';
- ServerPort = 0;
-
- /* Get the peer's name. */
- length = sizeof ssc;
- if (getpeername(STDIN_FILENO, (struct sockaddr *)&ssc, &length) < 0) {
- if (!isatty(STDIN_FILENO)) {
- syslog(L_TRACE, "%s cant getpeername %m", "?");
- /* so stats generation looks correct. */
- strlcpy(ClientHost, "?", sizeof(ClientHost));
- Printf("%d I can't get your name. Goodbye.\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
- }
- strlcpy(ClientHost, "stdin", sizeof(ClientHost));
- }
-
- else {
-#ifdef HAVE_INET6
- if ( ssc.ss_family != AF_INET && ssc.ss_family != AF_INET6) {
-#else
- if ( ssc.ss_family != AF_INET ) {
-#endif
- syslog(L_ERROR, "%s bad_address_family %ld",
- "?", (long)ssc.ss_family);
- Printf("%d Bad address family. Goodbye.\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
- }
-
- length = sizeof sss;
- if (getsockname(STDIN_FILENO, (struct sockaddr *)&sss, &length) < 0) {
- syslog(L_NOTICE, "%s can't getsockname %m", ClientHost);
- Printf("%d Can't figure out where you connected to. Goodbye\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
- }
-
- /* figure out client's IP address/hostname */
- HostErrorStr = default_host_error;
- if( ! Sock2String( (struct sockaddr *)&ssc, ClientIpString,
- sizeof( ClientIpString ), false ) ) {
- syslog(L_NOTICE, "? cant get client numeric address: %s", HostErrorStr);
- ExitWithStats(1, true);
- }
- if(GetHostByAddr) {
- HostErrorStr = default_host_error;
- if( ! Sock2String( (struct sockaddr *)&ssc, ClientHost,
- sizeof( ClientHost ), true ) ) {
- syslog(L_NOTICE,
- "? reverse lookup for %s failed: %s -- using IP address for access",
- ClientIpString, HostErrorStr);
- strlcpy(ClientHost, ClientIpString, sizeof(ClientHost));
- }
- } else {
- strlcpy(ClientHost, ClientIpString, sizeof(ClientHost));
- }
-
- /* figure out server's IP address/hostname */
- HostErrorStr = default_host_error;
- if( ! Sock2String( (struct sockaddr *)&sss, ServerIpString,
- sizeof( ServerIpString ), false ) ) {
- syslog(L_NOTICE, "? cant get server numeric address: %s", HostErrorStr);
- ExitWithStats(1, true);
- }
- if(GetHostByAddr) {
- HostErrorStr = default_host_error;
- if( ! Sock2String( (struct sockaddr *)&sss, ServerHost,
- sizeof( ServerHost ), true ) ) {
- syslog(L_NOTICE,
- "? reverse lookup for %s failed: %s -- using IP address for access",
- ServerIpString, HostErrorStr);
- strlcpy(ServerHost, ServerIpString, sizeof(ServerHost));
- }
- } else {
- strlcpy(ServerHost, ServerIpString, sizeof(ServerHost));
- }
-
- /* get port numbers */
- switch( ssc.ss_family ) {
- case AF_INET:
- ClientPort = ntohs( ((struct sockaddr_in *)&ssc)->sin_port );
- ServerPort = ntohs( ((struct sockaddr_in *)&sss)->sin_port );
- break;
-#ifdef HAVE_INET6
- case AF_INET6:
- ClientPort = ntohs( ((struct sockaddr_in6 *)&ssc)->sin6_port );
- ServerPort = ntohs( ((struct sockaddr_in6 *)&sss)->sin6_port );
- break;
-#endif
- }
- }
-
- strlcpy(LogName, ClientHost, sizeof(LogName));
-
- syslog(L_NOTICE, "%s (%s) connect", ClientHost, ClientIpString);
-
- PERMgetaccess(NNRPACCESS);
- PERMgetpermissions();
-}
-
-
-/*
-** Send a reply, possibly with debugging output.
-*/
-void
-Reply(const char *fmt, ...)
-{
- va_list args;
- int oerrno;
- char * p;
- char buff[2048];
-
-#ifdef HAVE_SSL
- if (tls_conn) {
- int r;
-
- va_start(args, fmt);
- vsnprintf(buff, sizeof(buff), fmt, args);
- va_end(args);
- TMRstart(TMR_NNTPWRITE);
-Again:
- r = SSL_write(tls_conn, buff, strlen(buff));
- switch (SSL_get_error(tls_conn, r)) {
- case SSL_ERROR_NONE:
- case SSL_ERROR_SYSCALL:
- break;
- case SSL_ERROR_WANT_WRITE:
- goto Again;
- break;
- case SSL_ERROR_SSL:
- SSL_shutdown(tls_conn);
- tls_conn = NULL;
- errno = ECONNRESET;
- break;
- case SSL_ERROR_ZERO_RETURN:
- break;
- }
- TMRstop(TMR_NNTPWRITE);
- } else {
- va_start(args, fmt);
- TMRstart(TMR_NNTPWRITE);
- vprintf(fmt, args);
- TMRstop(TMR_NNTPWRITE);
- va_end(args);
- }
-#else
- va_start(args, fmt);
- TMRstart(TMR_NNTPWRITE);
- vprintf(fmt, args);
- TMRstop(TMR_NNTPWRITE);
- va_end(args);
-#endif
- if (Tracing) {
- oerrno = errno;
- va_start(args, fmt);
-
- /* Copy output, but strip trailing CR-LF. Note we're assuming here
- that no output line can ever be longer than 2045 characters. */
- vsnprintf(buff, sizeof(buff), fmt, args);
- va_end(args);
- p = buff + strlen(buff) - 1;
- while (p >= buff && (*p == '\n' || *p == '\r'))
- *p-- = '\0';
- syslog(L_TRACE, "%s > %s", ClientHost, buff);
-
- errno = oerrno;
- }
-}
-
-void
-Printf(const char *fmt, ...)
-{
- va_list args;
-
-#ifdef HAVE_SSL
- if (tls_conn) {
- int r;
- char buff[2048];
-
- va_start(args, fmt);
- vsnprintf(buff, sizeof(buff), fmt, args);
- va_end(args);
- TMRstart(TMR_NNTPWRITE);
-Again:
- r = SSL_write(tls_conn, buff, strlen(buff));
- switch (SSL_get_error(tls_conn, r)) {
- case SSL_ERROR_NONE:
- case SSL_ERROR_SYSCALL:
- break;
- case SSL_ERROR_WANT_WRITE:
- goto Again;
- break;
- case SSL_ERROR_SSL:
- SSL_shutdown(tls_conn);
- tls_conn = NULL;
- errno = ECONNRESET;
- break;
- case SSL_ERROR_ZERO_RETURN:
- break;
- }
- TMRstop(TMR_NNTPWRITE);
- } else {
-#endif /* HAVE_SSL */
- va_start(args, fmt);
- TMRstart(TMR_NNTPWRITE);
- vprintf(fmt, args);
- TMRstop(TMR_NNTPWRITE);
- va_end(args);
-#ifdef HAVE_SSL
- }
-#endif /* HAVE_SSL */
-}
-
-
-#ifdef HAVE_SIGACTION
-#define NO_SIGACTION_UNUSED UNUSED
-#else
-#define NO_SIGACTION_UNUSED
-#endif
-/*
-** Got a signal; toggle tracing.
-*/
-static RETSIGTYPE
-ToggleTrace(int s NO_SIGACTION_UNUSED)
-{
- ChangeTrace = true;
-#ifndef HAVE_SIGACTION
- xsignal(s, ToggleTrace);
-#endif
-}
-
-/*
-** Got a SIGPIPE; exit cleanly
-*/
-static RETSIGTYPE
-CatchPipe(int s UNUSED)
-{
- ExitWithStats(0, false);
-}
-
-/*
-** Got a signal; wait for children.
-*/
-static RETSIGTYPE
-WaitChild(int s NO_SIGACTION_UNUSED)
-{
- int pid;
-
- for (;;) {
- pid = waitpid(-1, NULL, WNOHANG);
- if (pid <= 0)
- break;
- }
-#ifndef HAVE_SIGACTION
- xsignal(s, WaitChild);
-#endif
-}
-
-static void SetupDaemon(void) {
- bool val;
-
- val = true;
- if (SMsetup(SM_PREOPEN, (void *)&val) && !SMinit()) {
- syslog(L_NOTICE, "cant initialize storage method, %s", SMerrorstr);
- Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
- ExitWithStats(1, true);
- }
- OVextra = overview_extra_fields();
- if (OVextra == NULL) {
- /* overview_extra_fields should already have logged something
- * useful */
- Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
- ExitWithStats(1, true);
- }
- overhdr_xref = overview_index("Xref", OVextra);
- if (!OVopen(OV_READ)) {
- /* This shouldn't really happen. */
- syslog(L_NOTICE, "cant open overview %m");
- Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
- ExitWithStats(1, true);
- }
- if (!OVctl(OVCACHEKEEP, &val)) {
- syslog(L_NOTICE, "cant enable overview cache %m");
- Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
- ExitWithStats(1, true);
- }
-}
-
-/*
-** Print a usage message and exit.
-*/
-static void
-Usage(void)
-{
- fprintf(stderr, "Usage error.\n");
- exit(1);
-}
-
-
-/* ARGSUSED0 */
-int
-main(int argc, char *argv[])
-{
- const char *name;
- CMDENT *cp;
- char buff[NNTP_STRLEN];
- char **av;
- int ac;
- READTYPE r;
- int i;
- char *Reject;
- int timeout;
- unsigned int vid=0;
- int count=123456789;
- struct timeval tv;
- unsigned short ListenPort = NNTP_PORT;
-#ifdef HAVE_INET6
- char ListenAddr[INET6_ADDRSTRLEN];
-#else
- char ListenAddr[16];
-#endif
- int lfd, fd;
- socklen_t clen;
-#ifdef HAVE_INET6
- struct sockaddr_storage ssa, csa;
- struct sockaddr_in6 *ssa6 = (struct sockaddr_in6 *) &ssa;
-#else
- struct sockaddr_in ssa, csa;
-#endif
- struct sockaddr_in *ssa4 = (struct sockaddr_in *) &ssa;
- struct stat Sb;
- pid_t pid = -1;
- gid_t NewsGID;
- uid_t NewsUID;
- int one = 1;
- FILE *pidfile;
- struct passwd *pwd;
- int clienttimeout;
- char *ConfFile = NULL;
- char *path;
-#if HAVE_GETSPNAM
- struct group *grp;
- gid_t shadowgid;
-#endif /* HAVE_GETSPNAM */
-
- int respawn = 0;
-
- setproctitle_init(argc, argv);
-
- /* Parse arguments. Must xstrdup() optarg if used because setproctitle may
- clobber it! */
- Reject = NULL;
- LLOGenable = false;
- GRPcur = NULL;
- MaxBytesPerSecond = 0;
- strlcpy(Username, "unknown", sizeof(Username));
-
- /* Set up the pathname, first thing, and teach our error handlers about
- the name of the program. */
- name = argv[0];
- if (name == NULL || *name == '\0')
- name = "nnrpd";
- else {
- const char *p;
-
- p = strrchr(name, '/');
- if (p != NULL)
- name = p + 1;
- }
- message_program_name = xstrdup(name);
- openlog(message_program_name, L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
- message_handlers_die(1, message_log_syslog_crit);
- message_handlers_warn(1, message_log_syslog_warning);
- message_handlers_notice(1, message_log_syslog_notice);
-
- if (!innconf_read(NULL))
- exit(1);
-
-#ifdef HAVE_SSL
- while ((i = getopt(argc, argv, "c:b:Dfi:I:g:nop:P:r:s:tS")) != EOF)
-#else
- while ((i = getopt(argc, argv, "c:b:Dfi:I:g:nop:P:r:s:t")) != EOF)
-#endif /* HAVE_SSL */
- switch (i) {
- default:
- Usage();
- /* NOTREACHED */
- case 'c': /* use alternate readers.conf */
- ConfFile = concatpath(innconf->pathetc, optarg);
- break;
- case 'b': /* bind to a certain address in
- daemon mode */
- strlcpy(ListenAddr, optarg, sizeof(ListenAddr));
- break;
- case 'D': /* standalone daemon mode */
- DaemonMode = true;
- break;
- case 'P': /* prespawn count in daemon mode */
- respawn = atoi(optarg);
- break;
- case 'f': /* Don't fork on daemon mode */
- ForeGroundMode = true;
- break;
-#if HAVE_GETSPNAM
- case 'g':
- ShadowGroup = optarg;
- break;
-#endif /* HAVE_GETSPNAM */
- case 'i': /* Initial command */
- PushedBack = xstrdup(optarg);
- break;
- case 'I': /* Instance */
- NNRPinstance = xstrdup(optarg);
- break;
- case 'n': /* No DNS lookups */
- GetHostByAddr = false;
- break;
- case 'o':
- Offlinepost = true; /* Offline posting only */
- break;
- case 'p': /* tcp port for daemon mode */
- ListenPort = atoi(optarg);
- break;
- case 'r': /* Reject connection message */
- Reject = xstrdup(optarg);
- break;
- case 's': /* Unused title string */
- break;
- case 't': /* Tracing */
- Tracing = true;
- break;
-#ifdef HAVE_SSL
- case 'S': /* SSL negotiation as soon as connected */
- initialSSL = true;
- break;
-#endif /* HAVE_SSL */
- }
- argc -= optind;
- if (argc)
- Usage();
-
- /*
- * Make other processes happier if someone is reading
- * This allows other processes like 'overchan' to keep up when
- * there are lots of readers. Note that this is cumulative with
- * 'nicekids'
- */
- if (innconf->nicennrpd > 0)
- nice(innconf->nicennrpd);
-
- HISTORY = concatpath(innconf->pathdb, _PATH_HISTORY);
- ACTIVE = concatpath(innconf->pathdb, _PATH_ACTIVE);
- ACTIVETIMES = concatpath(innconf->pathdb, _PATH_ACTIVETIMES);
- NEWSGROUPS = concatpath(innconf->pathdb, _PATH_NEWSGROUPS);
- if(ConfFile)
- NNRPACCESS = ConfFile;
- else
- NNRPACCESS = concatpath(innconf->pathetc,_PATH_NNRPACCESS);
- SPOOLlen = strlen(innconf->patharticles);
-
- if (DaemonMode) {
-#ifdef HAVE_INET6
- memset(&ssa, '\0', sizeof(struct sockaddr_in6));
- ssa6->sin6_family = AF_INET6;
- ssa6->sin6_port = htons(ListenPort);
- if (inet_pton(AF_INET6, ListenAddr, ssa6->sin6_addr.s6_addr) > 0) {
- if ( (lfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {
- syslog(L_FATAL, "can't open socket (%m)");
- exit(1);
- }
- }
- else {
-#endif
- memset(&ssa, '\0', sizeof(struct sockaddr_in));
- ssa4->sin_family = AF_INET;
- ssa4->sin_port = htons(ListenPort);
- if (inet_aton(ListenAddr, &ssa4->sin_addr) <= 0 )
- ssa4->sin_addr.s_addr = htonl(INADDR_ANY);
- if ( (lfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
- syslog(L_FATAL, "can't open socket (%m)");
- exit(1);
- }
-#ifdef HAVE_INET6
- }
-#endif
-
- if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
- (char *)&one, sizeof(one)) < 0) {
- syslog(L_FATAL, "can't setsockopt(SO_REUSEADDR) (%m)");
- exit(1);
- }
-
- if (bind(lfd, (struct sockaddr *) &ssa, sizeof(ssa)) < 0) {
- fprintf(stderr, "%s: can't bind (%s)\n", argv[0], strerror(errno));
- syslog(L_FATAL, "can't bind local address (%m)");
- exit(1);
- }
-
- /* If started as root, switch to news uid */
- if (getuid() == 0) {
- if (stat(innconf->pathrun, &Sb) < 0 || !S_ISDIR(Sb.st_mode)) {
- syslog(L_FATAL, "nnrpd cant stat %s %m", innconf->pathrun);
- exit(1);
- }
- if (Sb.st_uid == 0) {
- syslog(L_FATAL, "nnrpd %s must not be owned by root", innconf->pathrun);
- exit(1);
- }
- pwd = getpwnam(NEWSUSER);
- if (pwd == (struct passwd *)NULL) {
- syslog(L_FATAL, "nnrpd getpwnam(%s): %s", NEWSUSER, strerror(errno));
- exit(1);
- } else if (pwd->pw_gid != Sb.st_gid) {
- syslog(L_FATAL, "nnrpd %s must have group %s", innconf->pathrun, NEWSGRP);
- exit(1);
- } else if (pwd->pw_uid != Sb.st_uid) {
- syslog(L_FATAL, "nnrpd %s must be owned by %s", innconf->pathrun, NEWSUSER);
- exit(1);
- }
-
-#if HAVE_GETSPNAM
- shadowgid = (gid_t) -1;
- /* Find shadowgroup gid if needed */
- if (ShadowGroup != NULL) {
- if ((grp = getgrnam(ShadowGroup)) == NULL)
- syslog(L_ERROR, "nnrpd cannot find group %s",
- ShadowGroup);
- else
- shadowgid = grp->gr_gid;
- } else if ((grp = getgrnam("shadow")) != NULL) {
- /* found default group "shadow" */
- shadowgid = grp->gr_gid;
- ShadowGroup = "shadow";
- }
- /* If we have a shadowgid, try to set it as an extra group. */
- if (shadowgid != (gid_t) -1) {
- if (setgroups(1, &shadowgid) < 0)
- syslog(L_ERROR, "nnrpd cannot set supplementary group %s %m",
- ShadowGroup);
- else
- syslog(L_NOTICE, "nnrpd added supplementary group %s",
- ShadowGroup);
- }
-#endif /* HAVE_GETSPNAM */
-
- NewsUID = Sb.st_uid;
- NewsGID = Sb.st_gid;
- setgid(NewsGID);
- if (getgid() != NewsGID)
- syslog(L_ERROR, "nnrpd cant setgid to %d %m", NewsGID);
- setuid(NewsUID);
- if (getuid() != NewsUID)
- syslog(L_ERROR, "nnrpd cant setuid to %d %m", NewsUID);
- }
-
- /* Detach */
- if (!ForeGroundMode) {
- daemonize("/");
- }
-
- if (ListenPort == NNTP_PORT)
- strlcpy(buff, "nnrpd.pid", sizeof(buff));
- else
- snprintf(buff, sizeof(buff), "nnrpd-%d.pid", ListenPort);
- path = concatpath(innconf->pathrun, buff);
- pidfile = fopen(path, "w");
- free(path);
- if (pidfile == NULL) {
- syslog(L_ERROR, "cannot write %s %m", buff);
- exit(1);
- }
- fprintf(pidfile,"%lu\n", (unsigned long) getpid());
- fclose(pidfile);
-
- /* Set signal handle to care for dead children */
- if (!respawn)
- xsignal(SIGCHLD, WaitChild);
-
- /* Arrange to toggle tracing. */
- xsignal(SIGHUP, ToggleTrace);
-
- setproctitle("accepting connections");
-
- listen(lfd, 128);
-
- if (respawn) {
- /* pre-forked mode */
- for (;;) {
- if (respawn > 0) {
- --respawn;
- pid = fork();
- if (pid == 0) {
- do {
- clen = sizeof(csa);
- fd = accept(lfd, (struct sockaddr *) &csa, &clen);
- } while (fd < 0);
- break;
- }
- }
- for (;;) {
- if (respawn == 0)
- pid = wait(NULL);
- else
- pid = waitpid(-1, NULL, WNOHANG);
- if (pid <= 0)
- break;
- ++respawn;
- }
- }
- } else {
- /* fork on demand */
- do {
- clen = sizeof(csa);
- fd = accept(lfd, (struct sockaddr *) &csa, &clen);
- if (fd < 0)
- continue;
-
- for (i = 0; i <= innconf->maxforks && (pid = fork()) < 0; i++) {
- if (i == innconf->maxforks) {
- syslog(L_FATAL, "cant fork (dropping connection): %m");
- continue;
- }
- syslog(L_NOTICE, "cant fork (waiting): %m");
- sleep(1);
- }
- if (ChangeTrace) {
- Tracing = Tracing ? false : true;
- syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
- ChangeTrace = false;
- }
- if (pid != 0)
- close(fd);
- } while (pid != 0);
- }
-
- /* child process starts here */
- setproctitle("connected");
- close(lfd);
- dup2(fd, 0);
- close(fd);
- dup2(0, 1);
- dup2(0, 2);
- if (innconf->timer != 0)
- TMRinit(TMR_MAX);
- STATstart = TMRnow_double();
- SetupDaemon();
-
- /* if we are a daemon innd didn't make us nice, so be nice kids */
- if (innconf->nicekids) {
- if (nice(innconf->nicekids) < 0)
- syslog(L_ERROR, "Could not nice child to %ld: %m", innconf->nicekids);
- }
-
- /* Only automatically reap children in the listening process */
- xsignal(SIGCHLD, SIG_DFL);
-
- } else {
- if (innconf->timer)
- TMRinit(TMR_MAX);
- STATstart = TMRnow_double();
- SetupDaemon();
- /* Arrange to toggle tracing. */
- xsignal(SIGHUP, ToggleTrace);
- }/* DaemonMode */
-
-#ifdef HAVE_SSL
- ClientSSL = false;
- if (initialSSL) {
- tls_init();
- if (tls_start_servertls(0, 1) == -1) {
- Reply("%d SSL connection failed\r\n", NNTP_STARTTLS_BAD_VAL);
- ExitWithStats(1, false);
- }
- nnrpd_starttls_done = 1;
- ClientSSL = true;
- }
-#endif /* HAVE_SSL */
-
- /* If requested, check the load average. */
- if (innconf->nnrpdloadlimit > 0) {
- double load[1];
-
- if (getloadavg(load, 1) < 0)
- warn("cannot obtain system load");
- else {
- if ((int)(load[0] + 0.5) > innconf->nnrpdloadlimit) {
- syslog(L_NOTICE, "load %.2f > %ld", load[0], innconf->nnrpdloadlimit);
- Reply("%d load at %.2f, try later\r\n", NNTP_GOODBYE_VAL,
- load[0]);
- ExitWithStats(1, true);
- }
- }
- }
-
- strlcpy(LogName, "?", sizeof(LogName));
-
- /* Catch SIGPIPE so that we can exit out of long write loops */
- xsignal(SIGPIPE, CatchPipe);
-
- /* Get permissions and see if we can talk to this client */
- StartConnection();
- if (!PERMcanread && !PERMcanpost && !PERMneedauth) {
- syslog(L_NOTICE, "%s no_permission", ClientHost);
- Printf("%d You have no permission to talk. Goodbye.\r\n",
- NNTP_ACCESS_VAL);
- ExitWithStats(1, false);
- }
-
- /* Proceed with initialization. */
- setproctitle("%s connect", ClientHost);
-
- /* Were we told to reject connections? */
- if (Reject) {
- syslog(L_NOTICE, "%s rejected %s", ClientHost, Reject);
- Reply("%s %s\r\n", NNTP_GOODBYE, Reject);
- ExitWithStats(0, false);
- }
-
- if (PERMaccessconf) {
- if (PERMaccessconf->readertrack)
- PERMaccessconf->readertrack=TrackClient(ClientHost,Username);
- } else {
- if (innconf->readertrack)
- innconf->readertrack=TrackClient(ClientHost,Username);
- }
-
- if ((PERMaccessconf && PERMaccessconf->readertrack)
- || (!PERMaccessconf && innconf->readertrack)) {
- int len;
- syslog(L_NOTICE, "%s Tracking Enabled (%s)", ClientHost, Username);
- pid=getpid();
- gettimeofday(&tv,NULL);
- count += pid;
- vid = tv.tv_sec ^ tv.tv_usec ^ pid ^ count;
- len = strlen("innconf->pathlog") + strlen("/tracklogs/log-") + BUFSIZ;
- LocalLogFileName = xmalloc(len);
- sprintf(LocalLogFileName, "%s/tracklogs/log-%d", innconf->pathlog, vid);
- if ((locallog = fopen(LocalLogFileName, "w")) == NULL) {
- LocalLogDirName = concatpath(innconf->pathlog, "tracklogs");
- MakeDirectory(LocalLogDirName, false);
- free(LocalLogDirName);
- }
- if (locallog == NULL && (locallog = fopen(LocalLogFileName, "w")) == NULL) {
- syslog(L_ERROR, "%s Local Logging failed (%s) %s: %m", ClientHost, Username, LocalLogFileName);
- } else {
- syslog(L_NOTICE, "%s Local Logging begins (%s) %s",ClientHost, Username, LocalLogFileName);
- fprintf(locallog, "%s Tracking Enabled (%s)\n", ClientHost, Username);
- fflush(locallog);
- LLOGenable = true;
- }
- }
-
- if (PERMaccessconf) {
- Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
- PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
- PERMaccessconf->pathhost, inn_version_string,
- PERMcanpost ? "posting ok" : "no posting");
- clienttimeout = PERMaccessconf->clienttimeout;
- } else {
- Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
- PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
- innconf->pathhost, inn_version_string,
- PERMcanpost ? "posting ok" : "no posting");
- clienttimeout = innconf->clienttimeout;
- }
-
- line_init(&NNTPline);
-
- /* Main dispatch loop. */
- for (timeout = innconf->initialtimeout, av = NULL, ac = 0; ;
- timeout = clienttimeout) {
- TMRstart(TMR_NNTPWRITE);
- fflush(stdout);
- TMRstop(TMR_NNTPWRITE);
- if (ChangeTrace) {
- Tracing = Tracing ? false : true;
- syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
- ChangeTrace = false;
- }
- if (PushedBack) {
- if (PushedBack[0] == '\0')
- continue;
- if (Tracing)
- syslog(L_TRACE, "%s < %s", ClientHost, PushedBack);
- ac = Argify(PushedBack, &av);
- r = RTok;
- }
- else {
- size_t len;
- const char *p;
-
- r = line_read(&NNTPline, timeout, &p, &len);
- switch (r) {
- default:
- syslog(L_ERROR, "%s internal %d in main", ClientHost, r);
- /* FALLTHROUGH */
- case RTtimeout:
- if (timeout < clienttimeout)
- syslog(L_NOTICE, "%s timeout short", ClientHost);
- else
- syslog(L_NOTICE, "%s timeout", ClientHost);
- ExitWithStats(1, false);
- break;
- case RTok:
- if (len < sizeof(buff)) {
- /* line_read guarantees null termination */
- memcpy(buff, p, len + 1);
- /* Do some input processing, check for blank line. */
- if (Tracing)
- syslog(L_TRACE, "%s < %s", ClientHost, buff);
- if (buff[0] == '\0')
- continue;
- ac = Argify(buff, &av);
- break;
- }
- /* FALLTHROUGH */
- case RTlong:
- Reply("%d Line too long\r\n", NNTP_BAD_COMMAND_VAL);
- continue;
- case RTeof:
- /* Handled below. */
- break;
- }
- }
- /* Client gone? */
- if (r == RTeof)
- break;
- if (ac == 0 || strcasecmp(av[0], "quit") == 0)
- break;
-
- /* Find command. */
- for (cp = CMDtable; cp->Name; cp++)
- if (strcasecmp(cp->Name, av[0]) == 0)
- break;
- if (cp->Name == NULL) {
- if ((int)strlen(buff) > 40)
- syslog(L_NOTICE, "%s unrecognized %.40s...", ClientHost, buff);
- else
- syslog(L_NOTICE, "%s unrecognized %s", ClientHost, buff);
- Reply("%d What?\r\n", NNTP_BAD_COMMAND_VAL);
- continue;
- }
-
- /* Check usage. */
- if ((cp->Minac != CMDany && ac < cp->Minac)
- || (cp->Maxac != CMDany && ac > cp->Maxac)) {
- Reply("%d %s\r\n",
- NNTP_SYNTAX_VAL, cp->Help ? cp->Help : "Usage error");
- continue;
- }
-
- /* Check permissions and dispatch. */
- if (cp->Needauth && PERMneedauth) {
- Reply("%d Authentication required for command\r\n",
- NNTP_AUTH_NEEDED_VAL);
- continue;
- }
- setproctitle("%s %s", ClientHost, av[0]);
- (*cp->Function)(ac, av);
- if (PushedBack)
- break;
- if (PERMaccessconf)
- clienttimeout = PERMaccessconf->clienttimeout;
- else
- clienttimeout = innconf->clienttimeout;
- }
-
- Reply("%s\r\n", NNTP_GOODBYE_ACK);
-
- ExitWithStats(0, false);
- /* NOTREACHED */
- return 1;
-}