uIP 1.0
C:/asm/STM32-ethernet/ENC28J60prj/uip-master/apps/resolv/resolv.c
См. документацию.
00001 /**
00002  * \addtogroup apps
00003  * @{
00004  */
00005 
00006 /**
00007  * \defgroup resolv DNS resolver - работа с именами DNS
00008  * @{
00009  *
00010  * Функции uIP DNS resolver используются для поиска имени хоста
00011  * (в базе данных имен DNS) и привязки его к цифровому адресу IP. 
00012  * Это поддерживает список распознанных имен хостов, который может
00013  * быть опрошен функцией resolv_lookup(). Новые имена хостов
00014  * могут быть распознаны с использованием функции resolv_query().
00015  *
00016  * Когда имя хоста было распознано (или найдено несуществующим),
00017  * код резолвера вызывает функцию обратного вызова (callback),
00018  * которая называется resolv_found(). Эта функция должна быть
00019  * реализована модулем, который использует резолвер.
00020  */
00021 
00022 /**
00023  * \file
00024  * Резолвер - преобразователь имени DNS хоста в адрес IP.
00025  * \author Adam Dunkels <adam@dunkels.com>
00026  *
00027  * Этот файл реализует преобразователь DNS имени хоста в адрес IP.
00028  */
00029 
00030 /*
00031  * Copyright (c) 2002-2003, Adam Dunkels.
00032  * Все права зарезервированы. *
00033  * Повторное распространение, использование в исходном и двоичном виде,
00034  * с модификацией или без - разрешается, если выполняются следующие
00035  * условия:
00036  * 1. Распространение исходного кода должно сохранить вышеуказанную пометку
00037  *    копирайта, этот список условий и следующую правовую оговорку.
00038  * 2. Распространение исходного кода должно сохранить вышеуказанную пометку
00039  *    копирайта, этот список условий и следующую правовую оговорку в
00040  *    документации и/или других материалах, которые будут предоставлены
00041  *    вместе с распространяемыми материалами.
00042  * 3. Имя автора не может использоваться, чтобы подтвердить или продвинуть
00043  *    продукты, написанные с использованием этого программного обеспечения
00044  *    без специального на то разрешения.
00045  *
00046  * ЭТО ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ АВТОРОМ ``КАК ЕСТЬ'', БЕЗ
00047  * КАКОЙ-ЛИБО ЛЮБОЙ РАСШИРЕННОЙ ИЛИ ПОДРАЗУМЕВАЕМОЙ ГАРАНТИИ, ВКЛЮЧАЯ,
00048  * НО НЕ ОГРАНИЧИВАЯСЬ ЭТИМ, ГАРАНТИИ ВЫСОКОГО СПРОСА И ПРИГОДНОСТИ
00049  * ДЛЯ КОНКРЕТНОЙ ЦЕЛИ. АВТОР НИ ПРИ КАКИХ УСЛОВИЯХ НЕ ОТВЕТСТВЕНЕН
00050  * ЗА ЛЮБЫЕ УБЫТКИ - ПРЯМЫЕ, КОСВЕННЫЕ, СЛУЧАЙНЫЕ, СПЕЦИАЛЬНЫЕ, ОБРАЗЦОВЫЕ
00051  * ИЛИ ПОСЛЕДОВАТЕЛЬНЫЕ (ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ ЭТИМ, ТРЕБОВАНИЯ
00052  * ЗАМЕНЫ ТОВАРА ИЛИ СЕРВИСА; ПОТЕРИ ИСПОЛЬЗОВАНИЯ, ДАННЫХ ИЛИ ВЫГОДЫ;
00053  * ИЛИ ПРЕКРАЩЕНИЕ БИЗНЕСА), ОДНАКО ВЫЗВАННЫЕ ПО ЛЮБОЙ ТЕОРИИ ОТВЕТСТВЕННОСТИ,
00054  * ЛИБО В КОНТРАКТЕ, ПРЯМОЙ ОТВЕТСТВЕННОСТИ, ЛИБО В НАРУШЕНИИ ЗАКОННЫХ ПРАВ
00055  * (ВКЛЮЧАЯ ТАК ИЛИ ИНАЧЕ НЕБРЕЖНОСТЬ), ВОЗНИКАЮЩИЕ ВСЕГДА ИЗ ИСПОЛЬЗОВАНИЯ
00056  * ЭТОГО ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ, ДАЖЕ ЕСЛИ БЫЛО ПРЕДУПРЕЖДЕНИЕ О ВОЗМОЖНОСТИ
00057  * ТАКОГО ПОВРЕЖДЕНИЯ.
00058  *
00059  * Этот файл является частью стека uIP TCP/IP.
00060  *
00061  * $Id: resolv.c,v 1.5 2006/06/11 21:46:37 adam Exp $
00062  *
00063  */
00064 
00065 #include "resolv.h"
00066 #include "uip.h"
00067 
00068 #include <string.h>
00069 
00070 #ifndef NULL
00071 #define NULL (void *)0
00072 #endif /* NULL */
00073 
00074 /** \internal Максимальное количество попыток запроса имени. */
00075 #define MAX_RETRIES 8
00076 
00077 /** \internal Заголовок сообщения DNS. */
00078 struct dns_hdr {
00079   u16_t id;
00080   u8_t flags1, flags2;
00081 #define DNS_FLAG1_RESPONSE        0x80
00082 #define DNS_FLAG1_OPCODE_STATUS   0x10
00083 #define DNS_FLAG1_OPCODE_INVERSE  0x08
00084 #define DNS_FLAG1_OPCODE_STANDARD 0x00
00085 #define DNS_FLAG1_AUTHORATIVE     0x04
00086 #define DNS_FLAG1_TRUNC           0x02
00087 #define DNS_FLAG1_RD              0x01
00088 #define DNS_FLAG2_RA              0x80
00089 #define DNS_FLAG2_ERR_MASK        0x0f
00090 #define DNS_FLAG2_ERR_NONE        0x00
00091 #define DNS_FLAG2_ERR_NAME        0x03
00092   u16_t numquestions;
00093   u16_t numanswers;
00094   u16_t numauthrr;
00095   u16_t numextrarr;
00096 };
00097 
00098 /** \internal Структура сообщения ответа DNS. */
00099 struct dns_answer {
00100   /* Запись ответа DNS начинается либо с имени домена, либо
00101      с указателя на имя, где-то уже присутствующее в пакете. */
00102   u16_t type;
00103   u16_t class;
00104   u16_t ttl[2];
00105   u16_t len;
00106   uip_ipaddr_t ipaddr;
00107 };
00108 
00109 struct namemap {
00110 #define STATE_UNUSED 0
00111 #define STATE_NEW    1
00112 #define STATE_ASKING 2
00113 #define STATE_DONE   3
00114 #define STATE_ERROR  4
00115   u8_t state;
00116   u8_t tmr;
00117   u8_t retries;
00118   u8_t seqno;
00119   u8_t err;
00120   char name[32];
00121   uip_ipaddr_t ipaddr;
00122 };
00123 
00124 #ifndef UIP_CONF_RESOLV_ENTRIES
00125 #define RESOLV_ENTRIES 4
00126 #else /* UIP_CONF_RESOLV_ENTRIES */
00127 #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES
00128 #endif /* UIP_CONF_RESOLV_ENTRIES */
00129 
00130 
00131 static struct namemap names[RESOLV_ENTRIES];
00132 
00133 static u8_t seqno;
00134 
00135 static struct uip_udp_conn *resolv_conn = NULL;
00136 
00137 
00138 /*---------------------------------------------------------------------------*/
00139 /** \internal
00140  * Просматривает компактно закодированное имя DNS и возвращает его конец.
00141  *
00142  * \return The end of the name.
00143  */
00144 /*---------------------------------------------------------------------------*/
00145 static unsigned char *
00146 parse_name(unsigned char *query)
00147 {
00148   unsigned char n;
00149 
00150   do {
00151     n = *query++;
00152     
00153     while(n > 0) {
00154       /*      printf("%c", *query);*/
00155       ++query;
00156       --n;
00157     };
00158     /*    printf(".");*/
00159   } while(*query != 0);
00160   /*  printf("\n");*/
00161   return query + 1;
00162 }
00163 /*---------------------------------------------------------------------------*/
00164 /** \internal
00165  * Пробегает по списку имен, чтобы посмотреть, есть ли в списке имена,
00166  * которые еще не опрошены, и если так, то посылвает запрос.
00167  */
00168 /*---------------------------------------------------------------------------*/
00169 static void
00170 check_entries(void)
00171 {
00172   register struct dns_hdr *hdr;
00173   char *query, *nptr, *nameptr;
00174   static u8_t i;
00175   static u8_t n;
00176   register struct namemap *namemapptr;
00177   
00178   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00179     namemapptr = &names[i];
00180     if(namemapptr->state == STATE_NEW ||
00181        namemapptr->state == STATE_ASKING) {
00182       if(namemapptr->state == STATE_ASKING) {
00183          if(--namemapptr->tmr == 0) {
00184             if(++namemapptr->retries == MAX_RETRIES) {
00185                namemapptr->state = STATE_ERROR;
00186                resolv_found(namemapptr->name, NULL);
00187                continue;
00188             }
00189             namemapptr->tmr = namemapptr->retries;
00190          } else {
00191             /*   printf("Timer %d\n", namemapptr->tmr);*/
00192             /* Его таймер не истек, так что перейдем
00193                к следующей записи. */
00194             continue;
00195          }
00196       } else {
00197          namemapptr->state = STATE_ASKING;
00198          namemapptr->tmr = 1;
00199          namemapptr->retries = 0;
00200       }
00201       hdr = (struct dns_hdr *)uip_appdata;
00202       memset(hdr, 0, sizeof(struct dns_hdr));
00203       hdr->id = htons(i);
00204       hdr->flags1 = DNS_FLAG1_RD;
00205       hdr->numquestions = HTONS(1);
00206       query = (char *)uip_appdata + 12;
00207       nameptr = namemapptr->name;
00208       --nameptr;
00209       /* Преобразует имя хоста в подходящий для запроса формат. */
00210       do {
00211          ++nameptr;
00212          nptr = query;
00213          ++query;
00214          for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
00215             *query = *nameptr;
00216             ++query;
00217             ++n;
00218          }
00219          *nptr = n;
00220       } while(*nameptr != 0);
00221       {
00222          static unsigned char endquery[] =
00223             {0,0,1,0,1};
00224          memcpy(query, endquery, 5);
00225       }
00226       uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata));
00227       break;
00228     }
00229   }
00230 }
00231 /*---------------------------------------------------------------------------*/
00232 /** \internal
00233  * Вызывается, когда поступят новые данные UDP.
00234  */
00235 /*---------------------------------------------------------------------------*/
00236 static void
00237 newdata(void)
00238 {
00239   char *nameptr;
00240   struct dns_answer *ans;
00241   struct dns_hdr *hdr;
00242   static u8_t nquestions, nanswers;
00243   static u8_t i;
00244   register struct namemap *namemapptr;
00245   
00246   hdr = (struct dns_hdr *)uip_appdata;
00247   /*  printf("ID %d\n", htons(hdr->id));
00248       printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
00249       printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
00250       printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
00251       htons(hdr->numquestions),
00252       htons(hdr->numanswers),
00253       htons(hdr->numauthrr),
00254       htons(hdr->numextrarr));
00255   */
00256 
00257   /* ID в заголовке DNS должен быть нашей записью в таблице имен. */
00258   i = htons(hdr->id);
00259   namemapptr = &names[i];
00260   if(i < RESOLV_ENTRIES &&
00261      namemapptr->state == STATE_ASKING) {
00262 
00263     /* Эта запись теперь завершена. */
00264     namemapptr->state = STATE_DONE;
00265     namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
00266 
00267     /* Проверка на ошибку. Если так, то вызов callback для оповещения. */
00268     if(namemapptr->err != 0) {
00269       namemapptr->state = STATE_ERROR;
00270       resolv_found(namemapptr->name, NULL);
00271       return;
00272     }
00273    
00274     /* Мы заботимся только о вопросе (вопросах) и ответах. Так что authrr
00275        и extrarr просто отбрасываются. */
00276     nquestions = htons(hdr->numquestions);
00277     nanswers = htons(hdr->numanswers);
00278    
00279     /* Пропуск имени в вопросе. XXX: в действительности нужно проверить
00280        это имя, чтобы убедиться в соответствии. */
00281     nameptr = parse_name((char *)uip_appdata + 12) + 4;
00282 
00283     while(nanswers > 0) {
00284       /* Первый байт записи ответа ресурса определяет, сжатая ли тут
00285          запись или нормальная. */
00286       if(*nameptr & 0xc0) {
00287          /* Сжатое имя. */
00288          nameptr +=2;
00289          /* printf("Compressed anwser\n");*/
00290       } else {
00291          /* Не сжатое имя. */
00292          nameptr = parse_name((char *)nameptr);
00293       }
00294 
00295       ans = (struct dns_answer *)nameptr;
00296       /*      printf("Answer: type %x, class %x, ttl %x, length %x\n",
00297          htons(ans->type), htons(ans->class), (htons(ans->ttl[0])
00298             << 16) | htons(ans->ttl[1]), htons(ans->len));*/
00299 
00300       /* Проверка на тип адреса IP и Internet class. Другие
00301          отбрасываются. */
00302       if(ans->type == HTONS(1) &&
00303          ans->class == HTONS(1) &&
00304          ans->len == HTONS(4)) {
00305             /* printf("IP address %d.%d.%d.%d\n",
00306                      htons(ans->ipaddr[0]) >> 8,
00307                      htons(ans->ipaddr[0]) & 0xff,
00308                      htons(ans->ipaddr[1]) >> 8,
00309                      htons(ans->ipaddr[1]) & 0xff);*/
00310          /* XXX: мы действительно должны сделать проверку, что этот
00311             адрес IP тот, что нам нужен. */
00312          namemapptr->ipaddr[0] = ans->ipaddr[0];
00313          namemapptr->ipaddr[1] = ans->ipaddr[1];
00314    
00315          resolv_found(namemapptr->name, namemapptr->ipaddr);
00316          return;
00317       } else {
00318          nameptr = nameptr + 10 + htons(ans->len);
00319       }
00320       --nanswers;
00321     }
00322   }
00323 }
00324 /*---------------------------------------------------------------------------*/
00325 /** \internal
00326  * Главная фукнция UDP.
00327  */
00328 /*---------------------------------------------------------------------------*/
00329 void
00330 resolv_appcall(void)
00331 {
00332   if(uip_udp_conn->rport == HTONS(53)) {
00333     if(uip_poll()) {
00334       check_entries();
00335     }
00336     if(uip_newdata()) {
00337       newdata();
00338     }
00339   }
00340 }
00341 /*---------------------------------------------------------------------------*/
00342 /**
00343  * Ставит имя в очередь, так что будет отправлен запрос на имя.
00344  *
00345  * \param name Запрашиваемое имя хоста.
00346  */
00347 /*---------------------------------------------------------------------------*/
00348 void
00349 resolv_query(char *name)
00350 {
00351   static u8_t i;
00352   static u8_t lseq, lseqi;
00353   register struct namemap *nameptr;
00354       
00355   lseq = lseqi = 0;
00356   
00357   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00358     nameptr = &names[i];
00359     if(nameptr->state == STATE_UNUSED) {
00360       break;
00361     }
00362     if(seqno - nameptr->seqno > lseq) {
00363       lseq = seqno - nameptr->seqno;
00364       lseqi = i;
00365     }
00366   }
00367 
00368   if(i == RESOLV_ENTRIES) {
00369     i = lseqi;
00370     nameptr = &names[i];
00371   }
00372 
00373   /*  printf("Using entry %d\n", i);*/
00374 
00375   strcpy(nameptr->name, name);
00376   nameptr->state = STATE_NEW;
00377   nameptr->seqno = seqno;
00378   ++seqno;
00379 }
00380 /*---------------------------------------------------------------------------*/
00381 /**
00382  * Ищет имя хоста в массиве известных имен.
00383  *
00384  * \note Эта функция просматривает только внутренний массив известных
00385  * имен хостов, и не отправляет запрос, если имя хоста не найдено. Для
00386  * отправки запроса на распознание имени хоста может использоваться
00387  * функция resolv_query().
00388  *
00389  * \return Указатель на 4-байтное представление адреса IP хоста,
00390  * или NULL, если имя хоста не было найдено в массиве имен.
00391  */
00392 /*---------------------------------------------------------------------------*/
00393 u16_t *
00394 resolv_lookup(char *name)
00395 {
00396   static u8_t i;
00397   struct namemap *nameptr;
00398   
00399   /* Просмотр списка, чтобы узнать, есть ли в нем искомое имя. Если нет,
00400      то вернем NULL. */
00401   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00402     nameptr = &names[i];
00403     if(nameptr->state == STATE_DONE &&
00404        strcmp(name, nameptr->name) == 0) {
00405       return nameptr->ipaddr;
00406     }
00407   }
00408   return NULL;
00409 }
00410 /*---------------------------------------------------------------------------*/
00411 /**
00412  * Получение текущего сконфигурированного сервера DNS.
00413  *
00414  * \return Указатель на 4-байтное представление адреса IP текущего
00415  * сконфигурированного сервера DNS, или NULL, если сервер DNS не был
00416  * сконфигурирован.
00417  */
00418 /*---------------------------------------------------------------------------*/
00419 u16_t *
00420 resolv_getserver(void)
00421 {
00422   if(resolv_conn == NULL) {
00423     return NULL;
00424   }
00425   return resolv_conn->ripaddr;
00426 }
00427 /*---------------------------------------------------------------------------*/
00428 /**
00429  * Конфигурирует сервер DNS, который будет использован для запросов.
00430  *
00431  * \param dnsserver Указатель на 4-байтное представление адреса IP сервера
00432  * DNS, который будет сконфигурирован.
00433  */
00434 /*---------------------------------------------------------------------------*/
00435 void
00436 resolv_conf(u16_t *dnsserver)
00437 {
00438   if(resolv_conn != NULL) {
00439     uip_udp_remove(resolv_conn);
00440   }
00441   
00442   resolv_conn = uip_udp_new(dnsserver, HTONS(53));
00443 }
00444 /*---------------------------------------------------------------------------*/
00445 /**
00446  * Инициализирует распознаватель имен.
00447  */
00448 /*---------------------------------------------------------------------------*/
00449 void
00450 resolv_init(void)
00451 {
00452   static u8_t i;
00453   
00454   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00455     names[i].state = STATE_DONE;
00456   }
00457 
00458 }
00459 /*---------------------------------------------------------------------------*/
00460 
00461 /** @} */
00462 /** @} */