1ebd1706eSgtb /* 2*ae5b046dSsemery * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3ebd1706eSgtb * Use is subject to license terms. 4ebd1706eSgtb */ 5ebd1706eSgtb 6505d05c7Sgtb #pragma ident "%Z%%M% %I% %E% SMI" 7505d05c7Sgtb /* 8505d05c7Sgtb * lib/krb5/os/dnsglue.c 9505d05c7Sgtb * 10505d05c7Sgtb * Copyright 2004 by the Massachusetts Institute of Technology. 11505d05c7Sgtb * All Rights Reserved. 12505d05c7Sgtb * 13505d05c7Sgtb * Export of this software from the United States of America may 14505d05c7Sgtb * require a specific license from the United States Government. 15505d05c7Sgtb * It is the responsibility of any person or organization contemplating 16505d05c7Sgtb * export to obtain such a license before exporting. 17505d05c7Sgtb * 18505d05c7Sgtb * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 19505d05c7Sgtb * distribute this software and its documentation for any purpose and 20505d05c7Sgtb * without fee is hereby granted, provided that the above copyright 21505d05c7Sgtb * notice appear in all copies and that both that copyright notice and 22505d05c7Sgtb * this permission notice appear in supporting documentation, and that 23505d05c7Sgtb * the name of M.I.T. not be used in advertising or publicity pertaining 24505d05c7Sgtb * to distribution of the software without specific, written prior 25505d05c7Sgtb * permission. Furthermore if you modify this software you must label 26505d05c7Sgtb * your software as modified software and not distribute it in such a 27505d05c7Sgtb * fashion that it might be confused with the original M.I.T. software. 28505d05c7Sgtb * M.I.T. makes no representations about the suitability of 29505d05c7Sgtb * this software for any purpose. It is provided "as is" without express 30505d05c7Sgtb * or implied warranty. 31505d05c7Sgtb * 32505d05c7Sgtb */ 33505d05c7Sgtb #ifdef KRB5_DNS_LOOKUP 34505d05c7Sgtb 35505d05c7Sgtb #include "dnsglue.h" 36505d05c7Sgtb 37505d05c7Sgtb /* 38505d05c7Sgtb * Opaque handle 39505d05c7Sgtb */ 40505d05c7Sgtb struct krb5int_dns_state { 41505d05c7Sgtb int nclass; 42505d05c7Sgtb int ntype; 43505d05c7Sgtb void *ansp; 44505d05c7Sgtb int anslen; 45505d05c7Sgtb int ansmax; 46505d05c7Sgtb #if HAVE_NS_INITPARSE 47505d05c7Sgtb int cur_ans; 48505d05c7Sgtb ns_msg msg; 49505d05c7Sgtb #else 50505d05c7Sgtb unsigned char *ptr; 51505d05c7Sgtb unsigned short nanswers; 52505d05c7Sgtb #endif 53505d05c7Sgtb }; 54505d05c7Sgtb 55505d05c7Sgtb #if !HAVE_NS_INITPARSE 56505d05c7Sgtb static int initparse(struct krb5int_dns_state *); 57505d05c7Sgtb #endif 58505d05c7Sgtb 59505d05c7Sgtb /* 60505d05c7Sgtb * krb5int_dns_init() 61505d05c7Sgtb * 62505d05c7Sgtb * Initialize an opaue handl. Do name lookup and initial parsing of 63505d05c7Sgtb * reply, skipping question section. Prepare to iterate over answer 64505d05c7Sgtb * section. Returns -1 on error, 0 on success. 65505d05c7Sgtb */ 66505d05c7Sgtb int 67505d05c7Sgtb krb5int_dns_init(struct krb5int_dns_state **dsp, 68505d05c7Sgtb char *host, int nclass, int ntype) 69505d05c7Sgtb { 70505d05c7Sgtb #if HAVE_RES_NSEARCH 71505d05c7Sgtb struct __res_state statbuf; 72505d05c7Sgtb #endif 73505d05c7Sgtb struct krb5int_dns_state *ds; 74505d05c7Sgtb int len, ret; 75505d05c7Sgtb size_t nextincr, maxincr; 76505d05c7Sgtb unsigned char *p; 77505d05c7Sgtb 78505d05c7Sgtb *dsp = ds = malloc(sizeof(*ds)); 79505d05c7Sgtb if (ds == NULL) 80505d05c7Sgtb return -1; 81505d05c7Sgtb 82505d05c7Sgtb ret = -1; 83505d05c7Sgtb ds->nclass = nclass; 84505d05c7Sgtb ds->ntype = ntype; 85505d05c7Sgtb ds->ansp = NULL; 86505d05c7Sgtb ds->anslen = 0; 87505d05c7Sgtb ds->ansmax = 0; 88505d05c7Sgtb nextincr = 2048; 89505d05c7Sgtb maxincr = INT_MAX; 90505d05c7Sgtb 91505d05c7Sgtb #if HAVE_NS_INITPARSE 92505d05c7Sgtb ds->cur_ans = 0; 93505d05c7Sgtb #endif 94505d05c7Sgtb 95505d05c7Sgtb #if HAVE_RES_NSEARCH 96*ae5b046dSsemery memset(&statbuf, 0, sizeof(statbuf)); 97505d05c7Sgtb ret = res_ninit(&statbuf); 98505d05c7Sgtb if (ret < 0) 99505d05c7Sgtb return -1; 100505d05c7Sgtb #endif 101505d05c7Sgtb 102505d05c7Sgtb do { 103505d05c7Sgtb p = (ds->ansp == NULL) 104505d05c7Sgtb ? malloc(nextincr) : realloc(ds->ansp, nextincr); 105505d05c7Sgtb 106505d05c7Sgtb if (p == NULL && ds->ansp != NULL) { 107505d05c7Sgtb ret = -1; 108505d05c7Sgtb goto errout; 109505d05c7Sgtb } 110505d05c7Sgtb ds->ansp = p; 111505d05c7Sgtb ds->ansmax = nextincr; 112505d05c7Sgtb 113505d05c7Sgtb #if HAVE_RES_NSEARCH 114505d05c7Sgtb len = res_nsearch(&statbuf, host, ds->nclass, ds->ntype, 115505d05c7Sgtb ds->ansp, ds->ansmax); 116505d05c7Sgtb #else 117505d05c7Sgtb len = res_search(host, ds->nclass, ds->ntype, 118505d05c7Sgtb ds->ansp, ds->ansmax); 119505d05c7Sgtb #endif 120505d05c7Sgtb if (len > maxincr) { 121505d05c7Sgtb ret = -1; 122505d05c7Sgtb goto errout; 123505d05c7Sgtb } 124505d05c7Sgtb while (nextincr < len) 125505d05c7Sgtb nextincr *= 2; 126505d05c7Sgtb if (len < 0 || nextincr > maxincr) { 127505d05c7Sgtb ret = -1; 128505d05c7Sgtb goto errout; 129505d05c7Sgtb } 130505d05c7Sgtb } while (len > ds->ansmax); 131505d05c7Sgtb 132505d05c7Sgtb ds->anslen = len; 133505d05c7Sgtb #if HAVE_NS_INITPARSE 134505d05c7Sgtb ret = ns_initparse(ds->ansp, ds->anslen, &ds->msg); 135505d05c7Sgtb #else 136505d05c7Sgtb ret = initparse(ds); 137505d05c7Sgtb #endif 138505d05c7Sgtb if (ret < 0) 139505d05c7Sgtb goto errout; 140505d05c7Sgtb 141505d05c7Sgtb ret = 0; 142505d05c7Sgtb 143505d05c7Sgtb errout: 144505d05c7Sgtb #if HAVE_RES_NSEARCH 145505d05c7Sgtb #if HAVE_RES_NDESTROY 146505d05c7Sgtb res_ndestroy(&statbuf); 147505d05c7Sgtb #else 148505d05c7Sgtb res_nclose(&statbuf); 149505d05c7Sgtb #endif 150505d05c7Sgtb #endif 151505d05c7Sgtb if (ret < 0) { 152505d05c7Sgtb if (ds->ansp != NULL) { 153505d05c7Sgtb free(ds->ansp); 154505d05c7Sgtb ds->ansp = NULL; 155505d05c7Sgtb } 156505d05c7Sgtb } 157505d05c7Sgtb 158505d05c7Sgtb return ret; 159505d05c7Sgtb } 160505d05c7Sgtb 161505d05c7Sgtb #if HAVE_NS_INITPARSE 162505d05c7Sgtb /* 163505d05c7Sgtb * krb5int_dns_nextans - get next matching answer record 164505d05c7Sgtb * 165505d05c7Sgtb * Sets pp to NULL if no more records. Returns -1 on error, 0 on 166505d05c7Sgtb * success. 167505d05c7Sgtb */ 168505d05c7Sgtb int 169505d05c7Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds, 170505d05c7Sgtb const unsigned char **pp, int *lenp) 171505d05c7Sgtb { 172505d05c7Sgtb int len; 173505d05c7Sgtb ns_rr rr; 174505d05c7Sgtb 175505d05c7Sgtb *pp = NULL; 176505d05c7Sgtb *lenp = 0; 177505d05c7Sgtb while (ds->cur_ans < ns_msg_count(ds->msg, ns_s_an)) { 178505d05c7Sgtb len = ns_parserr(&ds->msg, ns_s_an, ds->cur_ans, &rr); 179505d05c7Sgtb if (len < 0) 180505d05c7Sgtb return -1; 181505d05c7Sgtb ds->cur_ans++; 182505d05c7Sgtb if (ds->nclass == ns_rr_class(rr) 183505d05c7Sgtb && ds->ntype == ns_rr_type(rr)) { 184505d05c7Sgtb *pp = ns_rr_rdata(rr); 185505d05c7Sgtb *lenp = ns_rr_rdlen(rr); 186505d05c7Sgtb return 0; 187505d05c7Sgtb } 188505d05c7Sgtb } 189505d05c7Sgtb return 0; 190505d05c7Sgtb } 191505d05c7Sgtb #endif 192505d05c7Sgtb 193505d05c7Sgtb /* 194505d05c7Sgtb * krb5int_dns_expand - wrapper for dn_expand() 195505d05c7Sgtb */ 196505d05c7Sgtb int krb5int_dns_expand(struct krb5int_dns_state *ds, 197505d05c7Sgtb const unsigned char *p, 198505d05c7Sgtb char *buf, int len) 199505d05c7Sgtb { 200505d05c7Sgtb 201505d05c7Sgtb #if HAVE_NS_NAME_UNCOMPRESS 202505d05c7Sgtb return ns_name_uncompress(ds->ansp, 203505d05c7Sgtb (unsigned char *)ds->ansp + ds->anslen, 204505d05c7Sgtb p, buf, (size_t)len); 205505d05c7Sgtb #else 206505d05c7Sgtb return dn_expand(ds->ansp, 207505d05c7Sgtb (unsigned char *)ds->ansp + ds->anslen, 208505d05c7Sgtb p, buf, len); 209505d05c7Sgtb #endif 210505d05c7Sgtb } 211505d05c7Sgtb 212505d05c7Sgtb /* 213505d05c7Sgtb * Free stuff. 214505d05c7Sgtb */ 215505d05c7Sgtb void 216505d05c7Sgtb krb5int_dns_fini(struct krb5int_dns_state *ds) 217505d05c7Sgtb { 218505d05c7Sgtb if (ds == NULL) 219505d05c7Sgtb return; 220505d05c7Sgtb if (ds->ansp != NULL) 221505d05c7Sgtb free(ds->ansp); 222505d05c7Sgtb free(ds); 223505d05c7Sgtb } 224505d05c7Sgtb 225505d05c7Sgtb /* 226505d05c7Sgtb * Compat routines for BIND 4 227505d05c7Sgtb */ 228505d05c7Sgtb #if !HAVE_NS_INITPARSE 229505d05c7Sgtb 230505d05c7Sgtb /* 231505d05c7Sgtb * initparse 232505d05c7Sgtb * 233505d05c7Sgtb * Skip header and question section of reply. Set a pointer to the 234505d05c7Sgtb * beginning of the answer section, and prepare to iterate over 235505d05c7Sgtb * answer records. 236505d05c7Sgtb */ 237505d05c7Sgtb static int 238505d05c7Sgtb initparse(struct krb5int_dns_state *ds) 239505d05c7Sgtb { 240505d05c7Sgtb HEADER *hdr; 241505d05c7Sgtb unsigned char *p; 242505d05c7Sgtb unsigned short nqueries, nanswers; 243505d05c7Sgtb int len; 244505d05c7Sgtb #if !HAVE_DN_SKIPNAME 245505d05c7Sgtb char host[MAXDNAME]; 246505d05c7Sgtb #endif 247505d05c7Sgtb 248505d05c7Sgtb if (ds->anslen < sizeof(HEADER)) 249505d05c7Sgtb return -1; 250505d05c7Sgtb 251505d05c7Sgtb hdr = (HEADER *)ds->ansp; 252505d05c7Sgtb p = ds->ansp; 253505d05c7Sgtb nqueries = ntohs((unsigned short)hdr->qdcount); 254505d05c7Sgtb nanswers = ntohs((unsigned short)hdr->ancount); 255505d05c7Sgtb p += sizeof(HEADER); 256505d05c7Sgtb 257505d05c7Sgtb /* 258505d05c7Sgtb * Skip query records. 259505d05c7Sgtb */ 260505d05c7Sgtb while (nqueries--) { 261505d05c7Sgtb #if HAVE_DN_SKIPNAME 262505d05c7Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 263505d05c7Sgtb #else 264505d05c7Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 265505d05c7Sgtb p, host, sizeof(host)); 266505d05c7Sgtb #endif 267505d05c7Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len + 4)) 268505d05c7Sgtb return -1; 269505d05c7Sgtb p += len + 4; 270505d05c7Sgtb } 271505d05c7Sgtb ds->ptr = p; 272505d05c7Sgtb ds->nanswers = nanswers; 273505d05c7Sgtb return 0; 274505d05c7Sgtb } 275505d05c7Sgtb 276505d05c7Sgtb /* 277505d05c7Sgtb * krb5int_dns_nextans() - get next answer record 278505d05c7Sgtb * 279505d05c7Sgtb * Sets pp to NULL if no more records. 280505d05c7Sgtb */ 281505d05c7Sgtb int 282505d05c7Sgtb krb5int_dns_nextans(struct krb5int_dns_state *ds, 283505d05c7Sgtb const unsigned char **pp, int *lenp) 284505d05c7Sgtb { 285505d05c7Sgtb int len; 286505d05c7Sgtb unsigned char *p; 287505d05c7Sgtb unsigned short ntype, nclass, rdlen; 288505d05c7Sgtb #if !HAVE_DN_SKIPNAME 289505d05c7Sgtb char host[MAXDNAME]; 290505d05c7Sgtb #endif 291505d05c7Sgtb 292505d05c7Sgtb *pp = NULL; 293505d05c7Sgtb *lenp = 0; 294505d05c7Sgtb p = ds->ptr; 295505d05c7Sgtb 296505d05c7Sgtb while (ds->nanswers--) { 297505d05c7Sgtb #if HAVE_DN_SKIPNAME 298505d05c7Sgtb len = dn_skipname(p, (unsigned char *)ds->ansp + ds->anslen); 299505d05c7Sgtb #else 300505d05c7Sgtb len = dn_expand(ds->ansp, (unsigned char *)ds->ansp + ds->anslen, 301505d05c7Sgtb p, host, sizeof(host)); 302505d05c7Sgtb #endif 303505d05c7Sgtb if (len < 0 || !INCR_OK(ds->ansp, ds->anslen, p, len)) 304505d05c7Sgtb return -1; 305505d05c7Sgtb p += len; 306505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, ntype, out); 307505d05c7Sgtb /* Also skip 4 bytes of TTL */ 308505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 6, nclass, out); 309505d05c7Sgtb SAFE_GETUINT16(ds->ansp, ds->anslen, p, 2, rdlen, out); 310505d05c7Sgtb 311505d05c7Sgtb if (!INCR_OK(ds->ansp, ds->anslen, p, rdlen)) 312505d05c7Sgtb return -1; 313505d05c7Sgtb if (nclass == ds->nclass && ntype == ds->ntype) { 314505d05c7Sgtb *pp = p; 315505d05c7Sgtb *lenp = rdlen; 316505d05c7Sgtb ds->ptr = p + rdlen; 317505d05c7Sgtb return 0; 318505d05c7Sgtb } 319505d05c7Sgtb p += rdlen; 320505d05c7Sgtb } 321505d05c7Sgtb return 0; 322505d05c7Sgtb out: 323505d05c7Sgtb return -1; 324505d05c7Sgtb } 325505d05c7Sgtb 326505d05c7Sgtb #endif 327505d05c7Sgtb 328505d05c7Sgtb #endif /* KRB5_DNS_LOOKUP */ 329