1 /* This file is part of GNU Pies.
2 Copyright (C) 2009-2020 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "pies.h"
18 #include "prog.h"
19 #include <netdb.h>
20
21 #define INTBUFSIZE 8192
22
23 /* Echo protocol, RFC 862 */
24 static void
echo_stream(int fd,struct component const * comp)25 echo_stream (int fd, struct component const *comp)
26 {
27 int rc;
28 char buffer[INTBUFSIZE];
29
30 while ((rc = read (fd, buffer, sizeof buffer)) > 0
31 && write (fd, buffer, rc) > 0)
32 ;
33 }
34
35 static void
echo_dg(int fd,struct component const * comp)36 echo_dg (int fd, struct component const *comp)
37 {
38 int rc;
39 char buffer[INTBUFSIZE];
40 struct sockaddr sa;
41 socklen_t size = sizeof sa;
42
43 rc = recvfrom (fd, buffer, sizeof buffer, 0, &sa, &size);
44 if (rc < 0)
45 return;
46 sendto (fd, buffer, rc, 0, &sa, sizeof sa);
47 }
48
49 /* Discard protocol, RFC 863 */
50 static void
discard_stream(int fd,struct component const * comp)51 discard_stream (int fd, struct component const *comp)
52 {
53 int rc;
54 char buffer[INTBUFSIZE];
55
56 while (1)
57 {
58 while ((rc = read (fd, buffer, sizeof buffer)) > 0)
59 ;
60 if (rc == 0 || errno != EINTR)
61 break;
62 }
63 }
64
65 static void
discard_dg(int fd,struct component const * comp)66 discard_dg (int fd, struct component const *comp)
67 {
68 char buffer[INTBUFSIZE];
69 read (fd, buffer, sizeof buffer);
70 }
71
72
73 /* Time Protocol, RFC 868 */
74
75 /* Return a machine readable date and time as seconds since
76 midnight, Jan 1, 1900. */
77
78 #define SEVENTY_YEARS ((unsigned long)25567 * 24 * 60 * 60)
79
80 static unsigned long
time_since_1900(void)81 time_since_1900 (void)
82 {
83 struct timeval tv;
84
85 if (gettimeofday (&tv, NULL) < 0)
86 {
87 logmsg (LOG_ERR, "gettimeofday: %s", strerror (errno));
88 return 0;
89 }
90 return htonl ((long) (tv.tv_sec + SEVENTY_YEARS));
91 }
92
93 static void
time_stream(int fd,struct component const * comp)94 time_stream (int fd, struct component const *comp)
95 {
96 unsigned long result = time_since_1900 ();
97 if (write (fd, (char *) &result, sizeof result) == -1)
98 logmsg (LOG_ERR, "write: %s", strerror (errno));
99 }
100
101 static void
time_dg(int fd,struct component const * comp)102 time_dg (int fd, struct component const *comp)
103 {
104 unsigned long result;
105 struct sockaddr sa;
106 socklen_t size = sizeof sa;
107
108 if (recvfrom (fd, (char *) &result, sizeof result, 0, &sa, &size) < 0)
109 return;
110 result = time_since_1900 ();
111 sendto (fd, (char *) &result, sizeof result, 0, &sa, sizeof sa);
112 }
113
114 /* Daytime Protocol, RFC 867 */
115 static void
daytime_stream(int fd,struct component const * comp)116 daytime_stream (int fd, struct component const *comp)
117 {
118 char buffer[27];
119 time_t now;
120
121 time (&now);
122 sprintf (buffer, "%.24s\r\n", ctime (&now));
123 if (write (fd, buffer, strlen (buffer)) == -1)
124 logmsg (LOG_ERR, "write: %s", strerror (errno));
125 }
126
127 static void
daytime_dg(int fd,struct component const * comp)128 daytime_dg (int fd, struct component const *comp)
129 {
130 char buffer[27];
131 time_t now;
132 struct sockaddr sa;
133 socklen_t size = sizeof sa;
134
135 time (&now);
136
137 if (recvfrom (fd, buffer, sizeof buffer, 0, &sa, &size) < 0)
138 return;
139 sprintf (buffer, "%.24s\r\n", ctime (&now));
140 sendto (fd, buffer, strlen (buffer), 0, &sa, sizeof sa);
141 }
142
143 /* Character Generator Protocol, RFC 864 */
144
145 #define LINESIZ 72
146
147 static void
chargen_next_line(char * text)148 chargen_next_line (char *text)
149 {
150 static int ch = 0;
151 int i, c;
152
153 do
154 ch = (ch + 1) % 128;
155 while (!c_isprint (ch));
156
157 for (i = 0, c = ch; i < LINESIZ; )
158 {
159 if (c_isprint (c))
160 text[i++] = c;
161 c = (c + 1) % 128;
162 }
163 }
164
165 static void
chargen_stream(int fd,struct component const * comp)166 chargen_stream (int fd, struct component const *comp)
167 {
168 char text[LINESIZ + 2];
169
170 text[LINESIZ] = '\r';
171 text[LINESIZ + 1] = '\n';
172
173 while (1)
174 {
175 chargen_next_line (text);
176 if (write (fd, text, sizeof text) != sizeof text)
177 break;
178 }
179 }
180
181 static void
chargen_dg(int fd,struct component const * comp)182 chargen_dg (int fd, struct component const *comp)
183 {
184 struct sockaddr sa;
185 socklen_t size = sizeof sa;
186 char text[LINESIZ + 2];
187
188 if (recvfrom (fd, text, sizeof text, 0, &sa, &size) < 0)
189 return;
190 text[LINESIZ] = '\r';
191 text[LINESIZ + 1] = '\n';
192 chargen_next_line (text);
193 sendto (fd, text, sizeof text, 0, &sa, sizeof sa);
194 }
195
196
197 /* Quote of the Day Protocol, RFC 865 */
198
199 #define QOTD_MAX 512
200 #define QOTD_DEF "Quote of the Day\r\n"
201
202 static size_t
trnl(char * text,size_t size)203 trnl (char *text, size_t size)
204 {
205 size_t off = size;
206
207 for (; off > 0; off--)
208 if (text[off] == '\n')
209 {
210 if (text[off-1] == '\r')
211 off--;
212 else
213 {
214 if (size == QOTD_MAX)
215 size--;
216 memmove (text + off + 1, text + off, size - off);
217 text[off] = '\r';
218 size++;
219 }
220 }
221 return size;
222 }
223
224 static size_t
qotd_read(char * text)225 qotd_read (char *text)
226 {
227 ssize_t rc;
228 int fd = open (qotdfile, O_RDONLY);
229 if (fd == -1)
230 {
231 logmsg (LOG_ERR, _("cannot open file %s: %s"), qotdfile,
232 strerror (errno));
233 strncpy (text, QOTD_DEF, QOTD_MAX);
234 rc = QOTD_MAX;
235 }
236 else
237 {
238 rc = read (fd, text, QOTD_MAX);
239 if (rc >= 0)
240 rc = trnl (text, rc);
241 else
242 {
243 logmsg (LOG_ERR, _("error reading %s: %s"),
244 qotdfile, strerror (errno));
245 strncpy (text, QOTD_DEF, QOTD_MAX);
246 rc = QOTD_MAX;
247 }
248 }
249 close (fd);
250 return rc;
251 }
252
253 static void
qotd_stream(int fd,struct component const * comp)254 qotd_stream (int fd, struct component const *comp)
255 {
256 char text[QOTD_MAX];
257 size_t len = qotd_read (text);
258 if (write (fd, text, len) == -1)
259 logmsg (LOG_ERR, "write: %s", strerror (errno));
260 }
261
262 static void
qotd_dg(int fd,struct component const * comp)263 qotd_dg (int fd, struct component const *comp)
264 {
265 char text[QOTD_MAX];
266 struct sockaddr sa;
267 socklen_t size = sizeof sa;
268 size_t len;
269 if (recvfrom (fd, text, sizeof text, 0, &sa, &size) < 0)
270 return;
271 len = qotd_read (text);
272 sendto (fd, text, len, 0, &sa, sizeof sa);
273 }
274
275
276 /* TCPMUX service, RFC 1078 */
277 #define MAX_SERV_LEN (256+2)
278
279 static int
fd_getline(int fd,char * buf,int len)280 fd_getline (int fd, char *buf, int len)
281 {
282 int count = 0, n;
283
284 do
285 {
286 n = read (fd, buf, len - count);
287 if (n == 0)
288 return count;
289 if (n < 0)
290 return -1;
291 while (--n >= 0)
292 {
293 if (*buf == '\r' || *buf == '\n' || *buf == '\0')
294 return count;
295 count++;
296 buf++;
297 }
298 }
299 while (count < len);
300 return count;
301 }
302
303 static int
tcpmux_help(struct component * comp,void * data)304 tcpmux_help (struct component *comp, void *data)
305 {
306 int *pfd = data;
307
308 if (ISCF_TCPMUX (comp->flags) && comp->prog && comp->prog->active)
309 {
310 fd_report (*pfd, comp->service);
311 fd_report (*pfd, "\r\n");
312 }
313 return 0;
314 }
315
316 static void
tcpmux(int fd,struct component const * comp)317 tcpmux (int fd, struct component const *comp)
318 {
319 char service[MAX_SERV_LEN + 1];
320 size_t len;
321 struct component *srv_comp;
322 union pies_sockaddr_storage sa;
323 socklen_t salen = sizeof (sa);
324 int rc;
325
326 /* Read service name */
327 if ((len = fd_getline (fd, service, MAX_SERV_LEN)) < 0)
328 {
329 fd_report (fd, "-Error reading service name\r\n");
330 return;
331 }
332 service[len] = 0;
333
334 debug (2, ("tcpmux: someone wants %s", service));
335
336 if (!strcasecmp (service, "help"))
337 {
338 component_foreach (tcpmux_help, &fd);
339 return;
340 }
341
342 srv_comp = progman_lookup_tcpmux (service, comp->tag);
343 if (!srv_comp)
344 {
345 fd_report (fd, "-Service not available\r\n");
346 return;
347 }
348
349 rc = getpeername (fd, (struct sockaddr *) &sa, &salen);
350 if (rc)
351 logmsg (LOG_ERR, _("%s: cannot get peer name: %s"),
352 comp->tag, strerror (errno));
353
354 if (comp->acl)
355 {
356 if (rc)
357 {
358 fd_report (fd, "-Service not available\r\n");
359 return;
360 }
361
362 if (check_acl (comp->acl, (struct sockaddr *) &sa, salen, NULL))
363 {
364 fd_report (fd, "-Service not available\r\n");
365 return;
366 }
367 }
368 /* FIXME: What about max-instances, etc.? */
369
370 if (srv_comp->flags & CF_TCPMUXPLUS)
371 fd_report (fd, "+Go\r\n");
372
373 progman_run_comp (srv_comp, fd, &sa, salen);
374 }
375
376
377 struct inetd_builtin inetd_builtin_tab[] = {
378 /* Echo received data */
379 {"echo", SOCK_STREAM, 0, 0, echo_stream},
380 {"echo", SOCK_DGRAM, 1, 0, echo_dg},
381 /* Internet /dev/null */
382 {"discard", SOCK_STREAM, 0, 0, discard_stream},
383 {"discard", SOCK_DGRAM, 1, 0, discard_dg},
384 /* Return 32 bit time since 1900 */
385 {"time", SOCK_STREAM, 1, 0, time_stream},
386 {"time", SOCK_DGRAM, 1, 0, time_dg},
387 /* Return human-readable time */
388 {"daytime", SOCK_STREAM, 1, 0, daytime_stream},
389 {"daytime", SOCK_DGRAM, 1, 0, daytime_dg},
390 /* Character generator */
391 {"chargen", SOCK_STREAM, 0, 0, chargen_stream},
392 {"chargen", SOCK_DGRAM, 1, 0, chargen_dg},
393 /* Quote of the Day */
394 {"qotd", SOCK_STREAM, 0, 0, qotd_stream},
395 {"qotd", SOCK_DGRAM, 1, 0, qotd_dg},
396 /* TCPMUX */
397 {"tcpmux", SOCK_STREAM, 0, 0, tcpmux},
398 {NULL, 0, 0, 0, NULL}
399 };
400
401 struct inetd_builtin *
inetd_builtin_lookup(const char * service,int socktype)402 inetd_builtin_lookup (const char *service, int socktype)
403 {
404 struct inetd_builtin *bp;
405
406 for (bp = inetd_builtin_tab; bp->service; bp++)
407 if (bp->socktype == socktype && strcmp (bp->service, service) == 0)
408 return bp;
409 return NULL;
410 }
411