You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. /**************************************************************************
  2. *
  3. * dns_resolver.c: Etherboot support for resolution of host/domain
  4. * names in filename parameters
  5. * Written 2004 by Anselm M. Hoffmeister
  6. * <stockholm@users.sourceforge.net>
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21. *
  22. * This code is using nuts and bolts from throughout etherboot.
  23. * It is a fresh implementation according to the DNS RFC, #1035
  24. *
  25. * REVISION HISTORY:
  26. * ================
  27. * 2004-05-10 File created
  28. * 2004-05-19 First release to CVS
  29. * 2004-05-22 CNAME support first stage finished
  30. * 2004-05-24 First "stable" release to CVS
  31. * 2004-08-28 Improve readability, set recursion flag
  32. * 2005-04-30 Tidied up to the point of being a complete rewrite (mcb30)
  33. ***************************************************************************/
  34. #include "etherboot.h"
  35. #include "nic.h"
  36. #include "resolv.h"
  37. #include "dns.h"
  38. /*
  39. * await_dns
  40. * Shall be called on any incoming packet during the resolution process
  41. * (as is the case with all the other await_ functions in etherboot)
  42. * Param: as any await functions
  43. *
  44. */
  45. static int await_dns ( int ival, void *ptr,
  46. unsigned short ptype __unused,
  47. struct iphdr *ip __unused,
  48. struct udphdr *udp, struct tcphdr *tcp __unused ) {
  49. struct dns_header **header = ptr;
  50. if ( ! udp )
  51. return 0;
  52. if ( ntohs ( udp->dest ) != ival )
  53. return 0;
  54. *header = ( struct dns_header * ) ( udp + 1 );
  55. return 1;
  56. }
  57. /*
  58. * Send a name server query and wait for a response. Query is retried
  59. * up to DNS_MAX_RETRIES times. Returns a pointer to the answer
  60. * packet, or NULL if no answer was received.
  61. *
  62. */
  63. struct dns_header * dns_query ( struct dns_query *query,
  64. unsigned int query_len,
  65. struct sockaddr_in *nameserver ) {
  66. long timeout;
  67. int retry;
  68. struct dns_header *reply;
  69. for ( retry = 0 ; retry < DNS_MAX_RETRIES ; retry++ ) {
  70. udp_transmit ( nameserver->sin_addr.s_addr,
  71. nameserver->sin_port, nameserver->sin_port,
  72. query_len, query );
  73. timeout = rfc2131_sleep_interval ( TIMEOUT, retry );
  74. if ( ! await_reply ( await_dns, nameserver->sin_port,
  75. &reply, timeout ) )
  76. continue;
  77. if ( reply->id != query->dns.id ) {
  78. DBG ( "DNS received unexpected reply ID %d "
  79. "(wanted %d)\n",
  80. ntohs ( reply->id ), ntohs ( query->dns.id ) );
  81. continue;
  82. }
  83. /* We have a valid reply! */
  84. return reply;
  85. }
  86. return NULL;
  87. }
  88. /*
  89. * Compare two DNS names to see if they are the same. Takes
  90. * compressed names in the reply into account (though the query name
  91. * must be uncompressed). Returns 0 for a match (for consistency with
  92. * strcmp et al).
  93. *
  94. */
  95. static inline int dns_name_cmp ( const char *qname, const char *rname,
  96. struct dns_header *reply ) {
  97. int i;
  98. while ( 1 ) {
  99. /* Obtain next section of rname */
  100. while ( ( *rname ) & 0xc0 ) {
  101. rname = ( ( char * ) reply +
  102. ( ntohs( *((uint16_t *)rname) ) & ~0xc000 ));
  103. }
  104. /* Check that lengths match */
  105. if ( *rname != *qname )
  106. return 1;
  107. /* If length is zero, we have reached the end */
  108. if ( ! *qname )
  109. return 0;
  110. /* Check that data matches */
  111. for ( i = *qname + 1; i > 0 ; i-- ) {
  112. if ( *(rname++) != *(qname++) )
  113. return 1;
  114. }
  115. }
  116. }
  117. /*
  118. * Skip over a DNS name, which may be compressed
  119. *
  120. */
  121. static inline const char * dns_skip_name ( const char *name ) {
  122. while ( 1 ) {
  123. if ( ! *name ) {
  124. /* End of name */
  125. return ( name + 1);
  126. }
  127. if ( *name & 0xc0 ) {
  128. /* Start of a compressed name */
  129. return ( name + 2 );
  130. }
  131. /* Uncompressed name portion */
  132. name += *name + 1;
  133. }
  134. }
  135. /*
  136. * Find a Resource Record in a reply packet corresponding to our
  137. * query. Returns a pointer to the RR, or NULL if no answer found.
  138. *
  139. */
  140. struct dns_rr_info * dns_find_rr ( struct dns_query *query,
  141. struct dns_header *reply ) {
  142. int i, cmp;
  143. const char *p = ( ( char * ) reply ) + sizeof ( struct dns_header );
  144. /* Skip over the questions section */
  145. for ( i = ntohs ( reply->qdcount ) ; i > 0 ; i-- ) {
  146. p = dns_skip_name ( p ) + sizeof ( struct dns_query_info );
  147. }
  148. /* Process the answers section */
  149. for ( i = ntohs ( reply->ancount ) ; i > 0 ; i-- ) {
  150. cmp = dns_name_cmp ( query->payload, p, reply );
  151. p = dns_skip_name ( p );
  152. if ( cmp == 0 )
  153. return ( ( struct dns_rr_info * ) p );
  154. p += ( sizeof ( struct dns_rr_info ) +
  155. ntohs ( ( ( struct dns_rr_info * ) p )->rdlength ) );
  156. }
  157. return NULL;
  158. }
  159. /*
  160. * Convert a standard NUL-terminated string to a DNS query name,
  161. * consisting of "<length>element" pairs.
  162. *
  163. * Returns a pointer to the character following the constructed DNS
  164. * query name.
  165. *
  166. */
  167. static inline char * dns_make_name ( char *dest, const char *name ) {
  168. char *length_byte = dest++;
  169. char c;
  170. while ( ( c = *(name++) ) ) {
  171. if ( c == '.' ) {
  172. *length_byte = dest - length_byte - 1;
  173. length_byte = dest;
  174. }
  175. *(dest++) = c;
  176. }
  177. *length_byte = dest - length_byte - 1;
  178. *(dest++) = '\0';
  179. return dest;
  180. }
  181. /*
  182. * Produce a printable version of a DNS name. Used only for debugging.
  183. *
  184. */
  185. static inline char * dns_unmake_name ( char *name ) {
  186. char *p;
  187. unsigned int len;
  188. p = name;
  189. while ( ( len = *p ) ) {
  190. *(p++) = '.';
  191. p += len;
  192. }
  193. return name + 1;
  194. }
  195. /*
  196. * Decompress a DNS name.
  197. *
  198. * Returns a pointer to the character following the decompressed DNS
  199. * name.
  200. *
  201. */
  202. static inline char * dns_decompress_name ( char *dest, const char *name,
  203. struct dns_header *header ) {
  204. int i, len;
  205. do {
  206. /* Obtain next section of name */
  207. while ( ( *name ) & 0xc0 ) {
  208. name = ( ( char * ) header +
  209. ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
  210. }
  211. /* Copy data */
  212. len = *name;
  213. for ( i = len + 1 ; i > 0 ; i-- ) {
  214. *(dest++) = *(name++);
  215. }
  216. } while ( len );
  217. return dest;
  218. }
  219. /*
  220. * Resolve a name using DNS
  221. *
  222. */
  223. static int dns_resolv ( struct in_addr *addr, const char *name ) {
  224. struct dns_query query;
  225. struct dns_query_info *query_info;
  226. struct dns_header *reply;
  227. struct dns_rr_info *rr_info;
  228. struct sockaddr_in nameserver;
  229. uint16_t qtype;
  230. unsigned int recursion = 0;
  231. unsigned int id = 1;
  232. /* Fail immediately if we have no name server */
  233. if ( ! arptable[ARP_NAMESERVER].ipaddr.s_addr ) {
  234. DBG ( "DNS has no nameserver\n" );
  235. return 0;
  236. }
  237. DBG ( "DNS resolving %s\n", name );
  238. /* Set up the query data */
  239. nameserver.sin_addr = arptable[ARP_NAMESERVER].ipaddr;
  240. nameserver.sin_port = DNS_UDP_PORT;
  241. memset ( &query, 0, sizeof ( query ) );
  242. query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
  243. DNS_FLAG_RD );
  244. query.dns.qdcount = htons ( 1 );
  245. query_info = ( void * ) dns_make_name ( query.payload, name );
  246. query_info->qtype = htons ( DNS_TYPE_A );
  247. query_info->qclass = htons ( DNS_CLASS_IN );
  248. while ( 1 ) {
  249. /* Transmit current query, wait for reply */
  250. query.dns.id = htons ( id++ );
  251. qtype = ntohs ( query_info->qtype );
  252. reply = dns_query ( &query,
  253. ( ( ( char * ) query_info )
  254. + sizeof ( *query_info )
  255. - ( ( char * ) &query ) ),
  256. &nameserver );
  257. if ( ! reply ) {
  258. DBG ( "DNS got no response from server %@ (port %d)\n",
  259. nameserver.sin_addr.s_addr,
  260. nameserver.sin_port );
  261. return 0;
  262. }
  263. /* Search through response for useful answers. Do
  264. * this multiple times, to take advantage of useful
  265. * nameservers which send us e.g. the CNAME *and* the
  266. * A record for the pointed-to name.
  267. */
  268. while ( ( rr_info = dns_find_rr ( &query, reply ) ) ) {
  269. switch ( ntohs ( rr_info->type ) ) {
  270. case DNS_TYPE_A: {
  271. /* Found the target A record */
  272. struct dns_rr_info_a *rr_info_a =
  273. ( struct dns_rr_info_a * ) rr_info;
  274. *addr = rr_info_a->in_addr;
  275. DBG ( "DNS found address %@\n", addr->s_addr );
  276. return 1; }
  277. case DNS_TYPE_CNAME: {
  278. /* Found a CNAME record - update the query */
  279. struct dns_rr_info_cname *rr_info_cname =
  280. ( struct dns_rr_info_cname * ) rr_info;
  281. char *cname = rr_info_cname->cname;
  282. query_info = ( void * )
  283. dns_decompress_name ( query.payload,
  284. cname, reply );
  285. DBG ( "DNS found CNAME %s\n",
  286. dns_unmake_name ( query.payload ) );
  287. DBG ( "", /* Reconstruct name */
  288. dns_make_name ( query.payload,
  289. query.payload + 1 ) );
  290. query_info->qtype = htons ( DNS_TYPE_A );
  291. query_info->qclass = htons ( DNS_CLASS_IN );
  292. if ( ++recursion > DNS_MAX_CNAME_RECURSION ) {
  293. DBG ( "DNS recursion exceeded\n" );
  294. return 0;
  295. }
  296. break; }
  297. default:
  298. DBG ( "DNS got unknown record type %d\n",
  299. ntohs ( rr_info->type ) );
  300. return 0;
  301. }
  302. }
  303. /* Determine what to do next based on the type of
  304. * query we issued and the reponse we received
  305. */
  306. switch ( qtype ) {
  307. case DNS_TYPE_A :
  308. /* We asked for an A record and got nothing;
  309. * try the CNAME.
  310. */
  311. DBG ( "DNS found no A record; trying CNAME\n" );
  312. query_info->qtype = htons ( DNS_TYPE_CNAME );
  313. break;
  314. case DNS_TYPE_CNAME :
  315. /* We asked for a CNAME record. If we didn't
  316. * get any response (i.e. the next A query
  317. * isn't already set up), then abort.
  318. */
  319. if ( query_info->qtype != htons ( DNS_TYPE_A ) ) {
  320. DBG ( "DNS found no CNAME record\n" );
  321. return 0;
  322. }
  323. break;
  324. default:
  325. DBG ( "DNS internal error - inconsistent state\n" );
  326. }
  327. }
  328. }
  329. struct resolver dns_resolver __resolver = {
  330. .name = "DNS",
  331. .resolv = dns_resolv,
  332. };