Back to index

lightning-sunbird  0.9+nobinonly
cldap.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 /*
00038  *  Copyright (c) 1990, 1994 Regents of the University of Michigan.
00039  *  All rights reserved.
00040  */
00041 /*
00042  *  cldap.c - synchronous, retrying interface to the cldap protocol
00043  */
00044 
00045 
00046 #ifdef CLDAP
00047 
00048 XXX not MT-safe XXX
00049 
00050 #ifndef lint 
00051 static char copyright[] = "@(#) Copyright (c) 1990, 1994 Regents of the University of Michigan.\nAll rights reserved.\n";
00052 #endif
00053 
00054 #include <stdio.h>
00055 #include <string.h>
00056 #include <errno.h>
00057 #ifdef macintosh
00058 #include <stdlib.h>
00059 #include "macos.h"
00060 #else /* macintosh */
00061 #ifdef DOS
00062 #include "msdos.h"
00063 #else /* DOS */
00064 #ifdef _WINDOWS
00065 #include <windows.h>
00066 #else /* _WINDOWS */
00067 #include <sys/time.h>
00068 #include <sys/types.h>
00069 #include <sys/socket.h>
00070 #include <netinet/in.h>
00071 #include <netdb.h>
00072 #endif /* _WINDOWS */
00073 #endif /* DOS */
00074 #endif /* macintosh */
00075 
00076 #include "ldap-int.h"
00077 
00078 #define DEF_CLDAP_TIMEOUT   3
00079 #define DEF_CLDAP_TRIES            4
00080 
00081 #ifndef INADDR_LOOPBACK
00082 #define INADDR_LOOPBACK     ((unsigned long) 0x7f000001)
00083 #endif
00084 
00085 
00086 struct cldap_retinfo {
00087        int           cri_maxtries;
00088        int           cri_try;
00089        int           cri_useaddr;
00090        long          cri_timeout;
00091 };
00092 
00093 #ifdef NEEDPROTOS
00094 static int add_addr( LDAP *ld, struct sockaddr *sap );
00095 static int cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
00096        struct cldap_retinfo *crip, char *base );
00097 static int cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber,
00098        LDAPMessage **res, char *base );
00099 #else /* NEEDPROTOS */
00100 static int add_addr();
00101 static int cldap_result();
00102 static int cldap_parsemsg();
00103 #endif /* NEEDPROTOS */
00104 
00105 /*
00106  * cldap_open - initialize and connect to an ldap server.  A magic cookie to
00107  * be used for future communication is returned on success, NULL on failure.
00108  *
00109  * Example:
00110  *     LDAP   *ld;
00111  *     ld = cldap_open( hostname, port );
00112  */
00113 
00114 LDAP *
00115 cldap_open( char *host, int port )
00116 {
00117     int              s;
00118     ldap_x_in_addr_t address;
00119     struct sockaddr_in      sock;
00120     struct hostent   *hp;
00121     LDAP             *ld;
00122     char             *p;
00123     int                     i;
00124 
00125     LDAPDebug( LDAP_DEBUG_TRACE, "cldap_open\n", 0, 0, 0 );
00126 
00127     if ( port == 0 ) {
00128            port = LDAP_PORT;
00129     }
00130 
00131     if ( (s = socket( AF_INET, SOCK_DGRAM, 0 )) < 0 ) {
00132        return( NULL );
00133     }
00134 
00135     sock.sin_addr.s_addr = 0;
00136     sock.sin_family = AF_INET;
00137     sock.sin_port = 0;
00138     if ( bind(s, (struct sockaddr *) &sock, sizeof(sock)) < 0)  {
00139        close( s );
00140        return( NULL );
00141     }
00142 
00143     if (( ld = ldap_init( host, port )) == NULL ) {
00144        close( s );
00145        return( NULL );
00146     }
00147     if ( (ld->ld_sbp->sb_fromaddr = (void *)NSLDAPI_CALLOC( 1,
00148            sizeof( struct sockaddr ))) == NULL ) {
00149        NSLDAPI_FREE( ld );
00150        close( s );
00151        return( NULL );
00152     }  
00153     ld->ld_sbp->sb_sd = s;
00154     ld->ld_sbp->sb_naddr = 0;
00155     ld->ld_version = LDAP_VERSION;
00156 
00157     sock.sin_family = AF_INET;
00158     sock.sin_port = htons( port );
00159 
00160     /*
00161      * 'host' may be a space-separated list.
00162      */
00163     if ( host != NULL ) {
00164        for ( ; host != NULL; host = p ) {
00165            if (( p = strchr( host, ' ' )) != NULL ) {
00166               for (*p++ = '\0'; *p == ' '; p++) {
00167                   ;
00168               }
00169            }
00170 
00171            if ( (address = inet_addr( host )) == -1 ) {
00172 /* XXXmcs: need to use DNS callbacks here XXX */
00173 XXX
00174               if ( (hp = gethostbyname( host )) == NULL ) {
00175                   LDAP_SET_ERRNO( ld, EHOSTUNREACH );
00176                   continue;
00177               }
00178 
00179               for ( i = 0; hp->h_addr_list[ i ] != 0; ++i ) {
00180                   SAFEMEMCPY( (char *)&sock.sin_addr.s_addr,
00181                          (char *)hp->h_addr_list[ i ],
00182                          sizeof(sock.sin_addr.s_addr));
00183                   if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
00184                      close( s );
00185                      NSLDAPI_FREE( ld );
00186                      return( NULL );
00187                   }
00188               }
00189 
00190            } else {
00191               sock.sin_addr.s_addr = address;
00192               if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
00193                   close( s );
00194                   NSLDAPI_FREE( ld );
00195                   return( NULL );
00196               }
00197            }
00198 
00199            if ( ld->ld_host == NULL ) {
00200                   ld->ld_host = nsldapi_strdup( host );
00201            }
00202        }
00203 
00204     } else {
00205        address = INADDR_LOOPBACK;
00206        sock.sin_addr.s_addr = htonl( address );
00207        if ( add_addr( ld, (struct sockaddr *)&sock ) < 0 ) {
00208            close( s );
00209            NSLDAPI_FREE( ld );
00210            return( NULL );
00211        }
00212     }
00213 
00214     if ( ld->ld_sbp->sb_addrs == NULL
00215            || ( ld->ld_defconn = nsldapi_new_connection( ld, NULL, 1,0,0 )) == NULL ) {
00216        NSLDAPI_FREE( ld );
00217        return( NULL );
00218     }
00219 
00220     ld->ld_sbp->sb_useaddr = ld->ld_sbp->sb_addrs[ 0 ];
00221     cldap_setretryinfo( ld, 0, 0 );
00222 
00223 #ifdef LDAP_DEBUG
00224     putchar( '\n' );
00225     for ( i = 0; i < ld->ld_sbp->sb_naddr; ++i ) {
00226        LDAPDebug( LDAP_DEBUG_TRACE, "end of cldap_open address %d is %s\n",
00227               i, inet_ntoa( ((struct sockaddr_in *)
00228               ld->ld_sbp->sb_addrs[ i ])->sin_addr ), 0 );
00229     }
00230 #endif
00231 
00232     return( ld );
00233 }
00234 
00235 
00236 
00237 void
00238 cldap_close( LDAP *ld )
00239 {
00240        ldap_ld_free( ld, NULL, NULL, 0 );
00241 }
00242 
00243 
00244 void
00245 cldap_setretryinfo( LDAP *ld, int tries, int timeout )
00246 {
00247     ld->ld_cldaptries = ( tries <= 0 ) ? DEF_CLDAP_TRIES : tries;
00248     ld->ld_cldaptimeout = ( timeout <= 0 ) ? DEF_CLDAP_TIMEOUT : timeout;
00249 }
00250 
00251 
00252 int
00253 cldap_search_s( LDAP *ld, char *base, int scope, char *filter, char **attrs,
00254        int attrsonly, LDAPMessage **res, char *logdn )
00255 {
00256     int                            ret, msgid;
00257     struct cldap_retinfo    cri;
00258 
00259     *res = NULLMSG;
00260 
00261     (void) memset( &cri, 0, sizeof( cri ));
00262 
00263     if ( logdn != NULL ) {
00264        ld->ld_cldapdn = logdn;
00265     } else if ( ld->ld_cldapdn == NULL ) {
00266        ld->ld_cldapdn = "";
00267     }
00268 
00269     do {
00270        if ( cri.cri_try != 0 ) {
00271               --ld->ld_msgid;      /* use same id as before */
00272        }
00273        ld->ld_sbp->sb_useaddr = ld->ld_sbp->sb_addrs[ cri.cri_useaddr ];
00274 
00275        LDAPDebug( LDAP_DEBUG_TRACE, "cldap_search_s try %d (to %s)\n",
00276            cri.cri_try, inet_ntoa( ((struct sockaddr_in *)
00277            ld->ld_sbp->sb_useaddr)->sin_addr ), 0 );
00278 
00279            if ( (msgid = ldap_search( ld, base, scope, filter, attrs,
00280               attrsonly )) == -1 ) {
00281                   return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
00282            }
00283 #ifndef NO_CACHE
00284            if ( ld->ld_cache != NULL && ld->ld_responses != NULL ) {
00285               LDAPDebug( LDAP_DEBUG_TRACE, "cldap_search_s res from cache\n",
00286                      0, 0, 0 );
00287               *res = ld->ld_responses;
00288               ld->ld_responses = ld->ld_responses->lm_next;
00289               return( ldap_result2error( ld, *res, 0 ));
00290            }
00291 #endif /* NO_CACHE */
00292            ret = cldap_result( ld, msgid, res, &cri, base );
00293        } while (ret == -1);
00294 
00295        return( ret );
00296 }
00297 
00298 
00299 static int
00300 add_addr( LDAP *ld, struct sockaddr *sap )
00301 {
00302     struct sockaddr  *newsap, **addrs;
00303 
00304     if (( newsap = (struct sockaddr *)NSLDAPI_MALLOC(
00305            sizeof( struct sockaddr ))) == NULL ) {
00306        LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
00307        return( -1 );
00308     }
00309 
00310     if ( ld->ld_sbp->sb_naddr == 0 ) {
00311        addrs = (struct sockaddr **)NSLDAPI_MALLOC( sizeof(struct sockaddr *));
00312     } else {
00313        addrs = (struct sockaddr **)NSLDAPI_REALLOC( ld->ld_sbp->sb_addrs,
00314               ( ld->ld_sbp->sb_naddr + 1 ) * sizeof(struct sockaddr *));
00315     }
00316 
00317     if ( addrs == NULL ) {
00318        NSLDAPI_FREE( newsap );
00319        LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
00320        return( -1 );
00321     }
00322 
00323     SAFEMEMCPY( (char *)newsap, (char *)sap, sizeof( struct sockaddr ));
00324     addrs[ ld->ld_sbp->sb_naddr++ ] = newsap;
00325     ld->ld_sbp->sb_addrs = (void **)addrs;
00326     return( 0 );
00327 }
00328 
00329 
00330 static int
00331 cldap_result( LDAP *ld, int msgid, LDAPMessage **res,
00332        struct cldap_retinfo *crip, char *base )
00333 {
00334     Sockbuf          *sb = ld->ld_sbp;
00335     BerElement              ber;
00336     char             *logdn;
00337     int                     ret, fromaddr, i;
00338     long             id;
00339     struct timeval   tv;
00340 
00341     fromaddr = -1;
00342 
00343     if ( crip->cri_try == 0 ) {
00344        crip->cri_maxtries = ld->ld_cldaptries * sb->sb_naddr;
00345        crip->cri_timeout = ld->ld_cldaptimeout;
00346        crip->cri_useaddr = 0;
00347        LDAPDebug( LDAP_DEBUG_TRACE, "cldap_result tries %d timeout %d\n",
00348               ld->ld_cldaptries, ld->ld_cldaptimeout, 0 );
00349     }
00350 
00351     if ((tv.tv_sec = crip->cri_timeout / sb->sb_naddr) < 1 ) {
00352        tv.tv_sec = 1;
00353     }
00354     tv.tv_usec = 0;
00355 
00356     LDAPDebug( LDAP_DEBUG_TRACE,
00357            "cldap_result waiting up to %d seconds for a response\n",
00358            tv.tv_sec, 0, 0 );
00359     ber_init_w_nullchar( &ber, 0 );
00360     nsldapi_set_ber_options( ld, &ber );
00361 
00362     if ( cldap_getmsg( ld, &tv, &ber ) == -1 ) {
00363        ret = LDAP_GET_LDERRNO( ld, NULL, NULL );
00364        LDAPDebug( LDAP_DEBUG_TRACE, "cldap_getmsg returned -1 (%d)\n",
00365               ret, 0, 0 );
00366     } else if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) {
00367        LDAPDebug( LDAP_DEBUG_TRACE,
00368            "cldap_result timed out\n", 0, 0, 0 );
00369        /*
00370         * It timed out; is it time to give up?
00371         */
00372        if ( ++crip->cri_try >= crip->cri_maxtries ) {
00373            ret = LDAP_TIMEOUT;
00374            --crip->cri_try;
00375        } else {
00376            if ( ++crip->cri_useaddr >= sb->sb_naddr ) {
00377               /*
00378                * new round: reset address to first one and
00379                * double the timeout
00380                */
00381               crip->cri_useaddr = 0;
00382               crip->cri_timeout <<= 1;
00383            }
00384            ret = -1;
00385        }
00386 
00387     } else {
00388        /*
00389         * Got a response.  It should look like:
00390         * { msgid, logdn, { searchresponse...}}
00391         */
00392        logdn = NULL;
00393 
00394        if ( ber_scanf( &ber, "ia", &id, &logdn ) == LBER_ERROR ) {
00395            NSLDAPI_FREE( ber.ber_buf );   /* gack! */
00396            ret = LDAP_DECODING_ERROR;
00397            LDAPDebug( LDAP_DEBUG_TRACE,
00398                   "cldap_result: ber_scanf returned LBER_ERROR (%d)\n",
00399                   ret, 0, 0 );
00400        } else if ( id != msgid ) {
00401            NSLDAPI_FREE( ber.ber_buf );   /* gack! */
00402            LDAPDebug( LDAP_DEBUG_TRACE,
00403                   "cldap_result: looking for msgid %d; got %ld\n",
00404                   msgid, id, 0 );
00405            ret = -1; /* ignore and keep looking */
00406        } else {
00407            /*
00408             * got a result: determine which server it came from
00409             * decode into ldap message chain
00410             */
00411            for ( fromaddr = 0; fromaddr < sb->sb_naddr; ++fromaddr ) {
00412                   if ( memcmp( &((struct sockaddr_in *)
00413                          sb->sb_addrs[ fromaddr ])->sin_addr,
00414                          &((struct sockaddr_in *)sb->sb_fromaddr)->sin_addr,
00415                          sizeof( struct in_addr )) == 0 ) {
00416                      break;
00417                   }
00418            }
00419            ret = cldap_parsemsg( ld, msgid, &ber, res, base );
00420            NSLDAPI_FREE( ber.ber_buf );   /* gack! */
00421            LDAPDebug( LDAP_DEBUG_TRACE,
00422               "cldap_result got result (%d)\n", ret, 0, 0 );
00423        }
00424 
00425        if ( logdn != NULL ) {
00426               NSLDAPI_FREE( logdn );
00427        }
00428     }
00429     
00430 
00431     /*
00432      * If we are giving up (successfully or otherwise) then 
00433      * abandon any outstanding requests.
00434      */
00435     if ( ret != -1 ) {
00436        i = crip->cri_try;
00437        if ( i >= sb->sb_naddr ) {
00438            i = sb->sb_naddr - 1;
00439        }
00440 
00441        for ( ; i >= 0; --i ) {
00442            if ( i == fromaddr ) {
00443               continue;
00444            }
00445            sb->sb_useaddr = sb->sb_addrs[ i ];
00446            LDAPDebug( LDAP_DEBUG_TRACE, "cldap_result abandoning id %d (to %s)\n",
00447               msgid, inet_ntoa( ((struct sockaddr_in *)
00448               sb->sb_useaddr)->sin_addr ), 0 );
00449            (void) ldap_abandon( ld, msgid );
00450        }
00451     }
00452 
00453     LDAP_SET_LDERRNO( ld, ret, NULL, NULL );
00454     return( ret );
00455 }
00456 
00457 
00458 static int
00459 cldap_parsemsg( LDAP *ld, int msgid, BerElement *ber,
00460        LDAPMessage **res, char *base )
00461 {
00462     unsigned long    tag, len;
00463     int                     baselen, slen, rc;
00464     char             *dn, *p, *cookie;
00465     LDAPMessage             *chain, *prev, *ldm;
00466     struct berval    *bv;
00467 
00468     rc = LDAP_DECODING_ERROR;      /* pessimistic */
00469     ldm = chain = prev = NULLMSG;
00470     baselen = ( base == NULL ) ? 0 : strlen( base );
00471     bv = NULL;
00472 
00473     for ( tag = ber_first_element( ber, &len, &cookie );
00474            tag != LBER_ERROR && tag != LBER_END_OF_SEQOFSET
00475            && rc != LDAP_SUCCESS;
00476            tag = ber_next_element( ber, &len, cookie )) {
00477        if (( ldm = (LDAPMessage *)NSLDAPI_CALLOC( 1, sizeof(LDAPMessage)))
00478               == NULL ) {
00479            rc = LDAP_NO_MEMORY;
00480            break;    /* return with error */
00481        } else if (( rc = nsldapi_alloc_ber_with_options( ld, &ldm->lm_ber ))
00482               != LDAP_SUCCESS ) {
00483            break;    /* return with error*/
00484        }
00485        ldm->lm_msgid = msgid;
00486        ldm->lm_msgtype = tag;
00487 
00488        if ( tag == LDAP_RES_SEARCH_RESULT ) {
00489            LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg got search result\n",
00490                   0, 0, 0 );
00491 
00492            if ( ber_get_stringal( ber, &bv ) == LBER_DEFAULT ) {
00493               break; /* return w/error */
00494            }
00495 
00496            if ( ber_printf( ldm->lm_ber, "to", tag, bv->bv_val,
00497                   (int)bv->bv_len /* XXX lossy cast */ ) == -1 ) {
00498               break; /* return w/error */
00499            }
00500            ber_bvfree( bv );
00501            bv = NULL;
00502            rc = LDAP_SUCCESS;
00503 
00504        } else if ( tag == LDAP_RES_SEARCH_ENTRY ) {
00505            if ( ber_scanf( ber, "{aO", &dn, &bv ) == LBER_ERROR ) {
00506               break; /* return w/error */
00507            }
00508            LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg entry %s\n", dn, 0, 0 );
00509            if ( dn != NULL && *(dn + ( slen = strlen(dn)) - 1) == '*' &&
00510                   baselen > 0 ) {
00511               /*
00512                * substitute original searchbase for trailing '*'
00513                */
00514               if (( p = (char *)NSLDAPI_MALLOC( slen + baselen )) == NULL ) {
00515                   rc = LDAP_NO_MEMORY;
00516                   NSLDAPI_FREE( dn );
00517                   break;    /* return w/error */
00518               }
00519               strcpy( p, dn );
00520               strcpy( p + slen - 1, base );
00521               NSLDAPI_FREE( dn );
00522               dn = p;
00523            }
00524 
00525            if ( ber_printf( ldm->lm_ber, "t{so}", tag, dn, bv->bv_val,
00526                   (int)bv->bv_len /* XXX lossy cast */ ) == -1 ) {
00527               break; /* return w/error */
00528            }
00529            NSLDAPI_FREE( dn );
00530            ber_bvfree( bv );
00531            bv = NULL;
00532               
00533        } else {
00534            LDAPDebug( LDAP_DEBUG_TRACE, "cldap_parsemsg got unknown tag %d\n",
00535                   tag, 0, 0 );
00536            rc = LDAP_PROTOCOL_ERROR;
00537            break;    /* return w/error */
00538        }
00539 
00540        /* Reset message ber so we can read from it later.  Gack! */
00541        ldm->lm_ber->ber_end = ldm->lm_ber->ber_ptr;
00542        ldm->lm_ber->ber_ptr = ldm->lm_ber->ber_buf;
00543 
00544 #ifdef LDAP_DEBUG
00545        if ( ldap_debug & LDAP_DEBUG_PACKETS ) {
00546               char msg[80];
00547            sprintf( msg, "cldap_parsemsg add message id %d type %d:\n",
00548                   ldm->lm_msgid, ldm->lm_msgtype  );
00549               ber_err_print( msg );
00550            ber_dump( ldm->lm_ber, 1 );
00551        }
00552 #endif /* LDAP_DEBUG */
00553 
00554 #ifndef NO_CACHE
00555            if ( ld->ld_cache != NULL ) {
00556               nsldapi_add_result_to_cache( ld, ldm );
00557            }
00558 #endif /* NO_CACHE */
00559 
00560        if ( chain == NULL ) {
00561            chain = ldm;
00562        } else {
00563            prev->lm_chain = ldm;
00564        }
00565        prev = ldm;
00566        ldm = NULL;
00567     }
00568 
00569     /* dispose of any leftovers */
00570     if ( ldm != NULL ) {
00571        if ( ldm->lm_ber != NULLBER ) {
00572            ber_free( ldm->lm_ber, 1 );
00573        }
00574        NSLDAPI_FREE( ldm );
00575     }
00576     if ( bv != NULL ) {
00577        ber_bvfree( bv );
00578     }
00579 
00580     /* return chain, calling result2error if we got anything at all */
00581     *res = chain;
00582     return(( *res == NULLMSG ) ? rc : ldap_result2error( ld, *res, 0 ));
00583 }
00584 #endif /* CLDAP */