1 /*
2 * Portions Copyright 1998 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 /*
9 * Copyright (c) 1990, 1994 Regents of the University of Michigan.
10 * All rights reserved.
11 *
12 * cldap.c - synchronous, retrying interface to the cldap protocol
13 */
14
15
16 #ifdef CLDAP
17
18 #ifndef lint
19 static char copyright[] = "@(#) Copyright (c) 1990, 1994 Regents of the University of Michigan.\nAll rights reserved.\n";
20 #endif
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <errno.h>
25 #ifdef MACOS
26 #include <stdlib.h>
27 #include "macos.h"
28 #else /* MACOS */
29 #ifdef DOS
30 #include "msdos.h"
31 #else /* DOS */
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <netinet/in.h>
36 #include <netdb.h>
37 #endif /* DOS */
38 #endif /* MACOS */
39 #ifdef SUN
40 #include <nss_dbdefs.h>
41 #endif
42
43 #include "lber.h"
44 #include "ldap.h"
45 #include "ldap-private.h"
46 #include "ldap-int.h"
47
48 #define DEF_CLDAP_TIMEOUT 3
49 #define DEF_CLDAP_TRIES 4
50
51 #ifndef INADDR_LOOPBACK
52 #define INADDR_LOOPBACK ((in_addr_t) 0x7f000001)
53 #endif
54
55
56 struct cldap_retinfo {
57 int cri_maxtries;
58 int cri_try;
59 int cri_useaddr;
60 time_t cri_timeout;
61 };
62
63 #ifdef NEEDPROTOS
64 static int add_addr( LDAP *ld, struct sockaddr *sap );
65 static int cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
66 struct cldap_retinfo *crip, char *base );
67 static int cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber,
68 LDAPMessage **res, char *base );
69 #else /* NEEDPROTOS */
70 static int add_addr();
71 static int cldap_result();
72 static int cldap_parsemsg();
73 #endif /* NEEDPROTOS */
74
75 /*
76 * cldap_open - initialize and connect to an ldap server. A magic cookie to
77 * be used for future communication is returned on success, NULL on failure.
78 *
79 * Example:
80 * LDAP *ld;
81 * ld = cldap_open( hostname, port );
82 */
83
84 LDAP *
cldap_open(char * host,int port)85 cldap_open( char *host, int port )
86 {
87 int s;
88 in_addr_t address;
89 struct sockaddr_in sock;
90 struct hostent *hp;
91 LDAP *ld;
92 char *p;
93 int i;
94 #ifdef SUN
95 struct hostent hpret;
96 char hpbuf[NSS_BUFLEN_HOSTS];
97 int hperrno;
98 #endif
99 in_addr_t inet_addr(const char *);
100 int close(int);
101
102 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 113, "ldap_open\n"), 0, 0, 0 );
103
104 if ( port == 0 ) {
105 port = LDAP_PORT;
106 }
107
108 if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) {
109 return( NULL );
110 }
111
112 sock.sin_addr.s_addr = 0;
113 sock.sin_family = AF_INET;
114 sock.sin_port = 0;
115 if ( bind(s, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
116 close( s );
117 return( NULL );
118 }
119
120 if (( ld = ldap_init( host, port )) == NULL ) {
121 close( s );
122 return( NULL );
123 }
124 if ( (ld->ld_sb.sb_fromaddr = (void *) calloc( 1,
125 sizeof( struct sockaddr ))) == NULL ) {
126 free( ld );
127 close( s );
128 return( NULL );
129 }
130 ld->ld_sb.sb_sd = s;
131 ld->ld_sb.sb_naddr = 0;
132 ld->ld_version = LDAP_VERSION;
133
134 sock.sin_family = AF_INET;
135 sock.sin_port = htons( port );
136
137 /*
138 * 'host' may be a space-separated list.
139 */
140 if ( host != NULL ) {
141 for ( ; host != NULL; host = p ) {
142 if (( p = strchr( host, ' ' )) != NULL ) {
143 for (*p++ = '\0'; *p == ' '; p++) {
144 ;
145 }
146 }
147
148 if ( (address = inet_addr( host )) == -1 ) {
149 #ifdef SUN
150 if ( (hp = gethostbyname_r( host, &hpret, hpbuf, NSS_BUFLEN_HOSTS, &hperrno)) == NULL ) {
151 errno = EHOSTUNREACH;
152 continue;
153 }
154 #else
155 if ( (hp = gethostbyname( host )) == NULL ) {
156 errno = EHOSTUNREACH;
157 continue;
158 }
159 #endif
160
161 for ( i = 0; hp->h_addr_list[ i ] != 0; ++i ) {
162 SAFEMEMCPY( (char *)&sock.sin_addr.s_addr,
163 (char *)hp->h_addr_list[ i ],
164 sizeof(sock.sin_addr.s_addr));
165 if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
166 close( s );
167 free( ld );
168 return( NULL );
169 }
170 }
171
172 } else {
173 sock.sin_addr.s_addr = address;
174 if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
175 close( s );
176 free( ld );
177 return( NULL );
178 }
179 }
180
181 if ( ld->ld_host == NULL ) {
182 ld->ld_host = strdup( host );
183 }
184 }
185
186 } else {
187 address = INADDR_LOOPBACK;
188 sock.sin_addr.s_addr = htonl( address );
189 if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
190 close( s );
191 free( ld );
192 return( NULL );
193 }
194 }
195
196 if ( ld->ld_sb.sb_addrs == NULL
197 #ifdef LDAP_REFERRALS
198 || ( ld->ld_defconn = new_connection( ld, NULL, 1,0,0 )) == NULL
199 #endif /* LDAP_REFERRALS */
200 ) {
201 free( ld );
202 return( NULL );
203 }
204
205 ld->ld_sb.sb_useaddr = ld->ld_sb.sb_addrs[ 0 ];
206 cldap_setretryinfo( ld, 0, 0 );
207
208 #ifdef LDAP_DEBUG
209 putchar( '\n' );
210 for ( i = 0; i < ld->ld_sb.sb_naddr; ++i ) {
211 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 114, "end of cldap_open address %1$d is %2$s\n"),
212 i, inet_ntoa( ((struct sockaddr_in *)
213 ld->ld_sb.sb_addrs[ i ])->sin_addr ), 0 );
214 }
215 #endif
216
217 return( ld );
218 }
219
220
221
222 void
cldap_close(LDAP * ld)223 cldap_close( LDAP *ld )
224 {
225 ldap_ld_free( ld, 0 );
226 }
227
228
229 void
cldap_setretryinfo(LDAP * ld,int tries,time_t timeout)230 cldap_setretryinfo( LDAP *ld, int tries, time_t timeout )
231 {
232 #if defined( SUN ) && defined( _REENTRANT )
233 LOCK_LDAP(ld);
234 #endif
235 ld->ld_cldaptries = ( tries <= 0 ) ? DEF_CLDAP_TRIES : tries;
236 ld->ld_cldaptimeout = ( timeout <= 0 ) ? DEF_CLDAP_TIMEOUT : timeout;
237 #if defined( SUN ) && defined( _REENTRANT )
238 UNLOCK_LDAP(ld);
239 #endif
240 }
241
242
243 int
cldap_search_s(LDAP * ld,char * base,int scope,char * filter,char ** attrs,int attrsonly,LDAPMessage ** res,char * logdn)244 cldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
245 int attrsonly, LDAPMessage **res, char *logdn )
246 {
247 int ret, msgid;
248 struct cldap_retinfo cri;
249
250 *res = NULLMSG;
251
252 (void) memset( &cri, 0, sizeof( cri ));
253
254 #if defined( SUN ) && defined( _REENTRANT )
255 LOCK_LDAP(ld);
256 #endif
257
258 if ( logdn != NULL ) {
259 ld->ld_cldapdn = logdn;
260 } else if ( ld->ld_cldapdn == NULL ) {
261 ld->ld_cldapdn = "";
262 }
263
264 do {
265 if ( cri.cri_try != 0 ) {
266 --ld->ld_msgid; /* use same id as before */
267 }
268 ld->ld_sb.sb_useaddr = ld->ld_sb.sb_addrs[ cri.cri_useaddr ];
269
270 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 115, "cldap_search_s try %1$d (to %2$s)\n"),
271 cri.cri_try, inet_ntoa( ((struct sockaddr_in *)
272 ld->ld_sb.sb_useaddr)->sin_addr ), 0 );
273
274 if ( (msgid = ldap_search( ld, base, scope, filter, attrs,
275 attrsonly )) == -1 ) {
276 #if defined( SUN ) && defined( _REENTRANT )
277 UNLOCK_LDAP(ld);
278 #endif
279 return( ld->ld_errno );
280 }
281 #ifndef NO_CACHE
282 #if defined( SUN ) && defined( _REENTRANT )
283 LOCK_RESPONSE(ld);
284 #endif
285 if ( ld->ld_cache != NULL && ld->ld_responses != NULL ) {
286 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 116, "cldap_search_s res from cache\n"),
287 0, 0, 0 );
288 *res = ld->ld_responses;
289 ld->ld_responses = ld->ld_responses->lm_next;
290 #if defined( SUN ) && defined( _REENTRANT )
291 UNLOCK_LDAP(ld);
292 ret = ldap_result2error( ld, *res, 0 );
293 UNLOCK_RESPONSE(ld);
294 return( ret );
295 #else
296 return( ldap_result2error( ld, *res, 0 ));
297 #endif
298 }
299 #endif /* NO_CACHE */
300 ret = cldap_result( ld, msgid, res, &cri, base );
301 #if defined( SUN ) && defined( _REENTRANT )
302 UNLOCK_RESPONSE(ld);
303 #endif
304 } while (ret == -1);
305
306 return( ret );
307 }
308
309
310 static int
add_addr(LDAP * ld,struct sockaddr * sap)311 add_addr( LDAP *ld, struct sockaddr *sap )
312 {
313 struct sockaddr *newsap, **addrs;
314
315 if (( newsap = (struct sockaddr *)malloc( sizeof( struct sockaddr )))
316 == NULL ) {
317 ld->ld_errno = LDAP_NO_MEMORY;
318 return( -1 );
319 }
320
321 if ( ld->ld_sb.sb_naddr == 0 ) {
322 addrs = (struct sockaddr **)malloc( sizeof(struct sockaddr *));
323 } else {
324 addrs = (struct sockaddr **)realloc( ld->ld_sb.sb_addrs,
325 ( ld->ld_sb.sb_naddr + 1 ) * sizeof(struct sockaddr *));
326 }
327
328 if ( addrs == NULL ) {
329 free( newsap );
330 ld->ld_errno = LDAP_NO_MEMORY;
331 return( -1 );
332 }
333
334 SAFEMEMCPY( (char *)newsap, (char *)sap, sizeof( struct sockaddr ));
335 addrs[ ld->ld_sb.sb_naddr++ ] = newsap;
336 ld->ld_sb.sb_addrs = (void **)addrs;
337 return( 0 );
338 }
339
340
341 static int
cldap_result(LDAP * ld,int msgid,LDAPMessage ** res,struct cldap_retinfo * crip,char * base)342 cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
343 struct cldap_retinfo *crip, char *base )
344 {
345 Sockbuf *sb;
346 BerElement ber;
347 char *logdn;
348 int ret, id, fromaddr, i;
349 struct timeval tv;
350
351 #if defined( SUN ) && defined( _REENTRANT )
352 LOCK_LDAP(ld);
353 #endif
354
355 sb = &ld->ld_sb;
356 fromaddr = -1;
357
358 if ( crip->cri_try == 0 ) {
359 crip->cri_maxtries = ld->ld_cldaptries * sb->sb_naddr;
360 crip->cri_timeout = ld->ld_cldaptimeout;
361 crip->cri_useaddr = 0;
362 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 117, "cldap_result tries %1$d timeout %2$d\n"),
363 ld->ld_cldaptries, ld->ld_cldaptimeout, 0 );
364 }
365
366 if ((tv.tv_sec = crip->cri_timeout / sb->sb_naddr) < 1 ) {
367 tv.tv_sec = 1;
368 }
369 tv.tv_usec = 0;
370
371 Debug( LDAP_DEBUG_TRACE,
372 catgets(slapdcat, 1, 118, "cldap_result waiting up to %d seconds for a response\n"),
373 tv.tv_sec, 0, 0 );
374 ber_zero_init( &ber, 0 );
375 set_ber_options( ld, &ber );
376
377 if ( cldap_getmsg( ld, &tv, &ber ) == -1 ) {
378 ret = ld->ld_errno;
379 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 119, "cldap_getmsg returned -1 (%d)\n"),
380 ret, 0, 0 );
381 } else if ( ld->ld_errno == LDAP_TIMEOUT ) {
382 Debug( LDAP_DEBUG_TRACE,
383 catgets(slapdcat, 1, 120, "cldap_result timed out\n"), 0, 0, 0 );
384 /*
385 * It timed out; is it time to give up?
386 */
387 if ( ++crip->cri_try >= crip->cri_maxtries ) {
388 ret = LDAP_TIMEOUT;
389 --crip->cri_try;
390 } else {
391 if ( ++crip->cri_useaddr >= sb->sb_naddr ) {
392 /*
393 * new round: reset address to first one and
394 * double the timeout
395 */
396 crip->cri_useaddr = 0;
397 crip->cri_timeout <<= 1;
398 }
399 ret = -1;
400 }
401
402 } else {
403 /*
404 * Got a response. It should look like:
405 * { msgid, logdn, { searchresponse...}}
406 */
407 logdn = NULL;
408
409 if ( ber_scanf( &ber, "ia", &id, &logdn ) == LBER_ERROR ) {
410 free( ber.ber_buf ); /* gack! */
411 ret = LDAP_DECODING_ERROR;
412 Debug( LDAP_DEBUG_TRACE,
413 catgets(slapdcat, 1, 121, "cldap_result: ber_scanf returned LBER_ERROR (%d)\n"),
414 ret, 0, 0 );
415 } else if ( id != msgid ) {
416 free( ber.ber_buf ); /* gack! */
417 Debug( LDAP_DEBUG_TRACE,
418 catgets(slapdcat, 1, 122, "cldap_result: looking for msgid %1$d; got %2$d\n"),
419 msgid, id, 0 );
420 ret = -1; /* ignore and keep looking */
421 } else {
422 /*
423 * got a result: determine which server it came from
424 * decode into ldap message chain
425 */
426 for ( fromaddr = 0; fromaddr < sb->sb_naddr; ++fromaddr ) {
427 if ( memcmp( &((struct sockaddr_in *)
428 sb->sb_addrs[ fromaddr ])->sin_addr,
429 &((struct sockaddr_in *)sb->sb_fromaddr)->sin_addr,
430 sizeof( struct in_addr )) == 0 ) {
431 break;
432 }
433 }
434 ret = cldap_parsemsg( ld, msgid, &ber, res, base );
435 free( ber.ber_buf ); /* gack! */
436 Debug( LDAP_DEBUG_TRACE,
437 catgets(slapdcat, 1, 123, "cldap_result got result (%d)\n"), ret, 0, 0 );
438 }
439
440 if ( logdn != NULL ) {
441 free( logdn );
442 }
443 }
444
445
446 /*
447 * If we are giving up (successfully or otherwise) then
448 * abandon any outstanding requests.
449 */
450 if ( ret != -1 ) {
451 i = crip->cri_try;
452 if ( i >= sb->sb_naddr ) {
453 i = sb->sb_naddr - 1;
454 }
455
456 for ( ; i >= 0; --i ) {
457 if ( i == fromaddr ) {
458 continue;
459 }
460 sb->sb_useaddr = sb->sb_addrs[ i ];
461 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 124, "cldap_result abandoning id %1$d (to %2$s)\n"),
462 msgid, inet_ntoa( ((struct sockaddr_in *)
463 sb->sb_useaddr)->sin_addr ), 0 );
464 (void) ldap_abandon( ld, msgid );
465 }
466 }
467
468 #if defined( SUN ) && defined( _REENTRANT )
469 UNLOCK_LDAP(ld);
470 #endif
471 return( ld->ld_errno = ret );
472 }
473
474
475 static int
cldap_parsemsg(LDAP * ld,int msgid,BerElement * ber,LDAPMessage ** res,char * base)476 cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber,
477 LDAPMessage **res, char *base )
478 {
479 unsigned int tag, len;
480 int rc;
481 size_t baselen, slen;
482 char *dn, *p, *cookie;
483 LDAPMessage *chain, *prev, *ldm;
484 struct berval *bv;
485
486 rc = LDAP_DECODING_ERROR; /* pessimistic */
487 ldm = chain = prev = NULLMSG;
488 baselen = ( base == NULL ) ? 0 : strlen( base );
489 bv = NULL;
490
491 for ( tag = ber_first_element( ber, &len, &cookie );
492 tag != LBER_DEFAULT && rc != LDAP_SUCCESS;
493 tag = ber_next_element( ber, &len, cookie )) {
494 if (( ldm = (LDAPMessage *)calloc( 1, sizeof(LDAPMessage)))
495 == NULL || ( ldm->lm_ber = alloc_ber_with_options( ld ))
496 == NULLBER ) {
497 rc = LDAP_NO_MEMORY;
498 break; /* return w/error*/
499 }
500 ldm->lm_msgid = msgid;
501 ldm->lm_msgtype = tag;
502
503 if ( tag == LDAP_RES_SEARCH_RESULT ) {
504 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 125, "cldap_parsemsg got search result\n"),
505 0, 0, 0 );
506
507 if ( ber_get_stringal( ber, &bv ) == LBER_DEFAULT ) {
508 break; /* return w/error */
509 }
510
511 if ( ber_printf( ldm->lm_ber, "to", tag, bv->bv_val,
512 bv->bv_len ) == -1 ) {
513 break; /* return w/error */
514 }
515 ber_bvfree( bv );
516 bv = NULL;
517 rc = LDAP_SUCCESS;
518
519 } else if ( tag == LDAP_RES_SEARCH_ENTRY ) {
520 if ( ber_scanf( ber, "{aO", &dn, &bv ) == LBER_ERROR ) {
521 break; /* return w/error */
522 }
523 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 126, "cldap_parsemsg entry %s\n"), dn, 0, 0 );
524 if ( dn != NULL && *(dn + ( slen = strlen(dn)) - 1) == '*' &&
525 baselen > 0 ) {
526 /*
527 * substitute original searchbase for trailing '*'
528 */
529 if (( p = (char *)malloc( slen + baselen )) == NULL ) {
530 rc = LDAP_NO_MEMORY;
531 free( dn );
532 break; /* return w/error */
533 }
534 strcpy( p, dn );
535 strcpy( p + slen - 1, base );
536 free( dn );
537 dn = p;
538 }
539
540 if ( ber_printf( ldm->lm_ber, "t{so}", tag, dn, bv->bv_val,
541 bv->bv_len ) == -1 ) {
542 break; /* return w/error */
543 }
544 free( dn );
545 ber_bvfree( bv );
546 bv = NULL;
547
548 } else {
549 Debug( LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 127, "cldap_parsemsg got unknown tag %d\n"),
550 tag, 0, 0 );
551 rc = LDAP_PROTOCOL_ERROR;
552 break; /* return w/error */
553 }
554
555 /* Reset message ber so we can read from it later. Gack! */
556 ldm->lm_ber->ber_end = ldm->lm_ber->ber_ptr;
557 ldm->lm_ber->ber_ptr = ldm->lm_ber->ber_buf;
558
559 #ifdef LDAP_DEBUG
560 if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
561 fprintf( stderr, "cldap_parsemsg add message id %d type %d:\n",
562 ldm->lm_msgid, ldm->lm_msgtype );
563 ber_dump( ldm->lm_ber, 1 );
564 }
565 #endif /* LDAP_DEBUG */
566
567 #ifndef NO_CACHE
568 if ( ld->ld_cache != NULL ) {
569 add_result_to_cache( ld, ldm );
570 }
571 #endif /* NO_CACHE */
572
573 if ( chain == NULL ) {
574 chain = ldm;
575 } else {
576 prev->lm_chain = ldm;
577 }
578 prev = ldm;
579 ldm = NULL;
580 }
581
582 /* dispose of any leftovers */
583 if ( ldm != NULL ) {
584 if ( ldm->lm_ber != NULLBER ) {
585 ber_free( ldm->lm_ber, 1 );
586 }
587 free( ldm );
588 }
589 if ( bv != NULL ) {
590 ber_bvfree( bv );
591 }
592
593 /* return chain, calling result2error if we got anything at all */
594 *res = chain;
595 return(( *res == NULLMSG ) ? rc : ldap_result2error( ld, *res, 0 ));
596 }
597 #endif /* CLDAP */
598