home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / ldapsdk.zip / libraries / libldap / url.c < prev    next >
C/C++ Source or Header  |  2001-04-13  |  20KB  |  1,015 lines

  1. /* $OpenLDAP: pkg/ldap/libraries/libldap/url.c,v 1.29.2.12 2001/04/12 22:02:18 kurt Exp $ */
  2. /*
  3.  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
  4.  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
  5.  */
  6. /*  Portions
  7.  *  Copyright (c) 1996 Regents of the University of Michigan.
  8.  *  All rights reserved.
  9.  *
  10.  *  LIBLDAP url.c -- LDAP URL (RFC 2255) related routines
  11.  *
  12.  *  LDAP URLs look like this:
  13.  *    ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
  14.  *
  15.  *  where:
  16.  *   attributes is a comma separated list
  17.  *   scope is one of these three strings:  base one sub (default=base)
  18.  *   filter is an string-represented filter as in RFC 2254
  19.  *
  20.  *  e.g.,  ldap://host:port/dc=com?o,cn?base?o=openldap?extension
  21.  *
  22.  *  We also tolerate URLs that look like: <ldapurl> and <URL:ldapurl>
  23.  */
  24.  
  25. #include "portable.h"
  26.  
  27. #include <stdio.h>
  28.  
  29. #include <ac/stdlib.h>
  30.  
  31. #include <ac/socket.h>
  32. #include <ac/string.h>
  33. #include <ac/time.h>
  34.  
  35. #include "ldap-int.h"
  36.  
  37.  
  38. /* local functions */
  39. static const char* skip_url_prefix LDAP_P((
  40.     const char *url,
  41.     int *enclosedp,
  42.     const char **scheme ));
  43.  
  44. int ldap_pvt_url_scheme2proto( const char *scheme )
  45. {
  46.     assert( scheme );
  47.  
  48.     if( scheme == NULL ) {
  49.         return -1;
  50.     }
  51.  
  52.     if( strcmp("ldap", scheme) == 0 ) {
  53.         return LDAP_PROTO_TCP;
  54.     }
  55.  
  56.     if( strcmp("ldapi", scheme) == 0 ) {
  57.         return LDAP_PROTO_IPC;
  58.     }
  59.  
  60.     if( strcmp("ldaps", scheme) == 0 ) {
  61.         return LDAP_PROTO_TCP;
  62.     }
  63.  
  64.     return -1;
  65. }
  66.  
  67. int ldap_pvt_url_scheme2tls( const char *scheme )
  68. {
  69.     assert( scheme );
  70.  
  71.     if( scheme == NULL ) {
  72.         return -1;
  73.     }
  74.  
  75.     return strcmp("ldaps", scheme) == 0;
  76. }
  77.  
  78. int
  79. ldap_is_ldap_url( LDAP_CONST char *url )
  80. {
  81.     int    enclosed;
  82.     const char * scheme;
  83.  
  84.     if( url == NULL ) {
  85.         return 0;
  86.     }
  87.  
  88.     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
  89.         return 0;
  90.     }
  91.  
  92.     return 1;
  93. }
  94.  
  95. int
  96. ldap_is_ldaps_url( LDAP_CONST char *url )
  97. {
  98.     int    enclosed;
  99.     const char * scheme;
  100.  
  101.     if( url == NULL ) {
  102.         return 0;
  103.     }
  104.  
  105.     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
  106.         return 0;
  107.     }
  108.  
  109.     return strcmp(scheme, "ldaps") == 0;
  110. }
  111.  
  112. int
  113. ldap_is_ldapi_url( LDAP_CONST char *url )
  114. {
  115.     int    enclosed;
  116.     const char * scheme;
  117.  
  118.     if( url == NULL ) {
  119.         return 0;
  120.     }
  121.  
  122.     if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
  123.         return 0;
  124.     }
  125.  
  126.     return strcmp(scheme, "ldapi") == 0;
  127. }
  128.  
  129. static const char*
  130. skip_url_prefix(
  131.     const char *url,
  132.     int *enclosedp,
  133.     const char **scheme )
  134. {
  135. /*
  136.  * return non-zero if this looks like a LDAP URL; zero if not
  137.  * if non-zero returned, *urlp will be moved past "ldap://" part of URL
  138.  */
  139.     const char *p;
  140.  
  141.     if ( url == NULL ) {
  142.         return( NULL );
  143.     }
  144.  
  145.     p = url;
  146.  
  147.     /* skip leading '<' (if any) */
  148.     if ( *p == '<' ) {
  149.         *enclosedp = 1;
  150.         ++p;
  151.     } else {
  152.         *enclosedp = 0;
  153.     }
  154.  
  155.     /* skip leading "URL:" (if any) */
  156.     if ( strncasecmp( p, LDAP_URL_URLCOLON, LDAP_URL_URLCOLON_LEN ) == 0 ) {
  157.         p += LDAP_URL_URLCOLON_LEN;
  158.     }
  159.  
  160.     /* check for "ldap://" prefix */
  161.     if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
  162.         /* skip over "ldap://" prefix and return success */
  163.         p += LDAP_URL_PREFIX_LEN;
  164.         *scheme = "ldap";
  165.         return( p );
  166.     }
  167.  
  168.     /* check for "ldaps://" prefix */
  169.     if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
  170.         /* skip over "ldaps://" prefix and return success */
  171.         p += LDAPS_URL_PREFIX_LEN;
  172.         *scheme = "ldaps";
  173.         return( p );
  174.     }
  175.  
  176.     /* check for "ldapi://" prefix */
  177.     if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
  178.         /* skip over "ldapi://" prefix and return success */
  179.         p += LDAPI_URL_PREFIX_LEN;
  180.         *scheme = "ldapi";
  181.         return( p );
  182.     }
  183.  
  184.     return( NULL );
  185. }
  186.  
  187.  
  188. static int str2scope( const char *p )
  189. {
  190.     if ( strcasecmp( p, "one" ) == 0 ) {
  191.         return LDAP_SCOPE_ONELEVEL;
  192.  
  193.     } else if ( strcasecmp( p, "onetree" ) == 0 ) {
  194.         return LDAP_SCOPE_ONELEVEL;
  195.  
  196.     } else if ( strcasecmp( p, "base" ) == 0 ) {
  197.         return LDAP_SCOPE_BASE;
  198.  
  199.     } else if ( strcasecmp( p, "sub" ) == 0 ) {
  200.         return LDAP_SCOPE_SUBTREE;
  201.  
  202.     } else if ( strcasecmp( p, "subtree" ) == 0 ) {
  203.         return LDAP_SCOPE_SUBTREE;
  204.     }
  205.  
  206.     return( -1 );
  207. }
  208.  
  209.  
  210. int
  211. ldap_url_parse_ext( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
  212. {
  213. /*
  214.  *  Pick apart the pieces of an LDAP URL.
  215.  */
  216.  
  217.     LDAPURLDesc    *ludp;
  218.     char    *p, *q, *r;
  219.     int        i, enclosed;
  220.     const char *scheme = NULL;
  221.     const char *url_tmp;
  222.     char *url;
  223.  
  224.     if( url_in == NULL || ludpp == NULL ) {
  225.         return LDAP_URL_ERR_PARAM;
  226.     }
  227.  
  228. #ifndef LDAP_INT_IN_KERNEL
  229.     /* Global options may not be created yet
  230.      * We can't test if the global options are initialized
  231.      * because a call to LDAP_INT_GLOBAL_OPT() will try to allocate
  232.      * the options and cause infinite recursion
  233.      */
  234.     Debug( LDAP_DEBUG_TRACE, "ldap_url_parse_ext(%s)\n", url_in, 0, 0 );
  235. #endif
  236.  
  237.     *ludpp = NULL;    /* pessimistic */
  238.  
  239.     url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
  240.  
  241.     if ( url_tmp == NULL ) {
  242.         return LDAP_URL_ERR_BADSCHEME;
  243.     }
  244.  
  245.     assert( scheme );
  246.  
  247.     /* make working copy of the remainder of the URL */
  248.     url = LDAP_STRDUP( url_tmp );
  249.     if ( url == NULL ) {
  250.         return LDAP_URL_ERR_MEM;
  251.     }
  252.  
  253.     if ( enclosed ) {
  254.         p = &url[strlen(url)-1];
  255.  
  256.         if( *p != '>' ) {
  257.             LDAP_FREE( url );
  258.             return LDAP_URL_ERR_BADENCLOSURE;
  259.         }
  260.  
  261.         *p = '\0';
  262.     }
  263.  
  264.     /* allocate return struct */
  265.     ludp = (LDAPURLDesc *)LDAP_CALLOC( 1, sizeof( LDAPURLDesc ));
  266.  
  267.     if ( ludp == NULL ) {
  268.         LDAP_FREE( url );
  269.         return LDAP_URL_ERR_MEM;
  270.     }
  271.  
  272.     ludp->lud_next = NULL;
  273.     ludp->lud_host = NULL;
  274.     ludp->lud_port = LDAP_PORT;
  275.     ludp->lud_dn = NULL;
  276.     ludp->lud_attrs = NULL;
  277.     ludp->lud_filter = NULL;
  278.     ludp->lud_scope = LDAP_SCOPE_DEFAULT;
  279.     ludp->lud_filter = NULL;
  280.     ludp->lud_exts = NULL;
  281.  
  282.     ludp->lud_scheme = LDAP_STRDUP( scheme );
  283.  
  284.     if ( ludp->lud_scheme == NULL ) {
  285.         LDAP_FREE( url );
  286.         ldap_free_urldesc( ludp );
  287.         return LDAP_URL_ERR_MEM;
  288.     }
  289.  
  290.     if( strcasecmp( ludp->lud_scheme, "ldaps" ) == 0 ) {
  291.         ludp->lud_port = LDAPS_PORT;
  292.     }
  293.  
  294.     /* scan forward for '/' that marks end of hostport and begin. of dn */
  295.     p = strchr( url, '/' );
  296.  
  297.     if( p != NULL ) {
  298.         /* terminate hostport; point to start of dn */
  299.         *p++ = '\0';
  300.     }
  301.  
  302.     /* IPv6 syntax with [ip address]:port */
  303.     if ( *url == '[' ) {
  304.         r = strchr( url, ']' );
  305.         if ( r == NULL ) {
  306.             LDAP_FREE( url );
  307.             ldap_free_urldesc( ludp );
  308.             return LDAP_URL_ERR_BADURL;
  309.         }
  310.         *r++ = '\0';
  311.         q = strchr( r, ':' );
  312.     } else {
  313.         q = strchr( url, ':' );
  314.     }
  315.  
  316.     if ( q != NULL ) {
  317.         *q++ = '\0';
  318.         ldap_pvt_hex_unescape( q );
  319.  
  320.         if( *q == '\0' ) {
  321.             LDAP_FREE( url );
  322.             ldap_free_urldesc( ludp );
  323.             return LDAP_URL_ERR_BADURL;
  324.         }
  325.  
  326.         ludp->lud_port = atoi( q );
  327.     }
  328.  
  329.     ldap_pvt_hex_unescape( url );
  330.  
  331.     /* If [ip address]:port syntax, url is [ip and we skip the [ */
  332.     ludp->lud_host = LDAP_STRDUP( url + ( *url == '[' ) );
  333.  
  334.     if( ludp->lud_host == NULL ) {
  335.         LDAP_FREE( url );
  336.         ldap_free_urldesc( ludp );
  337.         return LDAP_URL_ERR_MEM;
  338.     }
  339.  
  340.     /*
  341.      * Kludge.  ldap://111.222.333.444:389??cn=abc,o=company
  342.      *
  343.      * On early Novell releases, search references/referrals were returned
  344.      * in this format, i.e., the dn was kind of in the scope position,
  345.      * but the required slash is missing. The whole thing is illegal syntax,
  346.      * but we need to account for it. Fortunately it can't be confused with
  347.      * anything real.
  348.      */
  349.     if( (p == NULL) && (q != NULL) && ((q = strchr( q, '?')) != NULL)) {
  350.         q++;        
  351.         /* ? immediately followed by question */
  352.         if( *q == '?') {
  353.             q++;
  354.             if( *q != '\0' ) {
  355.                 /* parse dn part */
  356.                 ldap_pvt_hex_unescape( q );
  357.                 ludp->lud_dn = LDAP_STRDUP( q );
  358.             } else {
  359.                 ludp->lud_dn = LDAP_STRDUP( "" );
  360.             }
  361.  
  362.             if( ludp->lud_dn == NULL ) {
  363.                 LDAP_FREE( url );
  364.                 ldap_free_urldesc( ludp );
  365.                 return LDAP_URL_ERR_MEM;
  366.             }
  367.         }
  368.     }
  369.  
  370.     if( p == NULL ) {
  371.         LDAP_FREE( url );
  372.         *ludpp = ludp;
  373.         return LDAP_URL_SUCCESS;
  374.     }
  375.  
  376.     /* scan forward for '?' that may marks end of dn */
  377.     q = strchr( p, '?' );
  378.  
  379.     if( q != NULL ) {
  380.         /* terminate dn part */
  381.         *q++ = '\0';
  382.     }
  383.  
  384.     if( *p != '\0' ) {
  385.         /* parse dn part */
  386.         ldap_pvt_hex_unescape( p );
  387.         ludp->lud_dn = LDAP_STRDUP( p );
  388.     } else {
  389.         ludp->lud_dn = LDAP_STRDUP( "" );
  390.     }
  391.  
  392.     if( ludp->lud_dn == NULL ) {
  393.         LDAP_FREE( url );
  394.         ldap_free_urldesc( ludp );
  395.         return LDAP_URL_ERR_MEM;
  396.     }
  397.  
  398.     if( q == NULL ) {
  399.         /* no more */
  400.         LDAP_FREE( url );
  401.         *ludpp = ludp;
  402.         return LDAP_URL_SUCCESS;
  403.     }
  404.  
  405.     /* scan forward for '?' that may marks end of attributes */
  406.     p = q;
  407.     q = strchr( p, '?' );
  408.  
  409.     if( q != NULL ) {
  410.         /* terminate attributes part */
  411.         *q++ = '\0';
  412.     }
  413.  
  414.     if( *p != '\0' ) {
  415.         /* parse attributes */
  416.         ldap_pvt_hex_unescape( p );
  417.         ludp->lud_attrs = ldap_str2charray( p, "," );
  418.  
  419.         if( ludp->lud_attrs == NULL ) {
  420.             LDAP_FREE( url );
  421.             ldap_free_urldesc( ludp );
  422.             return LDAP_URL_ERR_BADATTRS;
  423.         }
  424.     }
  425.  
  426.     if ( q == NULL ) {
  427.         /* no more */
  428.         LDAP_FREE( url );
  429.         *ludpp = ludp;
  430.         return LDAP_URL_SUCCESS;
  431.     }
  432.  
  433.     /* scan forward for '?' that may marks end of scope */
  434.     p = q;
  435.     q = strchr( p, '?' );
  436.  
  437.     if( q != NULL ) {
  438.         /* terminate the scope part */
  439.         *q++ = '\0';
  440.     }
  441.  
  442.     if( *p != '\0' ) {
  443.         /* parse the scope */
  444.         ldap_pvt_hex_unescape( p );
  445.         ludp->lud_scope = str2scope( p );
  446.  
  447.         if( ludp->lud_scope == -1 ) {
  448.             LDAP_FREE( url );
  449.             ldap_free_urldesc( ludp );
  450.             return LDAP_URL_ERR_BADSCOPE;
  451.         }
  452.     }
  453.  
  454.     if ( q == NULL ) {
  455.         /* no more */
  456.         LDAP_FREE( url );
  457.         *ludpp = ludp;
  458.         return LDAP_URL_SUCCESS;
  459.     }
  460.  
  461.     /* scan forward for '?' that may marks end of filter */
  462.     p = q;
  463.     q = strchr( p, '?' );
  464.  
  465.     if( q != NULL ) {
  466.         /* terminate the filter part */
  467.         *q++ = '\0';
  468.     }
  469.  
  470.     if( *p != '\0' ) {
  471.         /* parse the filter */
  472.         ldap_pvt_hex_unescape( p );
  473.  
  474.         if( ! *p ) {
  475.             /* missing filter */
  476.             LDAP_FREE( url );
  477.             ldap_free_urldesc( ludp );
  478.             return LDAP_URL_ERR_BADFILTER;
  479.         }
  480.  
  481.         LDAP_FREE( ludp->lud_filter );
  482.         ludp->lud_filter = LDAP_STRDUP( p );
  483.  
  484.         if( ludp->lud_filter == NULL ) {
  485.             LDAP_FREE( url );
  486.             ldap_free_urldesc( ludp );
  487.             return LDAP_URL_ERR_MEM;
  488.         }
  489.     }
  490.  
  491.     if ( q == NULL ) {
  492.         /* no more */
  493.         LDAP_FREE( url );
  494.         *ludpp = ludp;
  495.         return LDAP_URL_SUCCESS;
  496.     }
  497.  
  498.     /* scan forward for '?' that may marks end of extensions */
  499.     p = q;
  500.     q = strchr( p, '?' );
  501.  
  502.     if( q != NULL ) {
  503.         /* extra '?' */
  504.         LDAP_FREE( url );
  505.         ldap_free_urldesc( ludp );
  506.         return LDAP_URL_ERR_BADURL;
  507.     }
  508.  
  509.     /* parse the extensions */
  510.     ludp->lud_exts = ldap_str2charray( p, "," );
  511.  
  512.     if( ludp->lud_exts == NULL ) {
  513.         LDAP_FREE( url );
  514.         ldap_free_urldesc( ludp );
  515.         return LDAP_URL_ERR_BADEXTS;
  516.     }
  517.  
  518.     for( i=0; ludp->lud_exts[i] != NULL; i++ ) {
  519.         ldap_pvt_hex_unescape( ludp->lud_exts[i] );
  520.  
  521.         if( *ludp->lud_exts[i] == '!' ) {
  522.             /* count the number of critical extensions */
  523.             ludp->lud_crit_exts++;
  524.         }
  525.     }
  526.  
  527.     if( i == 0 ) {
  528.         /* must have 1 or more */
  529.         LDAP_FREE( url );
  530.         ldap_free_urldesc( ludp );
  531.         return LDAP_URL_ERR_BADEXTS;
  532.     }
  533.  
  534.     /* no more */
  535.     *ludpp = ludp;
  536.     LDAP_FREE( url );
  537.     return LDAP_URL_SUCCESS;
  538. }
  539.  
  540. int
  541. ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
  542. {
  543.     int rc = ldap_url_parse_ext( url_in, ludpp );
  544.  
  545.     if( rc != LDAP_URL_SUCCESS ) {
  546.         return rc;
  547.     }
  548.  
  549.     if ((*ludpp)->lud_scope == LDAP_SCOPE_DEFAULT) {
  550.         (*ludpp)->lud_scope = LDAP_SCOPE_BASE;
  551.     }
  552.  
  553.     if ((*ludpp)->lud_host != NULL && *(*ludpp)->lud_host == '\0') {
  554.         LDAP_FREE( (*ludpp)->lud_host );
  555.         (*ludpp)->lud_host = NULL;
  556.     }
  557.  
  558.     return rc;
  559. }
  560.  
  561. LDAPURLDesc *
  562. ldap_url_dup ( LDAPURLDesc *ludp )
  563. {
  564.     LDAPURLDesc *dest;
  565.  
  566.     if ( ludp == NULL ) {
  567.         return NULL;
  568.     }
  569.  
  570.     dest = LDAP_MALLOC( sizeof(LDAPURLDesc) );
  571.     if (dest == NULL)
  572.         return NULL;
  573.     
  574.     *dest = *ludp;
  575.     dest->lud_scheme = NULL;
  576.     dest->lud_host = NULL;
  577.     dest->lud_dn = NULL;
  578.     dest->lud_filter = NULL;
  579.     dest->lud_attrs = NULL;
  580.     dest->lud_exts = NULL;
  581.     dest->lud_next = NULL;
  582.  
  583.     if ( ludp->lud_scheme != NULL ) {
  584.         dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
  585.         if (dest->lud_scheme == NULL) {
  586.             ldap_free_urldesc(dest);
  587.             return NULL;
  588.         }
  589.     }
  590.  
  591.     if ( ludp->lud_host != NULL ) {
  592.         dest->lud_host = LDAP_STRDUP( ludp->lud_host );
  593.         if (dest->lud_host == NULL) {
  594.             ldap_free_urldesc(dest);
  595.             return NULL;
  596.         }
  597.     }
  598.  
  599.     if ( ludp->lud_dn != NULL ) {
  600.         dest->lud_dn = LDAP_STRDUP( ludp->lud_dn );
  601.         if (dest->lud_dn == NULL) {
  602.             ldap_free_urldesc(dest);
  603.             return NULL;
  604.         }
  605.     }
  606.  
  607.     if ( ludp->lud_filter != NULL ) {
  608.         dest->lud_filter = LDAP_STRDUP( ludp->lud_filter );
  609.         if (dest->lud_filter == NULL) {
  610.             ldap_free_urldesc(dest);
  611.             return NULL;
  612.         }
  613.     }
  614.  
  615.     if ( ludp->lud_attrs != NULL ) {
  616.         dest->lud_attrs = ldap_charray_dup( ludp->lud_attrs );
  617.         if (dest->lud_attrs == NULL) {
  618.             ldap_free_urldesc(dest);
  619.             return NULL;
  620.         }
  621.     }
  622.  
  623.     if ( ludp->lud_exts != NULL ) {
  624.         dest->lud_exts = ldap_charray_dup( ludp->lud_exts );
  625.         if (dest->lud_exts == NULL) {
  626.             ldap_free_urldesc(dest);
  627.             return NULL;
  628.         }
  629.     }
  630.  
  631.     return dest;
  632. }
  633.  
  634. LDAPURLDesc *
  635. ldap_url_duplist (LDAPURLDesc *ludlist)
  636. {
  637.     LDAPURLDesc *dest, *tail, *ludp, *newludp;
  638.  
  639.     dest = NULL;
  640.     tail = NULL;
  641.     for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
  642.         newludp = ldap_url_dup(ludp);
  643.         if (newludp == NULL) {
  644.             ldap_free_urllist(dest);
  645.             return NULL;
  646.         }
  647.         if (tail == NULL)
  648.             dest = newludp;
  649.         else
  650.             tail->lud_next = newludp;
  651.         tail = newludp;
  652.     }
  653.     return dest;
  654. }
  655.  
  656. int
  657. ldap_url_parselist (LDAPURLDesc **ludlist, const char *url )
  658. {
  659.     int i, rc;
  660.     LDAPURLDesc *ludp;
  661.     char **urls;
  662.  
  663.     *ludlist = NULL;
  664.  
  665.     if (url == NULL)
  666.         return LDAP_PARAM_ERROR;
  667.  
  668.     urls = ldap_str2charray((char *)url, ", ");
  669.     if (urls == NULL)
  670.         return LDAP_NO_MEMORY;
  671.  
  672.     /* count the URLs... */
  673.     for (i = 0; urls[i] != NULL; i++) ;
  674.     /* ...and put them in the "stack" backward */
  675.     while (--i >= 0) {
  676.         rc = ldap_url_parse( urls[i], &ludp );
  677.         if ( rc != 0 ) {
  678.             ldap_charray_free(urls);
  679.             ldap_free_urllist(*ludlist);
  680.             *ludlist = NULL;
  681.             return rc;
  682.         }
  683.         ludp->lud_next = *ludlist;
  684.         *ludlist = ludp;
  685.     }
  686.     ldap_charray_free(urls);
  687.     return LDAP_SUCCESS;
  688. }
  689.  
  690. int
  691. ldap_url_parsehosts(
  692.     LDAPURLDesc **ludlist,
  693.     const char *hosts,
  694.     int port )
  695. {
  696.     int i;
  697.     LDAPURLDesc *ludp;
  698.     char **specs, *p;
  699.  
  700.     *ludlist = NULL;
  701.  
  702.     if (hosts == NULL)
  703.         return LDAP_PARAM_ERROR;
  704.  
  705.     specs = ldap_str2charray((char *)hosts, ", ");
  706.     if (specs == NULL)
  707.         return LDAP_NO_MEMORY;
  708.  
  709.     /* count the URLs... */
  710.     for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
  711.  
  712.     /* ...and put them in the "stack" backward */
  713.     while (--i >= 0) {
  714.         ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
  715.         if (ludp == NULL) {
  716.             ldap_charray_free(specs);
  717.             ldap_free_urllist(*ludlist);
  718.             *ludlist = NULL;
  719.             return LDAP_NO_MEMORY;
  720.         }
  721.         ludp->lud_port = port;
  722.         ludp->lud_host = specs[i];
  723.         specs[i] = NULL;
  724.         p = strchr(ludp->lud_host, ':');
  725.         if (p != NULL) {
  726.             /* more than one :, IPv6 address */
  727.             if ( strchr(p+1, ':') != NULL ) {
  728.                 /* allow [address] and [address]:port */
  729.                 if ( *ludp->lud_host == '[' ) {
  730.                     p = LDAP_STRDUP(ludp->lud_host+1);
  731.                     /* copied, make sure we free source later */
  732.                     specs[i] = ludp->lud_host;
  733.                     ludp->lud_host = p;
  734.                     p = strchr( ludp->lud_host, ']' );
  735.                     if ( p == NULL )
  736.                         return LDAP_PARAM_ERROR;
  737.                     *p++ = '\0';
  738.                     if ( *p != ':' ) {
  739.                         if ( *p != '\0' )
  740.                             return LDAP_PARAM_ERROR;
  741.                         p = NULL;
  742.                     }
  743.                 } else {
  744.                     p = NULL;
  745.                 }
  746.             }
  747.             if (p != NULL) {
  748.                 *p++ = 0;
  749.                 ldap_pvt_hex_unescape(p);
  750.                 ludp->lud_port = atoi(p);
  751.             }
  752.         }
  753.         ldap_pvt_hex_unescape(ludp->lud_host);
  754.         ludp->lud_scheme = LDAP_STRDUP("ldap");
  755.         ludp->lud_next = *ludlist;
  756.         *ludlist = ludp;
  757.     }
  758.  
  759.     /* this should be an array of NULLs now */
  760.     /* except entries starting with [ */
  761.     ldap_charray_free(specs);
  762.     return LDAP_SUCCESS;
  763. }
  764.  
  765. char *
  766. ldap_url_list2hosts (LDAPURLDesc *ludlist)
  767. {
  768.     LDAPURLDesc *ludp;
  769.     int size;
  770.     char *s, *p, buf[32];    /* big enough to hold a long decimal # (overkill) */
  771.  
  772.     if (ludlist == NULL)
  773.         return NULL;
  774.  
  775.     /* figure out how big the string is */
  776.     size = 1;    /* nul-term */
  777.     for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
  778.         size += strlen(ludp->lud_host) + 1;        /* host and space */
  779.         if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
  780.             size += 2;
  781.         if (ludp->lud_port != 0)
  782.             size += sprintf(buf, ":%d", ludp->lud_port);
  783.     }
  784.     s = LDAP_MALLOC(size);
  785.     if (s == NULL)
  786.         return NULL;
  787.  
  788.     p = s;
  789.     for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
  790.         if (strchr(ludp->lud_host, ':')) {
  791.             p += sprintf(p, "[%s]", ludp->lud_host);
  792.         } else {
  793.             strcpy(p, ludp->lud_host);
  794.             p += strlen(ludp->lud_host);
  795.         }
  796.         if (ludp->lud_port != 0)
  797.             p += sprintf(p, ":%d", ludp->lud_port);
  798.         *p++ = ' ';
  799.     }
  800.     if (p != s)
  801.         p--;    /* nuke that extra space */
  802.     *p = 0;
  803.     return s;
  804. }
  805.  
  806. char *
  807. ldap_url_list2urls(
  808.     LDAPURLDesc *ludlist )
  809. {
  810.     LDAPURLDesc *ludp;
  811.     int size;
  812.     char *s, *p, buf[32];    /* big enough to hold a long decimal # (overkill) */
  813.  
  814.     if (ludlist == NULL)
  815.         return NULL;
  816.  
  817.     /* figure out how big the string is */
  818.     size = 1;    /* nul-term */
  819.     for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
  820.         size += strlen(ludp->lud_scheme) + strlen(ludp->lud_host);
  821.         if (strchr(ludp->lud_host, ':'))        /* will add [ ] below */
  822.             size += 2;
  823.         size += sizeof(":/// ");
  824.  
  825.         if (ludp->lud_port != 0) {
  826.             size += sprintf(buf, ":%d", ludp->lud_port);
  827.         }
  828.     }
  829.  
  830.     s = LDAP_MALLOC(size);
  831.     if (s == NULL) {
  832.         return NULL;
  833.     }
  834.  
  835.     p = s;
  836.     for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
  837.         p += sprintf(p,
  838.                  strchr(ludp->lud_host, ':') ? "%s://[%s]" : "%s://%s",
  839.                  ludp->lud_scheme, ludp->lud_host);
  840.         if (ludp->lud_port != 0)
  841.             p += sprintf(p, ":%d", ludp->lud_port);
  842.         *p++ = '/';
  843.         *p++ = ' ';
  844.     }
  845.     if (p != s)
  846.         p--;    /* nuke that extra space */
  847.     *p = 0;
  848.     return s;
  849. }
  850.  
  851. void
  852. ldap_free_urllist( LDAPURLDesc *ludlist )
  853. {
  854.     LDAPURLDesc *ludp, *next;
  855.  
  856.     for (ludp = ludlist; ludp != NULL; ludp = next) {
  857.         next = ludp->lud_next;
  858.         ldap_free_urldesc(ludp);
  859.     }
  860. }
  861.  
  862. void
  863. ldap_free_urldesc( LDAPURLDesc *ludp )
  864. {
  865.     if ( ludp == NULL ) {
  866.         return;
  867.     }
  868.     
  869.     if ( ludp->lud_scheme != NULL ) {
  870.         LDAP_FREE( ludp->lud_scheme );
  871.     }
  872.  
  873.     if ( ludp->lud_host != NULL ) {
  874.         LDAP_FREE( ludp->lud_host );
  875.     }
  876.  
  877.     if ( ludp->lud_dn != NULL ) {
  878.         LDAP_FREE( ludp->lud_dn );
  879.     }
  880.  
  881.     if ( ludp->lud_filter != NULL ) {
  882.         LDAP_FREE( ludp->lud_filter);
  883.     }
  884.  
  885.     if ( ludp->lud_attrs != NULL ) {
  886.         LDAP_VFREE( ludp->lud_attrs );
  887.     }
  888.  
  889.     if ( ludp->lud_exts != NULL ) {
  890.         LDAP_VFREE( ludp->lud_exts );
  891.     }
  892.  
  893.     LDAP_FREE( ludp );
  894. }
  895.  
  896.  
  897.  
  898. int
  899. ldap_url_search( LDAP *ld, LDAP_CONST char *url, int attrsonly )
  900. {
  901.     int        err;
  902.     LDAPURLDesc    *ludp;
  903.     BerElement    *ber;
  904.     LDAPreqinfo  bind;
  905.  
  906.     assert( ld != NULL );
  907.     assert( LDAP_VALID( ld ) );
  908.  
  909.     if ( ldap_url_parse( url, &ludp ) != 0 ) {
  910.         ld->ld_errno = LDAP_PARAM_ERROR;
  911.         return( -1 );
  912.     }
  913.  
  914.     if( ludp->lud_crit_exts ) {
  915.         /* we don't support any extension (yet) */
  916.         ld->ld_errno = LDAP_NOT_SUPPORTED;
  917.         return( -1 );
  918.     }
  919.  
  920.     ber = ldap_build_search_req( ld, ludp->lud_dn, ludp->lud_scope,
  921.         ludp->lud_filter, ludp->lud_attrs, attrsonly, NULL, NULL,
  922.         -1, -1 );
  923.  
  924.     if ( ber == NULL ) {
  925.         err = -1;
  926.     } else {
  927.         bind.ri_request = LDAP_REQ_SEARCH;
  928.         bind.ri_msgid = ld->ld_msgid;
  929.         bind.ri_url = (char *)url;
  930.         err = ldap_send_server_request(
  931.                     ld, ber, ld->ld_msgid, NULL,
  932.                     NULL, NULL, &bind );
  933.     }
  934.  
  935.     ldap_free_urldesc( ludp );
  936.     return( err );
  937. }
  938.  
  939.  
  940. int
  941. ldap_url_search_st( LDAP *ld, LDAP_CONST char *url, int attrsonly,
  942.     struct timeval *timeout, LDAPMessage **res )
  943. {
  944.     int    msgid;
  945.  
  946.     if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
  947.         return( ld->ld_errno );
  948.     }
  949.  
  950.     if ( ldap_result( ld, msgid, 1, timeout, res ) == -1 ) {
  951.         return( ld->ld_errno );
  952.     }
  953.  
  954.     if ( ld->ld_errno == LDAP_TIMEOUT ) {
  955.         (void) ldap_abandon( ld, msgid );
  956.         ld->ld_errno = LDAP_TIMEOUT;
  957.         return( ld->ld_errno );
  958.     }
  959.  
  960.     return( ldap_result2error( ld, *res, 0 ));
  961. }
  962.  
  963.  
  964. int
  965. ldap_url_search_s(
  966.     LDAP *ld, LDAP_CONST char *url, int attrsonly, LDAPMessage **res )
  967. {
  968.     int    msgid;
  969.  
  970.     if (( msgid = ldap_url_search( ld, url, attrsonly )) == -1 ) {
  971.         return( ld->ld_errno );
  972.     }
  973.  
  974.     if ( ldap_result( ld, msgid, 1, (struct timeval *)NULL, res ) == -1 ) {
  975.         return( ld->ld_errno );
  976.     }
  977.  
  978.     return( ldap_result2error( ld, *res, 0 ));
  979. }
  980.  
  981.  
  982. void
  983. ldap_pvt_hex_unescape( char *s )
  984. {
  985.     /*
  986.      * Remove URL hex escapes from s... done in place.  The basic concept for
  987.      * this routine is borrowed from the WWW library HTUnEscape() routine.
  988.      */
  989.     char    *p;
  990.  
  991.     for ( p = s; *s != '\0'; ++s ) {
  992.         if ( *s == '%' ) {
  993.             if ( *++s != '\0' ) {
  994.                 *p = ldap_pvt_unhex( *s ) << 4;
  995.             }
  996.             if ( *++s != '\0' ) {
  997.                 *p++ += ldap_pvt_unhex( *s );
  998.             }
  999.         } else {
  1000.             *p++ = *s;
  1001.         }
  1002.     }
  1003.  
  1004.     *p = '\0';
  1005. }
  1006.  
  1007.  
  1008. int
  1009. ldap_pvt_unhex( int c )
  1010. {
  1011.     return( c >= '0' && c <= '9' ? c - '0'
  1012.         : c >= 'A' && c <= 'F' ? c - 'A' + 10
  1013.         : c - 'a' + 10 );
  1014. }
  1015.