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