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