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