source: code/icbd.c@ e2fcbc7

Last change on this file since e2fcbc7 was 1d2125a, checked in by Mike Belopuhov <mike@…>, 11 years ago

Add support for the moderator table that specifies (currently up
to 50) users that are allowed to become moderators.

  • Property mode set to 100644
File size: 11.3 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>
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <string.h>
31#include <sysexits.h>
32#include <syslog.h>
33#include <pwd.h>
34#include <login_cap.h>
35#include <locale.h>
36#include <netdb.h>
37#include <event.h>
38#include <errno.h>
39#include <err.h>
40
41#include "icb.h"
42#include "icbd.h"
43
44extern char *__progname;
45
[1d2125a]46char modtab[ICB_MTABLEN][ICB_MAXNICKLEN];
47int modtabcnt;
[a785c27]48char srvname[MAXHOSTNAMELEN];
49int creategroups;
50int foreground;
51int verbose;
[cd7b81d]52
53void usage(void);
54void getpeerinfo(struct icb_session *);
55void icbd_accept(int, short, void *);
56void icbd_drop(struct icb_session *, char *);
57void icbd_ioerr(struct bufferevent *, short, void *);
58void icbd_dispatch(struct bufferevent *, void *);
59void icbd_log(struct icb_session *, int, const char *, ...);
60void icbd_grplist(char *);
[1d2125a]61void icbd_modtab(char *);
[cd7b81d]62void icbd_restrict(void);
63void icbd_write(struct icb_session *, char *, ssize_t);
64
65int
66main(int argc, char *argv[])
67{
68 struct icbd_callbacks ic = { icbd_drop, icbd_log, icbd_write };
69 const char *cause = NULL;
70 int ch, nsocks = 0, save_errno = 0;
71 int inet4 = 0, inet6 = 0;
72
73 /* init group lists before calling icb_addgroup */
74 icb_init(&ic);
75
[1d2125a]76 while ((ch = getopt(argc, argv, "46CdG:M:S:v")) != -1)
[cd7b81d]77 switch (ch) {
78 case '4':
79 inet4++;
80 break;
81 case '6':
82 inet6++;
83 break;
84 case 'C':
85 creategroups++;
86 break;
87 case 'd':
88 foreground++;
89 break;
90 case 'G':
91 icbd_grplist(optarg);
92 break;
[1d2125a]93 case 'M':
94 icbd_modtab(optarg);
95 break;
[a785c27]96 case 'S':
97 strlcpy(srvname, optarg, sizeof srvname);
98 break;
[cd7b81d]99 case 'v':
100 verbose++;
101 break;
102 default:
103 usage();
104 /* NOTREACHED */
105 }
106 argc -= optind;
107 argv += optind;
108
109 /* add group "1" as it's a login group for most of the clients */
110 if (icb_addgroup(NULL, "1", NULL) == NULL)
111 err(EX_UNAVAILABLE, NULL);
112
113 if (argc == 0)
114 argc++;
115
116 if (inet4 && inet6)
[2e37e9f]117 errx(EX_USAGE, "Can't specify both -4 and -6");
[cd7b81d]118
119 tzset();
120 (void)setlocale(LC_ALL, "C");
121
122 if (foreground)
123 openlog("icbd", LOG_PID | LOG_PERROR, LOG_DAEMON);
124 else
125 openlog("icbd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
126
127 if (!foreground && daemon(0, 0) < 0)
128 err(EX_OSERR, NULL);
129
130 (void)event_init();
131
132 for (ch = 0; ch < argc; ch++) {
133 struct addrinfo hints, *res, *res0;
134 struct event *ev;
135 char *addr, *port;
136 int error, s, on = 1;
137
138 addr = port = NULL;
139 if (argv[ch] != NULL) {
140 if (argv[ch][0] != ':')
141 addr = argv[ch];
142 if ((port = strrchr(argv[ch], ':')) != NULL)
143 *port++ = '\0';
144 }
145
146 bzero(&hints, sizeof hints);
147 if (inet4 || inet6)
148 hints.ai_family = inet4 ? PF_INET : PF_INET6;
149 else
150 hints.ai_family = PF_UNSPEC;
151 hints.ai_socktype = SOCK_STREAM;
152 hints.ai_flags = AI_PASSIVE;
153 if ((error = getaddrinfo(addr, port ? port : "icb", &hints,
154 &res0)) != 0) {
155 syslog(LOG_ERR, "%s", gai_strerror(error));
156 return (EX_UNAVAILABLE);
157 }
158
159 for (res = res0; res != NULL; res = res->ai_next) {
160 if ((s = socket(res->ai_family, res->ai_socktype,
161 res->ai_protocol)) < 0) {
162 cause = "socket";
163 save_errno = errno;
164 continue;
165 }
166
167 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on,
168 sizeof on) < 0) {
169 cause = "SO_REUSEADDR";
170 save_errno = errno;
171 (void)close(s);
172 continue;
173 }
174
175 if (bind(s, res->ai_addr, res->ai_addrlen) < 0) {
176 cause = "bind";
177 save_errno = errno;
178 (void)close(s);
179 continue;
180 }
181
182 (void)listen(s, TCP_BACKLOG);
183
184 if ((ev = calloc(1, sizeof *ev)) == NULL)
185 err(EX_UNAVAILABLE, NULL);
186 event_set(ev, s, EV_READ | EV_PERSIST, icbd_accept, ev);
187 if (event_add(ev, NULL) < 0) {
188 syslog(LOG_ERR, "event_add: %m");
189 return (EX_UNAVAILABLE);
190 }
191
192 nsocks++;
193 }
194
195 freeaddrinfo(res0);
196 }
197
198 if (nsocks == 0) {
199 errno = save_errno;
200 syslog(LOG_ERR, "%s: %m", cause);
201 return (EX_UNAVAILABLE);
202 }
203
204 /* start a dns resolver thread */
205 icbd_dns_init();
206
207 if (!foreground)
208 icbd_restrict();
209
210 (void)signal(SIGPIPE, SIG_IGN);
211
212 (void)event_dispatch();
213
214 syslog(LOG_ERR, "event_dispatch: %m");
215
216 return (EX_UNAVAILABLE);
217}
218
219void
220icbd_dns(int fd, short event, void *arg)
221{
222 struct icb_session *is = arg;
223
224 if (event != EV_READ)
225 return;
226
227 if (read(fd, is->host, sizeof is->host) < 0)
228 syslog(LOG_ERR, "read: %m");
229
230 is->host[sizeof is->host - 1] = '\0';
231
232 if (verbose)
233 syslog(LOG_DEBUG, "icbd_dns: resolved %s", is->host);
234}
235
236void
237icbd_accept(int fd, short event __attribute__((__unused__)),
238 void *arg __attribute__((__unused__)))
239{
240 struct sockaddr_storage ss;
241 struct icb_session *is;
242 socklen_t ss_len = sizeof ss;
[c8c9ccf]243 int s, tos = IPTOS_LOWDELAY;
[cd7b81d]244
245 ss.ss_len = ss_len;
246 if ((s = accept(fd, (struct sockaddr *)&ss, &ss_len)) < 0) {
247 syslog(LOG_ERR, "accept: %m");
248 return;
249 }
[c8c9ccf]250 if (ss.ss_family == AF_INET)
251 if (setsockopt(s, IPPROTO_IP, IP_TOS, &tos, sizeof tos) < 0)
252 syslog(LOG_WARNING, "IP_TOS: %m");
[cd7b81d]253 if ((is = calloc(1, sizeof *is)) == NULL) {
254 syslog(LOG_ERR, "calloc: %m");
255 (void)close(s);
256 return;
257 }
258 if ((is->bev = bufferevent_new(s, icbd_dispatch, NULL, icbd_ioerr,
259 is)) == NULL) {
260 syslog(LOG_ERR, "bufferevent_new: %m");
261 (void)close(s);
262 free(is);
263 return;
264 }
265 if (bufferevent_enable(is->bev, EV_READ)) {
266 syslog(LOG_ERR, "bufferevent_enable: %m");
267 (void)close(s);
268 bufferevent_free(is->bev);
269 free(is);
270 return;
271 }
272
273 /* save host information */
274 getpeerinfo(is);
275
276 /* start icb conversation */
277 icb_start(is);
278}
279
280__dead void
281usage(void)
282{
283 (void)fprintf(stderr, "usage: %s [-46Cdv] [-G group1[,group2,...]] "
[1d2125a]284 "[-M modtab]\n\t[-S name] [[addr][:port] ...]\n", __progname);
[cd7b81d]285 exit(EX_USAGE);
286}
287
288/*
289 * bufferevent functions
290 */
291
292void
293icbd_dispatch(struct bufferevent *bev, void *arg)
294{
295 struct icb_session *is = (struct icb_session *)arg;
296
297 if (is->length == 0) {
298 bzero(is->buffer, sizeof is->buffer);
299 /* read length */
300 (void)bufferevent_read(bev, is->buffer, 1);
301 /* we're about to read the whole packet */
302 is->length = (size_t)(unsigned char)is->buffer[0];
303 if (is->length == 0) {
304 icbd_drop(is, "invalid packet");
305 return;
306 }
307 if (EVBUFFER_LENGTH(EVBUFFER_INPUT(bev)) < is->length) {
308 /* set watermark to the expected length */
309 bufferevent_setwatermark(bev, EV_READ, is->length, 0);
310 return;
311 }
312 }
313 (void)bufferevent_read(bev, &is->buffer[1], is->length);
314#ifdef DEBUG
315 {
316 int i;
317
318 printf("-> read from %s:%d:\n", is->host, is->port);
319 for (i = 0; i < (int)is->length + 1; i++)
320 printf(" %02x", (unsigned char)is->buffer[i]);
321 printf("\n");
322 }
323#endif
324 icb_input(is);
325 is->length = 0;
326}
327
328void
329icbd_write(struct icb_session *is, char *buf, ssize_t size)
330{
331 if (bufferevent_write(is->bev, buf, size) == -1)
332 syslog(LOG_ERR, "bufferevent_write: %m");
333#ifdef DEBUG
334 {
335 int i;
336
337 printf("-> wrote to %s:%d:\n", is->host, is->port);
338 for (i = 0; i < size; i++)
339 printf(" %02x", (unsigned char)buf[i]);
340 printf("\n");
341 }
342#endif
343}
344
345void
346icbd_drop(struct icb_session *is, char *reason)
347{
348 if (reason) {
349 icb_remove(is, reason);
350 icbd_log(is, LOG_DEBUG, reason);
351 } else
352 icb_remove(is, NULL);
353 (void)evbuffer_write(EVBUFFER_OUTPUT(is->bev), EVBUFFER_FD(is->bev));
354 (void)close(EVBUFFER_FD(is->bev));
355 bufferevent_free(is->bev);
356 free(is);
357}
358
359void
360icbd_ioerr(struct bufferevent *bev __attribute__((__unused__)), short what,
361 void *arg)
362{
363 struct icb_session *is = (struct icb_session *)arg;
364
365 if (what & EVBUFFER_TIMEOUT)
366 icbd_drop(is, "timeout");
367 else if (what & EVBUFFER_EOF)
368 icbd_drop(is, NULL);
369 else if (what & EVBUFFER_ERROR)
370 icbd_drop(is, (what & EVBUFFER_READ) ? "read error" :
371 "write error");
372}
373
374void
375icbd_log(struct icb_session *is, int level, const char *fmt, ...)
376{
377 char buf[512];
378 va_list ap;
379
380 if (!verbose && level == LOG_DEBUG)
381 return;
382
383 va_start(ap, fmt);
384 (void)vsnprintf(buf, sizeof buf, fmt, ap);
385 va_end(ap);
386 if (is)
387 syslog(level, "%s:%u: %s", is->host, is->port, buf);
388 else
389 syslog(level, "%s", buf);
390}
391
392void
393icbd_restrict(void)
394{
395 struct stat sb;
396 struct passwd *pw;
397
398 if ((pw = getpwnam(ICBD_USER)) == NULL) {
399 syslog(LOG_ERR, "No passwd entry for %s", ICBD_USER);
400 exit(EX_NOUSER);
401 }
402
403 if (setusercontext(NULL, pw, pw->pw_uid,
[d554223]404 LOGIN_SETALL & ~LOGIN_SETUSER) < 0)
[cd7b81d]405 exit(EX_NOPERM);
406
407 if (stat(pw->pw_dir, &sb) == -1) {
408 syslog(LOG_ERR, "%s: %m", pw->pw_name);
409 exit(EX_NOPERM);
410 }
411
412 if (sb.st_uid != 0 || (sb.st_mode & (S_IWGRP|S_IWOTH)) != 0) {
413 syslog(LOG_ERR, "bad directory permissions");
414 exit(EX_NOPERM);
415 }
416
417 if (chroot(pw->pw_dir) < 0) {
418 syslog(LOG_ERR, "%s: %m", pw->pw_dir);
419 exit(EX_UNAVAILABLE);
420 }
421
422 if (setuid(pw->pw_uid) < 0) {
423 syslog(LOG_ERR, "%d: %m", pw->pw_uid);
424 exit(EX_NOPERM);
425 }
426
427 if (chdir("/") < 0) {
428 syslog(LOG_ERR, "/: %m");
429 exit(EX_UNAVAILABLE);
430 }
431
432 (void)setproctitle("icbd");
433}
434
435void
436icbd_grplist(char *list)
437{
438 char *s, *s1, *s2;
439 int last = 0;
440
441 if (!list || strlen(list) == 0)
442 return;
443
444 /* "group1[:pass1][,group2[:pass2],...]" */
445 s = list;
446 s1 = s2 = NULL;
447 while (!last && s) {
448 if ((s1 = strchr(s, ',')) != NULL)
449 *s1 = '\0';
450 else {
451 last = 1;
452 s1 = s;
453 }
454 if ((s2 = strchr(s, ':')) != NULL)
455 *s2 = '\0';
456 if (icb_addgroup(NULL, s, s2 ? ++s2 : NULL) == NULL)
457 err(EX_UNAVAILABLE, NULL);
458 s = ++s1;
459 s1 = s2 = NULL;
460 }
461}
462
[1d2125a]463void
464icbd_modtab(char *mtab)
465{
466 FILE *fp;
467 char *buf, *lbuf;
468 size_t len;
469
470 if ((fp = fopen(mtab, "r")) == NULL)
471 err(EX_NOINPUT, "%s", mtab);
472
473 bzero(modtab, ICB_MTABLEN * ICB_MAXNICKLEN);
474 lbuf = NULL;
475 while ((buf = fgetln(fp, &len)) && modtabcnt < ICB_MTABLEN) {
476 if (buf[len - 1] == '\n')
477 buf[len - 1] = '\0';
478 else {
479 /* EOF without EOL, copy and add the NUL */
480 if ((lbuf = malloc(len + 1)) == NULL)
481 err(1, NULL);
482 memcpy(lbuf, buf, len);
483 lbuf[len] = '\0';
484 buf = lbuf;
485 }
486 while (buf[0] == ' ' || buf[0] == '\t')
487 buf++;
488 if (buf[0] == '#' || buf[0] == '\0')
489 continue;
490 strlcpy(modtab[modtabcnt++], buf, ICB_MAXNICKLEN);
491 }
492 free(lbuf);
493
494 qsort(modtab, modtabcnt, ICB_MAXNICKLEN,
495 (int (*)(const void *, const void *))strcmp);
496
497 fclose(fp);
498}
499
[cd7b81d]500time_t
501getmonotime(void)
502{
503 struct timespec ts;
504
505 if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
506 syslog(LOG_ERR, "%m");
507 exit(EX_OSERR);
508 }
509 return (ts.tv_sec);
510}
511
512void
513getpeerinfo(struct icb_session *is)
514{
515 struct sockaddr_storage ss;
516 socklen_t ss_len = sizeof ss;
517
518 bzero(&ss, sizeof ss);
519 if (getpeername(EVBUFFER_FD(is->bev), (struct sockaddr *)&ss,
520 &ss_len) != 0)
521 return;
522
523 is->port = 0;
524 switch (ss.ss_family) {
525 case AF_INET:
526 is->port = ntohs(((struct sockaddr_in *)&ss)->sin_port);
527 break;
528
529 case AF_INET6:
530 is->port = ntohs(((struct sockaddr_in6 *)&ss)->sin6_port);
531 break;
532 }
533
534 dns_rresolv(is, &ss);
535}
Note: See TracBrowser for help on using the repository browser.