/* This file is part of GNU Pies.
Copyright (C) 2009-2010, 2013, 2017 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 "prog.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 file %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 (ISCF_TCPMUX (comp->flags) && comp->prog && comp->prog->active)
{
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"))
{
component_foreach (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;
}