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