1N/A/*
1N/A *
1N/A * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
1N/A * All rights reserved.
1N/A *
1N/A */
1N/A
1N/A#pragma ident "%Z%%M% %I% %E% SMI"
1N/A
1N/A/*
1N/A * Copyright (c) 1990 Regents of the University of Michigan.
1N/A * All rights reserved.
1N/A *
1N/A * search.c
1N/A */
1N/A
1N/A#ifndef lint
1N/Astatic char copyright[] = "@(#) Copyright (c) 1990 Regents of the University of Michigan.\nAll rights reserved.\n";
1N/A#endif
1N/A
1N/A#include <stdio.h>
1N/A#include <string.h>
1N/A#include <ctype.h>
1N/A#include <stdlib.h> /* free() for Solaris */
1N/A
1N/A#ifdef MACOS
1N/A#include <stdlib.h>
1N/A#include "macos.h"
1N/A#endif /* MACOS */
1N/A
1N/A#if defined(DOS) || defined(_WIN32)
1N/A#include "msdos.h"
1N/A#endif /* DOS */
1N/A
1N/A#if !defined(MACOS) && !defined(DOS) && !defined(_WIN32)
1N/A#include <sys/time.h>
1N/A#include <sys/types.h>
1N/A#include <sys/socket.h>
1N/A#endif
1N/A#include "lber.h"
1N/A#include "ldap.h"
1N/A#include "ldap-private.h"
1N/A#include "ldap-int.h"
1N/A
1N/A#ifdef NEEDPROTOS
1N/Astatic char *find_right_paren(char *s);
1N/Astatic char *put_complex_filter(BerElement *ber, char *str,
1N/A unsigned int tag, int not);
1N/Astatic int put_filter(BerElement *ber, char *str);
1N/Astatic int put_simple_filter(BerElement *ber, char *str);
1N/Astatic int put_substring_filter(BerElement *ber, char *type, char *str);
1N/Astatic int put_filter_list(BerElement *ber, char *str);
1N/Astatic char *star_search(char *str);
1N/Astatic int hex_char2int(char c);
1N/Astatic int decode_value(char *str);
1N/A#else
1N/Astatic char *find_right_paren();
1N/Astatic char *put_complex_filter();
1N/Astatic int put_filter();
1N/Astatic int put_simple_filter();
1N/Astatic int put_substring_filter();
1N/Astatic int put_filter_list();
1N/Astatic char *star_search();
1N/Astatic int hex_char2int();
1N/Astatic int decode_value();
1N/A#endif /* NEEDPROTOS */
1N/A
1N/A
1N/ABerElement *
1N/Aldap_build_search_req(LDAP *ld, char *base, int scope, char *filter,
1N/A char **attrs, int attrsonly, LDAPControl ** serverctrls,
1N/A struct timeval *timeoutp, int sizelimit)
1N/A{
1N/A BerElement *ber;
1N/A int err;
1N/A int theSizeLimit, theTimeLimit;
1N/A char *theFilter;
1N/A
1N/A /*
1N/A * Create the search request. It looks like this:
1N/A * SearchRequest := [APPLICATION 3] SEQUENCE {
1N/A * baseObject DistinguishedName,
1N/A * scope ENUMERATED {
1N/A * baseObject (0),
1N/A * singleLevel (1),
1N/A * wholeSubtree (2)
1N/A * },
1N/A * derefAliases ENUMERATED {
1N/A * neverDerefaliases (0),
1N/A * derefInSearching (1),
1N/A * derefFindingBaseObj (2),
1N/A * alwaysDerefAliases (3)
1N/A * },
1N/A * sizelimit INTEGER (0 .. 65535),
1N/A * timelimit INTEGER (0 .. 65535),
1N/A * attrsOnly BOOLEAN,
1N/A * filter Filter,
1N/A * attributes SEQUENCE OF AttributeType
1N/A * }
1N/A * wrapped in an ldap message.
1N/A */
1N/A
1N/A if (filter == NULL || *filter == '\0') {
1N/A ld->ld_errno = LDAP_PARAM_ERROR;
1N/A return (NULLBER);
1N/A }
1N/A
1N/A /* create a message to send */
1N/A if ((ber = alloc_ber_with_options(ld)) == NULLBER) {
1N/A return (NULLBER);
1N/A }
1N/A
1N/A if (base == NULL) {
1N/A base = "";
1N/A }
1N/A
1N/A if (timeoutp != NULL) {
1N/A if (timeoutp->tv_sec > 0) {
1N/A theTimeLimit = (int)(timeoutp->tv_sec +
1N/A (timeoutp->tv_usec / 1000000));
1N/A } else if (timeoutp->tv_usec > 0) {
1N/A theTimeLimit = 1; /* minimum we can express in LDAP */
1N/A } else {
1N/A theTimeLimit = 0; /* no limit */
1N/A }
1N/A } else {
1N/A theTimeLimit = ld->ld_timelimit;
1N/A }
1N/A
1N/A#ifdef CLDAP
1N/A if (ld->ld_sb.sb_naddr > 0) {
1N/A err = ber_printf(ber, "{ist{seeiib", ++ld->ld_msgid,
1N/A ld->ld_cldapdn, LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
1N/A sizelimit == -1 ? ld->ld_sizelimit : sizelimit, theTimeLimit,
1N/A attrsonly);
1N/A } else {
1N/A#endif /* CLDAP */
1N/A err = ber_printf(ber, "{it{seeiib", ++ld->ld_msgid,
1N/A LDAP_REQ_SEARCH, base, scope, ld->ld_deref,
1N/A sizelimit == -1 ? ld->ld_sizelimit : sizelimit,
1N/A theTimeLimit, attrsonly);
1N/A#ifdef CLDAP
1N/A }
1N/A#endif /* CLDAP */
1N/A
1N/A if (err == -1) {
1N/A ld->ld_errno = LDAP_ENCODING_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A
1N/A theFilter = filter;
1N/A while (*theFilter == ' ') theFilter++;
1N/A if ((*theFilter == '&') || (*theFilter == '|') || (*theFilter == '!')) {
1N/A char *ptr = theFilter;
1N/A theFilter = (char *)calloc(1, strlen(ptr) + 3);
1N/A sprintf(theFilter, "(%s)", ptr);
1N/A } else {
1N/A theFilter = strdup(filter);
1N/A }
1N/A err = put_filter(ber, theFilter);
1N/A free(theFilter);
1N/A
1N/A if (err == -1) {
1N/A ld->ld_errno = LDAP_FILTER_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A
1N/A if (ber_printf(ber, "{v}}", attrs) == -1) {
1N/A ld->ld_errno = LDAP_ENCODING_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A
1N/A /* LDAPv3 */
1N/A /* Code controls if any */
1N/A if (serverctrls && serverctrls[0]) {
1N/A if (ldap_controls_code(ber, serverctrls) != LDAP_SUCCESS) {
1N/A ld->ld_errno = LDAP_ENCODING_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A } else if (ld->ld_srvctrls && ld->ld_srvctrls[0]) {
1N/A /* Otherwise, is there any global server ctrls ? */
1N/A if (ldap_controls_code(ber, ld->ld_srvctrls) != LDAP_SUCCESS) {
1N/A ld->ld_errno = LDAP_ENCODING_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A }
1N/A
1N/A if (ber_printf(ber, "}") == -1) {
1N/A ld->ld_errno = LDAP_ENCODING_ERROR;
1N/A ber_free(ber, 1);
1N/A return (NULLBER);
1N/A }
1N/A
1N/A return (ber);
1N/A}
1N/A
1N/A/*
1N/A * ldap_search - initiate an ldap (and X.500) search operation. Parameters:
1N/A *
1N/A * ld LDAP descriptor
1N/A * base DN of the base object
1N/A * scope the search scope - one of LDAP_SCOPE_BASE,
1N/A * LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE
1N/A * filter a string containing the search filter
1N/A * (e.g., "(|(cn=bob)(sn=bob))")
1N/A * attrs list of attribute types to return for matches
1N/A * attrsonly 1 => attributes only 0 => attributes and values
1N/A *
1N/A * Example:
1N/A * char *attrs[] = { "mail", "title", 0 };
1N/A * msgid = ldap_search( ld, "c=us@o=UM", LDAP_SCOPE_SUBTREE, "cn~=bob",
1N/A * attrs, attrsonly );
1N/A */
1N/Aint
1N/Aldap_search(LDAP *ld, char *base, int scope, char *filter,
1N/A char **attrs, int attrsonly)
1N/A{
1N/A BerElement *ber;
1N/A
1N/A#if defined(SUN) && defined(_REENTRANT)
1N/A int rv;
1N/A
1N/A LOCK_LDAP(ld);
1N/A#endif
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 242, "ldap_search\n"),
1N/A 0, 0, 0);
1N/A
1N/A if ((ber = ldap_build_search_req(ld, base, scope, filter, attrs,
1N/A attrsonly, NULL, NULL, -1)) == NULLBER) {
1N/A#if defined(SUN) && defined(_REENTRANT)
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (-1);
1N/A }
1N/A
1N/A#ifndef NO_CACHE
1N/A if (ld->ld_cache != NULL) {
1N/A if (check_cache(ld, LDAP_REQ_SEARCH, ber) == 0) {
1N/A ber_free(ber, 1);
1N/A ld->ld_errno = LDAP_SUCCESS;
1N/A rv = ld->ld_msgid;
1N/A#ifdef _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (rv);
1N/A }
1N/A add_request_to_cache(ld, LDAP_REQ_SEARCH, ber);
1N/A }
1N/A#endif /* NO_CACHE */
1N/A
1N/A /* send the message */
1N/A rv = send_initial_request(ld, LDAP_REQ_SEARCH, base, ber);
1N/A#ifdef _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (rv);
1N/A}
1N/A
1N/A
1N/Astatic char *
1N/Afind_right_paren(char *s)
1N/A{
1N/A int balance, escape;
1N/A
1N/A balance = 1;
1N/A escape = 0;
1N/A while (*s && balance) {
1N/A if (escape == 0) {
1N/A if (*s == '(')
1N/A balance++;
1N/A else if (*s == ')')
1N/A balance--;
1N/A }
1N/A if (*s == '\\' && ! escape)
1N/A escape = 1;
1N/A else
1N/A escape = 0;
1N/A if (balance)
1N/A s++;
1N/A }
1N/A
1N/A return (*s ? s : NULL);
1N/A}
1N/A
1N/Astatic char *
1N/Aput_complex_filter(BerElement *ber, char *str, unsigned int tag, int not)
1N/A{
1N/A char *next;
1N/A
1N/A /*
1N/A * We have (x(filter)...) with str sitting on
1N/A * the x. We have to find the paren matching
1N/A * the one before the x and put the intervening
1N/A * filters by calling put_filter_list().
1N/A */
1N/A
1N/A /* put explicit tag */
1N/A if (ber_printf(ber, "t{", tag) == -1)
1N/A return (NULL);
1N/A/*
1N/A if (!not && ber_printf(ber, "{") == -1)
1N/A return (NULL);
1N/A*/
1N/A
1N/A str++;
1N/A if ((next = find_right_paren(str)) == NULL)
1N/A return (NULL);
1N/A
1N/A *next = '\0';
1N/A if (put_filter_list(ber, str) == -1)
1N/A return (NULL);
1N/A *next++ = ')';
1N/A
1N/A /* flush explicit tagged thang */
1N/A if (ber_printf(ber, "}") == -1)
1N/A return (NULL);
1N/A/*
1N/A if (!not && ber_printf(ber, "}") == -1)
1N/A return (NULL);
1N/A*/
1N/A
1N/A return (next);
1N/A}
1N/A
1N/Astatic int
1N/Aput_filter(BerElement *ber, char *str)
1N/A{
1N/A char *next, *tmp, *s, *d;
1N/A int parens, balance, escape;
1N/A int multipleparen = 0;
1N/A
1N/A /*
1N/A * A Filter looks like this:
1N/A * Filter ::= CHOICE {
1N/A * and [0] SET OF Filter,
1N/A * or [1] SET OF Filter,
1N/A * not [2] Filter,
1N/A * equalityMatch [3] AttributeValueAssertion,
1N/A * substrings [4] SubstringFilter,
1N/A * greaterOrEqual [5] AttributeValueAssertion,
1N/A * lessOrEqual [6] AttributeValueAssertion,
1N/A * present [7] AttributeType,
1N/A * approxMatch [8] AttributeValueAssertion,
1N/A * extensibleMatch [9] MatchingRuleAssertion
1N/A * }
1N/A *
1N/A * SubstringFilter ::= SEQUENCE {
1N/A * type AttributeType,
1N/A * SEQUENCE OF CHOICE {
1N/A * initial [0] IA5String,
1N/A * any [1] IA5String,
1N/A * final [2] IA5String
1N/A * }
1N/A * }
1N/A * MatchingRuleAssertion ::= SEQUENCE {
1N/A * matchingRule [1] MatchingRuleId OPTIONAL,
1N/A * type [2] AttributeDescription OPTIONAL,
1N/A * matchValue [3] AssertionValue,
1N/A * dnAttributes [4] BOOLEAN DEFAULT FALSE
1N/A * }
1N/A *
1N/A * Note: tags in a choice are always explicit
1N/A */
1N/A
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 243,
1N/A "put_filter \"%s\"\n"), str, 0, 0);
1N/A
1N/A parens = 0;
1N/A while (*str) {
1N/A switch (*str) {
1N/A case '(':
1N/A str++;
1N/A parens++;
1N/A switch (*str) {
1N/A case '&':
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
1N/A 244, "put_filter: AND\n"), 0, 0, 0);
1N/A
1N/A if ((str = put_complex_filter(ber, str,
1N/A LDAP_FILTER_AND, 0)) == NULL)
1N/A return (-1);
1N/A
1N/A parens--;
1N/A break;
1N/A
1N/A case '|':
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
1N/A 245, "put_filter: OR\n"), 0, 0, 0);
1N/A
1N/A if ((str = put_complex_filter(ber, str,
1N/A LDAP_FILTER_OR, 0)) == NULL)
1N/A return (-1);
1N/A
1N/A parens--;
1N/A break;
1N/A
1N/A case '!':
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
1N/A 246, "put_filter: NOT\n"), 0, 0, 0);
1N/A
1N/A if ((str = put_complex_filter(ber, str,
1N/A LDAP_FILTER_NOT, 1)) == NULL)
1N/A return (-1);
1N/A
1N/A parens--;
1N/A break;
1N/A
1N/A case '(':
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
1N/A 402, "put_filter: Double Parentheses\n"),
1N/A 0, 0, 0);
1N/A multipleparen++;
1N/A continue;
1N/A
1N/A default:
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1,
1N/A 247, "put_filter: simple\n"), 0, 0, 0);
1N/A
1N/A balance = 1;
1N/A escape = 0;
1N/A next = str;
1N/A while (*next && balance) {
1N/A if (escape == 0) {
1N/A if (*next == '(')
1N/A balance++;
1N/A else if (*next == ')')
1N/A balance--;
1N/A }
1N/A if (*next == '\\' && ! escape)
1N/A escape = 1;
1N/A else
1N/A escape = 0;
1N/A if (balance)
1N/A next++;
1N/A }
1N/A if (balance != 0)
1N/A return (-1);
1N/A
1N/A *next = '\0';
1N/A if (put_simple_filter(ber, str) == -1)
1N/A return (-1);
1N/A *next++ = ')';
1N/A str = next;
1N/A parens--;
1N/A break;
1N/A }
1N/A break;
1N/A
1N/A case ')':
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 248,
1N/A "put_filter: end\n"), 0, 0, 0);
1N/A if (multipleparen) {
1N/A multipleparen--;
1N/A } else {
1N/A if (ber_printf(ber, "]") == -1)
1N/A return (-1);
1N/A }
1N/A
1N/A str++;
1N/A parens--;
1N/A break;
1N/A
1N/A case ' ':
1N/A str++;
1N/A break;
1N/A
1N/A default: /* assume it's a simple type=value filter */
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 249,
1N/A "put_filter: default\n"), 0, 0, 0);
1N/A next = strchr(str, '\0');
1N/A if (put_simple_filter(ber, str) == -1) {
1N/A return (-1);
1N/A }
1N/A str = next;
1N/A break;
1N/A }
1N/A }
1N/A
1N/A return (parens ? -1 : 0);
1N/A}
1N/A
1N/A/*
1N/A * Put a list of filters like this "(filter1)(filter2)..."
1N/A */
1N/A
1N/Astatic int
1N/Aput_filter_list(BerElement *ber, char *str)
1N/A{
1N/A char *next;
1N/A char save;
1N/A
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 250,
1N/A "put_filter_list \"%s\"\n"), str, 0, 0);
1N/A
1N/A while (*str) {
1N/A while (*str && isspace(*str))
1N/A str++;
1N/A if (*str == '\0')
1N/A break;
1N/A
1N/A if ((next = find_right_paren(str + 1)) == NULL)
1N/A return (-1);
1N/A save = *++next;
1N/A
1N/A /* now we have "(filter)" with str pointing to it */
1N/A *next = '\0';
1N/A if (put_filter(ber, str) == -1)
1N/A return (-1);
1N/A *next = save;
1N/A
1N/A str = next;
1N/A }
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Aput_simple_filter(BerElement *ber, char *str)
1N/A{
1N/A char *s;
1N/A char *value, savechar;
1N/A unsigned int ftype;
1N/A int rc;
1N/A int len;
1N/A
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 251,
1N/A "put_simple_filter \"%s\"\n"), str, 0, 0);
1N/A
1N/A if ((s = strchr(str, '=')) == NULL)
1N/A return (-1);
1N/A value = s + 1;
1N/A *s-- = '\0';
1N/A savechar = *s;
1N/A
1N/A switch (*s) {
1N/A case '<':
1N/A ftype = LDAP_FILTER_LE;
1N/A *s = '\0';
1N/A break;
1N/A case '>':
1N/A ftype = LDAP_FILTER_GE;
1N/A *s = '\0';
1N/A break;
1N/A case '~':
1N/A ftype = LDAP_FILTER_APPROX;
1N/A *s = '\0';
1N/A break;
1N/A /* LDAP V3 : New extensible matching */
1N/A case ':':
1N/A rc = put_extensible_filter(ber, str, value);
1N/A *(value -1) = '=';
1N/A return (rc);
1N/A default:
1N/A if (star_search(value) == NULL) {
1N/A ftype = LDAP_FILTER_EQUALITY;
1N/A } else if (strcmp(value, "*") == 0) {
1N/A ftype = LDAP_FILTER_PRESENT;
1N/A } else {
1N/A rc = put_substring_filter(ber, str, value);
1N/A *(value-1) = '=';
1N/A return (rc);
1N/A }
1N/A break;
1N/A }
1N/A
1N/A if (*(value -1) == '=')
1N/A return (rc);
1N/A if (ftype == LDAP_FILTER_PRESENT) {
1N/A rc = ber_printf(ber, "ts", ftype, str);
1N/A } else {
1N/A if ((len = decode_value(value)) >= 0)
1N/A rc = ber_printf(ber, "t{so}", ftype, str, value, len);
1N/A }
1N/A
1N/A *s = savechar;
1N/A *(value-1) = '=';
1N/A return (rc == -1 ? rc : 0);
1N/A}
1N/A
1N/Astatic int
1N/Aput_substring_filter(BerElement *ber, char *type, char *val)
1N/A{
1N/A char *nextstar, gotstar = 0;
1N/A unsigned int ftype;
1N/A int len;
1N/A
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 252,
1N/A "put_substring_filter \"%1$s=%2$s\"\n"), type, val, 0);
1N/A
1N/A if (ber_printf(ber, "t{s{", LDAP_FILTER_SUBSTRINGS, type) == -1)
1N/A return (-1);
1N/A
1N/A while (val != NULL) {
1N/A if ((nextstar = star_search(val)) != NULL)
1N/A *nextstar++ = '\0';
1N/A
1N/A if (gotstar == 0) {
1N/A ftype = LDAP_SUBSTRING_INITIAL;
1N/A } else if (nextstar == NULL) {
1N/A ftype = LDAP_SUBSTRING_FINAL;
1N/A } else {
1N/A ftype = LDAP_SUBSTRING_ANY;
1N/A }
1N/A if (*val != '\0') {
1N/A if ((len = decode_value(val)) == -1 ||
1N/A ber_printf(ber, "to", ftype, val, len) == -1)
1N/A return (-1);
1N/A }
1N/A
1N/A gotstar = 1;
1N/A if (nextstar != NULL)
1N/A *(nextstar-1) = '*';
1N/A val = nextstar;
1N/A }
1N/A
1N/A if (ber_printf(ber, "}}") == -1)
1N/A return (-1);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Astatic int
1N/Aput_extensible_filter(BerElement *ber, char *type, char *val)
1N/A{
1N/A char *ptr, *ptype;
1N/A char *dn, *rule;
1N/A int len;
1N/A
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 252,
1N/A "put_extensible_filter \"%1$s=%2$s\"\n"), type, val, 0);
1N/A
1N/A /* type is off form : attr:dn:matchingrule: or :dn:matchingrule: */
1N/A /* type ends with ':', suppress it */
1N/A ptr = strdup(type);
1N/A ptype = ptr;
1N/A while (*ptype) {
1N/A *ptype = tolower(*ptype);
1N/A ptype++;
1N/A }
1N/A
1N/A len = strlen(ptr);
1N/A if (len > 0 && ptr[len -1] == ':')
1N/A ptr [len - 1] = '\0';
1N/A else {
1N/A return (-1);
1N/A }
1N/A
1N/A ptype = ptr;
1N/A /* Search first ':dn' */
1N/A if ((dn = strstr(ptype, ":dn")) == NULL) {
1N/A /* No dn */
1N/A /* if there's a : its separating type and matching rule */
1N/A rule = strchr(ptype, ':');
1N/A if (rule == ptype) {
1N/A ptype = NULL;
1N/A }
1N/A } else {
1N/A if (dn == ptype) {
1N/A ptype = NULL;
1N/A } else {
1N/A *dn = '\0';
1N/A }
1N/A
1N/A rule = dn + 3;
1N/A }
1N/A
1N/A if (rule && rule[0] == ':') {
1N/A rule[0] = '\0';
1N/A rule++;
1N/A } else {
1N/A rule = NULL;
1N/A }
1N/A
1N/A if ((ptype == NULL || *ptype == '\0') && rule == NULL) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A if (ber_printf(ber, "t{", LDAP_FILTER_EXTENSIBLE) == -1) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A if (rule && *rule && (ber_printf(ber, "ts",
1N/A LDAP_TAG_FEXT_RULE, rule) == -1)) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A if (ptype && *ptype && (ber_printf(ber, "ts",
1N/A LDAP_TAG_FEXT_TYPE, ptype) == -1)) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A /* Code value */
1N/A if ((len = decode_value(val)) == -1 ||
1N/A ber_printf(ber, "to", LDAP_TAG_FEXT_VAL, val, len) == -1) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A if (dn && (ber_printf(ber, "tb", LDAP_TAG_FEXT_DN, 1) == -1)) {
1N/A free(ptr);
1N/A return (-1);
1N/A }
1N/A
1N/A free(ptr);
1N/A
1N/A if (ber_printf(ber, "}") == -1)
1N/A return (-1);
1N/A
1N/A return (0);
1N/A}
1N/A
1N/Aint
1N/Aldap_search_st(LDAP *ld, char *base, int scope, char *filter, char **attrs,
1N/A int attrsonly, struct timeval *timeout, LDAPMessage **res)
1N/A{
1N/A int msgid;
1N/A
1N/A if ((msgid = ldap_search(ld, base, scope, filter, attrs, attrsonly))
1N/A == -1)
1N/A return (ld->ld_errno);
1N/A
1N/A if (ldap_result(ld, msgid, 1, timeout, res) == -1)
1N/A return (ld->ld_errno);
1N/A
1N/A if (ld->ld_errno == LDAP_TIMEOUT) {
1N/A (void) ldap_abandon(ld, msgid);
1N/A ld->ld_errno = LDAP_TIMEOUT;
1N/A return (ld->ld_errno);
1N/A }
1N/A
1N/A return (ldap_result2error(ld, *res, 0));
1N/A}
1N/A
1N/Aint
1N/Aldap_search_s(LDAP *ld, char *base, int scope, char *filter, char **attrs,
1N/A int attrsonly, LDAPMessage **res)
1N/A{
1N/A int msgid;
1N/A
1N/A if ((msgid = ldap_search(ld, base, scope, filter, attrs, attrsonly))
1N/A == -1)
1N/A return (ld->ld_errno);
1N/A
1N/A if (ldap_result(ld, msgid, 1, (struct timeval *)NULL, res) == -1)
1N/A return (ld->ld_errno);
1N/A
1N/A return (ldap_result2error(ld, *res, 0));
1N/A}
1N/A
1N/A/* LDAPv3 API EXTENSIONS */
1N/Aint ldap_search_ext(LDAP *ld, char *base, int scope, char *filter,
1N/A char **attrs, int attrsonly, LDAPControl **serverctrls,
1N/A LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit,
1N/A int *msgidp)
1N/A{
1N/A BerElement *ber;
1N/A int rv;
1N/A
1N/A if (timeoutp != NULL && timeoutp->tv_sec == 0 &&
1N/A timeoutp->tv_usec == 0) {
1N/A timeoutp = NULL;
1N/A }
1N/A
1N/A#ifdef _REENTRANT
1N/A LOCK_LDAP(ld);
1N/A#endif
1N/A Debug(LDAP_DEBUG_TRACE, catgets(slapdcat, 1, 242,
1N/A "ldap_search\n"), 0, 0, 0);
1N/A
1N/A if ((ber = ldap_build_search_req(ld, base, scope, filter, attrs,
1N/A attrsonly, serverctrls, timeoutp, sizelimit)) == NULLBER) {
1N/A rv = ld->ld_errno;
1N/A if (rv == LDAP_SUCCESS)
1N/A rv = LDAP_OTHER;
1N/A#ifdef _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (rv);
1N/A }
1N/A
1N/A#ifndef NO_CACHE
1N/A if (ld->ld_cache != NULL) {
1N/A if (check_cache(ld, LDAP_REQ_SEARCH, ber) == 0) {
1N/A ber_free(ber, 1);
1N/A ld->ld_errno = LDAP_SUCCESS;
1N/A *msgidp = ld->ld_msgid;
1N/A#ifdef _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (LDAP_SUCCESS);
1N/A }
1N/A add_request_to_cache(ld, LDAP_REQ_SEARCH, ber);
1N/A }
1N/A#endif /* NO_CACHE */
1N/A
1N/A /* send the message */
1N/A rv = send_initial_request(ld, LDAP_REQ_SEARCH, base, ber);
1N/A if (rv == -1) {
1N/A rv = ld->ld_errno;
1N/A if (rv == LDAP_SUCCESS) {
1N/A rv = LDAP_OTHER;
1N/A }
1N/A#ifdef _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (rv);
1N/A }
1N/A
1N/A *msgidp = rv;
1N/A#if _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (LDAP_SUCCESS);
1N/A}
1N/A
1N/A
1N/Aint ldap_search_ext_s(LDAP *ld, char *base, int scope, char *filter,
1N/A char **attrs, int attrsonly, LDAPControl **serverctrls,
1N/A LDAPControl **clientctrls, struct timeval *timeoutp, int sizelimit,
1N/A LDAPMessage **res)
1N/A{
1N/A int msgid;
1N/A int retcode = LDAP_SUCCESS;
1N/A
1N/A if ((retcode = ldap_search_ext(ld, base, scope, filter, attrs,
1N/A attrsonly, serverctrls, clientctrls, timeoutp, sizelimit,
1N/A &msgid)) != LDAP_SUCCESS)
1N/A return (retcode);
1N/A if (ldap_result(ld, msgid, 1, timeoutp, res) == -1)
1N/A return (ld->ld_errno);
1N/A
1N/A
1N/A#if _REENTRANT
1N/A LOCK_LDAP(ld);
1N/A#endif
1N/A retcode = ldap_parse_result(ld, *res, &ld->ld_errno, &ld->ld_matched,
1N/A &ld->ld_error, &ld->ld_referrals, &ld->ld_ret_ctrls, 0);
1N/A if (retcode == LDAP_SUCCESS)
1N/A retcode = ld->ld_errno;
1N/A#if _REENTRANT
1N/A UNLOCK_LDAP(ld);
1N/A#endif
1N/A return (retcode);
1N/A}
1N/A
1N/A/*
1N/A * Search string for ascii '*' (asterisk) character.
1N/A * RFC 1960 permits an escaped asterisk to pass through.
1N/A * RFC 2254 adds the definition of encoded characters:
1N/A *
1N/A * Character ASCII value
1N/A * ---------------------------
1N/A * * 0x2a
1N/A * ( 0x28
1N/A * ) 0x29
1N/A * \ 0x5c
1N/A * NUL 0x00
1N/A *
1N/A * No distinction of escaped characters is made here.
1N/A */
1N/Astatic char *
1N/Astar_search(char *str)
1N/A{
1N/A for (; *str; str++) {
1N/A switch (*str) {
1N/A case '*':
1N/A return (str);
1N/A case '\\':
1N/A if (str[1] == '\0')
1N/A break; /* input string exahausted */
1N/A ++str; /* Assume RFC 1960 escaped character */
1N/A /* Check for RFC 2254 hex encoding */
1N/A if (hex_char2int(str[0]) >= 0 &&
1N/A hex_char2int(str[1]) >= 0) {
1N/A str++; /* skip over RFC 2254 hex encoding */
1N/A }
1N/A default:
1N/A break;
1N/A }
1N/A }
1N/A return (NULL);
1N/A}
1N/A
1N/A/*
1N/A * Return integer value of hexadecimal character or (-1) if character is
1N/A * not a hexadecimal digit [0-9A-Fa-f].
1N/A */
1N/Astatic int
1N/Ahex_char2int(char c)
1N/A{
1N/A if (c >= '0' && c <= '9') {
1N/A return (c-'0');
1N/A } else if (c >= 'A' && c <= 'F') {
1N/A return (c-'A'+10);
1N/A } else if (c >= 'a' && c <= 'f') {
1N/A return (c-'a'+10);
1N/A }
1N/A return (-1);
1N/A}
1N/A
1N/A/*
1N/A * Modifys passed string converting escaped hexadecimal characters as
1N/A * per RFC 2254 and un-escapes escaped characters. Returns length of
1N/A * modified string as it may contain null characters as per RFC 2254.
1N/A */
1N/Astatic int
1N/Adecode_value(char *start)
1N/A{
1N/A char *read, *write;
1N/A int hn, ln;
1N/A
1N/A for (read = write = start; *read; *write++ = *read++) {
1N/A if (*read == '\\') {
1N/A if (*++read == '\0')
1N/A break; /* input string exahausted */
1N/A /*
1N/A * Assume *read is simple RFC 1960 escaped character.
1N/A * However check for RFC 2254 hex encoding.
1N/A */
1N/A if ((hn = hex_char2int(read[0])) >= 0 &&
1N/A (ln = hex_char2int(read[1])) >= 0) {
1N/A read++;
1N/A *read = (hn<<4)+ln;
1N/A }
1N/A }
1N/A }
1N/A *write = '\0';
1N/A return (write-start);
1N/A}
1N/A