Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  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. static 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. * Decompress a DNS name.
  183. *
  184. * Returns a pointer to the character following the decompressed DNS
  185. * name.
  186. *
  187. */
  188. static inline char * dns_decompress_name ( char *dest, const char *name,
  189. struct dns_header *header ) {
  190. int i, len;
  191. do {
  192. /* Obtain next section of name */
  193. while ( ( *name ) & 0xc0 ) {
  194. name = ( ( char * ) header +
  195. ( ntohs ( *((uint16_t *)name) ) & ~0xc000 ) );
  196. }
  197. /* Copy data */
  198. len = *name;
  199. for ( i = len + 1 ; i > 0 ; i-- ) {
  200. *(dest++) = *(name++);
  201. }
  202. } while ( len );
  203. return dest;
  204. }
  205. /*
  206. * Resolve a name using DNS
  207. *
  208. */
  209. static int dns_resolv ( struct in_addr *addr, const char *name ) {
  210. struct dns_query query;
  211. struct dns_query_info *query_info;
  212. struct dns_header *reply;
  213. struct dns_rr_info *rr_info;
  214. struct sockaddr_in nameserver;
  215. uint16_t qtype;
  216. unsigned int recursion = 0;
  217. unsigned int id = 1;
  218. DBG ( "DNS resolving %s\n", name );
  219. /* Set up the query data */
  220. nameserver.sin_addr = arptable[ARP_NAMESERVER].ipaddr;
  221. nameserver.sin_port = DNS_UDP_PORT;
  222. memset ( &query, 0, sizeof ( query ) );
  223. query.dns.flags = htons ( DNS_FLAG_QUERY | DNS_FLAG_OPCODE_QUERY |
  224. DNS_FLAG_RD );
  225. query.dns.qdcount = htons ( 1 );
  226. query_info = ( void * ) dns_make_name ( query.payload, name );
  227. query_info->qtype = htons ( DNS_TYPE_A );
  228. query_info->qclass = htons ( DNS_CLASS_IN );
  229. while ( 1 ) {
  230. /* Transmit current query, wait for reply */
  231. query.dns.id = htons ( id++ );
  232. qtype = ntohs ( query_info->qtype );
  233. reply = dns_query ( &query,
  234. ( ( ( char * ) query_info )
  235. + sizeof ( *query_info )
  236. - ( ( char * ) &query ) ),
  237. &nameserver );
  238. if ( ! reply ) {
  239. DBG ( "DNS got no response from server %@ (port %d)\n",
  240. nameserver.sin_addr.s_addr,
  241. nameserver.sin_port );
  242. return 0;
  243. }
  244. /* Search through response for useful answers. Do
  245. * this multiple times, to take advantage of useful
  246. * nameservers which send us e.g. the CNAME *and* the
  247. * A record for the pointed-to name.
  248. */
  249. while ( ( rr_info = dns_find_rr ( &query, reply ) ) ) {
  250. switch ( ntohs ( rr_info->type ) ) {
  251. case DNS_TYPE_A: {
  252. /* Found the target A record */
  253. struct dns_rr_info_a *rr_info_a =
  254. ( struct dns_rr_info_a * ) rr_info;
  255. *addr = rr_info_a->in_addr;
  256. DBG ( "DNS found address %@\n", addr->s_addr );
  257. return 1; }
  258. case DNS_TYPE_CNAME: {
  259. /* Found a CNAME record - update the query */
  260. struct dns_rr_info_cname *rr_info_cname =
  261. ( struct dns_rr_info_cname * ) rr_info;
  262. char *cname = rr_info_cname->cname;
  263. DBG ( "DNS found CNAME\n" );
  264. query_info = ( void * )
  265. dns_decompress_name ( query.payload,
  266. cname, reply );
  267. query_info->qtype = htons ( DNS_TYPE_A );
  268. query_info->qclass = htons ( DNS_CLASS_IN );
  269. if ( ++recursion > DNS_MAX_CNAME_RECURSION ) {
  270. DBG ( "DNS recursion exceeded\n" );
  271. return 0;
  272. }
  273. break; }
  274. default:
  275. DBG ( "DNS got unknown record type %d\n",
  276. ntohs ( rr_info->type ) );
  277. return 0;
  278. }
  279. }
  280. /* Determine what to do next based on the type of
  281. * query we issued and the reponse we received
  282. */
  283. switch ( qtype ) {
  284. case DNS_TYPE_A :
  285. /* We asked for an A record and got nothing;
  286. * try the CNAME.
  287. */
  288. DBG ( "DNS found no A record; trying CNAME\n" );
  289. query_info->qtype = htons ( DNS_TYPE_CNAME );
  290. break;
  291. case DNS_TYPE_CNAME :
  292. /* We asked for a CNAME record. If we didn't
  293. * get any response (i.e. the next A query
  294. * isn't already set up), then abort.
  295. */
  296. if ( query_info->qtype != htons ( DNS_TYPE_A ) ) {
  297. DBG ( "DNS found no CNAME record\n" );
  298. return 0;
  299. }
  300. break;
  301. default:
  302. DBG ( "DNS internal error - inconsistent state\n" );
  303. }
  304. }
  305. }
  306. static struct resolver dns_resolver __resolver = {
  307. .name = "DNS",
  308. .resolv = dns_resolv,
  309. };