1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * lib/krb5/os/dnsglue.c 8 * 9 * Copyright 2004 by the Massachusetts Institute of Technology. 10 * All Rights Reserved. 11 * 12 * Export of this software from the United States of America may 13 * require a specific license from the United States Government. 14 * It is the responsibility of any person or organization contemplating 15 * export to obtain such a license before exporting. 16 * 17 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 18 * distribute this software and its documentation for any purpose and 19 * without fee is hereby granted, provided that the above copyright 20 * notice appear in all copies and that both that copyright notice and 21 * this permission notice appear in supporting documentation, and that 22 * the name of M.I.T. not be used in advertising or publicity pertaining 23 * to distribution of the software without specific, written prior 24 * permission. Furthermore if you modify this software you must label 25 * your software as modified software and not distribute it in such a 26 * fashion that it might be confused with the original M.I.T. software. 27 * M.I.T. makes no representations about the suitability of 28 * this software for any purpose. It is provided "as is" without express 29 * or implied warranty. 30 * 31 */ 32 #include "autoconf.h" 33 #ifdef KRB5_DNS_LOOKUP 34 35 #include "dnsglue.h" 36 37 /* 38 * Only use res_ninit() if there's also a res_ndestroy(), to avoid 39 * memory leaks (Linux & Solaris) and outright corruption (AIX 4.x, 40 * 5.x). While we're at it, make sure res_nsearch() is there too. 41 * 42 * In any case, it is probable that platforms having broken 43 * res_ninit() will have thread safety hacks for res_init() and _res. 44 */ 45 #if HAVE_RES_NINIT && HAVE_RES_NDESTROY && HAVE_RES_NSEARCH 46 #define USE_RES_NINIT 1 47 #endif 48 49 /* 50 * Opaque handle 51 */ 52 struct krb5int_dns_state { 53 int nclass; 54 int ntype; 55 void *ansp; 56 int anslen; 57 int ansmax; 58 #if HAVE_NS_INITPARSE 59 int cur_ans; 60 ns_msg msg; 61 #else 62 unsigned char *ptr; 63 unsigned short nanswers; 64 #endif 65 }; 66 67 #if !HAVE_NS_INITPARSE 68 static int initparse(struct krb5int_dns_state *); 69 #endif 70 71 /* 72 * krb5int_dns_init() 73 * 74 * Initialize an opaque handle. Do name lookup and initial parsing of 75 * reply, skipping question section. Prepare to iterate over answer 76 * section. Returns -1 on error, 0 on success. 77 */ 78 int 79 krb5int_dns_init(struct krb5int_dns_state **dsp, 80 char *host, int nclass, int ntype) 81 { 82 #if USE_RES_NINIT 83 struct __res_state statbuf; 84 #endif 85 struct krb5int_dns_state *ds; 86 int len, ret; 87 size_t nextincr, maxincr; 88 unsigned char *p; 89 90 *dsp = ds = malloc(sizeof(*ds)); 91 if (ds == NULL) 92 return -1; 93 94 ret = -1; 95 ds->nclass = nclass; 96 ds->ntype = ntype; 97 ds->ansp = NULL; 98 ds->anslen = 0; 99 ds->ansmax = 0; 100 nextincr = 2048; 101 maxincr = INT_MAX; 102 103 #if HAVE_NS_INITPARSE 104 ds->cur_ans = 0; 105 #endif 106 107 #if USE_RES_NINIT 108 memset(&statbuf, 0, sizeof(statbuf)); 109 ret = res_ninit(&statbuf); 110 #else 111 ret = res_init(); 112 #endif 113 if (ret < 0) 114 return -1; 115 116 do { 117 p = (ds->ansp == NULL) 118 ? malloc(nextincr) : realloc(ds->ansp, nextincr); 119 120 if (p == NULL && ds->ansp != NULL) { 121 ret = -1; 122 goto errout; 123 } 124 ds->ansp = p; 125 ds->ansmax = nextincr; 126 127 #if USE_RES_NINIT 128 len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype, 129 ds->ansp, ds->ansmax); 130 #else 131 len = res_search(host, ds->nclass, ds->ntype, 132 ds->ansp, ds->ansmax); 133 #endif 134 if (len > maxincr) { 135 ret = -1; 136 goto errout; 137 } 138 while (nextincr < len) 139 nextincr *= 2; 140 if (len < 0 || nextincr > maxincr) { 141 ret = -1; 142 goto errout; 143 } 144 } while (len > ds->ansmax); 145 146 ds->anslen = len; 147 #if HAVE_NS_INITPARSE 148 ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg); 149 #else 150 ret = initparse(ds); 151 #endif 152 if (ret < 0) 153 goto errout; 154 155 ret = 0; 156 157 errout: 158 #if USE_RES_NINIT 159 res_ndestroy(&statbuf); 160 #endif 161 if (ret < 0) { 162 if (ds->ansp != NULL) { 163 free(ds->ansp); 164 ds->ansp = NULL; 165 } 166 } 167 168 return ret; 169 } 170 171 #if HAVE_NS_INITPARSE 172 /* 173 * krb5int_dns_nextans - get next matching answer record 174 * 175 * Sets pp to NULL if no more records. Returns -1 on error, 0 on 176 * success. 177 */ 178 int 179 krb5int_dns_nextans(struct krb5int_dns_state *ds, 180 const unsigned char **pp, int *lenp) 181 { 182 int len; 183 ns_rr rr; 184 185 *pp = NULL; 186 *lenp = 0; 187 while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) { 188 len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr); 189 if (len < 0) 190 return -1; 191 ds->cur_ans++; 192 if (ds->nclass == ns_rr_class(rr) 193 && ds->ntype == ns_rr_type(rr)) { 194 *pp = ns_rr_rdata(rr); 195 *lenp = ns_rr_rdlen(rr); 196 return 0; 197 } 198 } 199 return 0; 200 } 201 #endif 202 203 /* 204 * krb5int_dns_expand - wrapper for dn_expand() 205 */ 206 int krb5int_dns_expand(struct krb5int_dns_state *ds, 207 const unsigned char *p, 208 char *buf, int len) 209 { 210 211 #if HAVE_NS_NAME_UNCOMPRESS 212 return ns_name_uncompress(ds->ansp, 213 (unsigned char *)ds->ansp + ds->anslen, 214 p, buf, (size_t)len); 215 #else 216 return dn_expand(ds->ansp, 217 (unsigned char *)ds->ansp + ds->anslen, 218 p, buf, len); 219 #endif 220 } 221 222 /* 223 * Free stuff. 224 */ 225 void 226 krb5int_dns_fini(struct krb5int_dns_state *ds) 227 { 228 if (ds == NULL) 229 return; 230 if (ds->ansp != NULL) 231 free(ds->ansp); 232 free(ds); 233 } 234 235 /* 236 * Compat routines for BIND 4 237 */ 238 #if !HAVE_NS_INITPARSE 239 240 /* 241 * initparse 242 * 243 * Skip header and question section of reply. Set a pointer to the 244 * beginning of the answer section, and prepare to iterate over 245 * answer records. 246 */ 247 static int 248 initparse(struct krb5int_dns_state *ds) 249 { 250 HEADER *hdr; 251 unsigned char *p; 252 unsigned short nqueries, nanswers; 253 int len; 254 #if !HAVE_DN_SKIPNAME 255 char host[MAXDNAME]; 256 #endif 257 258 if (ds->anslen < sizeof(HEADER)) 259 return -1; 260 261 hdr = (HEADER *)ds->ansp; 262 p = ds->ansp; 263 nqueries = ntohs((unsigned short)hdr->qdcount); 264 nanswers = ntohs((unsigned short)hdr->ancount); 265 p += sizeof(HEADER); 266 267 /* 268 * Skip query records. 269 */ 270 while (nqueries--) { 271 #if HAVE_DN_SKIPNAME 272 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 273 #else 274 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 275 p, host, sizeof(host)); 276 #endif 277 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4)) 278 return -1; 279 p += len + 4; 280 } 281 ds->ptr = p; 282 ds->nanswers = nanswers; 283 return 0; 284 } 285 286 /* 287 * krb5int_dns_nextans() - get next answer record 288 * 289 * Sets pp to NULL if no more records. 290 */ 291 int 292 krb5int_dns_nextans(struct krb5int_dns_state *ds, 293 const unsigned char **pp, int *lenp) 294 { 295 int len; 296 unsigned char *p; 297 unsigned short ntype, nclass, rdlen; 298 #if !HAVE_DN_SKIPNAME 299 char host[MAXDNAME]; 300 #endif 301 302 *pp = NULL; 303 *lenp = 0; 304 p = ds->ptr; 305 306 while (ds->nanswers--) { 307 #if HAVE_DN_SKIPNAME 308 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 309 #else 310 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 311 p, host, sizeof(host)); 312 #endif 313 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len)) 314 return -1; 315 p += len; 316 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out); 317 /* Also skip 4 bytes of TTL */ 318 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out); 319 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out); 320 321 if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen)) 322 return -1; 323 /* Solaris Kerberos - resync */ 324 #if 0 325 if (rdlen > INT_MAX) 326 return -1; 327 #endif 328 if (nclass == ds->nclass && ntype == ds->ntype) { 329 *pp = p; 330 *lenp = rdlen; 331 ds->ptr = p + rdlen; 332 return 0; 333 } 334 p += rdlen; 335 } 336 return 0; 337 out: 338 return -1; 339 } 340 341 #endif 342 343 #endif /* KRB5_DNS_LOOKUP */ 344