]> jfr.im git - irc/quakenet/newserv.git/blame - nterfacer/nterfacer.c
nterfacer: Fix bounds check.
[irc/quakenet/newserv.git] / nterfacer / nterfacer.c
CommitLineData
18f8bd28
CP
1/*
2 nterfacer
d4897ed9 3 Copyright (C) 2004-2007 Chris Porter.
18f8bd28
CP
4*/
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <stdarg.h>
9#include <sys/poll.h>
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <errno.h>
13#include <unistd.h>
14#include <sys/ioctl.h>
15#include <netdb.h>
16#include <string.h>
9eed0e10 17#include <strings.h>
9c312e03
CP
18#include <netinet/in.h>
19#include <arpa/inet.h>
18f8bd28
CP
20
21#include "../lib/sstring.h"
22#include "../lib/irc_string.h"
23#include "../core/config.h"
24#include "../core/events.h"
87698d77 25#include "../lib/version.h"
dae6d3d7 26#include "../core/schedule.h"
18f8bd28
CP
27
28#include "nterfacer.h"
29#include "logging.h"
30
80d91dd6 31MODULE_VERSION("1.1p" PROTOCOL_VERSION);
87698d77 32
18f8bd28
CP
33struct service_node *tree = NULL;
34struct esocket_events nterfacer_events;
35struct esocket *nterfacer_sock;
36struct rline *rlines = NULL;
37unsigned short nterfacer_token = BLANK_TOKEN;
38struct nterface_auto_log *nrl;
39
c86a0114 40struct service_node *ping = NULL;
4e4920a4
CP
41int accept_fd = -1;
42struct permitted *permits;
43int permit_count = 0;
6cf2d6a5
CP
44
45int ping_handler(struct rline *ri, int argc, char **argv);
2a6be5fe 46static void nterfacer_sendcallback(struct rline *ri, int error, char *buf);
6cf2d6a5 47
18f8bd28
CP
48void _init(void) {
49 int loaded;
50 int debug_mode = getcopyconfigitemintpositive("nterfacer", "debug", 0);
51
52 nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode);
53
54 loaded = load_permits();
2a6be5fe
CP
55 nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
56
57 if(!loaded)
18f8bd28 58 return;
18f8bd28
CP
59
60 nterfacer_events.on_accept = nterfacer_accept_event;
61 nterfacer_events.on_line = nterfacer_line_event;
62 nterfacer_events.on_disconnect = NULL;
63
64 nterfacer_token = esocket_token();
65
6cf2d6a5
CP
66 ping = register_service("nterfacer");
67 if(!ping) {
68 MemError();
69 } else {
70 register_handler(ping, "ping", 0, ping_handler);
71 }
72
18f8bd28
CP
73 accept_fd = setup_listening_socket();
74 if(accept_fd == -1) {
75 nterface_log(nrl, NL_ERROR, "Unable to setup listening socket!");
76 } else {
77 nterfacer_sock = esocket_add(accept_fd, ESOCKET_UNIX_DOMAIN, &nterfacer_events, nterfacer_token);
78 }
79
80 /* the main unix domain socket must NOT have a disconnect event. */
81 nterfacer_events.on_disconnect = nterfacer_disconnect_event;
82}
83
504f62c7 84void free_handler(struct handler *hp) {
f435bd22
CP
85 struct rline *li, *pi = NULL;
86
87 for(li=rlines;li;) {
88 if(li->handler == hp) {
89 if(li->socket) {
90 esocket_write_line(li->socket, "%d,OE%d,%s", li->id, BF_UNLOADED, "Service was unloaded.");
91 } else if(li->callback) {
2a6be5fe 92 nterfacer_sendcallback(li, BF_UNLOADED, "Service was unloaded.");
f435bd22
CP
93 }
94 if(pi) {
95 pi->next = li->next;
96 ntfree(li);
97 li = pi->next;
98 } else {
99 rlines = li->next;
100 ntfree(li);
101 li = rlines;
102 }
103 } else {
104 pi=li,li=li->next;
105 }
106 }
107
504f62c7 108 freesstring(hp->command);
5ab07198 109 ntfree(hp);
504f62c7
CP
110}
111
18f8bd28
CP
112void free_handlers(struct service_node *tp) {
113 struct handler *hp, *lp;
114
115 for(hp=tp->handlers;hp;) {
116 lp = hp;
117 hp = hp->next;
504f62c7 118 free_handler(lp);
18f8bd28
CP
119 }
120
121 tp->handlers = NULL;
122}
123
124void _fini(void) {
125 struct service_node *tp, *lp;
126 int i;
127
6cf2d6a5
CP
128 if(ping)
129 deregister_service(ping);
130
18f8bd28
CP
131 for(tp=tree;tp;) {
132 lp = tp;
133 tp = tp->next;
134 free_handlers(lp);
5ab07198 135 ntfree(lp);
18f8bd28
CP
136 }
137 tree = NULL;
138
139 if((accept_fd != -1) && nterfacer_sock) {
140 esocket_clean_by_token(nterfacer_token);
141 nterfacer_sock = NULL;
142 accept_fd = -1;
143 }
144
145 if(permits && permit_count) {
146 for(i=0;i<permit_count;i++) {
147 freesstring(permits[i].hostname);
148 freesstring(permits[i].password);
149 }
5ab07198 150 ntfree(permits);
18f8bd28
CP
151 permit_count = 0;
152 permits = NULL;
153 }
154
155 nrl = nterface_close_log(nrl);
f8d5cfcf 156 nscheckfreeall(POOL_NTERFACER);
18f8bd28
CP
157}
158
159int load_permits(void) {
8238d9c1 160 int loaded_lines = 0, i, j;
18f8bd28
CP
161 struct permitted *new_permits, *resized, *item;
162 struct hostent *host;
8238d9c1
CP
163 array *hostnamesa, *passwordsa;
164 sstring **hostnames, **passwords;
18f8bd28 165
8238d9c1
CP
166 hostnamesa = getconfigitems("nterfacer", "hostname");
167 passwordsa = getconfigitems("nterfacer", "password");
2a6be5fe 168 if(!hostnamesa || !passwordsa)
18f8bd28 169 return 0;
8238d9c1
CP
170 if(hostnamesa->cursi != passwordsa->cursi) {
171 nterface_log(nrl, NL_ERROR, "Different number of hostnames/passwords in config file.");
172 return 0;
173 }
174
175 hostnames = (sstring **)hostnamesa->content;
176 passwords = (sstring **)passwordsa->content;
18f8bd28 177
5ab07198
CP
178 new_permits = ntmalloc(hostnamesa->cursi * sizeof(struct permitted));
179 memset(new_permits, 0, hostnamesa->cursi * sizeof(struct permitted));
18f8bd28
CP
180 item = new_permits;
181
8238d9c1
CP
182 for(i=0;i<hostnamesa->cursi;i++) {
183 item->hostname = getsstring(hostnames[i]->content, hostnames[i]->length);
18f8bd28
CP
184
185 host = gethostbyname(item->hostname->content);
186 if (!host) {
8238d9c1 187 nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i + 1);
18f8bd28
CP
188 freesstring(item->hostname);
189 continue;
190 }
191
00c5dbcb 192 item->ihost = (*(struct in_addr *)host->h_addr_list[0]).s_addr;
18f8bd28
CP
193 for(j=0;j<loaded_lines;j++) {
194 if(new_permits[j].ihost == item->ihost) {
8238d9c1 195 nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i + 1, i + 1);
18f8bd28
CP
196 host = NULL;
197 }
198 }
199
200 if(!host) {
201 freesstring(item->hostname);
202 continue;
203 }
204
8238d9c1 205 item->password = getsstring(passwords[i]->content, passwords[i]->length);
18f8bd28
CP
206 nterface_log(nrl, NL_DEBUG, "Loaded permit, hostname: %s.", item->hostname->content);
207
208 item++;
209 loaded_lines++;
210 }
211
212 if(!loaded_lines) {
5ab07198 213 ntfree(new_permits);
18f8bd28
CP
214 return 0;
215 }
216
5ab07198 217 resized = ntrealloc(new_permits, sizeof(struct permitted) * loaded_lines);
18f8bd28
CP
218 if(!resized) {
219 MemError();
5ab07198 220 ntfree(new_permits);
18f8bd28
CP
221 return 0;
222 }
223 permits = resized;
224 permit_count = loaded_lines;
225
226 return permit_count;
227}
228
229int setup_listening_socket(void) {
230 struct sockaddr_in sin;
231 int fd;
232 unsigned int opt = 1;
233
234 fd = socket(AF_INET, SOCK_STREAM, 0);
235
236 /* also shamelessly ripped from proxyscan */
237 if(fd == -1) {
238 nterface_log(nrl, NL_ERROR, "Unable to open listening socket (%d).", errno);
239 return -1;
240 }
241
242 if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt)) != 0) {
243 nterface_log(nrl, NL_ERROR, "Unable to set SO_REUSEADDR on listen socket.");
244 return -1;
245 }
246
247 /* Initialiase the addresses */
248 memset(&sin, 0, sizeof(sin));
249 sin.sin_family = AF_INET;
084e76d8 250 sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT));
18f8bd28
CP
251
252 if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) {
253 nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno);
ec83ad7e 254 close(fd);
18f8bd28
CP
255 return -1;
256 }
257
258 listen(fd, 5);
259
97eb3b6f 260 if(ioctl(fd, FIONBIO, &opt)) {
18f8bd28 261 nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking.");
ec83ad7e 262 close(fd);
18f8bd28
CP
263 return -1;
264 }
265
266 return fd;
267}
268
269struct service_node *register_service(char *name) {
5ab07198 270 struct service_node *np = ntmalloc(sizeof(service_node));
18f8bd28
CP
271 MemCheckR(np, NULL);
272
273 np->name = getsstring(name, strlen(name));
274 if(!np->name) {
275 MemError();
5ab07198 276 ntfree(np);
18f8bd28
CP
277 return NULL;
278 }
279
280 np->handlers = NULL;
281 np->next = tree;
282 tree = np;
283
284 return np;
285}
286
287struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) {
5ab07198 288 struct handler *hp = ntmalloc(sizeof(handler));
18f8bd28
CP
289 MemCheckR(hp, NULL);
290
291 hp->command = getsstring(command, strlen(command));
292 if(!hp->command) {
293 MemError();
5ab07198 294 ntfree(hp);
18f8bd28
CP
295 return NULL;
296 }
297
a690fb9f 298 hp->function = fp;
18f8bd28
CP
299 hp->args = args;
300
301 hp->next = service->handlers;
504f62c7 302 hp->service = service;
18f8bd28
CP
303 service->handlers = hp;
304
305 return hp;
306}
307
504f62c7
CP
308void deregister_handler(struct handler *hl) {
309 struct service_node *service = (struct service_node *)hl->service;
310 struct handler *np, *lp = NULL;
311 for(np=service->handlers;np;lp=np,np=np->next) {
312 if(hl == np) {
313 if(lp) {
314 lp->next = np->next;
315 } else {
316 service->handlers = np->next;
317 }
318 free_handler(np);
319 return;
320 }
321 }
322}
323
18f8bd28
CP
324void deregister_service(struct service_node *service) {
325 struct service_node *sp, *lp = NULL;
18f8bd28
CP
326
327 for(sp=tree;sp;lp=sp,sp=sp->next) {
328 if(sp == service) {
329 if(lp) {
330 lp->next = sp->next;
331 } else {
332 tree = sp->next;
333 }
334 break;
335 }
336 }
337
338 if(!sp) /* already freed */
339 return;
340
341 free_handlers(service);
342
18f8bd28
CP
343 freesstring(service->name);
344
5ab07198 345 ntfree(service);
18f8bd28
CP
346}
347
348void nterfacer_accept_event(struct esocket *socket) {
349 struct sockaddr_in sin;
350 unsigned int addrsize = sizeof(sin);
351 int newfd = accept(socket->fd, (struct sockaddr *)&sin, &addrsize), i;
352 struct sconnect *temp;
353 struct permitted *item = NULL;
354 struct esocket *newsocket;
084e76d8 355 unsigned int opt = 1;
18f8bd28
CP
356
357 if(newfd == -1) {
358 nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!");
359 return;
360 }
361
97eb3b6f
CP
362 if(ioctl(newfd, FIONBIO, &opt)) {
363 nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking.");
ec83ad7e 364 close(newfd);
3f15de97 365 return;
084e76d8
CP
366 }
367
18f8bd28
CP
368 for(i=0;i<permit_count;i++) {
369 if(permits[i].ihost == sin.sin_addr.s_addr) {
370 item = &permits[i];
371 break;
372 }
373 }
374
375 if(!item) {
9c312e03 376 nterface_log(nrl, NL_INFO, "Unauthorised connection from %s closed", inet_ntoa(sin.sin_addr));
18f8bd28
CP
377 close(newfd);
378 return;
379 }
380
5ab07198 381 temp = (struct sconnect *)ntmalloc(sizeof(struct sconnect));
18f8bd28
CP
382 if(!temp) {
383 MemError();
384 close(newfd);
385 return;
386 }
387
388 /* do checks on hostname first */
389
390 newsocket = esocket_add(newfd, ESOCKET_UNIX_DOMAIN_CONNECTED, &nterfacer_events, nterfacer_token);
391 if(!newsocket) {
5ab07198 392 ntfree(temp);
18f8bd28
CP
393 close(newfd);
394 return;
395 }
396 newsocket->tag = temp;
397
398 nterface_log(nrl, NL_INFO, "New connection from %s.", item->hostname->content);
399
400 temp->status = SS_IDLE;
401 temp->permit = item;
402
403 esocket_write_line(newsocket, "nterfacer " PROTOCOL_VERSION);
404}
405
80d91dd6 406void derive_key(unsigned char *out, char *password, char *segment, unsigned char *noncea, unsigned char *nonceb, unsigned char *extra, int extralen) {
1ac1d43c
CP
407 SHA256_CTX c;
408 SHA256_Init(&c);
409 SHA256_Update(&c, (unsigned char *)password, strlen(password));
410 SHA256_Update(&c, (unsigned char *)":", 1);
411 SHA256_Update(&c, (unsigned char *)segment, strlen(segment));
412 SHA256_Update(&c, (unsigned char *)":", 1);
413 SHA256_Update(&c, noncea, 16);
414 SHA256_Update(&c, (unsigned char *)":", 1);
415 SHA256_Update(&c, nonceb, 16);
80d91dd6
CP
416 SHA256_Update(&c, (unsigned char *)":", 1);
417 SHA256_Update(&c, extra, extralen);
1ac1d43c
CP
418 SHA256_Final(out, &c);
419
420 SHA256_Init(&c);
421 SHA256_Update(&c, out, 32);
422 SHA256_Final(out, &c);
423}
424
18f8bd28
CP
425int nterfacer_line_event(struct esocket *sock, char *newline) {
426 struct sconnect *socket = sock->tag;
1ac1d43c
CP
427 char *response, *theirnonceh = NULL, *theirivh = NULL;
428 unsigned char theirnonce[16], theiriv[16];
18f8bd28
CP
429 int number, reason;
430
431 switch(socket->status) {
432 case SS_IDLE:
433 if(strcasecmp(newline, ANTI_FULL_VERSION)) {
434 nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline);
435 return 1;
436 } else {
1ac1d43c
CP
437 unsigned char challenge[32];
438 char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1];
18f8bd28 439
1ac1d43c
CP
440 if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) {
441 nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!");
18f8bd28
CP
442 return 1;
443 }
444
1ac1d43c
CP
445 int_to_hex(challenge, socket->challenge, 32);
446 int_to_hex(socket->iv, ivhex, 16);
447
448 memcpy(socket->response, challenge_response(socket->challenge, socket->permit->password->content), sizeof(socket->response));
18f8bd28
CP
449 socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */
450
451 socket->status = SS_VERSIONED;
452 if(!generate_nonce(socket->ournonce, 1)) {
453 nterface_log(nrl, NL_ERROR, "Unable to generate nonce!");
454 return 1;
455 }
1ac1d43c 456 int_to_hex(socket->ournonce, noncehex, 16);
18f8bd28 457
1ac1d43c 458 if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex))
701500af
CP
459 return BUF_ERROR;
460 return 0;
18f8bd28
CP
461 }
462 break;
463 case SS_VERSIONED:
464 for(response=newline;*response;response++) {
465 if((*response == ' ') && (*(response + 1))) {
466 *response = '\0';
1ac1d43c 467 theirivh = response + 1;
18f8bd28
CP
468 break;
469 }
470 }
471
1ac1d43c
CP
472 if(theirivh) {
473 for(response=theirivh;*response;response++) {
474 if((*response == ' ') && (*(response + 1))) {
475 *response = '\0';
476 theirnonceh = response + 1;
477 break;
478 }
479 }
480 }
481
482 if(!theirivh || (strlen(theirivh) != 32) || !hex_to_int(theirivh, theiriv, sizeof(theiriv)) ||
483 !theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) {
18f8bd28
CP
484 nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content);
485 return 1;
486 }
487
488 if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) {
489 nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content);
490 return 1;
491 }
492
493 if(!strncasecmp(newline, socket->response, sizeof(socket->response))) {
80d91dd6
CP
494 unsigned char theirkey[32], ourkey[32];
495
e66c4243 496 derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6);
18f8bd28 497
e66c4243 498 derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6);
18f8bd28
CP
499 nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content);
500 socket->status = SS_AUTHENTICATED;
80d91dd6 501 switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv);
1ac1d43c 502
80d91dd6 503 if(esocket_write_line(sock, "Oauth"))
701500af 504 return BUF_ERROR;
18f8bd28
CP
505 } else {
506 nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content);
507
508 return 1;
509 }
510 break;
511 case SS_AUTHENTICATED:
512 nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline);
513 reason = nterfacer_new_rline(newline, sock, &number);
514 if(reason) {
5757e1ef
CP
515 if(reason == RE_SOCKET_ERROR)
516 return BUF_ERROR;
18f8bd28 517 if(reason != RE_BAD_LINE) {
a690fb9f
CP
518 if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason)))
519 return BUF_ERROR;
520 return 0;
18f8bd28
CP
521 } else {
522 return 1;
523 }
524 }
525 break;
526 }
527
528 return 0;
529}
530
531int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
1fbb1306 532 char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp;
18f8bd28
CP
533 int argcount;
534 struct service_node *service;
535 struct rline *prequest;
536 struct handler *hl;
537 int re;
538
539 if(!line || !line[0] || (line[0] == ','))
540 return 0;
541
542 for(sp=line;*sp;sp++)
543 if(*sp == ',')
544 break;
545
546 if(!*sp || !*(sp + 1))
547 return RE_BAD_LINE;
548
549 *sp = '\0';
550
551 for(service=tree;service;service=service->next)
552 if(!strcmp(service->name->content, line))
553 break;
554
555 for(p=sp+1;*p;p++)
556 if(*p == ',')
557 break;
558
b26fde4b 559 if(!*p || !*(p + 1))
18f8bd28
CP
560 return RE_BAD_LINE;
561
562 *p = '\0';
563 *number = positive_atoi(sp + 1);
564
565 if((*number < 1) || (*number > 0xffff))
566 return RE_BAD_LINE;
567
568 if (!service) {
569 nterface_log(nrl, NL_DEBUG, "Unable to find service: %s", line);
570 return RE_SERVICER_NOT_FOUND;
571 }
572
573 newp = commandbuf;
574
575 for(pp=p+1;*pp;pp++) {
576 if((*pp == '\\') && *(pp + 1)) {
577 if(*(pp + 1) == ',') {
578 *newp++ = ',';
579 } else if(*(pp + 1) == '\\') {
580 *newp++ = '\\';
581 }
582 pp++;
583 } else if(*pp == ',') {
584 break;
585 } else {
586 *newp++ = *pp;
587 }
588 }
589
590 if(*pp == '\0') { /* if we're at the end already, we have no arguments */
591 argcount = 0;
592 } else {
593 argcount = 1; /* we have a comma, so we have one already */
594 }
595
596 *newp = '\0';
597
598 for(hl=service->handlers;hl;hl=hl->next)
599 if(!strncmp(hl->command->content, commandbuf, sizeof(commandbuf)))
600 break;
601
602 if(!hl)
603 return RE_COMMAND_NOT_FOUND;
604
605 if(argcount) {
5ab07198 606 parsebuf = (char *)ntmalloc(strlen(pp) + 1);
18f8bd28 607 MemCheckR(parsebuf, RE_MEM_ERROR);
18f8bd28
CP
608
609 for(newp=args[0]=parsebuf,pp++;*pp;pp++) {
610 if((*pp == '\\') && *(pp + 1)) {
611 if(*(pp + 1) == ',') {
612 *newp++ = ',';
613 } else if(*(pp + 1) == '\\') {
614 *newp++ = '\\';
615 }
616 pp++;
617 } else if(*pp == ',') {
618 *newp++ = '\0';
619 args[argcount++] = newp;
620 if(argcount > MAX_ARGS) {
5ab07198 621 ntfree(parsebuf);
18f8bd28
CP
622 return RE_TOO_MANY_ARGS;
623 }
624 } else {
625 *newp++ = *pp;
626 }
627 }
628 *newp = '\0';
629 }
630 if(argcount < hl->args) {
1fbb1306 631 if(argcount && parsebuf)
5ab07198 632 ntfree(parsebuf);
18f8bd28
CP
633 return RE_WRONG_ARG_COUNT;
634 }
635
5ab07198 636 prequest = (struct rline *)ntmalloc(sizeof(struct rline));
18f8bd28
CP
637 if(!prequest) {
638 MemError();
1fbb1306 639 if(argcount && parsebuf)
5ab07198 640 ntfree(parsebuf);
18f8bd28
CP
641 return RE_MEM_ERROR;
642 }
643
644 prequest->service = service;
645 prequest->handler = hl;
95ee3dac 646 prequest->buf[0] = '\0';
18f8bd28
CP
647 prequest->curpos = prequest->buf;
648 prequest->tag = NULL;
649 prequest->id = *number;
650 prequest->next = rlines;
651 prequest->socket = socket;
e5a7c318 652 prequest->callback = NULL;
18f8bd28
CP
653
654 rlines = prequest;
a690fb9f 655 re = (hl->function)(prequest, argcount, args);
18f8bd28 656
1fbb1306 657 if(argcount && parsebuf)
5ab07198 658 ntfree(parsebuf);
18f8bd28
CP
659
660 return re;
661}
662
663void nterfacer_disconnect_event(struct esocket *sock) {
664 struct sconnect *socket = sock->tag;
665 struct rline *li;
666 /* not tested */
667
668 nterface_log(nrl, NL_INFO, "Disconnected from %s.", socket->permit->hostname->content);
669
670 /* not tested */
671 for(li=rlines;li;li=li->next)
2679891b 672 if(li->socket && (li->socket->tag == socket))
18f8bd28
CP
673 li->socket = NULL;
674
5ab07198 675 ntfree(socket);
18f8bd28
CP
676}
677
678int ri_append(struct rline *li, char *format, ...) {
679 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
680 int sizeleft = sizeof(li->buf) - (li->curpos - li->buf);
681 va_list ap;
682
683 va_start(ap, format);
684
685 if(vsnprintf(buf, sizeof(buf), format, ap) >= sizeof(buf)) {
686 va_end(ap);
687 return BF_OVER;
688 }
689
690 va_end(ap);
691
692 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
693 if((*p == ',') || (*p == '\\'))
694 *tp++ = '\\';
695
696 if(sizeleft > 0) {
697 if(li->curpos == li->buf) {
698 li->curpos+=snprintf(li->curpos, sizeleft, "%s", escapedbuf);
699 } else {
700 li->curpos+=snprintf(li->curpos, sizeleft, ",%s", escapedbuf);
701 }
702 }
703
704 if(sizeof(li->buf) - (li->curpos - li->buf) > 0) {
705 return BF_OK;
706 } else {
707 return BF_OVER;
708 }
709}
710
711int ri_error(struct rline *li, int error_code, char *format, ...) {
712 char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp;
713 struct rline *pp, *lp = NULL;
714 va_list ap;
a690fb9f 715 int retval = RE_OK;
18f8bd28 716
e5a7c318 717 if(li->socket || li->callback) {
18f8bd28
CP
718 va_start(ap, format);
719 vsnprintf(buf, sizeof(buf), format, ap);
720 va_end(ap);
721
722 for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++)
723 if((*p == ',') || (*p == '\\'))
724 *tp++ = '\\';
e5a7c318
CP
725 if(li->socket) {
726 if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf))
727 retval = RE_SOCKET_ERROR;
728 } else {
729 if(error_code == 0) /* :P */
730 error_code = -10000;
18f8bd28 731
2a6be5fe 732 nterfacer_sendcallback(li, error_code, escapedbuf);
e5a7c318 733 }
18f8bd28
CP
734 }
735
736 for(pp=rlines;pp;lp=pp,pp=pp->next) {
737 if(pp == li) {
738 if(lp) {
739 lp->next = li->next;
740 } else {
741 rlines = li->next;
742 }
5ab07198 743 ntfree(li);
18f8bd28
CP
744 break;
745 }
746 }
747
a690fb9f 748 return retval;
18f8bd28
CP
749}
750
751int ri_final(struct rline *li) {
752 struct rline *pp, *lp = NULL;
5757e1ef 753 int retval = RE_OK;
18f8bd28 754
e5a7c318 755 if(li->socket) {
5757e1ef
CP
756 if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf))
757 retval = RE_SOCKET_ERROR;
e5a7c318 758 } else if(li->callback) {
2a6be5fe 759 nterfacer_sendcallback(li, 0, li->buf);
e5a7c318 760 }
5757e1ef 761
18f8bd28
CP
762 for(pp=rlines;pp;lp=pp,pp=pp->next) {
763 if(pp == li) {
764 if(lp) {
765 lp->next = li->next;
766 } else {
767 rlines = li->next;
768 }
5ab07198 769 ntfree(li);
18f8bd28
CP
770 break;
771 }
772 }
773
5757e1ef 774 return retval;
18f8bd28 775}
6cf2d6a5
CP
776
777int ping_handler(struct rline *ri, int argc, char **argv) {
778 ri_append(ri, "OK");
779 return ri_final(ri);
780}
e5a7c318 781
dae6d3d7
CP
782struct sched_rline {
783 rline rl;
784 int argc;
785 handler *hl;
786 void *schedule;
787 char argv[];
788};
789
790static const int XMAXARGS = 50;
791
792static void execrline(void *arg) {
793 struct sched_rline *sr = arg;
794 int re, i;
795 char *argv[XMAXARGS], *buf;
796
797 sr->schedule = NULL;
798
799 buf = sr->argv;
800 for(i=0;i<sr->argc;i++) {
dae6d3d7
CP
801 argv[i] = buf;
802 buf+=strlen(buf) + 1;
803 }
804
805 re = (sr->hl->function)(&sr->rl, sr->argc, argv);
806
807 if(re)
808 Error("nterfacer", ERR_WARNING, "sendline: error occured calling %p %d: %s", sr->hl->function, re, request_error(re));
809}
810
e5a7c318
CP
811void *nterfacer_sendline(char *service, char *command, int argc, char **argv, rline_callback callback, void *tag) {
812 struct service_node *servicep;
813 struct rline *prequest;
dae6d3d7 814 struct sched_rline *sr;
e5a7c318 815 struct handler *hl;
dae6d3d7
CP
816 int totallen, i;
817 char *buf;
e5a7c318
CP
818
819 for(servicep=tree;servicep;servicep=servicep->next)
820 if(!strcmp(servicep->name->content, service))
821 break;
822
dae6d3d7
CP
823 if(argc > XMAXARGS)
824 Error("nterfacer", ERR_STOP, "Over maximum arguments.");
825
e5a7c318
CP
826 if(!servicep) {
827 Error("nterfacer", ERR_WARNING, "sendline: service not found: %s", service);
828 return NULL;
829 }
830
831 for(hl=servicep->handlers;hl;hl=hl->next)
832 if(!strcmp(hl->command->content, command))
833 break;
834
835 if(!hl) {
836 Error("nterfacer", ERR_WARNING, "sendline: command not found: %s", command);
837 return NULL;
838 }
839
840 if(argc < hl->args) {
841 Error("nterfacer", ERR_WARNING, "sendline: wrong number of arguments: %s", command);
842 return NULL;
843 }
844
dae6d3d7
CP
845 /* we have to create a copy of the arguments for reentrancy reasons, grr */
846 totallen = 0;
847 for(i=0;i<argc;i++)
848 totallen+=strlen(argv[i]) + 1;
849
850 /* HACKY but allows existing code to still work */
851 sr = (struct sched_rline *)ntmalloc(sizeof(struct sched_rline) + totallen);
852 if(!sr) {
e5a7c318
CP
853 MemError();
854 return NULL;
855 }
dae6d3d7
CP
856 prequest = &sr->rl;
857
858 sr->argc = argc;
859 buf = sr->argv;
860 for(i=0;i<argc;i++) {
861 size_t len = strlen(argv[i]) + 1;
862 memcpy(buf, argv[i], len);
863 buf+=len;
864 }
865 sr->hl = hl;
e5a7c318
CP
866
867 prequest->service = servicep;
868 prequest->handler = hl;
869 prequest->buf[0] = '\0';
870 prequest->curpos = prequest->buf;
871 prequest->tag = tag;
872 prequest->id = 0;
e5a7c318
CP
873 prequest->socket = NULL;
874 prequest->callback = callback;
875
dae6d3d7 876 prequest->next = rlines;
e5a7c318 877 rlines = prequest;
e5a7c318 878
dae6d3d7 879 scheduleoneshot(time(NULL), execrline, sr);
e5a7c318 880
dae6d3d7 881 return (void *)sr;
e5a7c318
CP
882}
883
884void nterfacer_freeline(void *tag) {
dae6d3d7 885 struct sched_rline *prequest = tag;
e5a7c318 886
dae6d3d7
CP
887 prequest->rl.callback = NULL;
888 if(prequest->schedule)
889 deleteschedule(prequest->schedule, execrline, NULL);
e5a7c318 890}
2a6be5fe
CP
891
892#define MAX_LINES 8192
893
894/* this bites */
895static void nterfacer_sendcallback(struct rline *ri, int error, char *buf) {
896 char *lines[MAX_LINES+1];
819da112 897 char newbuf[MAX_BUFSIZE+5];
2a6be5fe
CP
898 char *s, *d, *laststart;
899 int linec = 0;
900
819da112 901 for(s=buf,laststart=d=newbuf;*s;s++) {
2a6be5fe
CP
902 if((*s == '\\') && *(s + 1)) {
903 if(*(s + 1) == ',') {
904 *d++ = ',';
905 } else if(*(s + 1) == '\\') {
906 *d++ = '\\';
907 }
908 s++;
909 } else if(*s == ',') {
910 *d++ = '\0';
911 if(linec >= MAX_LINES - 5) {
912 nterfacer_sendcallback(ri, BF_OVER, "Buffer overflow.");
913 return;
914 }
915
916 lines[linec++] = laststart;
819da112 917 laststart = d;
2a6be5fe
CP
918 } else {
919 *d++ = *s;
920 }
921 }
819da112 922 *d = '\0';
2a6be5fe
CP
923 lines[linec++] = laststart;
924
925 ri->callback(error, linec, lines, ri->tag);
926}