1 | /* |
2 | This file is part of Mtproto-proxy Library. |
3 | |
4 | Mtproto-proxy Library is free software: you can redistribute it and/or modify |
5 | it under the terms of the GNU Lesser General Public License as published by |
6 | the Free Software Foundation, either version 2 of the License, or |
7 | (at your option) any later version. |
8 | |
9 | Mtproto-proxy Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | GNU Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public License |
15 | along with Mtproto-proxy Library. If not, see <http://www.gnu.org/licenses/>. |
16 | |
17 | Copyright 2010-2013 Vkontakte Ltd |
18 | 2010-2013 Nikolai Durov |
19 | 2010-2013 Andrey Lopatin |
20 | */ |
21 | |
22 | #define _FILE_OFFSET_BITS 64 |
23 | |
24 | #include <assert.h> |
25 | #include <string.h> |
26 | #include <stdio.h> |
27 | #include <stdlib.h> |
28 | #include <unistd.h> |
29 | #include <fcntl.h> |
30 | #include <sys/types.h> |
31 | #include <sys/stat.h> |
32 | #include <errno.h> |
33 | #include <netdb.h> |
34 | |
35 | #include "kprintf.h" |
36 | #include "resolver.h" |
37 | |
38 | #define HOSTS_FILE "/etc/hosts" |
39 | #define MAX_HOSTS_SIZE (1L << 24) |
40 | |
41 | int kdb_hosts_loaded; |
42 | |
43 | static int pr[] = { |
44 | 29, |
45 | 41, |
46 | 59, |
47 | 89, |
48 | 131, |
49 | 197, |
50 | 293, |
51 | 439, |
52 | 659, |
53 | 991, |
54 | 1481, |
55 | 2221, |
56 | 3329, |
57 | 4993, |
58 | 7487, |
59 | 11239, |
60 | 16843, |
61 | 25253, |
62 | 37879, |
63 | 56821, |
64 | 85223, |
65 | 127837, |
66 | 191773, |
67 | 287629, |
68 | 431441, |
69 | 647161, |
70 | 970747, |
71 | 1456121, |
72 | 2184179, |
73 | 3276253, |
74 | 4914373, |
75 | 7371571, |
76 | 11057357, |
77 | 16586039, |
78 | 24879017, |
79 | 37318507 |
80 | }; |
81 | |
82 | #pragma pack(push,1) |
83 | struct host { |
84 | unsigned ip; |
85 | char len; |
86 | char name[]; |
87 | }; |
88 | #pragma pack(pop) |
89 | |
90 | |
91 | static unsigned ipaddr; |
92 | static char *h_array[] = {(char *)&ipaddr, 0}; |
93 | static struct hostent hret = { |
94 | .h_aliases = 0, |
95 | .h_addrtype = AF_INET, |
96 | .h_length = 4, |
97 | .h_addr_list = h_array |
98 | }; |
99 | |
100 | /* |
101 | static unsigned char ipv6_addr[16]; |
102 | static char *h_array6[] = {(char *)ipv6_addr, 0}; |
103 | static struct hostent hret6 = { |
104 | .h_aliases = 0, |
105 | .h_addrtype = AF_INET6, |
106 | .h_length = 16, |
107 | .h_addr_list = h_array6 |
108 | }; |
109 | */ |
110 | |
111 | static struct resolver_conf { |
112 | int hosts_loaded; |
113 | int hsize; |
114 | struct host **htable; |
115 | long long fsize; |
116 | int ftime; |
117 | } Hosts, Hosts_new; |
118 | |
119 | static struct host *getHash (struct resolver_conf *R, const char *name, int len, unsigned ip) { |
120 | int h1 = 0, h2 = 0, i; |
121 | |
122 | assert ((unsigned)len < 128); |
123 | |
124 | for (i = 0; i < len; i++) { |
125 | h1 = (h1 * 17 + name[i]) % R->hsize; |
126 | h2 = (h2 * 239 + name[i]) % (R->hsize - 1); |
127 | } |
128 | ++h2; |
129 | while (R->htable[h1]) { |
130 | if (len == R->htable[h1]->len && !memcmp (R->htable[h1]->name, name, len)) { |
131 | return R->htable[h1]; |
132 | } |
133 | h1 += h2; |
134 | if (h1 >= R->hsize) { |
135 | h1 -= R->hsize; |
136 | } |
137 | } |
138 | if (!ip) { |
139 | return 0; |
140 | } |
141 | struct host *tmp = malloc (len + sizeof (struct host)); |
142 | assert (tmp); |
143 | tmp->ip = ip; |
144 | tmp->len = len; |
145 | memcpy (tmp->name, name, len); |
146 | return R->htable[h1] = tmp; |
147 | } |
148 | |
149 | static void free_resolver_data (struct resolver_conf *R) { |
150 | int s = R->hsize, i; |
151 | struct host **htable = R->htable; |
152 | if (htable) { |
153 | assert (s > 0); |
154 | for (i = 0; i < s; i++) { |
155 | struct host *tmp = htable[i]; |
156 | if (tmp) { |
157 | free (tmp); |
158 | htable[i] = 0; |
159 | } |
160 | } |
161 | free (htable); |
162 | R->htable = 0; |
163 | R->hsize = 0; |
164 | } |
165 | R->hosts_loaded = 0; |
166 | } |
167 | |
168 | |
169 | static char *skipspc (char *ptr) { |
170 | while (*ptr == ' ' || *ptr == '\t') { |
171 | ++ptr; |
172 | } |
173 | return ptr; |
174 | } |
175 | |
176 | |
177 | static char *skiptoeoln (char *ptr) { |
178 | while (*ptr && *ptr != '\n') { |
179 | ++ptr; |
180 | } |
181 | if (*ptr) { |
182 | ++ptr; |
183 | } |
184 | return ptr; |
185 | } |
186 | |
187 | |
188 | static char *getword (char **ptr, int *len) { |
189 | char *start = skipspc (*ptr), *tmp = start; |
190 | |
191 | while (*tmp && *tmp != ' ' && *tmp != '\t' && *tmp != '\n') { |
192 | ++tmp; |
193 | } |
194 | |
195 | *ptr = tmp; |
196 | *len = tmp - start; |
197 | |
198 | if (!*len) { |
199 | return 0; |
200 | } |
201 | |
202 | return start; |
203 | } |
204 | |
205 | |
206 | static int readbyte (char **ptr) { |
207 | char *tmp; |
208 | unsigned val = strtoul (*ptr, &tmp, 10); |
209 | if (tmp == *ptr || val > 255) { |
210 | return -1; |
211 | } |
212 | *ptr = tmp; |
213 | return val; |
214 | } |
215 | |
216 | |
217 | static int parse_hosts (struct resolver_conf *R, char *data, int mode) { |
218 | char *ptr; |
219 | int ans = 0; |
220 | |
221 | for (ptr = data; *ptr; ptr = skiptoeoln (ptr)) { |
222 | ptr = skipspc (ptr); |
223 | int i; |
224 | unsigned ip = 0; |
225 | |
226 | for (i = 0; i < 4; i++) { |
227 | int res = readbyte (&ptr); |
228 | if (res < 0) { |
229 | break; |
230 | } |
231 | ip = (ip << 8) | res; |
232 | if (i < 3 && *ptr++ != '.') { |
233 | break; |
234 | } |
235 | } |
236 | |
237 | //fprintf (stderr, "ip = %08x, i = %d\n", ip, i); |
238 | |
239 | if (i < 4 || (*ptr != ' ' && *ptr != '\t') || !ip) { |
240 | continue; |
241 | } |
242 | |
243 | char *word; |
244 | int wordlen; |
245 | |
246 | do { |
247 | word = getword (&ptr, &wordlen); |
248 | if (word && wordlen < 128) { |
249 | //fprintf (stderr, "word = %.*s\n", wordlen, word); |
250 | if (mode) { |
251 | getHash (R, word, wordlen, ip); |
252 | } |
253 | ++ans; |
254 | } |
255 | } while (word); |
256 | } |
257 | return ans; |
258 | } |
259 | |
260 | |
261 | static int kdb_load_hosts_internal (void) { |
262 | static struct stat s; |
263 | long long r; |
264 | int fd; |
265 | char *data; |
266 | |
267 | if (stat (HOSTS_FILE, &s) < 0) { |
268 | return Hosts_new.hosts_loaded = -1; |
269 | } |
270 | if (!S_ISREG (s.st_mode)) { |
271 | return Hosts_new.hosts_loaded = -1; |
272 | } |
273 | if (Hosts.hosts_loaded > 0 && Hosts.fsize == s.st_size && Hosts.ftime == s.st_mtime) { |
274 | return 0; |
275 | } |
276 | if (s.st_size >= MAX_HOSTS_SIZE) { |
277 | return Hosts_new.hosts_loaded = -1; |
278 | } |
279 | fd = open (HOSTS_FILE, O_RDONLY); |
280 | if (fd < 0) { |
281 | return Hosts_new.hosts_loaded = -1; |
282 | } |
283 | Hosts_new.fsize = s.st_size; |
284 | Hosts_new.ftime = s.st_mtime; |
285 | data = malloc (s.st_size + 1); |
286 | if (!data) { |
287 | close (fd); |
288 | return Hosts_new.hosts_loaded = -1; |
289 | } |
290 | r = read (fd, data, s.st_size + 1); |
291 | if (verbosity > 1) { |
292 | fprintf (stderr, "read %lld of %lld bytes of " HOSTS_FILE"\n" , r, Hosts_new.fsize); |
293 | } |
294 | close (fd); |
295 | if (r != s.st_size) { |
296 | free (data); |
297 | return Hosts_new.hosts_loaded = -1; |
298 | } |
299 | data[s.st_size] = 0; |
300 | |
301 | int ans = parse_hosts (&Hosts_new, data, 0), i; |
302 | |
303 | for (i = 0; i < sizeof (pr) / sizeof (int); i++) { |
304 | if (pr[i] > ans * 2) { |
305 | break; |
306 | } |
307 | } |
308 | |
309 | if (i >= sizeof (pr) / sizeof (int)) { |
310 | free (data); |
311 | return Hosts_new.hosts_loaded = -1; |
312 | } |
313 | Hosts_new.hsize = pr[i]; |
314 | |
315 | if (verbosity > 1) { |
316 | fprintf (stderr, "IP table hash size: %d (for %d entries)\n" , Hosts_new.hsize, ans); |
317 | } |
318 | |
319 | Hosts_new.htable = malloc (sizeof (void *) * Hosts_new.hsize); |
320 | assert (Hosts_new.htable); |
321 | |
322 | memset (Hosts_new.htable, 0, sizeof (void *) * Hosts_new.hsize); |
323 | |
324 | int res = parse_hosts (&Hosts_new, data, 1); |
325 | assert (res == ans); |
326 | |
327 | free (data); |
328 | return Hosts_new.hosts_loaded = 1; |
329 | } |
330 | |
331 | int kdb_load_hosts (void) { |
332 | int res = kdb_load_hosts_internal (); |
333 | if (res < 0) { |
334 | if (kdb_hosts_loaded <= 0) { |
335 | kdb_hosts_loaded = res; |
336 | } |
337 | return kdb_hosts_loaded < 0 ? -1 : 0; |
338 | } |
339 | if (!res) { |
340 | assert (kdb_hosts_loaded > 0); |
341 | return 0; |
342 | } |
343 | assert (Hosts_new.hosts_loaded > 0); |
344 | if (kdb_hosts_loaded > 0) { |
345 | assert (Hosts.hosts_loaded > 0); |
346 | free_resolver_data (&Hosts); |
347 | } |
348 | memcpy (&Hosts, &Hosts_new, sizeof (Hosts)); |
349 | memset (&Hosts_new, 0, sizeof (Hosts)); |
350 | kdb_hosts_loaded = Hosts.hosts_loaded; |
351 | |
352 | return 1; |
353 | } |
354 | |
355 | int parse_ipv6 (unsigned short ipv6[8], char *str) { |
356 | return -1; |
357 | } |
358 | |
359 | struct hostent *kdb_gethostbyname (const char *name) { |
360 | if (!kdb_hosts_loaded) { |
361 | kdb_load_hosts (); |
362 | } |
363 | |
364 | int len = strlen (name); |
365 | |
366 | |
367 | if (name[0] == '[' && name[len-1] == ']' && len <= 64) { |
368 | /* |
369 | if (parse_ipv6 ((unsigned short *) ipv6_addr, name + 1) == len - 2) { |
370 | hret6.h_name = (char *)name; |
371 | return &hret6; |
372 | } |
373 | */ |
374 | char buf[64]; |
375 | memcpy (buf, name + 1, len - 2); |
376 | buf[len - 2] = 0; |
377 | return gethostbyname2 (buf, AF_INET6); |
378 | } |
379 | |
380 | if (kdb_hosts_loaded <= 0) { |
381 | return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6); |
382 | } |
383 | |
384 | if (len >= 128) { |
385 | return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6); |
386 | } |
387 | |
388 | struct host *res = getHash (&Hosts, name, len, 0); |
389 | |
390 | |
391 | if (!res) { |
392 | if (strchr (name, '.') || strchr (name, ':')) { |
393 | return gethostbyname (name) ?: gethostbyname2 (name, AF_INET6); |
394 | } else { |
395 | return 0; |
396 | } |
397 | } |
398 | |
399 | hret.h_name = (char *)name; |
400 | ipaddr = htonl (res->ip); |
401 | return &hret; |
402 | } |
403 | |
404 | char *detect_hostname (void) { |
405 | static char *hostname = NULL; |
406 | static char hostname_buffer[256]; |
407 | int r, i; |
408 | if (!hostname || !*hostname) { |
409 | hostname = getenv ("HOSTNAME" ); |
410 | if (!hostname || !*hostname) { |
411 | int fd = open ("/etc/hostname" , O_RDONLY); |
412 | if (fd < 0) { |
413 | kprintf ("cannot read /etc/hostname: %m\n" ); |
414 | exit (2); |
415 | } |
416 | r = read (fd, hostname_buffer, 256); |
417 | if (r <= 0 || r >= 256) { |
418 | kprintf ("cannot read hostname from /etc/hostname: %d bytes read\n" , r); |
419 | exit (2); |
420 | } |
421 | hostname_buffer[r] = 0; |
422 | close (fd); |
423 | hostname = hostname_buffer; |
424 | while (*hostname == 9 || *hostname == 32) { |
425 | hostname++; |
426 | } |
427 | i = 0; |
428 | while (hostname[i] > 32) { |
429 | i++; |
430 | } |
431 | hostname[i] = 0; |
432 | } |
433 | } |
434 | if (!hostname || !*hostname) { |
435 | kprintf ("fatal: cannot detect hostname\n" ); |
436 | exit (2); |
437 | } |
438 | i = 0; |
439 | while ((hostname[i] >= '0' && hostname[i] <= '9') || hostname[i] == '.' || hostname[i] == '-' || hostname[i] == '_' || (hostname[i] >= 'A' && hostname[i] <= 'Z') || (hostname[i] >= 'a' && hostname[i] <= 'z')) { |
440 | i++; |
441 | } |
442 | if (hostname[i] || i >= 64) { |
443 | kprintf ("fatal: bad hostname '%s'\n" , hostname); |
444 | exit (2); |
445 | } |
446 | vkprintf (1, "hostname is %s\n" , hostname); |
447 | return hostname; |
448 | } |
449 | |