xref: /onnv-gate/usr/src/lib/libldap4/common/cldap.c (revision 3857:21b9b714e4ab)
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