1 /* $NetBSD: dnssrv.c,v 1.1.1.4 2014/05/28 09:58:41 tron Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2014 The OpenLDAP Foundation. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted only as authorized by the OpenLDAP 11 * Public License. 12 * 13 * A copy of this license is available in the file LICENSE in the 14 * top-level directory of the distribution or, alternatively, at 15 * <http://www.OpenLDAP.org/license.html>. 16 */ 17 18 /* 19 * locate LDAP servers using DNS SRV records. 20 * Location code based on MIT Kerberos KDC location code. 21 */ 22 #include "portable.h" 23 24 #include <stdio.h> 25 26 #include <ac/stdlib.h> 27 28 #include <ac/param.h> 29 #include <ac/socket.h> 30 #include <ac/string.h> 31 #include <ac/time.h> 32 33 #include "ldap-int.h" 34 35 #ifdef HAVE_ARPA_NAMESER_H 36 #include <arpa/nameser.h> 37 #endif 38 #ifdef HAVE_RESOLV_H 39 #include <resolv.h> 40 #endif 41 42 int ldap_dn2domain( 43 LDAP_CONST char *dn_in, 44 char **domainp) 45 { 46 int i, j; 47 char *ndomain; 48 LDAPDN dn = NULL; 49 LDAPRDN rdn = NULL; 50 LDAPAVA *ava = NULL; 51 struct berval domain = BER_BVNULL; 52 static const struct berval DC = BER_BVC("DC"); 53 static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25"); 54 55 assert( dn_in != NULL ); 56 assert( domainp != NULL ); 57 58 *domainp = NULL; 59 60 if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) { 61 return -2; 62 } 63 64 if( dn ) for( i=0; dn[i] != NULL; i++ ) { 65 rdn = dn[i]; 66 67 for( j=0; rdn[j] != NULL; j++ ) { 68 ava = rdn[j]; 69 70 if( rdn[j+1] == NULL && 71 (ava->la_flags & LDAP_AVA_STRING) && 72 ava->la_value.bv_len && 73 ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0 74 || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) ) 75 { 76 if( domain.bv_len == 0 ) { 77 ndomain = LDAP_REALLOC( domain.bv_val, 78 ava->la_value.bv_len + 1); 79 80 if( ndomain == NULL ) { 81 goto return_error; 82 } 83 84 domain.bv_val = ndomain; 85 86 AC_MEMCPY( domain.bv_val, ava->la_value.bv_val, 87 ava->la_value.bv_len ); 88 89 domain.bv_len = ava->la_value.bv_len; 90 domain.bv_val[domain.bv_len] = '\0'; 91 92 } else { 93 ndomain = LDAP_REALLOC( domain.bv_val, 94 ava->la_value.bv_len + sizeof(".") + domain.bv_len ); 95 96 if( ndomain == NULL ) { 97 goto return_error; 98 } 99 100 domain.bv_val = ndomain; 101 domain.bv_val[domain.bv_len++] = '.'; 102 AC_MEMCPY( &domain.bv_val[domain.bv_len], 103 ava->la_value.bv_val, ava->la_value.bv_len ); 104 domain.bv_len += ava->la_value.bv_len; 105 domain.bv_val[domain.bv_len] = '\0'; 106 } 107 } else { 108 domain.bv_len = 0; 109 } 110 } 111 } 112 113 114 if( domain.bv_len == 0 && domain.bv_val != NULL ) { 115 LDAP_FREE( domain.bv_val ); 116 domain.bv_val = NULL; 117 } 118 119 ldap_dnfree( dn ); 120 *domainp = domain.bv_val; 121 return 0; 122 123 return_error: 124 ldap_dnfree( dn ); 125 LDAP_FREE( domain.bv_val ); 126 return -1; 127 } 128 129 int ldap_domain2dn( 130 LDAP_CONST char *domain_in, 131 char **dnp) 132 { 133 char *domain, *s, *tok_r, *dn, *dntmp; 134 size_t loc; 135 136 assert( domain_in != NULL ); 137 assert( dnp != NULL ); 138 139 domain = LDAP_STRDUP(domain_in); 140 if (domain == NULL) { 141 return LDAP_NO_MEMORY; 142 } 143 dn = NULL; 144 loc = 0; 145 146 for (s = ldap_pvt_strtok(domain, ".", &tok_r); 147 s != NULL; 148 s = ldap_pvt_strtok(NULL, ".", &tok_r)) 149 { 150 size_t len = strlen(s); 151 152 dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len ); 153 if (dntmp == NULL) { 154 if (dn != NULL) 155 LDAP_FREE(dn); 156 LDAP_FREE(domain); 157 return LDAP_NO_MEMORY; 158 } 159 160 dn = dntmp; 161 162 if (loc > 0) { 163 /* not first time. */ 164 strcpy(dn + loc, ","); 165 loc++; 166 } 167 strcpy(dn + loc, "dc="); 168 loc += sizeof("dc=")-1; 169 170 strcpy(dn + loc, s); 171 loc += len; 172 } 173 174 LDAP_FREE(domain); 175 *dnp = dn; 176 return LDAP_SUCCESS; 177 } 178 179 /* 180 * Lookup and return LDAP servers for domain (using the DNS 181 * SRV record _ldap._tcp.domain). 182 */ 183 int ldap_domain2hostlist( 184 LDAP_CONST char *domain, 185 char **list ) 186 { 187 #ifdef HAVE_RES_QUERY 188 #define DNSBUFSIZ (64*1024) 189 char *request; 190 char *hostlist = NULL; 191 int rc, len, cur = 0; 192 unsigned char reply[DNSBUFSIZ]; 193 194 assert( domain != NULL ); 195 assert( list != NULL ); 196 197 if( *domain == '\0' ) { 198 return LDAP_PARAM_ERROR; 199 } 200 201 request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp.")); 202 if (request == NULL) { 203 return LDAP_NO_MEMORY; 204 } 205 sprintf(request, "_ldap._tcp.%s", domain); 206 207 LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); 208 209 rc = LDAP_UNAVAILABLE; 210 #ifdef NS_HFIXEDSZ 211 /* Bind 8/9 interface */ 212 len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply)); 213 # ifndef T_SRV 214 # define T_SRV ns_t_srv 215 # endif 216 #else 217 /* Bind 4 interface */ 218 # ifndef T_SRV 219 # define T_SRV 33 220 # endif 221 222 len = res_query(request, C_IN, T_SRV, reply, sizeof(reply)); 223 #endif 224 if (len >= 0) { 225 unsigned char *p; 226 char host[DNSBUFSIZ]; 227 int status; 228 u_short port; 229 /* int priority, weight; */ 230 231 /* Parse out query */ 232 p = reply; 233 234 #ifdef NS_HFIXEDSZ 235 /* Bind 8/9 interface */ 236 p += NS_HFIXEDSZ; 237 #elif defined(HFIXEDSZ) 238 /* Bind 4 interface w/ HFIXEDSZ */ 239 p += HFIXEDSZ; 240 #else 241 /* Bind 4 interface w/o HFIXEDSZ */ 242 p += sizeof(HEADER); 243 #endif 244 245 status = dn_expand(reply, reply + len, p, host, sizeof(host)); 246 if (status < 0) { 247 goto out; 248 } 249 p += status; 250 p += 4; 251 252 while (p < reply + len) { 253 int type, class, ttl, size; 254 status = dn_expand(reply, reply + len, p, host, sizeof(host)); 255 if (status < 0) { 256 goto out; 257 } 258 p += status; 259 type = (p[0] << 8) | p[1]; 260 p += 2; 261 class = (p[0] << 8) | p[1]; 262 p += 2; 263 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 264 p += 4; 265 size = (p[0] << 8) | p[1]; 266 p += 2; 267 if (type == T_SRV) { 268 int buflen; 269 status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); 270 if (status < 0) { 271 goto out; 272 } 273 /* ignore priority and weight for now */ 274 /* priority = (p[0] << 8) | p[1]; */ 275 /* weight = (p[2] << 8) | p[3]; */ 276 port = (p[4] << 8) | p[5]; 277 278 if ( port == 0 || host[ 0 ] == '\0' ) { 279 goto add_size; 280 } 281 282 buflen = strlen(host) + STRLENOF(":65355 "); 283 hostlist = (char *) LDAP_REALLOC(hostlist, cur + buflen + 1); 284 if (hostlist == NULL) { 285 rc = LDAP_NO_MEMORY; 286 goto out; 287 } 288 if (cur > 0) { 289 /* not first time around */ 290 hostlist[cur++] = ' '; 291 } 292 cur += sprintf(&hostlist[cur], "%s:%hu", host, port); 293 } 294 add_size:; 295 p += size; 296 } 297 } 298 if (hostlist == NULL) { 299 /* No LDAP servers found in DNS. */ 300 rc = LDAP_UNAVAILABLE; 301 goto out; 302 } 303 304 rc = LDAP_SUCCESS; 305 *list = hostlist; 306 307 out: 308 LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); 309 310 if (request != NULL) { 311 LDAP_FREE(request); 312 } 313 if (rc != LDAP_SUCCESS && hostlist != NULL) { 314 LDAP_FREE(hostlist); 315 } 316 return rc; 317 #else 318 return LDAP_NOT_SUPPORTED; 319 #endif /* HAVE_RES_QUERY */ 320 } 321