gnunet-zonewalk.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. /*
  2. This file is part of GNUnet
  3. Copyright (C) 2018 GNUnet e.V.
  4. GNUnet is free software: you can redistribute it and/or modify it
  5. under the terms of the GNU Affero General Public License as published
  6. by the Free Software Foundation, either version 3 of the License,
  7. or (at your option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Affero General Public License for more details.
  12. You should have received a copy of the GNU Affero General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. SPDX-License-Identifier: AGPL3.0-or-later
  15. */
  16. /**
  17. * @file src/dns/gnunet-zoneimport.c
  18. * @brief import a DNS zone for analysis, brute force
  19. * @author Christian Grothoff
  20. */
  21. #include "platform.h"
  22. #include <gnunet_util_lib.h>
  23. #include <gnunet_dnsstub_lib.h>
  24. #include <gnunet_dnsparser_lib.h>
  25. /**
  26. * Request we should make.
  27. */
  28. struct Request
  29. {
  30. /**
  31. * Requests are kept in a DLL.
  32. */
  33. struct Request *next;
  34. /**
  35. * Requests are kept in a DLL.
  36. */
  37. struct Request *prev;
  38. /**
  39. * Socket used to make the request, NULL if not active.
  40. */
  41. struct GNUNET_DNSSTUB_RequestSocket *rs;
  42. /**
  43. * Raw DNS query.
  44. */
  45. void *raw;
  46. /**
  47. * Number of bytes in @e raw.
  48. */
  49. size_t raw_len;
  50. /**
  51. * Hostname we are resolving.
  52. */
  53. char *hostname;
  54. /**
  55. * When did we last issue this request?
  56. */
  57. time_t time;
  58. /**
  59. * How often did we issue this query?
  60. */
  61. int issue_num;
  62. /**
  63. * random 16-bit DNS query identifier.
  64. */
  65. uint16_t id;
  66. };
  67. /**
  68. * Context for DNS resolution.
  69. */
  70. static struct GNUNET_DNSSTUB_Context *ctx;
  71. /**
  72. * The number of queries that are outstanding
  73. */
  74. static unsigned int pending;
  75. /**
  76. * Number of lookups we performed overall.
  77. */
  78. static unsigned int lookups;
  79. /**
  80. * Number of lookups that failed.
  81. */
  82. static unsigned int failures;
  83. /**
  84. * Number of records we found.
  85. */
  86. static unsigned int records;
  87. /**
  88. * Head of DLL of all requests to perform.
  89. */
  90. static struct Request *req_head;
  91. /**
  92. * Tail of DLL of all requests to perform.
  93. */
  94. static struct Request *req_tail;
  95. /**
  96. * Main task.
  97. */
  98. static struct GNUNET_SCHEDULER_Task *t;
  99. /**
  100. * Maximum number of queries pending at the same time.
  101. */
  102. #define THRESH 20
  103. /**
  104. * TIME_THRESH is in usecs. How quickly do we submit fresh queries.
  105. * Used as an additional throttle.
  106. */
  107. #define TIME_THRESH 10
  108. /**
  109. * How often do we retry a query before giving up for good?
  110. */
  111. #define MAX_RETRIES 5
  112. /**
  113. * We received @a rec for @a req. Remember the answer.
  114. *
  115. * @param req request
  116. * @param rec response
  117. */
  118. static void
  119. process_record (struct Request *req,
  120. struct GNUNET_DNSPARSER_Record *rec)
  121. {
  122. char buf[INET6_ADDRSTRLEN];
  123. records++;
  124. switch (rec->type)
  125. {
  126. case GNUNET_DNSPARSER_TYPE_A:
  127. fprintf (stdout,
  128. "%s A %s\n",
  129. req->hostname,
  130. inet_ntop (AF_INET,
  131. rec->data.raw.data,
  132. buf,
  133. sizeof (buf)));
  134. break;
  135. case GNUNET_DNSPARSER_TYPE_AAAA:
  136. fprintf (stdout,
  137. "%s AAAA %s\n",
  138. req->hostname,
  139. inet_ntop (AF_INET6,
  140. rec->data.raw.data,
  141. buf,
  142. sizeof (buf)));
  143. break;
  144. case GNUNET_DNSPARSER_TYPE_NS:
  145. fprintf (stdout,
  146. "%s NS %s\n",
  147. req->hostname,
  148. rec->data.hostname);
  149. break;
  150. case GNUNET_DNSPARSER_TYPE_CNAME:
  151. fprintf (stdout,
  152. "%s CNAME %s\n",
  153. req->hostname,
  154. rec->data.hostname);
  155. break;
  156. case GNUNET_DNSPARSER_TYPE_MX:
  157. fprintf (stdout,
  158. "%s MX %u %s\n",
  159. req->hostname,
  160. (unsigned int) rec->data.mx->preference,
  161. rec->data.mx->mxhost);
  162. break;
  163. case GNUNET_DNSPARSER_TYPE_SOA:
  164. fprintf (stdout,
  165. "%s SOA %s %s %u %u %u %u %u\n",
  166. req->hostname,
  167. rec->data.soa->mname,
  168. rec->data.soa->rname,
  169. (unsigned int) rec->data.soa->serial,
  170. (unsigned int) rec->data.soa->refresh,
  171. (unsigned int) rec->data.soa->retry,
  172. (unsigned int) rec->data.soa->expire,
  173. (unsigned int) rec->data.soa->minimum_ttl);
  174. break;
  175. case GNUNET_DNSPARSER_TYPE_SRV:
  176. fprintf (stdout,
  177. "%s SRV %s %u %u %u\n",
  178. req->hostname,
  179. rec->data.srv->target,
  180. rec->data.srv->priority,
  181. rec->data.srv->weight,
  182. rec->data.srv->port);
  183. break;
  184. case GNUNET_DNSPARSER_TYPE_PTR:
  185. fprintf (stdout,
  186. "%s PTR %s\n",
  187. req->hostname,
  188. rec->data.hostname);
  189. break;
  190. case GNUNET_DNSPARSER_TYPE_TXT:
  191. fprintf (stdout,
  192. "%s TXT %.*s\n",
  193. req->hostname,
  194. (int) rec->data.raw.data_len,
  195. (char *) rec->data.raw.data);
  196. break;
  197. case GNUNET_DNSPARSER_TYPE_DNAME:
  198. fprintf (stdout,
  199. "%s DNAME %s\n",
  200. req->hostname,
  201. rec->data.hostname);
  202. break;
  203. /* obscure records */
  204. case GNUNET_DNSPARSER_TYPE_AFSDB:
  205. case GNUNET_DNSPARSER_TYPE_NAPTR:
  206. case GNUNET_DNSPARSER_TYPE_APL:
  207. case GNUNET_DNSPARSER_TYPE_DHCID:
  208. case GNUNET_DNSPARSER_TYPE_HIP:
  209. case GNUNET_DNSPARSER_TYPE_LOC:
  210. case GNUNET_DNSPARSER_TYPE_RP:
  211. case GNUNET_DNSPARSER_TYPE_TKEY:
  212. case GNUNET_DNSPARSER_TYPE_TSIG:
  213. case GNUNET_DNSPARSER_TYPE_URI:
  214. case GNUNET_DNSPARSER_TYPE_TA:
  215. /* DNSSEC */
  216. case GNUNET_DNSPARSER_TYPE_DS:
  217. case GNUNET_DNSPARSER_TYPE_RRSIG:
  218. case GNUNET_DNSPARSER_TYPE_NSEC:
  219. case GNUNET_DNSPARSER_TYPE_DNSKEY:
  220. case GNUNET_DNSPARSER_TYPE_NSEC3:
  221. case GNUNET_DNSPARSER_TYPE_NSEC3PARAM:
  222. case GNUNET_DNSPARSER_TYPE_CDS:
  223. case GNUNET_DNSPARSER_TYPE_CDNSKEY:
  224. /* DNSSEC payload */
  225. case GNUNET_DNSPARSER_TYPE_CERT:
  226. case GNUNET_DNSPARSER_TYPE_SSHFP:
  227. case GNUNET_DNSPARSER_TYPE_IPSECKEY:
  228. case GNUNET_DNSPARSER_TYPE_TLSA:
  229. case GNUNET_DNSPARSER_TYPE_OPENPGPKEY:
  230. /* obsolete records */
  231. case GNUNET_DNSPARSER_TYPE_SIG:
  232. case GNUNET_DNSPARSER_TYPE_KEY:
  233. case GNUNET_DNSPARSER_TYPE_KX:
  234. {
  235. char *base32;
  236. base32 = GNUNET_STRINGS_data_to_string_alloc (rec->data.raw.data,
  237. rec->data.raw.data_len);
  238. fprintf (stdout,
  239. "%s (%u) %s\n",
  240. req->hostname,
  241. rec->type,
  242. base32);
  243. GNUNET_free (base32);
  244. }
  245. break;
  246. default:
  247. fprintf (stderr,
  248. "Unsupported type %u\n",
  249. (unsigned int) rec->type);
  250. break;
  251. }
  252. }
  253. /**
  254. * Function called with the result of a DNS resolution.
  255. *
  256. * @param cls closure with the `struct Request`
  257. * @param dns dns response, never NULL
  258. * @param dns_len number of bytes in @a dns
  259. */
  260. static void
  261. process_result (void *cls,
  262. const struct GNUNET_TUN_DnsHeader *dns,
  263. size_t dns_len)
  264. {
  265. struct Request *req = cls;
  266. struct GNUNET_DNSPARSER_Packet *p;
  267. if (NULL == dns)
  268. {
  269. /* stub gave up */
  270. pending--;
  271. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  272. "Stub gave up on DNS reply for `%s'\n",
  273. req->hostname);
  274. GNUNET_CONTAINER_DLL_remove (req_head,
  275. req_tail,
  276. req);
  277. if (req->issue_num > MAX_RETRIES)
  278. {
  279. failures++;
  280. GNUNET_free (req->hostname);
  281. GNUNET_free (req->raw);
  282. GNUNET_free (req);
  283. return;
  284. }
  285. GNUNET_CONTAINER_DLL_insert_tail (req_head,
  286. req_tail,
  287. req);
  288. req->rs = NULL;
  289. return;
  290. }
  291. if (req->id != dns->id)
  292. return;
  293. pending--;
  294. GNUNET_DNSSTUB_resolve_cancel (req->rs);
  295. req->rs = NULL;
  296. GNUNET_CONTAINER_DLL_remove (req_head,
  297. req_tail,
  298. req);
  299. p = GNUNET_DNSPARSER_parse ((const char *) dns,
  300. dns_len);
  301. if (NULL == p)
  302. {
  303. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  304. "Failed to parse DNS reply for `%s'\n",
  305. req->hostname);
  306. if (req->issue_num > MAX_RETRIES)
  307. {
  308. failures++;
  309. GNUNET_free (req->hostname);
  310. GNUNET_free (req->raw);
  311. GNUNET_free (req);
  312. return;
  313. }
  314. GNUNET_CONTAINER_DLL_insert_tail (req_head,
  315. req_tail,
  316. req);
  317. return;
  318. }
  319. for (unsigned int i=0;i<p->num_answers;i++)
  320. {
  321. struct GNUNET_DNSPARSER_Record *rs = &p->answers[i];
  322. process_record (req,
  323. rs);
  324. }
  325. for (unsigned int i=0;i<p->num_authority_records;i++)
  326. {
  327. struct GNUNET_DNSPARSER_Record *rs = &p->authority_records[i];
  328. process_record (req,
  329. rs);
  330. }
  331. for (unsigned int i=0;i<p->num_additional_records;i++)
  332. {
  333. struct GNUNET_DNSPARSER_Record *rs = &p->additional_records[i];
  334. process_record (req,
  335. rs);
  336. }
  337. GNUNET_DNSPARSER_free_packet (p);
  338. GNUNET_free (req->hostname);
  339. GNUNET_free (req->raw);
  340. GNUNET_free (req);
  341. }
  342. /**
  343. * Submit a request to DNS unless we need to slow down because
  344. * we are at the rate limit.
  345. *
  346. * @param req request to submit
  347. * @return #GNUNET_OK if request was submitted
  348. * #GNUNET_NO if request was already submitted
  349. * #GNUNET_SYSERR if we are at the rate limit
  350. */
  351. static int
  352. submit_req (struct Request *req)
  353. {
  354. static struct timeval last_request;
  355. struct timeval now;
  356. if (NULL != req->rs)
  357. return GNUNET_NO; /* already submitted */
  358. gettimeofday (&now,
  359. NULL);
  360. if ( ( ( (now.tv_sec - last_request.tv_sec) == 0) &&
  361. ( (now.tv_usec - last_request.tv_usec) < TIME_THRESH) ) ||
  362. (pending >= THRESH) )
  363. return GNUNET_SYSERR;
  364. GNUNET_assert (NULL == req->rs);
  365. req->rs = GNUNET_DNSSTUB_resolve (ctx,
  366. req->raw,
  367. req->raw_len,
  368. &process_result,
  369. req);
  370. GNUNET_assert (NULL != req->rs);
  371. req->issue_num++;
  372. last_request = now;
  373. lookups++;
  374. pending++;
  375. req->time = time (NULL);
  376. return GNUNET_OK;
  377. }
  378. /**
  379. * Process as many requests as possible from the queue.
  380. *
  381. * @param cls NULL
  382. */
  383. static void
  384. process_queue(void *cls)
  385. {
  386. (void) cls;
  387. t = NULL;
  388. for (struct Request *req = req_head;
  389. NULL != req;
  390. req = req->next)
  391. {
  392. if (GNUNET_SYSERR == submit_req (req))
  393. break;
  394. }
  395. if (NULL != req_head)
  396. t = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
  397. &process_queue,
  398. NULL);
  399. else
  400. GNUNET_SCHEDULER_shutdown ();
  401. }
  402. /**
  403. * Clean up and terminate the process.
  404. *
  405. * @param cls NULL
  406. */
  407. static void
  408. do_shutdown (void *cls)
  409. {
  410. (void) cls;
  411. if (NULL != t)
  412. {
  413. GNUNET_SCHEDULER_cancel (t);
  414. t = NULL;
  415. }
  416. GNUNET_DNSSTUB_stop (ctx);
  417. ctx = NULL;
  418. }
  419. /**
  420. * Process requests from the queue, then if the queue is
  421. * not empty, try again.
  422. *
  423. * @param cls NULL
  424. */
  425. static void
  426. run (void *cls)
  427. {
  428. (void) cls;
  429. GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
  430. NULL);
  431. t = GNUNET_SCHEDULER_add_now (&process_queue,
  432. NULL);
  433. }
  434. /**
  435. * Add @a hostname to the list of requests to be made.
  436. *
  437. * @param hostname name to resolve
  438. */
  439. static void
  440. queue (const char *hostname)
  441. {
  442. struct GNUNET_DNSPARSER_Packet p;
  443. struct GNUNET_DNSPARSER_Query q;
  444. struct Request *req;
  445. char *raw;
  446. size_t raw_size;
  447. int ret;
  448. if (GNUNET_OK !=
  449. GNUNET_DNSPARSER_check_name (hostname))
  450. {
  451. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  452. "Refusing invalid hostname `%s'\n",
  453. hostname);
  454. return;
  455. }
  456. q.name = (char *) hostname;
  457. q.type = GNUNET_DNSPARSER_TYPE_NS;
  458. q.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
  459. memset (&p,
  460. 0,
  461. sizeof (p));
  462. p.num_queries = 1;
  463. p.queries = &q;
  464. p.id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
  465. UINT16_MAX);
  466. ret = GNUNET_DNSPARSER_pack (&p,
  467. UINT16_MAX,
  468. &raw,
  469. &raw_size);
  470. if (GNUNET_OK != ret)
  471. {
  472. if (GNUNET_NO == ret)
  473. GNUNET_free (raw);
  474. GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
  475. "Failed to pack query for hostname `%s'\n",
  476. hostname);
  477. return;
  478. }
  479. req = GNUNET_new (struct Request);
  480. req->hostname = strdup (hostname);
  481. req->raw = raw;
  482. req->raw_len = raw_size;
  483. req->id = p.id;
  484. GNUNET_CONTAINER_DLL_insert_tail (req_head,
  485. req_tail,
  486. req);
  487. }
  488. /**
  489. * Call with IP address of resolver to query.
  490. *
  491. * @param argc should be 2
  492. * @param argv[1] should contain IP address
  493. * @return 0 on success
  494. */
  495. int
  496. main (int argc,
  497. char **argv)
  498. {
  499. char hn[256];
  500. if (2 != argc)
  501. {
  502. fprintf (stderr,
  503. "Missing required configuration argument\n");
  504. return -1;
  505. }
  506. ctx = GNUNET_DNSSTUB_start (256);
  507. if (NULL == ctx)
  508. {
  509. fprintf (stderr,
  510. "Failed to initialize GNUnet DNS STUB\n");
  511. return 1;
  512. }
  513. if (GNUNET_OK !=
  514. GNUNET_DNSSTUB_add_dns_ip (ctx,
  515. argv[1]))
  516. {
  517. fprintf (stderr,
  518. "Failed to use `%s' for DNS resolver\n",
  519. argv[1]);
  520. return 1;
  521. }
  522. while (NULL !=
  523. fgets (hn,
  524. sizeof (hn),
  525. stdin))
  526. {
  527. if (strlen(hn) > 0)
  528. hn[strlen(hn)-1] = '\0'; /* eat newline */
  529. queue (hn);
  530. }
  531. GNUNET_SCHEDULER_run (&run,
  532. NULL);
  533. fprintf (stderr,
  534. "Did %u lookups, found %u records, %u lookups failed, %u pending on shutdown\n",
  535. lookups,
  536. records,
  537. failures,
  538. pending);
  539. return 0;
  540. }