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