1 /* 2 * Copyright 2008 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 memset(&statbuf, 0, sizeof(statbuf)); 97 ret = res_ninit(&statbuf); 98 if (ret < 0) 99 return -1; 100 #endif 101 102 do { 103 p = (ds->ansp == NULL) 104 ? malloc(nextincr) : realloc(ds->ansp, nextincr); 105 106 if (p == NULL && ds->ansp != NULL) { 107 ret = -1; 108 goto errout; 109 } 110 ds->ansp = p; 111 ds->ansmax = nextincr; 112 113 #if HAVE_RES_NSEARCH 114 len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype, 115 ds->ansp, ds->ansmax); 116 #else 117 len = res_search(host, ds->nclass, ds->ntype, 118 ds->ansp, ds->ansmax); 119 #endif 120 if (len > maxincr) { 121 ret = -1; 122 goto errout; 123 } 124 while (nextincr < len) 125 nextincr *= 2; 126 if (len < 0 || nextincr > maxincr) { 127 ret = -1; 128 goto errout; 129 } 130 } while (len > ds->ansmax); 131 132 ds->anslen = len; 133 #if HAVE_NS_INITPARSE 134 ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg); 135 #else 136 ret = initparse(ds); 137 #endif 138 if (ret < 0) 139 goto errout; 140 141 ret = 0; 142 143 errout: 144 #if HAVE_RES_NSEARCH 145 #if HAVE_RES_NDESTROY 146 res_ndestroy(&statbuf); 147 #else 148 res_nclose(&statbuf); 149 #endif 150 #endif 151 if (ret < 0) { 152 if (ds->ansp != NULL) { 153 free(ds->ansp); 154 ds->ansp = NULL; 155 } 156 } 157 158 return ret; 159 } 160 161 #if HAVE_NS_INITPARSE 162 /* 163 * krb5int_dns_nextans - get next matching answer record 164 * 165 * Sets pp to NULL if no more records. Returns -1 on error, 0 on 166 * success. 167 */ 168 int 169 krb5int_dns_nextans(struct krb5int_dns_state *ds, 170 const unsigned char **pp, int *lenp) 171 { 172 int len; 173 ns_rr rr; 174 175 *pp = NULL; 176 *lenp = 0; 177 while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) { 178 len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr); 179 if (len < 0) 180 return -1; 181 ds->cur_ans++; 182 if (ds->nclass == ns_rr_class(rr) 183 && ds->ntype == ns_rr_type(rr)) { 184 *pp = ns_rr_rdata(rr); 185 *lenp = ns_rr_rdlen(rr); 186 return 0; 187 } 188 } 189 return 0; 190 } 191 #endif 192 193 /* 194 * krb5int_dns_expand - wrapper for dn_expand() 195 */ 196 int krb5int_dns_expand(struct krb5int_dns_state *ds, 197 const unsigned char *p, 198 char *buf, int len) 199 { 200 201 #if HAVE_NS_NAME_UNCOMPRESS 202 return ns_name_uncompress(ds->ansp, 203 (unsigned char *)ds->ansp + ds->anslen, 204 p, buf, (size_t)len); 205 #else 206 return dn_expand(ds->ansp, 207 (unsigned char *)ds->ansp + ds->anslen, 208 p, buf, len); 209 #endif 210 } 211 212 /* 213 * Free stuff. 214 */ 215 void 216 krb5int_dns_fini(struct krb5int_dns_state *ds) 217 { 218 if (ds == NULL) 219 return; 220 if (ds->ansp != NULL) 221 free(ds->ansp); 222 free(ds); 223 } 224 225 /* 226 * Compat routines for BIND 4 227 */ 228 #if !HAVE_NS_INITPARSE 229 230 /* 231 * initparse 232 * 233 * Skip header and question section of reply. Set a pointer to the 234 * beginning of the answer section, and prepare to iterate over 235 * answer records. 236 */ 237 static int 238 initparse(struct krb5int_dns_state *ds) 239 { 240 HEADER *hdr; 241 unsigned char *p; 242 unsigned short nqueries, nanswers; 243 int len; 244 #if !HAVE_DN_SKIPNAME 245 char host[MAXDNAME]; 246 #endif 247 248 if (ds->anslen < sizeof(HEADER)) 249 return -1; 250 251 hdr = (HEADER *)ds->ansp; 252 p = ds->ansp; 253 nqueries = ntohs((unsigned short)hdr->qdcount); 254 nanswers = ntohs((unsigned short)hdr->ancount); 255 p += sizeof(HEADER); 256 257 /* 258 * Skip query records. 259 */ 260 while (nqueries--) { 261 #if HAVE_DN_SKIPNAME 262 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 263 #else 264 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 265 p, host, sizeof(host)); 266 #endif 267 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4)) 268 return -1; 269 p += len + 4; 270 } 271 ds->ptr = p; 272 ds->nanswers = nanswers; 273 return 0; 274 } 275 276 /* 277 * krb5int_dns_nextans() - get next answer record 278 * 279 * Sets pp to NULL if no more records. 280 */ 281 int 282 krb5int_dns_nextans(struct krb5int_dns_state *ds, 283 const unsigned char **pp, int *lenp) 284 { 285 int len; 286 unsigned char *p; 287 unsigned short ntype, nclass, rdlen; 288 #if !HAVE_DN_SKIPNAME 289 char host[MAXDNAME]; 290 #endif 291 292 *pp = NULL; 293 *lenp = 0; 294 p = ds->ptr; 295 296 while (ds->nanswers--) { 297 #if HAVE_DN_SKIPNAME 298 len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 299 #else 300 len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 301 p, host, sizeof(host)); 302 #endif 303 if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len)) 304 return -1; 305 p += len; 306 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out); 307 /* Also skip 4 bytes of TTL */ 308 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out); 309 SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out); 310 311 if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen)) 312 return -1; 313 if (nclass == ds->nclass && ntype == ds->ntype) { 314 *pp = p; 315 *lenp = rdlen; 316 ds->ptr = p + rdlen; 317 return 0; 318 } 319 p += rdlen; 320 } 321 return 0; 322 out: 323 return -1; 324 } 325 326 #endif 327 328 #endif /* KRB5_DNS_LOOKUP */ 329