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