/***************************************************************************** * * Purpose ...............: MBSE NNTP Server * ***************************************************************************** * Copyright (C) 1997-2011 * * Michiel Broek FIDO: 2:280/2802 * Beekmansbos 10 * 1971 BV IJmuiden * the Netherlands * * This file is part of MBSE BBS. * * This BBS is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * MBSE BBS is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MBSE BBS; see the file COPYING. If not, write to the Free * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *****************************************************************************/ #include "../config.h" #include "../lib/mbselib.h" #include "../lib/users.h" #include "../lib/mbsedb.h" #include "../lib/msg.h" #include "openport.h" #include "ttyio.h" #include "auth.h" #include "commands.h" #include "mbnntp.h" time_t t_start; time_t t_end; char *envptr = NULL; struct sockaddr_in peeraddr4; struct sockaddr_in6 peeraddr6; pid_t mypid; unsigned int rcvdbytes = 0L; unsigned int sentbytes = 0L; int do_mailout = FALSE; extern char *ttystat[]; extern int authorized; void die(int onsig) { signal(onsig, SIG_IGN); CloseDupes(); Msg_Close(); if (onsig) { if (onsig <= NSIG) WriteError("Terminated on signal %d (%s)", onsig, SigName[onsig]); else WriteError("Terminated with error %d", onsig); } if (do_mailout) CreateSema((char *)"mailout"); t_end = time(NULL); Syslog('+', "Send [%6lu] Received [%6lu]", sentbytes, rcvdbytes); Syslog(' ', "MBNNTP finished in %s", t_elapsed(t_start, t_end)); if (envptr) free(envptr); ExitClient(onsig); msleep(1); /* For the linker only */ } #ifndef USE_NEWSGATE /* * Check if the system is available. */ int check_free(void); int check_free(void) { char buf[128]; strcpy(buf, SockR("SBBS:0;")); if (strncmp(buf, "100:2,1", 7) == 0) { Syslog('+', "The system is closed"); return FALSE; } return TRUE; } #endif #ifdef HAVE_GEOIP_H extern void _GeoIP_setup_dbfilename(void); void geoiplookup(GeoIP* gi, char *hostname, int i) { const char * country_code; const char * country_name; const char * country_continent; int country_id; if (GEOIP_COUNTRY_EDITION == i) { country_id = GeoIP_id_by_name(gi, hostname); country_code = GeoIP_country_code[country_id]; country_name = GeoIP_country_name[country_id]; country_continent = GeoIP_country_continent[country_id]; if (country_code == NULL) { Syslog('+', "%s: IP Address not found\n", GeoIPDBDescription[i]); } else { Syslog('+', "GeoIP location: %s, %s %s\n", country_name, country_code, country_continent); } } if (GEOIP_COUNTRY_EDITION_V6 == i) { country_id = GeoIP_id_by_name_v6(gi, hostname); country_code = GeoIP_country_code[country_id]; country_name = GeoIP_country_name[country_id]; country_continent = GeoIP_country_continent[country_id]; if (country_code == NULL) { Syslog('+', "%s: IP Address not found\n", GeoIPDBDescription[i]); } else { Syslog('+', "GeoIP location: %s, %s %s\n", country_name, country_code, country_continent); } } } #endif int main(int argc, char *argv[]) { struct passwd *pw; int i, rc; socklen_t addrlen = sizeof(struct sockaddr_in6); char str[INET6_ADDRSTRLEN]; #ifdef HAVE_GEOIP_H GeoIP *gi; #endif /* * The next trick is to supply a fake environment variable * MBSE_ROOT because this program is started from inetd. * This will setup the variable so InitConfig() will work. * The /etc/passwd must point to the correct homedirectory. */ pw = getpwuid(geteuid()); if (getenv("MBSE_ROOT") == NULL) { envptr = xstrcpy((char *)"MBSE_ROOT="); envptr = xstrcat(envptr, pw->pw_dir); putenv(envptr); } mypid = getpid(); /* * Read the global configuration data, registrate connection */ InitConfig(); InitMsgs(); InitUser(); InitFidonet(); InitNode(); umask(002); memset(&usrconfig, 0, sizeof(usrconfig)); t_start = time(NULL); InitClient(pw->pw_name, (char *)"mbnntp", CFG.location, CFG.logfile, CFG.util_loglevel, CFG.error_log, CFG.mgrlog, CFG.debuglog); Syslog(' ', "MBNNTP v%s", VERSION); IsDoing("Loging in"); #ifdef USE_NEWSGATE WriteError("MBSEBBS is compiled for full newsgate, you cannot use mbnntp!"); #endif /* * Catch all the signals we can, and ignore the rest. */ for(i = 0; i < NSIG; i++) { if ((i == SIGINT) || (i == SIGBUS) || (i == SIGILL) || (i == SIGSEGV) || (i == SIGTERM) || (i == SIGIOT)) signal(i, (void (*))die); else if (i == SIGCHLD) signal(i, SIG_DFL); else if ((i != SIGKILL) && (i != SIGSTOP)) signal(i, SIG_IGN); } if ((rc = rawport()) != 0) WriteError("Unable to set raw mode"); else { if (getpeername(0,(struct sockaddr*)&peeraddr6,&addrlen) == 0) { /* * Copy IPv4 part into the IPv6 structure. There has to be a better way * to deal with mixed incoming sockets ??? */ memcpy(&peeraddr4, &peeraddr6, sizeof(struct sockaddr_in)); if ((peeraddr6.sin6_family == AF_INET6) && (inet_ntop(AF_INET6, &peeraddr6.sin6_addr, str, sizeof(str)))) { Syslog('+', "Incoming IPv6 connection from %s", str); } else if ((peeraddr4.sin_family == AF_INET) && (inet_ntop(AF_INET, &peeraddr4.sin_addr, str, sizeof(str)))) { Syslog('+', "Incoming IPv4 connection from %s", str); } #ifdef HAVE_GEOIP_H _GeoIP_setup_dbfilename(); if (peeraddr6.sin6_family == AF_INET6) { if (GeoIP_db_avail(GEOIP_COUNTRY_EDITION_V6)) { if ((gi = GeoIP_open_type(GEOIP_COUNTRY_EDITION_V6, GEOIP_STANDARD)) != NULL) { geoiplookup(gi, str, GEOIP_COUNTRY_EDITION_V6); } GeoIP_delete(gi); } } else if (peeraddr6.sin6_family == AF_INET) { if (GeoIP_db_avail(GEOIP_COUNTRY_EDITION)) { if ((gi = GeoIP_open_type(GEOIP_COUNTRY_EDITION, GEOIP_STANDARD)) != NULL) { geoiplookup(gi, str, GEOIP_COUNTRY_EDITION); } GeoIP_delete(gi); } } #endif #ifdef USE_NEWSGATE send_nntp("400 Server closed"); #else if (! check_free()) { send_nntp("400 Server closed"); } else { send_nntp("200 MBNNTP v%s server ready -- posting allowed", VERSION); nntp(); } #endif } } cookedport(); die(0); return 0; } #ifndef USE_NEWSGATE /* * Get command from the client. * return < 0: error * return >= 0: size of buffer */ int get_nntp(char *buf, int max) { int c, len; len = 0; memset(buf, 0, sizeof(*buf)); while (TRUE) { c = tty_getc(180); if (c <= 0) { if (c == -2) { /* * Timeout */ send_nntp("400 Service discontinued, timeout"); } Syslog('+', "Receiver status %s", ttystat[- c]); return c; } if ((c == '\r') || (c == '\n')) { rcvdbytes += (len + 1); return len; } else { buf[len] = c; len++; buf[len] = '\0'; } if (len >= max) { WriteError("Input buffer full"); return len; } } return 0; /* Not reached */ } #endif void send_nntp(const char *format, ...) { char *out, p[4]; va_list va_ptr; out = calloc(4096, sizeof(char)); va_start(va_ptr, format); vsnprintf(out, 4096, format, va_ptr); va_end(va_ptr); /* * Only log responses */ if (out[3] == ' ') { memset(&p, 0, sizeof(p)); strncpy(p, out, 3); if (atoi(p) > 0) { Syslog('n', "> \"%s\"", printable(out, 0)); } } PUTSTR(out); PUTSTR((char *)"\r\n"); FLUSHOUT(); sentbytes += (strlen(out) + 2); free(out); } #ifndef USE_NEWSGATE void nntp(void) { char buf[4096]; int len; while (TRUE) { IsDoing("Waiting"); len = get_nntp(buf, sizeof(buf) -1); if (len < 0) return; if (len == 0) continue; if (strcasestr(buf, (char *)"AUTHINFO PASS") == NULL) { Syslog('n', "< \"%s\"", printable(buf, 0)); } else { Syslog('n', "< \"AUTHINFO PASS ********\""); } if (! check_free()) { send_nntp("400 server closed"); return; } /* * Process received command */ if (strncasecmp(buf, "QUIT", 4) == 0) { send_nntp("205 Goodbye\r\n"); return; } else if (strncasecmp(buf, "AUTHINFO USER", 13) == 0) { auth_user(buf); } else if (strncasecmp(buf, "AUTHINFO PASS", 13) == 0) { auth_pass(buf); } else if (strncasecmp(buf, "ARTICLE", 7) == 0) { if (check_auth(buf)) command_abhs(buf); } else if (strncasecmp(buf, "BODY", 4) == 0) { if (check_auth(buf)) command_abhs(buf); } else if (strncasecmp(buf, "LIST", 4) == 0) { if (check_auth(buf)) command_list(buf); } else if (strncasecmp(buf, "GROUP", 5) == 0) { if (check_auth(buf)) command_group(buf); } else if (strncasecmp(buf, "HEAD", 4) == 0) { if (check_auth(buf)) command_abhs(buf); } else if (strncasecmp(buf, "POST", 4) == 0) { if (check_auth(buf)) command_post(buf); } else if (strncasecmp(buf, "IHAVE", 5) == 0) { send_nntp("435 Article not wanted - do not send it"); } else if (strncasecmp(buf, "NEWGROUPS", 9) == 0) { send_nntp("235 Warning: NEWGROUPS not implemented, returning empty list"); send_nntp("."); } else if (strncasecmp(buf, "NEWNEWS", 7) == 0) { send_nntp("230 Warning: NEWNEWS not implemented, returning empty list"); send_nntp("."); } else if (strncasecmp(buf, "SLAVE", 5) == 0) { send_nntp("202 Slave status noted"); } else if (strncasecmp(buf, "STAT", 4) == 0) { if (check_auth(buf)) command_abhs(buf); } else if (strncasecmp(buf, "MODE READER", 11) == 0) { if (check_auth(buf)) { if (authorized) send_nntp("200 Server ready, posting allowed"); else send_nntp("201 Server ready, no posting allowed"); } } else if (strncasecmp(buf, "XOVER", 5) == 0) { if (check_auth(buf)) command_xover(buf); } else if (strncasecmp(buf, "HELP", 4) == 0) { send_nntp("100 Help text follows"); send_nntp("Recognized commands:"); send_nntp(""); send_nntp("ARTICLE"); send_nntp("AUTHINFO"); send_nntp("BODY"); send_nntp("GROUP"); send_nntp("HEAD"); send_nntp("IHAVE (not implemented, messages are always rejected)"); send_nntp("LIST"); send_nntp("NEWGROUPS (not implemented, always returns an empty list)"); send_nntp("NEWNEWS (not implemented, always returns an empty list)"); send_nntp("POST"); send_nntp("QUIT"); send_nntp("SLAVE (has no effect)"); send_nntp("STAT"); send_nntp("XOVER"); send_nntp(""); send_nntp("MBNNTP supports most of RFC-977 and also has support for AUTHINFO and"); send_nntp("limited XOVER support (RFC-2980)"); send_nntp("."); } else { send_nntp("500 Unknown command"); } } } #endif