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