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