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