]> jfr.im git - solanum.git/blame - authd/providers/opm.c
Add HTTP CONNECT proxy scanning
[solanum.git] / authd / providers / opm.c
CommitLineData
4e85459a
EM
1/* authd/providers/opm.c - small open proxy monitor
2 * Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice is present in all copies.
7 *
8 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
12 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
14 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
16 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
17 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
18 * POSSIBILITY OF SUCH DAMAGE.
19 */
20
468ef960 21#include "rb_lib.h"
4e85459a
EM
22#include "stdinc.h"
23#include "setup.h"
24#include "authd.h"
25#include "notice.h"
26#include "provider.h"
27
28#define OPM_READSIZE 128
29
30typedef enum protocol_t
31{
32 PROTO_NONE,
33 PROTO_SOCKS4,
34 PROTO_SOCKS5,
fabe8b94 35 PROTO_HTTP_CONNECT,
4e85459a
EM
36} protocol_t;
37
38struct opm_lookup
39{
40 rb_dlink_list scans; /* List of scans */
41};
42
43struct opm_proxy
44{
1de169a2 45 char note[16];
4e85459a
EM
46 protocol_t proto;
47 uint16_t port;
1de169a2
EM
48
49 rb_dlink_node node;
4e85459a
EM
50};
51
52struct opm_scan
53{
54 struct auth_client *auth;
55
56 struct opm_proxy *proxy;
57 rb_fde_t *F; /* fd for scan */
58
59 rb_dlink_node node;
60};
61
62struct opm_listener
63{
64 char ip[HOSTIPLEN];
65 uint16_t port;
766d4ffc 66 struct rb_sockaddr_storage addr;
4e85459a
EM
67 rb_fde_t *F;
68};
69
70/* Proxies that we scan for */
1de169a2 71static rb_dlink_list proxy_scanners;
4e85459a
EM
72
73static ACCB accept_opm;
74static PF read_opm_reply;
75
76static CNCB socks4_connected;
77static CNCB socks5_connected;
78
79static void opm_cancel(struct auth_client *auth);
4deb334f 80static bool create_listener(const char *ip, uint16_t port);
4e85459a 81
8275e270 82static int opm_timeout = 10;
4e85459a
EM
83static bool opm_enable = false;
84
85#define LISTEN_IPV4 0
86#define LISTEN_IPV6 1
87
88/* IPv4 and IPv6 */
89static struct opm_listener listeners[2];
90
1de169a2 91static inline protocol_t
fabe8b94 92get_protocol_from_string(const char *str)
1de169a2 93{
fabe8b94 94 if(strcasecmp(str, "socks4") == 0)
1de169a2 95 return PROTO_SOCKS4;
fabe8b94 96 else if(strcasecmp(str, "socks5") == 0)
1de169a2 97 return PROTO_SOCKS5;
fabe8b94
EM
98 else if(strcasecmp(str, "httpconnect") == 0)
99 return PROTO_HTTP_CONNECT;
1de169a2
EM
100 else
101 return PROTO_NONE;
102}
103
104static inline struct opm_proxy *
105find_proxy_scanner(protocol_t proto, uint16_t port)
106{
107 rb_dlink_node *ptr;
108
109 RB_DLINK_FOREACH(ptr, proxy_scanners.head)
110 {
111 struct opm_proxy *proxy = ptr->data;
112
113 if(proxy->proto == proto && proxy->port == port)
114 return proxy;
115 }
116
117 return NULL;
118}
4e85459a
EM
119
120static void
121read_opm_reply(rb_fde_t *F, void *data)
122{
1de169a2 123 rb_dlink_node *ptr;
4e85459a
EM
124 struct auth_client *auth = data;
125 struct opm_lookup *lookup;
126 char readbuf[OPM_READSIZE];
127 ssize_t len;
128
129 if(auth == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
130 {
131 rb_close(F);
132 return;
133 }
134
135 if((len = rb_read(F, readbuf, sizeof(readbuf))) < 0 && rb_ignore_errno(errno))
136 {
137 rb_setselect(F, RB_SELECT_READ, read_opm_reply, auth);
138 return;
139 }
140 else if(len <= 0)
141 {
142 /* Dead */
143 rb_close(F);
144 return;
145 }
146
1de169a2 147 RB_DLINK_FOREACH(ptr, proxy_scanners.head)
4e85459a 148 {
1de169a2
EM
149 struct opm_proxy *proxy = ptr->data;
150
fabe8b94 151 if(strncmp(proxy->note, readbuf, strlen(proxy->note)) == 0)
8860e46a
EM
152 {
153 rb_dlink_node *ptr, *nptr;
154
155 /* Cancel outstanding lookups */
156 RB_DLINK_FOREACH_SAFE(ptr, nptr, lookup->scans.head)
157 {
158 struct opm_scan *scan = ptr->data;
159
160 rb_close(scan->F);
161 rb_free(scan);
162 }
163
164 /* No longer needed, client is going away */
165 rb_free(lookup);
166
167 reject_client(auth, PROVIDER_OPM, readbuf, "Open proxy detected");
168 break;
169 }
4e85459a
EM
170 }
171
172 rb_close(F);
173}
174
175static void
176accept_opm(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t len, void *data)
177{
178 struct auth_client *auth = NULL;
179 struct opm_listener *listener = data;
272af6a5 180 struct rb_sockaddr_storage localaddr;
4e85459a
EM
181 unsigned int llen = sizeof(struct rb_sockaddr_storage);
182 rb_dictionary_iter iter;
183
184 if(status != 0 || listener == NULL)
185 {
186 rb_close(F);
187 return;
188 }
189
272af6a5 190 if(getsockname(rb_get_fd(F), (struct sockaddr *)&localaddr, &llen))
4e85459a
EM
191 {
192 /* This can happen if the client goes away after accept */
193 rb_close(F);
194 return;
195 }
196
197 /* Correlate connection with client(s) */
198 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
199 {
272af6a5 200 if(GET_SS_FAMILY(&auth->c_addr) != GET_SS_FAMILY(&localaddr))
4e85459a
EM
201 continue;
202
203 /* Compare the addresses */
272af6a5 204 switch(GET_SS_FAMILY(&localaddr))
4e85459a
EM
205 {
206 case AF_INET:
207 {
272af6a5 208 struct sockaddr_in *s = (struct sockaddr_in *)&localaddr, *c = (struct sockaddr_in *)&auth->c_addr;
4e85459a
EM
209
210 if(s->sin_addr.s_addr == c->sin_addr.s_addr)
211 {
212 /* Match... check if it's real */
213 rb_setselect(F, RB_SELECT_READ, read_opm_reply, auth);
214 return;
215 }
216 break;
217 }
218#ifdef RB_IPV6
219 case AF_INET6:
220 {
272af6a5 221 struct sockaddr_in6 *s = (struct sockaddr_in6 *)&localaddr, *c = (struct sockaddr_in6 *)&auth->c_addr;
4e85459a 222
d86692fa 223 if(IN6_ARE_ADDR_EQUAL(&s->sin6_addr, &c->sin6_addr))
4e85459a
EM
224 {
225 rb_setselect(F, RB_SELECT_READ, read_opm_reply, auth);
226 return;
227 }
228 break;
229 }
230#endif
231 default:
232 warn_opers(L_CRIT, "OPM: unknown address type in listen function");
233 exit(EX_PROVIDER_ERROR);
234 }
235 }
236
237 /* We don't care about the socket if we get here */
238 rb_close(F);
239}
240
241/* Scanners */
242
243static void
244socks4_connected(rb_fde_t *F, int error, void *data)
245{
246 struct opm_scan *scan = data;
247 struct opm_lookup *lookup;
248 struct auth_client *auth;
249 uint8_t sendbuf[9]; /* Size we're building */
250 uint8_t *c = sendbuf;
251 ssize_t ret;
252
253 if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
254 return;
255
256 if(error || !opm_enable)
257 goto end;
258
259 memcpy(c, "\x04\x01", 2); c += 2; /* Socks version 4, connect command */
260
261 switch(GET_SS_FAMILY(&auth->c_addr))
262 {
263 case AF_INET:
264 if(listeners[LISTEN_IPV4].F == NULL)
265 /* They cannot respond to us */
266 goto end;
267
268 memcpy(c, &(((struct sockaddr_in *)&listeners[LISTEN_IPV4].addr)->sin_port), 2); c += 2; /* Port */
269 memcpy(c, &(((struct sockaddr_in *)&listeners[LISTEN_IPV4].addr)->sin_addr.s_addr), 4); c += 4; /* Address */
270 break;
271#ifdef RB_IPV6
272 case AF_INET6:
273 /* socks4 doesn't support IPv6 */
274#endif
275 default:
276 goto end;
277 }
278
279 *c = '\x00'; /* No userid */
280
281 /* Send header */
282 if(rb_write(scan->F, sendbuf, sizeof(sendbuf)) < 0)
283 goto end;
284
285 /* Send note */
286 if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) < 0)
287 goto end;
288
289end:
290 rb_close(scan->F);
291 rb_dlinkDelete(&scan->node, &lookup->scans);
292 rb_free(scan);
293}
294
295static void
296socks5_connected(rb_fde_t *F, int error, void *data)
297{
298 struct opm_scan *scan = data;
fabe8b94 299 struct opm_lookup *lookup;
4e85459a
EM
300 struct opm_listener *listener;
301 struct auth_client *auth;
302 uint8_t sendbuf[25]; /* Size we're building */
303 uint8_t *c = sendbuf;
304 ssize_t ret;
305
306 if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
307 return;
308
309 if(error || !opm_enable)
310 goto end;
311
312 /* Build the version header and socks request
fabe8b94 313 * version header (3 bytes): version, number of auth methods, auth type (0 for none)
4e85459a
EM
314 * connect req (3 bytes): version, command (1 = connect), reserved (0)
315 */
316 memcpy(c, "\x05\x01\x00\x05\x01\x00", 6); c += 6;
317
318 switch(GET_SS_FAMILY(&auth->c_addr))
319 {
320 case AF_INET:
321 listener = &listeners[LISTEN_IPV4];
322 if(!listener->F)
323 goto end;
324
325 *(c++) = '\x01'; /* Address type (1 = IPv4) */
326 memcpy(c, &(((struct sockaddr_in *)&listener->addr)->sin_addr.s_addr), 4); c += 4;/* Address */
327 memcpy(c, &(((struct sockaddr_in *)&listener->addr)->sin_port), 2); c += 2; /* Port */
328 break;
329#ifdef RB_IPV6
330 case AF_INET6:
331 listener = &listeners[LISTEN_IPV6];
332 if(!listener->F)
333 goto end;
334
335 *(c++) = '\x04'; /* Address type (4 = IPv6) */
336 memcpy(c, ((struct sockaddr_in6 *)&listener->addr)->sin6_addr.s6_addr, 16); c += 16; /* Address */
337 memcpy(c, &(((struct sockaddr_in6 *)&listener->addr)->sin6_port), 2); c += 2; /* Port */
338 break;
339#endif
340 default:
341 goto end;
342 }
343
344 /* Send header */
345 if(rb_write(scan->F, sendbuf, (size_t)(sendbuf - c)) <= 0)
346 goto end;
347
348 /* Now the note in a separate write */
349 if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) <= 0)
350 goto end;
351
352end:
353 rb_close(scan->F);
354 rb_dlinkDelete(&scan->node, &lookup->scans);
355 rb_free(scan);
356}
357
fabe8b94
EM
358static void
359http_connect_connected(rb_fde_t *F, int error, void *data)
360{
361 struct opm_scan *scan = data;
362 struct opm_lookup *lookup;
363 struct opm_listener *listener;
364 struct auth_client *auth;
365 char sendbuf[128]; /* A bit bigger than we need but better safe than sorry */
366 char *c = sendbuf;
367 ssize_t ret;
368
369 if(scan == NULL || (auth = scan->auth) == NULL || (lookup = auth->data[PROVIDER_OPM]) == NULL)
370 return;
371
372 if(error || !opm_enable)
373 goto end;
374
375 switch(GET_SS_FAMILY(&auth->c_addr))
376 {
377 case AF_INET:
378 listener = &listeners[LISTEN_IPV4];
379 if(!listener->F)
380 goto end;
381 break;
382#ifdef RB_IPV6
383 case AF_INET6:
384 listener = &listeners[LISTEN_IPV6];
385 if(!listener->F)
386 goto end;
387 break;
388#endif
389 default:
390 goto end;
391 }
392
393 /* Simple enough to build */
394 snprintf(sendbuf, sizeof(sendbuf), "CONNECT %s:%hu HTTP/1.0\r\n\r\n", listener->ip, listener->port);
395
396 /* Send request */
397 if(rb_write(scan->F, sendbuf, strlen(sendbuf)) <= 0)
398 goto end;
399
400 /* Now the note in a separate write */
401 if(rb_write(scan->F, scan->proxy->note, strlen(scan->proxy->note) + 1) <= 0)
402 goto end;
403
404 /* MiroTik needs this, and as a separate write */
405 if(rb_write(scan->F, "\r\n", 2) <= 0)
406 goto end;
407
408end:
409 rb_close(scan->F);
410 rb_dlinkDelete(&scan->node, &lookup->scans);
411 rb_free(scan);
412}
413
4e85459a
EM
414/* Establish connections */
415static inline void
416establish_connection(struct auth_client *auth, struct opm_proxy *proxy)
417{
418 struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
419 struct opm_listener *listener;
420 struct opm_scan *scan = rb_malloc(sizeof(struct opm_scan));
766d4ffc 421 struct rb_sockaddr_storage c_a, l_a;
4e85459a
EM
422 int opt = 1;
423 CNCB *callback;
424
425 switch(proxy->proto)
426 {
427 case PROTO_SOCKS4:
8aacefa3
EM
428#ifdef RB_IPV6
429 if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
430 {
431 rb_free(scan);
432 return;
433 }
434#endif
4e85459a
EM
435 callback = socks4_connected;
436 break;
437 case PROTO_SOCKS5:
438 callback = socks5_connected;
439 break;
fabe8b94
EM
440 case PROTO_HTTP_CONNECT:
441 callback = http_connect_connected;
4e85459a
EM
442 default:
443 return;
444 }
445
446#ifdef RB_IPV6
447 if(GET_SS_FAMILY(&auth->c_addr) == AF_INET6)
448 listener = &listeners[LISTEN_IPV6];
449 else
450#endif
451 listener = &listeners[LISTEN_IPV4];
452
453 c_a = auth->c_addr; /* Client */
454 l_a = listener->addr; /* Listener */
455
456 scan->auth = auth;
457 scan->proxy = proxy;
458 if((scan->F = rb_socket(GET_SS_FAMILY(&auth->c_addr), SOCK_STREAM, 0, proxy->note)) == NULL)
459 {
460 warn_opers(L_CRIT, "OPM: could not create OPM socket (proto %s): %s", proxy->note, strerror(errno));
461 rb_free(scan);
462 return;
463 }
464
465 /* Disable Nagle's algorithim - buffering could affect scans */
466 (void)setsockopt(rb_get_fd(scan->F), IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt));
467
d86692fa 468 SET_SS_PORT(&l_a, 0);
8aacefa3 469 SET_SS_PORT(&c_a, htons(proxy->port));
4e85459a
EM
470
471 rb_dlinkAdd(scan, &scan->node, &lookup->scans);
472 rb_connect_tcp(scan->F,
473 (struct sockaddr *)&c_a,
474 (struct sockaddr *)&l_a,
475 GET_SS_LEN(&l_a),
476 callback, scan, opm_timeout);
477}
478
1de169a2
EM
479static bool
480create_listener(const char *ip, uint16_t port)
481{
482 struct auth_client *auth;
483 struct opm_listener *listener;
484 struct rb_sockaddr_storage addr;
485 rb_dictionary_iter iter;
486 rb_fde_t *F;
487 int opt = 1;
488
489 if(!rb_inet_pton_sock(ip, (struct sockaddr *)&addr))
490 {
491 warn_opers(L_CRIT, "OPM: got a bad listener: %s:%hu", ip, port);
492 exit(EX_PROVIDER_ERROR);
493 }
494
495#ifdef RB_IPV6
496 if(GET_SS_FAMILY(&addr) == AF_INET6)
497 {
498 struct sockaddr_in6 *a1, *a2;
499
500 listener = &listeners[LISTEN_IPV6];
501
502 a1 = (struct sockaddr_in6 *)&addr;
503 a2 = (struct sockaddr_in6 *)&listener->addr;
504
505 if(IN6_ARE_ADDR_EQUAL(&a1->sin6_addr, &a2->sin6_addr) &&
506 GET_SS_PORT(&addr) == GET_SS_PORT(&listener->addr) &&
507 listener->F != NULL)
508 {
509 /* Listener already exists */
510 return false;
511 }
512 }
513 else
514#endif
515 {
516 struct sockaddr_in *a1, *a2;
517
518 listener = &listeners[LISTEN_IPV4];
519
520 a1 = (struct sockaddr_in *)&addr;
521 a2 = (struct sockaddr_in *)&listener->addr;
522
523 if(a1->sin_addr.s_addr == a2->sin_addr.s_addr &&
524 GET_SS_PORT(&addr) == GET_SS_PORT(&listener->addr) &&
525 listener->F != NULL)
526 {
527 /* Listener already exists */
528 return false;
529 }
530 }
531
532 if((F = rb_socket(GET_SS_FAMILY(&addr), SOCK_STREAM, 0, "OPM listener socket")) == NULL)
533 {
534 /* This shouldn't fail, or we have big problems... */
535 warn_opers(L_CRIT, "OPM: cannot create socket: %s", strerror(errno));
536 exit(EX_PROVIDER_ERROR);
537 }
538
539 if(setsockopt(rb_get_fd(F), SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)))
540 {
541 /* This shouldn't fail either... */
542 warn_opers(L_CRIT, "OPM: cannot set options on socket: %s", strerror(errno));
543 exit(EX_PROVIDER_ERROR);
544 }
545
546 /* Set up ports for binding */
547#ifdef RB_IPV6
548 if(GET_SS_FAMILY(&addr) == AF_INET6)
549 ((struct sockaddr_in6 *)&addr)->sin6_port = htons(port);
550 else
551#endif
552 ((struct sockaddr_in *)&addr)->sin_port = htons(port);
553
554 if(bind(rb_get_fd(F), (struct sockaddr *)&addr, GET_SS_LEN(&addr)))
555 {
556 /* Shit happens, let's not cripple authd over /this/ since it could be user error */
557 warn_opers(L_WARN, "OPM: cannot bind on socket: %s", strerror(errno));
558 rb_close(F);
559 return false;
560 }
561
562 if(rb_listen(F, SOMAXCONN, false)) /* deferred accept could interfere with detection */
563 {
564 /* Again, could be user error */
565 warn_opers(L_WARN, "OPM: cannot listen on socket: %s", strerror(errno));
566 rb_close(F);
567 return false;
568 }
569
570 /* From this point forward we assume we have a listener */
571
572 if(listener->F != NULL)
573 /* Close old listener */
574 rb_close(listener->F);
575
576 listener->F = F;
577
578 /* Cancel clients that may be on old listener
579 * XXX - should rescan clients that need it
580 */
581 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
582 {
583 opm_cancel(auth);
584 }
585
586 /* Copy data */
587 rb_strlcpy(listener->ip, ip, sizeof(listener->ip));
588 listener->port = port;
589 listener->addr = addr;
590
591 opm_enable = true; /* Implicitly set this to true for now if we have a listener */
592 rb_accept_tcp(listener->F, NULL, accept_opm, listener);
593 return true;
594}
595
596
4e85459a
EM
597static void
598opm_destroy(void)
599{
600 struct auth_client *auth;
601 rb_dictionary_iter iter;
602
603 /* Nuke all opm lookups */
604 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
605 {
606 opm_cancel(auth);
607 }
608}
609
610static bool
611opm_start(struct auth_client *auth)
612{
1de169a2 613 rb_dlink_node *ptr;
4e85459a
EM
614 struct opm_lookup *lookup = rb_malloc(sizeof(struct opm_lookup));
615
1de169a2 616 if(!opm_enable || !rb_dlink_list_length(&proxy_scanners))
4e85459a
EM
617 {
618 notice_client(auth->cid, "*** Proxy scanning disabled, not scanning");
619 return true;
620 }
621
622 auth->data[PROVIDER_OPM] = lookup = rb_malloc(sizeof(struct opm_lookup));
623 auth->timeout[PROVIDER_OPM] = rb_current_time() + opm_timeout;
624
1de169a2
EM
625 RB_DLINK_FOREACH(ptr, proxy_scanners.head)
626 {
627 struct opm_proxy *proxy = ptr->data;
4e85459a 628 establish_connection(auth, proxy);
1de169a2 629 }
4e85459a
EM
630
631 notice_client(auth->cid, "*** Scanning for open proxies...");
632 set_provider_on(auth, PROVIDER_OPM);
633
634 return true;
635}
636
637static void
638opm_cancel(struct auth_client *auth)
639{
640 struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
641
642 if(lookup != NULL)
643 {
644 rb_dlink_node *ptr, *nptr;
645
1661e365
EM
646 notice_client(auth->cid, "*** Did not detect open proxies");
647
4e85459a
EM
648 RB_DLINK_FOREACH_SAFE(ptr, nptr, lookup->scans.head)
649 {
650 struct opm_scan *scan = ptr->data;
651
652 rb_close(scan->F);
653 rb_free(scan);
654 }
655
656 rb_free(lookup);
657 provider_done(auth, PROVIDER_OPM);
658 }
659}
660
661static void
662add_conf_opm_timeout(const char *key __unused, int parc __unused, const char **parv)
663{
664 int timeout = atoi(parv[0]);
665
666 if(timeout < 0)
667 {
668 warn_opers(L_CRIT, "opm: opm timeout < 0 (value: %d)", timeout);
669 return;
670 }
671
672 opm_timeout = timeout;
673}
674
675static void
676set_opm_enabled(const char *key __unused, int parc __unused, const char **parv)
677{
4deb334f
EM
678 bool enable = (*parv[0] == '1');
679
680 if(!enable)
1661e365 681 {
4deb334f 682 if(listeners[LISTEN_IPV4].F != NULL || listeners[LISTEN_IPV6].F != NULL)
1661e365
EM
683 {
684 struct auth_client *auth;
685 rb_dictionary_iter iter;
686
687 /* Close the listening socket */
688 if(listeners[LISTEN_IPV4].F != NULL)
689 rb_close(listeners[LISTEN_IPV4].F);
690
691 if(listeners[LISTEN_IPV6].F != NULL)
692 rb_close(listeners[LISTEN_IPV6].F);
693
4deb334f
EM
694 listeners[LISTEN_IPV4].F = listeners[LISTEN_IPV6].F = NULL;
695
1661e365
EM
696 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
697 {
698 opm_cancel(auth);
699 }
700 }
701 }
4e85459a 702 else
4deb334f
EM
703 {
704 if(listeners[LISTEN_IPV4].ip[0] != '\0' && listeners[LISTEN_IPV4].port != 0)
705 {
706 lrb_assert(listeners[LISTEN_IPV4].F == NULL);
707
708 /* Pre-configured IP/port, just re-establish */
709 create_listener(listeners[LISTEN_IPV4].ip, listeners[LISTEN_IPV4].port);
710 }
711
712 if(listeners[LISTEN_IPV6].ip[0] != '\0' && listeners[LISTEN_IPV6].port != 0)
713 {
714 lrb_assert(listeners[LISTEN_IPV6].F == NULL);
715
716 /* Pre-configured IP/port, just re-establish */
717 create_listener(listeners[LISTEN_IPV6].ip, listeners[LISTEN_IPV6].port);
718 }
719 }
720
721 opm_enable = enable;
4e85459a
EM
722}
723
1de169a2
EM
724static void
725set_opm_listener(const char *key __unused, int parc __unused, const char **parv)
4e85459a 726{
1de169a2
EM
727 const char *ip = parv[0];
728 int iport = atoi(parv[1]);
4e85459a 729
1de169a2 730 if(iport > 65535 || iport <= 0)
4e85459a 731 {
1de169a2 732 warn_opers(L_CRIT, "OPM: got a bad listener: %s:%s", parv[0], parv[1]);
4e85459a
EM
733 exit(EX_PROVIDER_ERROR);
734 }
735
1de169a2
EM
736 create_listener(ip, (uint16_t)iport);
737}
4deb334f 738
1de169a2
EM
739static void
740create_opm_scanner(const char *key __unused, int parc __unused, const char **parv)
741{
742 int iport = atoi(parv[1]);
743 struct opm_proxy *proxy = rb_malloc(sizeof(struct opm_proxy));
4deb334f 744
1de169a2 745 if(iport <= 0 || iport > 65535)
4deb334f 746 {
1de169a2
EM
747 warn_opers(L_CRIT, "OPM: got a bad scanner: %s (port %s)", parv[0], parv[1]);
748 exit(EX_PROVIDER_ERROR);
749 }
4deb334f 750
1de169a2 751 proxy->port = (uint16_t)iport;
4e85459a 752
1de169a2
EM
753 switch((proxy->proto = get_protocol_from_string(parv[0])))
754 {
755 case PROTO_SOCKS4:
756 snprintf(proxy->note, sizeof(proxy->note), "socks4:%hu", proxy->port);
757 break;
758 case PROTO_SOCKS5:
759 snprintf(proxy->note, sizeof(proxy->note), "socks5:%hu", proxy->port);
760 break;
fabe8b94
EM
761 case PROTO_HTTP_CONNECT:
762 snprintf(proxy->note, sizeof(proxy->note), "httpconnect:%hu", proxy->port);
763 break;
1de169a2
EM
764 default:
765 warn_opers(L_CRIT, "OPM: got an unknown proxy type: %s (port %hu)", parv[0], proxy->port);
766 exit(EX_PROVIDER_ERROR);
4deb334f 767 }
4e85459a 768
1de169a2 769 if(find_proxy_scanner(proxy->proto, proxy->port) != NULL)
4e85459a 770 {
1de169a2 771 warn_opers(L_CRIT, "OPM: got a duplicate scanner: %s (port %hu)", parv[0], proxy->port);
4e85459a
EM
772 exit(EX_PROVIDER_ERROR);
773 }
774
1de169a2
EM
775 rb_dlinkAdd(proxy, &proxy->node, &proxy_scanners);
776}
777
778static void
779delete_opm_scanner(const char *key __unused, int parc __unused, const char **parv)
780{
781 struct auth_client *auth;
782 struct opm_proxy *proxy;
783 protocol_t proto = get_protocol_from_string(parv[0]);
784 int iport = atoi(parv[1]);
785 rb_dictionary_iter iter;
786
787 if(iport <= 0 || iport > 65535)
4e85459a 788 {
1de169a2 789 warn_opers(L_CRIT, "OPM: got a bad scanner to delete: %s (port %s)", parv[0], parv[1]);
4e85459a
EM
790 exit(EX_PROVIDER_ERROR);
791 }
792
1de169a2 793 if(proto == PROTO_NONE)
4e85459a 794 {
1de169a2
EM
795 warn_opers(L_CRIT, "OPM: got an unknown proxy type to delete: %s (port %d)", parv[0], iport);
796 exit(EX_PROVIDER_ERROR);
4e85459a
EM
797 }
798
1de169a2 799 if((proxy = find_proxy_scanner(proto, (uint16_t)iport)) == NULL)
4e85459a 800 {
1de169a2
EM
801 warn_opers(L_CRIT, "OPM: cannot find proxy to delete: %s (port %d)", parv[0], iport);
802 exit(EX_PROVIDER_ERROR);
4e85459a
EM
803 }
804
1de169a2
EM
805 /* Abort remaining clients on this scanner */
806 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
807 {
808 rb_dlink_node *ptr;
809 struct opm_lookup *lookup = auth->data[PROVIDER_OPM];
8c0b90de 810
1de169a2
EM
811 if(lookup == NULL)
812 continue;
4e85459a 813
1de169a2
EM
814 RB_DLINK_FOREACH(ptr, lookup->scans.head)
815 {
816 struct opm_scan *scan = ptr->data;
4e85459a 817
1de169a2
EM
818 if(scan->proxy->port == proxy->port && scan->proxy->proto == proxy->proto)
819 {
820 /* Match */
821 rb_dlinkDelete(&scan->node, &lookup->scans);
822 rb_free(scan);
4e85459a 823
1de169a2
EM
824 if(!rb_dlink_list_length(&lookup->scans))
825 opm_cancel(auth);
4e85459a 826
1de169a2
EM
827 break;
828 }
829 }
830 }
831
832 rb_dlinkDelete(&proxy->node, &proxy_scanners);
833 rb_free(proxy);
4deb334f
EM
834}
835
836static void
1de169a2 837delete_opm_scanner_all(const char *key __unused, int parc __unused, const char **parv __unused)
4deb334f 838{
1de169a2
EM
839 struct auth_client *auth;
840 rb_dlink_node *ptr, *nptr;
841 rb_dictionary_iter iter;
4deb334f 842
1de169a2 843 RB_DLINK_FOREACH_SAFE(ptr, nptr, proxy_scanners.head)
4deb334f 844 {
1de169a2
EM
845 rb_free(ptr->data);
846 rb_dlinkDelete(ptr, &proxy_scanners);
4deb334f
EM
847 }
848
1de169a2
EM
849 RB_DICTIONARY_FOREACH(auth, &iter, auth_clients)
850 {
851 opm_cancel(auth);
852 }
4e85459a
EM
853}
854
1de169a2 855
4e85459a
EM
856struct auth_opts_handler opm_options[] =
857{
858 { "opm_timeout", 1, add_conf_opm_timeout },
859 { "opm_enabled", 1, set_opm_enabled },
860 { "opm_listener", 2, set_opm_listener },
1de169a2
EM
861 { "opm_scanner", 2, create_opm_scanner },
862 { "opm_scanner_del", 2, delete_opm_scanner },
863 { "opm_scanner_del_all", 0, delete_opm_scanner_all },
4e85459a
EM
864 { NULL, 0, NULL },
865};
866
867struct auth_provider opm_provider =
868{
869 .id = PROVIDER_OPM,
870 .destroy = opm_destroy,
871 .start = opm_start,
872 .cancel = opm_cancel,
873 .timeout = opm_cancel,
874 .opt_handlers = opm_options,
875};