LDAP without COMatePlus [SOLVED]

Just starting out? Need help? Post your questions and find answers here.
mikejs
Enthusiast
Enthusiast
Posts: 175
Joined: Thu Oct 21, 2010 9:46 pm

LDAP without COMatePlus [SOLVED]

Post by mikejs »

I'm trying to do some LDAP queries without using COMatePlus (long story).

Options for doing this seem to be limited and of the "roll your own" variety, but I'm having a go. Problem is, I don't know C well enough to follow the function definitions perfectly or know what to pass when it asks for things like a pointer to a "NULL-terminated array of strings". And this is my first time trying to get to grips with prototypes, so apologies for any stupid questions here.

I'm trying to get a useable wrapper for the Novell LDAPSDK dll, because this seems to be a reasonably comprehensive and reasonably well documented library, and has both 32 and 64bit windows binaries (I'll need both 32 and 64bit exes for the resulting project). Documentation starts here http://www.novell.com/documentation/developer/cldap

All I really need is the ability to bind, search, get string, integer and binary properties, and unbind when done. It's just read access I'm after, and all my queries will return either one result, or no results.

I have prototypes that seem to work for ldap_init and ldap_simple_bind_s, and for ldap_err2string:

Code: Select all

PrototypeC.i p_LDAP_init(host$, port.w=389)
PrototypeC.i p_LDAP_simple_bind_s(ld.i, dn$, pw$)
PrototypeC.i p_LDAP_unbind(ld.i)
PrototypeC.i p_LDAP_err2string(err.w)

Global.i ldap_lib
Global LDAP_init.p_LDAP_init
Global LDAP_simple_bind_s.p_LDAP_simple_bind_s
Global LDAP_unbind.p_LDAP_unbind
Global LDAP_err2string.p_LDAP_err2string

#LDAP_SUCCESS = 0

Procedure LDAPLoadLib()
  ldap_lib=OpenLibrary(#PB_Any, "ldapsdk.dll")
  If ldap_lib
    Debug "Opened LDAPSDK"   
    LDAP_init.p_LDAP_init=GetFunction(ldap_lib, "ldap_init")
    LDAP_simple_bind_s.p_LDAP_simple_bind_s=GetFunction(ldap_lib, "ldap_simple_bind_s")
    LDAP_unbind.p_LDAP_unbind=GetFunction(ldap_lib, "ldap_unbind")
    LDAP_err2string.p_LDAP_err2string=GetFunction(ldap_lib, "ldap_err2string")
  Else
    Debug "Unable to load LDAP library"
  EndIf
EndProcedure

Procedure.s LDAPError(err.w)
  Protected out$
  Protected *string
  out$="Unknown error"
  *string=LDAP_err2string(err)
  If *string
    out$=PeekS(*string)
  EndIf
  ProcedureReturn out$
EndProcedure
Using this code with things like:

Code: Select all

Define.i ldap_handle
Define.w result
LDAPLoadLib()
ldap_handle=LDAP_init("mydomain.com")
If ldap_handle
  result.w=LDAP_simple_bind_s(ldap_handle, "username@mydomain", "password")
  Debug "result: "+Str(result)
Else
  Debug "LDAP init failed"
EndIf
This works and I get a result of 0 (LDAP_SUCCESS).
Where I then run into a brick wall is with prototyping the ldap_seach_ext_s function. Documentation for this function is here: http://www.novell.com/documentation/dev ... wuhb3.html

The function is defined as:

Code: Select all

  int ldap_search_ext_s (
     LDAP             *ld,
     const char       *base,
     int               scope,
     const char       *filter,
     char            **attrs,
     int               attrsonly,
     LDAPControl     **serverctrls,
     LDAPControl     **clientctrls,
     struct timeval   *timeout,
     int               sizelimit,
     LDAPMessage     **res);
I've tried a few ways of prototyping that, but I always end up with either invalid memory access, or an LDAP_PARAM_ERROR result. I'm also unclear on what the **'s mean in the function definition. A single * I take to mean a pointer, but ** eludes me. **attrs is the "pointer to null-terminated array of strings", and I'm not sure how that relates to PB arrays (defining an array of strings with the first few populated and the next one set to #null$, then passing @array() doesn't seem to work - should it?). Here's one attempt that doesn't work:

Code: Select all

PrototypeC.i p_LDAP_init(host$, port.w=389)
PrototypeC.i p_LDAP_simple_bind_s(ld.i, dn$, pw$)
PrototypeC.i p_LDAP_unbind(ld.i)
PrototypeC.i p_LDAP_err2string(err.w)
PrototypeC.i p_LDAP_search_ext_s(ld.i, base$, scope.w, filter$, *attrs, attrsonly.w, *serverctrls, *clientctrls, *timeout, sizelimit.w, results.i)

Global.i ldap_lib
Global LDAP_init.p_LDAP_init
Global LDAP_simple_bind_s.p_LDAP_simple_bind_s
Global LDAP_unbind.p_LDAP_unbind
Global LDAP_err2string.p_LDAP_err2string
Global LDAP_search_ext_s.p_LDAP_search_ext_s

Structure ldapcontrol
  *oid    ; points to string
  *berval ; points to berval structure
  iscritical$
EndStructure

Structure ldap_berval
  len.l
  val$
EndStructure

Structure ldap_timeval
  secs.l
  usecs.l
EndStructure

#LDAP_SUCCESS                         = 0

#LDAP_SCOPE_BASE                      = 0
#LDAP_SCOPE_ONELEVEL                  = 1
#LDAP_SCOPE_SUBTREE                   = 2

Procedure LDAPLoadLib()
  ldap_lib=OpenLibrary(#PB_Any, "ldapsdk.dll")
  If ldap_lib
    Debug "Opened LDAPSDK"   
    LDAP_init.p_LDAP_init=GetFunction(ldap_lib, "ldap_init")
    LDAP_simple_bind_s.p_LDAP_simple_bind_s=GetFunction(ldap_lib, "ldap_simple_bind_s")
    LDAP_unbind.p_LDAP_unbind=GetFunction(ldap_lib, "ldap_unbind")
    LDAP_err2string.p_LDAP_err2string=GetFunction(ldap_lib, "ldap_err2string")
    LDAP_search_ext_s.p_LDAP_search_ext_s=GetFunction(ldap_lib, "ldap_search_ext_s")
  Else
    Debug "Unable to load LDAP library"
  EndIf
EndProcedure

Procedure.s LDAPError(err.w)
  Protected out$
  Protected *string
  out$="Unknown error"
  *string=LDAP_err2string(err)
  If *string
    out$=PeekS(*string)
  EndIf
  ProcedureReturn out$
EndProcedure
Trying to do a search via:

Code: Select all

Define.i ldap_handle
Define.w result
Define.s base$, filter$
Define *ldapresults
Define timeout.ldap_timeval
Define Dim attrs$(10)

LDAPLoadLib()
ldap_handle=LDAP_init("mydomain.com")
If ldap_handle
  result.w=LDAP_simple_bind_s(ldap_handle, "username@mydomain", "password")
  Debug "result: "+Str(result)
  base$="OU=this,DC=that,DC=theother"
  filter$="(&(objectclass=User)(samAccountName=someuser))"
  attrs$(0)="cn"
  attrs$(1)="department"
  attrs$(2)=#NULL$
  timeout\secs=10
  timeout\usecs=0
  result=LDAP_search_ext_s(ldap_handle, base$, #LDAP_SCOPE_SUBTREE, filter$, @attrs$(), 0, 0, 0, @timeout, 0, *ldapresults)
  Debug "result: "+Str(result)
Else
  Debug "LDAP init failed"
EndIf
results in the LDAP_PARAM_ERROR, but it's not clear why (or which parameter it doesn't like)

Any suggestions on what a prototype for this function should look like?
Last edited by mikejs on Thu Mar 21, 2013 10:22 am, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 7582
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: LDAP without COMatePlus

Post by infratec »

Hi mikejs,

since I have no LDAP, I can test nothing.
But...

An int is never a .w (only on 16bit CPUs)
Try to replace this with .l

Maybe something like this works:

Code: Select all

Structure ldap_control
  *oid    ; points to string
  *berval ; points to berval structure
  iscritical$
EndStructure

Structure ldap_berval
  len.l
  val$
EndStructure

Structure ldap_timeval
  secs.l
  usecs.l
EndStructure


PrototypeC.i p_ldap_search_ext_s(*ld, base$, scope.l, filter$, *attrs, attrsonly.l, *serverctrls.ldap_control, *clientctrls.ldap_control, *timeout.ldap_timeval, sizelimit.l, *res)
Bernd
User avatar
spikey
Enthusiast
Enthusiast
Posts: 750
Joined: Wed Sep 22, 2010 1:17 pm
Location: United Kingdom

Re: LDAP without COMatePlus

Post by spikey »

The prototype should look like:-

Code: Select all

PrototypeC.i p_LDAP_search_ext_s(*ld, *base, scope.i, *filter, *attrs, attrsonly.i, *serverctrls, *clientctrls, *timeout.ldap_timeval, sizelimit.i, *results)
You need to supply pointer values to the strings for *base and *filter with @.

BTW **Attrs literally means a pointer to a pointer to "something" (in this case a string) - its to do with the way that arrays of things other than primitive types are actually stored in memory.
mikejs
Enthusiast
Enthusiast
Posts: 175
Joined: Thu Oct 21, 2010 9:46 pm

Re: LDAP without COMatePlus

Post by mikejs »

Cheers guys.

I think I'm getting somewhere with this - I'm able to fire off queries, and get strings or binary blobs back correctly.

I'll tidy up what I've got and post it on this thread in case anyone else finds it useful - probably tomorrow.

Two things confused the heck out of me and took a while to get past. Firstly, although the documentation is for the "Novell LDAP Libraries for C", things don't work properly with PrototypeC, but do work fine with plain old Prototype.

The second gotcha was passing a pointer to something that returns a pointer. This is the search_ext_s prototype that worked in the end (passing the strings as strings rather than pointers to strings seems to be fine - the right base and filter are used in the search):

Code: Select all

Prototype.i p_LDAP_search_ext_s(ld.l, base$, scope.l, filter$, *attrs, attrsonly.l, *serverctrls.ldapcontrol, *clientctrls.ldapcontrol, *timeout.ldap_timeval, sizelimit.l, results.i)
I kept trying to call it with code like this:

Code: Select all

Define *ldapresults
result=LDAP_search_ext_s(ldap_handle, base$, #LDAP_SCOPE_SUBTREE, filter$, @attrs$(), 0, 0, 0, @timeout, 0, *ldapresults)
But what it actually wants is this:

Code: Select all

Define ldapresults.i
result=LDAP_search_ext_s(ldap_handle, base$, #LDAP_SCOPE_SUBTREE, filter$, @attrs$(), 0, 0, 0, @timeout, 0, @ldapresults)
So in my earlier attempts I was defining a pointer, but that pointer was still zero (no memory allocated to it) and I was then passing that to ldap as the location to store the results. What it actually does is allocate its own memory to store the results, and pass you a pointer to it.

Another thing that took a little while to unpick, having got working 32bit code, was getting the 64bit version working properly. This was mostly just a case of figuring out where to use .i and where to use .l, but the berval structure still didn't work properly. Despite the C stuff talking about "char"s, the right struct looks like this:

Code: Select all

Structure ldap_berval
  len.l   ; specifies length of value (bytes)
  *value  ; value
EndStructure
Obviously *value is a pointer, and is 32bit on x86 and 64bit on x64. But, on x64, it appears to get populated the wrong way up, with the low 32bits swapped with the high 32bits. I ended up having to do this sort of thing to get the pointer to the right location:

Code: Select all

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  upper=(*berval\value)>>32
  lower=(*berval\value) & $00000000ffffffff
  fixedpointer=(lower<<32) | upper
CompilerEndIf
mikejs
Enthusiast
Enthusiast
Posts: 175
Joined: Thu Oct 21, 2010 9:46 pm

Re: LDAP without COMatePlus [SOLVED]

Post by mikejs »

Ok, here's the wrapper stuff. This only imports the functions I needed for what I was doing, but it should be a useful starting point if anyone else needs to go further with it.

Code: Select all

EnableExplicit

;- structures
Structure ldapcontrol
  *oid    ; points to string
  *berval ; points to berval structure
  iscritical$
EndStructure

Structure ldap_berval
  len.l   ; specifies length of value (bytes)
  *value  ; value
EndStructure

Structure ldap_timeval
  secs.l
  usecs.l
EndStructure

;- constants
;- --return codes
;    server
#LDAP_SUCCESS                         = 0
#LDAP_OPERATIONS_ERROR                = 1
#LDAP_PROTOCOL_ERROR                  = 2
#LDAP_TIMELIMIT_EXCEEDED              = 3
#LDAP_SIZELIMIT_EXCEEDED              = 4
#LDAP_COMPARE_FALSE                   = 5
#LDAP_COMPARE_TRUE                    = 6
#LDAP_AUTH_METHOD_NOT_SUPPORTED       = 7
#LDAP_STRONG_AUTH_REQUIRED            = 8
#LDAP_REFERRAL                        = 10
#LDAP_ADMINLIMIT_EXCEEDED             = 11
#LDAP_UNAVAILABLE_CRITICAL_EXTENSION  = 12
#LDAP_CONFIDENTIALITY_REQUIRED        = 13
#LDAP_SASL_BIND_IN_PROGRESS           = 14
#LDAP_NO_SUCH_ATTRIBUTE               = 16
#LDAP_UNDEFINED_TYPE                  = 17
#LDAP_INAPPROPRIATE_MATCHING          = 18
#LDAP_CONSTRAINT_VIOLATION            = 19
#LDAP_TYPE_OR_VALUE_EXISTS            = 20
#LDAP_INVALID_SYNTAX                  = 21
#LDAP_NO_SUCH_OBJECT                  = 32
#LDAP_ALIAS_PROBLEM                   = 33
#LDAP_INVALID_DN_SYNTAX               = 34
#LDAP_IS_LEAF                         = 35
#LDAP_ALIAS_DEREF_PROBLEM             = 36
#LDAP_INAPPROPRIATE_AUTH              = 48
#LDAP_INVALID_CREDENTIALS             = 49
#LDAP_INSUFFICIENT_ACCESS             = 50
#LDAP_BUSY                            = 51
#LDAP_UNAVAILABLE                     = 52
#LDAP_UNWILLING_TO_PERFORM            = 53
#LDAP_LOOP_DETECT                     = 54
#LDAP_NAMING_VIOLATION                = 64
#LDAP_OBJECT_CLASS_VIOLATION          = 65
#LDAP_NOT_ALLOWED_ON_NONLEAF          = 66
#LDAP_NOT_ALLOWED_ON_RDN              = 67
#LDAP_ALREADY_EXISTS                  = 68
#LDAP_NO_OBJECT_CLASS_MODS            = 69
#LDAP_RESULTS_TOO_LARGE               = 70
#LDAP_AFFECTS_MULTIPLE_DSAS           = 71
#LDAP_OTHER                           = 80
;    client
#LDAP_SERVER_DOWN                     = 81
#LDAP_LOCAL_ERROR                     = 82
#LDAP_ENCODING_ERROR                  = 83
#LDAP_DECODING_ERROR	               = 84
#LDAP_TIMEOUT                         = 85
#LDAP_AUTH_UNKNOWN                    = 86
#LDAP_FILTER_ERROR                    = 87
#LDAP_USER_CANCELLED                  = 88
#LDAP_PARAM_ERROR                     = 89
#LDAP_NO_MEMORY                       = 90
#LDAP_CONNECT_ERROR                   = 91
#LDAP_NOT_SUPPORTED                   = 92
#LDAP_CONTROL_NOT_FOUND               = 93
#LDAP_NO_RESULTS_RETURNED             = 94
#LDAP_MORE_RESULTS_TO_RETURN          = 95
#LDAP_CLIENT_LOOP                     = 96
#LDAP_REFERRAL_LIMIT_EXCEEDED         = 97

;- --scopes
#LDAP_SCOPE_BASE                      = 0
#LDAP_SCOPE_ONELEVEL                  = 1
#LDAP_SCOPE_SUBTREE                   = 2

;- prototypes
Prototype.i p_LDAP_init(host$, port.l=389)
Prototype.i p_LDAP_simple_bind_s(ld.l, dn$, pw$)
Prototype.i p_LDAP_unbind(ld.l)
Prototype.i p_LDAP_err2string(err.l)
Prototype.i p_LDAP_search_ext_s(ld.l, base$, scope.l, filter$, *attrs, attrsonly.l, *serverctrls.ldapcontrol, *clientctrls.ldapcontrol, *timeout.ldap_timeval, sizelimit.l, results.i)
Prototype.i p_LDAP_get_dn(ld.l, entry.l)
Prototype.i p_LDAP_count_entries(ld.l, *results)
Prototype.i p_LDAP_first_entry(ld.l, *results)
Prototype.i p_LDAP_next_entry(ld.l, *results)
Prototype.i p_LDAP_first_attribute(ld.l, entry.l, *berelement)
Prototype.i p_LDAP_next_attribute(ld.l, entry.l, *berelement)
Prototype.i p_LDAP_count_values(*values)
Prototype.i p_LDAP_count_values_len(*values)
Prototype.i p_LDAP_get_values(ld.l, entry.l, attribute$)
Prototype.i p_LDAP_get_values_len(ld.l, entry.l, attribute$)
Prototype.i p_LDAP_value_free(*values)
Prototype.i p_LDAP_value_free_len(*values)
Prototype.i p_LDAP_memfree(*mem)
Prototype.i p_LDAP_ber_free(*berelement, fbuf.l)

;- variables
Global.i ldap_lib
Global LDAP_init.p_LDAP_init
Global LDAP_simple_bind_s.p_LDAP_simple_bind_s
Global LDAP_unbind.p_LDAP_unbind
Global LDAP_err2string.p_LDAP_err2string
Global LDAP_search_ext_s.p_LDAP_search_ext_s
Global LDAP_get_dn.p_LDAP_get_dn
Global LDAP_count_entries.p_LDAP_count_entries
Global LDAP_first_entry.p_LDAP_first_entry
Global LDAP_next_entry.p_LDAP_next_entry
Global LDAP_first_attribute.p_LDAP_first_attribute
Global LDAP_next_attribute.p_LDAP_next_attribute
Global LDAP_count_values.p_LDAP_count_values
Global LDAP_count_values_len.p_LDAP_count_values_len
Global LDAP_get_values.p_LDAP_get_values
Global LDAP_get_values_len.p_LDAP_get_values_len
Global LDAP_value_free.p_LDAP_value_free
Global LDAP_value_free_len.p_LDAP_value_free_len
Global LDAP_memfree.p_LDAP_memfree
Global LDAP_ber_free.p_LDAP_ber_free


Procedure LDAPLoadLib()
  ldap_lib=OpenLibrary(#PB_Any, "ldapsdk.dll")
  If ldap_lib
    Debug "Opened LDAPSDK"   
    LDAP_init.p_LDAP_init=GetFunction(ldap_lib, "ldap_init")
    LDAP_simple_bind_s.p_LDAP_simple_bind_s=GetFunction(ldap_lib, "ldap_simple_bind_s")
    LDAP_unbind.p_LDAP_unbind=GetFunction(ldap_lib, "ldap_unbind")
    LDAP_err2string.p_LDAP_err2string=GetFunction(ldap_lib, "ldap_err2string")
    LDAP_search_ext_s.p_LDAP_search_ext_s=GetFunction(ldap_lib, "ldap_search_ext_s")
    LDAP_get_dn.p_LDAP_get_dn=GetFunction(ldap_lib, "ldap_get_dn")
    LDAP_count_entries.p_LDAP_count_entries=GetFunction(ldap_lib, "ldap_count_entries")
    LDAP_first_entry.p_LDAP_first_entry=GetFunction(ldap_lib, "ldap_first_entry")
    LDAP_next_entry.p_LDAP_next_entry=GetFunction(ldap_lib, "ldap_next_entry")
    LDAP_first_attribute.p_LDAP_first_attribute=GetFunction(ldap_lib, "ldap_first_attribute")
    LDAP_next_attribute.p_LDAP_next_attribute=GetFunction(ldap_lib, "ldap_next_attribute")
    LDAP_count_values.p_LDAP_count_values=GetFunction(ldap_lib, "ldap_count_values")
    LDAP_count_values_len.p_LDAP_count_values_len=GetFunction(ldap_lib, "ldap_count_values_len")
    LDAP_get_values.p_LDAP_get_values=GetFunction(ldap_lib, "ldap_get_values")
    LDAP_get_values_len.p_LDAP_get_values_len=GetFunction(ldap_lib, "ldap_get_values_len")
    LDAP_value_free.p_LDAP_value_free=GetFunction(ldap_lib, "ldap_value_free")
    LDAP_value_free_len.p_LDAP_value_free_len=GetFunction(ldap_lib, "ldap_value_free_len")
    LDAP_memfree.p_LDAP_memfree=GetFunction(ldap_lib, "ldap_memfree")
    LDAP_ber_free.p_LDAP_ber_free=GetFunction(ldap_lib, "ber_free")
  Else
    Debug "Unable to load LDAP library"
  EndIf
EndProcedure

Procedure.s LDAPError(err.i)
  Protected.s out$
  Protected *string
  out$="Unknown Error"
  *string=LDAP_err2string(err)
  If *string
    out$=PeekS(*string,-1,#PB_Ascii)
  EndIf
  ProcedureReturn out$
EndProcedure
And here's some code that uses it.

Code: Select all

Define.i ld
Define.i ldapresults, ldapentry, entries, c, d, e, f, values, result, length, berelement
Define timeout.ldap_timeval
Define Dim attrs$(10)
Define.s base$, filter$, attribute$
Define *tmp
Define *values
Define.b flag
Define *berval.ldap_berval
Define.i fixedpointer
Define.l upper, lower
Define.s attribstring$, octetstring$
Define.s dn$, cn$, memberof$, department$, objectsid$
Define.b sizeofint

; probably an easier way of doing this, but this works.
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  sizeofint=8
CompilerElse
  sizeofint=4
CompilerEndIf

LDAPLoadLib()
ld=LDAP_init("mydomain.com")
If ld
  result=LDAP_simple_bind_s(ld, "username@mydomain", "password")
  Debug "result: "+Str(result)+" "+LDAPError(result)
  base$="OU=Whatever,DC=mydomain,DC=com"
  filter$="(&(objectClass=User)(samAccountName=someuser))"
  attrs$(0)="cn"
  attrs$(1)="department"
  attrs$(2)="memberOf"
  attrs$(3)="objectSid"
  attrs$(4)=#NULL$
  timeout\secs=10
  timeout\usecs=0
  result=LDAP_search_ext_s(ld, base$, #LDAP_SCOPE_SUBTREE, filter$, @attrs$(), 0, 0, 0, @timeout, 1, @ldapresults)
  Debug "result: "+Str(result)+" "+LDAPError(result)
  If result=#LDAP_SUCCESS
    entries=LDAP_count_entries(ld, ldapresults)
    Debug "entries: "+Str(entries)
    If entries
      ; loop through the objects we got back
      For c=1 To entries
        If c=1
          ldapentry=LDAP_first_entry(ld, ldapresults)
        Else
          ldapentry=LDAP_next_entry(ld, ldapresults)
        EndIf
        If ldapentry
          *tmp=LDAP_get_dn(ld, ldapentry)
          If *tmp
            dn$=PeekS(*tmp)
            Debug "Object dn: "+dn$
            LDAP_memfree(*tmp)
          EndIf
          ; Ok, now the attributes.
          ; In this case we asked for specific attributes, so we know how many there will be and in practice
          ; they seem to arrive in the order we asked for them. If we'd asked for all attributes we wouldn't
          ; know how many or what the order would be though.
          d=1
          flag=#False
          Repeat
            If d=1
              *tmp=LDAP_first_attribute(ld, ldapentry, @berelement)
            Else
              ; Yes. next_attribute takes berelement while the first_attribute one needs @berelement
              ; This seems to be to do with first_attribute creating the structure, and next_attribute
              ; reusing it. Either way, the docs and example C source do the same thing.
              ; The berelement structure is "opaque", but we still have to call something to free it at the end.
              *tmp=LDAP_next_attribute(ld, ldapentry, berelement)
            EndIf
            If *tmp=0
              flag=#True
            Else
              attribute$=LCase(PeekS(*tmp))
              Debug "Attribute: "+attribute$
              ; OK, here we go two ways depending on whether it's a string attribute or not.
              ; There's probably a way of finding out the type but I haven't needed to do that so I didn't.
              ; Doing this without hardcoding attribute types is left as an exercise for the reader...
              If attribute$="cn" Or attribute$="memberof" Or attribute$="department"
                Debug "Handling string attribute"
                *values=LDAP_get_values(ld, ldapentry, attribute$)
                values=LDAP_count_values(*values)
                ; attributes like memberOf can have multiple values, so we may need to loop through those.
                Debug "Values: "+Str(values)
                If values
                  For e=0 To values-1
                    attribstring$=PeekS(PeekI(*values+(sizeofint*e)))
                    ;logfile("value: "+Str(e)+" "+attribstring$)
                    If attribute$="memberof"
                      If memberof$=""
                        memberof$=attribstring$
                      Else
                        memberof$+#CRLF$+attribstring$
                      EndIf
                    EndIf
                  Next e
                  ; Assume cn and department have just one value.
                  If attribute$="cn" : cn$=attribstring$ : EndIf
                  If attribute$="department" : department$=attribstring$ : EndIf
                  ; done with this set of values
                  LDAP_value_free(*values)
                Else
                  Debug "Attribute "+attribute$+" has no values."
                EndIf
              Else
                Debug "Handling binary attribute"
                *values=LDAP_get_values_len(ld, ldapentry, attribute$)
                values=LDAP_count_values_len(*values)
                ; again, potentially multi-valued.
                Debug "Values: "+Str(values)
                If values
                  For e=0 To values-1
                    *berval=PeekI(*values+(sizeofint*e))
                    length=*berval\len
                    octetstring$=""
                    ; Right.
                    ; When in 64bit mode, *berval\value contains a pointer to the value, but
                    ; upsidedown, with the least significant 32bit int first.
                    ; i.e. you get something like $4A32751600000000
                    ; The right value is actually $000000004A327516
                    ; But, there's no guarantee that the most significant int is all zeros.
                    ; so, we have to swap them over.
                    fixedpointer=*berval\value
                    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
                      upper=(*berval\value)>>32
                      lower=(*berval\value) & $00000000ffffffff
                      fixedpointer=(lower<<32) | upper
                    CompilerEndIf
                    
                    For f=0 To length-1
                      octetstring$+RSet(Right(Hex(PeekB(fixedpointer+f)),2) ,2,"0")
                    Next f
                    If attribute$="objectsid" : objectsid$=octetstring$ : EndIf
                  Next e
                  ; done with this set of values
                  LDAP_value_free_len(*values)
                Else
                  Debug "Attribute has no values."
                EndIf
              EndIf
              ; done with this attribute
              LDAP_memfree(*tmp)
            EndIf
            d+1
          Until flag
          ; no more attributes on this entry.
          LDAP_ber_free(berelement,1)
        Else
          Debug "Invalid ldap entry"
        EndIf
      Next c
      
      Debug "dn:            "+dn$
      Debug "cn:            "+cn$
      Debug "department:    "+department$
      Debug "memberof:      "+memberof$
      Debug "objectsid:     "+objectsid$
    Else
      Debug "LDAP query failed. No entries returned."
    EndIf
  Else
    Debug "LDAP query failed. Query not valid?"
  EndIf
  ; Done with ldap.
  LDAP_unbind(ld)
Else
  Debug "LDAP init failed"
EndIf
This all works for me in PB 5.10, 32 or 64bit, using the Novell LDAPSDK.DLL 3.5.2.0, from Oct 2009. Obviously the 64bit code needs to load the 64bit DLL, so LDAPLoadLib may need to be a bit more specific about where to find the DLL.
Atlante
User
User
Posts: 22
Joined: Mon Jun 27, 2011 1:39 pm
Location: FRANCE

Re: LDAP without COMatePlus [SOLVED]

Post by Atlante »

Hello mikejs,

I try to understand your code :) But i have an error :

ld=LDAP_init("mydomain.com")
accès mémoire invalide; (erreur de lecture à l'adresse 0) : invalid memory access, (read error at address 0)

Do you have an idea ?

Perhaps because it doesn't find ldapsdk.dll : OpenLibrary(#PB_Any, "ldapsdk.dll")

Could you help me ?i try to use it in my job for find users (login, name, mail ...) in active directory without mmc... only with cmd.

regards,

Atlante
Devops Engineer
PB French Moderator
http://www.purebasic.fr/french/
mikejs
Enthusiast
Enthusiast
Posts: 175
Joined: Thu Oct 21, 2010 9:46 pm

Re: LDAP without COMatePlus [SOLVED]

Post by mikejs »

It uses the Novell LDAP SDK library, downloadable from here:

http://www.novell.com/developer/ndk/lda ... for_c.html

NB, there are separate 32 and 64bit versions of the DLL.
Post Reply