fix missing bracket in solaris codepath (#589)
[rofl0r-proxychains-ng.git] / src / daemon / daemon.c
bloba0e8f2a493aa3a1db8ff24965788fbf46a10fa39
1 /*
2 proxychains-ng DNS daemon
4 Copyright (C) 2020 rofl0r.
6 */
8 #define _GNU_SOURCE
9 #include <unistd.h>
10 #define _POSIX_C_SOURCE 200809L
11 #include <stdlib.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <pthread.h>
15 #include <signal.h>
16 #include <sys/select.h>
17 #include <arpa/inet.h>
18 #include <errno.h>
19 #include <limits.h>
20 #include "udpserver.h"
21 #include "sblist.h"
22 #include "hsearch.h"
23 #include "../remotedns.h"
24 #include "../ip_type.h"
26 #ifndef MAX
27 #define MAX(x, y) ((x) > (y) ? (x) : (y))
28 #endif
30 static struct htab *ip_lookup_table;
31 static sblist *hostnames;
32 static unsigned remote_subnet;
33 static const struct server* server;
35 #ifndef CONFIG_LOG
36 #define CONFIG_LOG 1
37 #endif
38 #if CONFIG_LOG
39 /* we log to stderr because it's not using line buffering, i.e. malloc which would need
40 locking when called from different threads. for the same reason we use dprintf,
41 which writes directly to an fd. */
42 #define dolog(...) dprintf(2, __VA_ARGS__)
43 #else
44 static void dolog(const char* fmt, ...) { }
45 #endif
47 static char* my_inet_ntoa(unsigned char *ip_buf_4_bytes, char *outbuf_16_bytes) {
48 unsigned char *p;
49 char *o = outbuf_16_bytes;
50 unsigned char n;
51 for(p = ip_buf_4_bytes; p < ip_buf_4_bytes + 4; p++) {
52 n = *p;
53 if(*p >= 100) {
54 if(*p >= 200)
55 *(o++) = '2';
56 else
57 *(o++) = '1';
58 n %= 100;
60 if(*p >= 10) {
61 *(o++) = (n / 10) + '0';
62 n %= 10;
64 *(o++) = n + '0';
65 *(o++) = '.';
67 o[-1] = 0;
68 return outbuf_16_bytes;
72 /* buf needs to be long enough for an ipv6 addr, i.e. INET6_ADDRSTRLEN + 1 */
73 static char* ipstr(union sockaddr_union *su, char* buf) {
74 int af = SOCKADDR_UNION_AF(su);
75 void *ipdata = SOCKADDR_UNION_ADDRESS(su);
76 inet_ntop(af, ipdata, buf, INET6_ADDRSTRLEN+1);
77 char portbuf[7];
78 snprintf(portbuf, sizeof portbuf, ":%u", (unsigned) ntohs(SOCKADDR_UNION_PORT(su)));
79 strcat(buf, portbuf);
80 return buf;
83 static int usage(char *a0) {
84 dprintf(2,
85 "Proxychains-NG remote dns daemon\n"
86 "--------------------------------\n"
87 "usage: %s -i listenip -p port -r remotesubnet\n"
88 "all arguments are optional.\n"
89 "by default listenip is 127.0.0.1, port 1053 and remotesubnet 224.\n\n", a0
91 return 1;
94 unsigned index_from_ip(ip_type4 internalip) {
95 ip_type4 tmp = internalip;
96 uint32_t ret;
97 ret = tmp.octet[3] + (tmp.octet[2] << 8) + (tmp.octet[1] << 16);
98 ret -= 1;
99 return ret;
102 char *host_from_ip(ip_type4 internalip) {
103 char *res = NULL;
104 unsigned index = index_from_ip(internalip);
105 if(index < sblist_getsize(hostnames)) {
106 char **tmp = sblist_get(hostnames, index);
107 if(tmp && *tmp) res = *tmp;
109 return res;
112 ip_type4 get_ip_from_index(unsigned index) {
113 ip_type4 ret;
114 index++; // so we can start at .0.0.1
115 if(index > 0xFFFFFF)
116 return IPT4_INVALID;
117 ret.octet[0] = remote_subnet & 0xFF;
118 ret.octet[1] = (index & 0xFF0000) >> 16;
119 ret.octet[2] = (index & 0xFF00) >> 8;
120 ret.octet[3] = index & 0xFF;
121 return ret;
124 ip_type4 get_ip(char* hn) {
125 htab_value *v = htab_find(ip_lookup_table, hn);
126 if(v) return get_ip_from_index(v->n);
127 char *n = strdup(hn);
128 if(!n) return IPT4_INVALID;
129 if(!sblist_add(hostnames, &n)) {
130 o_out:;
131 free(n);
132 return IPT4_INVALID;
134 if(!htab_insert(ip_lookup_table, n, HTV_N(sblist_getsize(hostnames)-1))) {
135 sblist_delete(hostnames, sblist_getsize(hostnames)-1);
136 goto o_out;
138 return get_ip_from_index(sblist_getsize(hostnames)-1);
141 int main(int argc, char** argv) {
142 int ch;
143 const char *listenip = "127.0.0.1";
144 unsigned port = 1053;
145 remote_subnet = 224;
146 while((ch = getopt(argc, argv, ":r:i:p:")) != -1) {
147 switch(ch) {
148 case 'r':
149 remote_subnet = atoi(optarg);
150 break;
151 case 'i':
152 listenip = optarg;
153 break;
154 case 'p':
155 port = atoi(optarg);
156 break;
157 case ':':
158 dprintf(2, "error: option -%c requires an operand\n", optopt);
159 /* fall through */
160 case '?':
161 return usage(argv[0]);
164 signal(SIGPIPE, SIG_IGN);
165 struct server s;
166 if(server_setup(&s, listenip, port)) {
167 perror("server_setup");
168 return 1;
170 server = &s;
172 ip_lookup_table = htab_create(64);
173 hostnames = sblist_new(sizeof(char*), 64);
175 while(1) {
176 struct client c;
177 char ipstr_buf[INET6_ADDRSTRLEN+6+1];
178 char ip4str_buf[16];
179 struct at_msg msg, out;
180 size_t msgl = sizeof(msg);
181 int failed = 0;
183 #define FAIL() do { failed=1; goto sendresp; } while(0)
185 if(server_waitclient(&s, &c, &msg, &msgl)) continue;
186 msg.h.datalen = ntohs(msg.h.datalen);
187 if(msgl != sizeof(msg.h)+msg.h.datalen) {
188 dolog("%s: invalid datalen\n", ipstr(&c.addr, ipstr_buf));
189 FAIL();
192 out.h.msgtype = msg.h.msgtype;
193 if(msg.h.msgtype == ATM_GETIP) {
194 if(!memchr(msg.m.host, 0, msg.h.datalen)) {
195 dolog("%s: nul terminator missing\n", ipstr(&c.addr, ipstr_buf));
196 FAIL();
198 out.h.datalen = sizeof(ip_type4);
199 out.m.ip = get_ip(msg.m.host);
200 failed = !memcmp(&out.m.ip, &IPT4_INVALID, 4);
201 dolog("%s requested ip for %s (%s)\n", ipstr(&c.addr, ipstr_buf),
202 msg.m.host, failed?"FAIL":my_inet_ntoa((void*)&out.m.ip, ip4str_buf));
203 if(failed) FAIL();
204 } else if (msg.h.msgtype == ATM_GETNAME) {
205 if(msg.h.datalen != 4) {
206 dolog("%s: invalid len for getname request\n", ipstr(&c.addr, ipstr_buf));
207 FAIL();
209 char *hn = host_from_ip(msg.m.ip);
210 if(hn) {
211 size_t l = strlen(hn);
212 memcpy(out.m.host, hn, l+1);
213 out.h.datalen = l+1;
215 dolog("%s requested name for %s (%s)\n", ipstr(&c.addr, ipstr_buf),
216 my_inet_ntoa((void*) &msg.m.ip, ip4str_buf), hn?hn:"FAIL");
217 if(!hn) FAIL();
218 } else {
219 dolog("%s: unknown request %u\n", ipstr(&c.addr, ipstr_buf),
220 (unsigned) msg.h.msgtype);
222 sendresp:;
223 if(failed) {
224 out.h.msgtype = ATM_FAIL;
225 out.h.datalen = 0;
227 unsigned short dlen = out.h.datalen;
228 out.h.datalen = htons(dlen);
229 sendto(server->fd, &out, sizeof(out.h)+dlen, 0, (void*) &c.addr, SOCKADDR_UNION_LENGTH(&c.addr));