]> jfr.im git - irc/quakenet/newserv.git/blob - nterface/nterfacer.c
r439@blue (orig r429): slug | 2006-02-23 13:39:37 +0000
[irc/quakenet/newserv.git] / nterface / nterfacer.c
1 /*
2 nterfacer
3 Copyright (C) 2004-2006 Chris Porter.
4
5 v1.07a
6 - dumb config bug
7 v1.07
8 - made sure buf[0] = '\0'
9 v1.06
10 - tidy up
11 v1.05
12 - added application level ping support
13 v1.04
14 - modified for new logging system
15 v1.03
16 - newserv seems to unload this module before the ones that depend on us,
17 so deregister_service now checks to see if it's been freed already
18 v1.02
19 - moronic bug in linked lists fixed
20 v1.01
21 - logging
22 */
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <stdarg.h>
27 #include <sys/poll.h>
28 #include <sys/types.h>
29 #include <sys/socket.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <netdb.h>
34 #include <string.h>
35 #include <strings.h>
36
37 #include "../lib/sstring.h"
38 #include "../lib/irc_string.h"
39 #include "../core/config.h"
40 #include "../core/events.h"
41 #include "../lib/version.h"
42
43 #include "nterfacer.h"
44 #include "logging.h"
45
46 MODULE_VERSION("$Id: nterfacer.c 786 2007-05-13 20:22:35Z newserv $")
47
48 struct service_node *tree = NULL;
49 struct esocket_events nterfacer_events;
50 struct esocket *nterfacer_sock;
51 struct rline *rlines = NULL;
52 unsigned short nterfacer_token = BLANK_TOKEN;
53 struct nterface_auto_log *nrl;
54
55 struct service_node *ping = NULL;
56
57 int ping_handler(struct rline *ri, int argc, char **argv);
58
59 void _init(void) {
60 int loaded;
61 int debug_mode = getcopyconfigitemintpositive("nterfacer", "debug", 0);
62
63 nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode);
64
65 loaded = load_permits();
66 if(!loaded) {
67 nterface_log(nrl, NL_ERROR, "No permits loaded successfully.");
68 return;
69 } else {
70 nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
71 }
72
73 nterfacer_events.on_accept = nterfacer_accept_event;
74 nterfacer_events.on_line = nterfacer_line_event;
75 nterfacer_events.on_disconnect = NULL;
76
77 nterfacer_token = esocket_token();
78
79 ping = register_service("nterfacer");
80 if(!ping) {
81 MemError();
82 } else {
83 register_handler(ping, "ping", 0, ping_handler);
84 }
85
86 accept_fd = setup_listening_socket();
87 if(accept_fd == -1) {
88 nterface_log(nrl, NL_ERROR, "Unable to setup listening socket!");
89 } else {
90 nterfacer_sock = esocket_add(accept_fd, ESOCKET_UNIX_DOMAIN, &nterfacer_events, nterfacer_token);
91 }
92
93 /* the main unix domain socket must NOT have a disconnect event. */
94 nterfacer_events.on_disconnect = nterfacer_disconnect_event;
95 }
96
97 void free_handler(struct handler *hp) {
98 freesstring(hp->command);
99 free(hp);
100 }
101
102 void free_handlers(struct service_node *tp) {
103 struct handler *hp, *lp;
104
105 for(hp=tp->handlers;hp;) {
106 lp = hp;
107 hp = hp->next;
108 free_handler(lp);
109 }
110
111 tp->handlers = NULL;
112 }
113
114 void _fini(void) {
115 struct service_node *tp, *lp;
116 int i;
117
118 if(ping)
119 deregister_service(ping);
120
121 for(tp=tree;tp;) {
122 lp = tp;
123 tp = tp->next;
124 free_handlers(lp);
125 free(lp);
126 }
127 tree = NULL;
128
129 if((accept_fd != -1) && nterfacer_sock) {
130 esocket_clean_by_token(nterfacer_token);
131 nterfacer_sock = NULL;
132 accept_fd = -1;
133 }
134
135 if(permits && permit_count) {
136 for(i=0;i<permit_count;i++) {
137 freesstring(permits[i].hostname);
138 freesstring(permits[i].password);
139 }
140 free(permits);
141 permit_count = 0;
142 permits = NULL;
143 }
144
145 nrl = nterface_close_log(nrl);
146 }
147
148 int load_permits(void) {
149 int lines, loaded_lines = 0, i, j;
150 struct permitted *new_permits, *resized, *item;
151 struct hostent *host;
152 char buf[50];
153
154 lines = getcopyconfigitemintpositive("nterfacer", "permits", 0);
155 if(lines < 1) {
156 nterface_log(nrl, NL_ERROR, "No permits found in config file.");
157 return 0;
158 }
159 nterface_log(nrl, NL_INFO, "Loading %d permit%s from config file", lines, lines==1?"":"s");
160
161 new_permits = calloc(lines, sizeof(struct permitted));
162 item = new_permits;
163
164 for(i=1;i<=lines;i++) {
165 snprintf(buf, sizeof(buf), "hostname%d", i);
166 item->hostname = getcopyconfigitem("nterfacer", buf, "", 100);
167 if(!item->hostname) {
168 nterface_log(nrl, NL_ERROR, "No hostname found for item %d.", i);
169 continue;
170 }
171
172 host = gethostbyname(item->hostname->content);
173 if (!host) {
174 nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i);
175 freesstring(item->hostname);
176 continue;
177 }
178
179 item->ihost = (*(struct in_addr *)host->h_addr).s_addr;
180 for(j=0;j<loaded_lines;j++) {
181 if(new_permits[j].ihost == item->ihost) {
182 nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i, i);
183 host = NULL;
184 }
185 }
186
187 if(!host) {
188 freesstring(item->hostname);
189 continue;
190 }
191
192 snprintf(buf, sizeof(buf), "password%d", i);
193 item->password = getcopyconfigitem("nterfacer", buf, "", 100);
194 if(!item->password) {
195 nterface_log(nrl, NL_ERROR, "No password found for item %d.", item->hostname->content, i);
196 freesstring(item->hostname);
197 continue;
198 }
199
200 nterface_log(nrl, NL_DEBUG, "Loaded permit, hostname: %s.", item->hostname->content);
201
202 item++;
203 loaded_lines++;
204 }
205
206 if(!loaded_lines) {
207 free(new_permits);
208 return 0;
209 }
210
211 resized = realloc(new_permits, sizeof(struct permitted) * loaded_lines);
212 if(!resized) {
213 MemError();
214 free(new_permits);
215 return 0;
216 }
217 permits = resized;
218 permit_count = loaded_lines;
219
220 return permit_count;
221 }
222
223 int setup_listening_socket(void) {
224 struct sockaddr_in sin;
225 int fd;
226 unsigned int opt = 1;
227
228 fd = socket(AF_INET, SOCK_STREAM, 0);
229
230 /* also shamelessly ripped from proxyscan */
231 if(fd == -1) {
232 nterface_log(nrl, NL_ERROR, "Unable to open listening socket (%d).", errno);
233 return -1;
234 }
235
236 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt)) != 0) {
237 nterface_log(nrl, NL_ERROR, "Unable to set SO_REUSEADDR on listen socket.");
238 return -1;
239 }
240
241 /* Initialiase the addresses */
242 memset(&sin, 0, sizeof(sin));
243 sin.sin_family = AF_INET;
244 sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT));
245
246 if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) {
247 nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno);
248 return -1;
249 }
250
251 listen(fd, 5);
252
253 if(ioctl(fd, FIONBIO, &opt)) {
254 nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking.");
255 return -1;
256 }
257
258 return fd;
259 }
260
261 struct service_node *register_service(char *name) {
262 struct service_node *np = malloc(sizeof(service_node));
263 MemCheckR(np, NULL);
264
265 np->name = getsstring(name, strlen(name));
266 if(!np->name) {
267 MemError();
268 free(np);
269 return NULL;
270 }
271
272 np->handlers = NULL;
273 np->next = tree;
274 tree = np;
275
276 return np;
277 }
278
279 struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) {
280 struct handler *hp = malloc(sizeof(handler));
281 MemCheckR(hp, NULL);
282
283 hp->command = getsstring(command, strlen(command));
284 if(!hp->command) {
285 MemError();
286 free(hp);
287 return NULL;
288 }
289
290 hp->function = fp;
291 hp->args = args;
292
293 hp->next = service->handlers;
294 hp->service = service;
295 service->handlers = hp;
296
297 return hp;
298 }
299
300 void deregister_handler(struct handler *hl) {
301 struct service_node *service = (struct service_node *)hl->service;
302 struct handler *np, *lp = NULL;
303 for(np=service->handlers;np;lp=np,np=np->next) {
304 if(hl == np) {
305 if(lp) {
306 lp->next = np->next;
307 } else {
308 service->handlers = np->next;
309 }
310 free_handler(np);
311 return;
312 }
313 }
314 }
315
316 void deregister_service(struct service_node *service) {
317 struct service_node *sp, *lp = NULL;
318 struct rline *li, *pi = NULL;
319
320 for(sp=tree;sp;lp=sp,sp=sp->next) {
321 if(sp == service) {
322 if(lp) {
323 lp->next = sp->next;
324 } else {
325 tree = sp->next;
326 }
327 break;
328 }
329 }
330
331 if(!sp) /* already freed */
332 return;
333
334 free_handlers(service);
335
336 for(li=rlines;li;) {
337 if(li->service == service) {
338 if(pi) {
339 pi->next = li->next;
340 free(li);
341 li = pi->next;
342 } else {
343 rlines = li->next;
344 free(li);
345 li = rlines;
346 }
347 } else {
348 pi=li,li=li->next;
349 }
350 }
351 freesstring(service->name);
352
353 free(service);
354 }
355
356 void nterfacer_accept_event(struct esocket *socket) {
357 struct sockaddr_in sin;
358 unsigned int addrsize = sizeof(sin);
359 int newfd = accept(socket->fd, (struct sockaddr *)&sin, &addrsize), i;
360 struct sconnect *temp;
361 struct permitted *item = NULL;
362 struct esocket *newsocket;
363 unsigned int opt = 1;
364
365 if(newfd == -1) {
366 nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!");
367 return;
368 }
369
370 if(ioctl(newfd, FIONBIO, &opt)) {
371 nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking.");
372 return;
373 }
374
375 for(i=0;i<permit_count;i++) {
376 if(permits[i].ihost == sin.sin_addr.s_addr) {
377 item = &permits[i];
378 break;
379 }
380 }
381
382 if(!item) {
383 nterface_log(nrl, NL_INFO, "Unauthorised connection from: %s", IPtostr(htonl(sin.sin_addr.s_addr)));
384 close(newfd);
385 return;
386 }
387
388 temp = (struct sconnect *)malloc(sizeof(struct sconnect));
389 if(!temp) {
390 MemError();
391 close(newfd);
392 return;
393 }
394
395 /* do checks on hostname first */
396
397 newsocket = esocket_add(newfd, ESOCKET_UNIX_DOMAIN_CONNECTED, &nterfacer_events, nterfacer_token);
398 if(!newsocket) {
399 free(temp);
400 close(newfd);
401 return;
402 }
403 newsocket->tag = temp;
404
405 nterface_log(nrl, NL_INFO, "New connection from %s.", item->hostname->content);
406
407 temp->status = SS_IDLE;
408 temp->permit = item;
409
410 esocket_write_line(newsocket, "nterfacer " PROTOCOL_VERSION);
411 }
412
413 int nterfacer_line_event(struct esocket *sock, char *newline) {
414 struct sconnect *socket = sock->tag;
415 char *response, *theirnonceh = NULL;
416 unsigned char theirnonce[NONCE_LEN];
417 int number, reason;
418
419 switch(socket->status) {
420 case SS_IDLE:
421 if(strcasecmp(newline, ANTI_FULL_VERSION)) {
422 nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline);
423 return 1;
424 } else {
425 char *hex, hexbuf[NONCE_LEN * 2 + 1]; /* Vekoma mAD HoUSe */
426
427 hex = get_random_hex();
428 if(!hex) {
429 nterface_log(nrl, NL_ERROR, "Unable to open challenge entropy bin!");
430 return 1;
431 }
432
433 memcpy(socket->response, challenge_response(hex, socket->permit->password->content), sizeof(socket->response));
434 socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */
435
436 socket->status = SS_VERSIONED;
437 if(!generate_nonce(socket->ournonce, 1)) {
438 nterface_log(nrl, NL_ERROR, "Unable to generate nonce!");
439 return 1;
440 }
441
442 if(esocket_write_line(sock, "%s %s", hex, int_to_hex(socket->ournonce, hexbuf, NONCE_LEN)))
443 return BUF_ERROR;
444 return 0;
445 }
446 break;
447 case SS_VERSIONED:
448 for(response=newline;*response;response++) {
449 if((*response == ' ') && (*(response + 1))) {
450 *response = '\0';
451 theirnonceh = response + 1;
452 break;
453 }
454 }
455
456 if(!theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) {
457 nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content);
458 return 1;
459 }
460
461 if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) {
462 nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content);
463 return 1;
464 }
465
466 if(!strncasecmp(newline, socket->response, sizeof(socket->response))) {
467 int ret;
468
469 nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content);
470 socket->status = SS_AUTHENTICATED;
471 ret = esocket_write_line(sock, "Oauth");
472 if(!ret) {
473 switch_buffer_mode(sock, socket->permit->password->content, socket->ournonce, theirnonce);
474 } else {
475 return BUF_ERROR;
476 }
477 } else {
478 nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content);
479
480 return 1;
481 }
482 break;
483 case SS_AUTHENTICATED:
484 nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline);
485 reason = nterfacer_new_rline(newline, sock, &number);
486 if(reason) {
487 if(reason == RE_SOCKET_ERROR)
488 return BUF_ERROR;
489 if(reason != RE_BAD_LINE) {
490 if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason)))
491 return BUF_ERROR;
492 return 0;
493 } else {
494 return 1;
495 }
496 }
497 break;
498 }
499
500 return 0;
501 }
502
503 int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
504 char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp;
505 int argcount;
506 struct service_node *service;
507 struct rline *prequest;
508 struct handler *hl;
509 int re;
510
511 if(!line || !line[0] || (line[0] == ','))
512 return 0;
513
514 for(sp=line;*sp;sp++)
515 if(*sp == ',')
516 break;
517
518 if(!*sp || !*(sp + 1))
519 return RE_BAD_LINE;
520
521 *sp = '\0';
522
523 for(service=tree;service;service=service->next)
524 if(!strcmp(service->name->content, line))
525 break;
526
527 for(p=sp+1;*p;p++)
528 if(*p == ',')
529 break;
530
531 if(!*p || !(p + 1))
532 return RE_BAD_LINE;
533
534 *p = '\0';
535 *number = positive_atoi(sp + 1);
536
537 if((*number < 1) || (*number > 0xffff))
538 return RE_BAD_LINE;
539
540 if (!service) {
541 nterface_log(nrl, NL_DEBUG, "Unable to find service: %s", line);
542 return RE_SERVICER_NOT_FOUND;
543 }
544
545 newp = commandbuf;
546
547 for(pp=p+1;*pp;pp++) {
548 if((*pp == '\\') && *(pp + 1)) {
549 if(*(pp + 1) == ',') {
550 *newp++ = ',';
551 } else if(*(pp + 1) == '\\') {
552 *newp++ = '\\';
553 }
554 pp++;
555 } else if(*pp == ',') {
556 break;
557 } else {
558 *newp++ = *pp;
559 }
560 }
561
562 if(*pp == '\0') { /* if we're at the end already, we have no arguments */
563 argcount = 0;
564 } else {
565 argcount = 1; /* we have a comma, so we have one already */
566 }
567
568 *newp = '\0';
569
570 for(hl=service->handlers;hl;hl=hl->next)
571 if(!strncmp(hl->command->content, commandbuf, sizeof(commandbuf)))
572 break;
573
574 if(!hl)
575 return RE_COMMAND_NOT_FOUND;
576
577 if(argcount) {
578 parsebuf = (char *)malloc(strlen(pp) + 1);
579 MemCheckR(parsebuf, RE_MEM_ERROR);
580 newp = parsebuf;
581
582 for(newp=args[0]=parsebuf,pp++;*pp;pp++) {
583 if((*pp == '\\') && *(pp + 1)) {
584 if(*(pp + 1) == ',') {
585 *newp++ = ',';
586 } else if(*(pp + 1) == '\\') {
587 *newp++ = '\\';
588 }
589 pp++;
590 } else if(*pp == ',') {
591 *newp++ = '\0';
592 args[argcount++] = newp;
593 if(argcount > MAX_ARGS) {
594 free(parsebuf);
595 return RE_TOO_MANY_ARGS;
596 }
597 } else {
598 *newp++ = *pp;
599 }
600 }
601 *newp = '\0';
602 }
603 if(argcount < hl->args) {
604 if(argcount && parsebuf)
605 free(parsebuf);
606 return RE_WRONG_ARG_COUNT;
607 }
608
609 prequest = (struct rline *)malloc(sizeof(struct rline));
610 if(!prequest) {
611 MemError();
612 if(argcount && parsebuf)
613 free(parsebuf);
614 return RE_MEM_ERROR;
615 }
616
617 prequest->service = service;
618 prequest->handler = hl;
619 prequest->buf[0] = '\0';
620 prequest->curpos = prequest->buf;
621 prequest->tag = NULL;
622 prequest->id = *number;
623 prequest->next = rlines;
624 prequest->socket = socket;
625
626 rlines = prequest;
627 re = (hl->function)(prequest, argcount, args);
628
629 if(argcount && parsebuf)
630 free(parsebuf);
631
632 return re;
633 }
634
635 void nterfacer_disconnect_event(struct esocket *sock) {
636 struct sconnect *socket = sock->tag;
637 struct rline *li;
638 /* not tested */
639
640 nterface_log(nrl, NL_INFO, "Disconnected from %s.", socket->permit->hostname->content);
641
642 /* not tested */
643 for(li=rlines;li;li=li->next)
644 if(li->socket->tag == socket)
645 li->socket = NULL;
646
647 free(socket);
648 }
649
650 int ri_append(struct rline *li, char *format, ...) {
651 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
652 int sizeleft = sizeof(li->buf) - (li->curpos - li->buf);
653 va_list ap;
654
655 va_start(ap, format);
656
657 if(vsnprintf(buf, sizeof(buf), format, ap) >= sizeof(buf)) {
658 va_end(ap);
659 return BF_OVER;
660 }
661
662 va_end(ap);
663
664 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
665 if((*p == ',') || (*p == '\\'))
666 *tp++ = '\\';
667
668 if(sizeleft > 0) {
669 if(li->curpos == li->buf) {
670 li->curpos+=snprintf(li->curpos, sizeleft, "%s", escapedbuf);
671 } else {
672 li->curpos+=snprintf(li->curpos, sizeleft, ",%s", escapedbuf);
673 }
674 }
675
676 if(sizeof(li->buf) - (li->curpos - li->buf) > 0) {
677 return BF_OK;
678 } else {
679 return BF_OVER;
680 }
681 }
682
683 int ri_error(struct rline *li, int error_code, char *format, ...) {
684 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
685 struct rline *pp, *lp = NULL;
686 va_list ap;
687 int retval = RE_OK;
688
689 if(li->socket) {
690 va_start(ap, format);
691 vsnprintf(buf, sizeof(buf), format, ap);
692 va_end(ap);
693
694 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
695 if((*p == ',') || (*p == '\\'))
696 *tp++ = '\\';
697
698 if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf))
699 retval = RE_SOCKET_ERROR;
700 }
701
702 for(pp=rlines;pp;lp=pp,pp=pp->next) {
703 if(pp == li) {
704 if(lp) {
705 lp->next = li->next;
706 } else {
707 rlines = li->next;
708 }
709 free(li);
710 break;
711 }
712 }
713
714 return retval;
715 }
716
717 int ri_final(struct rline *li) {
718 struct rline *pp, *lp = NULL;
719 int retval = RE_OK;
720
721 if(li->socket)
722 if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf))
723 retval = RE_SOCKET_ERROR;
724
725 for(pp=rlines;pp;lp=pp,pp=pp->next) {
726 if(pp == li) {
727 if(lp) {
728 lp->next = li->next;
729 } else {
730 rlines = li->next;
731 }
732 free(li);
733 break;
734 }
735 }
736
737 return retval;
738 }
739
740 int ping_handler(struct rline *ri, int argc, char **argv) {
741 ri_append(ri, "OK");
742 return ri_final(ri);
743 }