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