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
41int kdb_hosts_loaded;
42
43static int pr[] = {
4429,
4541,
4659,
4789,
48131,
49197,
50293,
51439,
52659,
53991,
541481,
552221,
563329,
574993,
587487,
5911239,
6016843,
6125253,
6237879,
6356821,
6485223,
65127837,
66191773,
67287629,
68431441,
69647161,
70970747,
711456121,
722184179,
733276253,
744914373,
757371571,
7611057357,
7716586039,
7824879017,
7937318507
80};
81
82#pragma pack(push,1)
83struct host {
84 unsigned ip;
85 char len;
86 char name[];
87};
88#pragma pack(pop)
89
90
91static unsigned ipaddr;
92static char *h_array[] = {(char *)&ipaddr, 0};
93static 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/*
101static unsigned char ipv6_addr[16];
102static char *h_array6[] = {(char *)ipv6_addr, 0};
103static 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
111static 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
119static 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
149static 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
169static char *skipspc (char *ptr) {
170 while (*ptr == ' ' || *ptr == '\t') {
171 ++ptr;
172 }
173 return ptr;
174}
175
176
177static char *skiptoeoln (char *ptr) {
178 while (*ptr && *ptr != '\n') {
179 ++ptr;
180 }
181 if (*ptr) {
182 ++ptr;
183 }
184 return ptr;
185}
186
187
188static 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
206static 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
217static 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
261static 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
331int 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
355int parse_ipv6 (unsigned short ipv6[8], char *str) {
356 return -1;
357}
358
359struct 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
404char *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