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