s4:libcli/ldap: split out a ldap_decode_attribs_bare() function
[kai/samba.git] / source4 / libcli / ldap / ldap.c
index 5b22825da4b331f2482c378a60c8895560877260..7a65cc5c2746fad95cebc851d4a1240930a29488 100644 (file)
@@ -4,10 +4,12 @@
    
    Copyright (C) Andrew Tridgell  2004
    Copyright (C) Volker Lendecke 2004
+   Copyright (C) Stefan Metzmacher 2004
+   Copyright (C) Simo Sorce 2004
     
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
 */
 
 #include "includes.h"
+#include "../lib/util/asn1.h"
+#include "libcli/ldap/ldap.h"
+#include "libcli/ldap/ldap_proto.h"
 
-/****************************************************************************
- *
- * LDAP filter parser -- main routine is ldap_parse_filter
- *
- * Shamelessly stolen and adapted from ldb.
- *
- ***************************************************************************/
-
-/* Hmm. A blob might be more appropriate here :-) */
-
-struct ldap_val {
-       unsigned int length;
-       void *data;
-};
-
-enum ldap_parse_op {LDAP_OP_SIMPLE, LDAP_OP_AND, LDAP_OP_OR, LDAP_OP_NOT};
-
-struct ldap_parse_tree {
-       enum ldap_parse_op operation;
-       union {
-               struct {
-                       char *attr;
-                       struct ldap_val value;
-               } simple;
-               struct {
-                       unsigned int num_elements;
-                       struct ldap_parse_tree **elements;
-               } list;
-               struct {
-                       struct ldap_parse_tree *child;
-               } not;
-       } u;
-};
-
-#define LDAP_ALL_SEP "()&|=!"
-#define LDAP_CONNECTION_TIMEOUT 10000
 
-/*
-  return next token element. Caller frees
-*/
-static char *ldap_parse_lex(TALLOC_CTX *mem_ctx, const char **s,
-                          const char *sep)
-{
-       const char *p = *s;
-       char *ret;
-
-       while (isspace(*p)) {
-               p++;
-       }
-       *s = p;
-
-       if (*p == 0) {
-               return NULL;
-       }
-
-       if (strchr(sep, *p)) {
-               (*s) = p+1;
-               ret = talloc_strndup(mem_ctx, p, 1);
-               if (!ret) {
-                       errno = ENOMEM;
-               }
-               return ret;
-       }
-
-       while (*p && (isalnum(*p) || !strchr(sep, *p))) {
-               p++;
-       }
-
-       if (p == *s) {
-               return NULL;
-       }
-
-       ret = talloc_strndup(mem_ctx, *s, p - *s);
-       if (!ret) {
-               errno = ENOMEM;
-       }
-
-       *s = p;
-
-       return ret;
-}
-
-
-/*
-  find a matching close brace in a string
-*/
-static const char *match_brace(const char *s)
-{
-       unsigned int count = 0;
-       while (*s && (count != 0 || *s != ')')) {
-               if (*s == '(') {
-                       count++;
-               }
-               if (*s == ')') {
-                       count--;
-               }
-               s++;
-       }
-       if (! *s) {
-               return NULL;
-       }
-       return s;
-}
-
-static struct ldap_parse_tree *ldap_parse_filter(TALLOC_CTX *mem_ctx,
-                                              const char **s);
-
-/*
-  <simple> ::= <attributetype> <filtertype> <attributevalue>
-*/
-static struct ldap_parse_tree *ldap_parse_simple(TALLOC_CTX *mem_ctx,
-                                              const char *s)
-{
-       char *eq, *val, *l;
-       struct ldap_parse_tree *ret;
-
-       l = ldap_parse_lex(mem_ctx, &s, LDAP_ALL_SEP);
-       if (!l) {
-               return NULL;
-       }
-
-       if (strchr("()&|=", *l))
-               return NULL;
-
-       eq = ldap_parse_lex(mem_ctx, &s, LDAP_ALL_SEP);
-       if (!eq || strcmp(eq, "=") != 0)
-               return NULL;
-
-       val = ldap_parse_lex(mem_ctx, &s, ")");
-       if (val && strchr("()&|", *val))
-               return NULL;
-       
-       ret = talloc(mem_ctx, sizeof(*ret));
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = LDAP_OP_SIMPLE;
-       ret->u.simple.attr = l;
-       ret->u.simple.value.data = val;
-       ret->u.simple.value.length = val?strlen(val):0;
-
-       return ret;
-}
-
-
-/*
-  parse a filterlist
-  <and> ::= '&' <filterlist>
-  <or> ::= '|' <filterlist>
-  <filterlist> ::= <filter> | <filter> <filterlist>
-*/
-static struct ldap_parse_tree *ldap_parse_filterlist(TALLOC_CTX *mem_ctx,
-                                                  enum ldap_parse_op op,
-                                                  const char *s)
-{
-       struct ldap_parse_tree *ret, *next;
-
-       ret = talloc(mem_ctx, sizeof(*ret));
-
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = op;
-       ret->u.list.num_elements = 1;
-       ret->u.list.elements = talloc(mem_ctx, sizeof(*ret->u.list.elements));
-       if (!ret->u.list.elements) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->u.list.elements[0] = ldap_parse_filter(mem_ctx, &s);
-       if (!ret->u.list.elements[0]) {
-               return NULL;
-       }
-
-       while (isspace(*s)) s++;
-
-       while (*s && (next = ldap_parse_filter(mem_ctx, &s))) {
-               struct ldap_parse_tree **e;
-               e = talloc_realloc(mem_ctx, ret->u.list.elements,
-                                  sizeof(struct ldap_parse_tree) *
-                                  (ret->u.list.num_elements+1));
-               if (!e) {
-                       errno = ENOMEM;
-                       return NULL;
-               }
-               ret->u.list.elements = e;
-               ret->u.list.elements[ret->u.list.num_elements] = next;
-               ret->u.list.num_elements++;
-               while (isspace(*s)) s++;
-       }
-
-       return ret;
-}
-
-
-/*
-  <not> ::= '!' <filter>
-*/
-static struct ldap_parse_tree *ldap_parse_not(TALLOC_CTX *mem_ctx, const char *s)
-{
-       struct ldap_parse_tree *ret;
-
-       ret = talloc(mem_ctx, sizeof(*ret));
-       if (!ret) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret->operation = LDAP_OP_NOT;
-       ret->u.not.child = ldap_parse_filter(mem_ctx, &s);
-       if (!ret->u.not.child)
-               return NULL;
-
-       return ret;
-}
-
-/*
-  parse a filtercomp
-  <filtercomp> ::= <and> | <or> | <not> | <simple>
-*/
-static struct ldap_parse_tree *ldap_parse_filtercomp(TALLOC_CTX *mem_ctx,
-                                                  const char *s)
-{
-       while (isspace(*s)) s++;
-
-       switch (*s) {
-       case '&':
-               return ldap_parse_filterlist(mem_ctx, LDAP_OP_AND, s+1);
-
-       case '|':
-               return ldap_parse_filterlist(mem_ctx, LDAP_OP_OR, s+1);
-
-       case '!':
-               return ldap_parse_not(mem_ctx, s+1);
-
-       case '(':
-       case ')':
-               return NULL;
-       }
-
-       return ldap_parse_simple(mem_ctx, s);
-}
-
-
-/*
-  <filter> ::= '(' <filtercomp> ')'
-*/
-static struct ldap_parse_tree *ldap_parse_filter(TALLOC_CTX *mem_ctx,
-                                              const char **s)
-{
-       char *l, *s2;
-       const char *p, *p2;
-       struct ldap_parse_tree *ret;
-
-       l = ldap_parse_lex(mem_ctx, s, LDAP_ALL_SEP);
-       if (!l) {
-               return NULL;
-       }
-
-       if (strcmp(l, "(") != 0) {
-               return NULL;
-       }
-
-       p = match_brace(*s);
-       if (!p) {
-               return NULL;
-       }
-       p2 = p + 1;
-
-       s2 = talloc_strndup(mem_ctx, *s, p - *s);
-       if (!s2) {
-               errno = ENOMEM;
-               return NULL;
-       }
-
-       ret = ldap_parse_filtercomp(mem_ctx, s2);
-
-       *s = p2;
-
-       return ret;
-}
-
-/*
-  main parser entry point. Takes a search string and returns a parse tree
-
-  expression ::= <simple> | <filter>
-*/
-static struct ldap_parse_tree *ldap_parse_tree(TALLOC_CTX *mem_ctx, const char *s)
+static bool ldap_push_filter(struct asn1_data *data, struct ldb_parse_tree *tree)
 {
-       while (isspace(*s)) s++;
-
-       if (*s == '(') {
-               return ldap_parse_filter(mem_ctx, &s);
-       }
-
-       return ldap_parse_simple(mem_ctx, s);
-}
+       int i;
 
-static BOOL ldap_push_filter(ASN1_DATA *data, struct ldap_parse_tree *tree)
-{
        switch (tree->operation) {
-       case LDAP_OP_SIMPLE: {
-               if ((tree->u.simple.value.length == 1) &&
-                   (((char *)(tree->u.simple.value.data))[0] == '*')) {
-                       /* Just a presence test */
-                       asn1_push_tag(data, 0x87);
-                       asn1_write(data, tree->u.simple.attr,
-                                  strlen(tree->u.simple.attr));
-                       asn1_pop_tag(data);
-                       return !data->has_error;
+       case LDB_OP_AND:
+       case LDB_OP_OR:
+               asn1_push_tag(data, ASN1_CONTEXT(tree->operation==LDB_OP_AND?0:1));
+               for (i=0; i<tree->u.list.num_elements; i++) {
+                       if (!ldap_push_filter(data, tree->u.list.elements[i])) {
+                               return false;
+                       }
                }
-
-               /* Equality is all we currently do... */
-               asn1_push_tag(data, 0xa3);
-               asn1_write_OctetString(data, tree->u.simple.attr,
-                                     strlen(tree->u.simple.attr));
-               asn1_write_OctetString(data, tree->u.simple.value.data,
-                                     tree->u.simple.value.length);
                asn1_pop_tag(data);
                break;
-       }
 
-       case LDAP_OP_AND: {
-               int i;
-
-               asn1_push_tag(data, 0xa0);
-               for (i=0; i<tree->u.list.num_elements; i++) {
-                       ldap_push_filter(data, tree->u.list.elements[i]);
+       case LDB_OP_NOT:
+               asn1_push_tag(data, ASN1_CONTEXT(2));
+               if (!ldap_push_filter(data, tree->u.isnot.child)) {
+                       return false;
                }
                asn1_pop_tag(data);
                break;
-       }
-
-       case LDAP_OP_OR: {
-               int i;
 
-               asn1_push_tag(data, 0xa1);
-               for (i=0; i<tree->u.list.num_elements; i++) {
-                       ldap_push_filter(data, tree->u.list.elements[i]);
-               }
+       case LDB_OP_EQUALITY:
+               /* equality test */
+               asn1_push_tag(data, ASN1_CONTEXT(3));
+               asn1_write_OctetString(data, tree->u.equality.attr,
+                                     strlen(tree->u.equality.attr));
+               asn1_write_OctetString(data, tree->u.equality.value.data,
+                                     tree->u.equality.value.length);
                asn1_pop_tag(data);
                break;
-       }
-       default:
-               return False;
-       }
-       return !data->has_error;
-}
-
-/****************************************************************************
- *
- * LDIF parser
- *
- * Shamelessly stolen and adapted from Samba 4.
- *
- ***************************************************************************/
-
-/*
-  pull a ldif chunk, which is defined as a piece of data ending in \n\n or EOF
-  this routine removes any RFC2849 continuations and comments
-
-  caller frees
-*/
-static char *next_chunk(TALLOC_CTX *mem_ctx,
-                       int (*fgetc_fn)(void *), void *private_data)
-{
-       size_t alloc_size=0, chunk_size = 0;
-       char *chunk = NULL;
-       int c;
-       int in_comment = 0;
-
-       while ((c = fgetc_fn(private_data)) != EOF) {
-               if (chunk_size+1 >= alloc_size) {
-                       char *c2;
-                       alloc_size += 1024;
-                       c2 = talloc_realloc(mem_ctx, chunk, alloc_size);
-                       if (!c2) {
-                               errno = ENOMEM;
-                               return NULL;
-                       }
-                       chunk = c2;
-               }
-
-               if (in_comment) {
-                       if (c == '\n') {
-                               in_comment = 0;
-                       }
-                       continue;                       
-               }
-               
-               /* handle continuation lines - see RFC2849 */
-               if (c == ' ' && chunk_size > 1 &&
-                   chunk[chunk_size-1] == '\n') {
-                       chunk_size--;
-                       continue;
-               }
-               
-               /* chunks are terminated by a double line-feed */
-               if (c == '\n' && chunk_size > 0 &&
-                   chunk[chunk_size-1] == '\n') {
-                       chunk[chunk_size-1] = 0;
-                       return chunk;
-               }
-
-               if (c == '#' &&
-                   (chunk_size == 0 || chunk[chunk_size-1] == '\n')) {
-                       in_comment = 1;
-                       continue;
-               }
-
-               /* ignore leading blank lines */
-               if (chunk_size == 0 && c == '\n') {
-                       continue;
-               }
-
-               chunk[chunk_size++] = c;
-       }
-
-       if (chunk) {
-               chunk[chunk_size] = 0;
-       }
-
-       return chunk;
-}
-
-/* simple ldif attribute parser */
-static int next_attr(char **s, const char **attr, struct ldap_val *value)
-{
-       char *p;
-       int base64_encoded = 0;
-
-       if (strncmp(*s, "-\n", 2) == 0) {
-               value->length = 0;
-               *attr = "-";
-               *s += 2;
-               return 0;
-       }
-
-       p = strchr(*s, ':');
-       if (!p) {
-               return -1;
-       }
-
-       *p++ = 0;
-
-       if (*p == ':') {
-               base64_encoded = 1;
-               p++;
-       }
-
-       *attr = *s;
-
-       while (isspace(*p)) {
-               p++;
-       }
-
-       value->data = p;
-
-       p = strchr(p, '\n');
-
-       if (!p) {
-               value->length = strlen((char *)value->data);
-               *s = ((char *)value->data) + value->length;
-       } else {
-               value->length = p - (char *)value->data;
-               *s = p+1;
-               *p = 0;
-       }
-
-       if (base64_encoded) {
-               DATA_BLOB blob = base64_decode_data_blob(value->data);
-               memcpy(value->data, blob.data, blob.length);
-               value->length = blob.length;
-               ((char *)value->data)[value->length] = '\0';
-       }
 
-       return 0;
-}
-
-static BOOL add_value_to_attrib(TALLOC_CTX *mem_ctx, struct ldap_val *value,
-                               struct ldap_attribute *attrib)
-{
-       attrib->values = talloc_realloc(mem_ctx, attrib->values,
-                                       sizeof(*attrib->values) *
-                                       (attrib->num_values+1));
-       if (attrib->values == NULL)
-               return False;
-
-       attrib->values[attrib->num_values] =
-               data_blob_talloc(mem_ctx, value->data, value->length);
-       attrib->num_values += 1;
-       return True;
-}
-
-static BOOL fill_add_attributes(struct ldap_message *msg, char **chunk)
-{
-       struct ldap_AddRequest *r = &msg->r.AddRequest;
-       const char *attr_name;
-       struct ldap_val value;
-
-       r->num_attributes = 0;
-       r->attributes = NULL;
-
-       while (next_attr(chunk, &attr_name, &value) == 0) {
-               int i;
-               struct ldap_attribute *attrib = NULL;
-               
-               for (i=0; i<r->num_attributes; i++) {
-                       if (strequal(r->attributes[i].name, attr_name)) {
-                               attrib = &r->attributes[i];
-                               break;
-                       }
-               }
-
-               if (attrib == NULL) {
-                       r->attributes = talloc_realloc(msg->mem_ctx,
-                                                      r->attributes,
-                                                      sizeof(*r->attributes) *
-                                                      (r->num_attributes+1));
-                       if (r->attributes == NULL)
-                               return False;
-
-                       attrib = &(r->attributes[r->num_attributes]);
-                       r->num_attributes += 1;
-                       ZERO_STRUCTP(attrib);
-                       attrib->name = talloc_strdup(msg->mem_ctx,
-                                                    attr_name);
-               }
-
-               if (!add_value_to_attrib(msg->mem_ctx, &value, attrib))
-                       return False;
-       }
-       return True;
-}
-
-static BOOL add_mod_to_array_talloc(TALLOC_CTX *mem_ctx,
-                                   struct ldap_mod *mod,
-                                   struct ldap_mod **mods,
-                                   int *num_mods)
-{
-       *mods = talloc_realloc(mem_ctx, *mods,
-                              sizeof(**mods) * ((*num_mods)+1));
-
-       if (*mods == NULL)
-               return False;
-
-       (*mods)[*num_mods] = *mod;
-       *num_mods += 1;
-       return True;
-}
-
-static BOOL fill_mods(struct ldap_message *msg, char **chunk)
-{
-       struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
-       const char *attr_name;
-       struct ldap_val value;
-
-       r->num_mods = 0;
-       r->mods = NULL;
-
-       while (next_attr(chunk, &attr_name, &value) == 0) {
-
-               struct ldap_mod mod;
-               mod.type = LDAP_MODIFY_NONE;
-
-               mod.attrib.name = talloc_strdup(msg->mem_ctx, value.data);
-
-               if (strequal(attr_name, "add"))
-                       mod.type = LDAP_MODIFY_ADD;
-
-               if (strequal(attr_name, "delete"))
-                       mod.type = LDAP_MODIFY_DELETE;
-
-               if (strequal(attr_name, "replace"))
-                       mod.type = LDAP_MODIFY_REPLACE;
-
-               if (mod.type == LDAP_MODIFY_NONE) {
-                       DEBUG(2, ("ldif modification type %s unsupported\n",
-                                 attr_name));
-                       return False;
+       case LDB_OP_SUBSTRING:
+               /*
+                 SubstringFilter ::= SEQUENCE {
+                         type            AttributeDescription,
+                         -- at least one must be present
+                         substrings      SEQUENCE OF CHOICE {
+                                 initial [0] LDAPString,
+                                 any     [1] LDAPString,
+                                 final   [2] LDAPString } }
+               */
+               asn1_push_tag(data, ASN1_CONTEXT(4));
+               asn1_write_OctetString(data, tree->u.substring.attr, strlen(tree->u.substring.attr));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               i = 0;
+               if ( ! tree->u.substring.start_with_wildcard) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
+                       asn1_pop_tag(data);
+                       i++;
                }
-
-               mod.attrib.num_values = 0;
-               mod.attrib.values = NULL;
-
-               while (next_attr(chunk, &attr_name, &value) == 0) {
-                       if (strequal(attr_name, "-"))
-                               break;
-                       if (!strequal(attr_name, mod.attrib.name)) {
-                               DEBUG(3, ("attrib name %s does not "
-                                         "match %s\n", attr_name,
-                                         mod.attrib.name));
-                               return False;
-                       }
-                       if (!add_value_to_attrib(msg->mem_ctx, &value,
-                                                &mod.attrib)) {
-                               DEBUG(3, ("Could not add value\n"));
-                               return False;
+               while (tree->u.substring.chunks[i]) {
+                       int ctx;
+
+                       if (( ! tree->u.substring.chunks[i + 1]) &&
+                           (tree->u.substring.end_with_wildcard == 0)) {
+                               ctx = 2;
+                       } else {
+                               ctx = 1;
                        }
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(ctx));
+                       asn1_write_DATA_BLOB_LDAPString(data, tree->u.substring.chunks[i]);
+                       asn1_pop_tag(data);
+                       i++;
                }
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
+               break;
 
-               if (!add_mod_to_array_talloc(msg->mem_ctx, &mod, &r->mods,
-                                            &r->num_mods))
-                       return False;
-       }
-
-       return True;
-}
-
-/*
- read from a LDIF source, creating a ldap_message
-*/
-static struct ldap_message *ldif_read(int (*fgetc_fn)(void *),
-                                     void *private_data)
-{
-       struct ldap_message *msg;
-       const char *attr=NULL;
-       const char *dn;
-       char *chunk=NULL, *s;
-       struct ldap_val value;
-
-       value.data = NULL;
-
-       msg = new_ldap_message();
-       if (msg == NULL)
-               return NULL;
-
-       chunk = next_chunk(msg->mem_ctx, fgetc_fn, private_data);
-       if (!chunk) {
-               goto failed;
-       }
-
-       s = chunk;
-
-       if (next_attr(&s, &attr, &value) != 0) {
-               goto failed;
-       }
-       
-       /* first line must be a dn */
-       if (!strequal(attr, "dn")) {
-               DEBUG(5, ("Error: First line of ldif must be a dn not '%s'\n",
-                         attr));
-               goto failed;
-       }
+       case LDB_OP_GREATER:
+               /* greaterOrEqual test */
+               asn1_push_tag(data, ASN1_CONTEXT(5));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
+               asn1_pop_tag(data);
+               break;
 
-       dn = talloc_strdup(msg->mem_ctx, value.data);
-
-       if (next_attr(&s, &attr, &value) != 0) {
-               goto failed;
-       }
-
-       if (!strequal(attr, "changetype")) {
-               DEBUG(5, ("Error: Second line of ldif must be a changetype "
-                         "not '%s'\n",  attr));
-               goto failed;
-       }
-
-       if (strequal(value.data, "delete")) {
-               msg->type = LDAP_TAG_DelRequest;
-               msg->r.DelRequest.dn = dn;
-               return msg;
-       }
-
-       if (strequal(value.data, "add")) {
-               msg->type = LDAP_TAG_AddRequest;
-
-               msg->r.AddRequest.dn = dn;
-
-               if (!fill_add_attributes(msg, &s))
-                       goto failed;
-
-               return msg;
-       }
-
-       if (strequal(value.data, "modify")) {
-               msg->type = LDAP_TAG_ModifyRequest;
-
-               msg->r.ModifyRequest.dn = dn;
-
-               if (!fill_mods(msg, &s))
-                       goto failed;
-
-               return msg;
-       }
-
-       DEBUG(3, ("changetype %s not supported\n", (char *)value.data));
-
-failed:
-       destroy_ldap_message(msg);
-       return NULL;
-}
-
-/*
-  a wrapper around ldif_read() for reading from const char*
-*/
-struct ldif_read_string_state {
-       const char *s;
-};
-
-static int fgetc_string(void *private_data)
-{
-       struct ldif_read_string_state *state = private_data;
-       if (state->s[0] != 0) {
-               return *state->s++;
-       }
-       return EOF;
-}
-
-struct ldap_message *ldap_ldif2msg(const char *s)
-{
-       struct ldif_read_string_state state;
-       state.s = s;
-       return ldif_read(fgetc_string, &state);
-}
-
-static void ldap_encode_response(enum ldap_request_tag tag,
-                                struct ldap_Result *result,
-                                ASN1_DATA *data)
-{
-       asn1_push_tag(data, ASN1_APPLICATION(tag));
-       asn1_write_enumerated(data, result->resultcode);
-       asn1_write_OctetString(data, result->dn,
-                              (result->dn) ? strlen(result->dn) : 0);
-       asn1_write_OctetString(data, result->errormessage,
-                              (result->errormessage) ?
-                              strlen(result->errormessage) : 0);
-       if (result->referral != NULL)
-               asn1_write_OctetString(data, result->referral,
-                                      strlen(result->referral));
-       asn1_pop_tag(data);
-}
-
-BOOL ldap_encode(struct ldap_message *msg, DATA_BLOB *result)
-{
-       ASN1_DATA data;
-       int i, j;
-
-       ZERO_STRUCT(data);
-       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-       asn1_write_Integer(&data, msg->messageid);
-
-       switch (msg->type) {
-       case LDAP_TAG_BindRequest: {
-               struct ldap_BindRequest *r = &msg->r.BindRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_BindRequest));
-               asn1_write_Integer(&data, r->version);
-               asn1_write_OctetString(&data, r->dn,
-                                      (r->dn != NULL) ? strlen(r->dn) : 0);
-
-               switch (r->mechanism) {
-               case LDAP_AUTH_MECH_SIMPLE:
-                       /* context, primitive */
-                       asn1_push_tag(&data, r->mechanism | 0x80);
-                       asn1_write(&data, r->creds.password,
-                                  strlen(r->creds.password));
-                       asn1_pop_tag(&data);
-                       break;
-               case LDAP_AUTH_MECH_SASL:
-                       /* context, constructed */
-                       asn1_push_tag(&data, r->mechanism | 0xa0);
-                       asn1_write_OctetString(&data, r->creds.SASL.mechanism,
-                                              strlen(r->creds.SASL.mechanism));
-                       asn1_write_OctetString(&data, r->creds.SASL.secblob.data,
-                                              r->creds.SASL.secblob.length);
-                       asn1_pop_tag(&data);
-                       break;
-               default:
-                       return False;
-               }
-
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_BindResponse: {
-               struct ldap_BindResponse *r = &msg->r.BindResponse;
-               ldap_encode_response(msg->type, &r->response, &data);
-               break;
-       }
-       case LDAP_TAG_UnbindRequest: {
-/*             struct ldap_UnbindRequest *r = &msg->r.UnbindRequest; */
-               break;
-       }
-       case LDAP_TAG_SearchRequest: {
-               struct ldap_SearchRequest *r = &msg->r.SearchRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_SearchRequest));
-               asn1_write_OctetString(&data, r->basedn, strlen(r->basedn));
-               asn1_write_enumerated(&data, r->scope);
-               asn1_write_enumerated(&data, r->deref);
-               asn1_write_Integer(&data, r->sizelimit);
-               asn1_write_Integer(&data, r->timelimit);
-               asn1_write_BOOLEAN2(&data, r->attributesonly);
-
-               {
-                       TALLOC_CTX *mem_ctx = talloc_init("ldap_parse_tree");
-                       struct ldap_parse_tree *tree;
-
-                       if (mem_ctx == NULL)
-                               return False;
-
-                       tree = ldap_parse_tree(mem_ctx, r->filter);
-
-                       if (tree == NULL)
-                               return False;
-
-                       ldap_push_filter(&data, tree);
-
-                       talloc_destroy(mem_ctx);
-               }
-
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-               for (i=0; i<r->num_attributes; i++) {
-                       asn1_write_OctetString(&data, r->attributes[i],
-                                              strlen(r->attributes[i]));
-               }
-               asn1_pop_tag(&data);
-
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_SearchResultEntry: {
-               struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_SearchResultEntry));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-               for (i=0; i<r->num_attributes; i++) {
-                       struct ldap_attribute *attr = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attr->name,
-                                              strlen(attr->name));
-                       asn1_push_tag(&data, ASN1_SEQUENCE(1));
-                       for (j=0; j<attr->num_values; j++) {
-                               asn1_write_OctetString(&data,
-                                                      attr->values[j].data,
-                                                      attr->values[j].length);
-                       }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
-               }
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_SearchResultDone: {
-               struct ldap_Result *r = &msg->r.SearchResultDone;
-               ldap_encode_response(msg->type, r, &data);
-               break;
-       }
-       case LDAP_TAG_ModifyRequest: {
-               struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-
-               for (i=0; i<r->num_mods; i++) {
-                       struct ldap_attribute *attrib = &r->mods[i].attrib;
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_enumerated(&data, r->mods[i].type);
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attrib->name,
-                                              strlen(attrib->name));
-                       asn1_push_tag(&data, ASN1_SET);
-                       for (j=0; j<attrib->num_values; j++) {
-                               asn1_write_OctetString(&data,
-                                                      attrib->values[j].data,
-                                                      attrib->values[j].length);
-       
-                       }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
-               }
-               
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_ModifyResponse: {
-               struct ldap_Result *r = &msg->r.ModifyResponse;
-               ldap_encode_response(msg->type, r, &data);
-               break;
-       }
-       case LDAP_TAG_AddRequest: {
-               struct ldap_AddRequest *r = &msg->r.AddRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_AddRequest));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-
-               for (i=0; i<r->num_attributes; i++) {
-                       struct ldap_attribute *attrib = &r->attributes[i];
-                       asn1_push_tag(&data, ASN1_SEQUENCE(0));
-                       asn1_write_OctetString(&data, attrib->name,
-                                              strlen(attrib->name));
-                       asn1_push_tag(&data, ASN1_SET);
-                       for (j=0; j<r->attributes[i].num_values; j++) {
-                               asn1_write_OctetString(&data,
-                                                      attrib->values[j].data,
-                                                      attrib->values[j].length);
-                       }
-                       asn1_pop_tag(&data);
-                       asn1_pop_tag(&data);
-               }
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_AddResponse: {
-               struct ldap_Result *r = &msg->r.AddResponse;
-               ldap_encode_response(msg->type, r, &data);
-               break;
-       }
-       case LDAP_TAG_DelRequest: {
-               struct ldap_DelRequest *r = &msg->r.DelRequest;
-               asn1_push_tag(&data,
-                             ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
-               asn1_write(&data, r->dn, strlen(r->dn));
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_DelResponse: {
-               struct ldap_Result *r = &msg->r.DelResponse;
-               ldap_encode_response(msg->type, r, &data);
-               break;
-       }
-       case LDAP_TAG_ModifyDNRequest: {
-               struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest;
-               asn1_push_tag(&data,
-                             ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_write_OctetString(&data, r->newrdn, strlen(r->newrdn));
-               asn1_write_BOOLEAN2(&data, r->deleteolddn);
-               if (r->newsuperior != NULL) {
-                       asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
-                       asn1_write(&data, r->newsuperior,
-                                  strlen(r->newsuperior));
-                       asn1_pop_tag(&data);
-               }
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_ModifyDNResponse: {
-/*             struct ldap_Result *r = &msg->r.ModifyDNResponse; */
-               break;
-       }
-       case LDAP_TAG_CompareRequest: {
-               struct ldap_CompareRequest *r = &msg->r.CompareRequest;
-               asn1_push_tag(&data,
-                             ASN1_APPLICATION(LDAP_TAG_CompareRequest));
-               asn1_write_OctetString(&data, r->dn, strlen(r->dn));
-               asn1_push_tag(&data, ASN1_SEQUENCE(0));
-               asn1_write_OctetString(&data, r->attribute,
-                                      strlen(r->attribute));
-               asn1_write_OctetString(&data, r->value,
-                                      strlen(r->value));
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_CompareResponse: {
-/*             struct ldap_Result *r = &msg->r.CompareResponse; */
-               break;
-       }
-       case LDAP_TAG_AbandonRequest: {
-               struct ldap_AbandonRequest *r = &msg->r.AbandonRequest;
-               asn1_push_tag(&data,
-                             ASN1_APPLICATION_SIMPLE(LDAP_TAG_AbandonRequest));
-               asn1_write_Integer(&data, r->messageid);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_SearchResultReference: {
-/*             struct ldap_SearchResRef *r = &msg->r.SearchResultReference; */
-               break;
-       }
-       case LDAP_TAG_ExtendedRequest: {
-               struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest;
-               asn1_push_tag(&data, ASN1_APPLICATION(LDAP_TAG_ExtendedRequest));
-               asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(0));
-               asn1_write(&data, r->oid, strlen(r->oid));
-               asn1_pop_tag(&data);
-               asn1_push_tag(&data, ASN1_CONTEXT_SIMPLE(1));
-               asn1_write(&data, r->value.data, r->value.length);
-               asn1_pop_tag(&data);
-               asn1_pop_tag(&data);
-               break;
-       }
-       case LDAP_TAG_ExtendedResponse: {
-               struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
-               ldap_encode_response(msg->type, &r->response, &data);
-               break;
-       }
-       default:
-               return False;
-       }
-
-       asn1_pop_tag(&data);
-       *result = data_blob(data.data, data.length);
-       asn1_free(&data);
-       return True;
-}
-
-static const char *blob2string_talloc(TALLOC_CTX *mem_ctx,
-                                     DATA_BLOB blob)
-{
-       char *result = talloc(mem_ctx, blob.length+1);
-       memcpy(result, blob.data, blob.length);
-       result[blob.length] = '\0';
-       return result;
-}
-
-static BOOL asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx,
-                                        ASN1_DATA *data,
-                                        const char **result)
-{
-       DATA_BLOB string;
-       if (!asn1_read_OctetString(data, &string))
-               return False;
-       *result = blob2string_talloc(mem_ctx, string);
-       data_blob_free(&string);
-       return True;
-}
-
-static void ldap_decode_response(TALLOC_CTX *mem_ctx,
-                                ASN1_DATA *data,
-                                enum ldap_request_tag tag,
-                                struct ldap_Result *result)
-{
-       asn1_start_tag(data, ASN1_APPLICATION(tag));
-       asn1_read_enumerated(data, &result->resultcode);
-       asn1_read_OctetString_talloc(mem_ctx, data, &result->dn);
-       asn1_read_OctetString_talloc(mem_ctx, data, &result->errormessage);
-       if (asn1_peek_tag(data, ASN1_OCTET_STRING))
-               asn1_read_OctetString_talloc(mem_ctx, data, &result->referral);
-       else
-               result->referral = NULL;
-       asn1_end_tag(data);
-}
-
-/* read a octet string blob */
-static BOOL asn1_read_ContextSimple(ASN1_DATA *data, uint8_t num, DATA_BLOB *blob)
-{
-       int len;
-       ZERO_STRUCTP(blob);
-       if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(num))) return False;
-       len = asn1_tag_remaining(data);
-       if (len < 0) {
-               data->has_error = True;
-               return False;
-       }
-       *blob = data_blob(NULL, len);
-       asn1_read(data, blob->data, len);
-       asn1_end_tag(data);
-       return !data->has_error;
-}
-
-static void ldap_decode_BindResponse(TALLOC_CTX *mem_ctx,
-                                ASN1_DATA *data,
-                                enum ldap_request_tag tag,
-                                struct ldap_BindResponse *BindResp)
-{
-       asn1_start_tag(data, ASN1_APPLICATION(tag));
-       asn1_read_enumerated(data, &BindResp->response.resultcode);
-       asn1_read_OctetString_talloc(mem_ctx, data, &BindResp->response.dn);
-       asn1_read_OctetString_talloc(mem_ctx, data, &BindResp->response.errormessage);
-       if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(7))) {
-               DATA_BLOB tmp_blob = data_blob(NULL, 0);
-               asn1_read_ContextSimple(data, 7, &tmp_blob);
-               BindResp->SASL.secblob = data_blob_talloc(mem_ctx, tmp_blob.data, tmp_blob.length);
-               data_blob_free(&tmp_blob);
-       } else {
-               BindResp->SASL.secblob = data_blob(NULL, 0);
-       }
-       asn1_end_tag(data);
-}
-
-static BOOL add_attrib_to_array_talloc(TALLOC_CTX *mem_ctx,
-                                      const struct ldap_attribute *attrib,
-                                      struct ldap_attribute **attribs,
-                                      int *num_attribs)
-{
-       *attribs = talloc_realloc(mem_ctx, *attribs,
-                                 sizeof(**attribs) * (*num_attribs+1));
-
-       if (*attribs == NULL)
-               return False;
-
-       (*attribs)[*num_attribs] = *attrib;
-       *num_attribs += 1;
-       return True;
-}
-                                      
-static BOOL ldap_decode_filter(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
-                              char **filter)
-{
-       uint8 filter_tag, tag_desc;
-
-       if (!asn1_peek_uint8(data, &filter_tag))
-               return False;
-
-       tag_desc = filter_tag;
-       filter_tag &= 0x1f;     /* strip off the asn1 stuff */
-       tag_desc &= 0xe0;
-
-       switch(filter_tag) {
-       case 0: {
-               /* AND of one or more filters */
-               if (tag_desc != 0xa0) /* context compount */
-                       return False;
-
-               asn1_start_tag(data, ASN1_CONTEXT(0));
-
-               *filter = talloc_strdup(mem_ctx, "(&");
-               if (*filter == NULL)
-                       return False;
-
-               while (asn1_tag_remaining(data) > 0) {
-                       char *subfilter;
-                       if (!ldap_decode_filter(mem_ctx, data, &subfilter))
-                               return False;
-                       *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
-                                                 subfilter);
-                       if (*filter == NULL)
-                               return False;
-               }
-               asn1_end_tag(data);
-
-               *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
-               break;
-       }
-       case 1: {
-               /* OR of one or more filters */
-               if (tag_desc != 0xa0) /* context compount */
-                       return False;
-
-               asn1_start_tag(data, ASN1_CONTEXT(1));
-
-               *filter = talloc_strdup(mem_ctx, "(|");
-               if (*filter == NULL)
-                       return False;
-
-               while (asn1_tag_remaining(data) > 0) {
-                       char *subfilter;
-                       if (!ldap_decode_filter(mem_ctx, data, &subfilter))
-                               return False;
-                       *filter = talloc_asprintf(mem_ctx, "%s%s", *filter,
-                                                 subfilter);
-                       if (*filter == NULL)
-                               return False;
-               }
-
-               asn1_end_tag(data);
-
-               *filter = talloc_asprintf(mem_ctx, "%s)", *filter);
-               break;
-       }
-       case 3: {
-               /* equalityMatch */
-               const char *attrib, *value;
-               if (tag_desc != 0xa0) /* context compound */
-                       return False;
-               asn1_start_tag(data, ASN1_CONTEXT(3));
-               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
-               asn1_read_OctetString_talloc(mem_ctx, data, &value);
-               asn1_end_tag(data);
-               if ((data->has_error) || (attrib == NULL) || (value == NULL))
-                       return False;
-               *filter = talloc_asprintf(mem_ctx, "(%s=%s)", attrib, value);
+       case LDB_OP_LESS:
+               /* lessOrEqual test */
+               asn1_push_tag(data, ASN1_CONTEXT(6));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
+               asn1_pop_tag(data);
                break;
-       }
-       case 7: {
-               /* Normal presence, "attribute=*" */
-               int attr_len;
-               char *attr_name;
-               if (tag_desc != 0x80) /* context simple */
-                       return False;
-               if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(7)))
-                       return False;
-               attr_len = asn1_tag_remaining(data);
-               attr_name = malloc(attr_len+1);
-               if (attr_name == NULL)
-                       return False;
-               asn1_read(data, attr_name, attr_len);
-               attr_name[attr_len] = '\0';
-               *filter = talloc_asprintf(mem_ctx, "(%s=*)", attr_name);
-               SAFE_FREE(attr_name);
-               asn1_end_tag(data);
+
+       case LDB_OP_PRESENT:
+               /* present test */
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(7));
+               asn1_write_LDAPString(data, tree->u.present.attr);
+               asn1_pop_tag(data);
+               return !data->has_error;
+
+       case LDB_OP_APPROX:
+               /* approx test */
+               asn1_push_tag(data, ASN1_CONTEXT(8));
+               asn1_write_OctetString(data, tree->u.comparison.attr,
+                                     strlen(tree->u.comparison.attr));
+               asn1_write_OctetString(data, tree->u.comparison.value.data,
+                                     tree->u.comparison.value.length);
+               asn1_pop_tag(data);
                break;
-       }
-       default:
-               return False;
-       }
-       if (*filter == NULL)
-               return False;
-       return True;
-}
 
-static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
-                              struct ldap_attribute *attrib)
-{
-       asn1_start_tag(data, ASN1_SEQUENCE(0));
-       asn1_read_OctetString_talloc(mem_ctx, data, &attrib->name);
-       asn1_start_tag(data, ASN1_SET);
-       while (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
-               DATA_BLOB blob;
-               struct ldap_val value;
-               asn1_read_OctetString(data, &blob);
-               value.data = blob.data;
-               value.length = blob.length;
-               add_value_to_attrib(mem_ctx, &value, attrib);
-               data_blob_free(&blob);
+       case LDB_OP_EXTENDED:
+               /*
+                 MatchingRuleAssertion ::= SEQUENCE {
+                 matchingRule    [1] MatchingRuleID OPTIONAL,
+                 type            [2] AttributeDescription OPTIONAL,
+                 matchValue      [3] AssertionValue,
+                 dnAttributes    [4] BOOLEAN DEFAULT FALSE
+                 }
+               */
+               asn1_push_tag(data, ASN1_CONTEXT(9));
+               if (tree->u.extended.rule_id) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_write_LDAPString(data, tree->u.extended.rule_id);
+                       asn1_pop_tag(data);
+               }
+               if (tree->u.extended.attr) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(2));
+                       asn1_write_LDAPString(data, tree->u.extended.attr);
+                       asn1_pop_tag(data);
+               }
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(3));
+               asn1_write_DATA_BLOB_LDAPString(data, &tree->u.extended.value);
+               asn1_pop_tag(data);
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(4));
+               asn1_write_uint8(data, tree->u.extended.dnAttributes);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
+               break;
+
+       default:
+               return false;
        }
-       asn1_end_tag(data);
-       asn1_end_tag(data);
-       
+       return !data->has_error;
 }
 
-static void ldap_decode_attribs(TALLOC_CTX *mem_ctx, ASN1_DATA *data,
-                               struct ldap_attribute **attributes,
-                               int *num_attributes)
+static void ldap_encode_response(struct asn1_data *data, struct ldap_Result *result)
 {
-       asn1_start_tag(data, ASN1_SEQUENCE(0));
-       while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
-               struct ldap_attribute attrib;
-               ZERO_STRUCT(attrib);
-               ldap_decode_attrib(mem_ctx, data, &attrib);
-               add_attrib_to_array_talloc(mem_ctx, &attrib,
-                                          attributes, num_attributes);
+       asn1_write_enumerated(data, result->resultcode);
+       asn1_write_OctetString(data, result->dn,
+                              (result->dn) ? strlen(result->dn) : 0);
+       asn1_write_OctetString(data, result->errormessage,
+                              (result->errormessage) ?
+                              strlen(result->errormessage) : 0);
+       if (result->referral) {
+               asn1_push_tag(data, ASN1_CONTEXT(3));
+               asn1_write_OctetString(data, result->referral,
+                                      strlen(result->referral));
+               asn1_pop_tag(data);
        }
-       asn1_end_tag(data);
 }
 
-BOOL ldap_decode(ASN1_DATA *data, struct ldap_message *msg)
+_PUBLIC_ bool ldap_encode(struct ldap_message *msg, DATA_BLOB *result, TALLOC_CTX *mem_ctx)
 {
-       uint8 tag;
-
-       asn1_start_tag(data, ASN1_SEQUENCE(0));
-       asn1_read_Integer(data, &msg->messageid);
+       struct asn1_data *data = asn1_init(mem_ctx);
+       int i, j;
 
-       if (!asn1_peek_uint8(data, &tag))
-               return False;
+       if (!data) return false;
 
-       switch(tag) {
+       asn1_push_tag(data, ASN1_SEQUENCE(0));
+       asn1_write_Integer(data, msg->messageid);
 
-       case ASN1_APPLICATION(LDAP_TAG_BindRequest): {
+       switch (msg->type) {
+       case LDAP_TAG_BindRequest: {
                struct ldap_BindRequest *r = &msg->r.BindRequest;
-               msg->type = LDAP_TAG_BindRequest;
-               asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_BindRequest));
-               asn1_read_Integer(data, &r->version);
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-               if (asn1_peek_tag(data, 0x80)) {
-                       int pwlen;
-                       r->creds.password = "";
-                       /* Mechanism 0 (SIMPLE) */
-                       asn1_start_tag(data, 0x80);
-                       pwlen = asn1_tag_remaining(data);
-                       if (pwlen != 0) {
-                               char *pw = talloc(msg->mem_ctx, pwlen+1);
-                               asn1_read(data, pw, pwlen);
-                               pw[pwlen] = '\0';
-                               r->creds.password = pw;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_Integer(data, r->version);
+               asn1_write_OctetString(data, r->dn,
+                                      (r->dn != NULL) ? strlen(r->dn) : 0);
+
+               switch (r->mechanism) {
+               case LDAP_AUTH_MECH_SIMPLE:
+                       /* context, primitive */
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write(data, r->creds.password,
+                                  strlen(r->creds.password));
+                       asn1_pop_tag(data);
+                       break;
+               case LDAP_AUTH_MECH_SASL:
+                       /* context, constructed */
+                       asn1_push_tag(data, ASN1_CONTEXT(3));
+                       asn1_write_OctetString(data, r->creds.SASL.mechanism,
+                                              strlen(r->creds.SASL.mechanism));
+                       if (r->creds.SASL.secblob) {
+                               asn1_write_OctetString(data, r->creds.SASL.secblob->data,
+                                                      r->creds.SASL.secblob->length);
                        }
-                       asn1_end_tag(data);
+                       asn1_pop_tag(data);
+                       break;
+               default:
+                       return false;
                }
-               asn1_end_tag(data);
+
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_BindResponse): {
+       case LDAP_TAG_BindResponse: {
                struct ldap_BindResponse *r = &msg->r.BindResponse;
-               msg->type = LDAP_TAG_BindResponse;
-               ldap_decode_BindResponse(msg->mem_ctx,
-                                    data, LDAP_TAG_BindResponse,
-                                    r);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, &r->response);
+               if (r->SASL.secblob) {
+                       asn1_write_ContextSimple(data, 7, r->SASL.secblob);
+               }
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_UnbindRequest): {
-               msg->type = LDAP_TAG_UnbindRequest;
+       case LDAP_TAG_UnbindRequest: {
+/*             struct ldap_UnbindRequest *r = &msg->r.UnbindRequest; */
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_SearchRequest): {
+       case LDAP_TAG_SearchRequest: {
                struct ldap_SearchRequest *r = &msg->r.SearchRequest;
-               msg->type = LDAP_TAG_SearchRequest;
-               asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_SearchRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->basedn);
-               asn1_read_enumerated(data, (int *)&(r->scope));
-               asn1_read_enumerated(data, (int *)&(r->deref));
-               asn1_read_Integer(data, &r->sizelimit);
-               asn1_read_Integer(data, &r->timelimit);
-               asn1_read_BOOLEAN2(data, &r->attributesonly);
-
-               /* Maybe create a TALLOC_CTX for the filter? This can waste
-                * quite a bit of memory recursing down. */
-               ldap_decode_filter(msg->mem_ctx, data, &r->filter);
-
-               asn1_start_tag(data, ASN1_SEQUENCE(0));
-
-               r->num_attributes = 0;
-               r->attributes = NULL;
-
-               while (asn1_tag_remaining(data) > 0) {
-                       const char *attr;
-                       if (!asn1_read_OctetString_talloc(msg->mem_ctx, data,
-                                                         &attr))
-                               return False;
-                       if (!add_string_to_array(msg->mem_ctx, attr,
-                                                &r->attributes,
-                                                &r->num_attributes))
-                               return False;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->basedn, strlen(r->basedn));
+               asn1_write_enumerated(data, r->scope);
+               asn1_write_enumerated(data, r->deref);
+               asn1_write_Integer(data, r->sizelimit);
+               asn1_write_Integer(data, r->timelimit);
+               asn1_write_BOOLEAN(data, r->attributesonly);
+
+               if (!ldap_push_filter(data, r->tree)) {
+                       return false;
                }
 
-               asn1_end_tag(data);
-               asn1_end_tag(data);
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               for (i=0; i<r->num_attributes; i++) {
+                       asn1_write_OctetString(data, r->attributes[i],
+                                              strlen(r->attributes[i]));
+               }
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_SearchResultEntry): {
+       case LDAP_TAG_SearchResultEntry: {
                struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
-               msg->type = LDAP_TAG_SearchResultEntry;
-               r->attributes = NULL;
-               r->num_attributes = 0;
-               asn1_start_tag(data,
-                              ASN1_APPLICATION(LDAP_TAG_SearchResultEntry));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-               ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
-                                   &r->num_attributes);
-               asn1_end_tag(data);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               for (i=0; i<r->num_attributes; i++) {
+                       struct ldb_message_element *attr = &r->attributes[i];
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attr->name,
+                                              strlen(attr->name));
+                       asn1_push_tag(data, ASN1_SEQUENCE(1));
+                       for (j=0; j<attr->num_values; j++) {
+                               asn1_write_OctetString(data,
+                                                      attr->values[j].data,
+                                                      attr->values[j].length);
+                       }
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_SearchResultDone): {
+       case LDAP_TAG_SearchResultDone: {
                struct ldap_Result *r = &msg->r.SearchResultDone;
-               msg->type = LDAP_TAG_SearchResultDone;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_SearchResultDone, r);
-               break;
-       }
-
-       case ASN1_APPLICATION(LDAP_TAG_SearchResultReference): {
-/*             struct ldap_SearchResRef *r = &msg->r.SearchResultReference; */
-               msg->type = LDAP_TAG_SearchResultReference;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ModifyRequest): {
+       case LDAP_TAG_ModifyRequest: {
                struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
-               msg->type = LDAP_TAG_ModifyRequest;
-               asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-               asn1_start_tag(data, ASN1_SEQUENCE(0));
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
 
-               r->num_mods = 0;
-               r->mods = NULL;
-
-               while (asn1_tag_remaining(data) > 0) {
-                       struct ldap_mod mod;
-                       ZERO_STRUCT(mod);
-                       asn1_start_tag(data, ASN1_SEQUENCE(0));
-                       asn1_read_enumerated(data, &mod.type);
-                       ldap_decode_attrib(msg->mem_ctx, data, &mod.attrib);
-                       asn1_end_tag(data);
-                       if (!add_mod_to_array_talloc(msg->mem_ctx, &mod,
-                                                    &r->mods, &r->num_mods))
-                               break;
+               for (i=0; i<r->num_mods; i++) {
+                       struct ldb_message_element *attrib = &r->mods[i].attrib;
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_enumerated(data, r->mods[i].type);
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attrib->name,
+                                              strlen(attrib->name));
+                       asn1_push_tag(data, ASN1_SET);
+                       for (j=0; j<attrib->num_values; j++) {
+                               asn1_write_OctetString(data,
+                                                      attrib->values[j].data,
+                                                      attrib->values[j].length);
+       
+                       }
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
                }
-
-               asn1_end_tag(data);
-               asn1_end_tag(data);
+               
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ModifyResponse): {
+       case LDAP_TAG_ModifyResponse: {
                struct ldap_Result *r = &msg->r.ModifyResponse;
-               msg->type = LDAP_TAG_ModifyResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_ModifyResponse, r);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_AddRequest): {
+       case LDAP_TAG_AddRequest: {
                struct ldap_AddRequest *r = &msg->r.AddRequest;
-               msg->type = LDAP_TAG_AddRequest;
-               asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_AddRequest));
-               asn1_read_OctetString_talloc(msg->mem_ctx, data, &r->dn);
-
-               r->attributes = NULL;
-               r->num_attributes = 0;
-               ldap_decode_attribs(msg->mem_ctx, data, &r->attributes,
-                                   &r->num_attributes);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
 
-               asn1_end_tag(data);
+               for (i=0; i<r->num_attributes; i++) {
+                       struct ldb_message_element *attrib = &r->attributes[i];
+                       asn1_push_tag(data, ASN1_SEQUENCE(0));
+                       asn1_write_OctetString(data, attrib->name,
+                                              strlen(attrib->name));
+                       asn1_push_tag(data, ASN1_SET);
+                       for (j=0; j<r->attributes[i].num_values; j++) {
+                               asn1_write_OctetString(data,
+                                                      attrib->values[j].data,
+                                                      attrib->values[j].length);
+                       }
+                       asn1_pop_tag(data);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_AddResponse): {
+       case LDAP_TAG_AddResponse: {
                struct ldap_Result *r = &msg->r.AddResponse;
-               msg->type = LDAP_TAG_AddResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_AddResponse, r);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest): {
+       case LDAP_TAG_DelRequest: {
                struct ldap_DelRequest *r = &msg->r.DelRequest;
-               int len;
-               char *dn;
-               msg->type = LDAP_TAG_DelRequest;
-               asn1_start_tag(data,
-                              ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
-               len = asn1_tag_remaining(data);
-               dn = talloc(msg->mem_ctx, len+1);
-               if (dn == NULL)
-                       break;
-               asn1_read(data, dn, len);
-               dn[len] = '\0';
-               r->dn = dn;
-               asn1_end_tag(data);
+               asn1_push_tag(data, ASN1_APPLICATION_SIMPLE(msg->type));
+               asn1_write(data, r->dn, strlen(r->dn));
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_DelResponse): {
+       case LDAP_TAG_DelResponse: {
                struct ldap_Result *r = &msg->r.DelResponse;
-               msg->type = LDAP_TAG_DelResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_DelResponse, r);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest): {
-/*             struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest; */
-               msg->type = LDAP_TAG_ModifyDNRequest;
+       case LDAP_TAG_ModifyDNRequest: {
+               struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_write_OctetString(data, r->newrdn, strlen(r->newrdn));
+               asn1_write_BOOLEAN(data, r->deleteolddn);
+               if (r->newsuperior) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       asn1_write(data, r->newsuperior,
+                                  strlen(r->newsuperior));
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ModifyDNResponse): {
+       case LDAP_TAG_ModifyDNResponse: {
                struct ldap_Result *r = &msg->r.ModifyDNResponse;
-               msg->type = LDAP_TAG_ModifyDNResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_ModifyDNResponse, r);
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_CompareRequest): {
-/*             struct ldap_CompareRequest *r = &msg->r.CompareRequest; */
-               msg->type = LDAP_TAG_CompareRequest;
+       case LDAP_TAG_CompareRequest: {
+               struct ldap_CompareRequest *r = &msg->r.CompareRequest;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->dn, strlen(r->dn));
+               asn1_push_tag(data, ASN1_SEQUENCE(0));
+               asn1_write_OctetString(data, r->attribute,
+                                      strlen(r->attribute));
+               asn1_write_OctetString(data, r->value.data,
+                                      r->value.length);
+               asn1_pop_tag(data);
+               asn1_pop_tag(data);
+               break;
+       }
+       case LDAP_TAG_CompareResponse: {
+               struct ldap_Result *r = &msg->r.ModifyDNResponse;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, r);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_CompareResponse): {
-               struct ldap_Result *r = &msg->r.CompareResponse;
-               msg->type = LDAP_TAG_CompareResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_CompareResponse, r);
+       case LDAP_TAG_AbandonRequest: {
+               struct ldap_AbandonRequest *r = &msg->r.AbandonRequest;
+               asn1_push_tag(data, ASN1_APPLICATION_SIMPLE(msg->type));
+               asn1_write_implicit_Integer(data, r->messageid);
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_AbandonRequest): {
-/*             struct ldap_AbandonRequest *r = &msg->r.AbandonRequest; */
-               msg->type = LDAP_TAG_AbandonRequest;
+       case LDAP_TAG_SearchResultReference: {
+               struct ldap_SearchResRef *r = &msg->r.SearchResultReference;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_write_OctetString(data, r->referral, strlen(r->referral));
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ExtendedRequest): {
-/*             struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest; */
-               msg->type = LDAP_TAG_ExtendedRequest;
+       case LDAP_TAG_ExtendedRequest: {
+               struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(0));
+               asn1_write(data, r->oid, strlen(r->oid));
+               asn1_pop_tag(data);
+               if (r->value) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_write(data, r->value->data, r->value->length);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
                break;
        }
-
-       case ASN1_APPLICATION(LDAP_TAG_ExtendedResponse): {
+       case LDAP_TAG_ExtendedResponse: {
                struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
-               msg->type = LDAP_TAG_ExtendedResponse;
-               ldap_decode_response(msg->mem_ctx, data,
-                                    LDAP_TAG_ExtendedResponse, &r->response);
-               /* I have to come across an operation that actually sends
-                * something back to really see what's going on. The currently
-                * needed pwdchange does not send anything back. */
-               r->name = NULL;
-               r->value.data = NULL;
-               r->value.length = 0;
+               asn1_push_tag(data, ASN1_APPLICATION(msg->type));
+               ldap_encode_response(data, &r->response);
+               if (r->oid) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(10));
+                       asn1_write(data, r->oid, strlen(r->oid));
+                       asn1_pop_tag(data);
+               }
+               if (r->value) {
+                       asn1_push_tag(data, ASN1_CONTEXT_SIMPLE(11));
+                       asn1_write(data, r->value->data, r->value->length);
+                       asn1_pop_tag(data);
+               }
+               asn1_pop_tag(data);
                break;
        }
-
+       default:
+               return false;
        }
 
-       asn1_end_tag(data);
-       return ((!data->has_error) && (data->nesting == NULL));
-}
+       if (msg->controls != NULL) {
+               asn1_push_tag(data, ASN1_CONTEXT(0));
+               
+               for (i = 0; msg->controls[i] != NULL; i++) {
+                       if (!ldap_encode_control(mem_ctx, data, msg->controls[i])) {
+                               return false;
+                       }
+               }
 
-BOOL ldap_parse_basic_url(TALLOC_CTX *mem_ctx, const char *url,
-                         char **host, uint16 *port, BOOL *ldaps)
-{
-       int tmp_port = 0;
-       fstring protocol;
-       fstring tmp_host;
-       const char *p = url;
-
-       /* skip leading "URL:" (if any) */
-       if ( strnequal( p, "URL:", 4 ) ) {
-               p += 4;
+               asn1_pop_tag(data);
        }
 
-       /* Paranoia check */
-       SMB_ASSERT(sizeof(protocol)>10 && sizeof(tmp_host)>254);
-               
-       sscanf(p, "%10[^:]://%254[^:/]:%d", protocol, tmp_host, &tmp_port);
-
-       if (strequal(protocol, "ldap")) {
-               *port = 389;
-               *ldaps = False;
-       } else if (strequal(protocol, "ldaps")) {
-               *port = 636;
-               *ldaps = True;
-       } else {
-               DEBUG(0, ("unrecognised protocol (%s)!\n", protocol));
-               return False;
-       }
+       asn1_pop_tag(data);
 
-       if (tmp_port != 0)
-               *port = tmp_port;
-       
-       *host = talloc_strdup(mem_ctx, tmp_host);
+       if (data->has_error) {
+               asn1_free(data);
+               return false;
+       }
 
-       return (*host != NULL);
+       *result = data_blob_talloc(mem_ctx, data->data, data->length);
+       asn1_free(data);
+       return true;
 }
 
-struct ldap_connection *new_ldap_connection(void)
+static const char *blob2string_talloc(TALLOC_CTX *mem_ctx,
+                                     DATA_BLOB blob)
 {
-       TALLOC_CTX *mem_ctx = talloc_init("ldap_connection");
-       struct ldap_connection *result;
-
-       if (mem_ctx == NULL)
-               return NULL;
-
-       result = talloc(mem_ctx, sizeof(*result));
-
-       if (result == NULL)
-               return NULL;
-
-       result->mem_ctx = mem_ctx;
-       result->next_msgid = 1;
-       result->outstanding = NULL;
-       result->searchid = 0;
-       result->search_entries = NULL;
-       result->auth_dn = NULL;
-       result->simple_pw = NULL;
-       result->gensec = NULL;
-
+       char *result = talloc_array(mem_ctx, char, blob.length+1);
+       memcpy(result, blob.data, blob.length);
+       result[blob.length] = '\0';
        return result;
 }
 
-BOOL ldap_connect(struct ldap_connection *conn, const char *url)
+bool asn1_read_OctetString_talloc(TALLOC_CTX *mem_ctx,
+                                 struct asn1_data *data,
+                                 const char **result)
 {
-       struct hostent *hp;
-       struct in_addr ip;
-
-       if (!ldap_parse_basic_url(conn->mem_ctx, url, &conn->host,
-                                 &conn->port, &conn->ldaps))
-               return False;
-
-       hp = sys_gethostbyname(conn->host);
-
-       if ((hp == NULL) || (hp->h_addr == NULL))
-               return False;
-
-       putip((char *)&ip, (char *)hp->h_addr);
-
-       conn->sock = open_socket_out(SOCK_STREAM, &ip, conn->port, LDAP_CONNECTION_TIMEOUT);
-
-       return (conn->sock >= 0);
+       DATA_BLOB string;
+       if (!asn1_read_OctetString(data, mem_ctx, &string))
+               return false;
+       *result = blob2string_talloc(mem_ctx, string);
+       data_blob_free(&string);
+       return true;
 }
 
-BOOL ldap_set_simple_creds(struct ldap_connection *conn,
-                          const char *dn, const char *password)
+static void ldap_decode_response(TALLOC_CTX *mem_ctx,
+                                struct asn1_data *data,
+                                struct ldap_Result *result)
 {
-       conn->auth_dn = talloc_strdup(conn->mem_ctx, dn);
-       conn->simple_pw = talloc_strdup(conn->mem_ctx, password);
-
-       return ((conn->auth_dn != NULL) && (conn->simple_pw != NULL));
+       asn1_read_enumerated(data, &result->resultcode);
+       asn1_read_OctetString_talloc(mem_ctx, data, &result->dn);
+       asn1_read_OctetString_talloc(mem_ctx, data, &result->errormessage);
+       if (asn1_peek_tag(data, ASN1_CONTEXT(3))) {
+               asn1_start_tag(data, ASN1_CONTEXT(3));
+               asn1_read_OctetString_talloc(mem_ctx, data, &result->referral);
+               asn1_end_tag(data);
+       } else {
+               result->referral = NULL;
+       }
 }
 
-struct ldap_message *new_ldap_message(void)
+static struct ldb_val **ldap_decode_substring(TALLOC_CTX *mem_ctx, struct ldb_val **chunks, int chunk_num, char *value)
 {
-       TALLOC_CTX *mem_ctx = talloc_init("ldap_message");
-       struct ldap_message *result;
 
-       if (mem_ctx == NULL)
+       chunks = talloc_realloc(mem_ctx, chunks, struct ldb_val *, chunk_num + 2);
+       if (chunks == NULL) {
                return NULL;
+       }
 
-       result = talloc(mem_ctx, sizeof(*result));
+       chunks[chunk_num] = talloc(mem_ctx, struct ldb_val);
+       if (chunks[chunk_num] == NULL) {
+               return NULL;
+       }
 
-       if (result == NULL)
+       chunks[chunk_num]->data = (uint8_t *)talloc_strdup(mem_ctx, value);
+       if (chunks[chunk_num]->data == NULL) {
                return NULL;
+       }
+       chunks[chunk_num]->length = strlen(value);
 
-       result->mem_ctx = mem_ctx;
-       return result;
-}
+       chunks[chunk_num + 1] = '\0';
 
-void destroy_ldap_message(struct ldap_message *msg)
-{
-       if (msg != NULL)
-               talloc_destroy(msg->mem_ctx);
+       return chunks;
 }
 
-BOOL ldap_send_msg(struct ldap_connection *conn, struct ldap_message *msg,
-                  const struct timeval *endtime)
-{
-       DATA_BLOB request;
-       BOOL result;
-       struct ldap_queue_entry *entry;
 
-       msg->messageid = conn->next_msgid++;
+/*
+  parse the ASN.1 formatted search string into a ldb_parse_tree
+*/
+static struct ldb_parse_tree *ldap_decode_filter_tree(TALLOC_CTX *mem_ctx, 
+                                                     struct asn1_data *data)
+{
+       uint8_t filter_tag;
+       struct ldb_parse_tree *ret;
 
-       if (!ldap_encode(msg, &request))
-               return False;
+       if (!asn1_peek_uint8(data, &filter_tag)) {
+               return NULL;
+       }
 
-       result = (write_data_until(conn->sock, request.data, request.length,
-                                  endtime) == request.length);
+       filter_tag &= 0x1f;     /* strip off the asn1 stuff */
 
-       data_blob_free(&request);
+       ret = talloc(mem_ctx, struct ldb_parse_tree);
+       if (ret == NULL) return NULL;
 
-       if (!result)
-               return result;
+       switch(filter_tag) {
+       case 0:
+       case 1:
+               /* AND or OR of one or more filters */
+               ret->operation = (filter_tag == 0)?LDB_OP_AND:LDB_OP_OR;
+               ret->u.list.num_elements = 0;
+               ret->u.list.elements = NULL;
+
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
 
-       /* abandon and unbind don't expect results */
+               while (asn1_tag_remaining(data) > 0) {
+                       struct ldb_parse_tree *subtree;
+                       subtree = ldap_decode_filter_tree(ret, data);
+                       if (subtree == NULL) {
+                               goto failed;
+                       }
+                       ret->u.list.elements = 
+                               talloc_realloc(ret, ret->u.list.elements, 
+                                              struct ldb_parse_tree *, 
+                                              ret->u.list.num_elements+1);
+                       if (ret->u.list.elements == NULL) {
+                               goto failed;
+                       }
+                       talloc_steal(ret->u.list.elements, subtree);
+                       ret->u.list.elements[ret->u.list.num_elements] = subtree;
+                       ret->u.list.num_elements++;
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
 
-       if ((msg->type == LDAP_TAG_AbandonRequest) ||
-           (msg->type == LDAP_TAG_UnbindRequest))
-               return True;
+       case 2:
+               /* 'not' operation */
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
 
-       entry = malloc(sizeof(*entry)); 
+               ret->operation = LDB_OP_NOT;
+               ret->u.isnot.child = ldap_decode_filter_tree(ret, data);
+               if (ret->u.isnot.child == NULL) {
+                       goto failed;
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
 
-       if (entry == NULL)
-               return False;
+       case 3: {
+               /* equalityMatch */
+               const char *attrib;
+               DATA_BLOB value;
 
-       entry->msgid = msg->messageid;
-       entry->msg = NULL;
-       DLIST_ADD(conn->outstanding, entry);
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
+               asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-       return True;
-}
+               ret->operation = LDB_OP_EQUALITY;
+               ret->u.equality.attr = talloc_steal(ret, attrib);
+               ret->u.equality.value.data = talloc_steal(ret, value.data);
+               ret->u.equality.value.length = value.length;
+               break;
+       }
+       case 4: {
+               /* substrings */
+               DATA_BLOB attr;
+               uint8_t subs_tag;
+               char *value;
+               int chunk_num = 0;
 
-BOOL ldap_receive_msg(struct ldap_connection *conn, struct ldap_message *msg,
-                     const struct timeval *endtime)
-{
-        struct asn1_data data;
-        BOOL result;
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
+               if (!asn1_read_OctetString(data, mem_ctx, &attr)) {
+                       goto failed;
+               }
 
-        if (!asn1_read_sequence_until(conn->sock, &data, endtime))
-                return False;
+               ret->operation = LDB_OP_SUBSTRING;
+               ret->u.substring.attr = talloc_strndup(ret, (char *)attr.data, attr.length);
+               ret->u.substring.chunks = NULL;
+               ret->u.substring.start_with_wildcard = 1;
+               ret->u.substring.end_with_wildcard = 1;
 
-        result = ldap_decode(&data, msg);
+               if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) {
+                       goto failed;
+               }
 
-        asn1_free(&data);
-        return result;
-}
+               while (asn1_tag_remaining(data)) {
+                       asn1_peek_uint8(data, &subs_tag);
+                       subs_tag &= 0x1f;       /* strip off the asn1 stuff */
+                       if (subs_tag > 2) goto failed;
 
-static struct ldap_message *recv_from_queue(struct ldap_connection *conn,
-                                           int msgid)
-{
-       struct ldap_queue_entry *e;
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(subs_tag));
+                       asn1_read_LDAPString(data, mem_ctx, &value);
+                       asn1_end_tag(data);
 
-       for (e = conn->outstanding; e != NULL; e = e->next) {
+                       switch (subs_tag) {
+                       case 0:
+                               if (ret->u.substring.chunks != NULL) {
+                                       /* initial value found in the middle */
+                                       goto failed;
+                               }
 
-               if (e->msgid == msgid) {
-                       struct ldap_message *result = e->msg;
-                       DLIST_REMOVE(conn->outstanding, e);
-                       SAFE_FREE(e);
-                       return result;
-               }
-       }
+                               ret->u.substring.chunks = ldap_decode_substring(ret, NULL, 0, value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
 
-       return NULL;
-}
+                               ret->u.substring.start_with_wildcard = 0;
+                               chunk_num = 1;
+                               break;
 
-static void add_search_entry(struct ldap_connection *conn,
-                            struct ldap_message *msg)
-{
-       struct ldap_queue_entry *e = malloc(sizeof *e);
+                       case 1:
+                               if (ret->u.substring.end_with_wildcard == 0) {
+                                       /* "any" value found after a "final" value */
+                                       goto failed;
+                               }
+
+                               ret->u.substring.chunks = ldap_decode_substring(ret,
+                                                                               ret->u.substring.chunks,
+                                                                               chunk_num,
+                                                                               value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
+
+                               chunk_num++;
+                               break;
 
-       if (e == NULL)
-               return;
+                       case 2:
+                               ret->u.substring.chunks = ldap_decode_substring(ret,
+                                                                               ret->u.substring.chunks,
+                                                                               chunk_num,
+                                                                               value);
+                               if (ret->u.substring.chunks == NULL) {
+                                       goto failed;
+                               }
 
-       e->msg = msg;
-       DLIST_ADD_END(conn->search_entries, e, struct ldap_queue_entry *);
-       return;
-}
+                               ret->u.substring.end_with_wildcard = 0;
+                               break;
 
-static void fill_outstanding_request(struct ldap_connection *conn,
-                                    struct ldap_message *msg)
-{
-       struct ldap_queue_entry *e;
+                       default:
+                               goto failed;
+                       }
 
-       for (e = conn->outstanding; e != NULL; e = e->next) {
-               if (e->msgid == msg->messageid) {
-                       e->msg = msg;
-                       return;
                }
-       }
 
-       /* This reply has not been expected, destroy the incoming msg */
-       destroy_ldap_message(msg);
-       return;
-}
-
-struct ldap_message *ldap_receive(struct ldap_connection *conn, int msgid,
-                                 const struct timeval *endtime)
-{
-       struct ldap_message *result = recv_from_queue(conn, msgid);
+               if (!asn1_end_tag(data)) { /* SEQUENCE */
+                       goto failed;
+               }
 
-       if (result != NULL)
-               return result;
+               if (!asn1_end_tag(data)) {
+                       goto failed;
+               }
+               break;
+       }
+       case 5: {
+               /* greaterOrEqual */
+               const char *attrib;
+               DATA_BLOB value;
 
-       while (True) {
-               struct asn1_data data;
-               BOOL res;
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
+               asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-               result = new_ldap_message();
+               ret->operation = LDB_OP_GREATER;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
+               break;
+       }
+       case 6: {
+               /* lessOrEqual */
+               const char *attrib;
+               DATA_BLOB value;
 
-               if (!asn1_read_sequence_until(conn->sock, &data, endtime))
-                       return NULL;
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
+               asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-               res = ldap_decode(&data, result);
-               asn1_free(&data);
+               ret->operation = LDB_OP_LESS;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
+               break;
+       }
+       case 7: {
+               /* Normal presence, "attribute=*" */
+               char *attr;
 
-               if (!res)
-                       return NULL;
+               if (!asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(filter_tag))) {
+                       goto failed;
+               }
+               if (!asn1_read_LDAPString(data, ret, &attr)) {
+                       goto failed;
+               }
 
-               if (result->messageid == msgid)
-                       return result;
+               ret->operation = LDB_OP_PRESENT;
+               ret->u.present.attr = talloc_steal(ret, attr);
 
-               if (result->type == LDAP_TAG_SearchResultEntry) {
-                       add_search_entry(conn, result);
-               } else {
-                       fill_outstanding_request(conn, result);
+               if (!asn1_end_tag(data)) {
+                       goto failed;
                }
+               break;
        }
+       case 8: {
+               /* approx */
+               const char *attrib;
+               DATA_BLOB value;
 
-       return NULL;
-}
-
-struct ldap_message *ldap_transaction(struct ldap_connection *conn,
-                                     struct ldap_message *request)
-{
-       if (!ldap_send_msg(conn, request, NULL))
-               return False;
-
-       return ldap_receive(conn, request->messageid, NULL);
-}
-
-int ldap_bind_simple(struct ldap_connection *conn, const char *userdn, const char *password)
-{
-       struct ldap_message *response;
-       struct ldap_message *msg;
-       const char *dn, *pw;
-       int result = LDAP_OTHER;
+               asn1_start_tag(data, ASN1_CONTEXT(filter_tag));
+               asn1_read_OctetString_talloc(mem_ctx, data, &attrib);
+               asn1_read_OctetString(data, mem_ctx, &value);
+               asn1_end_tag(data);
+               if ((data->has_error) || (attrib == NULL) || (value.data == NULL)) {
+                       goto failed;
+               }
 
-       if (conn == NULL)
-               return result;
+               ret->operation = LDB_OP_APPROX;
+               ret->u.comparison.attr = talloc_steal(ret, attrib);
+               ret->u.comparison.value.data = talloc_steal(ret, value.data);
+               ret->u.comparison.value.length = value.length;
+               break;
+       }
+       case 9: {
+               char *oid = NULL, *attr = NULL, *value;
+               uint8_t dnAttributes;
+               /* an extended search */
+               if (!asn1_start_tag(data, ASN1_CONTEXT(filter_tag))) {
+                       goto failed;
+               }
 
-       if (userdn) {
-               dn = userdn;
-       } else {
-               if (conn->auth_dn) {
-                       dn = conn->auth_dn;
+               /* FIXME: read carefully rfc2251.txt there are a number of 'MUST's
+                  we need to check we properly implement --SSS */ 
+               /* either oid or type must be defined */
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) { /* optional */
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(1));
+                       asn1_read_LDAPString(data, ret, &oid);
+                       asn1_end_tag(data);
+               }
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(2))) {      /* optional  */
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(2));
+                       asn1_read_LDAPString(data, ret, &attr);
+                       asn1_end_tag(data);
+               }
+               asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(3));
+               asn1_read_LDAPString(data, ret, &value);
+               asn1_end_tag(data);
+               /* dnAttributes is marked as BOOLEAN DEFAULT FALSE
+                  it is not marked as OPTIONAL but openldap tools
+                  do not set this unless it is to be set as TRUE
+                  NOTE: openldap tools do not work with AD as it
+                  seems that AD always requires the dnAttributes
+                  boolean value to be set */
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(4))) {
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(4));
+                       asn1_read_uint8(data, &dnAttributes);
+                       asn1_end_tag(data);
                } else {
-                       dn = "";
+                       dnAttributes = 0;
+               }
+               if ((oid == NULL && attr == NULL) || (value == NULL)) {
+                       goto failed;
                }
-       }
 
-       if (password) {
-               pw = password;
-       } else {
-               if (conn->simple_pw) {
-                       pw = conn->simple_pw;
+               if (oid) {
+                       ret->operation               = LDB_OP_EXTENDED;
+
+                       /* From the RFC2251: If the type field is
+                          absent and matchingRule is present, the matchValue is compared
+                          against all attributes in an entry which support that matchingRule
+                       */
+                       if (attr) {
+                               ret->u.extended.attr = talloc_steal(ret, attr);
+                       } else {
+                               ret->u.extended.attr = talloc_strdup(ret, "*");
+                       }
+                       ret->u.extended.rule_id      = talloc_steal(ret, oid);
+                       ret->u.extended.value.data   = talloc_steal(ret, value);
+                       ret->u.extended.value.length = strlen(value);
+                       ret->u.extended.dnAttributes = dnAttributes;
                } else {
-                       pw = "";
+                       ret->operation               = LDB_OP_EQUALITY;
+                       ret->u.equality.attr         = talloc_steal(ret, attr);
+                       ret->u.equality.value.data   = talloc_steal(ret, value);
+                       ret->u.equality.value.length = strlen(value);
+               }
+               if (!asn1_end_tag(data)) {
+                       goto failed;
                }
+               break;
        }
 
-       msg =  new_ldap_simple_bind_msg(dn, pw);
-       if (!msg)
-               return result;
-
-       response = ldap_transaction(conn, msg);
-       if (!response) {
-               destroy_ldap_message(msg);
-               return result;
+       default:
+               DEBUG(0,("Unsupported LDAP filter operation 0x%x\n", filter_tag));
+               goto failed;
        }
-               
-       result = response->r.BindResponse.response.resultcode;
-
-       destroy_ldap_message(msg);
-       destroy_ldap_message(response);
+       
+       return ret;
 
-       return result;
+failed:
+       talloc_free(ret);
+       return NULL;    
 }
 
-int ldap_bind_sasl(struct ldap_connection *conn, const char *username, const char *domain, const char *password)
+/* Decode a single LDAP attribute, possibly containing multiple values */
+static void ldap_decode_attrib(TALLOC_CTX *mem_ctx, struct asn1_data *data,
+                              struct ldb_message_element *attrib)
 {
-       NTSTATUS status;
-       TALLOC_CTX *mem_ctx = NULL;
-       struct ldap_message *response;
-       struct ldap_message *msg;
-       DATA_BLOB input = data_blob(NULL, 0);
-       DATA_BLOB output = data_blob(NULL, 0);
-       int result = LDAP_OTHER;
-
-       if (conn == NULL)
-               return result;
-
-       status = gensec_client_start(&conn->gensec);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(0, ("Failed to start GENSEC engine (%s)\n", nt_errstr(status)));
-               return result;
+       asn1_start_tag(data, ASN1_SEQUENCE(0));
+       asn1_read_OctetString_talloc(mem_ctx, data, &attrib->name);
+       asn1_start_tag(data, ASN1_SET);
+       while (asn1_peek_tag(data, ASN1_OCTET_STRING)) {
+               DATA_BLOB blob;
+               asn1_read_OctetString(data, mem_ctx, &blob);
+               add_value_to_attrib(mem_ctx, &blob, attrib);
        }
+       asn1_end_tag(data);
+       asn1_end_tag(data);
+       
+}
 
-       status = gensec_set_domain(conn->gensec, domain);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client domain to %s: %s\n", 
-                         domain, nt_errstr(status)));
-               goto done;
+/* Decode a set of LDAP attributes, as found in the dereference control */
+void ldap_decode_attribs_bare(TALLOC_CTX *mem_ctx, struct asn1_data *data,
+                             struct ldb_message_element **attributes,
+                             int *num_attributes)
+{
+       while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
+               struct ldb_message_element attrib;
+               ZERO_STRUCT(attrib);
+               ldap_decode_attrib(mem_ctx, data, &attrib);
+               add_attrib_to_array_talloc(mem_ctx, &attrib,
+                                          attributes, num_attributes);
        }
+}
 
-       status = gensec_set_username(conn->gensec, username);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client username to %s: %s\n", 
-                         username, nt_errstr(status)));
-               goto done;
-       }
+/* Decode a set of LDAP attributes, as found in a search entry */
+void ldap_decode_attribs(TALLOC_CTX *mem_ctx, struct asn1_data *data,
+                        struct ldb_message_element **attributes,
+                        int *num_attributes)
+{
+       asn1_start_tag(data, ASN1_SEQUENCE(0));
+       ldap_decode_attribs_bare(mem_ctx, data, 
+                                attributes, num_attributes);
+       asn1_end_tag(data);
+}
+
+/* This routine returns LDAP status codes */
+
+_PUBLIC_ NTSTATUS ldap_decode(struct asn1_data *data, struct ldap_message *msg)
+{
+       uint8_t tag;
+
+       asn1_start_tag(data, ASN1_SEQUENCE(0));
+       asn1_read_Integer(data, &msg->messageid);
+
+       if (!asn1_peek_uint8(data, &tag))
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+
+       switch(tag) {
 
-       status = gensec_set_password(conn->gensec, password);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client password: %s\n", 
-                         nt_errstr(status)));
-               goto done;
+       case ASN1_APPLICATION(LDAP_TAG_BindRequest): {
+               struct ldap_BindRequest *r = &msg->r.BindRequest;
+               msg->type = LDAP_TAG_BindRequest;
+               asn1_start_tag(data, tag);
+               asn1_read_Integer(data, &r->version);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(0))) {
+                       int pwlen;
+                       r->creds.password = "";
+                       r->mechanism = LDAP_AUTH_MECH_SIMPLE;
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       pwlen = asn1_tag_remaining(data);
+                       if (pwlen == -1) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
+                       if (pwlen != 0) {
+                               char *pw = talloc_array(msg, char, pwlen+1);
+                               if (!pw) {
+                                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                               }
+                               asn1_read(data, pw, pwlen);
+                               pw[pwlen] = '\0';
+                               r->creds.password = pw;
+                       }
+                       asn1_end_tag(data);
+               } else if (asn1_peek_tag(data, ASN1_CONTEXT(3))){
+                       asn1_start_tag(data, ASN1_CONTEXT(3));
+                       r->mechanism = LDAP_AUTH_MECH_SASL;
+                       asn1_read_OctetString_talloc(msg, data, &r->creds.SASL.mechanism);
+                       if (asn1_peek_tag(data, ASN1_OCTET_STRING)) { /* optional */
+                               DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                               asn1_read_OctetString(data, msg, &tmp_blob);
+                               r->creds.SASL.secblob = talloc(msg, DATA_BLOB);
+                               if (!r->creds.SASL.secblob) {
+                                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                               }
+                               *r->creds.SASL.secblob = data_blob_talloc(r->creds.SASL.secblob,
+                                                                         tmp_blob.data, tmp_blob.length);
+                               data_blob_free(&tmp_blob);
+                       } else {
+                               r->creds.SASL.secblob = NULL;
+                       }
+                       asn1_end_tag(data);
+               } else {
+                       /* Neither Simple nor SASL bind */
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+               }
+               asn1_end_tag(data);
+               break;
        }
 
-       status = gensec_set_target_hostname(conn->gensec, conn->host);
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC target hostname: %s\n", 
-                         nt_errstr(status)));
-               goto done;
+       case ASN1_APPLICATION(LDAP_TAG_BindResponse): {
+               struct ldap_BindResponse *r = &msg->r.BindResponse;
+               msg->type = LDAP_TAG_BindResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, &r->response);
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(7))) {
+                       DATA_BLOB tmp_blob = data_blob(NULL, 0);
+                       asn1_read_ContextSimple(data, 7, &tmp_blob);
+                       r->SASL.secblob = talloc(msg, DATA_BLOB);
+                       if (!r->SASL.secblob) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->SASL.secblob = data_blob_talloc(r->SASL.secblob,
+                                                           tmp_blob.data, tmp_blob.length);
+                       data_blob_free(&tmp_blob);
+               } else {
+                       r->SASL.secblob = NULL;
+               }
+               asn1_end_tag(data);
+               break;
        }
 
-       status = gensec_start_mech_by_sasl_name(conn->gensec, "GSS-SPNEGO");
-       if (!NT_STATUS_IS_OK(status)) {
-               DEBUG(1, ("Failed to start set GENSEC client SPNEGO mechanism: %s\n",
-                         nt_errstr(status)));
-               goto done;
+       case ASN1_APPLICATION_SIMPLE(LDAP_TAG_UnbindRequest): {
+               msg->type = LDAP_TAG_UnbindRequest;
+               asn1_start_tag(data, tag);
+               asn1_end_tag(data);
+               break;
        }
 
-       mem_ctx = talloc_init("ldap_bind_sasl");
-       if (!mem_ctx)
-               goto done;
-
-       status = gensec_update(conn->gensec, mem_ctx,
-                              input,
-                              &output);
+       case ASN1_APPLICATION(LDAP_TAG_SearchRequest): {
+               struct ldap_SearchRequest *r = &msg->r.SearchRequest;
+               msg->type = LDAP_TAG_SearchRequest;
+               asn1_start_tag(data, tag);
+               asn1_read_OctetString_talloc(msg, data, &r->basedn);
+               asn1_read_enumerated(data, (int *)&(r->scope));
+               asn1_read_enumerated(data, (int *)&(r->deref));
+               asn1_read_Integer(data, &r->sizelimit);
+               asn1_read_Integer(data, &r->timelimit);
+               asn1_read_BOOLEAN(data, &r->attributesonly);
 
-       while(1) {
-               if (NT_STATUS_IS_OK(status) && output.length == 0) {
-                       break;
-               }
-               if (!NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED) && !NT_STATUS_IS_OK(status)) {
-                       break;
+               r->tree = ldap_decode_filter_tree(msg, data);
+               if (r->tree == NULL) {
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
 
-               msg =  new_ldap_sasl_bind_msg("GSS-SPNEGO", &output);
-               if (!msg)
-                       goto done;
-
-               response = ldap_transaction(conn, msg);
-               destroy_ldap_message(msg);
+               asn1_start_tag(data, ASN1_SEQUENCE(0));
 
-               if (!response) {
-                       goto done;
-               }
+               r->num_attributes = 0;
+               r->attributes = NULL;
 
-               result = response->r.BindResponse.response.resultcode;
+               while (asn1_tag_remaining(data) > 0) {                                  
 
-               if (result != LDAP_SUCCESS && result != LDAP_SASL_BIND_IN_PROGRESS) {
-                       break;
+                       const char *attr;
+                       if (!asn1_read_OctetString_talloc(msg, data,
+                                                         &attr))
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       if (!add_string_to_array(msg, attr,
+                                                &r->attributes,
+                                                &r->num_attributes))
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
                }
 
-               status = gensec_update(conn->gensec, mem_ctx,
-                                      response->r.BindResponse.SASL.secblob,
-                                      &output);
-
-               destroy_ldap_message(response);
-       }
-
-done:
-       if (conn->gensec)
-               gensec_end(&conn->gensec);
-       if (mem_ctx)
-               talloc_destroy(mem_ctx);
-
-       return result;
-}
-
-BOOL ldap_setup_connection(struct ldap_connection *conn,
-                          const char *url, const char *userdn, const char *password)
-{
-       int result;
-
-       if (!ldap_connect(conn, url)) {
-               return False;
+               asn1_end_tag(data);
+               asn1_end_tag(data);
+               break;
        }
 
-       result = ldap_bind_simple(conn, userdn, password);
-       if (result == LDAP_SUCCESS) {
-               return True;
+       case ASN1_APPLICATION(LDAP_TAG_SearchResultEntry): {
+               struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
+               msg->type = LDAP_TAG_SearchResultEntry;
+               r->attributes = NULL;
+               r->num_attributes = 0;
+               asn1_start_tag(data, tag);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               ldap_decode_attribs(msg, data, &r->attributes,
+                                   &r->num_attributes);
+               asn1_end_tag(data);
+               break;
        }
 
-       return False;
-}
-
-BOOL ldap_setup_connection_with_sasl(struct ldap_connection *conn, const char *url, const char *username, const char *domain, const char *password)
-{
-       int result;
-
-       if (!ldap_connect(conn, url)) {
-               return False;
+       case ASN1_APPLICATION(LDAP_TAG_SearchResultDone): {
+               struct ldap_Result *r = &msg->r.SearchResultDone;
+               msg->type = LDAP_TAG_SearchResultDone;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
        }
 
-       result = ldap_bind_sasl(conn, username, domain, password);
-       if (result == LDAP_SUCCESS) {
-               return True;
+       case ASN1_APPLICATION(LDAP_TAG_SearchResultReference): {
+               struct ldap_SearchResRef *r = &msg->r.SearchResultReference;
+               msg->type = LDAP_TAG_SearchResultReference;
+               asn1_start_tag(data, tag);
+               asn1_read_OctetString_talloc(msg, data, &r->referral);
+               asn1_end_tag(data);
+               break;
        }
 
-       return False;
-}
-
-static BOOL ldap_abandon_message(struct ldap_connection *conn, int msgid,
-                                const struct timeval *endtime)
-{
-       struct ldap_message *msg = new_ldap_message();
-       BOOL result;
-
-       if (msg == NULL)
-               return False;
-
-       msg->type = LDAP_TAG_AbandonRequest;
-       msg->r.AbandonRequest.messageid = msgid;
-
-       result = ldap_send_msg(conn, msg, endtime);
-       destroy_ldap_message(msg);
-       return result;
-}
-
-struct ldap_message *new_ldap_search_message(const char *base,
-                                            enum ldap_scope scope,
-                                            char *filter,
-                                            int num_attributes,
-                                            const char **attributes)
-{
-       struct ldap_message *res = new_ldap_message();
-
-       if (res == NULL)
-               return NULL;
-
-       res->type = LDAP_TAG_SearchRequest;
-       res->r.SearchRequest.basedn = base;
-       res->r.SearchRequest.scope = scope;
-       res->r.SearchRequest.deref = LDAP_DEREFERENCE_NEVER;
-       res->r.SearchRequest.timelimit = 0;
-       res->r.SearchRequest.sizelimit = 0;
-       res->r.SearchRequest.attributesonly = False;
-       res->r.SearchRequest.filter = filter;
-       res->r.SearchRequest.num_attributes = num_attributes;
-       res->r.SearchRequest.attributes = attributes;
-       return res;
-}
-
-struct ldap_message *new_ldap_simple_bind_msg(const char *dn, const char *pw)
-{
-       struct ldap_message *res = new_ldap_message();
-
-       if (res == NULL)
-               return NULL;
-
-       res->type = LDAP_TAG_BindRequest;
-       res->r.BindRequest.version = 3;
-       res->r.BindRequest.dn = talloc_strdup(res->mem_ctx, dn);
-       res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SIMPLE;
-       res->r.BindRequest.creds.password = talloc_strdup(res->mem_ctx, pw);
-       return res;
-}
-
-struct ldap_message *new_ldap_sasl_bind_msg(const char *sasl_mechanism, DATA_BLOB *secblob)
-{
-       struct ldap_message *res = new_ldap_message();
+       case ASN1_APPLICATION(LDAP_TAG_ModifyRequest): {
+               struct ldap_ModifyRequest *r = &msg->r.ModifyRequest;
+               msg->type = LDAP_TAG_ModifyRequest;
+               asn1_start_tag(data, ASN1_APPLICATION(LDAP_TAG_ModifyRequest));
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               asn1_start_tag(data, ASN1_SEQUENCE(0));
 
-       if (res == NULL)
-               return NULL;
+               r->num_mods = 0;
+               r->mods = NULL;
 
-       res->type = LDAP_TAG_BindRequest;
-       res->r.BindRequest.version = 3;
-       res->r.BindRequest.dn = "";
-       res->r.BindRequest.mechanism = LDAP_AUTH_MECH_SASL;
-       res->r.BindRequest.creds.SASL.mechanism = talloc_strdup(res->mem_ctx, sasl_mechanism);
-       res->r.BindRequest.creds.SASL.secblob = *secblob;
-       return res;
-}
+               while (asn1_tag_remaining(data) > 0) {
+                       struct ldap_mod mod;
+                       int v;
+                       ZERO_STRUCT(mod);
+                       asn1_start_tag(data, ASN1_SEQUENCE(0));
+                       asn1_read_enumerated(data, &v);
+                       mod.type = v;
+                       ldap_decode_attrib(msg, data, &mod.attrib);
+                       asn1_end_tag(data);
+                       if (!add_mod_to_array_talloc(msg, &mod,
+                                                    &r->mods, &r->num_mods)) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+               }
 
-BOOL ldap_setsearchent(struct ldap_connection *conn, struct ldap_message *msg,
-                      const struct timeval *endtime)
-{
-       if ((conn->searchid != 0) &&
-           (!ldap_abandon_message(conn, conn->searchid, endtime)))
-               return False;
+               asn1_end_tag(data);
+               asn1_end_tag(data);
+               break;
+       }
 
-       conn->searchid = conn->next_msgid;
-       return ldap_send_msg(conn, msg, endtime);
-}
+       case ASN1_APPLICATION(LDAP_TAG_ModifyResponse): {
+               struct ldap_Result *r = &msg->r.ModifyResponse;
+               msg->type = LDAP_TAG_ModifyResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
+       }
 
-struct ldap_message *ldap_getsearchent(struct ldap_connection *conn,
-                                      const struct timeval *endtime)
-{
-       struct ldap_message *result;
+       case ASN1_APPLICATION(LDAP_TAG_AddRequest): {
+               struct ldap_AddRequest *r = &msg->r.AddRequest;
+               msg->type = LDAP_TAG_AddRequest;
+               asn1_start_tag(data, tag);
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
 
-       if (conn->search_entries != NULL) {
-               struct ldap_queue_entry *e = conn->search_entries;
+               r->attributes = NULL;
+               r->num_attributes = 0;
+               ldap_decode_attribs(msg, data, &r->attributes,
+                                   &r->num_attributes);
 
-               result = e->msg;
-               DLIST_REMOVE(conn->search_entries, e);
-               SAFE_FREE(e);
-               return result;
+               asn1_end_tag(data);
+               break;
        }
 
-       result = ldap_receive(conn, conn->searchid, endtime);
-
-       if (result->type == LDAP_TAG_SearchResultEntry)
-               return result;
+       case ASN1_APPLICATION(LDAP_TAG_AddResponse): {
+               struct ldap_Result *r = &msg->r.AddResponse;
+               msg->type = LDAP_TAG_AddResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
+       }
 
-       if (result->type == LDAP_TAG_SearchResultDone) {
-               /* TODO: Handle Paged Results */
-               destroy_ldap_message(result);
-               return NULL;
+       case ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest): {
+               struct ldap_DelRequest *r = &msg->r.DelRequest;
+               int len;
+               char *dn;
+               msg->type = LDAP_TAG_DelRequest;
+               asn1_start_tag(data,
+                              ASN1_APPLICATION_SIMPLE(LDAP_TAG_DelRequest));
+               len = asn1_tag_remaining(data);
+               if (len == -1) {
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+               }
+               dn = talloc_array(msg, char, len+1);
+               if (dn == NULL)
+                       break;
+               asn1_read(data, dn, len);
+               dn[len] = '\0';
+               r->dn = dn;
+               asn1_end_tag(data);
+               break;
        }
 
-       /* TODO: Handle Search References here */
-       return NULL;
-}
+       case ASN1_APPLICATION(LDAP_TAG_DelResponse): {
+               struct ldap_Result *r = &msg->r.DelResponse;
+               msg->type = LDAP_TAG_DelResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
+       }
 
-void ldap_endsearchent(struct ldap_connection *conn,
-                      const struct timeval *endtime)
-{
-       struct ldap_queue_entry *e;
+       case ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest): {
+               struct ldap_ModifyDNRequest *r = &msg->r.ModifyDNRequest;
+               msg->type = LDAP_TAG_ModifyDNRequest;
+               asn1_start_tag(data,
+                              ASN1_APPLICATION(LDAP_TAG_ModifyDNRequest));
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               asn1_read_OctetString_talloc(msg, data, &r->newrdn);
+               asn1_read_BOOLEAN(data, &r->deleteolddn);
+               r->newsuperior = NULL;
+               if (asn1_tag_remaining(data) > 0) {
+                       int len;
+                       char *newsup;
+                       asn1_start_tag(data, ASN1_CONTEXT_SIMPLE(0));
+                       len = asn1_tag_remaining(data);
+                       if (len == -1) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
+                       newsup = talloc_array(msg, char, len+1);
+                       if (newsup == NULL) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       asn1_read(data, newsup, len);
+                       newsup[len] = '\0';
+                       r->newsuperior = newsup;
+                       asn1_end_tag(data);
+               }
+               asn1_end_tag(data);
+               break;
+       }
 
-       e = conn->search_entries;
+       case ASN1_APPLICATION(LDAP_TAG_ModifyDNResponse): {
+               struct ldap_Result *r = &msg->r.ModifyDNResponse;
+               msg->type = LDAP_TAG_ModifyDNResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
+       }
 
-       while (e != NULL) {
-               struct ldap_queue_entry *next = e->next;
-               DLIST_REMOVE(conn->search_entries, e);
-               SAFE_FREE(e);
-               e = next;
+       case ASN1_APPLICATION(LDAP_TAG_CompareRequest): {
+               struct ldap_CompareRequest *r = &msg->r.CompareRequest;
+               msg->type = LDAP_TAG_CompareRequest;
+               asn1_start_tag(data,
+                              ASN1_APPLICATION(LDAP_TAG_CompareRequest));
+               asn1_read_OctetString_talloc(msg, data, &r->dn);
+               asn1_start_tag(data, ASN1_SEQUENCE(0));
+               asn1_read_OctetString_talloc(msg, data, &r->attribute);
+               asn1_read_OctetString(data, msg, &r->value);
+               if (r->value.data) {
+                       talloc_steal(msg, r->value.data);
+               }
+               asn1_end_tag(data);
+               asn1_end_tag(data);
+               break;
        }
-}
 
-struct ldap_message *ldap_searchone(struct ldap_connection *conn,
-                                   struct ldap_message *msg,
-                                   const struct timeval *endtime)
-{
-       struct ldap_message *res1, *res2 = NULL;
-       if (!ldap_setsearchent(conn, msg, endtime))
-               return NULL;
+       case ASN1_APPLICATION(LDAP_TAG_CompareResponse): {
+               struct ldap_Result *r = &msg->r.CompareResponse;
+               msg->type = LDAP_TAG_CompareResponse;
+               asn1_start_tag(data, tag);
+               ldap_decode_response(msg, data, r);
+               asn1_end_tag(data);
+               break;
+       }
 
-       res1 = ldap_getsearchent(conn, endtime);
+       case ASN1_APPLICATION_SIMPLE(LDAP_TAG_AbandonRequest): {
+               struct ldap_AbandonRequest *r = &msg->r.AbandonRequest;
+               msg->type = LDAP_TAG_AbandonRequest;
+               asn1_start_tag(data, tag);
+               asn1_read_implicit_Integer(data, &r->messageid);
+               asn1_end_tag(data);
+               break;
+       }
 
-       if (res1 != NULL)
-               res2 = ldap_getsearchent(conn, endtime);
+       case ASN1_APPLICATION(LDAP_TAG_ExtendedRequest): {
+               struct ldap_ExtendedRequest *r = &msg->r.ExtendedRequest;
+               DATA_BLOB tmp_blob = data_blob(NULL, 0);
 
-       ldap_endsearchent(conn, endtime);
+               msg->type = LDAP_TAG_ExtendedRequest;
+               asn1_start_tag(data,tag);
+               if (!asn1_read_ContextSimple(data, 0, &tmp_blob)) {
+                       return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+               }
+               r->oid = blob2string_talloc(msg, tmp_blob);
+               data_blob_free(&tmp_blob);
+               if (!r->oid) {
+                       return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+               }
 
-       if (res1 == NULL)
-               return NULL;
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(1))) {
+                       asn1_read_ContextSimple(data, 1, &tmp_blob);
+                       r->value = talloc(msg, DATA_BLOB);
+                       if (!r->value) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->value = data_blob_talloc(r->value, tmp_blob.data, tmp_blob.length);
+                       data_blob_free(&tmp_blob);
+               } else {
+                       r->value = NULL;
+               }
 
-       if (res2 != NULL) {
-               /* More than one entry */
-               destroy_ldap_message(res1);
-               destroy_ldap_message(res2);
-               return NULL;
+               asn1_end_tag(data);
+               break;
        }
 
-       return res1;
-}
-
-BOOL ldap_find_single_value(struct ldap_message *msg, const char *attr,
-                           DATA_BLOB *value)
-{
-       int i;
-       struct ldap_SearchResEntry *r = &msg->r.SearchResultEntry;
-
-       if (msg->type != LDAP_TAG_SearchResultEntry)
-               return False;
-
-       for (i=0; i<r->num_attributes; i++) {
-               if (strequal(attr, r->attributes[i].name)) {
-                       if (r->attributes[i].num_values != 1)
-                               return False;
+       case ASN1_APPLICATION(LDAP_TAG_ExtendedResponse): {
+               struct ldap_ExtendedResponse *r = &msg->r.ExtendedResponse;
+               DATA_BLOB tmp_blob = data_blob(NULL, 0);
 
-                       *value = r->attributes[i].values[0];
-                       return True;
+               msg->type = LDAP_TAG_ExtendedResponse;
+               asn1_start_tag(data, tag);              
+               ldap_decode_response(msg, data, &r->response);
+
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(10))) {
+                       asn1_read_ContextSimple(data, 1, &tmp_blob);
+                       r->oid = blob2string_talloc(msg, tmp_blob);
+                       data_blob_free(&tmp_blob);
+                       if (!r->oid) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+               } else {
+                       r->oid = NULL;
                }
-       }
-       return False;
-}
 
-BOOL ldap_find_single_string(struct ldap_message *msg, const char *attr,
-                            TALLOC_CTX *mem_ctx, char **value)
-{
-       DATA_BLOB blob;
+               if (asn1_peek_tag(data, ASN1_CONTEXT_SIMPLE(11))) {
+                       asn1_read_ContextSimple(data, 1, &tmp_blob);
+                       r->value = talloc(msg, DATA_BLOB);
+                       if (!r->value) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
+                       *r->value = data_blob_talloc(r->value, tmp_blob.data, tmp_blob.length);
+                       data_blob_free(&tmp_blob);
+               } else {
+                       r->value = NULL;
+               }
 
-       if (!ldap_find_single_value(msg, attr, &blob))
-               return False;
+               asn1_end_tag(data);
+               break;
+       }
+       default: 
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
 
-       *value = talloc(mem_ctx, blob.length+1);
+       msg->controls = NULL;
+       msg->controls_decoded = NULL;
 
-       if (*value == NULL)
-               return False;
+       if (asn1_peek_tag(data, ASN1_CONTEXT(0))) {
+               int i = 0;
+               struct ldb_control **ctrl = NULL;
+               bool *decoded = NULL;
 
-       memcpy(*value, blob.data, blob.length);
-       (*value)[blob.length] = '\0';
-       return True;
-}
+               asn1_start_tag(data, ASN1_CONTEXT(0));
 
-BOOL ldap_find_single_int(struct ldap_message *msg, const char *attr,
-                         int *value)
-{
-       DATA_BLOB blob;
-       char *val;
-       int errno_save;
-       BOOL res;
+               while (asn1_peek_tag(data, ASN1_SEQUENCE(0))) {
+                       DATA_BLOB value;
+                       /* asn1_start_tag(data, ASN1_SEQUENCE(0)); */
 
-       if (!ldap_find_single_value(msg, attr, &blob))
-               return False;
+                       ctrl = talloc_realloc(msg, ctrl, struct ldb_control *, i+2);
+                       if (!ctrl) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
 
-       val = malloc(blob.length+1);
-       if (val == NULL)
-               return False;
+                       decoded = talloc_realloc(msg, decoded, bool, i+1);
+                       if (!decoded) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
 
-       memcpy(val, blob.data, blob.length);
-       val[blob.length] = '\0';
+                       ctrl[i] = talloc(ctrl, struct ldb_control);
+                       if (!ctrl[i]) {
+                               return NT_STATUS_LDAP(LDAP_OPERATIONS_ERROR);
+                       }
 
-       errno_save = errno;
-       errno = 0;
+                       if (!ldap_decode_control_wrapper(ctrl, data, ctrl[i], &value)) {
+                               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+                       }
+                       
+                       if (!ldap_decode_control_value(ctrl, value, ctrl[i])) {
+                               if (ctrl[i]->critical) {
+                                       ctrl[i]->data = NULL;
+                                       decoded[i] = false;
+                                       i++;
+                               } else {
+                                       talloc_free(ctrl[i]);
+                                       ctrl[i] = NULL;
+                               }
+                       } else {
+                               decoded[i] = true;
+                               i++;
+                       }
+               }
 
-       *value = strtol(val, NULL, 10);
+               if (ctrl != NULL) {
+                       ctrl[i] = NULL;
+               }
 
-       res = (errno == 0);
+               msg->controls = ctrl;
+               msg->controls_decoded = decoded;
 
-       free(val);
-       errno = errno_save;
+               asn1_end_tag(data);
+       }
 
-       return res;
+       asn1_end_tag(data);
+       if ((data->has_error) || (data->nesting != NULL)) {
+               return NT_STATUS_LDAP(LDAP_PROTOCOL_ERROR);
+       }
+       return NT_STATUS_OK;
 }
 
-int ldap_error(struct ldap_connection *conn)
-{
-       return 0;
-}
 
-NTSTATUS ldap2nterror(int ldaperror)
+/*
+  return NT_STATUS_OK if a blob has enough bytes in it to be a full
+  ldap packet. Set packet_size if true.
+*/
+NTSTATUS ldap_full_packet(void *private_data, DATA_BLOB blob, size_t *packet_size)
 {
-       return NT_STATUS_OK;
+       return asn1_full_tag(blob, ASN1_SEQUENCE(0), packet_size);
 }