Free (GPLv2) TCP/IP stack developed by TASS Belgium
Fork of PicoTCP by
modules/pico_dns_client.c@1:cfe8984a32b4, 2013-05-17 (annotated)
- Committer:
- tass
- Date:
- Fri May 17 12:09:59 2013 +0000
- Revision:
- 1:cfe8984a32b4
- Parent:
- libraries/picotcp/modules/pico_dns_client.c@0:d7f2341ab245
Update for smaller SOCKETQ
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
daniele | 0:d7f2341ab245 | 1 | /********************************************************************* |
daniele | 0:d7f2341ab245 | 2 | PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. |
daniele | 0:d7f2341ab245 | 3 | See LICENSE and COPYING for usage. |
daniele | 0:d7f2341ab245 | 4 | |
daniele | 0:d7f2341ab245 | 5 | . |
daniele | 0:d7f2341ab245 | 6 | |
daniele | 0:d7f2341ab245 | 7 | Authors: Kristof Roelants |
daniele | 0:d7f2341ab245 | 8 | *********************************************************************/ |
daniele | 0:d7f2341ab245 | 9 | #include "pico_config.h" |
daniele | 0:d7f2341ab245 | 10 | #include "pico_stack.h" |
daniele | 0:d7f2341ab245 | 11 | #include "pico_addressing.h" |
daniele | 0:d7f2341ab245 | 12 | #include "pico_socket.h" |
daniele | 0:d7f2341ab245 | 13 | #include "pico_ipv4.h" |
daniele | 0:d7f2341ab245 | 14 | #include "pico_dns_client.h" |
daniele | 0:d7f2341ab245 | 15 | #include "pico_tree.h" |
daniele | 0:d7f2341ab245 | 16 | |
daniele | 0:d7f2341ab245 | 17 | #ifdef PICO_SUPPORT_DNS_CLIENT |
daniele | 0:d7f2341ab245 | 18 | |
daniele | 0:d7f2341ab245 | 19 | #define dns_dbg(...) do{}while(0) |
daniele | 0:d7f2341ab245 | 20 | //#define dns_dbg dbg |
daniele | 0:d7f2341ab245 | 21 | |
daniele | 0:d7f2341ab245 | 22 | /* DNS response length */ |
daniele | 0:d7f2341ab245 | 23 | #define PICO_DNS_MAX_RESPONSE_LEN 256 |
daniele | 0:d7f2341ab245 | 24 | |
daniele | 0:d7f2341ab245 | 25 | /* DNS client retransmission time (msec) + frequency */ |
daniele | 0:d7f2341ab245 | 26 | #define PICO_DNS_CLIENT_RETRANS 4000 |
daniele | 0:d7f2341ab245 | 27 | #define PICO_DNS_CLIENT_MAX_RETRANS 3 |
daniele | 0:d7f2341ab245 | 28 | |
daniele | 0:d7f2341ab245 | 29 | /* Default nameservers */ |
daniele | 0:d7f2341ab245 | 30 | #define PICO_DNS_NS_GOOGLE "8.8.8.8" |
daniele | 0:d7f2341ab245 | 31 | |
daniele | 0:d7f2341ab245 | 32 | /* Nameserver port */ |
daniele | 0:d7f2341ab245 | 33 | #define PICO_DNS_NS_PORT 53 |
daniele | 0:d7f2341ab245 | 34 | |
daniele | 0:d7f2341ab245 | 35 | /* FLAG values */ |
daniele | 0:d7f2341ab245 | 36 | #define PICO_DNS_QR_QUERY 0 |
daniele | 0:d7f2341ab245 | 37 | #define PICO_DNS_QR_RESPONSE 1 |
daniele | 0:d7f2341ab245 | 38 | #define PICO_DNS_OPCODE_QUERY 0 |
daniele | 0:d7f2341ab245 | 39 | #define PICO_DNS_OPCODE_IQUERY 1 |
daniele | 0:d7f2341ab245 | 40 | #define PICO_DNS_OPCODE_STATUS 2 |
daniele | 0:d7f2341ab245 | 41 | #define PICO_DNS_AA_NO_AUTHORITY 0 |
daniele | 0:d7f2341ab245 | 42 | #define PICO_DNS_AA_IS_AUTHORITY 1 |
daniele | 0:d7f2341ab245 | 43 | #define PICO_DNS_TC_NO_TRUNCATION 0 |
daniele | 0:d7f2341ab245 | 44 | #define PICO_DNS_TC_IS_TRUNCATED 1 |
daniele | 0:d7f2341ab245 | 45 | #define PICO_DNS_RD_NO_DESIRE 0 |
daniele | 0:d7f2341ab245 | 46 | #define PICO_DNS_RD_IS_DESIRED 1 |
daniele | 0:d7f2341ab245 | 47 | #define PICO_DNS_RA_NO_SUPPORT 0 |
daniele | 0:d7f2341ab245 | 48 | #define PICO_DNS_RA_IS_SUPPORTED 1 |
daniele | 0:d7f2341ab245 | 49 | #define PICO_DNS_RCODE_NO_ERROR 0 |
daniele | 0:d7f2341ab245 | 50 | #define PICO_DNS_RCODE_EFORMAT 1 |
daniele | 0:d7f2341ab245 | 51 | #define PICO_DNS_RCODE_ESERVER 2 |
daniele | 0:d7f2341ab245 | 52 | #define PICO_DNS_RCODE_ENAME 3 |
daniele | 0:d7f2341ab245 | 53 | #define PICO_DNS_RCODE_ENOIMP 4 |
daniele | 0:d7f2341ab245 | 54 | #define PICO_DNS_RCODE_EREFUSED 5 |
daniele | 0:d7f2341ab245 | 55 | |
daniele | 0:d7f2341ab245 | 56 | /* QTYPE values */ |
daniele | 0:d7f2341ab245 | 57 | #define PICO_DNS_TYPE_A 1 |
daniele | 0:d7f2341ab245 | 58 | #define PICO_DNS_TYPE_PTR 12 |
daniele | 0:d7f2341ab245 | 59 | |
daniele | 0:d7f2341ab245 | 60 | /* QCLASS values */ |
daniele | 0:d7f2341ab245 | 61 | #define PICO_DNS_CLASS_IN 1 |
daniele | 0:d7f2341ab245 | 62 | |
daniele | 0:d7f2341ab245 | 63 | /* Compression values */ |
daniele | 0:d7f2341ab245 | 64 | #define PICO_DNS_LABEL 0 |
daniele | 0:d7f2341ab245 | 65 | #define PICO_DNS_POINTER 3 |
daniele | 0:d7f2341ab245 | 66 | |
daniele | 0:d7f2341ab245 | 67 | /* TTL values */ |
daniele | 0:d7f2341ab245 | 68 | #define PICO_DNS_MAX_TTL 604800 /* one week */ |
daniele | 0:d7f2341ab245 | 69 | |
daniele | 0:d7f2341ab245 | 70 | /* Header flags */ |
daniele | 0:d7f2341ab245 | 71 | #define FLAG_QR(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 15)) | (x << 15)) |
daniele | 0:d7f2341ab245 | 72 | #define FLAG_OPCODE(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0xF << 11)) | (x << 11)) |
daniele | 0:d7f2341ab245 | 73 | #define FLAG_AA(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 10)) | (x << 10)) |
daniele | 0:d7f2341ab245 | 74 | #define FLAG_TC(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 9)) | (x << 9)) |
daniele | 0:d7f2341ab245 | 75 | #define FLAG_RD(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 8)) | (x << 8)) |
daniele | 0:d7f2341ab245 | 76 | #define FLAG_RA(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x1 << 7)) | (x << 7)) |
daniele | 0:d7f2341ab245 | 77 | #define FLAG_Z(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0x7 << 4)) | (x << 4)) |
daniele | 0:d7f2341ab245 | 78 | #define FLAG_RCODE(hdr, x) ((hdr)->flags = ((hdr)->flags & ~(0xF)) | x) |
daniele | 0:d7f2341ab245 | 79 | |
daniele | 0:d7f2341ab245 | 80 | #define GET_FLAG_QR(hdr) ((((hdr)->flags) & (1 << 15)) != 0) |
daniele | 0:d7f2341ab245 | 81 | #define GET_FLAG_OPCODE(hdr) ((((hdr)->flags) & (0xF << 11)) >> 11) |
daniele | 0:d7f2341ab245 | 82 | #define GET_FLAG_AA(hdr) ((((hdr)->flags) & (1 << 10)) != 0) |
daniele | 0:d7f2341ab245 | 83 | #define GET_FLAG_TC(hdr) ((((hdr)->flags) & (1 << 9)) != 0) |
daniele | 0:d7f2341ab245 | 84 | #define GET_FLAG_RD(hdr) ((((hdr)->flags) & (1 << 8)) != 0) |
daniele | 0:d7f2341ab245 | 85 | #define GET_FLAG_RA(hdr) ((((hdr)->flags) & (1 << 7)) != 0) |
daniele | 0:d7f2341ab245 | 86 | #define GET_FLAG_Z(hdr) ((((hdr)->flags) & (0x7 << 4)) >> 4) |
daniele | 0:d7f2341ab245 | 87 | #define GET_FLAG_RCODE(hdr) (((hdr)->flags) & (0x0F)) |
daniele | 0:d7f2341ab245 | 88 | |
daniele | 0:d7f2341ab245 | 89 | /* RFC 1025 section 4. MESSAGES */ |
daniele | 0:d7f2341ab245 | 90 | struct __attribute__((packed)) dns_message_hdr |
daniele | 0:d7f2341ab245 | 91 | { |
daniele | 0:d7f2341ab245 | 92 | uint16_t id; |
daniele | 0:d7f2341ab245 | 93 | uint16_t flags; |
daniele | 0:d7f2341ab245 | 94 | uint16_t qdcount; |
daniele | 0:d7f2341ab245 | 95 | uint16_t ancount; |
daniele | 0:d7f2341ab245 | 96 | uint16_t nscount; |
daniele | 0:d7f2341ab245 | 97 | uint16_t arcount; |
daniele | 0:d7f2341ab245 | 98 | }; |
daniele | 0:d7f2341ab245 | 99 | |
daniele | 0:d7f2341ab245 | 100 | struct __attribute__((packed)) dns_query_suffix |
daniele | 0:d7f2341ab245 | 101 | { |
daniele | 0:d7f2341ab245 | 102 | /* NAME - domain name to which this resource record pertains */ |
daniele | 0:d7f2341ab245 | 103 | uint16_t qtype; |
daniele | 0:d7f2341ab245 | 104 | uint16_t qclass; |
daniele | 0:d7f2341ab245 | 105 | }; |
daniele | 0:d7f2341ab245 | 106 | |
daniele | 0:d7f2341ab245 | 107 | struct __attribute__((packed)) dns_answer_suffix |
daniele | 0:d7f2341ab245 | 108 | { |
daniele | 0:d7f2341ab245 | 109 | /* NAME - domain name to which this resource record pertains */ |
daniele | 0:d7f2341ab245 | 110 | uint16_t qtype; |
daniele | 0:d7f2341ab245 | 111 | uint16_t qclass; |
daniele | 0:d7f2341ab245 | 112 | uint32_t ttl; |
daniele | 0:d7f2341ab245 | 113 | uint16_t rdlength; |
daniele | 0:d7f2341ab245 | 114 | /* RDATA - variable length string of octets that describes the resource */ |
daniele | 0:d7f2341ab245 | 115 | }; |
daniele | 0:d7f2341ab245 | 116 | |
daniele | 0:d7f2341ab245 | 117 | struct pico_dns_ns |
daniele | 0:d7f2341ab245 | 118 | { |
daniele | 0:d7f2341ab245 | 119 | struct pico_ip4 ns; /* Nameserver */ |
daniele | 0:d7f2341ab245 | 120 | }; |
daniele | 0:d7f2341ab245 | 121 | |
daniele | 0:d7f2341ab245 | 122 | static int dns_ns_cmp(void *ka, void *kb) |
daniele | 0:d7f2341ab245 | 123 | { |
daniele | 0:d7f2341ab245 | 124 | struct pico_dns_ns *a = ka, *b = kb; |
daniele | 0:d7f2341ab245 | 125 | if (a->ns.addr < b->ns.addr) |
daniele | 0:d7f2341ab245 | 126 | return -1; |
daniele | 0:d7f2341ab245 | 127 | else if (a->ns.addr > b->ns.addr) |
daniele | 0:d7f2341ab245 | 128 | return 1; |
daniele | 0:d7f2341ab245 | 129 | else |
daniele | 0:d7f2341ab245 | 130 | return 0; |
daniele | 0:d7f2341ab245 | 131 | } |
daniele | 0:d7f2341ab245 | 132 | |
daniele | 0:d7f2341ab245 | 133 | PICO_TREE_DECLARE(NSTable,dns_ns_cmp); |
daniele | 0:d7f2341ab245 | 134 | |
daniele | 0:d7f2341ab245 | 135 | int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag) |
daniele | 0:d7f2341ab245 | 136 | { |
daniele | 0:d7f2341ab245 | 137 | struct pico_dns_ns test, *key = NULL; |
daniele | 0:d7f2341ab245 | 138 | |
daniele | 0:d7f2341ab245 | 139 | if (!ns) { |
daniele | 0:d7f2341ab245 | 140 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 141 | return -1; |
daniele | 0:d7f2341ab245 | 142 | } |
daniele | 0:d7f2341ab245 | 143 | |
daniele | 0:d7f2341ab245 | 144 | switch (flag) |
daniele | 0:d7f2341ab245 | 145 | { |
daniele | 0:d7f2341ab245 | 146 | case PICO_DNS_NS_ADD: |
daniele | 0:d7f2341ab245 | 147 | key = pico_zalloc(sizeof(struct pico_dns_ns)); |
daniele | 0:d7f2341ab245 | 148 | if (!key) { |
daniele | 0:d7f2341ab245 | 149 | pico_err = PICO_ERR_ENOMEM; |
daniele | 0:d7f2341ab245 | 150 | return -1; |
daniele | 0:d7f2341ab245 | 151 | } |
daniele | 0:d7f2341ab245 | 152 | key->ns = *ns; |
daniele | 0:d7f2341ab245 | 153 | |
daniele | 0:d7f2341ab245 | 154 | if(pico_tree_insert(&NSTable,key)){ |
daniele | 0:d7f2341ab245 | 155 | dns_dbg("DNS WARNING: nameserver %08X already added\n",ns->addr); |
daniele | 0:d7f2341ab245 | 156 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 157 | pico_free(key); |
daniele | 0:d7f2341ab245 | 158 | return -1; /* Element key already exists */ |
daniele | 0:d7f2341ab245 | 159 | } |
daniele | 0:d7f2341ab245 | 160 | dns_dbg("DNS: nameserver %08X added\n", ns->addr); |
daniele | 0:d7f2341ab245 | 161 | /* If default NS found, remove it */ |
daniele | 0:d7f2341ab245 | 162 | pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &test.ns.addr); |
daniele | 0:d7f2341ab245 | 163 | if (ns->addr != test.ns.addr) { |
daniele | 0:d7f2341ab245 | 164 | |
daniele | 0:d7f2341ab245 | 165 | key = pico_tree_findKey(&NSTable,&test); |
daniele | 0:d7f2341ab245 | 166 | if (key) { |
daniele | 0:d7f2341ab245 | 167 | if(pico_tree_delete(&NSTable,key)) { |
daniele | 0:d7f2341ab245 | 168 | dns_dbg("DNS: default nameserver %08X removed\n", test.ns.addr); |
daniele | 0:d7f2341ab245 | 169 | pico_free(key); |
daniele | 0:d7f2341ab245 | 170 | } else { |
daniele | 0:d7f2341ab245 | 171 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 172 | return -1; |
daniele | 0:d7f2341ab245 | 173 | } |
daniele | 0:d7f2341ab245 | 174 | } |
daniele | 0:d7f2341ab245 | 175 | } |
daniele | 0:d7f2341ab245 | 176 | break; |
daniele | 0:d7f2341ab245 | 177 | |
daniele | 0:d7f2341ab245 | 178 | case PICO_DNS_NS_DEL: |
daniele | 0:d7f2341ab245 | 179 | test.ns = *ns; |
daniele | 0:d7f2341ab245 | 180 | |
daniele | 0:d7f2341ab245 | 181 | key = pico_tree_findKey(&NSTable,&test); |
daniele | 0:d7f2341ab245 | 182 | if (!key) { |
daniele | 0:d7f2341ab245 | 183 | dns_dbg("DNS WARNING: nameserver %08X not found\n", ns->addr); |
daniele | 0:d7f2341ab245 | 184 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 185 | return -1; |
daniele | 0:d7f2341ab245 | 186 | } |
daniele | 0:d7f2341ab245 | 187 | /* RB_REMOVE returns pointer to removed element, NULL to indicate error */ |
daniele | 0:d7f2341ab245 | 188 | |
daniele | 0:d7f2341ab245 | 189 | if(pico_tree_delete(&NSTable,key)) { |
daniele | 0:d7f2341ab245 | 190 | dns_dbg("DNS: nameserver %08X removed\n",key->ns.addr); |
daniele | 0:d7f2341ab245 | 191 | pico_free(key); |
daniele | 0:d7f2341ab245 | 192 | } else { |
daniele | 0:d7f2341ab245 | 193 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 194 | return -1; |
daniele | 0:d7f2341ab245 | 195 | } |
daniele | 0:d7f2341ab245 | 196 | /* If no NS left, add default NS */ |
daniele | 0:d7f2341ab245 | 197 | if(pico_tree_first(&NSTable) == NULL){ |
daniele | 0:d7f2341ab245 | 198 | dns_dbg("DNS: add default nameserver\n"); |
daniele | 0:d7f2341ab245 | 199 | return pico_dns_client_init(); |
daniele | 0:d7f2341ab245 | 200 | } |
daniele | 0:d7f2341ab245 | 201 | break; |
daniele | 0:d7f2341ab245 | 202 | |
daniele | 0:d7f2341ab245 | 203 | default: |
daniele | 0:d7f2341ab245 | 204 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 205 | return -1; |
daniele | 0:d7f2341ab245 | 206 | } |
daniele | 0:d7f2341ab245 | 207 | return 0; |
daniele | 0:d7f2341ab245 | 208 | } |
daniele | 0:d7f2341ab245 | 209 | |
daniele | 0:d7f2341ab245 | 210 | int pico_dns_client_init() |
daniele | 0:d7f2341ab245 | 211 | { |
daniele | 0:d7f2341ab245 | 212 | struct pico_ip4 default_ns; |
daniele | 0:d7f2341ab245 | 213 | if (pico_string_to_ipv4(PICO_DNS_NS_GOOGLE, &default_ns.addr) != 0) { |
daniele | 0:d7f2341ab245 | 214 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 215 | return -1; |
daniele | 0:d7f2341ab245 | 216 | } |
daniele | 0:d7f2341ab245 | 217 | return pico_dns_client_nameserver(&default_ns, PICO_DNS_NS_ADD); |
daniele | 0:d7f2341ab245 | 218 | } |
daniele | 0:d7f2341ab245 | 219 | |
daniele | 0:d7f2341ab245 | 220 | struct pico_dns_key |
daniele | 0:d7f2341ab245 | 221 | { |
daniele | 0:d7f2341ab245 | 222 | char *q_hdr; |
daniele | 0:d7f2341ab245 | 223 | uint16_t len; |
daniele | 0:d7f2341ab245 | 224 | uint16_t id; |
daniele | 0:d7f2341ab245 | 225 | uint16_t qtype; |
daniele | 0:d7f2341ab245 | 226 | uint16_t qclass; |
daniele | 0:d7f2341ab245 | 227 | uint8_t retrans; |
daniele | 0:d7f2341ab245 | 228 | struct pico_dns_ns q_ns; |
daniele | 0:d7f2341ab245 | 229 | struct pico_socket *s; |
daniele | 0:d7f2341ab245 | 230 | void (*callback)(char *, void *); |
daniele | 0:d7f2341ab245 | 231 | void *arg; |
daniele | 0:d7f2341ab245 | 232 | }; |
daniele | 0:d7f2341ab245 | 233 | |
daniele | 0:d7f2341ab245 | 234 | static int dns_cmp(void *ka, void *kb) |
daniele | 0:d7f2341ab245 | 235 | { |
daniele | 0:d7f2341ab245 | 236 | struct pico_dns_key *a = ka,*b = kb; |
daniele | 0:d7f2341ab245 | 237 | if (a->id < b->id) |
daniele | 0:d7f2341ab245 | 238 | return -1; |
daniele | 0:d7f2341ab245 | 239 | else if (a->id > b->id) |
daniele | 0:d7f2341ab245 | 240 | return 1; |
daniele | 0:d7f2341ab245 | 241 | else |
daniele | 0:d7f2341ab245 | 242 | return 0; |
daniele | 0:d7f2341ab245 | 243 | } |
daniele | 0:d7f2341ab245 | 244 | |
daniele | 0:d7f2341ab245 | 245 | PICO_TREE_DECLARE(DNSTable,dns_cmp); |
daniele | 0:d7f2341ab245 | 246 | |
daniele | 0:d7f2341ab245 | 247 | static int pico_dns_client_strlen(const char *url) |
daniele | 0:d7f2341ab245 | 248 | { |
daniele | 0:d7f2341ab245 | 249 | uint16_t len = 0; |
daniele | 0:d7f2341ab245 | 250 | int p; |
daniele | 0:d7f2341ab245 | 251 | |
daniele | 0:d7f2341ab245 | 252 | if (!url) |
daniele | 0:d7f2341ab245 | 253 | return -1; |
daniele | 0:d7f2341ab245 | 254 | |
daniele | 0:d7f2341ab245 | 255 | while ((p = *url++) != 0) { |
daniele | 0:d7f2341ab245 | 256 | len++; |
daniele | 0:d7f2341ab245 | 257 | } |
daniele | 0:d7f2341ab245 | 258 | return len; |
daniele | 0:d7f2341ab245 | 259 | } |
daniele | 0:d7f2341ab245 | 260 | |
daniele | 0:d7f2341ab245 | 261 | /* Replace '.' by the label length */ |
daniele | 0:d7f2341ab245 | 262 | static int pico_dns_client_label(char *ptr) |
daniele | 0:d7f2341ab245 | 263 | { |
daniele | 0:d7f2341ab245 | 264 | char *l; |
daniele | 0:d7f2341ab245 | 265 | uint8_t lbl_len = 0; |
daniele | 0:d7f2341ab245 | 266 | int p; |
daniele | 0:d7f2341ab245 | 267 | |
daniele | 0:d7f2341ab245 | 268 | if (!ptr) |
daniele | 0:d7f2341ab245 | 269 | return -1; |
daniele | 0:d7f2341ab245 | 270 | |
daniele | 0:d7f2341ab245 | 271 | l = ptr++; |
daniele | 0:d7f2341ab245 | 272 | while ((p = *ptr++) != 0){ |
daniele | 0:d7f2341ab245 | 273 | if (p == '.') { |
daniele | 0:d7f2341ab245 | 274 | *l = lbl_len; |
daniele | 0:d7f2341ab245 | 275 | l = ptr - 1; |
daniele | 0:d7f2341ab245 | 276 | lbl_len = 0; |
daniele | 0:d7f2341ab245 | 277 | } else { |
daniele | 0:d7f2341ab245 | 278 | lbl_len++; |
daniele | 0:d7f2341ab245 | 279 | } |
daniele | 0:d7f2341ab245 | 280 | } |
daniele | 0:d7f2341ab245 | 281 | *l = lbl_len; |
daniele | 0:d7f2341ab245 | 282 | return 0; |
daniele | 0:d7f2341ab245 | 283 | } |
daniele | 0:d7f2341ab245 | 284 | |
daniele | 0:d7f2341ab245 | 285 | /* Replace the label length by '.' */ |
daniele | 0:d7f2341ab245 | 286 | static int pico_dns_client_reverse_label(char *ptr) |
daniele | 0:d7f2341ab245 | 287 | { |
daniele | 0:d7f2341ab245 | 288 | char *l; |
daniele | 0:d7f2341ab245 | 289 | int p; |
daniele | 0:d7f2341ab245 | 290 | |
daniele | 0:d7f2341ab245 | 291 | if(!ptr) |
daniele | 0:d7f2341ab245 | 292 | return -1; |
daniele | 0:d7f2341ab245 | 293 | |
daniele | 0:d7f2341ab245 | 294 | l = ptr; |
daniele | 0:d7f2341ab245 | 295 | while ((p = *ptr++) != 0){ |
daniele | 0:d7f2341ab245 | 296 | ptr += p; |
daniele | 0:d7f2341ab245 | 297 | *l = '.'; |
daniele | 0:d7f2341ab245 | 298 | l = ptr; |
daniele | 0:d7f2341ab245 | 299 | } |
daniele | 0:d7f2341ab245 | 300 | return 0; |
daniele | 0:d7f2341ab245 | 301 | } |
daniele | 0:d7f2341ab245 | 302 | |
daniele | 0:d7f2341ab245 | 303 | /* Seek the end of a string */ |
daniele | 0:d7f2341ab245 | 304 | static char *pico_dns_client_seek(char *ptr) |
daniele | 0:d7f2341ab245 | 305 | { |
daniele | 0:d7f2341ab245 | 306 | int p; |
daniele | 0:d7f2341ab245 | 307 | |
daniele | 0:d7f2341ab245 | 308 | if (!ptr) |
daniele | 0:d7f2341ab245 | 309 | return NULL; |
daniele | 0:d7f2341ab245 | 310 | |
daniele | 0:d7f2341ab245 | 311 | while ((p = *ptr++) != 0); |
daniele | 0:d7f2341ab245 | 312 | |
daniele | 0:d7f2341ab245 | 313 | return ptr++; |
daniele | 0:d7f2341ab245 | 314 | } |
daniele | 0:d7f2341ab245 | 315 | |
daniele | 0:d7f2341ab245 | 316 | static inline void pico_dns_client_construct_hdr(struct dns_message_hdr *hdr, uint16_t id) |
daniele | 0:d7f2341ab245 | 317 | { |
daniele | 0:d7f2341ab245 | 318 | hdr->id = short_be(id); |
daniele | 0:d7f2341ab245 | 319 | FLAG_QR(hdr, PICO_DNS_QR_QUERY); |
daniele | 0:d7f2341ab245 | 320 | FLAG_OPCODE(hdr, PICO_DNS_OPCODE_QUERY); |
daniele | 0:d7f2341ab245 | 321 | FLAG_AA(hdr, PICO_DNS_AA_NO_AUTHORITY); |
daniele | 0:d7f2341ab245 | 322 | FLAG_TC(hdr, PICO_DNS_TC_NO_TRUNCATION); |
daniele | 0:d7f2341ab245 | 323 | FLAG_RD(hdr, PICO_DNS_RD_IS_DESIRED); |
daniele | 0:d7f2341ab245 | 324 | FLAG_RA(hdr, PICO_DNS_RA_NO_SUPPORT); |
daniele | 0:d7f2341ab245 | 325 | FLAG_Z(hdr, 0); |
daniele | 0:d7f2341ab245 | 326 | FLAG_RCODE(hdr, PICO_DNS_RCODE_NO_ERROR); |
daniele | 0:d7f2341ab245 | 327 | hdr->flags = short_be(hdr->flags); |
daniele | 0:d7f2341ab245 | 328 | hdr->qdcount = short_be(1); |
daniele | 0:d7f2341ab245 | 329 | hdr->ancount = short_be(0); |
daniele | 0:d7f2341ab245 | 330 | hdr->nscount = short_be(0); |
daniele | 0:d7f2341ab245 | 331 | hdr->arcount = short_be(0); |
daniele | 0:d7f2341ab245 | 332 | } |
daniele | 0:d7f2341ab245 | 333 | |
daniele | 0:d7f2341ab245 | 334 | static inline void pico_dns_client_hdr_ntoh(struct dns_message_hdr *hdr) |
daniele | 0:d7f2341ab245 | 335 | { |
daniele | 0:d7f2341ab245 | 336 | hdr->id = short_be(hdr->id); |
daniele | 0:d7f2341ab245 | 337 | hdr->flags = short_be(hdr->flags); |
daniele | 0:d7f2341ab245 | 338 | hdr->qdcount = short_be(hdr->qdcount); |
daniele | 0:d7f2341ab245 | 339 | hdr->ancount = short_be(hdr->ancount); |
daniele | 0:d7f2341ab245 | 340 | hdr->nscount = short_be(hdr->nscount); |
daniele | 0:d7f2341ab245 | 341 | hdr->arcount = short_be(hdr->arcount); |
daniele | 0:d7f2341ab245 | 342 | } |
daniele | 0:d7f2341ab245 | 343 | |
daniele | 0:d7f2341ab245 | 344 | |
daniele | 0:d7f2341ab245 | 345 | static int pico_dns_client_mirror(char *ptr) |
daniele | 0:d7f2341ab245 | 346 | { |
daniele | 0:d7f2341ab245 | 347 | unsigned char buf[4] = {0}; |
daniele | 0:d7f2341ab245 | 348 | char *m; |
daniele | 0:d7f2341ab245 | 349 | int cnt = 0; |
daniele | 0:d7f2341ab245 | 350 | int p, i; |
daniele | 0:d7f2341ab245 | 351 | |
daniele | 0:d7f2341ab245 | 352 | if (!ptr) |
daniele | 0:d7f2341ab245 | 353 | return -1; |
daniele | 0:d7f2341ab245 | 354 | |
daniele | 0:d7f2341ab245 | 355 | m = ptr; |
daniele | 0:d7f2341ab245 | 356 | while ((p = *ptr++) != 0) |
daniele | 0:d7f2341ab245 | 357 | { |
daniele | 0:d7f2341ab245 | 358 | if (pico_is_digit(p)) { |
daniele | 0:d7f2341ab245 | 359 | buf[cnt] = (10 * buf[cnt]) + (p - '0'); |
daniele | 0:d7f2341ab245 | 360 | } else if (p == '.') { |
daniele | 0:d7f2341ab245 | 361 | cnt++; |
daniele | 0:d7f2341ab245 | 362 | } else { |
daniele | 0:d7f2341ab245 | 363 | return -1; |
daniele | 0:d7f2341ab245 | 364 | } |
daniele | 0:d7f2341ab245 | 365 | } |
daniele | 0:d7f2341ab245 | 366 | |
daniele | 0:d7f2341ab245 | 367 | /* Handle short notation */ |
daniele | 0:d7f2341ab245 | 368 | if(cnt == 1){ |
daniele | 0:d7f2341ab245 | 369 | buf[3] = buf[1]; |
daniele | 0:d7f2341ab245 | 370 | buf[1] = 0; |
daniele | 0:d7f2341ab245 | 371 | buf[2] = 0; |
daniele | 0:d7f2341ab245 | 372 | }else if (cnt == 2){ |
daniele | 0:d7f2341ab245 | 373 | buf[3] = buf[2]; |
daniele | 0:d7f2341ab245 | 374 | buf[2] = 0; |
daniele | 0:d7f2341ab245 | 375 | }else if(cnt != 3){ |
daniele | 0:d7f2341ab245 | 376 | /* String could not be parsed, return error */ |
daniele | 0:d7f2341ab245 | 377 | return -1; |
daniele | 0:d7f2341ab245 | 378 | } |
daniele | 0:d7f2341ab245 | 379 | |
daniele | 0:d7f2341ab245 | 380 | ptr = m; |
daniele | 0:d7f2341ab245 | 381 | for(i = 3; i >= 0; i--) |
daniele | 0:d7f2341ab245 | 382 | { |
daniele | 0:d7f2341ab245 | 383 | if(buf[i] > 99){ |
daniele | 0:d7f2341ab245 | 384 | *ptr++ = '0' + (buf[i] / 100); |
daniele | 0:d7f2341ab245 | 385 | *ptr++ = '0' + ((buf[i] % 100) / 10); |
daniele | 0:d7f2341ab245 | 386 | *ptr++ = '0' + ((buf[i] % 100) % 10); |
daniele | 0:d7f2341ab245 | 387 | }else if(buf[i] > 9){ |
daniele | 0:d7f2341ab245 | 388 | *ptr++ = '0' + (buf[i] / 10); |
daniele | 0:d7f2341ab245 | 389 | *ptr++ = '0' + (buf[i] % 10); |
daniele | 0:d7f2341ab245 | 390 | }else{ |
daniele | 0:d7f2341ab245 | 391 | *ptr++ = '0' + buf[i]; |
daniele | 0:d7f2341ab245 | 392 | } |
daniele | 0:d7f2341ab245 | 393 | if(i > 0) |
daniele | 0:d7f2341ab245 | 394 | *ptr++ = '.'; |
daniele | 0:d7f2341ab245 | 395 | } |
daniele | 0:d7f2341ab245 | 396 | |
daniele | 0:d7f2341ab245 | 397 | return 0; |
daniele | 0:d7f2341ab245 | 398 | } |
daniele | 0:d7f2341ab245 | 399 | |
daniele | 0:d7f2341ab245 | 400 | static struct pico_dns_key *pico_dns_client_idcheck(uint16_t id) |
daniele | 0:d7f2341ab245 | 401 | { |
daniele | 0:d7f2341ab245 | 402 | struct pico_dns_key test; |
daniele | 0:d7f2341ab245 | 403 | |
daniele | 0:d7f2341ab245 | 404 | test.id = id; |
daniele | 0:d7f2341ab245 | 405 | return pico_tree_findKey(&DNSTable,&test); |
daniele | 0:d7f2341ab245 | 406 | } |
daniele | 0:d7f2341ab245 | 407 | |
daniele | 0:d7f2341ab245 | 408 | static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s); |
daniele | 0:d7f2341ab245 | 409 | |
daniele | 0:d7f2341ab245 | 410 | static int pico_dns_client_send(struct pico_dns_key *key) |
daniele | 0:d7f2341ab245 | 411 | { |
daniele | 0:d7f2341ab245 | 412 | struct pico_socket *s; |
daniele | 0:d7f2341ab245 | 413 | int w = 0; |
daniele | 0:d7f2341ab245 | 414 | |
daniele | 0:d7f2341ab245 | 415 | dns_dbg("DNS: sending query to %08X\n", key->q_ns.ns.addr); |
daniele | 0:d7f2341ab245 | 416 | s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dns_client_callback); |
daniele | 0:d7f2341ab245 | 417 | if (!s) |
daniele | 0:d7f2341ab245 | 418 | return -1; |
daniele | 0:d7f2341ab245 | 419 | key->s = s; |
daniele | 0:d7f2341ab245 | 420 | if (pico_socket_connect(s, &key->q_ns.ns, short_be(PICO_DNS_NS_PORT)) != 0) |
daniele | 0:d7f2341ab245 | 421 | return -1; |
daniele | 0:d7f2341ab245 | 422 | w = pico_socket_send(s, key->q_hdr, key->len); |
daniele | 0:d7f2341ab245 | 423 | if (w <= 0) |
daniele | 0:d7f2341ab245 | 424 | return -1; |
daniele | 0:d7f2341ab245 | 425 | |
daniele | 0:d7f2341ab245 | 426 | return 0; |
daniele | 0:d7f2341ab245 | 427 | } |
daniele | 0:d7f2341ab245 | 428 | |
daniele | 0:d7f2341ab245 | 429 | static void pico_dns_client_retransmission(unsigned long now, void *arg) |
daniele | 0:d7f2341ab245 | 430 | { |
daniele | 0:d7f2341ab245 | 431 | struct pico_dns_key *key = (struct pico_dns_key *)arg; |
daniele | 0:d7f2341ab245 | 432 | struct pico_dns_ns *q_ns = NULL; |
daniele | 0:d7f2341ab245 | 433 | |
daniele | 0:d7f2341ab245 | 434 | if (!key->retrans) { |
daniele | 0:d7f2341ab245 | 435 | dns_dbg("DNS: no retransmission!\n"); |
daniele | 0:d7f2341ab245 | 436 | pico_free(key->q_hdr); |
daniele | 0:d7f2341ab245 | 437 | |
daniele | 0:d7f2341ab245 | 438 | if(pico_tree_delete(&DNSTable,key)) |
daniele | 0:d7f2341ab245 | 439 | pico_free(key); |
daniele | 0:d7f2341ab245 | 440 | } |
daniele | 0:d7f2341ab245 | 441 | else if (key->retrans <= PICO_DNS_CLIENT_MAX_RETRANS) { |
daniele | 0:d7f2341ab245 | 442 | key->retrans++; |
daniele | 0:d7f2341ab245 | 443 | dns_dbg("DNS: retransmission! (%u attempts)\n", key->retrans); |
daniele | 0:d7f2341ab245 | 444 | // ugly hack |
daniele | 0:d7f2341ab245 | 445 | q_ns = pico_tree_next(pico_tree_findNode(&NSTable,&key->q_ns))->keyValue; |
daniele | 0:d7f2341ab245 | 446 | if (q_ns) |
daniele | 0:d7f2341ab245 | 447 | key->q_ns = *q_ns; |
daniele | 0:d7f2341ab245 | 448 | else |
daniele | 0:d7f2341ab245 | 449 | key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable)); |
daniele | 0:d7f2341ab245 | 450 | pico_dns_client_send(key); |
daniele | 0:d7f2341ab245 | 451 | pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key); |
daniele | 0:d7f2341ab245 | 452 | } else { |
daniele | 0:d7f2341ab245 | 453 | dns_dbg("DNS ERROR: no reply from nameservers! (%u attempts)\n", key->retrans); |
daniele | 0:d7f2341ab245 | 454 | pico_socket_close(key->s); |
daniele | 0:d7f2341ab245 | 455 | pico_err = PICO_ERR_EIO; |
daniele | 0:d7f2341ab245 | 456 | key->callback(NULL, key->arg); |
daniele | 0:d7f2341ab245 | 457 | pico_free(key->q_hdr); |
daniele | 0:d7f2341ab245 | 458 | /* RB_REMOVE returns pointer to removed element, NULL to indicate error */ |
daniele | 0:d7f2341ab245 | 459 | |
daniele | 0:d7f2341ab245 | 460 | if(pico_tree_delete(&DNSTable,key)) |
daniele | 0:d7f2341ab245 | 461 | pico_free(key); |
daniele | 0:d7f2341ab245 | 462 | } |
daniele | 0:d7f2341ab245 | 463 | } |
daniele | 0:d7f2341ab245 | 464 | |
daniele | 0:d7f2341ab245 | 465 | static void pico_dns_client_callback(uint16_t ev, struct pico_socket *s) |
daniele | 0:d7f2341ab245 | 466 | { |
daniele | 0:d7f2341ab245 | 467 | char *q_qname, *q_suf, *a_hdr, *a_qname, *a_suf, *a_rdata; |
daniele | 0:d7f2341ab245 | 468 | struct dns_message_hdr *hdr; |
daniele | 0:d7f2341ab245 | 469 | struct dns_query_suffix query_suf; |
daniele | 0:d7f2341ab245 | 470 | struct dns_answer_suffix answer_suf; |
daniele | 0:d7f2341ab245 | 471 | struct pico_dns_key test, *key; |
daniele | 0:d7f2341ab245 | 472 | char *answer; |
daniele | 0:d7f2341ab245 | 473 | char dns_answer[PICO_DNS_MAX_RESPONSE_LEN] = {0}; |
daniele | 0:d7f2341ab245 | 474 | uint8_t valid_suffix = 0; |
daniele | 0:d7f2341ab245 | 475 | uint16_t compression = 0; |
daniele | 0:d7f2341ab245 | 476 | int i = 0, r = 0; |
daniele | 0:d7f2341ab245 | 477 | |
daniele | 0:d7f2341ab245 | 478 | if (ev & PICO_SOCK_EV_RD) { |
daniele | 0:d7f2341ab245 | 479 | r = pico_socket_read(s, dns_answer, PICO_DNS_MAX_RESPONSE_LEN); |
daniele | 0:d7f2341ab245 | 480 | pico_socket_close(s); |
daniele | 0:d7f2341ab245 | 481 | if (r == PICO_DNS_MAX_RESPONSE_LEN || r < (int)sizeof(struct dns_message_hdr)) { |
daniele | 0:d7f2341ab245 | 482 | dns_dbg("DNS ERROR: received incorrect number(%d) of bytes\n", r); |
daniele | 0:d7f2341ab245 | 483 | return; |
daniele | 0:d7f2341ab245 | 484 | } |
daniele | 0:d7f2341ab245 | 485 | |
daniele | 0:d7f2341ab245 | 486 | /* Check header validity */ |
daniele | 0:d7f2341ab245 | 487 | a_hdr = dns_answer; |
daniele | 0:d7f2341ab245 | 488 | hdr = (struct dns_message_hdr *) a_hdr; |
daniele | 0:d7f2341ab245 | 489 | pico_dns_client_hdr_ntoh(hdr); |
daniele | 0:d7f2341ab245 | 490 | if (GET_FLAG_QR(hdr) != PICO_DNS_QR_RESPONSE || GET_FLAG_OPCODE(hdr) != PICO_DNS_OPCODE_QUERY |
daniele | 0:d7f2341ab245 | 491 | || GET_FLAG_TC(hdr) == PICO_DNS_TC_IS_TRUNCATED || GET_FLAG_RCODE(hdr) != PICO_DNS_RCODE_NO_ERROR) { |
daniele | 0:d7f2341ab245 | 492 | dns_dbg("DNS ERROR: OPCODE %d | TC %d | RCODE %d\n", GET_FLAG_OPCODE(hdr), GET_FLAG_TC(hdr), GET_FLAG_RCODE(hdr)); |
daniele | 0:d7f2341ab245 | 493 | return; |
daniele | 0:d7f2341ab245 | 494 | } |
daniele | 0:d7f2341ab245 | 495 | |
daniele | 0:d7f2341ab245 | 496 | if (hdr->ancount < 1 || r < (int)(sizeof(struct dns_message_hdr) + hdr->qdcount * sizeof(struct dns_query_suffix) |
daniele | 0:d7f2341ab245 | 497 | + hdr->ancount * sizeof(struct dns_answer_suffix))) { |
daniele | 0:d7f2341ab245 | 498 | dns_dbg("DNS ERROR: ancount < 1 OR received number(%d) of bytes too low\n", r); |
daniele | 0:d7f2341ab245 | 499 | return; |
daniele | 0:d7f2341ab245 | 500 | } |
daniele | 0:d7f2341ab245 | 501 | |
daniele | 0:d7f2341ab245 | 502 | /* Find DNS key */ |
daniele | 0:d7f2341ab245 | 503 | test.id = hdr->id; |
daniele | 0:d7f2341ab245 | 504 | |
daniele | 0:d7f2341ab245 | 505 | key = pico_tree_findKey(&DNSTable,&test); |
daniele | 0:d7f2341ab245 | 506 | if (!key) { |
daniele | 0:d7f2341ab245 | 507 | dns_dbg("DNS WARNING: key with id %u not found\n", hdr->id); |
daniele | 0:d7f2341ab245 | 508 | return; |
daniele | 0:d7f2341ab245 | 509 | } |
daniele | 0:d7f2341ab245 | 510 | key->retrans = 0; |
daniele | 0:d7f2341ab245 | 511 | |
daniele | 0:d7f2341ab245 | 512 | /* Check query suffix validity */ |
daniele | 0:d7f2341ab245 | 513 | q_qname = a_hdr + sizeof(struct dns_message_hdr); |
daniele | 0:d7f2341ab245 | 514 | q_suf = pico_dns_client_seek(q_qname); |
daniele | 0:d7f2341ab245 | 515 | query_suf = *(struct dns_query_suffix *) q_suf; |
daniele | 0:d7f2341ab245 | 516 | if (short_be(query_suf.qtype) != key->qtype || short_be(query_suf.qclass) != key->qclass) { |
daniele | 0:d7f2341ab245 | 517 | dns_dbg("DNS ERROR: received qtype (%u) or qclass (%u) incorrect\n", short_be(query_suf.qtype), short_be(query_suf.qclass)); |
daniele | 0:d7f2341ab245 | 518 | return; |
daniele | 0:d7f2341ab245 | 519 | } |
daniele | 0:d7f2341ab245 | 520 | |
daniele | 0:d7f2341ab245 | 521 | /* Seek answer suffix */ |
daniele | 0:d7f2341ab245 | 522 | a_qname = q_suf + sizeof(struct dns_query_suffix); |
daniele | 0:d7f2341ab245 | 523 | a_suf = a_qname; |
daniele | 0:d7f2341ab245 | 524 | while(i++ < hdr->ancount) { |
daniele | 0:d7f2341ab245 | 525 | compression = short_be(*(uint16_t *)a_suf); |
daniele | 0:d7f2341ab245 | 526 | switch (compression >> 14) |
daniele | 0:d7f2341ab245 | 527 | { |
daniele | 0:d7f2341ab245 | 528 | case PICO_DNS_POINTER: |
daniele | 0:d7f2341ab245 | 529 | while (compression >> 14 == PICO_DNS_POINTER) { |
daniele | 0:d7f2341ab245 | 530 | dns_dbg("DNS: pointer\n"); |
daniele | 0:d7f2341ab245 | 531 | a_suf += sizeof(uint16_t); |
daniele | 0:d7f2341ab245 | 532 | compression = short_be(*(uint16_t *)a_suf); |
daniele | 0:d7f2341ab245 | 533 | } |
daniele | 0:d7f2341ab245 | 534 | break; |
daniele | 0:d7f2341ab245 | 535 | |
daniele | 0:d7f2341ab245 | 536 | case PICO_DNS_LABEL: |
daniele | 0:d7f2341ab245 | 537 | dns_dbg("DNS: label\n"); |
daniele | 0:d7f2341ab245 | 538 | a_suf = pico_dns_client_seek(a_qname); |
daniele | 0:d7f2341ab245 | 539 | break; |
daniele | 0:d7f2341ab245 | 540 | |
daniele | 0:d7f2341ab245 | 541 | default: |
daniele | 0:d7f2341ab245 | 542 | dns_dbg("DNS ERROR: incorrect compression (%u) value\n", compression); |
daniele | 0:d7f2341ab245 | 543 | return; |
daniele | 0:d7f2341ab245 | 544 | } |
daniele | 0:d7f2341ab245 | 545 | |
daniele | 0:d7f2341ab245 | 546 | /* Check answer suffix validity */ |
daniele | 0:d7f2341ab245 | 547 | answer_suf = *(struct dns_answer_suffix *)a_suf; |
daniele | 0:d7f2341ab245 | 548 | if (short_be(answer_suf.qtype) != key->qtype || short_be(answer_suf.qclass) != key->qclass) { |
daniele | 0:d7f2341ab245 | 549 | dns_dbg("DNS WARNING: received qtype (%u) or qclass (%u) incorrect\n", short_be(answer_suf.qtype), short_be(answer_suf.qclass)); |
daniele | 0:d7f2341ab245 | 550 | a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength); |
daniele | 0:d7f2341ab245 | 551 | continue; |
daniele | 0:d7f2341ab245 | 552 | } |
daniele | 0:d7f2341ab245 | 553 | |
daniele | 0:d7f2341ab245 | 554 | if (short_be(answer_suf.ttl) > PICO_DNS_MAX_TTL) { |
daniele | 0:d7f2341ab245 | 555 | dns_dbg("DNS WARNING: received TTL (%u) > MAX (%u)\n", short_be(answer_suf.ttl), PICO_DNS_MAX_TTL); |
daniele | 0:d7f2341ab245 | 556 | a_suf = a_suf + sizeof(struct dns_answer_suffix) + short_be(answer_suf.rdlength); |
daniele | 0:d7f2341ab245 | 557 | continue; |
daniele | 0:d7f2341ab245 | 558 | } |
daniele | 0:d7f2341ab245 | 559 | |
daniele | 0:d7f2341ab245 | 560 | valid_suffix = 1; |
daniele | 0:d7f2341ab245 | 561 | break; |
daniele | 0:d7f2341ab245 | 562 | } |
daniele | 0:d7f2341ab245 | 563 | |
daniele | 0:d7f2341ab245 | 564 | if (!valid_suffix) { |
daniele | 0:d7f2341ab245 | 565 | dns_dbg("DNS ERROR: invalid dns answer suffix\n"); |
daniele | 0:d7f2341ab245 | 566 | return; |
daniele | 0:d7f2341ab245 | 567 | } |
daniele | 0:d7f2341ab245 | 568 | |
daniele | 0:d7f2341ab245 | 569 | a_rdata = a_suf + sizeof(struct dns_answer_suffix); |
daniele | 0:d7f2341ab245 | 570 | if (key->qtype == PICO_DNS_TYPE_A) { |
daniele | 0:d7f2341ab245 | 571 | dns_dbg("DNS: length %u | ip %08X\n", short_be(answer_suf.rdlength), long_be(*(uint32_t *)a_rdata)); |
daniele | 0:d7f2341ab245 | 572 | answer = pico_zalloc(16); |
daniele | 0:d7f2341ab245 | 573 | pico_ipv4_to_string(answer, *(uint32_t *)a_rdata); |
daniele | 0:d7f2341ab245 | 574 | key->callback(answer, key->arg); |
daniele | 0:d7f2341ab245 | 575 | } else if (key->qtype == PICO_DNS_TYPE_PTR) { |
daniele | 0:d7f2341ab245 | 576 | pico_dns_client_reverse_label((char *) a_rdata); |
daniele | 0:d7f2341ab245 | 577 | dns_dbg("DNS: length %u | name %s\n", short_be(answer_suf.rdlength), (char *)a_rdata + 1); |
daniele | 0:d7f2341ab245 | 578 | answer = pico_zalloc(answer_suf.rdlength - 1); |
daniele | 0:d7f2341ab245 | 579 | memcpy(answer, (char *)a_rdata + 1, short_be(answer_suf.rdlength) - 1); |
daniele | 0:d7f2341ab245 | 580 | key->callback(answer, key->arg); |
daniele | 0:d7f2341ab245 | 581 | } else { |
daniele | 0:d7f2341ab245 | 582 | dns_dbg("DNS ERROR: incorrect qtype (%u)\n", key->qtype); |
daniele | 0:d7f2341ab245 | 583 | return; |
daniele | 0:d7f2341ab245 | 584 | } |
daniele | 0:d7f2341ab245 | 585 | } |
daniele | 0:d7f2341ab245 | 586 | |
daniele | 0:d7f2341ab245 | 587 | if (ev == PICO_SOCK_EV_ERR) { |
daniele | 0:d7f2341ab245 | 588 | dns_dbg("DNS: socket error received\n"); |
daniele | 0:d7f2341ab245 | 589 | } |
daniele | 0:d7f2341ab245 | 590 | } |
daniele | 0:d7f2341ab245 | 591 | |
daniele | 0:d7f2341ab245 | 592 | int pico_dns_client_getaddr(const char *url, void (*callback)(char *, void *), void *arg) |
daniele | 0:d7f2341ab245 | 593 | { |
daniele | 0:d7f2341ab245 | 594 | char *q_hdr, *q_qname, *q_suf; |
daniele | 0:d7f2341ab245 | 595 | struct dns_message_hdr *hdr; |
daniele | 0:d7f2341ab245 | 596 | struct dns_query_suffix query_suf; |
daniele | 0:d7f2341ab245 | 597 | struct pico_dns_key *key; |
daniele | 0:d7f2341ab245 | 598 | uint16_t url_len = 0; |
daniele | 0:d7f2341ab245 | 599 | uint16_t id = 0; |
daniele | 0:d7f2341ab245 | 600 | |
daniele | 0:d7f2341ab245 | 601 | if (!url || !callback) { |
daniele | 0:d7f2341ab245 | 602 | dns_dbg("DNS ERROR: NULL parameters\n"); |
daniele | 0:d7f2341ab245 | 603 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 604 | return -1; |
daniele | 0:d7f2341ab245 | 605 | } |
daniele | 0:d7f2341ab245 | 606 | |
daniele | 0:d7f2341ab245 | 607 | url_len = pico_dns_client_strlen(url); |
daniele | 0:d7f2341ab245 | 608 | /* 2 extra bytes for url_len to account for 2 extra label length octets */ |
daniele | 0:d7f2341ab245 | 609 | q_hdr = pico_zalloc(sizeof(struct dns_message_hdr) + (1 + url_len + 1) + sizeof(struct dns_query_suffix)); |
daniele | 0:d7f2341ab245 | 610 | if (!q_hdr) { |
daniele | 0:d7f2341ab245 | 611 | pico_err = PICO_ERR_ENOMEM; |
daniele | 0:d7f2341ab245 | 612 | return -1; |
daniele | 0:d7f2341ab245 | 613 | } |
daniele | 0:d7f2341ab245 | 614 | q_qname = q_hdr + sizeof(struct dns_message_hdr); |
daniele | 0:d7f2341ab245 | 615 | q_suf = q_qname + (1 + url_len + 1); |
daniele | 0:d7f2341ab245 | 616 | |
daniele | 0:d7f2341ab245 | 617 | /* Construct query header */ |
daniele | 0:d7f2341ab245 | 618 | hdr = (struct dns_message_hdr *) q_hdr; |
daniele | 0:d7f2341ab245 | 619 | do { |
daniele | 0:d7f2341ab245 | 620 | id = (uint16_t) (pico_rand() & 0xFFFFU); |
daniele | 0:d7f2341ab245 | 621 | dns_dbg("DNS: generated id %u\n", id); |
daniele | 0:d7f2341ab245 | 622 | } while (pico_dns_client_idcheck(id)); |
daniele | 0:d7f2341ab245 | 623 | pico_dns_client_construct_hdr(hdr, id); |
daniele | 0:d7f2341ab245 | 624 | /* Add and manipulate domain name */ |
daniele | 0:d7f2341ab245 | 625 | memcpy(q_qname + 1, url, url_len + 1); |
daniele | 0:d7f2341ab245 | 626 | pico_dns_client_label(q_qname); |
daniele | 0:d7f2341ab245 | 627 | /* Add type and class of query */ |
daniele | 0:d7f2341ab245 | 628 | query_suf.qtype = short_be(PICO_DNS_TYPE_A); |
daniele | 0:d7f2341ab245 | 629 | query_suf.qclass = short_be(PICO_DNS_CLASS_IN); |
daniele | 0:d7f2341ab245 | 630 | memcpy(q_suf, &query_suf, sizeof(struct dns_query_suffix)); |
daniele | 0:d7f2341ab245 | 631 | /* Create RB entry */ |
daniele | 0:d7f2341ab245 | 632 | key = pico_zalloc(sizeof(struct pico_dns_key)); |
daniele | 0:d7f2341ab245 | 633 | if (!key) { |
daniele | 0:d7f2341ab245 | 634 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 635 | pico_err = PICO_ERR_ENOMEM; |
daniele | 0:d7f2341ab245 | 636 | return -1; |
daniele | 0:d7f2341ab245 | 637 | } |
daniele | 0:d7f2341ab245 | 638 | key->q_hdr = q_hdr; |
daniele | 0:d7f2341ab245 | 639 | key->len = sizeof(struct dns_message_hdr) + (1 + url_len + 1) + sizeof(struct dns_query_suffix); |
daniele | 0:d7f2341ab245 | 640 | key->id = id; |
daniele | 0:d7f2341ab245 | 641 | key->qtype = PICO_DNS_TYPE_A; |
daniele | 0:d7f2341ab245 | 642 | key->qclass = PICO_DNS_CLASS_IN; |
daniele | 0:d7f2341ab245 | 643 | key->retrans = 1; |
daniele | 0:d7f2341ab245 | 644 | |
daniele | 0:d7f2341ab245 | 645 | key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable)); |
daniele | 0:d7f2341ab245 | 646 | key->s = NULL; |
daniele | 0:d7f2341ab245 | 647 | key->callback = callback; |
daniele | 0:d7f2341ab245 | 648 | key->arg = arg; |
daniele | 0:d7f2341ab245 | 649 | /* Send query */ |
daniele | 0:d7f2341ab245 | 650 | if (pico_dns_client_send(key) < 0) { |
daniele | 0:d7f2341ab245 | 651 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 652 | if (key->s) |
daniele | 0:d7f2341ab245 | 653 | pico_socket_close(key->s); |
daniele | 0:d7f2341ab245 | 654 | pico_free(key); |
daniele | 0:d7f2341ab245 | 655 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 656 | return -1; |
daniele | 0:d7f2341ab245 | 657 | } |
daniele | 0:d7f2341ab245 | 658 | /* Insert RB entry */ |
daniele | 0:d7f2341ab245 | 659 | |
daniele | 0:d7f2341ab245 | 660 | if(pico_tree_insert(&DNSTable,key)) { |
daniele | 0:d7f2341ab245 | 661 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 662 | pico_free(key); |
daniele | 0:d7f2341ab245 | 663 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 664 | return -1; /* Element key already exists */ |
daniele | 0:d7f2341ab245 | 665 | } |
daniele | 0:d7f2341ab245 | 666 | |
daniele | 0:d7f2341ab245 | 667 | pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key); |
daniele | 0:d7f2341ab245 | 668 | return 0; |
daniele | 0:d7f2341ab245 | 669 | } |
daniele | 0:d7f2341ab245 | 670 | |
daniele | 0:d7f2341ab245 | 671 | int pico_dns_client_getname(const char *ip, void (*callback)(char *, void *), void *arg) |
daniele | 0:d7f2341ab245 | 672 | { |
daniele | 0:d7f2341ab245 | 673 | char *q_hdr, *q_qname, *q_suf; |
daniele | 0:d7f2341ab245 | 674 | struct dns_message_hdr *hdr; |
daniele | 0:d7f2341ab245 | 675 | struct dns_query_suffix query_suf; |
daniele | 0:d7f2341ab245 | 676 | struct pico_dns_key *key; |
daniele | 0:d7f2341ab245 | 677 | uint16_t ip_len = 0; |
daniele | 0:d7f2341ab245 | 678 | uint16_t arpa_len = 0; |
daniele | 0:d7f2341ab245 | 679 | uint16_t id = 0; |
daniele | 0:d7f2341ab245 | 680 | |
daniele | 0:d7f2341ab245 | 681 | if (!ip || !callback) { |
daniele | 0:d7f2341ab245 | 682 | dns_dbg("DNS ERROR: NULL parameters\n"); |
daniele | 0:d7f2341ab245 | 683 | pico_err = PICO_ERR_EINVAL; |
daniele | 0:d7f2341ab245 | 684 | return -1; |
daniele | 0:d7f2341ab245 | 685 | } |
daniele | 0:d7f2341ab245 | 686 | |
daniele | 0:d7f2341ab245 | 687 | ip_len = pico_dns_client_strlen(ip); |
daniele | 0:d7f2341ab245 | 688 | arpa_len = pico_dns_client_strlen(".in-addr.arpa"); |
daniele | 0:d7f2341ab245 | 689 | /* 2 extra bytes for ip_len and arpa_len to account for 2 extra length octets */ |
daniele | 0:d7f2341ab245 | 690 | q_hdr = pico_zalloc(sizeof(struct dns_message_hdr) + (1 + ip_len + arpa_len + 1) + sizeof(struct dns_query_suffix)); |
daniele | 0:d7f2341ab245 | 691 | if (!q_hdr) { |
daniele | 0:d7f2341ab245 | 692 | pico_err = PICO_ERR_ENOMEM; |
daniele | 0:d7f2341ab245 | 693 | return -1; |
daniele | 0:d7f2341ab245 | 694 | } |
daniele | 0:d7f2341ab245 | 695 | q_qname = q_hdr + sizeof(struct dns_message_hdr); |
daniele | 0:d7f2341ab245 | 696 | q_suf = q_qname + (1 + ip_len + arpa_len + 1); |
daniele | 0:d7f2341ab245 | 697 | |
daniele | 0:d7f2341ab245 | 698 | /* Construct query header */ |
daniele | 0:d7f2341ab245 | 699 | hdr = (struct dns_message_hdr *)q_hdr; |
daniele | 0:d7f2341ab245 | 700 | do { |
daniele | 0:d7f2341ab245 | 701 | id = (uint16_t) (pico_rand() & 0xFFFFU); |
daniele | 0:d7f2341ab245 | 702 | dns_dbg("DNS: generated id %u\n", id); |
daniele | 0:d7f2341ab245 | 703 | } while (pico_dns_client_idcheck(id)); |
daniele | 0:d7f2341ab245 | 704 | pico_dns_client_construct_hdr(hdr, id); |
daniele | 0:d7f2341ab245 | 705 | /* Add and manipulate domain name */ |
daniele | 0:d7f2341ab245 | 706 | memcpy(q_qname + 1, ip, ip_len + 1); |
daniele | 0:d7f2341ab245 | 707 | pico_dns_client_mirror(q_qname + 1); |
daniele | 0:d7f2341ab245 | 708 | memcpy(q_qname + 1 + ip_len, ".in-addr.arpa", arpa_len); |
daniele | 0:d7f2341ab245 | 709 | pico_dns_client_label(q_qname); |
daniele | 0:d7f2341ab245 | 710 | /* Add type and class of query */ |
daniele | 0:d7f2341ab245 | 711 | query_suf.qtype = short_be(PICO_DNS_TYPE_PTR); |
daniele | 0:d7f2341ab245 | 712 | query_suf.qclass = short_be(PICO_DNS_CLASS_IN); |
daniele | 0:d7f2341ab245 | 713 | memcpy(q_suf, &query_suf, sizeof(struct dns_query_suffix)); |
daniele | 0:d7f2341ab245 | 714 | /* Create RB entry */ |
daniele | 0:d7f2341ab245 | 715 | key = pico_zalloc(sizeof(struct pico_dns_key)); |
daniele | 0:d7f2341ab245 | 716 | if (!key) { |
daniele | 0:d7f2341ab245 | 717 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 718 | pico_err = PICO_ERR_ENOMEM; |
daniele | 0:d7f2341ab245 | 719 | return -1; |
daniele | 0:d7f2341ab245 | 720 | } |
daniele | 0:d7f2341ab245 | 721 | key->q_hdr = q_hdr; |
daniele | 0:d7f2341ab245 | 722 | key->len = sizeof(struct dns_message_hdr) + (1 + ip_len + arpa_len + 1) + sizeof(struct dns_query_suffix); |
daniele | 0:d7f2341ab245 | 723 | key->id = id; |
daniele | 0:d7f2341ab245 | 724 | key->qtype = PICO_DNS_TYPE_PTR; |
daniele | 0:d7f2341ab245 | 725 | key->qclass = PICO_DNS_CLASS_IN; |
daniele | 0:d7f2341ab245 | 726 | key->retrans = 1; |
daniele | 0:d7f2341ab245 | 727 | key->q_ns = *((struct pico_dns_ns *)pico_tree_first(&NSTable)); |
daniele | 0:d7f2341ab245 | 728 | key->s = NULL; |
daniele | 0:d7f2341ab245 | 729 | key->callback = callback; |
daniele | 0:d7f2341ab245 | 730 | key->arg = arg; |
daniele | 0:d7f2341ab245 | 731 | /* Send query */ |
daniele | 0:d7f2341ab245 | 732 | if (pico_dns_client_send(key) < 0) { |
daniele | 0:d7f2341ab245 | 733 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 734 | if (key->s) |
daniele | 0:d7f2341ab245 | 735 | pico_socket_close(key->s); |
daniele | 0:d7f2341ab245 | 736 | pico_free(key); |
daniele | 0:d7f2341ab245 | 737 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 738 | return -1; |
daniele | 0:d7f2341ab245 | 739 | } |
daniele | 0:d7f2341ab245 | 740 | /* Insert RB entry */ |
daniele | 0:d7f2341ab245 | 741 | |
daniele | 0:d7f2341ab245 | 742 | if(pico_tree_insert(&DNSTable,key)) { |
daniele | 0:d7f2341ab245 | 743 | pico_free(q_hdr); |
daniele | 0:d7f2341ab245 | 744 | pico_free(key); |
daniele | 0:d7f2341ab245 | 745 | pico_err = PICO_ERR_EAGAIN; |
daniele | 0:d7f2341ab245 | 746 | return -1; /* Element key already exists */ |
daniele | 0:d7f2341ab245 | 747 | } |
daniele | 0:d7f2341ab245 | 748 | |
daniele | 0:d7f2341ab245 | 749 | pico_timer_add(PICO_DNS_CLIENT_RETRANS, pico_dns_client_retransmission, key); |
daniele | 0:d7f2341ab245 | 750 | return 0; |
daniele | 0:d7f2341ab245 | 751 | } |
daniele | 0:d7f2341ab245 | 752 | |
daniele | 0:d7f2341ab245 | 753 | #ifdef PICO_DNS_CLIENT_MAIN |
daniele | 0:d7f2341ab245 | 754 | int main(int argc, char *argv[]) |
daniele | 0:d7f2341ab245 | 755 | { |
daniele | 0:d7f2341ab245 | 756 | dns_dbg(">>>>> DNS GET ADDR\n"); |
daniele | 0:d7f2341ab245 | 757 | pico_dns_client_getaddr("www.google.be"); |
daniele | 0:d7f2341ab245 | 758 | dns_dbg(">>>>> DNS GET NAME\n"); |
daniele | 0:d7f2341ab245 | 759 | pico_dns_client_getname("173.194.67.94"); |
daniele | 0:d7f2341ab245 | 760 | |
daniele | 0:d7f2341ab245 | 761 | return 0; |
daniele | 0:d7f2341ab245 | 762 | } |
daniele | 0:d7f2341ab245 | 763 | #endif /* PICO_DNS_CLIENT_MAIN */ |
daniele | 0:d7f2341ab245 | 764 | #endif /* PICO_SUPPORT_DNS_CLIENT */ |