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