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