]> jfr.im git - irc/quakenet/newserv.git/blob - nterfacer/nterfacer.c
BUILD: add require-all build mode
[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 #include <stdint.h>
21
22 #include "../lib/sstring.h"
23 #include "../lib/irc_string.h"
24 #include "../core/config.h"
25 #include "../core/events.h"
26 #include "../lib/version.h"
27 #include "../core/schedule.h"
28
29 #include "nterfacer.h"
30 #include "logging.h"
31
32 MODULE_VERSION("1.1p" PROTOCOL_VERSION);
33
34 struct service_node *tree = NULL;
35 struct esocket_events nterfacer_events;
36 struct esocket *nterfacer_sock;
37 struct rline *rlines = NULL;
38 unsigned short nterfacer_token = BLANK_TOKEN;
39 struct nterface_auto_log *nrl;
40
41 struct service_node *ping = NULL;
42 int accept_fd = -1;
43 struct permitted *permits;
44 int permit_count = 0;
45
46 int ping_handler(struct rline *ri, int argc, char **argv);
47 static void nterfacer_sendcallback(struct rline *ri, int error, char *buf);
48
49 void _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();
56 nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s");
57
58 if(!loaded)
59 return;
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
67 ping = register_service("nterfacer");
68 if(!ping) {
69 MemError();
70 } else {
71 register_handler(ping, "ping", 0, ping_handler);
72 }
73
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
85 void free_handler(struct handler *hp) {
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) {
93 nterfacer_sendcallback(li, BF_UNLOADED, "Service was unloaded.");
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
109 freesstring(hp->command);
110 ntfree(hp);
111 }
112
113 void 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;
119 free_handler(lp);
120 }
121
122 tp->handlers = NULL;
123 }
124
125 void _fini(void) {
126 struct service_node *tp, *lp;
127 int i;
128
129 if(ping)
130 deregister_service(ping);
131
132 for(tp=tree;tp;) {
133 lp = tp;
134 tp = tp->next;
135 free_handlers(lp);
136 ntfree(lp);
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 }
151 ntfree(permits);
152 permit_count = 0;
153 permits = NULL;
154 }
155
156 nrl = nterface_close_log(nrl);
157 nscheckfreeall(POOL_NTERFACER);
158 }
159
160 int load_permits(void) {
161 int loaded_lines = 0, i, j;
162 struct permitted *new_permits, *resized, *item;
163 struct hostent *host;
164 array *hostnamesa, *passwordsa;
165 sstring **hostnames, **passwords;
166
167 hostnamesa = getconfigitems("nterfacer", "hostname");
168 passwordsa = getconfigitems("nterfacer", "password");
169 if(!hostnamesa || !passwordsa)
170 return 0;
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;
178
179 new_permits = ntmalloc(hostnamesa->cursi * sizeof(struct permitted));
180 memset(new_permits, 0, hostnamesa->cursi * sizeof(struct permitted));
181 item = new_permits;
182
183 for(i=0;i<hostnamesa->cursi;i++) {
184 item->hostname = getsstring(hostnames[i]->content, hostnames[i]->length);
185
186 host = gethostbyname(item->hostname->content);
187 if (!host) {
188 nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i + 1);
189 freesstring(item->hostname);
190 continue;
191 }
192
193 item->ihost = (*(struct in_addr *)host->h_addr_list[0]).s_addr;
194 for(j=0;j<loaded_lines;j++) {
195 if(new_permits[j].ihost == item->ihost) {
196 nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i + 1, i + 1);
197 host = NULL;
198 }
199 }
200
201 if(!host) {
202 freesstring(item->hostname);
203 continue;
204 }
205
206 item->password = getsstring(passwords[i]->content, passwords[i]->length);
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) {
214 ntfree(new_permits);
215 return 0;
216 }
217
218 resized = ntrealloc(new_permits, sizeof(struct permitted) * loaded_lines);
219 if(!resized) {
220 MemError();
221 ntfree(new_permits);
222 return 0;
223 }
224 permits = resized;
225 permit_count = loaded_lines;
226
227 return permit_count;
228 }
229
230 int 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;
251 sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT));
252
253 if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) {
254 nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno);
255 close(fd);
256 return -1;
257 }
258
259 listen(fd, 5);
260
261 if(ioctl(fd, FIONBIO, &opt)) {
262 nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking.");
263 close(fd);
264 return -1;
265 }
266
267 return fd;
268 }
269
270 struct service_node *register_service(char *name) {
271 struct service_node *np = ntmalloc(sizeof(service_node));
272 MemCheckR(np, NULL);
273
274 np->name = getsstring(name, strlen(name));
275 if(!np->name) {
276 MemError();
277 ntfree(np);
278 return NULL;
279 }
280
281 np->handlers = NULL;
282 np->next = tree;
283 tree = np;
284
285 return np;
286 }
287
288 struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) {
289 struct handler *hp = ntmalloc(sizeof(handler));
290 MemCheckR(hp, NULL);
291
292 hp->command = getsstring(command, strlen(command));
293 if(!hp->command) {
294 MemError();
295 ntfree(hp);
296 return NULL;
297 }
298
299 hp->function = fp;
300 hp->args = args;
301
302 hp->next = service->handlers;
303 hp->service = service;
304 service->handlers = hp;
305
306 return hp;
307 }
308
309 void 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
325 void deregister_service(struct service_node *service) {
326 struct service_node *sp, *lp = NULL;
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
344 freesstring(service->name);
345
346 ntfree(service);
347 }
348
349 void 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;
356 unsigned int opt = 1;
357
358 if(newfd == -1) {
359 nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!");
360 return;
361 }
362
363 if(ioctl(newfd, FIONBIO, &opt)) {
364 nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking.");
365 close(newfd);
366 return;
367 }
368
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 closed", inet_ntoa(sin.sin_addr));
378 close(newfd);
379 return;
380 }
381
382 temp = (struct sconnect *)ntmalloc(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 ntfree(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
407 void derive_key(unsigned char *out, char *password, char *segment, unsigned char *noncea, unsigned char *nonceb, unsigned char *extra, int extralen) {
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);
417 SHA256_Update(&c, (unsigned char *)":", 1);
418 SHA256_Update(&c, extra, extralen);
419 SHA256_Final(out, &c);
420
421 SHA256_Init(&c);
422 SHA256_Update(&c, out, 32);
423 SHA256_Final(out, &c);
424 }
425
426 int nterfacer_line_event(struct esocket *sock, char *newline) {
427 struct sconnect *socket = sock->tag;
428 char *response, *theirnonceh = NULL, *theirivh = NULL;
429 unsigned char theirnonce[16], theiriv[16];
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 {
438 unsigned char challenge[32];
439 char ivhex[16 * 2 + 1], noncehex[16 * 2 + 1];
440
441 if(!get_entropy(challenge, 32) || !get_entropy(socket->iv, 16)) {
442 nterface_log(nrl, NL_ERROR, "Unable to open challenge/IV entropy bin!");
443 return 1;
444 }
445
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));
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 }
457 int_to_hex(socket->ournonce, noncehex, 16);
458
459 if(esocket_write_line(sock, "%s %s %s", socket->challenge, ivhex, noncehex))
460 return BUF_ERROR;
461 return 0;
462 }
463 break;
464 case SS_VERSIONED:
465 for(response=newline;*response;response++) {
466 if((*response == ' ') && (*(response + 1))) {
467 *response = '\0';
468 theirivh = response + 1;
469 break;
470 }
471 }
472
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))) {
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))) {
495 unsigned char theirkey[32], ourkey[32];
496
497 derive_key(ourkey, socket->permit->password->content, socket->challenge, socket->ournonce, theirnonce, (unsigned char *)"SERVER", 6);
498
499 derive_key(theirkey, socket->permit->password->content, socket->response, theirnonce, socket->ournonce, (unsigned char *)"CLIENT", 6);
500 nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content);
501 socket->status = SS_AUTHENTICATED;
502 switch_buffer_mode(sock, ourkey, socket->iv, theirkey, theiriv);
503
504 if(esocket_write_line(sock, "Oauth"))
505 return BUF_ERROR;
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) {
516 if(reason == RE_SOCKET_ERROR)
517 return BUF_ERROR;
518 if(reason != RE_BAD_LINE) {
519 if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason)))
520 return BUF_ERROR;
521 return 0;
522 } else {
523 return 1;
524 }
525 }
526 break;
527 }
528
529 return 0;
530 }
531
532 int nterfacer_new_rline(char *line, struct esocket *socket, int *number) {
533 char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp;
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
560 if(!*p || !*(p + 1))
561 return RE_BAD_LINE;
562
563 *p = '\0';
564 *number = positive_atoi(sp + 1);
565
566 if((*number < 1) || (*number > INT32_MAX))
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) {
607 parsebuf = (char *)ntmalloc(strlen(pp) + 1);
608 MemCheckR(parsebuf, RE_MEM_ERROR);
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) {
622 ntfree(parsebuf);
623 return RE_TOO_MANY_ARGS;
624 }
625 } else {
626 *newp++ = *pp;
627 }
628 }
629 *newp = '\0';
630 }
631 if(argcount < hl->args) {
632 if(argcount && parsebuf)
633 ntfree(parsebuf);
634 return RE_WRONG_ARG_COUNT;
635 }
636
637 prequest = (struct rline *)ntmalloc(sizeof(struct rline));
638 if(!prequest) {
639 MemError();
640 if(argcount && parsebuf)
641 ntfree(parsebuf);
642 return RE_MEM_ERROR;
643 }
644
645 prequest->service = service;
646 prequest->handler = hl;
647 prequest->buf[0] = '\0';
648 prequest->curpos = prequest->buf;
649 prequest->tag = NULL;
650 prequest->id = *number;
651 prequest->next = rlines;
652 prequest->socket = socket;
653 prequest->callback = NULL;
654
655 rlines = prequest;
656 re = (hl->function)(prequest, argcount, args);
657
658 if(argcount && parsebuf)
659 ntfree(parsebuf);
660
661 return re;
662 }
663
664 void 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)
673 if(li->socket && (li->socket->tag == socket))
674 li->socket = NULL;
675
676 ntfree(socket);
677 }
678
679 int 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
712 int 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;
716 int retval = RE_OK;
717
718 if(li->socket || li->callback) {
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++ = '\\';
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;
732
733 nterfacer_sendcallback(li, error_code, escapedbuf);
734 }
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 }
744 ntfree(li);
745 break;
746 }
747 }
748
749 return retval;
750 }
751
752 int ri_final(struct rline *li) {
753 struct rline *pp, *lp = NULL;
754 int retval = RE_OK;
755
756 if(li->socket) {
757 if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf))
758 retval = RE_SOCKET_ERROR;
759 } else if(li->callback) {
760 nterfacer_sendcallback(li, 0, li->buf);
761 }
762
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 }
770 ntfree(li);
771 break;
772 }
773 }
774
775 return retval;
776 }
777
778 int ping_handler(struct rline *ri, int argc, char **argv) {
779 ri_append(ri, "OK");
780 return ri_final(ri);
781 }
782
783 struct sched_rline {
784 rline rl;
785 int argc;
786 handler *hl;
787 void *schedule;
788 char argv[];
789 };
790
791 static const int XMAXARGS = 50;
792
793 static 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++) {
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
812 void *nterfacer_sendline(char *service, char *command, int argc, char **argv, rline_callback callback, void *tag) {
813 struct service_node *servicep;
814 struct rline *prequest;
815 struct sched_rline *sr;
816 struct handler *hl;
817 int totallen, i;
818 char *buf;
819
820 for(servicep=tree;servicep;servicep=servicep->next)
821 if(!strcmp(servicep->name->content, service))
822 break;
823
824 if(argc > XMAXARGS)
825 Error("nterfacer", ERR_STOP, "Over maximum arguments.");
826
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
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) {
854 MemError();
855 return NULL;
856 }
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;
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;
874 prequest->socket = NULL;
875 prequest->callback = callback;
876
877 prequest->next = rlines;
878 rlines = prequest;
879
880 scheduleoneshot(time(NULL), execrline, sr);
881
882 return (void *)sr;
883 }
884
885 void nterfacer_freeline(void *tag) {
886 struct sched_rline *prequest = tag;
887
888 prequest->rl.callback = NULL;
889 if(prequest->schedule)
890 deleteschedule(prequest->schedule, execrline, NULL);
891 }
892
893 #define MAX_LINES 8192
894
895 /* this bites */
896 static void nterfacer_sendcallback(struct rline *ri, int error, char *buf) {
897 char *lines[MAX_LINES+1];
898 char newbuf[MAX_BUFSIZE+5];
899 char *s, *d, *laststart;
900 int linec = 0;
901
902 for(s=buf,laststart=d=newbuf;*s;s++) {
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;
918 laststart = d;
919 } else {
920 *d++ = *s;
921 }
922 }
923 *d = '\0';
924 lines[linec++] = laststart;
925
926 ri->callback(error, linec, lines, ri->tag);
927 }