1/* $OpenLDAP$ */ 2/* This work is part of OpenLDAP Software <http://www.openldap.org/>. 3 * 4 * Copyright 1998-2011 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 40int 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 121return_error: 122 ldap_dnfree( dn ); 123 LDAP_FREE( domain.bv_val ); 124 return -1; 125} 126 127int 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 */ 181int 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 LDAP_MUTEX_LOCK(&ldap_int_resolv_mutex); 206 207 rc = LDAP_UNAVAILABLE; 208#ifdef NS_HFIXEDSZ 209 /* Bind 8/9 interface */ 210 len = res_query(request, ns_c_in, ns_t_srv, reply, sizeof(reply)); 211# ifndef T_SRV 212# define T_SRV ns_t_srv 213# endif 214#else 215 /* Bind 4 interface */ 216# ifndef T_SRV 217# define T_SRV 33 218# endif 219 220 len = res_query(request, C_IN, T_SRV, reply, sizeof(reply)); 221#endif 222 if (len >= 0) { 223 unsigned char *p; 224 char host[DNSBUFSIZ]; 225 int status; 226 u_short port; 227 /* int priority, weight; */ 228 229 /* Parse out query */ 230 p = reply; 231 232#ifdef NS_HFIXEDSZ 233 /* Bind 8/9 interface */ 234 p += NS_HFIXEDSZ; 235#elif defined(HFIXEDSZ) 236 /* Bind 4 interface w/ HFIXEDSZ */ 237 p += HFIXEDSZ; 238#else 239 /* Bind 4 interface w/o HFIXEDSZ */ 240 p += sizeof(HEADER); 241#endif 242 243 status = dn_expand(reply, reply + len, p, host, sizeof(host)); 244 if (status < 0) { 245 goto out; 246 } 247 p += status; 248 p += 4; 249 250 while (p < reply + len) { 251 int type, class, ttl, size; 252 status = dn_expand(reply, reply + len, p, host, sizeof(host)); 253 if (status < 0) { 254 goto out; 255 } 256 p += status; 257 type = (p[0] << 8) | p[1]; 258 p += 2; 259 class = (p[0] << 8) | p[1]; 260 p += 2; 261 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 262 p += 4; 263 size = (p[0] << 8) | p[1]; 264 p += 2; 265 if (type == T_SRV) { 266 int buflen; 267 status = dn_expand(reply, reply + len, p + 6, host, sizeof(host)); 268 if (status < 0) { 269 goto out; 270 } 271 /* ignore priority and weight for now */ 272 /* priority = (p[0] << 8) | p[1]; */ 273 /* weight = (p[2] << 8) | p[3]; */ 274 port = (p[4] << 8) | p[5]; 275 276 if ( port == 0 || host[ 0 ] == '\0' ) { 277 goto add_size; 278 } 279 280 buflen = strlen(host) + STRLENOF(":65355 "); 281 hostlist = (char *) LDAP_REALLOC(hostlist, cur + buflen + 1); 282 if (hostlist == NULL) { 283 rc = LDAP_NO_MEMORY; 284 goto out; 285 } 286 if (cur > 0) { 287 /* not first time around */ 288 hostlist[cur++] = ' '; 289 } 290 cur += sprintf(&hostlist[cur], "%s:%hu", host, port); 291 } 292add_size:; 293 p += size; 294 } 295 } 296 if (hostlist == NULL) { 297 /* No LDAP servers found in DNS. */ 298 rc = LDAP_UNAVAILABLE; 299 goto out; 300 } 301 302 rc = LDAP_SUCCESS; 303 *list = hostlist; 304 305 out: 306 LDAP_MUTEX_UNLOCK(&ldap_int_resolv_mutex); 307 308 if (request != NULL) { 309 LDAP_FREE(request); 310 } 311 if (rc != LDAP_SUCCESS && hostlist != NULL) { 312 LDAP_FREE(hostlist); 313 } 314 return rc; 315#else 316 return LDAP_NOT_SUPPORTED; 317#endif /* HAVE_RES_QUERY */ 318} 319