release 0.92.3
[cntlm.git] / main.c
blob3800dc899daf92521f06ecac31b22f32b683b47f
1 /*
2 * This is the main module of the CNTLM
4 * CNTLM is free software; you can redistribute it and/or modify it under the
5 * terms of the GNU General Public License as published by the Free Software
6 * Foundation; either version 2 of the License, or (at your option) any later
7 * version.
9 * CNTLM is distributed in the hope that it will be useful, but WITHOUT ANY
10 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12 * details.
14 * You should have received a copy of the GNU General Public License along with
15 * this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
16 * St, Fifth Floor, Boston, MA 02110-1301, USA.
18 * Copyright (c) 2007 David Kubicek
22 #include <sys/types.h>
23 #include <sys/time.h>
24 #include <sys/select.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <pthread.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
32 #include <signal.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <strings.h>
37 #include <netdb.h>
38 #include <ctype.h>
39 #include <pwd.h>
40 #include <fcntl.h>
41 #include <syslog.h>
42 #include <termios.h>
43 #include <fnmatch.h>
46 * Some helping routines like linked list manipulation substr(), memory
47 * allocation, NTLM authentication routines, etc.
49 #include "config/config.h"
50 #include "socket.h"
51 #include "utils.h"
52 #include "ntlm.h"
53 #include "swap.h"
54 #include "config.h"
55 #include "acl.h"
56 #include "auth.h"
57 #include "http.h"
58 #include "globals.h"
59 #include "pages.h"
60 #include "forward.h" /* code serving via parent proxy */
61 #include "direct.h" /* code serving directly without proxy */
63 #define STACK_SIZE sizeof(void *)*8*1024
66 * Global "read-only" data initialized in main(). Comments list funcs. which use
67 * them. Having these global avoids the need to pass them to each thread and
68 * from there again a few times to inner calls.
70 int debug = 0; /* all debug printf's and possibly external modules */
72 struct auth_s *g_creds = NULL; /* throughout the whole module */
74 int quit = 0; /* sighandler() */
75 int ntlmbasic = 0; /* forward_request() */
76 int serialize = 0;
77 int scanner_plugin = 0;
78 long scanner_plugin_maxsize = 0;
81 * List of finished threads. Each forward_request() thread adds itself to it when
82 * finished. Main regularly joins and removes all tid's in there.
84 plist_t threads_list = NULL;
85 pthread_mutex_t threads_mtx = PTHREAD_MUTEX_INITIALIZER;
88 * List of cached connections. Accessed by each thread forward_request().
90 plist_t connection_list = NULL;
91 pthread_mutex_t connection_mtx = PTHREAD_MUTEX_INITIALIZER;
94 * List of available proxies and current proxy id for proxy_connect().
96 int parent_count = 0;
97 plist_t parent_list = NULL;
100 * List of custom header substitutions, SOCKS5 proxy users and
101 * UserAgents for the scanner plugin.
103 hlist_t header_list = NULL; /* forward_request() */
104 hlist_t users_list = NULL; /* socks5_thread() */
105 plist_t scanner_agent_list = NULL; /* scanner_hook() */
106 plist_t noproxy_list = NULL; /* proxy_thread() */
109 * General signal handler. If in debug mode, quit immediately.
111 void sighandler(int p) {
112 if (!quit)
113 syslog(LOG_INFO, "Signal %d received, issuing clean shutdown\n", p);
114 else
115 syslog(LOG_INFO, "Signal %d received, forcing shutdown\n", p);
117 if (quit++ || debug)
118 quit++;
122 * Parse proxy parameter and add it to the global list.
124 int parent_add(char *parent, int port) {
125 int len, i;
126 char *proxy;
127 proxy_t *aux;
130 * Check format and parse it.
132 proxy = strdup(parent);
133 len = strlen(proxy);
134 i = strcspn(proxy, ": ");
135 if (i != len) {
136 proxy[i++] = 0;
137 while (i < len && (proxy[i] == ' ' || proxy[i] == '\t'))
138 i++;
140 if (i >= len) {
141 free(proxy);
142 return 0;
145 port = atoi(proxy+i);
149 * No port argument and not parsed from proxy?
151 if (!port) {
152 syslog(LOG_ERR, "Invalid proxy specification %s.\n", parent);
153 free(proxy);
154 myexit(1);
158 * Try to resolve proxy address
160 if (debug)
161 syslog(LOG_INFO, "Resolving proxy %s...\n", proxy);
162 if (!so_resolv(&host, proxy)) {
163 syslog(LOG_ERR, "Cannot resolve proxy %s, discarding.\n", parent);
164 free(proxy);
165 return 0;
169 aux = (proxy_t *)new(sizeof(proxy_t));
170 strlcpy(aux->hostname, proxy, sizeof(aux->hostname));
171 aux->port = port;
172 aux->resolved = 0;
173 parent_list = plist_add(parent_list, ++parent_count, (char *)aux);
175 free(proxy);
176 return 1;
180 * Register and bind new proxy service port.
182 void listen_add(const char *service, plist_t *list, char *spec, int gateway) {
183 struct in_addr source;
184 int i, p, len, port;
185 char *tmp;
187 len = strlen(spec);
188 p = strcspn(spec, ":");
189 if (p < len-1) {
190 tmp = substr(spec, 0, p);
191 if (!so_resolv(&source, tmp)) {
192 syslog(LOG_ERR, "Cannot resolve listen address %s\n", tmp);
193 myexit(1);
195 free(tmp);
196 port = atoi(tmp = spec+p+1);
197 } else {
198 source.s_addr = htonl(gateway ? INADDR_ANY : INADDR_LOOPBACK);
199 port = atoi(tmp = spec);
202 if (!port) {
203 syslog(LOG_ERR, "Invalid listen port %s.\n", tmp);
204 myexit(1);
207 i = so_listen(port, source);
208 if (i > 0) {
209 *list = plist_add(*list, i, NULL);
210 syslog(LOG_INFO, "%s listening on %s:%d\n", service, inet_ntoa(source), port);
215 * Register a new tunnel definition, bind service port.
217 void tunnel_add(plist_t *list, char *spec, int gateway) {
218 struct in_addr source;
219 int i, len, count, pos, port;
220 char *field[4];
221 char *tmp;
223 spec = strdup(spec);
224 len = strlen(spec);
225 field[0] = spec;
226 for (count = 1, i = 0; count < 4 && i < len; ++i)
227 if (spec[i] == ':') {
228 spec[i] = 0;
229 field[count++] = spec+i+1;
232 pos = 0;
233 if (count == 4) {
234 if (!so_resolv(&source, field[pos])) {
235 syslog(LOG_ERR, "Cannot resolve tunel bind address: %s\n", field[pos]);
236 myexit(1);
238 pos++;
239 } else
240 source.s_addr = htonl(gateway ? INADDR_ANY : INADDR_LOOPBACK);
242 if (count-pos == 3) {
243 port = atoi(field[pos]);
244 if (port == 0) {
245 syslog(LOG_ERR, "Invalid tunnel local port: %s\n", field[pos]);
246 myexit(1);
249 if (!strlen(field[pos+1]) || !strlen(field[pos+2])) {
250 syslog(LOG_ERR, "Invalid tunnel target: %s:%s\n", field[pos+1], field[pos+2]);
251 myexit(1);
254 tmp = new(strlen(field[pos+1]) + strlen(field[pos+2]) + 2 + 1);
255 strcpy(tmp, field[pos+1]);
256 strcat(tmp, ":");
257 strcat(tmp, field[pos+2]);
259 i = so_listen(port, source);
260 if (i > 0) {
261 *list = plist_add(*list, i, tmp);
262 syslog(LOG_INFO, "New tunnel from %s:%d to %s\n", inet_ntoa(source), port, tmp);
263 } else
264 free(tmp);
265 } else {
266 printf("Tunnel specification incorrect ([laddress:]lport:rserver:rport).\n");
267 myexit(1);
270 free(spec);
274 * Add no-proxy hostname/IP
276 plist_t noproxy_add(plist_t list, char *spec) {
277 char *tok, *save;
279 tok = strtok_r(spec, ", ", &save);
280 while ( tok != NULL ) {
281 if (debug)
282 printf("Adding no-proxy for: '%s'\n", tok);
283 list = plist_add(list, 0, strdup(tok));
284 tok = strtok_r(NULL, ", ", &save);
287 return list;
290 int noproxy_match(const char *addr) {
291 plist_t list;
293 list = noproxy_list;
294 while (list) {
295 if (list->aux && strlen(list->aux)
296 && fnmatch(list->aux, addr, 0) == 0) {
297 if (debug)
298 printf("MATCH: %s (%s)\n", addr, (char *)list->aux);
299 return 1;
300 } else if (debug)
301 printf(" NO: %s (%s)\n", addr, (char *)list->aux);
303 list = list->next;
306 return 0;
310 * Proxy thread - decide between direct and forward based on NoProxy
312 void *proxy_thread(void *thread_data) {
313 rr_data_t request, ret;
314 int keep_alive; /* Proxy-Connection */
316 int cd = ((struct thread_arg_s *)thread_data)->fd;
318 do {
319 ret = NULL;
320 keep_alive = 0;
322 if (debug) {
323 printf("\n******* Round 1 C: %d *******\n", cd);
324 printf("Reading headers (%d)...\n", cd);
327 request = new_rr_data();
328 if (!headers_recv(cd, request)) {
329 free_rr_data(request);
330 break;
333 do {
335 * Are we being returned a request by forward_request or direct_request?
337 if (ret) {
338 free_rr_data(request);
339 request = ret;
342 keep_alive = hlist_subcmp(request->headers, "Proxy-Connection", "keep-alive");
344 if (noproxy_match(request->hostname))
345 ret = direct_request(thread_data, request);
346 else
347 ret = forward_request(thread_data, request);
349 if (debug)
350 printf("proxy_thread: request rc = %p\n", (void *)ret);
351 } while (ret != NULL && ret != (void *)-1);
353 free_rr_data(request);
355 * If client asked for proxy keep-alive, loop unless the last server response
356 * requested (Proxy-)Connection: close.
358 } while (keep_alive && ret != (void *)-1 && !serialize);
361 * Add ourselves to the "threads to join" list.
363 if (!serialize) {
364 pthread_mutex_lock(&threads_mtx);
365 threads_list = plist_add(threads_list, (unsigned long)pthread_self(), NULL);
366 pthread_mutex_unlock(&threads_mtx);
369 free(thread_data);
370 close(cd);
372 return NULL;
376 * Tunnel/port forward thread - this method is obviously better solution than using extra
377 * tools like "corkscrew" which after all require us for authentication and tunneling
378 * their HTTP CONNECT in the first place.
380 void *tunnel_thread(void *thread_data) {
381 char *hostname, *pos;
382 char *thost = ((struct thread_arg_s *)thread_data)->target;
384 hostname = strdup(thost);
385 if ((pos = strchr(hostname, ':')) != NULL)
386 *pos = 0;
388 if (noproxy_match(hostname))
389 direct_tunnel(thread_data);
390 else
391 forward_tunnel(thread_data);
393 free(hostname);
394 free(thread_data);
397 * Add ourself to the "threads to join" list.
399 pthread_mutex_lock(&threads_mtx);
400 threads_list = plist_add(threads_list, (unsigned long)pthread_self(), NULL);
401 pthread_mutex_unlock(&threads_mtx);
403 return NULL;
407 * SOCKS5 thread
409 void *socks5_thread(void *thread_data) {
410 char *tmp, *thost, *tport, *uname, *upass;
411 unsigned short port;
412 int ver, r, c, i, w;
414 struct auth_s *tcreds = NULL;
415 unsigned char *bs = NULL, *auths = NULL, *addr = NULL;
416 int found = -1;
417 int sd = -1;
418 int open = !hlist_count(users_list);
420 int cd = ((struct thread_arg_s *)thread_data)->fd;
421 struct sockaddr_in caddr = ((struct thread_arg_s *)thread_data)->addr;
422 free(thread_data);
425 * Check client's version, possibly fuck'em
427 bs = (unsigned char *)new(10);
428 thost = new(MINIBUF_SIZE);
429 tport = new(MINIBUF_SIZE);
430 r = read(cd, bs, 2);
431 if (r != 2 || bs[0] != 5)
432 goto bailout;
435 * Read offered auth schemes
437 c = bs[1];
438 auths = (unsigned char *)new(c+1);
439 r = read(cd, auths, c);
440 if (r != c)
441 goto bailout;
444 * Are we wide open and client is OK with no auth?
446 if (open) {
447 for (i = 0; i < c && (auths[i] || (found = 0)); ++i);
451 * If not, accept plain auth if offered
453 if (found < 0) {
454 for (i = 0; i < c && (auths[i] != 2 || !(found = 2)); ++i);
458 * If not open and no auth offered or open and auth requested, fuck'em
459 * and complete the handshake
461 if (found < 0) {
462 bs[0] = 5;
463 bs[1] = 0xFF;
464 w = write(cd, bs, 2);
465 goto bailout;
466 } else {
467 bs[0] = 5;
468 bs[1] = found;
469 w = write(cd, bs, 2);
473 * Plain auth negotiated?
475 if (found != 0) {
477 * Check ver and read username len
479 r = read(cd, bs, 2);
480 if (r != 2) {
481 bs[0] = 1;
482 bs[1] = 0xFF; /* Unsuccessful (not supported) */
483 w = write(cd, bs, 2);
484 goto bailout;
486 c = bs[1];
489 * Read username and pass len
491 uname = new(c+1);
492 r = read(cd, uname, c+1);
493 if (r != c+1) {
494 free(uname);
495 goto bailout;
497 i = uname[c];
498 uname[c] = 0;
499 c = i;
502 * Read pass
504 upass = new(c+1);
505 r = read(cd, upass, c);
506 if (r != c) {
507 free(upass);
508 free(uname);
509 goto bailout;
511 upass[c] = 0;
514 * Check credentials against the list
516 tmp = hlist_get(users_list, uname);
517 if (!hlist_count(users_list) || (tmp && !strcmp(tmp, upass))) {
518 bs[0] = 1;
519 bs[1] = 0; /* Success */
520 } else {
521 bs[0] = 1;
522 bs[1] = 0xFF; /* Failed */
526 * Send response
528 w = write(cd, bs, 2);
529 free(upass);
530 free(uname);
533 * Fuck'em if auth failed
535 if (bs[1])
536 goto bailout;
540 * Read request type
542 r = read(cd, bs, 4);
543 if (r != 4)
544 goto bailout;
547 * Is it connect for supported address type (IPv4 or DNS)? If not, fuck'em
549 if (bs[1] != 1 || (bs[3] != 1 && bs[3] != 3)) {
550 bs[0] = 5;
551 bs[1] = 2; /* Not allowed */
552 bs[2] = 0;
553 bs[3] = 1; /* Dummy IPv4 */
554 memset(bs+4, 0, 6);
555 w = write(cd, bs, 10);
556 goto bailout;
560 * Ok, it's connect to a domain or IP
561 * Let's read dest address
563 if (bs[3] == 1) {
564 ver = 1; /* IPv4, we know the length */
565 c = 4;
566 } else if (bs[3] == 3) {
567 ver = 2; /* FQDN, get string length */
568 r = read(cd, &c, 1);
569 if (r != 1)
570 goto bailout;
571 } else
572 goto bailout;
574 addr = (unsigned char *)new(c+10 + 1);
575 r = read(cd, addr, c);
576 if (r != c)
577 goto bailout;
578 addr[c] = 0;
581 * Convert the address to character string
583 if (ver == 1) {
584 sprintf(thost, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]); /* It's in network byte order */
585 } else {
586 strlcpy(thost, (char *)addr, MINIBUF_SIZE);
590 * Read port number and convert to host byte order int
592 r = read(cd, &port, 2);
593 if (r != 2)
594 goto bailout;
596 i = 0;
597 if (noproxy_match(thost)) {
598 sd = host_connect(thost, ntohs(port));
599 i = (sd >= 0);
600 } else {
601 sprintf(tport, "%d", ntohs(port));
602 strlcat(thost, ":", MINIBUF_SIZE);
603 strlcat(thost, tport, MINIBUF_SIZE);
605 tcreds = new_auth();
606 sd = proxy_connect(tcreds);
607 if (sd >= 0)
608 i = prepare_http_connect(sd, tcreds, thost);
612 * Direct or proxy connect?
614 if (!i) {
616 * Connect/tunnel failed, report
618 bs[0] = 5;
619 bs[1] = 1; /* General failure */
620 bs[2] = 0;
621 bs[3] = 1; /* Dummy IPv4 */
622 memset(bs+4, 0, 6);
623 w = write(cd, bs, 10);
624 goto bailout;
625 } else {
627 * All right
629 bs[0] = 5;
630 bs[1] = 0; /* Success */
631 bs[2] = 0;
632 bs[3] = 1; /* Dummy IPv4 */
633 memset(bs+4, 0, 6);
634 w = write(cd, bs, 10);
637 syslog(LOG_DEBUG, "%s SOCKS %s", inet_ntoa(caddr.sin_addr), thost);
640 * Let's give them bi-directional connection they asked for
642 tunnel(cd, sd);
644 bailout:
645 if (addr)
646 free(addr);
647 if (auths)
648 free(auths);
649 if (thost)
650 free(thost);
651 if (tport)
652 free(tport);
653 if (bs)
654 free(bs);
655 if (tcreds)
656 free(tcreds);
657 if (sd)
658 close(sd);
659 close(cd);
661 return NULL;
664 int main(int argc, char **argv) {
665 char *tmp, *head;
666 char *cpassword, *cpassntlm2, *cpassnt, *cpasslm;
667 char *cuser, *cdomain, *cworkstation, *cuid, *cpidfile, *cauth;
668 struct passwd *pw;
669 struct termios termold, termnew;
670 pthread_attr_t pattr;
671 pthread_t pthr;
672 hlist_t list;
673 int i, w;
675 int cd = 0;
676 int help = 0;
677 int nuid = 0;
678 int ngid = 0;
679 int gateway = 0;
680 int tc = 0;
681 int tj = 0;
682 int interactivepwd = 0;
683 int interactivehash = 0;
684 int tracefile = 0;
685 int cflags = 0;
686 int asdaemon = 1;
687 plist_t tunneld_list = NULL;
688 plist_t proxyd_list = NULL;
689 plist_t socksd_list = NULL;
690 plist_t rules = NULL;
691 config_t cf = NULL;
692 char *magic_detect = NULL;
694 g_creds = new_auth();
695 cuser = new(MINIBUF_SIZE);
696 cdomain = new(MINIBUF_SIZE);
697 cpassword = new(MINIBUF_SIZE);
698 cpassntlm2 = new(MINIBUF_SIZE);
699 cpassnt = new(MINIBUF_SIZE);
700 cpasslm = new(MINIBUF_SIZE);
701 cworkstation = new(MINIBUF_SIZE);
702 cpidfile = new(MINIBUF_SIZE);
703 cuid = new(MINIBUF_SIZE);
704 cauth = new(MINIBUF_SIZE);
706 openlog("cntlm", LOG_CONS, LOG_DAEMON);
708 #if config_endian == 0
709 syslog(LOG_INFO, "Starting cntlm version " VERSION " for BIG endian\n");
710 #else
711 syslog(LOG_INFO, "Starting cntlm version " VERSION " for LITTLE endian\n");
712 #endif
714 while ((i = getopt(argc, argv, ":-:a:c:d:fghIl:p:r:su:vw:A:BD:F:G:HL:M:N:O:P:R:S:T:U:")) != -1) {
715 switch (i) {
716 case 'A':
717 case 'D':
718 if (!acl_add(&rules, optarg, (i == 'A' ? ACL_ALLOW : ACL_DENY)))
719 myexit(1);
720 break;
721 case 'a':
722 strlcpy(cauth, optarg, MINIBUF_SIZE);
723 break;
724 case 'B':
725 ntlmbasic = 1;
726 break;
727 case 'c':
728 if (!(cf = config_open(optarg))) {
729 syslog(LOG_ERR, "Cannot access specified config file: %s\n", optarg);
730 myexit(1);
732 break;
733 case 'd':
734 strlcpy(cdomain, optarg, MINIBUF_SIZE);
735 break;
736 case 'F':
737 cflags = swap32(strtoul(optarg, &tmp, 0));
738 break;
739 case 'f':
740 asdaemon = 0;
741 break;
742 case 'G':
743 if (strlen(optarg)) {
744 scanner_plugin = 1;
745 if (!scanner_plugin_maxsize)
746 scanner_plugin_maxsize = 1;
747 i = strlen(optarg) + 3;
748 tmp = new(i);
749 snprintf(tmp, i, "*%s*", optarg);
750 scanner_agent_list = plist_add(scanner_agent_list, 0, tmp);
752 break;
753 case 'g':
754 gateway = 1;
755 break;
756 case 'H':
757 interactivehash = 1;
758 break;
759 case 'I':
760 interactivepwd = 1;
761 break;
762 case 'L':
764 * Parse and validate the argument.
765 * Create a listening socket for tunneling.
767 tunnel_add(&tunneld_list, optarg, gateway);
768 break;
769 case 'l':
771 * Create a listening socket for proxy function.
773 listen_add("Proxy", &proxyd_list, optarg, gateway);
774 break;
775 case 'M':
776 magic_detect = strdup(optarg);
777 break;
778 case 'N':
779 noproxy_list = noproxy_add(noproxy_list, tmp=strdup(optarg));
780 free(tmp);
781 break;
782 case 'O':
783 listen_add("SOCKS5 proxy", &socksd_list, optarg, gateway);
784 break;
785 case 'P':
786 strlcpy(cpidfile, optarg, MINIBUF_SIZE);
787 break;
788 case 'p':
790 * Overwrite the password parameter with '*'s to make it
791 * invisible in "ps", /proc, etc.
793 strlcpy(cpassword, optarg, MINIBUF_SIZE);
794 for (i = strlen(optarg)-1; i >= 0; --i)
795 optarg[i] = '*';
796 break;
797 case 'R':
798 tmp = strdup(optarg);
799 head = strchr(tmp, ':');
800 if (!head) {
801 fprintf(stderr, "Invalid username:password format for -R: %s\n", tmp);
802 } else {
803 head[0] = 0;
804 users_list = hlist_add(users_list, tmp, head+1,
805 HLIST_ALLOC, HLIST_ALLOC);
807 break;
808 case 'r':
809 if (is_http_header(optarg))
810 header_list = hlist_add(header_list,
811 get_http_header_name(optarg),
812 get_http_header_value(optarg),
813 HLIST_NOALLOC, HLIST_NOALLOC);
814 break;
815 case 'S':
816 scanner_plugin = 1;
817 scanner_plugin_maxsize = atol(optarg);
818 break;
819 case 's':
821 * Do not use threads - for debugging purposes only
823 serialize = 1;
824 break;
825 case 'T':
826 tracefile = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0600);
827 if (tracefile < 0) {
828 fprintf(stderr, "Cannot create trace file.\n");
829 myexit(1);
830 } else {
831 printf("Redirecting all output to %s\n", optarg);
832 dup2(tracefile, 1);
833 dup2(tracefile, 2);
834 printf("Cntlm debug trace, version " VERSION);
835 #ifdef __CYGWIN__
836 printf(" windows/cygwin port");
837 #endif
838 printf(".\nCommand line: ");
839 for (i = 0; i < argc; ++i)
840 printf("%s ", argv[i]);
841 printf("\n");
843 break;
844 case 'U':
845 strlcpy(cuid, optarg, MINIBUF_SIZE);
846 break;
847 case 'u':
848 i = strcspn(optarg, "@");
849 if (i != strlen(optarg)) {
850 strlcpy(cuser, optarg, MIN(MINIBUF_SIZE, i+1));
851 strlcpy(cdomain, optarg+i+1, MINIBUF_SIZE);
852 } else {
853 strlcpy(cuser, optarg, MINIBUF_SIZE);
855 break;
856 case 'v':
857 debug = 1;
858 asdaemon = 0;
859 openlog("cntlm", LOG_CONS | LOG_PERROR, LOG_DAEMON);
860 break;
861 case 'w':
862 strlcpy(cworkstation, optarg, MINIBUF_SIZE);
863 break;
864 case 'h':
865 default:
866 help = 1;
871 * Help requested?
873 if (help) {
874 printf("CNTLM - Accelerating NTLM Authentication Proxy version " VERSION "\n");
875 printf("Copyright (c) 2oo7-2o1o David Kubicek\n\n"
876 "This program comes with NO WARRANTY, to the extent permitted by law. You\n"
877 "may redistribute copies of it under the terms of the GNU GPL Version 2 or\n"
878 "newer. For more information about these matters, see the file LICENSE.\n"
879 "For copyright holders of included encryption routines see headers.\n\n");
881 fprintf(stderr, "Usage: %s [-AaBcDdFfgHhILlMPpSsTUuvw] <proxy_host>[:]<proxy_port> ...\n", argv[0]);
882 fprintf(stderr, "\t-A <address>[/<net>]\n"
883 "\t ACL allow rule. IP or hostname, net must be a number (CIDR notation)\n");
884 fprintf(stderr, "\t-a ntlm | nt | lm\n"
885 "\t Authentication type - combined NTLM, just LM, or just NT. Default NTLM.\n"
886 "\t It is the most versatile setting and likely to work for you.\n");
887 fprintf(stderr, "\t-B Enable NTLM-to-basic authentication.\n");
888 fprintf(stderr, "\t-c <config_file>\n"
889 "\t Configuration file. Other arguments can be used as well, overriding\n"
890 "\t config file settings.\n");
891 fprintf(stderr, "\t-D <address>[/<net>]\n"
892 "\t ACL deny rule. Syntax same as -A.\n");
893 fprintf(stderr, "\t-d <domain>\n"
894 "\t Domain/workgroup can be set separately.\n");
895 fprintf(stderr, "\t-f Run in foreground, do not fork into daemon mode.\n");
896 fprintf(stderr, "\t-F <flags>\n"
897 "\t NTLM authentication flags.\n");
898 fprintf(stderr, "\t-G <pattern>\n"
899 "\t User-Agent matching for the trans-isa-scan plugin.\n");
900 fprintf(stderr, "\t-g Gateway mode - listen on all interfaces, not only loopback.\n");
901 fprintf(stderr, "\t-H Print password hashes for use in config file (NTLMv2 needs -u and -d).\n");
902 fprintf(stderr, "\t-h Print this help info along with version number.\n");
903 fprintf(stderr, "\t-I Prompt for the password interactively.\n");
904 fprintf(stderr, "\t-L [<saddr>:]<lport>:<rhost>:<rport>\n"
905 "\t Forwarding/tunneling a la OpenSSH. Same syntax - listen on lport\n"
906 "\t and forward all connections through the proxy to rhost:rport.\n"
907 "\t Can be used for direct tunneling without corkscrew, etc.\n");
908 fprintf(stderr, "\t-l [<saddr>:]<lport>\n"
909 "\t Main listening port for the NTLM proxy.\n");
910 fprintf(stderr, "\t-M <testurl>\n"
911 "\t Magic autodetection of proxy's NTLM dialect.\n");
912 fprintf(stderr, "\t-N \"<hostname_wildcard1>[, <hostname_wildcardN>\"\n"
913 "\t List of URL's to serve direcly as stand-alone proxy (e.g. '*.local')\n");
914 fprintf(stderr, "\t-O [<saddr>:]<lport>\n"
915 "\t Enable SOCKS5 proxy on port lport (binding to address saddr)\n");
916 fprintf(stderr, "\t-P <pidfile>\n"
917 "\t Create a PID file upon successful start.\n");
918 fprintf(stderr, "\t-p <password>\n"
919 "\t Account password. Will not be visible in \"ps\", /proc, etc.\n");
920 fprintf(stderr, "\t-r \"HeaderName: value\"\n"
921 "\t Add a header substitution. All such headers will be added/replaced\n"
922 "\t in the client's requests.\n");
923 fprintf(stderr, "\t-S <size_in_kb>\n"
924 "\t Enable automation of GFI WebMonitor ISA scanner for files < size_in_kb.\n");
925 fprintf(stderr, "\t-s Do not use threads, serialize all requests - for debugging only.\n");
926 fprintf(stderr, "\t-U <uid>\n"
927 "\t Run as uid. It is an important security measure not to run as root.\n");
928 fprintf(stderr, "\t-u <user>[@<domain]\n"
929 "\t Domain/workgroup can be set separately.\n");
930 fprintf(stderr, "\t-v Print debugging information.\n");
931 fprintf(stderr, "\t-w <workstation>\n"
932 "\t Some proxies require correct NetBIOS hostname.\n\n");
933 exit(1);
937 * More arguments on the command-line? Must be proxies.
939 i = optind;
940 while (i < argc) {
941 tmp = strchr(argv[i], ':');
942 parent_add(argv[i], !tmp && i+1 < argc ? atoi(argv[i+1]) : 0);
943 i += (!tmp ? 2 : 1);
947 * No configuration file yet? Load the default.
949 #ifdef SYSCONFDIR
950 if (!cf) {
951 #ifdef __CYGWIN__
952 tmp = getenv("PROGRAMFILES(X86)");
953 if (tmp == NULL || strlen(tmp) == 0)
954 tmp = getenv("PROGRAMFILES");
955 if (tmp == NULL)
956 tmp = "C:\\Program Files";
958 head = new(MINIBUF_SIZE);
959 strlcpy(head, tmp, MINIBUF_SIZE);
960 strlcat(head, "\\Cntlm\\cntlm.ini", MINIBUF_SIZE);
961 cf = config_open(head);
962 #else
963 cf = config_open(SYSCONFDIR "/cntlm.conf");
964 #endif
965 if (debug) {
966 if (cf)
967 printf("Default config file opened successfully\n");
968 else
969 syslog(LOG_ERR, "Could not open default config file\n");
972 #endif
975 * If any configuration file was successfully opened, parse it.
977 if (cf) {
979 * Check if gateway mode is requested before actually binding any ports.
981 tmp = new(MINIBUF_SIZE);
982 CFG_DEFAULT(cf, "Gateway", tmp, MINIBUF_SIZE);
983 if (!strcasecmp("yes", tmp))
984 gateway = 1;
985 free(tmp);
988 * Check for NTLM-to-basic settings
990 tmp = new(MINIBUF_SIZE);
991 CFG_DEFAULT(cf, "NTLMToBasic", tmp, MINIBUF_SIZE);
992 if (!strcasecmp("yes", tmp))
993 ntlmbasic = 1;
994 free(tmp);
997 * Setup the rest of tunnels.
999 while ((tmp = config_pop(cf, "Tunnel"))) {
1000 tunnel_add(&tunneld_list, tmp, gateway);
1001 free(tmp);
1005 * Bind the rest of proxy service ports.
1007 while ((tmp = config_pop(cf, "Listen"))) {
1008 listen_add("Proxy", &proxyd_list, tmp, gateway);
1009 free(tmp);
1013 * Bind the rest of SOCKS5 service ports.
1015 while ((tmp = config_pop(cf, "SOCKS5Proxy"))) {
1016 listen_add("SOCKS5 proxy", &socksd_list, tmp, gateway);
1017 free(tmp);
1021 * Accept only headers not specified on the command line.
1022 * Command line has higher priority.
1024 while ((tmp = config_pop(cf, "Header"))) {
1025 if (is_http_header(tmp)) {
1026 head = get_http_header_name(tmp);
1027 if (!hlist_in(header_list, head))
1028 header_list = hlist_add(header_list, head, get_http_header_value(tmp),
1029 HLIST_ALLOC, HLIST_NOALLOC);
1030 free(head);
1031 } else
1032 syslog(LOG_ERR, "Invalid header format: %s\n", tmp);
1034 free(tmp);
1038 * Add the rest of parent proxies.
1040 while ((tmp = config_pop(cf, "Proxy"))) {
1041 parent_add(tmp, 0);
1042 free(tmp);
1046 * No ACLs on the command line? Use config file.
1048 if (rules == NULL) {
1049 list = cf->options;
1050 while (list) {
1051 if (!(i=strcasecmp("Allow", list->key)) || !strcasecmp("Deny", list->key))
1052 if (!acl_add(&rules, list->value, i ? ACL_DENY : ACL_ALLOW))
1053 myexit(1);
1054 list = list->next;
1057 while ((tmp = config_pop(cf, "Allow")))
1058 free(tmp);
1059 while ((tmp = config_pop(cf, "Deny")))
1060 free(tmp);
1064 * Single options.
1066 CFG_DEFAULT(cf, "Auth", cauth, MINIBUF_SIZE);
1067 CFG_DEFAULT(cf, "Domain", cdomain, MINIBUF_SIZE);
1068 CFG_DEFAULT(cf, "Password", cpassword, MINIBUF_SIZE);
1069 CFG_DEFAULT(cf, "PassNTLMv2", cpassntlm2, MINIBUF_SIZE);
1070 CFG_DEFAULT(cf, "PassNT", cpassnt, MINIBUF_SIZE);
1071 CFG_DEFAULT(cf, "PassLM", cpasslm, MINIBUF_SIZE);
1072 CFG_DEFAULT(cf, "Username", cuser, MINIBUF_SIZE);
1073 CFG_DEFAULT(cf, "Workstation", cworkstation, MINIBUF_SIZE);
1075 tmp = new(MINIBUF_SIZE);
1076 CFG_DEFAULT(cf, "Flags", tmp, MINIBUF_SIZE);
1077 if (!cflags)
1078 cflags = swap32(strtoul(tmp, NULL, 0));
1079 free(tmp);
1081 tmp = new(MINIBUF_SIZE);
1082 CFG_DEFAULT(cf, "ISAScannerSize", tmp, MINIBUF_SIZE);
1083 if (!scanner_plugin_maxsize && strlen(tmp)) {
1084 scanner_plugin = 1;
1085 scanner_plugin_maxsize = atoi(tmp);
1087 free(tmp);
1089 while ((tmp = config_pop(cf, "NoProxy"))) {
1090 if (strlen(tmp)) {
1091 noproxy_list = noproxy_add(noproxy_list, tmp);
1093 free(tmp);
1096 while ((tmp = config_pop(cf, "SOCKS5Users"))) {
1097 head = strchr(tmp, ':');
1098 if (!head) {
1099 syslog(LOG_ERR, "Invalid username:password format for SOCKS5User: %s\n", tmp);
1100 } else {
1101 head[0] = 0;
1102 users_list = hlist_add(users_list, tmp, head+1, HLIST_ALLOC, HLIST_ALLOC);
1108 * Add User-Agent matching patterns.
1110 while ((tmp = config_pop(cf, "ISAScannerAgent"))) {
1111 scanner_plugin = 1;
1112 if (!scanner_plugin_maxsize)
1113 scanner_plugin_maxsize = 1;
1115 if ((i = strlen(tmp))) {
1116 head = new(i + 3);
1117 snprintf(head, i+3, "*%s*", tmp);
1118 scanner_agent_list = plist_add(scanner_agent_list, 0, head);
1120 free(tmp);
1124 * Print out unused/unknown options.
1126 list = cf->options;
1127 while (list) {
1128 syslog(LOG_INFO, "Ignoring config file option: %s\n", list->key);
1129 list = list->next;
1133 config_close(cf);
1135 if (!interactivehash && !parent_list)
1136 croak("Parent proxy address missing.\n", interactivepwd || magic_detect);
1138 if (!interactivehash && !magic_detect && !proxyd_list)
1139 croak("No proxy service ports were successfully opened.\n", interactivepwd);
1142 * Set default value for the workstation. Hostname if possible.
1144 if (!strlen(cworkstation)) {
1145 #if config_gethostname == 1
1146 gethostname(cworkstation, MINIBUF_SIZE);
1147 #endif
1148 if (!strlen(cworkstation))
1149 strlcpy(cworkstation, "cntlm", MINIBUF_SIZE);
1151 syslog(LOG_INFO, "Workstation name used: %s\n", cworkstation);
1155 * Parse selected NTLM hash combination.
1157 if (strlen(cauth)) {
1158 if (!strcasecmp("ntlm", cauth)) {
1159 g_creds->hashnt = 1;
1160 g_creds->hashlm = 1;
1161 g_creds->hashntlm2 = 0;
1162 } else if (!strcasecmp("nt", cauth)) {
1163 g_creds->hashnt = 1;
1164 g_creds->hashlm = 0;
1165 g_creds->hashntlm2 = 0;
1166 } else if (!strcasecmp("lm", cauth)) {
1167 g_creds->hashnt = 0;
1168 g_creds->hashlm = 1;
1169 g_creds->hashntlm2 = 0;
1170 } else if (!strcasecmp("ntlmv2", cauth)) {
1171 g_creds->hashnt = 0;
1172 g_creds->hashlm = 0;
1173 g_creds->hashntlm2 = 1;
1174 } else if (!strcasecmp("ntlm2sr", cauth)) {
1175 g_creds->hashnt = 2;
1176 g_creds->hashlm = 0;
1177 g_creds->hashntlm2 = 0;
1178 } else {
1179 syslog(LOG_ERR, "Unknown NTLM auth combination.\n");
1180 myexit(1);
1184 if (socksd_list && !users_list)
1185 syslog(LOG_WARNING, "SOCKS5 proxy will NOT require any authentication\n");
1187 if (!magic_detect)
1188 syslog(LOG_INFO, "Using following NTLM hashes: NTLMv2(%d) NT(%d) LM(%d)\n",
1189 g_creds->hashntlm2, g_creds->hashnt, g_creds->hashlm);
1191 if (cflags) {
1192 syslog(LOG_INFO, "Using manual NTLM flags: 0x%X\n", swap32(cflags));
1193 g_creds->flags = cflags;
1197 * Last chance to get password from the user
1199 if (interactivehash || magic_detect || (interactivepwd && !ntlmbasic)) {
1200 printf("Password: ");
1201 tcgetattr(0, &termold);
1202 termnew = termold;
1203 termnew.c_lflag &= ~(ISIG | ECHO);
1204 tcsetattr(0, TCSADRAIN, &termnew);
1205 tmp = fgets(cpassword, MINIBUF_SIZE, stdin);
1206 tcsetattr(0, TCSADRAIN, &termold);
1207 i = strlen(cpassword) - 1;
1208 if (cpassword[i] == '\n') {
1209 cpassword[i] = 0;
1210 if (cpassword[i - 1] == '\r')
1211 cpassword[i - 1] = 0;
1213 printf("\n");
1217 * Convert optional PassNT, PassLM and PassNTLMv2 strings to hashes
1218 * unless plaintext pass was used, which has higher priority.
1220 * If plain password is present, calculate its NT and LM hashes
1221 * and remove it from the memory.
1223 if (!strlen(cpassword)) {
1224 if (strlen(cpassntlm2)) {
1225 tmp = scanmem(cpassntlm2, 8);
1226 if (!tmp) {
1227 syslog(LOG_ERR, "Invalid PassNTLMv2 hash, terminating\n");
1228 exit(1);
1230 auth_memcpy(g_creds, passntlm2, tmp, 16);
1231 free(tmp);
1233 if (strlen(cpassnt)) {
1234 tmp = scanmem(cpassnt, 8);
1235 if (!tmp) {
1236 syslog(LOG_ERR, "Invalid PassNT hash, terminating\n");
1237 exit(1);
1239 auth_memcpy(g_creds, passnt, tmp, 16);
1240 free(tmp);
1242 if (strlen(cpasslm)) {
1243 tmp = scanmem(cpasslm, 8);
1244 if (!tmp) {
1245 syslog(LOG_ERR, "Invalid PassLM hash, terminating\n");
1246 exit(1);
1248 auth_memcpy(g_creds, passlm, tmp, 16);
1249 free(tmp);
1251 } else {
1252 if (g_creds->hashnt || magic_detect || interactivehash) {
1253 tmp = ntlm_hash_nt_password(cpassword);
1254 auth_memcpy(g_creds, passnt, tmp, 21);
1255 free(tmp);
1256 } if (g_creds->hashlm || magic_detect || interactivehash) {
1257 tmp = ntlm_hash_lm_password(cpassword);
1258 auth_memcpy(g_creds, passlm, tmp, 21);
1259 free(tmp);
1260 } if (g_creds->hashntlm2 || magic_detect || interactivehash) {
1261 tmp = ntlm2_hash_password(cuser, cdomain, cpassword);
1262 auth_memcpy(g_creds, passntlm2, tmp, 16);
1263 free(tmp);
1265 memset(cpassword, 0, strlen(cpassword));
1268 auth_strcpy(g_creds, user, cuser);
1269 auth_strcpy(g_creds, domain, cdomain);
1270 auth_strcpy(g_creds, workstation, cworkstation);
1272 free(cuser);
1273 free(cdomain);
1274 free(cworkstation);
1275 free(cpassword);
1276 free(cpassntlm2);
1277 free(cpassnt);
1278 free(cpasslm);
1279 free(cauth);
1282 * Try known NTLM auth combinations and print which ones work.
1283 * User can pick the best (most secure) one as his config.
1285 if (magic_detect) {
1286 magic_auth_detect(magic_detect);
1287 goto bailout;
1290 if (interactivehash) {
1291 if (g_creds->passlm) {
1292 tmp = printmem(g_creds->passlm, 16, 8);
1293 printf("PassLM %s\n", tmp);
1294 free(tmp);
1297 if (g_creds->passnt) {
1298 tmp = printmem(g_creds->passnt, 16, 8);
1299 printf("PassNT %s\n", tmp);
1300 free(tmp);
1303 if (g_creds->passntlm2) {
1304 tmp = printmem(g_creds->passntlm2, 16, 8);
1305 printf("PassNTLMv2 %s # Only for user '%s', domain '%s'\n",
1306 tmp, g_creds->user, g_creds->domain);
1307 free(tmp);
1309 goto bailout;
1313 * If we're going to need a password, check we really have it.
1315 if (!ntlmbasic && (
1316 (g_creds->hashnt && !g_creds->passnt)
1317 || (g_creds->hashlm && !g_creds->passlm)
1318 || (g_creds->hashntlm2 && !g_creds->passntlm2))) {
1319 syslog(LOG_ERR, "Parent proxy account password (or required hashes) missing.\n");
1320 myexit(1);
1324 * Ok, we are ready to rock. If daemon mode was requested,
1325 * fork and die. The child will not be group leader anymore
1326 * and can thus create a new session for itself and detach
1327 * from the controlling terminal.
1329 if (asdaemon) {
1330 if (debug)
1331 printf("Forking into background as requested.\n");
1333 i = fork();
1334 if (i == -1) {
1335 perror("Fork into background failed"); /* fork failed */
1336 myexit(1);
1337 } else if (i)
1338 myexit(0); /* parent */
1340 setsid();
1341 umask(0);
1342 w = chdir("/");
1343 i = open("/dev/null", O_RDWR);
1344 if (i >= 0) {
1345 dup2(i, 0);
1346 dup2(i, 1);
1347 dup2(i, 2);
1348 if (i > 2)
1349 close(i);
1354 * Reinit syslog logging to include our PID, after forking
1355 * it is going to be OK
1357 if (asdaemon) {
1358 openlog("cntlm", LOG_CONS | LOG_PID, LOG_DAEMON);
1359 syslog(LOG_INFO, "Daemon ready");
1360 } else {
1361 openlog("cntlm", LOG_CONS | LOG_PID | LOG_PERROR, LOG_DAEMON);
1362 syslog(LOG_INFO, "Cntlm ready, staying in the foreground");
1366 * Check and change UID.
1368 if (strlen(cuid)) {
1369 if (getuid() && geteuid()) {
1370 syslog(LOG_WARNING, "No root privileges; keeping identity %d:%d\n", getuid(), getgid());
1371 } else {
1372 if (isdigit(cuid[0])) {
1373 nuid = atoi(cuid);
1374 ngid = nuid;
1375 if (nuid <= 0) {
1376 syslog(LOG_ERR, "Numerical uid parameter invalid\n");
1377 myexit(1);
1379 } else {
1380 pw = getpwnam(cuid);
1381 if (!pw || !pw->pw_uid) {
1382 syslog(LOG_ERR, "Username %s in -U is invalid\n", cuid);
1383 myexit(1);
1385 nuid = pw->pw_uid;
1386 ngid = pw->pw_gid;
1388 setgid(ngid);
1389 i = setuid(nuid);
1390 syslog(LOG_INFO, "Changing uid:gid to %d:%d - %s\n", nuid, ngid, strerror(errno));
1391 if (i) {
1392 syslog(LOG_ERR, "Terminating\n");
1393 myexit(1);
1399 * PID file requested? Try to create one (it must not exist).
1400 * If we fail, exit with error.
1402 if (strlen(cpidfile)) {
1403 umask(0);
1404 cd = open(cpidfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
1405 if (cd < 0) {
1406 syslog(LOG_ERR, "Error creating a new PID file\n");
1407 myexit(1);
1410 tmp = new(50);
1411 snprintf(tmp, 50, "%d\n", getpid());
1412 w = write(cd, tmp, strlen(tmp));
1413 free(tmp);
1414 close(cd);
1418 * Change the handler for signals recognized as clean shutdown.
1419 * When the handler is called (termination request), it signals
1420 * this news by adding 1 to the global quit variable.
1422 signal(SIGPIPE, SIG_IGN);
1423 signal(SIGINT, &sighandler);
1424 signal(SIGTERM, &sighandler);
1425 signal(SIGHUP, &sighandler);
1428 * Initialize the random number generator
1430 srandom(time(NULL));
1433 * This loop iterates over every connection request on any of
1434 * the listening ports. We keep the number of created threads.
1436 * We also check the "finished threads" list, threads_list, here and
1437 * free the memory of all inactive threads. Then, we update the
1438 * number of finished threads.
1440 * The loop ends, when we were "killed" and all threads created
1441 * are finished, OR if we were killed more than once. This way,
1442 * we have a "clean" shutdown (wait for all connections to finish
1443 * after the first kill) and a "forced" one (user insists and
1444 * killed us twice).
1446 while (quit == 0 || (tc != tj && quit < 2)) {
1447 struct thread_arg_s *data;
1448 struct sockaddr_in caddr;
1449 struct timeval tv;
1450 socklen_t clen;
1451 fd_set set;
1452 plist_t t;
1453 int tid = 0;
1455 FD_ZERO(&set);
1458 * Watch for proxy ports.
1460 t = proxyd_list;
1461 while (t) {
1462 FD_SET(t->key, &set);
1463 t = t->next;
1467 * Watch for SOCKS5 ports.
1469 t = socksd_list;
1470 while (t) {
1471 FD_SET(t->key, &set);
1472 t = t->next;
1476 * Watch for tunneled ports.
1478 t = tunneld_list;
1479 while (t) {
1480 FD_SET(t->key, &set);
1481 t = t->next;
1484 tv.tv_sec = 1;
1485 tv.tv_usec = 0;
1488 * Wait here for data (connection request) on any of the listening
1489 * sockets. When ready, establish the connection. For the main
1490 * port, a new proxy_thread() thread is spawned to service the HTTP
1491 * request. For tunneled ports, tunnel_thread() thread is created
1492 * and for SOCKS port, socks5_thread() is created.
1494 * All threads are defined in forward.c, except for local proxy_thread()
1495 * which routes the request as forwarded or direct, depending on the
1496 * URL host name and NoProxy settings.
1498 cd = select(FD_SETSIZE, &set, NULL, NULL, &tv);
1499 if (cd > 0) {
1500 for (i = 0; i < FD_SETSIZE; ++i) {
1501 if (!FD_ISSET(i, &set))
1502 continue;
1504 clen = sizeof(caddr);
1505 cd = accept(i, (struct sockaddr *)&caddr, (socklen_t *)&clen);
1507 if (cd < 0) {
1508 syslog(LOG_ERR, "Serious error during accept: %s\n", strerror(errno));
1509 continue;
1513 * Check main access control list.
1515 if (acl_check(rules, caddr.sin_addr) != ACL_ALLOW) {
1516 syslog(LOG_WARNING, "Connection denied for %s:%d\n",
1517 inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
1518 tmp = gen_denied_page(inet_ntoa(caddr.sin_addr));
1519 w = write(cd, tmp, strlen(tmp));
1520 free(tmp);
1521 close(cd);
1522 continue;
1526 * Log peer IP if it's not localhost
1528 * if (debug || (gateway && caddr.sin_addr.s_addr != htonl(INADDR_LOOPBACK)))
1529 * syslog(LOG_INFO, "Connection accepted from %s:%d\n",
1530 * inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
1533 pthread_attr_init(&pattr);
1534 pthread_attr_setstacksize(&pattr, STACK_SIZE);
1535 #ifndef __CYGWIN__
1536 pthread_attr_setguardsize(&pattr, 256);
1537 #endif
1539 if (plist_in(proxyd_list, i)) {
1540 data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s));
1541 data->fd = cd;
1542 data->addr = caddr;
1543 if (!serialize)
1544 tid = pthread_create(&pthr, &pattr, proxy_thread, (void *)data);
1545 else
1546 proxy_thread((void *)data);
1547 } else if (plist_in(socksd_list, i)) {
1548 data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s));
1549 data->fd = cd;
1550 data->addr = caddr;
1551 tid = pthread_create(&pthr, &pattr, socks5_thread, (void *)data);
1552 } else {
1553 data = (struct thread_arg_s *)new(sizeof(struct thread_arg_s));
1554 data->fd = cd;
1555 data->addr = caddr;
1556 data->target = plist_get(tunneld_list, i);
1557 tid = pthread_create(&pthr, &pattr, tunnel_thread, (void *)data);
1560 pthread_attr_destroy(&pattr);
1562 if (tid)
1563 syslog(LOG_ERR, "Serious error during pthread_create: %d\n", tid);
1564 else
1565 tc++;
1567 } else if (cd < 0 && !quit)
1568 syslog(LOG_ERR, "Serious error during select: %s\n", strerror(errno));
1570 if (threads_list) {
1571 pthread_mutex_lock(&threads_mtx);
1572 t = threads_list;
1573 while (t) {
1574 plist_t tmp = t->next;
1575 tid = pthread_join((pthread_t)t->key, (void *)&i);
1577 if (!tid) {
1578 tj++;
1579 if (debug)
1580 printf("Joining thread %lu; rc: %d\n", t->key, i);
1581 } else
1582 syslog(LOG_ERR, "Serious error during pthread_join: %d\n", tid);
1584 free(t);
1585 t = tmp;
1587 threads_list = NULL;
1588 pthread_mutex_unlock(&threads_mtx);
1592 bailout:
1593 if (strlen(cpidfile))
1594 unlink(cpidfile);
1596 syslog(LOG_INFO, "Terminating with %d active threads\n", tc - tj);
1597 pthread_mutex_lock(&connection_mtx);
1598 plist_free(connection_list);
1599 pthread_mutex_unlock(&connection_mtx);
1601 hlist_free(header_list);
1602 plist_free(scanner_agent_list);
1603 plist_free(noproxy_list);
1604 plist_free(tunneld_list);
1605 plist_free(proxyd_list);
1606 plist_free(socksd_list);
1607 plist_free(rules);
1609 free(cuid);
1610 free(cpidfile);
1611 free(magic_detect);
1612 free(g_creds);
1614 plist_free(parent_list);
1616 exit(0);