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