source: code/icbd.c@ 3347cd9

Last change on this file since 3347cd9 was 3347cd9, checked in by Mike Belopuhov <mike@…>, 10 years ago

Make packet dump in debug mode more readable

  • Property mode set to 100644
File size: 13.0 KB
RevLine 
[cd7b81d]1/*
2 * Copyright (c) 2009 Mike Belopuhov
3 * Copyright (c) 2007 Oleg Safiullin
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/types.h>
19#include <sys/queue.h>
20#include <sys/socket.h>
21#include <sys/stat.h>
[c8c9ccf]22#include <netinet/in_systm.h>
[cd7b81d]23#include <netinet/in.h>
[c8c9ccf]24#include <netinet/ip.h>
[cd7b81d]25#include <arpa/inet.h>
26#include <fcntl.h>
[d27dc1e]27#include <limits.h>
28#include <signal.h>
[cd7b81d]29#include <stdio.h>
30#include <stdlib.h>
31#include <unistd.h>
32#include <string.h>
33#include <sysexits.h>
34#include <syslog.h>
35#include <pwd.h>
36#include <login_cap.h>
37#include <locale.h>
[3347cd9]38#include <ctype.h>
[cd7b81d]39#include <netdb.h>
40#include <event.h>
41#include <errno.h>
42#include <err.h>
[e87ab6d]43#include <resolv.h>
[cd7b81d]44
45#include "icb.h"
46#include "icbd.h"
47
[82d3c1f]48struct stat modtabst;
[d27dc1e]49char modtabpath[PATH_MAX];
[1d2125a]50char modtab[ICB_MTABLEN][ICB_MAXNICKLEN];
51int modtabcnt;
[d27dc1e]52char srvname[NI_MAXHOST];
[a785c27]53int creategroups;
54int foreground;
[d27dc1e]55char logprefix[PATH_MAX/2];
[460786f]56int dodns = 1;
[3dba97d]57int dologging;
[a785c27]58int verbose;
[cd7b81d]59
60void usage(void);
61void getpeerinfo(struct icb_session *);
62void icbd_accept(int, short, void *);
[6e89d69]63void icbd_paused(int, short, void *);
[cd7b81d]64void icbd_drop(struct icb_session *, char *);
65void icbd_ioerr(struct bufferevent *, short, void *);
66void icbd_dispatch(struct bufferevent *, void *);
67void icbd_log(struct icb_session *, int, const char *, ...);
68void icbd_restrict(void);
[7882a6f]69void icbd_send(struct icb_session *, char *, ssize_t);
[cd7b81d]70
[6e89d69]71struct icbd_listener {
72 struct event ev, pause;
73};
74
[cd7b81d]75int
76main(int argc, char *argv[])
77{
78 const char *cause = NULL;
79 int ch, nsocks = 0, save_errno = 0;
80 int inet4 = 0, inet6 = 0;
[677a45b]81 char group[ICB_MAXGRPLEN], *grplist = NULL;
82 char *ptr = NULL;
[cd7b81d]83
84 /* init group lists before calling icb_addgroup */
[7882a6f]85 icb_init();
[cd7b81d]86
[460786f]87 while ((ch = getopt(argc, argv, "46CdG:M:nL:S:v")) != -1)
[cd7b81d]88 switch (ch) {
89 case '4':
90 inet4++;
91 break;
92 case '6':
93 inet6++;
94 break;
95 case 'C':
96 creategroups++;
97 break;
98 case 'd':
99 foreground++;
100 break;
101 case 'G':
[677a45b]102 grplist = optarg;
[cd7b81d]103 break;
[3dba97d]104 case 'L':
105 strlcpy(logprefix, optarg, sizeof logprefix);
106 dologging++;
107 break;
[1d2125a]108 case 'M':
[270fd23]109 strlcpy(modtabpath, optarg, sizeof modtabpath);
[1d2125a]110 break;
[460786f]111 case 'n':
112 dodns = 0;
113 break;
[a785c27]114 case 'S':
115 strlcpy(srvname, optarg, sizeof srvname);
116 break;
[cd7b81d]117 case 'v':
118 verbose++;
119 break;
120 default:
121 usage();
122 /* NOTREACHED */
123 }
124 argc -= optind;
125 argv += optind;
126
127 /* add group "1" as it's a login group for most of the clients */
[677a45b]128 if (icb_addgroup(NULL, "1") == NULL)
[cd7b81d]129 err(EX_UNAVAILABLE, NULL);
130
[677a45b]131 if (grplist) {
132 while (icb_token(grplist, strlen(grplist), &ptr, group,
[176b6ef]133 ICB_MAXGRPLEN, ',', 0) > 0)
[677a45b]134 if (icb_addgroup(NULL, group) == NULL)
135 err(EX_UNAVAILABLE, NULL);
136 }
137
[cd7b81d]138 if (argc == 0)
139 argc++;
140
141 if (inet4 && inet6)
[2e37e9f]142 errx(EX_USAGE, "Can't specify both -4 and -6");
[cd7b81d]143
144 tzset();
145 (void)setlocale(LC_ALL, "C");
146
147 if (foreground)
148 openlog("icbd", LOG_PID | LOG_PERROR, LOG_DAEMON);
149 else
150 openlog("icbd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
151
152 if (!foreground && daemon(0, 0) < 0)
153 err(EX_OSERR, NULL);
154
155 (void)event_init();
156
157 for (ch = 0; ch < argc; ch++) {
158 struct addrinfo hints, *res, *res0;
[6e89d69]159 struct icbd_listener *l;
[cd7b81d]160 char *addr, *port;
161 int error, s, on = 1;
162
163 addr = port = NULL;
164 if (argv[ch] != NULL) {
165 if (argv[ch][0] != ':')
166 addr = argv[ch];
167 if ((port = strrchr(argv[ch], ':')) != NULL)
168 *port++ = '\0';
169 }
170
171 bzero(&hints, sizeof hints);
172 if (inet4 || inet6)
173 hints.ai_family = inet4 ? PF_INET : PF_INET6;
174 else
175 hints.ai_family = PF_UNSPEC;
176 hints.ai_socktype = SOCK_STREAM;
177 hints.ai_flags = AI_PASSIVE;
178 if ((error = getaddrinfo(addr, port ? port : "icb", &hints,
179 &res0)) != 0) {
180 syslog(LOG_ERR, "%s", gai_strerror(error));
181 return (EX_UNAVAILABLE);
182 }
183
184 for (res = res0; res != NULL; res = res->ai_next) {
185 if ((s = socket(res->ai_family, res->ai_socktype,
186 res->ai_protocol)) < 0) {
187 cause = "socket";
188 save_errno = errno;
189 continue;
190 }
191
192 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on,
193 sizeof on) < 0) {
194 cause = "SO_REUSEADDR";
195 save_errno = errno;
196 (void)close(s);
197 continue;
198 }
199
200 if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
201 cause = "bind";
202 save_errno = errno;
203 (void)close(s);
204 continue;
205 }
206
207 (void)listen(s, TCP_BACKLOG);
208
[6e89d69]209 if ((l = calloc(1, sizeof *l)) == NULL)
[cd7b81d]210 err(EX_UNAVAILABLE, NULL);
[bacf9da]211 event_set(&l->ev, s, EV_READ | EV_PERSIST,
212 icbd_accept, l);
[6e89d69]213 if (event_add(&l->ev, NULL) < 0) {
[cd7b81d]214 syslog(LOG_ERR, "event_add: %m");
215 return (EX_UNAVAILABLE);
216 }
[6e89d69]217 evtimer_set(&l->pause, icbd_paused, l);
[cd7b81d]218
219 nsocks++;
220 }
221
222 freeaddrinfo(res0);
223 }
224
225 if (nsocks == 0) {
226 errno = save_errno;
227 syslog(LOG_ERR, "%s: %m", cause);
228 return (EX_UNAVAILABLE);
229 }
230
[55923b7]231 /* start the logger service */
232 logger_init();
233
[e87ab6d]234 /* initialize resolver */
235 res_init();
[cd7b81d]236
237 if (!foreground)
238 icbd_restrict();
239
[82d3c1f]240 icbd_modupdate();
241
[cd7b81d]242 (void)signal(SIGPIPE, SIG_IGN);
243
244 (void)event_dispatch();
245
246 syslog(LOG_ERR, "event_dispatch: %m");
247
248 return (EX_UNAVAILABLE);
249}
250
251void
252icbd_accept(int fd, short event __attribute__((__unused__)),
[6e89d69]253 void *arg)
[cd7b81d]254{
[6e89d69]255 struct icbd_listener *l = arg;
[cd7b81d]256 struct sockaddr_storage ss;
[6e89d69]257 struct timeval p = { 1, 0 };
[cd7b81d]258 struct icb_session *is;
259 socklen_t ss_len = sizeof ss;
[8ef8c4e]260 int s, on = 1, tos = IPTOS_LOWDELAY;
[cd7b81d]261
262 ss.ss_len = ss_len;
[6e89d69]263 s = accept(fd, (struct sockaddr *)&ss, &ss_len);
264 if (s == -1) {
265 switch (errno) {
266 case EINTR:
267 case EWOULDBLOCK:
268 case ECONNABORTED:
269 return;
270 case EMFILE:
271 case ENFILE:
272 event_del(&l->ev);
273 evtimer_add(&l->pause, &p);
274 return;
275 default:
276 syslog(LOG_ERR, "accept: %m");
277 return;
278 }
[cd7b81d]279 }
[6e89d69]280
[c8c9ccf]281 if (ss.ss_family == AF_INET)
282 if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof tos) < 0)
283 syslog(LOG_WARNING, "IP_TOS: %m");
[8ef8c4e]284 if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof on) < 0)
285 syslog(LOG_WARNING, "SO_KEEPALIVE: %m");
[cd7b81d]286 if ((is = calloc(1, sizeof *is)) == NULL) {
287 syslog(LOG_ERR, "calloc: %m");
288 (void)close(s);
289 return;
290 }
291 if ((is->bev = bufferevent_new(s, icbd_dispatch, NULL, icbd_ioerr,
292 is)) == NULL) {
293 syslog(LOG_ERR, "bufferevent_new: %m");
294 (void)close(s);
295 free(is);
296 return;
297 }
298 if (bufferevent_enable(is->bev, EV_READ)) {
299 syslog(LOG_ERR, "bufferevent_enable: %m");
300 (void)close(s);
301 bufferevent_free(is->bev);
302 free(is);
303 return;
304 }
305
306 /* save host information */
307 getpeerinfo(is);
308
309 /* start icb conversation */
310 icb_start(is);
311}
312
[6e89d69]313void
314icbd_paused(int fd __attribute__((__unused__)),
315 short events __attribute__((__unused__)), void *arg)
316{
317 struct icbd_listener *l = arg;
318 event_add(&l->ev, NULL);
319}
320
[cd7b81d]321__dead void
322usage(void)
323{
[3dba97d]324 extern char *__progname;
325
[cd7b81d]326 (void)fprintf(stderr, "usage: %s [-46Cdv] [-G group1[,group2,...]] "
[3dba97d]327 "[-L prefix] [-M modtab]\n\t[-S name] [[addr][:port] ...]\n",
328 __progname);
[cd7b81d]329 exit(EX_USAGE);
330}
331
332/*
333 * bufferevent functions
334 */
[e13307d]335void
336icbd_ioerr(struct bufferevent *bev __attribute__((__unused__)), short what,
337 void *arg)
338{
339 struct icb_session *is = (struct icb_session *)arg;
340
341 if (what & EVBUFFER_TIMEOUT)
342 icbd_drop(is, "timeout");
343 else if (what & EVBUFFER_EOF)
344 icbd_drop(is, NULL);
345 else if (what & EVBUFFER_ERROR)
346 icbd_drop(is, (what & EVBUFFER_READ) ? "read error" :
347 "write error");
348}
[cd7b81d]349
350void
351icbd_dispatch(struct bufferevent *bev, void *arg)
352{
353 struct icb_session *is = (struct icb_session *)arg;
[a2fadb4]354 unsigned char length;
[de86230]355 size_t res;
[cd7b81d]356
[c9402c3]357 while (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) > 0) {
[cd7b81d]358 if (is->length == 0) {
[c9402c3]359 /* read length */
[a2fadb4]360 bufferevent_read(bev, &length, 1);
361 if (length == 0) {
362 /*
363 * An extension has been proposed:
364 * if length is 0, the packet is part of an
365 * "extended packet". The packet should be
366 * treated as if length was 255 and the next
367 * packet received from the sender should be
368 * appended to this packet.
369 *
370 * This server doesn't support this yet.
371 */
[c9402c3]372 icbd_drop(is, "invalid packet");
373 return;
374 }
[a2fadb4]375 is->length = (size_t)length;
376 is->rlen = 0;
[cd7b81d]377 }
[c9402c3]378 /* read as much as we can */
[de86230]379 res = bufferevent_read(bev, &is->buffer[is->rlen],
380 is->length - is->rlen);
381 is->rlen += res;
[cd7b81d]382#ifdef DEBUG
[c9402c3]383 {
384 int i;
385
386 printf("-> read %lu out of %lu from %s:%d:\n",
387 is->rlen, is->length, is->host, is->port);
388 for (i = 0; i < (int)is->rlen; i++)
[3347cd9]389 printf(isprint(is->buffer[i]) ? "%c" :
390 "\\x%02x", (unsigned char)is->buffer[i]);
[c9402c3]391 printf("\n");
392 }
[cd7b81d]393#endif
[c9402c3]394 /* see you next time around */
395 if (is->rlen < is->length)
396 return;
[a2fadb4]397 /* null-terminate the data */
398 is->buffer[MIN(is->rlen, ICB_MSGSIZE - 1)] = '\0';
[c9402c3]399 /* process the message in full */
[d45051e]400 if (icb_input(is))
401 return;
[863edf1]402 /* cleanup the input buffer */
403 memset(is->buffer, 0, ICB_MSGSIZE);
[c9402c3]404 is->rlen = is->length = 0;
405 }
[cd7b81d]406}
407
408void
[7882a6f]409icbd_send(struct icb_session *is, char *buf, ssize_t size)
[cd7b81d]410{
411 if (bufferevent_write(is->bev, buf, size) == -1)
412 syslog(LOG_ERR, "bufferevent_write: %m");
413#ifdef DEBUG
414 {
415 int i;
416
[c9402c3]417 printf("-> wrote %lu to %s:%d:\n", size, is->host, is->port);
[cd7b81d]418 for (i = 0; i < size; i++)
[3347cd9]419 printf(isprint(buf[i]) ? "%c" : "\\x%02x",
420 (unsigned char)buf[i]);
[cd7b81d]421 printf("\n");
422 }
423#endif
424}
425
426void
427icbd_drop(struct icb_session *is, char *reason)
428{
429 if (reason) {
430 icb_remove(is, reason);
431 icbd_log(is, LOG_DEBUG, reason);
432 } else
433 icb_remove(is, NULL);
[863edf1]434
435 /* cleanup the input buffer */
436 memset(is->buffer, 0, ICB_MSGSIZE);
437 is->rlen = is->length = 0;
438
[cd7b81d]439 (void)close(EVBUFFER_FD(is->bev));
440 bufferevent_free(is->bev);
[120eedd]441 if (!ISSETF(is->flags, ICB_SF_DNSINPROGRESS))
442 free(is);
443 else
444 SETF(is->flags, ICB_SF_PENDINGDROP);
[cd7b81d]445}
446
447void
448icbd_log(struct icb_session *is, int level, const char *fmt, ...)
449{
450 char buf[512];
451 va_list ap;
452
453 if (!verbose && level == LOG_DEBUG)
454 return;
455
456 va_start(ap, fmt);
457 (void)vsnprintf(buf, sizeof buf, fmt, ap);
458 va_end(ap);
459 if (is)
460 syslog(level, "%s:%u: %s", is->host, is->port, buf);
461 else
462 syslog(level, "%s", buf);
463}
464
465void
466icbd_restrict(void)
467{
468 struct stat sb;
469 struct passwd *pw;
470
471 if ((pw = getpwnam(ICBD_USER)) == NULL) {
472 syslog(LOG_ERR, "No passwd entry for %s", ICBD_USER);
473 exit(EX_NOUSER);
474 }
475
476 if (setusercontext(NULL, pw, pw->pw_uid,
[d554223]477 LOGIN_SETALL & ~LOGIN_SETUSER) < 0)
[cd7b81d]478 exit(EX_NOPERM);
479
480 if (stat(pw->pw_dir, &sb) == -1) {
481 syslog(LOG_ERR, "%s: %m", pw->pw_name);
482 exit(EX_NOPERM);
483 }
484
485 if (sb.st_uid != 0 || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
486 syslog(LOG_ERR, "bad directory permissions");
487 exit(EX_NOPERM);
488 }
489
490 if (chroot(pw->pw_dir) < 0) {
491 syslog(LOG_ERR, "%s: %m", pw->pw_dir);
492 exit(EX_UNAVAILABLE);
493 }
494
[cdd2ff5]495 if (chdir("/") < 0) {
[ad8b08d]496 syslog(LOG_ERR, "/" ICBD_HOME ": %m");
[cd7b81d]497 exit(EX_UNAVAILABLE);
498 }
499
[cdd2ff5]500 chdir(ICBD_HOME);
501
[e54f151]502 if (setuid(pw->pw_uid) < 0) {
503 syslog(LOG_ERR, "%d: %m", pw->pw_uid);
504 exit(EX_NOPERM);
505 }
506
[cd7b81d]507 (void)setproctitle("icbd");
508}
509
[1d2125a]510void
[82d3c1f]511icbd_modupdate(void)
[1d2125a]512{
[82d3c1f]513 struct stat st;
[1d2125a]514 FILE *fp;
515 char *buf, *lbuf;
516 size_t len;
517
[82d3c1f]518 if (strlen(modtabpath) == 0)
519 return;
520 if (stat(modtabpath, &st)) {
[709589d]521 syslog(LOG_ERR, "stat %s: %m", modtabpath);
[82d3c1f]522 return;
523 }
524 /* see if there are any changes */
525 if (timespeccmp(&st.st_mtim, &modtabst.st_mtim, ==) ||
526 st.st_size == 0)
527 return;
528
[709589d]529 if ((fp = fopen(modtabpath, "r")) == NULL) {
530 syslog(LOG_ERR, "open %s: %m", modtabpath);
531 return;
532 }
[1d2125a]533
[2cdab10]534 modtabcnt = 0;
[1d2125a]535 bzero(modtab, ICB_MTABLEN * ICB_MAXNICKLEN);
536 lbuf = NULL;
537 while ((buf = fgetln(fp, &len)) && modtabcnt < ICB_MTABLEN) {
538 if (buf[len - 1] == '\n')
539 buf[len - 1] = '\0';
540 else {
541 /* EOF without EOL, copy and add the NUL */
542 if ((lbuf = malloc(len + 1)) == NULL)
543 err(1, NULL);
544 memcpy(lbuf, buf, len);
545 lbuf[len] = '\0';
546 buf = lbuf;
547 }
548 while (buf[0] == ' ' || buf[0] == '\t')
549 buf++;
550 if (buf[0] == '#' || buf[0] == '\0')
551 continue;
552 strlcpy(modtab[modtabcnt++], buf, ICB_MAXNICKLEN);
553 }
554 free(lbuf);
555
556 qsort(modtab, modtabcnt, ICB_MAXNICKLEN,
557 (int (*)(const void *, const void *))strcmp);
558
559 fclose(fp);
[709589d]560
561 memcpy(&modtabst, &st, sizeof modtabst);
[1d2125a]562}
563
[cd7b81d]564time_t
565getmonotime(void)
566{
567 struct timespec ts;
568
569 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
570 syslog(LOG_ERR, "%m");
571 exit(EX_OSERR);
572 }
573 return (ts.tv_sec);
574}
575
576void
577getpeerinfo(struct icb_session *is)
578{
[e68221b]579 struct sockaddr_in *sin = (struct sockaddr_in *)&is->ss;
580 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&is->ss;
581 socklen_t ss_len = sizeof is->ss;
[cd7b81d]582
[e68221b]583 bzero(&is->ss, sizeof is->ss);
584 if (getpeername(EVBUFFER_FD(is->bev), (struct sockaddr *)&is->ss,
[cd7b81d]585 &ss_len) != 0)
586 return;
587
588 is->port = 0;
[e68221b]589 switch (is->ss.ss_family) {
[cd7b81d]590 case AF_INET:
[b4049f9]591 is->port = ntohs(sin->sin_port);
[cd7b81d]592 break;
593
594 case AF_INET6:
[b4049f9]595 is->port = ntohs(sin6->sin6_port);
[cd7b81d]596 break;
597 }
598
[e68221b]599 inet_ntop(is->ss.ss_family, is->ss.ss_family == AF_INET ?
[b4049f9]600 (void *)&sin->sin_addr : (void *)&sin6->sin6_addr,
601 is->host, sizeof is->host);
602
[e68221b]603 dns_resolve(is);
[cd7b81d]604}
Note: See TracBrowser for help on using the repository browser.