]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | nterfacer | |
3 | Copyright (C) 2004-2006 Chris Porter. | |
4 | ||
5 | v1.07a | |
6 | - dumb config bug | |
7 | v1.07 | |
8 | - made sure buf[0] = '\0' | |
9 | v1.06 | |
10 | - tidy up | |
11 | v1.05 | |
12 | - added application level ping support | |
13 | v1.04 | |
14 | - modified for new logging system | |
15 | v1.03 | |
16 | - newserv seems to unload this module before the ones that depend on us, | |
17 | so deregister_service now checks to see if it's been freed already | |
18 | v1.02 | |
19 | - moronic bug in linked lists fixed | |
20 | v1.01 | |
21 | - logging | |
22 | */ | |
23 | ||
24 | #include <stdio.h> | |
25 | #include <stdlib.h> | |
26 | #include <stdarg.h> | |
27 | #include <sys/poll.h> | |
28 | #include <sys/types.h> | |
29 | #include <sys/socket.h> | |
30 | #include <errno.h> | |
31 | #include <unistd.h> | |
32 | #include <sys/ioctl.h> | |
33 | #include <netdb.h> | |
34 | #include <string.h> | |
35 | #include <strings.h> | |
36 | ||
37 | #include "../lib/sstring.h" | |
38 | #include "../lib/irc_string.h" | |
39 | #include "../core/config.h" | |
40 | #include "../core/events.h" | |
41 | #include "../lib/version.h" | |
42 | ||
43 | #include "nterfacer.h" | |
44 | #include "logging.h" | |
45 | ||
46 | MODULE_VERSION("$Id") | |
47 | ||
48 | struct service_node *tree = NULL; | |
49 | struct esocket_events nterfacer_events; | |
50 | struct esocket *nterfacer_sock; | |
51 | struct rline *rlines = NULL; | |
52 | unsigned short nterfacer_token = BLANK_TOKEN; | |
53 | struct nterface_auto_log *nrl; | |
54 | ||
55 | struct service_node *ping = NULL; | |
56 | ||
57 | int ping_handler(struct rline *ri, int argc, char **argv); | |
58 | ||
59 | void _init(void) { | |
60 | int loaded; | |
61 | int debug_mode = getcopyconfigitemintpositive("nterfacer", "debug", 0); | |
62 | ||
63 | nrl = nterface_open_log("nterfacer", "logs/nterfacer.log", debug_mode); | |
64 | ||
65 | loaded = load_permits(); | |
66 | if(!loaded) { | |
67 | nterface_log(nrl, NL_ERROR, "No permits loaded successfully."); | |
68 | return; | |
69 | } else { | |
70 | nterface_log(nrl, NL_INFO, "Loaded %d permit%s successfully.", loaded, loaded==1?"":"s"); | |
71 | } | |
72 | ||
73 | nterfacer_events.on_accept = nterfacer_accept_event; | |
74 | nterfacer_events.on_line = nterfacer_line_event; | |
75 | nterfacer_events.on_disconnect = NULL; | |
76 | ||
77 | nterfacer_token = esocket_token(); | |
78 | ||
79 | ping = register_service("nterfacer"); | |
80 | if(!ping) { | |
81 | MemError(); | |
82 | } else { | |
83 | register_handler(ping, "ping", 0, ping_handler); | |
84 | } | |
85 | ||
86 | accept_fd = setup_listening_socket(); | |
87 | if(accept_fd == -1) { | |
88 | nterface_log(nrl, NL_ERROR, "Unable to setup listening socket!"); | |
89 | } else { | |
90 | nterfacer_sock = esocket_add(accept_fd, ESOCKET_UNIX_DOMAIN, &nterfacer_events, nterfacer_token); | |
91 | } | |
92 | ||
93 | /* the main unix domain socket must NOT have a disconnect event. */ | |
94 | nterfacer_events.on_disconnect = nterfacer_disconnect_event; | |
95 | } | |
96 | ||
97 | void free_handler(struct handler *hp) { | |
98 | freesstring(hp->command); | |
99 | free(hp); | |
100 | } | |
101 | ||
102 | void free_handlers(struct service_node *tp) { | |
103 | struct handler *hp, *lp; | |
104 | ||
105 | for(hp=tp->handlers;hp;) { | |
106 | lp = hp; | |
107 | hp = hp->next; | |
108 | free_handler(lp); | |
109 | } | |
110 | ||
111 | tp->handlers = NULL; | |
112 | } | |
113 | ||
114 | void _fini(void) { | |
115 | struct service_node *tp, *lp; | |
116 | int i; | |
117 | ||
118 | if(ping) | |
119 | deregister_service(ping); | |
120 | ||
121 | for(tp=tree;tp;) { | |
122 | lp = tp; | |
123 | tp = tp->next; | |
124 | free_handlers(lp); | |
125 | free(lp); | |
126 | } | |
127 | tree = NULL; | |
128 | ||
129 | if((accept_fd != -1) && nterfacer_sock) { | |
130 | esocket_clean_by_token(nterfacer_token); | |
131 | nterfacer_sock = NULL; | |
132 | accept_fd = -1; | |
133 | } | |
134 | ||
135 | if(permits && permit_count) { | |
136 | for(i=0;i<permit_count;i++) { | |
137 | freesstring(permits[i].hostname); | |
138 | freesstring(permits[i].password); | |
139 | } | |
140 | free(permits); | |
141 | permit_count = 0; | |
142 | permits = NULL; | |
143 | } | |
144 | ||
145 | nrl = nterface_close_log(nrl); | |
146 | } | |
147 | ||
148 | int load_permits(void) { | |
149 | int lines, loaded_lines = 0, i, j; | |
150 | struct permitted *new_permits, *resized, *item; | |
151 | struct hostent *host; | |
152 | char buf[50]; | |
153 | ||
154 | lines = getcopyconfigitemintpositive("nterfacer", "permits", 0); | |
155 | if(lines < 1) { | |
156 | nterface_log(nrl, NL_ERROR, "No permits found in config file."); | |
157 | return 0; | |
158 | } | |
159 | nterface_log(nrl, NL_INFO, "Loading %d permit%s from config file", lines, lines==1?"":"s"); | |
160 | ||
161 | new_permits = calloc(lines, sizeof(struct permitted)); | |
162 | item = new_permits; | |
163 | ||
164 | for(i=1;i<=lines;i++) { | |
165 | snprintf(buf, sizeof(buf), "hostname%d", i); | |
166 | item->hostname = getcopyconfigitem("nterfacer", buf, "", 100); | |
167 | if(!item->hostname) { | |
168 | nterface_log(nrl, NL_ERROR, "No hostname found for item %d.", i); | |
169 | continue; | |
170 | } | |
171 | ||
172 | host = gethostbyname(item->hostname->content); | |
173 | if (!host) { | |
174 | nterface_log(nrl, NL_WARNING, "Couldn't resolve hostname: %s (item %d).", item->hostname->content, i); | |
175 | freesstring(item->hostname); | |
176 | continue; | |
177 | } | |
178 | ||
179 | item->ihost = (*(struct in_addr *)host->h_addr).s_addr; | |
180 | for(j=0;j<loaded_lines;j++) { | |
181 | if(new_permits[j].ihost == item->ihost) { | |
182 | nterface_log(nrl, NL_WARNING, "Host with items %d and %d is identical, dropping item %d.", j + 1, i, i); | |
183 | host = NULL; | |
184 | } | |
185 | } | |
186 | ||
187 | if(!host) { | |
188 | freesstring(item->hostname); | |
189 | continue; | |
190 | } | |
191 | ||
192 | snprintf(buf, sizeof(buf), "password%d", i); | |
193 | item->password = getcopyconfigitem("nterfacer", buf, "", 100); | |
194 | if(!item->password) { | |
195 | nterface_log(nrl, NL_ERROR, "No password found for item %d.", item->hostname->content, i); | |
196 | freesstring(item->hostname); | |
197 | continue; | |
198 | } | |
199 | ||
200 | nterface_log(nrl, NL_DEBUG, "Loaded permit, hostname: %s.", item->hostname->content); | |
201 | ||
202 | item++; | |
203 | loaded_lines++; | |
204 | } | |
205 | ||
206 | if(!loaded_lines) { | |
207 | free(new_permits); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | resized = realloc(new_permits, sizeof(struct permitted) * loaded_lines); | |
212 | if(!resized) { | |
213 | MemError(); | |
214 | free(new_permits); | |
215 | return 0; | |
216 | } | |
217 | permits = resized; | |
218 | permit_count = loaded_lines; | |
219 | ||
220 | return permit_count; | |
221 | } | |
222 | ||
223 | int setup_listening_socket(void) { | |
224 | struct sockaddr_in sin; | |
225 | int fd; | |
226 | unsigned int opt = 1; | |
227 | ||
228 | fd = socket(AF_INET, SOCK_STREAM, 0); | |
229 | ||
230 | /* also shamelessly ripped from proxyscan */ | |
231 | if(fd == -1) { | |
232 | nterface_log(nrl, NL_ERROR, "Unable to open listening socket (%d).", errno); | |
233 | return -1; | |
234 | } | |
235 | ||
236 | if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char *) &opt, sizeof(opt)) != 0) { | |
237 | nterface_log(nrl, NL_ERROR, "Unable to set SO_REUSEADDR on listen socket."); | |
238 | return -1; | |
239 | } | |
240 | ||
241 | /* Initialiase the addresses */ | |
242 | memset(&sin, 0, sizeof(sin)); | |
243 | sin.sin_family = AF_INET; | |
244 | sin.sin_port = htons(getcopyconfigitemintpositive("nterfacer", "port", NTERFACER_PORT)); | |
245 | ||
246 | if(bind(fd, (struct sockaddr *) &sin, sizeof(sin))) { | |
247 | nterface_log(nrl, NL_ERROR, "Unable to bind listen socket (%d).", errno); | |
248 | return -1; | |
249 | } | |
250 | ||
251 | listen(fd, 5); | |
252 | ||
253 | if(ioctl(fd, FIONBIO, &opt)) { | |
254 | nterface_log(nrl, NL_ERROR, "Unable to set listen socket non-blocking."); | |
255 | return -1; | |
256 | } | |
257 | ||
258 | return fd; | |
259 | } | |
260 | ||
261 | struct service_node *register_service(char *name) { | |
262 | struct service_node *np = malloc(sizeof(service_node)); | |
263 | MemCheckR(np, NULL); | |
264 | ||
265 | np->name = getsstring(name, strlen(name)); | |
266 | if(!np->name) { | |
267 | MemError(); | |
268 | free(np); | |
269 | return NULL; | |
270 | } | |
271 | ||
272 | np->handlers = NULL; | |
273 | np->next = tree; | |
274 | tree = np; | |
275 | ||
276 | return np; | |
277 | } | |
278 | ||
279 | struct handler *register_handler(struct service_node *service, char *command, int args, handler_function fp) { | |
280 | struct handler *hp = malloc(sizeof(handler)); | |
281 | MemCheckR(hp, NULL); | |
282 | ||
283 | hp->command = getsstring(command, strlen(command)); | |
284 | if(!hp->command) { | |
285 | MemError(); | |
286 | free(hp); | |
287 | return NULL; | |
288 | } | |
289 | ||
290 | hp->function = fp; | |
291 | hp->args = args; | |
292 | ||
293 | hp->next = service->handlers; | |
294 | hp->service = service; | |
295 | service->handlers = hp; | |
296 | ||
297 | return hp; | |
298 | } | |
299 | ||
300 | void deregister_handler(struct handler *hl) { | |
301 | struct service_node *service = (struct service_node *)hl->service; | |
302 | struct handler *np, *lp = NULL; | |
303 | for(np=service->handlers;np;lp=np,np=np->next) { | |
304 | if(hl == np) { | |
305 | if(lp) { | |
306 | lp->next = np->next; | |
307 | } else { | |
308 | service->handlers = np->next; | |
309 | } | |
310 | free_handler(np); | |
311 | return; | |
312 | } | |
313 | } | |
314 | } | |
315 | ||
316 | void deregister_service(struct service_node *service) { | |
317 | struct service_node *sp, *lp = NULL; | |
318 | struct rline *li, *pi = NULL; | |
319 | ||
320 | for(sp=tree;sp;lp=sp,sp=sp->next) { | |
321 | if(sp == service) { | |
322 | if(lp) { | |
323 | lp->next = sp->next; | |
324 | } else { | |
325 | tree = sp->next; | |
326 | } | |
327 | break; | |
328 | } | |
329 | } | |
330 | ||
331 | if(!sp) /* already freed */ | |
332 | return; | |
333 | ||
334 | free_handlers(service); | |
335 | ||
336 | for(li=rlines;li;) { | |
337 | if(li->service == service) { | |
338 | if(pi) { | |
339 | pi->next = li->next; | |
340 | free(li); | |
341 | li = pi->next; | |
342 | } else { | |
343 | rlines = li->next; | |
344 | free(li); | |
345 | li = rlines; | |
346 | } | |
347 | } else { | |
348 | pi=li,li=li->next; | |
349 | } | |
350 | } | |
351 | freesstring(service->name); | |
352 | ||
353 | free(service); | |
354 | } | |
355 | ||
356 | void nterfacer_accept_event(struct esocket *socket) { | |
357 | struct sockaddr_in sin; | |
358 | unsigned int addrsize = sizeof(sin); | |
359 | int newfd = accept(socket->fd, (struct sockaddr *)&sin, &addrsize), i; | |
360 | struct sconnect *temp; | |
361 | struct permitted *item = NULL; | |
362 | struct esocket *newsocket; | |
363 | unsigned int opt = 1; | |
364 | ||
365 | if(newfd == -1) { | |
366 | nterface_log(nrl, NL_WARNING, "Unable to accept nterfacer fd!"); | |
367 | return; | |
368 | } | |
369 | ||
370 | if(ioctl(newfd, FIONBIO, &opt)) { | |
371 | nterface_log(nrl, NL_ERROR, "Unable to set accepted socket non-blocking."); | |
372 | return; | |
373 | } | |
374 | ||
375 | for(i=0;i<permit_count;i++) { | |
376 | if(permits[i].ihost == sin.sin_addr.s_addr) { | |
377 | item = &permits[i]; | |
378 | break; | |
379 | } | |
380 | } | |
381 | ||
382 | if(!item) { | |
383 | nterface_log(nrl, NL_INFO, "Unauthorised connection from: %s", IPtostr(htonl(sin.sin_addr.s_addr))); | |
384 | close(newfd); | |
385 | return; | |
386 | } | |
387 | ||
388 | temp = (struct sconnect *)malloc(sizeof(struct sconnect)); | |
389 | if(!temp) { | |
390 | MemError(); | |
391 | close(newfd); | |
392 | return; | |
393 | } | |
394 | ||
395 | /* do checks on hostname first */ | |
396 | ||
397 | newsocket = esocket_add(newfd, ESOCKET_UNIX_DOMAIN_CONNECTED, &nterfacer_events, nterfacer_token); | |
398 | if(!newsocket) { | |
399 | free(temp); | |
400 | close(newfd); | |
401 | return; | |
402 | } | |
403 | newsocket->tag = temp; | |
404 | ||
405 | nterface_log(nrl, NL_INFO, "New connection from %s.", item->hostname->content); | |
406 | ||
407 | temp->status = SS_IDLE; | |
408 | temp->permit = item; | |
409 | ||
410 | esocket_write_line(newsocket, "nterfacer " PROTOCOL_VERSION); | |
411 | } | |
412 | ||
413 | int nterfacer_line_event(struct esocket *sock, char *newline) { | |
414 | struct sconnect *socket = sock->tag; | |
415 | char *response, *theirnonceh = NULL; | |
416 | unsigned char theirnonce[NONCE_LEN]; | |
417 | int number, reason; | |
418 | ||
419 | switch(socket->status) { | |
420 | case SS_IDLE: | |
421 | if(strcasecmp(newline, ANTI_FULL_VERSION)) { | |
422 | nterface_log(nrl, NL_INFO, "Protocol mismatch from %s: %s", socket->permit->hostname->content, newline); | |
423 | return 1; | |
424 | } else { | |
425 | char *hex, hexbuf[NONCE_LEN * 2 + 1]; /* Vekoma mAD HoUSe */ | |
426 | ||
427 | hex = get_random_hex(); | |
428 | if(!hex) { | |
429 | nterface_log(nrl, NL_ERROR, "Unable to open challenge entropy bin!"); | |
430 | return 1; | |
431 | } | |
432 | ||
433 | memcpy(socket->response, challenge_response(hex, socket->permit->password->content), sizeof(socket->response)); | |
434 | socket->response[sizeof(socket->response) - 1] = '\0'; /* just in case */ | |
435 | ||
436 | socket->status = SS_VERSIONED; | |
437 | if(!generate_nonce(socket->ournonce, 1)) { | |
438 | nterface_log(nrl, NL_ERROR, "Unable to generate nonce!"); | |
439 | return 1; | |
440 | } | |
441 | ||
442 | if(esocket_write_line(sock, "%s %s", hex, int_to_hex(socket->ournonce, hexbuf, NONCE_LEN))) | |
443 | return BUF_ERROR; | |
444 | return 0; | |
445 | } | |
446 | break; | |
447 | case SS_VERSIONED: | |
448 | for(response=newline;*response;response++) { | |
449 | if((*response == ' ') && (*(response + 1))) { | |
450 | *response = '\0'; | |
451 | theirnonceh = response + 1; | |
452 | break; | |
453 | } | |
454 | } | |
455 | ||
456 | if(!theirnonceh || (strlen(theirnonceh) != 32) || !hex_to_int(theirnonceh, theirnonce, sizeof(theirnonce))) { | |
457 | nterface_log(nrl, NL_INFO, "Protocol error drop: %s", socket->permit->hostname->content); | |
458 | return 1; | |
459 | } | |
460 | ||
461 | if(!memcmp(socket->ournonce, theirnonce, sizeof(theirnonce))) { | |
462 | nterface_log(nrl, NL_INFO, "Bad nonce drop: %s", socket->permit->hostname->content); | |
463 | return 1; | |
464 | } | |
465 | ||
466 | if(!strncasecmp(newline, socket->response, sizeof(socket->response))) { | |
467 | int ret; | |
468 | ||
469 | nterface_log(nrl, NL_INFO, "Authed: %s", socket->permit->hostname->content); | |
470 | socket->status = SS_AUTHENTICATED; | |
471 | ret = esocket_write_line(sock, "Oauth"); | |
472 | if(!ret) { | |
473 | switch_buffer_mode(sock, socket->permit->password->content, socket->ournonce, theirnonce); | |
474 | } else { | |
475 | return BUF_ERROR; | |
476 | } | |
477 | } else { | |
478 | nterface_log(nrl, NL_INFO, "Bad CR drop: %s", socket->permit->hostname->content); | |
479 | ||
480 | return 1; | |
481 | } | |
482 | break; | |
483 | case SS_AUTHENTICATED: | |
484 | nterface_log(nrl, NL_INFO|NL_LOG_ONLY, "L(%s): %s", socket->permit->hostname->content, newline); | |
485 | reason = nterfacer_new_rline(newline, sock, &number); | |
486 | if(reason) { | |
487 | if(reason == RE_SOCKET_ERROR) | |
488 | return BUF_ERROR; | |
489 | if(reason != RE_BAD_LINE) { | |
490 | if(esocket_write_line(sock, "%d,E%d,%s", number, reason, request_error(reason))) | |
491 | return BUF_ERROR; | |
492 | return 0; | |
493 | } else { | |
494 | return 1; | |
495 | } | |
496 | } | |
497 | break; | |
498 | } | |
499 | ||
500 | return 0; | |
501 | } | |
502 | ||
503 | int nterfacer_new_rline(char *line, struct esocket *socket, int *number) { | |
504 | char *sp, *p, *parsebuf = NULL, *pp, commandbuf[MAX_BUFSIZE], *args[MAX_ARGS], *newp; | |
505 | int argcount; | |
506 | struct service_node *service; | |
507 | struct rline *prequest; | |
508 | struct handler *hl; | |
509 | int re; | |
510 | ||
511 | if(!line || !line[0] || (line[0] == ',')) | |
512 | return 0; | |
513 | ||
514 | for(sp=line;*sp;sp++) | |
515 | if(*sp == ',') | |
516 | break; | |
517 | ||
518 | if(!*sp || !*(sp + 1)) | |
519 | return RE_BAD_LINE; | |
520 | ||
521 | *sp = '\0'; | |
522 | ||
523 | for(service=tree;service;service=service->next) | |
524 | if(!strcmp(service->name->content, line)) | |
525 | break; | |
526 | ||
527 | for(p=sp+1;*p;p++) | |
528 | if(*p == ',') | |
529 | break; | |
530 | ||
531 | if(!*p || !(p + 1)) | |
532 | return RE_BAD_LINE; | |
533 | ||
534 | *p = '\0'; | |
535 | *number = positive_atoi(sp + 1); | |
536 | ||
537 | if((*number < 1) || (*number > 0xffff)) | |
538 | return RE_BAD_LINE; | |
539 | ||
540 | if (!service) { | |
541 | nterface_log(nrl, NL_DEBUG, "Unable to find service: %s", line); | |
542 | return RE_SERVICER_NOT_FOUND; | |
543 | } | |
544 | ||
545 | newp = commandbuf; | |
546 | ||
547 | for(pp=p+1;*pp;pp++) { | |
548 | if((*pp == '\\') && *(pp + 1)) { | |
549 | if(*(pp + 1) == ',') { | |
550 | *newp++ = ','; | |
551 | } else if(*(pp + 1) == '\\') { | |
552 | *newp++ = '\\'; | |
553 | } | |
554 | pp++; | |
555 | } else if(*pp == ',') { | |
556 | break; | |
557 | } else { | |
558 | *newp++ = *pp; | |
559 | } | |
560 | } | |
561 | ||
562 | if(*pp == '\0') { /* if we're at the end already, we have no arguments */ | |
563 | argcount = 0; | |
564 | } else { | |
565 | argcount = 1; /* we have a comma, so we have one already */ | |
566 | } | |
567 | ||
568 | *newp = '\0'; | |
569 | ||
570 | for(hl=service->handlers;hl;hl=hl->next) | |
571 | if(!strncmp(hl->command->content, commandbuf, sizeof(commandbuf))) | |
572 | break; | |
573 | ||
574 | if(!hl) | |
575 | return RE_COMMAND_NOT_FOUND; | |
576 | ||
577 | if(argcount) { | |
578 | parsebuf = (char *)malloc(strlen(pp) + 1); | |
579 | MemCheckR(parsebuf, RE_MEM_ERROR); | |
580 | newp = parsebuf; | |
581 | ||
582 | for(newp=args[0]=parsebuf,pp++;*pp;pp++) { | |
583 | if((*pp == '\\') && *(pp + 1)) { | |
584 | if(*(pp + 1) == ',') { | |
585 | *newp++ = ','; | |
586 | } else if(*(pp + 1) == '\\') { | |
587 | *newp++ = '\\'; | |
588 | } | |
589 | pp++; | |
590 | } else if(*pp == ',') { | |
591 | *newp++ = '\0'; | |
592 | args[argcount++] = newp; | |
593 | if(argcount > MAX_ARGS) { | |
594 | free(parsebuf); | |
595 | return RE_TOO_MANY_ARGS; | |
596 | } | |
597 | } else { | |
598 | *newp++ = *pp; | |
599 | } | |
600 | } | |
601 | *newp = '\0'; | |
602 | } | |
603 | if(argcount < hl->args) { | |
604 | if(argcount && parsebuf) | |
605 | free(parsebuf); | |
606 | return RE_WRONG_ARG_COUNT; | |
607 | } | |
608 | ||
609 | prequest = (struct rline *)malloc(sizeof(struct rline)); | |
610 | if(!prequest) { | |
611 | MemError(); | |
612 | if(argcount && parsebuf) | |
613 | free(parsebuf); | |
614 | return RE_MEM_ERROR; | |
615 | } | |
616 | ||
617 | prequest->service = service; | |
618 | prequest->handler = hl; | |
619 | prequest->buf[0] = '\0'; | |
620 | prequest->curpos = prequest->buf; | |
621 | prequest->tag = NULL; | |
622 | prequest->id = *number; | |
623 | prequest->next = rlines; | |
624 | prequest->socket = socket; | |
625 | ||
626 | rlines = prequest; | |
627 | re = (hl->function)(prequest, argcount, args); | |
628 | ||
629 | if(argcount && parsebuf) | |
630 | free(parsebuf); | |
631 | ||
632 | return re; | |
633 | } | |
634 | ||
635 | void nterfacer_disconnect_event(struct esocket *sock) { | |
636 | struct sconnect *socket = sock->tag; | |
637 | struct rline *li; | |
638 | /* not tested */ | |
639 | ||
640 | nterface_log(nrl, NL_INFO, "Disconnected from %s.", socket->permit->hostname->content); | |
641 | ||
642 | /* not tested */ | |
643 | for(li=rlines;li;li=li->next) | |
644 | if(li->socket->tag == socket) | |
645 | li->socket = NULL; | |
646 | ||
647 | free(socket); | |
648 | } | |
649 | ||
650 | int ri_append(struct rline *li, char *format, ...) { | |
651 | char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp; | |
652 | int sizeleft = sizeof(li->buf) - (li->curpos - li->buf); | |
653 | va_list ap; | |
654 | ||
655 | va_start(ap, format); | |
656 | ||
657 | if(vsnprintf(buf, sizeof(buf), format, ap) >= sizeof(buf)) { | |
658 | va_end(ap); | |
659 | return BF_OVER; | |
660 | } | |
661 | ||
662 | va_end(ap); | |
663 | ||
664 | for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++) | |
665 | if((*p == ',') || (*p == '\\')) | |
666 | *tp++ = '\\'; | |
667 | ||
668 | if(sizeleft > 0) { | |
669 | if(li->curpos == li->buf) { | |
670 | li->curpos+=snprintf(li->curpos, sizeleft, "%s", escapedbuf); | |
671 | } else { | |
672 | li->curpos+=snprintf(li->curpos, sizeleft, ",%s", escapedbuf); | |
673 | } | |
674 | } | |
675 | ||
676 | if(sizeof(li->buf) - (li->curpos - li->buf) > 0) { | |
677 | return BF_OK; | |
678 | } else { | |
679 | return BF_OVER; | |
680 | } | |
681 | } | |
682 | ||
683 | int ri_error(struct rline *li, int error_code, char *format, ...) { | |
684 | char buf[MAX_BUFSIZE], escapedbuf[MAX_BUFSIZE * 2 + 1], *p, *tp; | |
685 | struct rline *pp, *lp = NULL; | |
686 | va_list ap; | |
687 | int retval = RE_OK; | |
688 | ||
689 | if(li->socket) { | |
690 | va_start(ap, format); | |
691 | vsnprintf(buf, sizeof(buf), format, ap); | |
692 | va_end(ap); | |
693 | ||
694 | for(tp=escapedbuf,p=buf;*p||(*tp='\0');*tp++=*p++) | |
695 | if((*p == ',') || (*p == '\\')) | |
696 | *tp++ = '\\'; | |
697 | ||
698 | if(esocket_write_line(li->socket, "%d,OE%d,%s", li->id, error_code, escapedbuf)) | |
699 | retval = RE_SOCKET_ERROR; | |
700 | } | |
701 | ||
702 | for(pp=rlines;pp;lp=pp,pp=pp->next) { | |
703 | if(pp == li) { | |
704 | if(lp) { | |
705 | lp->next = li->next; | |
706 | } else { | |
707 | rlines = li->next; | |
708 | } | |
709 | free(li); | |
710 | break; | |
711 | } | |
712 | } | |
713 | ||
714 | return retval; | |
715 | } | |
716 | ||
717 | int ri_final(struct rline *li) { | |
718 | struct rline *pp, *lp = NULL; | |
719 | int retval = RE_OK; | |
720 | ||
721 | if(li->socket) | |
722 | if(esocket_write_line(li->socket, "%d,OO%s", li->id, li->buf)) | |
723 | retval = RE_SOCKET_ERROR; | |
724 | ||
725 | for(pp=rlines;pp;lp=pp,pp=pp->next) { | |
726 | if(pp == li) { | |
727 | if(lp) { | |
728 | lp->next = li->next; | |
729 | } else { | |
730 | rlines = li->next; | |
731 | } | |
732 | free(li); | |
733 | break; | |
734 | } | |
735 | } | |
736 | ||
737 | return retval; | |
738 | } | |
739 | ||
740 | int ping_handler(struct rline *ri, int argc, char **argv) { | |
741 | ri_append(ri, "OK"); | |
742 | return ri_final(ri); | |
743 | } |