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