Back to index

lightning-sunbird  0.9+nobinonly
url.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) 1996 Regents of the University of Michigan.
00039  *  All rights reserved.
00040  *
00041  */
00042 /*  LIBLDAP url.c -- LDAP URL related routines
00043  *
00044  *  LDAP URLs look like this:
00045  *    l d a p : / / [ hostport ] [ / dn [ ? [ attributes ] [ ? [ scope ]
00046  *                   [ ? [ filter ] [ ? extensions ] ] ] ] ]
00047  *
00048  *  where:
00049  *   hostport is a host or a host:port list that can be space-separated.
00050  *   attributes is a comma separated list
00051  *   scope is one of these three strings:  base one sub (default=base)
00052  *   filter is a string-represented filter as in RFC 2254
00053  *   extensions is a comma-separated list of name=value pairs.
00054  *
00055  *  e.g.,  ldap://ldap.itd.umich.edu/c=US?o,description?one?o=umich
00056  *
00057  *  To accomodate IPv6 addresses, the host portion of a host that appears
00058  *  in hostport can be enclosed in square brackets, e.g
00059  *
00060  *  e.g.,  ldap://[fe80::a00:20ff:fee5:c0b4]:3389/dc=siroe,dc=com
00061  *
00062  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
00063  */
00064 
00065 #if 0
00066 #ifndef lint 
00067 static char copyright[] = "@(#) Copyright (c) 1996 Regents of the University of Michigan.\nAll rights reserved.\n";
00068 #endif
00069 #endif
00070 
00071 #include "ldap-int.h"
00072 
00073 
00074 static int skip_url_prefix( const char **urlp, int *enclosedp, int *securep );
00075 
00076 
00077 int
00078 LDAP_CALL
00079 ldap_is_ldap_url( const char *url )
00080 {
00081        int    enclosed, secure;
00082 
00083        return( url != NULL
00084            && skip_url_prefix( &url, &enclosed, &secure ));
00085 }
00086 
00087 
00088 static int
00089 skip_url_prefix( const char **urlp, int *enclosedp, int *securep )
00090 {
00091 /*
00092  * return non-zero if this looks like a LDAP URL; zero if not
00093  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
00094  * The data that *urlp points to is not changed by this function.
00095  */
00096        if ( *urlp == NULL ) {
00097               return( 0 );
00098        }
00099 
00100        /* skip leading '<' (if any) */
00101        if ( **urlp == '<' ) {
00102               *enclosedp = 1;
00103               ++*urlp;
00104        } else {
00105               *enclosedp = 0;
00106        }
00107 
00108        /* skip leading "URL:" (if any) */
00109        if ( strlen( *urlp ) >= LDAP_URL_URLCOLON_LEN && strncasecmp(
00110            *urlp, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
00111               *urlp += LDAP_URL_URLCOLON_LEN;
00112        }
00113 
00114        /* check for an "ldap://" prefix */
00115        if ( strlen( *urlp ) >= LDAP_URL_PREFIX_LEN && strncasecmp( *urlp,
00116            LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
00117               /* skip over URL prefix and return success */
00118               *urlp += LDAP_URL_PREFIX_LEN;
00119               *securep = 0;
00120               return( 1 );
00121        }
00122 
00123        /* check for an "ldaps://" prefix */
00124        if ( strlen( *urlp ) >= LDAPS_URL_PREFIX_LEN && strncasecmp( *urlp,
00125            LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
00126               /* skip over URL prefix and return success */
00127               *urlp += LDAPS_URL_PREFIX_LEN;
00128               *securep = 1;
00129               return( 1 );
00130        }
00131 
00132        return( 0 );  /* not an LDAP URL */
00133 }
00134 
00135 
00136 
00137 
00138 int
00139 LDAP_CALL
00140 ldap_url_parse( const char *url, LDAPURLDesc **ludpp )
00141 {
00142 /*
00143  *  Pick apart the pieces of an LDAP URL.
00144  */
00145        int    rc;
00146 
00147        if (( rc = nsldapi_url_parse( url, ludpp, 1 )) == 0 ) {
00148               if ( (*ludpp)->lud_scope == -1 ) {
00149                      (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
00150               }
00151               if ( (*ludpp)->lud_filter == NULL ) {
00152                      (*ludpp)->lud_filter = "(objectclass=*)";
00153               }
00154               if ( *((*ludpp)->lud_dn) == '\0' ) {
00155                      (*ludpp)->lud_dn = NULL;
00156               }
00157        } else if ( rc == LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION ) {
00158               rc = LDAP_URL_ERR_PARAM;    /* mapped for backwards compatibility */
00159        }
00160 
00161        return( rc );
00162 }
00163 
00164 
00165 /*
00166  * like ldap_url_parse() with a few exceptions:
00167  *   1) if dn_required is zero, a missing DN does not generate an error
00168  *     (we just leave the lud_dn field NULL)
00169  *   2) no defaults are set for lud_scope and lud_filter (they are set to -1
00170  *     and NULL respectively if no SCOPE or FILTER are present in the URL).
00171  *   3) when there is a zero-length DN in a URL we do not set lud_dn to NULL.
00172  *
00173  * note that LDAPv3 URL extensions are ignored unless they are marked
00174  * critical, in which case an LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION error
00175  * is returned.
00176  */
00177 int
00178 nsldapi_url_parse( const char *url, LDAPURLDesc **ludpp, int dn_required )
00179 {
00180 
00181        LDAPURLDesc   *ludp;
00182        char          *urlcopy, *attrs, *scope, *extensions = NULL, *p, *q;
00183        int           enclosed, secure, i, nattrs, at_start;
00184 
00185        LDAPDebug( LDAP_DEBUG_TRACE, "nsldapi_url_parse(%s)\n", url, 0, 0 );
00186 
00187        if ( url == NULL || ludpp == NULL ) {
00188               return( LDAP_URL_ERR_PARAM );
00189        }
00190 
00191        *ludpp = NULL;       /* pessimistic */
00192 
00193        if ( !skip_url_prefix( &url, &enclosed, &secure )) {
00194               return( LDAP_URL_ERR_NOTLDAP );
00195        }
00196 
00197        /* allocate return struct */
00198        if (( ludp = (LDAPURLDesc *)NSLDAPI_CALLOC( 1, sizeof( LDAPURLDesc )))
00199            == NULLLDAPURLDESC ) {
00200               return( LDAP_URL_ERR_MEM );
00201        }
00202 
00203        if ( secure ) {
00204               ludp->lud_options |= LDAP_URL_OPT_SECURE;
00205        }
00206 
00207        /* make working copy of the remainder of the URL */
00208        if (( urlcopy = nsldapi_strdup( url )) == NULL ) {
00209               ldap_free_urldesc( ludp );
00210               return( LDAP_URL_ERR_MEM );
00211        }
00212 
00213        if ( enclosed && *((p = urlcopy + strlen( urlcopy ) - 1)) == '>' ) {
00214               *p = '\0';
00215        }
00216 
00217        /* initialize scope and filter */
00218        ludp->lud_scope = -1;
00219        ludp->lud_filter = NULL;
00220 
00221        /* lud_string is the only malloc'd string space we use */
00222        ludp->lud_string = urlcopy;
00223 
00224        /* scan forward for '/' that marks end of hostport and begin. of dn */
00225        if (( ludp->lud_dn = strchr( urlcopy, '/' )) == NULL ) {
00226               if ( dn_required ) {
00227                      ldap_free_urldesc( ludp );
00228                      return( LDAP_URL_ERR_NODN );
00229               }
00230        } else {
00231               /* terminate hostport; point to start of dn */
00232               *ludp->lud_dn++ = '\0';
00233        }
00234 
00235 
00236        if ( *urlcopy == '\0' ) {
00237               ludp->lud_host = NULL;
00238        } else {
00239               ludp->lud_host = urlcopy;
00240               nsldapi_hex_unescape( ludp->lud_host );
00241 
00242               /*
00243                * Locate and strip off optional port number (:#) in host
00244                * portion of URL.
00245                *
00246                * If more than one space-separated host is listed, we only
00247                * look for a port number within the right-most one since
00248                * ldap_init() will handle host parameters that look like
00249                * host:port anyway.
00250                */
00251               if (( p = strrchr( ludp->lud_host, ' ' )) == NULL ) {
00252                      p = ludp->lud_host;
00253               } else {
00254                      ++p;
00255               }
00256               if ( *p == '[' && ( q = strchr( p, ']' )) != NULL ) {
00257                       /* square brackets present -- skip past them */
00258                      p = q++;
00259               }
00260               if (( p = strchr( p, ':' )) != NULL ) {
00261                      *p++ = '\0';
00262                      ludp->lud_port = atoi( p );
00263                      if ( *ludp->lud_host == '\0' ) {
00264                             ludp->lud_host = NULL;  /* no hostname */
00265                      }
00266               }
00267        }
00268 
00269        /* scan for '?' that marks end of dn and beginning of attributes */
00270        attrs = NULL;
00271        if ( ludp->lud_dn != NULL &&
00272            ( attrs = strchr( ludp->lud_dn, '?' )) != NULL ) {
00273               /* terminate dn; point to start of attrs. */
00274               *attrs++ = '\0';
00275 
00276               /* scan for '?' that marks end of attrs and begin. of scope */
00277               if (( p = strchr( attrs, '?' )) != NULL ) {
00278                      /*
00279                       * terminate attrs; point to start of scope and scan for
00280                       * '?' that marks end of scope and begin. of filter
00281                       */
00282                      *p++ = '\0';
00283                      scope = p;
00284 
00285                      if (( p = strchr( scope, '?' )) != NULL ) {
00286                             /* terminate scope; point to start of filter */
00287                             *p++ = '\0';
00288                             if ( *p != '\0' ) {
00289                                    ludp->lud_filter = p;
00290                                    /*
00291                                     * scan for the '?' that marks the end
00292                                     * of the filter and the start of any
00293                                     * extensions
00294                                     */
00295                                    if (( p = strchr( ludp->lud_filter, '?' ))
00296                                        != NULL ) {
00297                                           *p++ = '\0'; /* term. filter */
00298                                           extensions = p;
00299                                    }
00300                                    if ( *ludp->lud_filter == '\0' ) {
00301                                           ludp->lud_filter = NULL;
00302                                    } else {
00303                                           nsldapi_hex_unescape( ludp->lud_filter );
00304                                    }
00305                             }
00306                      }
00307 
00308                      if ( strcasecmp( scope, "one" ) == 0 ) {
00309                             ludp->lud_scope = LDAP_SCOPE_ONELEVEL;
00310                      } else if ( strcasecmp( scope, "base" ) == 0 ) {
00311                             ludp->lud_scope = LDAP_SCOPE_BASE;
00312                      } else if ( strcasecmp( scope, "sub" ) == 0 ) {
00313                             ludp->lud_scope = LDAP_SCOPE_SUBTREE;
00314                      } else if ( *scope != '\0' ) {
00315                             ldap_free_urldesc( ludp );
00316                             return( LDAP_URL_ERR_BADSCOPE );
00317                      }
00318               }
00319        }
00320 
00321        if ( ludp->lud_dn != NULL ) {
00322               nsldapi_hex_unescape( ludp->lud_dn );
00323        }
00324 
00325        /*
00326         * if attrs list was included, turn it into a null-terminated array
00327         */
00328        if ( attrs != NULL && *attrs != '\0' ) {
00329               nsldapi_hex_unescape( attrs );
00330               for ( nattrs = 1, p = attrs; *p != '\0'; ++p ) {
00331                   if ( *p == ',' ) {
00332                          ++nattrs;
00333                   }
00334               }
00335 
00336               if (( ludp->lud_attrs = (char **)NSLDAPI_CALLOC( nattrs + 1,
00337                   sizeof( char * ))) == NULL ) {
00338                      ldap_free_urldesc( ludp );
00339                      return( LDAP_URL_ERR_MEM );
00340               }
00341 
00342               for ( i = 0, p = attrs; i < nattrs; ++i ) {
00343                      ludp->lud_attrs[ i ] = p;
00344                      if (( p = strchr( p, ',' )) != NULL ) {
00345                             *p++ ='\0';
00346                      }
00347                      nsldapi_hex_unescape( ludp->lud_attrs[ i ] );
00348               }
00349        }
00350 
00351        /* if extensions list was included, check for critical ones */
00352        if ( extensions != NULL && *extensions != '\0' ) {
00353               /* Note: at present, we do not recognize ANY extensions */
00354               at_start = 1;
00355               for ( p = extensions; *p != '\0'; ++p ) {
00356                      if ( at_start ) {
00357                             if ( *p == '!' ) {   /* critical extension */
00358                                    ldap_free_urldesc( ludp );
00359                                    return( LDAP_URL_UNRECOGNIZED_CRITICAL_EXTENSION );
00360                             }
00361                             at_start = 0;
00362                      } else if ( *p == ',' ) {
00363                             at_start = 1;
00364                      }
00365               }
00366        }
00367 
00368        *ludpp = ludp;
00369 
00370        return( 0 );
00371 }
00372 
00373 
00374 void
00375 LDAP_CALL
00376 ldap_free_urldesc( LDAPURLDesc *ludp )
00377 {
00378        if ( ludp != NULLLDAPURLDESC ) {
00379               if ( ludp->lud_string != NULL ) {
00380                      NSLDAPI_FREE( ludp->lud_string );
00381               }
00382               if ( ludp->lud_attrs != NULL ) {
00383                      NSLDAPI_FREE( ludp->lud_attrs );
00384               }
00385               NSLDAPI_FREE( ludp );
00386        }
00387 }
00388 
00389 
00390 int
00391 LDAP_CALL
00392 ldap_url_search( LDAP *ld, const char *url, int attrsonly )
00393 {
00394        int           err, msgid;
00395        LDAPURLDesc   *ludp;
00396        BerElement    *ber;
00397        LDAPServer    *srv;
00398        char          *host;
00399 
00400        if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
00401               return( -1 );        /* punt */
00402        }
00403 
00404        if ( ldap_url_parse( url, &ludp ) != 0 ) {
00405               LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
00406               return( -1 );
00407        }
00408 
00409        LDAP_MUTEX_LOCK( ld, LDAP_MSGID_LOCK );
00410        msgid = ++ld->ld_msgid;
00411        LDAP_MUTEX_UNLOCK( ld, LDAP_MSGID_LOCK );
00412 
00413        if ( nsldapi_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
00414            ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
00415            -1, -1, msgid, &ber ) != LDAP_SUCCESS ) {
00416               return( -1 );
00417        }
00418 
00419        err = 0;
00420 
00421        if ( ludp->lud_host == NULL ) {
00422               host = ld->ld_defhost;
00423        } else {
00424               host = ludp->lud_host;
00425        }
00426 
00427        if (( srv = (LDAPServer *)NSLDAPI_CALLOC( 1, sizeof( LDAPServer )))
00428            == NULL || ( host != NULL &&
00429            ( srv->lsrv_host = nsldapi_strdup( host )) == NULL )) {
00430               if ( srv != NULL ) {
00431                      NSLDAPI_FREE( srv );
00432               }
00433               LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
00434               err = -1;
00435        } else {
00436               if ( ludp->lud_port == 0 ) {
00437                      if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) == 0 ) {
00438                             srv->lsrv_port = LDAP_PORT;
00439                      } else {
00440                             srv->lsrv_port = LDAPS_PORT;
00441                      }
00442               } else {
00443                       srv->lsrv_port = ludp->lud_port;
00444               }
00445        }
00446 
00447        if (( ludp->lud_options & LDAP_URL_OPT_SECURE ) != 0 ) {
00448               srv->lsrv_options |= LDAP_SRV_OPT_SECURE;
00449        }
00450 
00451        if ( err != 0 ) {
00452               ber_free( ber, 1 );
00453        } else {
00454               err = nsldapi_send_server_request( ld, ber, msgid, NULL, srv,
00455                   NULL, NULL, 1 );
00456        }
00457 
00458        ldap_free_urldesc( ludp );
00459        return( err );
00460 }
00461 
00462 
00463 int
00464 LDAP_CALL
00465 ldap_url_search_st( LDAP *ld, const char *url, int attrsonly,
00466        struct timeval *timeout, LDAPMessage **res )
00467 {
00468        int    msgid;
00469 
00470        /*
00471         * It is an error to pass in a zero'd timeval.
00472         */
00473        if ( timeout != NULL && timeout->tv_sec == 0 &&
00474            timeout->tv_usec == 0 ) {
00475               if ( ld != NULL ) {
00476                      LDAP_SET_LDERRNO( ld, LDAP_PARAM_ERROR, NULL, NULL );
00477               }
00478               if ( res != NULL ) {
00479                      *res = NULL;
00480               }
00481                 return( LDAP_PARAM_ERROR );
00482         }
00483 
00484        if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
00485               return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
00486        }
00487 
00488        if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
00489               return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
00490        }
00491 
00492        if ( LDAP_GET_LDERRNO( ld, NULL, NULL ) == LDAP_TIMEOUT ) {
00493               (void) ldap_abandon( ld, msgid );
00494               LDAP_SET_LDERRNO( ld, LDAP_TIMEOUT, NULL, NULL );
00495               return( LDAP_TIMEOUT );
00496        }
00497 
00498        return( ldap_result2error( ld, *res, 0 ));
00499 }
00500 
00501 
00502 int
00503 LDAP_CALL
00504 ldap_url_search_s( LDAP *ld, const char *url, int attrsonly, LDAPMessage **res )
00505 {
00506        int    msgid;
00507 
00508        if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
00509               return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
00510        }
00511 
00512        if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
00513               return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
00514        }
00515 
00516        return( ldap_result2error( ld, *res, 0 ));
00517 }