1 /* $OpenLDAP: pkg/ldap/libraries/libldap/dnssrv.c,v 1.39.2.4 2008/02/11 23:26:41 kurt Exp $ */ 2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2008 The OpenLDAP Foundation. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted only as authorized by the OpenLDAP 9 * Public License. 10 * 11 * A copy of this license is available in the file LICENSE in the 12 * top-level directory of the distribution or, alternatively, at 13 * <http://www.OpenLDAP.org/license.html>. 14 */ 15 16 /* 17 * locate LDAP servers using DNS SRV records. 18 * Location code based on MIT Kerberos KDC location code. 19 */ 20 #include "portable.h" 21 22 #include <stdio.h> 23 24 #include <ac/stdlib.h> 25 26 #include <ac/param.h> 27 #include <ac/socket.h> 28 #include <ac/string.h> 29 #include <ac/time.h> 30 31 #include "ldap-int.h" 32 33 #ifdef HAVE_ARPA_NAMESER_H 34 #include <arpa/nameser.h> 35 #endif 36 #ifdef HAVE_RESOLV_H 37 #include <resolv.h> 38 #endif 39 40 int ldap_dn2domain( 41 LDAP_CONST char *dn_in, 42 char **domainp) 43 { 44 int i, j; 45 char *ndomain; 46 LDAPDN dn = NULL; 47 LDAPRDN rdn = NULL; 48 LDAPAVA *ava = NULL; 49 struct berval domain = BER_BVNULL; 50 static const struct berval DC = BER_BVC("DC"); 51 static const struct berval DCOID = BER_BVC("0.9.2342.19200300.100.1.25"); 52 53 assert( dn_in != NULL ); 54 assert( domainp != NULL ); 55 56 *domainp = NULL; 57 58 if ( ldap_str2dn( dn_in, &dn, LDAP_DN_FORMAT_LDAP ) != LDAP_SUCCESS ) { 59 return -2; 60 } 61 62 if( dn ) for( i=0; dn[i] != NULL; i++ ) { 63 rdn = dn[i]; 64 65 for( j=0; rdn[j] != NULL; j++ ) { 66 ava = rdn[j]; 67 68 if( rdn[j+1] == NULL && 69 (ava->la_flags & LDAP_AVA_STRING) && 70 ava->la_value.bv_len && 71 ( ber_bvstrcasecmp( &ava->la_attr, &DC ) == 0 72 || ber_bvcmp( &ava->la_attr, &DCOID ) == 0 ) ) 73 { 74 if( domain.bv_len == 0 ) { 75 ndomain = LDAP_REALLOC( domain.bv_val, 76 ava->la_value.bv_len + 1); 77 78 if( ndomain == NULL ) { 79 goto return_error; 80 } 81 82 domain.bv_val = ndomain; 83 84 AC_MEMCPY( domain.bv_val, ava->la_value.bv_val, 85 ava->la_value.bv_len ); 86 87 domain.bv_len = ava->la_value.bv_len; 88 domain.bv_val[domain.bv_len] = '\0'; 89 90 } else { 91 ndomain = LDAP_REALLOC( domain.bv_val, 92 ava->la_value.bv_len + sizeof(".") + domain.bv_len ); 93 94 if( ndomain == NULL ) { 95 goto return_error; 96 } 97 98 domain.bv_val = ndomain; 99 domain.bv_val[domain.bv_len++] = '.'; 100 AC_MEMCPY( &domain.bv_val[domain.bv_len], 101 ava->la_value.bv_val, ava->la_value.bv_len ); 102 domain.bv_len += ava->la_value.bv_len; 103 domain.bv_val[domain.bv_len] = '\0'; 104 } 105 } else { 106 domain.bv_len = 0; 107 } 108 } 109 } 110 111 112 if( domain.bv_len == 0 && domain.bv_val != NULL ) { 113 LDAP_FREE( domain.bv_val ); 114 domain.bv_val = NULL; 115 } 116 117 ldap_dnfree( dn ); 118 *domainp = domain.bv_val; 119 return 0; 120 121 return_error: 122 ldap_dnfree( dn ); 123 LDAP_FREE( domain.bv_val ); 124 return -1; 125 } 126 127 int ldap_domain2dn( 128 LDAP_CONST char *domain_in, 129 char **dnp) 130 { 131 char *domain, *s, *tok_r, *dn, *dntmp; 132 size_t loc; 133 134 assert( domain_in != NULL ); 135 assert( dnp != NULL ); 136 137 domain = LDAP_STRDUP(domain_in); 138 if (domain == NULL) { 139 return LDAP_NO_MEMORY; 140 } 141 dn = NULL; 142 loc = 0; 143 144 for (s = ldap_pvt_strtok(domain, ".", &tok_r); 145 s != NULL; 146 s = ldap_pvt_strtok(NULL, ".", &tok_r)) 147 { 148 size_t len = strlen(s); 149 150 dntmp = (char *) LDAP_REALLOC(dn, loc + sizeof(",dc=") + len ); 151 if (dntmp == NULL) { 152 if (dn != NULL) 153 LDAP_FREE(dn); 154 LDAP_FREE(domain); 155 return LDAP_NO_MEMORY; 156 } 157 158 dn = dntmp; 159 160 if (loc > 0) { 161 /* not first time. */ 162 strcpy(dn + loc, ","); 163 loc++; 164 } 165 strcpy(dn + loc, "dc="); 166 loc += sizeof("dc=")-1; 167 168 strcpy(dn + loc, s); 169 loc += len; 170 } 171 172 LDAP_FREE(domain); 173 *dnp = dn; 174 return LDAP_SUCCESS; 175 } 176 177 /* 178 * Lookup and return LDAP servers for domain (using the DNS 179 * SRV record _ldap._tcp.domain). 180 */ 181 int ldap_domain2hostlist( 182 LDAP_CONST char *domain, 183 char **list ) 184 { 185 #ifdef HAVE_RES_QUERY 186 #define DNSBUFSIZ (64*1024) 187 char *request; 188 char *hostlist = NULL; 189 int rc, len, cur = 0; 190 unsigned char reply[DNSBUFSIZ]; 191 192 assert( domain != NULL ); 193 assert( list != NULL ); 194 195 if( *domain == '\0' ) { 196 return LDAP_PARAM_ERROR; 197 } 198 199 request = LDAP_MALLOC(strlen(domain) + sizeof("_ldap._tcp.")); 200 if (request == NULL) { 201 return LDAP_NO_MEMORY; 202 } 203 sprintf(request, "_ldap._tcp.%s", domain); 204 205 #ifdef LDAP_R_COMPILE 206 ldap_pvt_thread_mutex_lock(&ldap_int_resolv_mutex); 207 #endif 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:%hd", 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 #ifdef LDAP_R_COMPILE 309 ldap_pvt_thread_mutex_unlock(&ldap_int_resolv_mutex); 310 #endif 311 312 if (request != NULL) { 313 LDAP_FREE(request); 314 } 315 if (rc != LDAP_SUCCESS && hostlist != NULL) { 316 LDAP_FREE(hostlist); 317 } 318 return rc; 319 #else 320 return LDAP_NOT_SUPPORTED; 321 #endif /* HAVE_RES_QUERY */ 322 } 323