dns.c (8577B)
1 #include "dns.h" 2 3 #include <stdio.h> 4 #include <netdb.h> 5 #include <sys/types.h> 6 #include <netinet/in.h> 7 #ifndef BIND_8_COMPAT 8 #define BIND_8_COMPAT /* Mac OS X: if Bind 9, Bind 8 compatibility */ 9 #endif 10 #include <arpa/nameser.h> 11 #include <resolv.h> 12 #include <errno.h> 13 extern int res_query(); 14 extern int res_search(); 15 #include "ip.h" 16 #include "ipalloc.h" 17 #include "fmt.h" 18 #include "alloc.h" 19 #include "str.h" 20 #include "stralloc.h" 21 #include "case.h" 22 23 #define MAX_EDNS_RESPONSE_SIZE 65536 24 25 static unsigned short getshort(c) unsigned char *c; 26 { unsigned short u; u = c[0]; return (u << 8) + c[1]; } 27 28 static struct { unsigned char *buf; } response; 29 static int responsebuflen = 0; 30 static int responselen; 31 static unsigned char *responseend; 32 static unsigned char *responsepos; 33 static unsigned long saveresoptions; 34 35 static int numanswers; 36 static char name[MAXDNAME]; 37 static struct ip_address ip; 38 unsigned short pref; 39 40 static stralloc glue = {0}; 41 42 static int (*lookup)() = res_query; 43 44 static int resolve(domain,type) 45 stralloc *domain; 46 int type; 47 { 48 int n; 49 int i; 50 51 errno = 0; 52 if (!stralloc_copy(&glue,domain)) return DNS_MEM; 53 if (!stralloc_0(&glue)) return DNS_MEM; 54 if (!responsebuflen) { 55 if ((response.buf = malloc(PACKETSZ+1))) 56 responsebuflen = PACKETSZ+1; 57 else return DNS_MEM; 58 } 59 60 responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); 61 if ((responselen >= responsebuflen) || 62 (responselen > 0 && (((HEADER *)response.buf)->tc))) 63 { 64 if (responsebuflen < MAX_EDNS_RESPONSE_SIZE) { 65 unsigned char *newbuf = realloc(response.buf, MAX_EDNS_RESPONSE_SIZE); 66 if (newbuf) { 67 response.buf = newbuf; 68 responsebuflen = MAX_EDNS_RESPONSE_SIZE; 69 } 70 else return DNS_MEM; 71 saveresoptions = _res.options; 72 _res.options |= RES_USEVC; 73 responselen = lookup(glue.s,C_IN,type,response.buf,responsebuflen); 74 _res.options = saveresoptions; 75 } 76 } 77 if (responselen <= 0) 78 { 79 if (errno == ECONNREFUSED) return DNS_SOFT; 80 if (h_errno == TRY_AGAIN) return DNS_SOFT; 81 return DNS_HARD; 82 } 83 responseend = response.buf + responselen; 84 responsepos = response.buf + sizeof(HEADER); 85 n = ntohs(((HEADER *)response.buf)->qdcount); 86 while (n-- > 0) 87 { 88 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); 89 if (i < 0) return DNS_SOFT; 90 responsepos += i; 91 i = responseend - responsepos; 92 if (i < QFIXEDSZ) return DNS_SOFT; 93 responsepos += QFIXEDSZ; 94 } 95 numanswers = ntohs(((HEADER *)response.buf)->ancount); 96 return 0; 97 } 98 99 static int findname(wanttype) 100 int wanttype; 101 { 102 unsigned short rrtype; 103 unsigned short rrdlen; 104 int i; 105 106 if (numanswers <= 0) return 2; 107 --numanswers; 108 if (responsepos == responseend) return DNS_SOFT; 109 110 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); 111 if (i < 0) return DNS_SOFT; 112 responsepos += i; 113 114 i = responseend - responsepos; 115 if (i < 4 + 3 * 2) return DNS_SOFT; 116 117 rrtype = getshort(responsepos); 118 rrdlen = getshort(responsepos + 8); 119 responsepos += 10; 120 121 if (rrtype == wanttype) 122 { 123 if (dn_expand(response.buf,responseend,responsepos,name,MAXDNAME) < 0) 124 return DNS_SOFT; 125 responsepos += rrdlen; 126 return 1; 127 } 128 129 responsepos += rrdlen; 130 return 0; 131 } 132 133 // XXX A only 134 static int findip(int wanttype) 135 { 136 unsigned short rrtype; 137 unsigned short rrdlen; 138 int i; 139 140 if (numanswers <= 0) return 2; 141 --numanswers; 142 if (responsepos == responseend) return DNS_SOFT; 143 144 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); 145 if (i < 0) return DNS_SOFT; 146 responsepos += i; 147 148 i = responseend - responsepos; 149 if (i < 4 + 3 * 2) return DNS_SOFT; 150 151 rrtype = getshort(responsepos); 152 rrdlen = getshort(responsepos + 8); 153 responsepos += 10; 154 155 if (rrtype == wanttype) 156 { 157 if (rrdlen < 4) 158 return DNS_SOFT; 159 ip.is6 = 0; 160 ip.ip.a.d[0] = responsepos[0]; 161 ip.ip.a.d[1] = responsepos[1]; 162 ip.ip.a.d[2] = responsepos[2]; 163 ip.ip.a.d[3] = responsepos[3]; 164 responsepos += rrdlen; 165 return 1; 166 } 167 168 responsepos += rrdlen; 169 return 0; 170 } 171 172 static int findmx(wanttype) 173 int wanttype; 174 { 175 unsigned short rrtype; 176 unsigned short rrdlen; 177 int i; 178 179 if (numanswers <= 0) return 2; 180 --numanswers; 181 if (responsepos == responseend) return DNS_SOFT; 182 183 i = dn_expand(response.buf,responseend,responsepos,name,MAXDNAME); 184 if (i < 0) return DNS_SOFT; 185 responsepos += i; 186 187 i = responseend - responsepos; 188 if (i < 4 + 3 * 2) return DNS_SOFT; 189 190 rrtype = getshort(responsepos); 191 rrdlen = getshort(responsepos + 8); 192 responsepos += 10; 193 194 if (rrtype == wanttype) 195 { 196 if (rrdlen < 3) 197 return DNS_SOFT; 198 pref = (responsepos[0] << 8) + responsepos[1]; 199 if (dn_expand(response.buf,responseend,responsepos + 2,name,MAXDNAME) < 0) 200 return DNS_SOFT; 201 responsepos += rrdlen; 202 return 1; 203 } 204 205 responsepos += rrdlen; 206 return 0; 207 } 208 209 void dns_init(flagsearch) 210 int flagsearch; 211 { 212 res_init(); 213 if (flagsearch) lookup = res_search; 214 } 215 216 #define FMT_IAA 40 217 218 static int iaafmt(char *s, struct ip_address *ipa) 219 { 220 unsigned int i; 221 unsigned int len; 222 len = 0; 223 i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[3]); len += i; if (s) s += i; 224 i = fmt_str(s,"."); len += i; if (s) s += i; 225 i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[2]); len += i; if (s) s += i; 226 i = fmt_str(s,"."); len += i; if (s) s += i; 227 i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[1]); len += i; if (s) s += i; 228 i = fmt_str(s,"."); len += i; if (s) s += i; 229 i = fmt_ulong(s,(unsigned long) ipa->ip.a.d[0]); len += i; if (s) s += i; 230 i = fmt_str(s,".in-addr.arpa."); len += i; if (s) s += i; 231 return len; 232 } 233 234 int dns_ptr(stralloc *sa, struct ip_address *ipa) 235 { 236 int r; 237 238 if (!stralloc_ready(sa,iaafmt(NULL,ipa))) return DNS_MEM; 239 sa->len = iaafmt(sa->s,ipa); 240 switch(resolve(sa,T_PTR)) 241 { 242 case DNS_MEM: return DNS_MEM; 243 case DNS_SOFT: return DNS_SOFT; 244 case DNS_HARD: return DNS_HARD; 245 } 246 while ((r = findname(T_PTR)) != 2) 247 { 248 if (r == DNS_SOFT) return DNS_SOFT; 249 if (r == 1) 250 { 251 if (!stralloc_copys(sa,name)) return DNS_MEM; 252 return 0; 253 } 254 } 255 return DNS_HARD; 256 } 257 258 static int dns_ipplus(ipalloc *ia, stralloc *sa, int dpref) 259 { 260 int r; 261 struct ip_mx ix; 262 263 if (!stralloc_copy(&glue,sa)) return DNS_MEM; 264 if (!stralloc_0(&glue)) return DNS_MEM; 265 if (glue.s[0]) { 266 ix.pref = 0; 267 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) 268 { 269 if (!ipalloc_append(ia,&ix)) return DNS_MEM; 270 return 0; 271 } 272 } 273 274 switch(resolve(sa,T_A)) 275 { 276 case DNS_MEM: return DNS_MEM; 277 case DNS_SOFT: return DNS_SOFT; 278 case DNS_HARD: return DNS_HARD; 279 } 280 while ((r = findip(T_A)) != 2) 281 { 282 ix.ip = ip; 283 ix.pref = dpref; 284 if (r == DNS_SOFT) return DNS_SOFT; 285 if (r == 1) 286 if (!ipalloc_append(ia,&ix)) return DNS_MEM; 287 } 288 return 0; 289 } 290 291 int dns_ip(ia,sa) 292 ipalloc *ia; 293 stralloc *sa; 294 { 295 if (!ipalloc_readyplus(ia,0)) return DNS_MEM; 296 ia->len = 0; 297 return dns_ipplus(ia,sa,0); 298 } 299 300 int dns_mxip(ia,sa,random) 301 ipalloc *ia; 302 stralloc *sa; 303 unsigned long random; 304 { 305 int r; 306 struct mx { stralloc sa; unsigned short p; } *mx; 307 struct ip_mx ix; 308 int nummx; 309 int i; 310 int j; 311 int flagsoft; 312 313 if (!ipalloc_readyplus(ia,0)) return DNS_MEM; 314 ia->len = 0; 315 316 if (!stralloc_copy(&glue,sa)) return DNS_MEM; 317 if (!stralloc_0(&glue)) return DNS_MEM; 318 if (glue.s[0]) { 319 ix.pref = 0; 320 if (!glue.s[ip_scan(glue.s,&ix.ip)] || !glue.s[ip_scanbracket(glue.s,&ix.ip)]) 321 { 322 if (!ipalloc_append(ia,&ix)) return DNS_MEM; 323 return 0; 324 } 325 } 326 327 switch(resolve(sa,T_MX)) 328 { 329 case DNS_MEM: return DNS_MEM; 330 case DNS_SOFT: return DNS_SOFT; 331 case DNS_HARD: return dns_ip(ia,sa); 332 } 333 334 mx = (struct mx *) alloc(numanswers * sizeof(struct mx)); 335 if (!mx) return DNS_MEM; 336 nummx = 0; 337 338 while ((r = findmx(T_MX)) != 2) 339 { 340 if (r == DNS_SOFT) { alloc_free(mx); return DNS_SOFT; } 341 if (r == 1) 342 { 343 mx[nummx].p = pref; 344 mx[nummx].sa.s = 0; 345 if (!stralloc_copys(&mx[nummx].sa,name)) 346 { 347 while (nummx > 0) alloc_free(mx[--nummx].sa.s); 348 alloc_free(mx); return DNS_MEM; 349 } 350 ++nummx; 351 } 352 } 353 354 if (!nummx) return dns_ip(ia,sa); /* e.g., CNAME -> A */ 355 356 flagsoft = 0; 357 while (nummx > 0) 358 { 359 unsigned long numsame; 360 361 i = 0; 362 numsame = 1; 363 for (j = 1;j < nummx;++j) 364 if (mx[j].p < mx[i].p) 365 { 366 i = j; 367 numsame = 1; 368 } 369 else if (mx[j].p == mx[i].p) 370 { 371 ++numsame; 372 random = random * 69069 + 1; 373 if ((random / 2) < (2147483647 / numsame)) 374 i = j; 375 } 376 377 switch(dns_ipplus(ia,&mx[i].sa,mx[i].p)) 378 { 379 case DNS_MEM: case DNS_SOFT: 380 flagsoft = 1; break; 381 } 382 383 alloc_free(mx[i].sa.s); 384 mx[i] = mx[--nummx]; 385 } 386 387 alloc_free(mx); 388 return flagsoft; 389 }