/* This file is part of GNU Pies. Copyright (C) 2009, 2010, 2013 Sergey Poznyakoff GNU Pies 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 3, or (at your option) any later version. GNU Pies 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 GNU Pies. If not, see . */ #include "pies.h" #include #define INTBUFSIZE 8192 /* Echo protocol, RFC 862 */ static void echo_stream (int fd, struct component const *comp) { int rc; char buffer[INTBUFSIZE]; while ((rc = read (fd, buffer, sizeof buffer)) > 0 && write (fd, buffer, rc) > 0) ; } static void echo_dg (int fd, struct component const *comp) { int rc; char buffer[INTBUFSIZE]; struct sockaddr sa; socklen_t size = sizeof sa; rc = recvfrom (fd, buffer, sizeof buffer, 0, &sa, &size); if (rc < 0) return; sendto (fd, buffer, rc, 0, &sa, sizeof sa); } /* Discard protocol, RFC 863 */ static void discard_stream (int fd, struct component const *comp) { int rc; char buffer[INTBUFSIZE]; while (1) { while ((rc = read (fd, buffer, sizeof buffer)) > 0) ; if (rc == 0 || errno != EINTR) break; } } static void discard_dg (int fd, struct component const *comp) { char buffer[INTBUFSIZE]; read (fd, buffer, sizeof buffer); } /* Time Protocol, RFC 868 */ /* Return a machine readable date and time as seconds since midnight, Jan 1, 1900. */ #define SEVENTY_YEARS ((unsigned long)25567 * 24 * 60 * 60) static unsigned long time_since_1900 (void) { struct timeval tv; if (gettimeofday (&tv, NULL) < 0) { logmsg (LOG_ERR, "gettimeofday: %s", strerror (errno)); return 0; } return htonl ((long) (tv.tv_sec + SEVENTY_YEARS)); } static void time_stream (int fd, struct component const *comp) { unsigned long result = time_since_1900 (); if (write (fd, (char *) &result, sizeof result) == -1) logmsg (LOG_ERR, "write: %s", strerror (errno)); } static void time_dg (int fd, struct component const *comp) { unsigned long result; struct sockaddr sa; socklen_t size = sizeof sa; if (recvfrom (fd, (char *) &result, sizeof result, 0, &sa, &size) < 0) return; result = time_since_1900 (); sendto (fd, (char *) &result, sizeof result, 0, &sa, sizeof sa); } /* Daytime Protocol, RFC 867 */ static void daytime_stream (int fd, struct component const *comp) { char buffer[27]; time_t now; time (&now); sprintf (buffer, "%.24s\r\n", ctime (&now)); if (write (fd, buffer, strlen (buffer)) == -1) logmsg (LOG_ERR, "write: %s", strerror (errno)); } static void daytime_dg (int fd, struct component const *comp) { char buffer[27]; time_t now; struct sockaddr sa; socklen_t size = sizeof sa; time (&now); if (recvfrom (fd, buffer, sizeof buffer, 0, &sa, &size) < 0) return; sprintf (buffer, "%.24s\r\n", ctime (&now)); sendto (fd, buffer, strlen (buffer), 0, &sa, sizeof sa); } /* Character Generator Protocol, RFC 864 */ #define LINESIZ 72 static void chargen_next_line (char *text) { static int ch = 0; int i, c; do ch = (ch + 1) % 128; while (!c_isprint (ch)); for (i = 0, c = ch; i < LINESIZ; ) { if (c_isprint (c)) text[i++] = c; c = (c + 1) % 128; } } static void chargen_stream (int fd, struct component const *comp) { char text[LINESIZ + 2]; text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; while (1) { chargen_next_line (text); if (write (fd, text, sizeof text) != sizeof text) break; } } static void chargen_dg (int fd, struct component const *comp) { struct sockaddr sa; socklen_t size = sizeof sa; char text[LINESIZ + 2]; if (recvfrom (fd, text, sizeof text, 0, &sa, &size) < 0) return; text[LINESIZ] = '\r'; text[LINESIZ + 1] = '\n'; chargen_next_line (text); sendto (fd, text, sizeof text, 0, &sa, sizeof sa); } /* Quote of the Day Protocol, RFC 865 */ #define QOTD_MAX 512 #define QOTD_DEF "Quote of the Day\r\n" static size_t trnl (char *text, size_t size) { size_t off = size; for (; off > 0; off--) if (text[off] == '\n') { if (text[off-1] == '\r') off--; else { if (size == QOTD_MAX) size--; memmove (text + off + 1, text + off, size - off); text[off] = '\r'; size++; } } return size; } static size_t qotd_read (char *text) { ssize_t rc; int fd = open (qotdfile, O_RDONLY); if (fd == -1) { logmsg (LOG_ERR, _("cannot open %s: %s"), qotdfile, strerror (errno)); strncpy (text, QOTD_DEF, QOTD_MAX); rc = QOTD_MAX; } else { rc = read (fd, text, QOTD_MAX); if (rc >= 0) rc = trnl (text, rc); else { logmsg (LOG_ERR, _("error reading %s: %s"), qotdfile, strerror (errno)); strncpy (text, QOTD_DEF, QOTD_MAX); rc = QOTD_MAX; } } close (fd); return rc; } static void qotd_stream (int fd, struct component const *comp) { char text[QOTD_MAX]; size_t len = qotd_read (text); if (write (fd, text, len) == -1) logmsg (LOG_ERR, "write: %s", strerror (errno)); } static void qotd_dg (int fd, struct component const *comp) { char text[QOTD_MAX]; struct sockaddr sa; socklen_t size = sizeof sa; size_t len; if (recvfrom (fd, text, sizeof text, 0, &sa, &size) < 0) return; len = qotd_read (text); sendto (fd, text, len, 0, &sa, sizeof sa); } /* TCPMUX service, RFC 1078 */ #define MAX_SERV_LEN (256+2) static int fd_getline (int fd, char *buf, int len) { int count = 0, n; do { n = read (fd, buf, len - count); if (n == 0) return count; if (n < 0) return -1; while (--n >= 0) { if (*buf == '\r' || *buf == '\n' || *buf == '\0') return count; count++; buf++; } } while (count < len); return count; } static int tcpmux_help (struct component *comp, void *data) { int *pfd = data; if (!(comp->flags & CF_DISABLED) && ISCF_TCPMUX (comp->flags)) { fd_report (*pfd, comp->service); fd_report (*pfd, "\r\n"); } return 0; } static void tcpmux (int fd, struct component const *comp) { char service[MAX_SERV_LEN + 1]; size_t len; struct component *srv_comp; union pies_sockaddr_storage sa; socklen_t salen = sizeof (sa); int rc; /* Read service name */ if ((len = fd_getline (fd, service, MAX_SERV_LEN)) < 0) { fd_report (fd, "-Error reading service name\r\n"); return; } service[len] = 0; debug (2, ("tcpmux: someone wants %s", service)); if (!strcasecmp (service, "help")) { progman_iterate_comp (tcpmux_help, &fd); return; } srv_comp = progman_lookup_tcpmux (service, comp->tag); if (!srv_comp) { fd_report (fd, "-Service not available\r\n"); return; } rc = getpeername (fd, (struct sockaddr *) &sa, &salen); if (rc) logmsg (LOG_ERR, _("%s: cannot get peer name: %s"), comp->tag, strerror (errno)); if (comp->acl) { if (rc) { fd_report (fd, "-Service not available\r\n"); return; } if (check_acl (comp->acl, (struct sockaddr *) &sa, salen, NULL)) { fd_report (fd, "-Service not available\r\n"); return; } } /* FIXME: What about max-instances, etc.? */ if (srv_comp->flags & CF_TCPMUXPLUS) fd_report (fd, "+Go\r\n"); progman_run_comp (srv_comp, fd, &sa, salen); } struct inetd_builtin inetd_builtin_tab[] = { /* Echo received data */ {"echo", SOCK_STREAM, 0, 0, echo_stream}, {"echo", SOCK_DGRAM, 1, 0, echo_dg}, /* Internet /dev/null */ {"discard", SOCK_STREAM, 0, 0, discard_stream}, {"discard", SOCK_DGRAM, 1, 0, discard_dg}, /* Return 32 bit time since 1900 */ {"time", SOCK_STREAM, 1, 0, time_stream}, {"time", SOCK_DGRAM, 1, 0, time_dg}, /* Return human-readable time */ {"daytime", SOCK_STREAM, 1, 0, daytime_stream}, {"daytime", SOCK_DGRAM, 1, 0, daytime_dg}, /* Character generator */ {"chargen", SOCK_STREAM, 0, 0, chargen_stream}, {"chargen", SOCK_DGRAM, 1, 0, chargen_dg}, /* Quote of the Day */ {"qotd", SOCK_STREAM, 0, 0, qotd_stream}, {"qotd", SOCK_DGRAM, 1, 0, qotd_dg}, /* TCPMUX */ {"tcpmux", SOCK_STREAM, 0, 0, tcpmux}, {NULL, 0, 0, 0, NULL} }; struct inetd_builtin * inetd_builtin_lookup (const char *service, int socktype) { struct inetd_builtin *bp; for (bp = inetd_builtin_tab; bp->service; bp++) if (bp->socktype == socktype && strcmp (bp->service, service) == 0) return bp; return NULL; }