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