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