├── LICENCE.txt ├── README.md ├── _examples ├── ldapsearch.go └── test-openldap.go ├── add-modify-delete.go ├── defines.go ├── openldap.go ├── options-errors.go ├── results.go └── types.go /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 - Marc Quinton. 2 | 3 | Use of this source code is governed by the MIT Licence : 4 | http://opensource.org/licenses/mit-license.php 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | "Software"), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OpenLDAP 2 | ==== 3 | 4 | this is Openldap binding in GO language. __I don't work any more with golang, so, please fork this project__. 5 | 6 | 7 | Installation : 8 | ----- 9 | 10 | Installation is easy and very quick, as you can see : 11 | 12 | # install openldap library and devel packages 13 | sudo apt-get install libldap libldap2-dev # debian/ubuntu. 14 | sudo urpmi openldap-devel # fedora, RH, ... 15 | 16 | # install go 17 | go get github.com/mqu/openldap 18 | 19 | # verify you've got it : 20 | (cd $GOPATH ; go list ./...) | grep openldap 21 | 22 | Usage 23 | ---- 24 | 25 | - Look a this [exemple](https://github.com/mqu/openldap/blob/master/_examples/test-openldap.go). 26 | - a more complex example making [LDAP search](https://github.com/mqu/openldap/blob/master/_examples/ldapsearch.go) that mimics ldapsearch command, printing out result on console. 27 | 28 | Doc: 29 | --- 30 | - run _go doc openldap_, 31 | - will come soon, complete documentation in this [Wiki](https://github.com/mqu/openldap/wiki). 32 | - look at [_examples/](https://github.com/mqu/openldap/blob/master/_examples/)*.go to see how to use this library. 33 | 34 | Todo : 35 | ---- 36 | 37 | - thread-safe test, 38 | - complete LDAP:GetOption() and LDAP:SetOption() method : now, they work only for integer values, 39 | - avoid using deprecated function (see LDAP_DEPRECATED flag and "// DEPRECATED" comments in *.go sources), 40 | - write some tests, 41 | - verify memory leaks (Valgrind), 42 | - support LDIF format (in, out), 43 | - add support for external commands (ldapadd, ldapdelete) 44 | - create an LDAP CLI (command line interface), like lftp, with commands like shell, 45 | - a nice GUI with GTK, 46 | - proxy, server, 47 | - what else ? 48 | 49 | 50 | Links : 51 | ---- 52 | 53 | - goc : http://code.google.com/p/go-wiki/wiki/cgo (how to bind native libraries to GO) 54 | - Openldap library (and server) : http://www.openldap.org/ 55 | - Pure Go [LDAP](https://github.com/mmitton/ldap) library, with [ASN1](https://github.com/mmitton/asn1-ber) support. 56 | 57 | Licence : 58 | ---- 59 | 60 | Copyright (C) 2012 - Marc Quinton. 61 | 62 | Use of this source code is governed by the MIT Licence : 63 | http://opensource.org/licenses/mit-license.php 64 | 65 | Permission is hereby granted, free of charge, to any person obtaining 66 | a copy of this software and associated documentation files (the 67 | "Software"), to deal in the Software without restriction, including 68 | without limitation the rights to use, copy, modify, merge, publish, 69 | distribute, sublicense, and/or sell copies of the Software, and to 70 | permit persons to whom the Software is furnished to do so, subject to 71 | the following conditions: 72 | 73 | The above copyright notice and this permission notice shall be 74 | included in all copies or substantial portions of the Software. 75 | 76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 77 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 78 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 79 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 80 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 81 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 82 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 83 | -------------------------------------------------------------------------------- /_examples/ldapsearch.go: -------------------------------------------------------------------------------- 1 | package main 2 | /* 3 | * Author : Marc Quinton / 2012. 4 | * 5 | * ldapsearch command mimics openldap/seach command. Supported options : 6 | * - host : ldap[s]://hostname:port/ format, 7 | * - user, 8 | * - password, 9 | * - base 10 | * 11 | * arguments : filter [attributes] 12 | * - filter is an LDAP filter (ex: objectClass=*, cn=*admin*", ... 13 | * - attributes is an LDAP attribute list ; can be empty. ex: cn, sn, givenName, mail, ... 14 | * 15 | */ 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | "errors" 21 | "flag" 22 | "github.com/mqu/openldap" 23 | ) 24 | 25 | type LdapSearchOptions struct { 26 | host string 27 | user string 28 | passwd string 29 | 30 | base string 31 | filter string 32 | attributes []string 33 | 34 | scope int 35 | } 36 | 37 | type LdapSearchApp struct { 38 | ldap *openldap.Ldap 39 | opts *LdapSearchOptions 40 | } 41 | 42 | func NewLdapSearchApp() *LdapSearchApp{ 43 | app := new(LdapSearchApp) 44 | return app 45 | } 46 | 47 | // Show ldapsearch app usage 48 | func (self *LdapSearchApp) Usage(){ 49 | fmt.Printf("usage: %s filter [attribute list]\n", os.Args[0]) 50 | flag.PrintDefaults() 51 | } 52 | 53 | // Parse ldapsearch app options using flag package. 54 | func (self *LdapSearchApp) ParseOpts() (*LdapSearchOptions, error){ 55 | var opts LdapSearchOptions 56 | 57 | flag.StringVar(&opts.host, "host", "ldap://localhost:389/", "ldap server URL format : ldap[s]://hostname:port/") 58 | flag.StringVar(&opts.user, "user", "" , "user for authentification") 59 | flag.StringVar(&opts.passwd, "passwd", "" , "password for authentification") 60 | flag.StringVar(&opts.base, "base", "" , "base DN for search") 61 | 62 | flag.Parse() 63 | 64 | if flag.NArg() == 0 { 65 | self.Usage() 66 | return nil, errors.New(fmt.Sprintf("ParseOpts() error ; see usage for more information")) 67 | } 68 | 69 | opts.filter = flag.Arg(0) 70 | 71 | if len(flag.Args()) == 1 { 72 | opts.attributes = []string{} 73 | } else { 74 | opts.attributes = flag.Args()[1:] 75 | } 76 | 77 | return &opts, nil 78 | } 79 | 80 | // Connect and Bind to LDAP server using self.opts 81 | func (self *LdapSearchApp) Connect() (error){ 82 | 83 | var err error 84 | self.ldap, err = openldap.Initialize(self.opts.host) 85 | 86 | if err != nil { 87 | return err 88 | } 89 | 90 | //FIXME: should be an external option 91 | self.ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3) 92 | 93 | err = self.ldap.Bind(self.opts.user, self.opts.passwd) 94 | if err != nil { 95 | return err 96 | } 97 | 98 | return nil 99 | } 100 | 101 | // Close() disconnect application from Ldap server 102 | func (self *LdapSearchApp) Close() (error){ 103 | return self.ldap.Close() 104 | } 105 | 106 | // Search using filter and returning attributes list 107 | func (self *LdapSearchApp) Search() (*openldap.LdapSearchResult, error){ 108 | 109 | //FIXME: should be an external option 110 | scope := openldap.LDAP_SCOPE_SUBTREE 111 | 112 | return self.ldap.SearchAll( 113 | self.opts.base, 114 | scope, 115 | self.opts.filter, 116 | self.opts.attributes) 117 | } 118 | 119 | // Print search result 120 | func (self *LdapSearchApp) Print(res *openldap.LdapSearchResult) (error){ 121 | fmt.Println(res) 122 | return nil 123 | } 124 | 125 | func main() { 126 | 127 | var err error 128 | app := NewLdapSearchApp() 129 | 130 | app.opts, err = app.ParseOpts() 131 | 132 | if err != nil { 133 | fmt.Println(err) 134 | return 135 | } 136 | 137 | err = app.Connect() 138 | 139 | if err != nil { 140 | fmt.Println(err) 141 | return 142 | } 143 | 144 | result, err := app.Search() 145 | 146 | if(err != nil) { 147 | fmt.Println("search error:", err) 148 | return 149 | } 150 | 151 | app.Print(result) 152 | app.Close() 153 | 154 | } 155 | -------------------------------------------------------------------------------- /_examples/test-openldap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mqu/openldap" 6 | ) 7 | 8 | /* 9 | * 10 | * openldap example program : 11 | * 12 | * - 1 : 13 | * 14 | * - specify URL for LDAP connexion with user and passwd 15 | * - ldap and ldaps is supported, 16 | * - anonymous connexion is done with an empty user string 17 | * - base (DN) is needed for many LDAP server (it depends on LDAP data design) 18 | * 19 | * - 2 : 20 | * 21 | * - you can set some LDAP options. 22 | * - authentification with Bind() 23 | * 24 | * - 3 : setup LDAP query search. 25 | * - 4 : print search results. 26 | * 27 | */ 28 | func main() { 29 | 30 | var user, passwd, url, base string 31 | 32 | // (1) - connexion options 33 | url = "ldap://some.host:389/" 34 | // url = "ldaps://some.host:636/" 35 | user = "..." 36 | passwd = "..." 37 | base = "" 38 | 39 | ldap, err := openldap.Initialize(url) 40 | 41 | if err != nil { 42 | fmt.Printf("LDAP::Initialize() : connexion error\n") 43 | return 44 | } 45 | 46 | // (2.1) - options 47 | ldap.SetOption(openldap.LDAP_OPT_PROTOCOL_VERSION, openldap.LDAP_VERSION3) 48 | 49 | // (2.2) - authentification (Bind) 50 | err = ldap.Bind(user, passwd) 51 | if err != nil { 52 | fmt.Printf("LDAP::Bind() : bind error\n") 53 | fmt.Println(err) 54 | return 55 | } 56 | defer ldap.Close() 57 | 58 | // (3) : search method 59 | // -------------------------------------- Ldap::SearchAll() -------------------------------------- 60 | scope := openldap.LDAP_SCOPE_SUBTREE // LDAP_SCOPE_BASE, LDAP_SCOPE_ONELEVEL, LDAP_SCOPE_SUBTREE 61 | filter := "cn=*admin*" 62 | attributes := []string{"cn", "sn", "givenname", "mail"} // leave empty for all attributes 63 | 64 | // SearchAll(base string, scope int, filter string, attributes []string) (*LdapSearchResult, error) 65 | result, err := ldap.SearchAll(base, scope, filter, attributes) 66 | 67 | if err != nil { 68 | fmt.Println(err) 69 | return 70 | } 71 | 72 | // (4) - print LdapSearchResult(s) 73 | fmt.Printf("# num results : %d\n", result.Count()) 74 | fmt.Printf("# search : %s\n", result.Filter()) 75 | fmt.Printf("# base : %s\n", result.Base()) 76 | fmt.Printf("# attributes : [%s]\n", strings.Join(result.Attributes(), ", ")) 77 | 78 | for _, entry := range result.Entries() { 79 | fmt.Printf("dn=%s\n", entry.Dn()) 80 | for _, attr := range entry.Attributes() { 81 | fmt.Printf("%s=[%s]\n", attr.Name(), strings.Join(attr.Values(), ", ")) 82 | } 83 | 84 | fmt.Printf("\n") 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /add-modify-delete.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2012 - Marc Quinton. 4 | * 5 | * Use of this source code is governed by the MIT Licence : 6 | * http://opensource.org/licenses/mit-license.php 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | package openldap 29 | 30 | /* #include 31 | #include 32 | #define LDAP_DEPRECATED 1 33 | #include 34 | 35 | // goc can not use union on structs ; so create a new type with same attributes and size 36 | // fixme : support binary mods (mod_bvalues) 37 | typedef struct ldapmod_str { 38 | int mod_op; 39 | char *mod_type; 40 | char **mod_vals; 41 | } LDAPModStr; 42 | 43 | 44 | int _ldap_add(LDAP *ld, char* dn, LDAPModStr **attrs){ 45 | 46 | //API: int ldap_add_ext_s(LDAP *ld, char *dn, LDAPMod **attrs, LDAPControl *sctrls, LDAPControl *cctrls ); 47 | // nota : cast (LDAPMod **) is possible because structs have same size 48 | return ldap_add_ext_s(ld, dn, (LDAPMod **)attrs, NULL, NULL); 49 | } 50 | 51 | int _ldap_modify(LDAP *ld, char* dn, LDAPModStr **mods ){ 52 | 53 | // nota : cast (LDAPMod **) is possible because structs have same size 54 | return ldap_modify_ext_s( ld, dn, (LDAPMod **)mods, NULL, NULL); 55 | } 56 | 57 | int _ldap_rename (LDAP *ld, char *dn, char *newrdn, char *newSuperior, int deleteoldrdn){ 58 | //API: int ldap_rename_s( ld, dn, newrdn, newparent, deleteoldrdn, sctrls[], cctrls[]) 59 | 60 | return ldap_rename_s(ld, dn, newrdn, newSuperior, deleteoldrdn, NULL, NULL); 61 | } 62 | 63 | void _ldap_mods_free (LDAPModStr **mods, int freemods){ 64 | //API: void ldap_mods_free(LDAPMod **mods, int freemods); 65 | return ldap_mods_free((LDAPMod **)mods, freemods); 66 | } 67 | 68 | 69 | */ 70 | // #cgo CFLAGS: -DLDAP_DEPRECATED=1 71 | // #cgo linux CFLAGS: -DLINUX=1 72 | // #cgo LDFLAGS: -lldap -llber 73 | import "C" 74 | 75 | import ( 76 | "errors" 77 | "unsafe" 78 | "fmt" 79 | ) 80 | 81 | 82 | func (self *Ldap) doModify(dn string, attrs map[string][]string, changeType int, full_add bool) (int){ 83 | 84 | _dn := C.CString(dn) 85 | defer C.free(unsafe.Pointer(_dn)) 86 | 87 | mods := make([]*C.LDAPModStr, len(attrs)+1) 88 | // mods[len] = nil by default 89 | 90 | count:= 0 91 | for key, values := range attrs { 92 | 93 | // transform []string to C.char** null terminated array (attributes argument) 94 | _values := make([]*C.char, len(values)+1) // default set to nil (NULL in C) 95 | 96 | for i, value := range values { 97 | _values[i] = C.CString(value) 98 | defer C.free(unsafe.Pointer(_values[i])) 99 | } 100 | 101 | var mod C.LDAPModStr 102 | 103 | mod.mod_op = C.int(changeType) 104 | mod.mod_type = C.CString(key) 105 | mod.mod_vals = &_values[0] 106 | 107 | defer C.free(unsafe.Pointer(mod.mod_type)) 108 | 109 | mods[count] = &mod 110 | 111 | count++ 112 | } 113 | 114 | var rv int 115 | 116 | if full_add { 117 | // API: int ldap_add (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) 118 | rv = int(C._ldap_add(self.conn, _dn, &mods[0])) 119 | } else{ 120 | // API: int ldap_modify (LDAP *ld, LDAP_CONST char *dn, LDAPMod **mods ) 121 | rv = int(C._ldap_modify(self.conn, _dn, &mods[0])) 122 | } 123 | 124 | // FIXME: need to call ldap_mods_free(&mods) some where. 125 | // C._ldap_mods_free(&mods[0], 1) // not OK. 126 | return rv 127 | } 128 | 129 | func (self *Ldap) Modify(dn string, attrs map[string][]string) (error){ 130 | 131 | changeType := C.LDAP_MOD_REPLACE 132 | full_add := false 133 | rv := self.doModify(dn, attrs, changeType, full_add) 134 | 135 | if rv != LDAP_OPT_SUCCESS { 136 | return errors.New(fmt.Sprintf("LDAP::Modify() error : %d (%s)", rv, ErrorToString(rv))) 137 | } 138 | return nil 139 | } 140 | 141 | func (self *Ldap) ModifyDel(dn string, attrs map[string][]string) (error){ 142 | 143 | changeType := C.LDAP_MOD_DELETE 144 | full_add := false 145 | rv := self.doModify(dn, attrs, changeType, full_add) 146 | 147 | if rv != LDAP_OPT_SUCCESS { 148 | return errors.New(fmt.Sprintf("LDAP::ModifyDel() error : %d (%s)", rv, ErrorToString(rv))) 149 | } 150 | return nil 151 | } 152 | 153 | func (self *Ldap) ModifyAdd(dn string, attrs map[string][]string) (error){ 154 | 155 | changeType := C.LDAP_MOD_ADD 156 | full_add := false 157 | rv := self.doModify(dn, attrs, changeType, full_add) 158 | if rv != LDAP_OPT_SUCCESS { 159 | return errors.New(fmt.Sprintf("LDAP::ModifyAdd() error : %d (%s)", rv, ErrorToString(rv))) 160 | } 161 | return nil 162 | } 163 | 164 | func (self *Ldap) Add(dn string, attrs map[string][]string) (error){ 165 | 166 | changeType := C.LDAP_MOD_ADD 167 | full_add := true 168 | rv := self.doModify(dn, attrs, changeType, full_add) 169 | if rv != LDAP_OPT_SUCCESS { 170 | return errors.New(fmt.Sprintf("LDAP::Add() error : %d (%s)", rv, ErrorToString(rv))) 171 | } 172 | return nil 173 | } 174 | 175 | func (self *Ldap) Delete(dn string) (error){ 176 | 177 | _dn := C.CString(dn) 178 | defer C.free(unsafe.Pointer(_dn)) 179 | 180 | // API: int ldap_delete (LDAP *ld, LDAP_CONST char *dn) 181 | rv := C.ldap_delete_s(self.conn, _dn) 182 | 183 | if rv != LDAP_OPT_SUCCESS { 184 | return errors.New(fmt.Sprintf("LDAP::Delete() error : %d (%s)", rv, ErrorToString(int(rv)))) 185 | } 186 | 187 | return nil 188 | } 189 | 190 | // Rename() to rename LDAP entries. 191 | // 192 | // These routines are used to perform a LDAP rename operation. The function changes the leaf compo- 193 | // nent of an entry's distinguished name and optionally moves the entry to a new parent container. 194 | // The ldap_rename_s performs a rename operation synchronously. The method takes dn, which points to 195 | // the distinguished name of the entry whose attribute is being compared, newparent,the distinguished 196 | // name of the entry's new parent. If this parameter is NULL, only the RDN is changed. The root DN is 197 | // specified by passing a zero length string, "". deleteoldrdn specifies whether the old RDN should 198 | // be retained or deleted. Zero indicates that the old RDN should be retained. If you choose this 199 | // option, the attribute will contain both names (the old and the new). Non-zero indicates that the 200 | // old RDN should be deleted. serverctrls points to an array of LDAPControl structures that list the 201 | // client controls to use with this extended operation. Use NULL to specify no client controls. 202 | // clientctrls points to an array of LDAPControl structures that list the client controls to use with 203 | // the search. 204 | // FIXME: support NULL and "" values for newSuperior parameter. 205 | // 206 | func (self *Ldap) Rename(dn string, newrdn string, newSuperior string, deleteOld bool) (error){ 207 | 208 | _dn := C.CString(dn) 209 | defer C.free(unsafe.Pointer(_dn)) 210 | 211 | _newrdn := C.CString(newrdn) 212 | defer C.free(unsafe.Pointer(_newrdn)) 213 | 214 | _newSuperior := C.CString(newSuperior) 215 | defer C.free(unsafe.Pointer(_newSuperior)) 216 | 217 | var _delete C.int = 0 218 | if deleteOld { 219 | _delete = 1 220 | } 221 | 222 | // API: int ldap_rename (LDAP *ld, char *newrdn, char *newSuperior, int deleteoldrdn) 223 | rv := C._ldap_rename(self.conn, _dn, _newrdn, _newSuperior, _delete) 224 | 225 | if rv != LDAP_OPT_SUCCESS { 226 | return errors.New(fmt.Sprintf("LDAP::Rename() error : %d (%s)", rv, ErrorToString(int(rv)))) 227 | } 228 | 229 | return nil 230 | } 231 | -------------------------------------------------------------------------------- /defines.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2012 - Marc Quinton. 4 | * 5 | * Use of this source code is governed by the MIT Licence : 6 | * http://opensource.org/licenses/mit-license.php 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | package openldap 29 | 30 | const ( 31 | // first version for this GO API binding 32 | OPENLDAP_API_BINDING_VERSION = "0.2" 33 | ) 34 | 35 | const ( 36 | LDAP_VERSION1 = 1 37 | LDAP_VERSION2 = 2 38 | LDAP_VERSION3 = 3 39 | ) 40 | 41 | const ( 42 | LDAP_VERSION_MIN = LDAP_VERSION2 43 | LDAP_VERSION = LDAP_VERSION2 44 | LDAP_VERSION_MAX = LDAP_VERSION3 45 | ) 46 | 47 | const ( 48 | LDAP_API_VERSION = 3001 49 | LDAP_VENDOR_NAME = "OpenLDAP" 50 | ) 51 | 52 | const ( 53 | LDAP_PORT = 389 54 | LDAPS_PORT = 636 55 | ) 56 | 57 | const ( 58 | LDAP_OPT_SUCCESS = 0 59 | LDAP_OPT_ERROR = -1 60 | ) 61 | 62 | // search scopes 63 | const ( 64 | LDAP_SCOPE_BASE = 0x0000 65 | LDAP_SCOPE_ONELEVEL = 0x0001 66 | LDAP_SCOPE_SUBTREE = 0x0002 67 | LDAP_SCOPE_SUBORDINATE = 0x0003 // OpenLDAP extension 68 | LDAP_SCOPE_DEFAULT = -1 // OpenLDAP extension 69 | ) 70 | 71 | const ( 72 | LDAP_SCOPE_BASEOBJECT = LDAP_SCOPE_BASE 73 | LDAP_SCOPE_ONE = LDAP_SCOPE_ONELEVEL 74 | LDAP_SCOPE_SUB = LDAP_SCOPE_SUBTREE 75 | LDAP_SCOPE_CHILDREN = LDAP_SCOPE_SUBORDINATE 76 | ) 77 | 78 | const ( 79 | LDAP_RES_ANY = -1 80 | LDAP_RES_UNSOLICITED = 0 81 | ) 82 | 83 | //const ( 84 | //LDAP_API_FEATURE_THREAD_SAFE = 1 85 | //LDAP_API_FEATURE_SESSION_THREAD_SAFE = 1 86 | //LDAP_API_FEATURE_OPERATION_THREAD_SAFE = 1 87 | //) 88 | 89 | const ( 90 | LDAP_SUCCESS = 0x00 91 | LDAP_OPERATIONS_ERROR = 0x01 92 | LDAP_PROTOCOL_ERROR = 0x02 93 | LDAP_TIMELIMIT_EXCEEDED = 0x03 94 | LDAP_SIZELIMIT_EXCEEDED = 0x04 95 | LDAP_COMPARE_FALSE = 0x05 96 | LDAP_COMPARE_TRUE = 0x06 97 | LDAP_AUTH_METHOD_NOT_SUPPORTED = 0x07 98 | LDAP_STRONG_AUTH_REQUIRED = 0x08 99 | // Not used in LDAPv3 100 | LDAP_PARTIAL_RESULTS = 0x09 101 | 102 | // Next 5 new in LDAPv3 103 | LDAP_REFERRAL = 0x0a 104 | LDAP_ADMINLIMIT_EXCEEDED = 0x0b 105 | LDAP_UNAVAILABLE_CRITICAL_EXTENSION = 0x0c 106 | LDAP_CONFIDENTIALITY_REQUIRED = 0x0d 107 | LDAP_SASL_BIND_INPROGRESS = 0x0e 108 | 109 | LDAP_NO_SUCH_ATTRIBUTE = 0x10 110 | LDAP_UNDEFINED_TYPE = 0x11 111 | LDAP_INAPPROPRIATE_MATCHING = 0x12 112 | LDAP_CONSTRAINT_VIOLATION = 0x13 113 | LDAP_TYPE_OR_VALUE_EXISTS = 0x14 114 | LDAP_INVALID_SYNTAX = 0x15 115 | 116 | LDAP_NO_SUCH_OBJECT = 0x20 /* 32 */ 117 | LDAP_ALIAS_PROBLEM = 0x21 118 | LDAP_INVALID_DN_SYNTAX = 0x22 119 | // Next two not used in LDAPv3 120 | LDAP_IS_LEAF = 0x23 121 | LDAP_ALIAS_DEREF_PROBLEM = 0x24 122 | 123 | LDAP_INAPPROPRIATE_AUTH = 0x30 /* 48 */ 124 | LDAP_INVALID_CREDENTIALS = 0x31 /* 49 */ 125 | LDAP_INSUFFICIENT_ACCESS = 0x32 126 | LDAP_BUSY = 0x33 127 | LDAP_UNAVAILABLE = 0x34 128 | LDAP_UNWILLING_TO_PERFORM = 0x35 129 | LDAP_LOOP_DETECT = 0x36 130 | 131 | LDAP_SORT_CONTROL_MISSING = 0x3C /* 60 */ 132 | LDAP_INDEX_RANGE_ERROR = 0x3D /* 61 */ 133 | 134 | LDAP_NAMING_VIOLATION = 0x40 135 | LDAP_OBJECT_CLASS_VIOLATION = 0x41 136 | LDAP_NOT_ALLOWED_ON_NONLEAF = 0x42 137 | LDAP_NOT_ALLOWED_ON_RDN = 0x43 138 | LDAP_ALREADY_EXISTS = 0x44 /* 68 */ 139 | LDAP_NO_OBJECT_CLASS_MODS = 0x45 140 | LDAP_RESULTS_TOO_LARGE = 0x46 141 | // Next two for LDAPv3 142 | LDAP_AFFECTS_MULTIPLE_DSAS = 0x47 143 | LDAP_OTHER = 0x50 144 | 145 | // Used by some APIs 146 | LDAP_SERVER_DOWN = 0x51 147 | LDAP_LOCAL_ERROR = 0x52 148 | LDAP_ENCODING_ERROR = 0x53 149 | LDAP_DECODING_ERROR = 0x54 150 | LDAP_TIMEOUT = 0x55 151 | LDAP_AUTH_UNKNOWN = 0x56 152 | LDAP_FILTER_ERROR = 0x57 /* 87 */ 153 | LDAP_USER_CANCELLED = 0x58 154 | LDAP_PARAM_ERROR = 0x59 155 | LDAP_NO_MEMORY = 0x5a 156 | 157 | // Preliminary LDAPv3 codes 158 | LDAP_CONNECT_ERROR = 0x5b 159 | LDAP_NOT_SUPPORTED = 0x5c 160 | LDAP_CONTROL_NOT_FOUND = 0x5d 161 | LDAP_NO_RESULTS_RETURNED = 0x5e 162 | LDAP_MORE_RESULTS_TO_RETURN = 0x5f 163 | LDAP_CLIENT_LOOP = 0x60 164 | LDAP_REFERRAL_LIMIT_EXCEEDED = 0x61 165 | ) 166 | 167 | const ( 168 | LDAP_DEREF_NEVER = 0 169 | LDAP_DEREF_SEARCHING = 1 170 | LDAP_DEREF_FINDING = 2 171 | LDAP_DEREF_ALWAYS = 3 172 | ) 173 | 174 | const ( 175 | LDAP_NO_LIMIT = 0 176 | ) 177 | 178 | const ( 179 | LDAP_MSG_ONE = 0 180 | LDAP_MSG_ALL = 1 181 | LDAP_MSG_RECEIVED = 2 182 | ) 183 | 184 | // LDAP_OPTions 185 | // 0x0000 - 0x0fff reserved for api options 186 | // 0x1000 - 0x3fff reserved for api extended options 187 | // 0x4000 - 0x7fff reserved for private and experimental options 188 | 189 | const ( 190 | LDAP_OPT_API_INFO = 0x0000 191 | LDAP_OPT_DESC = 0x0001 // historic 192 | LDAP_OPT_DEREF = 0x0002 193 | LDAP_OPT_SIZELIMIT = 0x0003 194 | LDAP_OPT_TIMELIMIT = 0x0004 195 | // 0x05 - 0x07 not defined 196 | 197 | LDAP_OPT_REFERRALS = 0x0008 198 | LDAP_OPT_RESTART = 0x0009 199 | // 0x0a - 0x10 not defined 200 | 201 | LDAP_OPT_PROTOCOL_VERSION = 0x0011 202 | LDAP_OPT_SERVER_CONTROLS = 0x0012 203 | LDAP_OPT_CLIENT_CONTROLS = 0x0013 204 | // 0x14 not defined 205 | 206 | LDAP_OPT_API_FEATURE_INFO = 0x0015 207 | // 0x16 - 0x2f not defined 208 | 209 | LDAP_OPT_HOST_NAME = 0x0030 210 | LDAP_OPT_RESULT_CODE = 0x0031 211 | LDAP_OPT_ERROR_NUMBER = LDAP_OPT_RESULT_CODE 212 | LDAP_OPT_DIAGNOSTIC_MESSAGE = 0x0032 213 | LDAP_OPT_ERROR_STRING = LDAP_OPT_DIAGNOSTIC_MESSAGE 214 | LDAP_OPT_MATCHED_DN = 0x0033 215 | // 0x0034 - 0x3fff not defined 216 | 217 | // 0x0091 used by Microsoft for LDAP_OPT_AUTO_RECONNECT 218 | 219 | LDAP_OPT_SSPI_FLAGS = 0x0092 220 | // 0x0093 used by Microsoft for LDAP_OPT_SSL_INFO 221 | 222 | // 0x0094 used by Microsoft for LDAP_OPT_REF_DEREF_CONN_PER_MSG 223 | 224 | LDAP_OPT_SIGN = 0x0095 225 | LDAP_OPT_ENCRYPT = 0x0096 226 | LDAP_OPT_SASL_METHOD = 0x0097 227 | // 0x0098 used by Microsoft for LDAP_OPT_AREC_EXCLUSIVE 228 | 229 | LDAP_OPT_SECURITY_CONTEXT = 0x0099 230 | 231 | // 0x009A used by Microsoft for LDAP_OPT_ROOTDSE_CACHE 232 | 233 | // 0x009B - 0x3fff not defined 234 | 235 | ) 236 | 237 | // API Extensions 238 | 239 | const LDAP_OPT_API_EXTENSION_BASE = 0x4000 // API extensions 240 | 241 | // private and experimental options 242 | 243 | // OpenLDAP specific options 244 | 245 | const ( 246 | LDAP_OPT_DEBUG_LEVEL = 0x5001 // debug level 247 | LDAP_OPT_TIMEOUT = 0x5002 // default timeout 248 | LDAP_OPT_REFHOPLIMIT = 0x5003 // ref hop limit 249 | LDAP_OPT_NETWORK_TIMEOUT = 0x5005 // socket level timeout 250 | LDAP_OPT_URI = 0x5006 251 | LDAP_OPT_REFERRAL_URLS = 0x5007 // Referral URLs 252 | LDAP_OPT_SOCKBUF = 0x5008 // sockbuf 253 | LDAP_OPT_DEFBASE = 0x5009 // searchbase 254 | LDAP_OPT_CONNECT_ASYNC = 0x5010 // create connections asynchronously 255 | LDAP_OPT_CONNECT_CB = 0x5011 // connection callbacks 256 | LDAP_OPT_SESSION_REFCNT = 0x5012 // session reference count 257 | ) 258 | 259 | // OpenLDAP TLS options 260 | 261 | const ( 262 | LDAP_OPT_X_TLS = 0x6000 263 | LDAP_OPT_X_TLS_CTX = 0x6001 // OpenSSL CTX* 264 | LDAP_OPT_X_TLS_CACERTFILE = 0x6002 265 | LDAP_OPT_X_TLS_CACERTDIR = 0x6003 266 | LDAP_OPT_X_TLS_CERTFILE = 0x6004 267 | LDAP_OPT_X_TLS_KEYFILE = 0x6005 268 | LDAP_OPT_X_TLS_REQUIRE_CERT = 0x6006 269 | LDAP_OPT_X_TLS_PROTOCOL_MIN = 0x6007 270 | LDAP_OPT_X_TLS_CIPHER_SUITE = 0x6008 271 | LDAP_OPT_X_TLS_RANDOM_FILE = 0x6009 272 | LDAP_OPT_X_TLS_SSL_CTX = 0x600a // OpenSSL SSL* 273 | LDAP_OPT_X_TLS_CRLCHECK = 0x600b 274 | LDAP_OPT_X_TLS_CONNECT_CB = 0x600c 275 | LDAP_OPT_X_TLS_CONNECT_ARG = 0x600d 276 | LDAP_OPT_X_TLS_DHFILE = 0x600e 277 | LDAP_OPT_X_TLS_NEWCTX = 0x600f 278 | LDAP_OPT_X_TLS_CRLFILE = 0x6010 // GNUtls only 279 | LDAP_OPT_X_TLS_PACKAGE = 0x6011 280 | ) 281 | 282 | const ( 283 | LDAP_OPT_X_TLS_NEVER = 0 284 | LDAP_OPT_X_TLS_HARD = 1 285 | LDAP_OPT_X_TLS_DEMAND = 2 286 | LDAP_OPT_X_TLS_ALLOW = 3 287 | LDAP_OPT_X_TLS_TRY = 4 288 | ) 289 | 290 | const ( 291 | LDAP_OPT_X_TLS_CRL_NONE = 0 292 | LDAP_OPT_X_TLS_CRL_PEER = 1 293 | LDAP_OPT_X_TLS_CRL_ALL = 2 294 | ) 295 | 296 | // for LDAP_OPT_X_TLS_PROTOCOL_MIN 297 | 298 | //!!! const ( 299 | //!!! LDAP_OPT_X_TLS_PROTOCOL(maj,min) = (((maj) << 8) + (min)) 300 | //!!! LDAP_OPT_X_TLS_PROTOCOL_SSL2 = (2 << 8) 301 | //!!! LDAP_OPT_X_TLS_PROTOCOL_SSL3 = (3 << 8) 302 | //!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_0 = ((3 << 8) + 1) 303 | //!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_1 = ((3 << 8) + 2) 304 | //!!! LDAP_OPT_X_TLS_PROTOCOL_TLS1_2 = ((3 << 8) + 3) 305 | //!!! ) 306 | 307 | // OpenLDAP SASL options 308 | 309 | const ( 310 | LDAP_OPT_X_SASL_MECH = 0x6100 311 | LDAP_OPT_X_SASL_REALM = 0x6101 312 | LDAP_OPT_X_SASL_AUTHCID = 0x6102 313 | LDAP_OPT_X_SASL_AUTHZID = 0x6103 314 | LDAP_OPT_X_SASL_SSF = 0x6104 // read-only 315 | LDAP_OPT_X_SASL_SSF_EXTERNAL = 0x6105 // write-only 316 | LDAP_OPT_X_SASL_SECPROPS = 0x6106 // write-only 317 | LDAP_OPT_X_SASL_SSF_MIN = 0x6107 318 | LDAP_OPT_X_SASL_SSF_MAX = 0x6108 319 | LDAP_OPT_X_SASL_MAXBUFSIZE = 0x6109 320 | LDAP_OPT_X_SASL_MECHLIST = 0x610a // read-only 321 | LDAP_OPT_X_SASL_NOCANON = 0x610b 322 | LDAP_OPT_X_SASL_USERNAME = 0x610c // read-only 323 | LDAP_OPT_X_SASL_GSS_CREDS = 0x610d 324 | ) 325 | 326 | // OpenLDAP GSSAPI options 327 | 328 | const ( 329 | LDAP_OPT_X_GSSAPI_DO_NOT_FREE_CONTEXT = 0x6200 330 | LDAP_OPT_X_GSSAPI_ALLOW_REMOTE_PRINCIPAL = 0x6201 331 | ) 332 | 333 | // 334 | // OpenLDAP per connection tcp-keepalive settings 335 | // (Linux only, ignored where unsupported) 336 | const ( 337 | LDAP_OPT_X_KEEPALIVE_IDLE = 0x6300 338 | LDAP_OPT_X_KEEPALIVE_PROBES = 0x6301 339 | LDAP_OPT_X_KEEPALIVE_INTERVAL = 0x6302 340 | ) 341 | 342 | /* authentication methods available */ 343 | const ( 344 | LDAP_AUTH_NONE = 0x00 // no authentication 345 | LDAP_AUTH_SIMPLE = 0x80 // context specific + primitive 346 | LDAP_AUTH_SASL = 0xa3 // context specific + constructed 347 | LDAP_AUTH_KRBV4 = 0xff // means do both of the following 348 | LDAP_AUTH_KRBV41 = 0x81 // context specific + primitive 349 | LDAP_AUTH_KRBV42 = 0x82 // context specific + primitive 350 | ) 351 | -------------------------------------------------------------------------------- /openldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Openldap (2.4.30) binding in GO 3 | * 4 | * 5 | * link to ldap or ldap_r (for thread-safe binding) 6 | * 7 | * 8 | * Copyright (C) 2012 - Marc Quinton. 9 | * 10 | * Use of this source code is governed by the MIT Licence : 11 | * http://opensource.org/licenses/mit-license.php 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining 14 | * a copy of this software and associated documentation files (the 15 | * "Software"), to deal in the Software without restriction, including 16 | * without limitation the rights to use, copy, modify, merge, publish, 17 | * distribute, sublicense, and/or sell copies of the Software, and to 18 | * permit persons to whom the Software is furnished to do so, subject to 19 | * the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be 22 | * included in all copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 28 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 29 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 30 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 31 | * 32 | */ 33 | 34 | package openldap 35 | 36 | /* 37 | 38 | #define LDAP_DEPRECATED 1 39 | #include 40 | #include 41 | 42 | static inline char* to_charptr(const void* s) { return (char*)s; } 43 | static inline LDAPControl** to_ldapctrlptr(const void* s) { 44 | return (LDAPControl**) s; 45 | } 46 | */ 47 | // #cgo CFLAGS: -DLDAP_DEPRECATED=1 48 | // #cgo linux CFLAGS: -DLINUX=1 49 | // #cgo LDFLAGS: -lldap -llber 50 | import "C" 51 | 52 | import ( 53 | "errors" 54 | "fmt" 55 | "unsafe" 56 | "strings" 57 | "strconv" 58 | ) 59 | 60 | /* Intialize() open an LDAP connexion ; supported url formats : 61 | * 62 | * ldap://host:389/ 63 | * ldaps://secure-host:636/ 64 | * 65 | * return values : 66 | * - on success : LDAP object, nil 67 | * - on error : nil and error with error description. 68 | */ 69 | func Initialize(url string) (*Ldap, error) { 70 | _url := C.CString(url) 71 | defer C.free(unsafe.Pointer(_url)) 72 | 73 | var ldap *C.LDAP 74 | 75 | // API: int ldap_initialize (LDAP **ldp, LDAP_CONST char *url ) 76 | rv := C.ldap_initialize(&ldap, _url) 77 | 78 | if rv != 0 { 79 | err := errors.New(fmt.Sprintf("LDAP::Initialize() error (%d) : %s", rv, ErrorToString(int(rv)))) 80 | return nil, err 81 | } 82 | 83 | return &Ldap{ldap}, nil 84 | } 85 | 86 | /* 87 | * StartTLS() is used for regular LDAP (not 88 | * LDAPS) connections to establish encryption 89 | * after the session is running. 90 | * 91 | * return value : 92 | * - nil on success, 93 | * - error with error description on error. 94 | */ 95 | func (self *Ldap) StartTLS() error { 96 | var rv int 97 | 98 | // API: int ldap_start_tls_s(LDAP *ld, LDAPControl **serverctrls, LDAPControl **clientctrls); 99 | rv = int(C.ldap_start_tls_s(self.conn, 100 | C.to_ldapctrlptr(unsafe.Pointer(nil)), 101 | C.to_ldapctrlptr(unsafe.Pointer(nil)))) 102 | 103 | if rv == LDAP_OPT_SUCCESS { 104 | return nil 105 | } 106 | 107 | return errors.New(fmt.Sprintf("LDAP::StartTLS() error (%d) : %s", rv, 108 | ErrorToString(rv))) 109 | } 110 | 111 | /* 112 | * Bind() is used for LDAP authentifications 113 | * 114 | * if who is empty this is an anonymous bind 115 | * else this is an authentificated bind 116 | * 117 | * return value : 118 | * - nil on succes, 119 | * - error with error description on error. 120 | * 121 | */ 122 | func (self *Ldap) Bind(who, cred string) error { 123 | var rv int 124 | 125 | authmethod := C.int(LDAP_AUTH_SIMPLE) 126 | 127 | // DEPRECATED 128 | // API: int ldap_bind_s (LDAP *ld, LDAP_CONST char *who, LDAP_CONST char *cred, int authmethod ); 129 | if who == "" { 130 | _who := C.to_charptr(unsafe.Pointer(nil)) 131 | _cred := C.to_charptr(unsafe.Pointer(nil)) 132 | 133 | rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod)) 134 | } else { 135 | _who := C.CString(who) 136 | _cred := C.CString(cred) 137 | defer C.free(unsafe.Pointer(_who)) 138 | rv = int(C.ldap_bind_s(self.conn, _who, _cred, authmethod)) 139 | } 140 | 141 | if rv == LDAP_OPT_SUCCESS { 142 | return nil 143 | } 144 | 145 | self.conn = nil 146 | return errors.New(fmt.Sprintf("LDAP::Bind() error (%d) : %s", rv, ErrorToString(rv))) 147 | } 148 | 149 | /* 150 | * close LDAP connexion 151 | * 152 | * return value : 153 | * - nil on succes, 154 | * - error with error description on error. 155 | * 156 | */ 157 | func (self *Ldap) Close() error { 158 | 159 | // DEPRECATED 160 | // API: int ldap_unbind(LDAP *ld) 161 | rv := C.ldap_unbind(self.conn) 162 | 163 | if rv == LDAP_OPT_SUCCESS { 164 | return nil 165 | } 166 | 167 | self.conn = nil 168 | return errors.New(fmt.Sprintf("LDAP::Close() error (%d) : %s", int(rv), ErrorToString(int(rv)))) 169 | 170 | } 171 | /* 172 | * Unbind() close LDAP connexion 173 | * 174 | * an alias to Ldap::Close() 175 | * 176 | */ 177 | func (self *Ldap) Unbind() error { 178 | return self.Close() 179 | } 180 | 181 | /* Search() is used to search LDAP server 182 | - base is where search is starting 183 | - scope allows local or deep search. Supported values : 184 | - LDAP_SCOPE_BASE 185 | - LDAP_SCOPE_ONELEVEL 186 | - LDAP_SCOPE_SUBTREE 187 | - filter is an LDAP search expression, 188 | - attributes is an array of string telling with LDAP attribute to get from this request 189 | */ 190 | func (self *Ldap) Search(base string, scope int, filter string, attributes []string) (*LdapMessage, error) { 191 | 192 | var attrsonly int = 0 // false: returns all, true, returns only attributes without values 193 | 194 | _base := C.CString(base) 195 | defer C.free(unsafe.Pointer(_base)) 196 | 197 | _filter := C.CString(filter) 198 | defer C.free(unsafe.Pointer(_filter)) 199 | 200 | // transform []string to C.char** null terminated array (attributes argument) 201 | _attributes := make([]*C.char, len(attributes)+1) // default set to nil (NULL in C) 202 | 203 | for i, arg := range attributes { 204 | _attributes[i] = C.CString(arg) 205 | defer C.free(unsafe.Pointer(_attributes[i])) 206 | } 207 | 208 | var msg *C.LDAPMessage 209 | 210 | // DEPRECATED 211 | // API: int ldap_search_s (LDAP *ld, char *base, int scope, char *filter, char **attrs, int attrsonly, LdapMessage * ldap_res) 212 | rv := int(C.ldap_search_s(self.conn, _base, C.int(scope), _filter, &_attributes[0], C.int(attrsonly), &msg)) 213 | 214 | if rv == LDAP_OPT_SUCCESS { 215 | _msg := new(LdapMessage) 216 | _msg.ldap = self 217 | _msg.errno = rv 218 | _msg.msg = msg 219 | return _msg, nil 220 | } 221 | 222 | return nil, errors.New(fmt.Sprintf("LDAP::Search() error : %d (%s)", rv, ErrorToString(rv))) 223 | } 224 | 225 | // ------------------------------------- Ldap* method (object oriented) ------------------------------------------------------------------- 226 | 227 | // Create a new LdapAttribute entry with name and values. 228 | func LdapAttributeNew(name string, values []string)(*LdapAttribute){ 229 | a := new(LdapAttribute) 230 | a.values = values 231 | a.name = name 232 | return a 233 | } 234 | 235 | // Append() adds an LdapAttribute to self LdapEntry 236 | func (self *LdapEntry) Append(a LdapAttribute){ 237 | self.values = append(self.values, a) 238 | } 239 | 240 | // String() is used for fmt.Println(self) 241 | // 242 | func (self *LdapAttribute) String() string{ 243 | return self.ToText() 244 | } 245 | 246 | // ToText() returns a text string representation of LdapAttribute 247 | // avoiding displaying binary data. 248 | // 249 | func (self *LdapAttribute) ToText() string{ 250 | 251 | var list []string 252 | 253 | for _, a := range self.Values() { 254 | if (!_isPrint(a)) { 255 | list = append(list, fmt.Sprintf("binary-data[%d]", len(a))) 256 | } else { 257 | list = append(list, a) 258 | } 259 | } 260 | if len(list) > 1 { 261 | return fmt.Sprintf("%s: (%d)[%s]", self.name, len(list), strings.Join(list, ", ")) 262 | } 263 | return fmt.Sprintf("%s: [%s]", self.name, strings.Join(list, ", ")) 264 | } 265 | 266 | // Name() return attribute name 267 | func (self *LdapAttribute) Name() string{ 268 | return self.name 269 | } 270 | 271 | // Values() returns array values for self LdapAttribute 272 | // 273 | func (self *LdapAttribute) Values() []string{ 274 | return self.values 275 | } 276 | 277 | // _isPrint() returns true if str is printable 278 | // 279 | // @private method 280 | func _isPrint(str string) bool{ 281 | for _, c := range str{ 282 | 283 | if !strconv.IsPrint(rune(c)) { 284 | return false 285 | } 286 | } 287 | 288 | return true 289 | } 290 | 291 | // IsPrint() returns true is self LdapAttribute is printable. 292 | func (self *LdapAttribute) IsPrint() bool{ 293 | for _, a := range self.Values() { 294 | if (!_isPrint(a)) { 295 | return false 296 | } 297 | } 298 | return true 299 | } 300 | 301 | // Dn() returns DN (Distinguish Name) for self LdapEntry 302 | func (self *LdapEntry) Dn() string{ 303 | return self.dn 304 | } 305 | 306 | // Attributes() returns an array of LdapAttribute 307 | func (self *LdapEntry) Attributes() []LdapAttribute{ 308 | return self.values 309 | } 310 | 311 | // Print() allow printing self LdapEntry with fmt.Println() 312 | func (self *LdapEntry) String() string { 313 | return self.ToText() 314 | } 315 | 316 | // GetValuesByName() get a list of values for self LdapEntry, using "name" attribute 317 | func (self *LdapEntry) GetValuesByName(attrib string) []string{ 318 | 319 | for _, a := range self.values{ 320 | if a.Name() == attrib { 321 | return a.values 322 | } 323 | } 324 | 325 | return []string{} 326 | } 327 | // GetOneValueByName() ; a quick way to get a single attribute value 328 | func (self *LdapEntry) GetOneValueByName(attrib string) (string, error){ 329 | 330 | for _, a := range self.values{ 331 | if a.Name() == attrib { 332 | return a.values[0], nil 333 | } 334 | } 335 | 336 | return "", errors.New(fmt.Sprintf("LdapEntry::GetOneValueByName() error : attribute %s not found", attrib)) 337 | } 338 | 339 | // ToText() return a string representating self LdapEntry 340 | func (self *LdapEntry) ToText() string{ 341 | 342 | txt := fmt.Sprintf("dn: %s\n", self.dn) 343 | 344 | for _, a := range self.values{ 345 | txt = txt + fmt.Sprintf("%s\n", a.ToText()) 346 | } 347 | 348 | return txt 349 | } 350 | 351 | // Append() add e to LdapSearchResult array 352 | func (self *LdapSearchResult) Append(e LdapEntry){ 353 | self.entries = append(self.entries, e) 354 | } 355 | 356 | // ToText() : a quick way to print an LdapSearchResult 357 | func (self *LdapSearchResult) ToText() string{ 358 | 359 | txt := fmt.Sprintf("# query : %s\n", self.filter) 360 | txt = txt + fmt.Sprintf("# num results : %d\n", self.Count()) 361 | txt = txt + fmt.Sprintf("# search : %s\n", self.Filter()) 362 | txt = txt + fmt.Sprintf("# base : %s\n", self.Base()) 363 | txt = txt + fmt.Sprintf("# attributes : [%s]\n", strings.Join(self.Attributes(), ", ")) 364 | 365 | for _, e := range self.entries{ 366 | txt = txt + fmt.Sprintf("%s\n", e.ToText()) 367 | } 368 | 369 | return txt 370 | } 371 | 372 | // String() : used for fmt.Println(self) 373 | func (self *LdapSearchResult) String() string{ 374 | return self.ToText() 375 | } 376 | 377 | // Entries() : returns an array of LdapEntry for self 378 | func (self *LdapSearchResult) Entries() []LdapEntry{ 379 | return self.entries 380 | } 381 | 382 | // Count() : returns number of results for self search. 383 | func (self *LdapSearchResult) Count() int{ 384 | return len(self.entries) 385 | } 386 | 387 | // Filter() : returns filter for self search 388 | func (self *LdapSearchResult) Filter() string{ 389 | return self.filter 390 | } 391 | 392 | // Filter() : returns base DN for self search 393 | func (self *LdapSearchResult) Base() string{ 394 | return self.base 395 | } 396 | 397 | // Filter() : returns scope for self search 398 | func (self *LdapSearchResult) Scope() int{ 399 | return self.scope 400 | } 401 | 402 | // Filter() : returns an array of attributes used for this actual search 403 | func (self *LdapSearchResult) Attributes() []string{ 404 | return self.attributes 405 | } 406 | 407 | // SearchAll() : a quick way to make search. This method returns an LdapSearchResult with all necessary methods to 408 | // access data. Result is a collection (tree) of []LdapEntry / []LdapAttribute. 409 | // 410 | func (self *Ldap) SearchAll(base string, scope int, filter string, attributes []string) (*LdapSearchResult, error) { 411 | 412 | sr := new(LdapSearchResult) 413 | 414 | sr.ldap = self 415 | sr.base = base 416 | sr.scope = scope 417 | sr.filter = filter 418 | sr.attributes = attributes 419 | 420 | // Search(base string, scope int, filter string, attributes []string) (*LDAPMessage, error) 421 | result, err := self.Search(base, scope, filter, attributes) 422 | 423 | if err != nil { 424 | fmt.Println(err) 425 | return sr, err 426 | } 427 | 428 | // Free LDAP::Result() allocated data 429 | defer result.MsgFree() 430 | 431 | e := result.FirstEntry() 432 | 433 | for e != nil { 434 | _e := new(LdapEntry) 435 | 436 | _e.dn = e.GetDn() 437 | 438 | attr, _ := e.FirstAttribute() 439 | for attr != "" { 440 | 441 | _attr := LdapAttributeNew(attr, e.GetValues(attr)) 442 | _e.Append(*_attr) 443 | 444 | attr, _ = e.NextAttribute() 445 | 446 | } 447 | 448 | sr.Append(*_e) 449 | 450 | e = e.NextEntry() 451 | } 452 | 453 | return sr, nil 454 | } 455 | -------------------------------------------------------------------------------- /options-errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2012 - Marc Quinton. 4 | * 5 | * Use of this source code is governed by the MIT Licence : 6 | * http://opensource.org/licenses/mit-license.php 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | package openldap 29 | 30 | /* 31 | #include 32 | 33 | static inline char* to_charptr(const void* s) { return (char*)s; } 34 | 35 | */ 36 | // #cgo CFLAGS: -DLDAP_DEPRECATED=1 37 | // #cgo linux CFLAGS: -DLINUX=1 38 | // #cgo LDFLAGS: -lldap -llber 39 | import "C" 40 | 41 | import ( 42 | "errors" 43 | "fmt" 44 | "unsafe" 45 | ) 46 | 47 | // FIXME : support all kind of option (int, int*, ...) 48 | func (self *Ldap) SetOption(opt int, val int) error { 49 | 50 | // API: ldap_set_option (LDAP *ld,int option, LDAP_CONST void *invalue)); 51 | rv := C.ldap_set_option(self.conn, C.int(opt), unsafe.Pointer(&val)) 52 | 53 | if rv == LDAP_OPT_SUCCESS { 54 | return nil 55 | } 56 | 57 | return errors.New(fmt.Sprintf("LDAP::SetOption() error (%d) : %s", int(rv), ErrorToString(int(rv)))) 58 | } 59 | 60 | // FIXME : support all kind of option (int, int*, ...) should take care of all return type for ldap_get_option 61 | func (self *Ldap) GetOption(opt int) (val int, err error) { 62 | 63 | // API: int ldap_get_option (LDAP *ld,int option, LDAP_CONST void *invalue)); 64 | rv := C.ldap_get_option(self.conn, C.int(opt), unsafe.Pointer(&val)) 65 | 66 | if rv == LDAP_OPT_SUCCESS { 67 | return val, nil 68 | } 69 | 70 | return 0, errors.New(fmt.Sprintf("LDAP::GetOption() error (%d) : %s", rv, ErrorToString(int(rv)))) 71 | } 72 | 73 | /* 74 | ** WORK IN PROGRESS! 75 | ** 76 | ** OpenLDAP reentrancy/thread-safeness should be dynamically 77 | ** checked using ldap_get_option(). 78 | ** 79 | ** The -lldap implementation is not thread-safe. 80 | ** 81 | ** The -lldap_r implementation is: 82 | ** LDAP_API_FEATURE_THREAD_SAFE (basic thread safety) 83 | ** but also be: 84 | ** LDAP_API_FEATURE_SESSION_THREAD_SAFE 85 | ** LDAP_API_FEATURE_OPERATION_THREAD_SAFE 86 | ** 87 | ** The preprocessor flag LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE 88 | ** can be used to determine if -lldap_r is available at compile 89 | ** time. You must define LDAP_THREAD_SAFE if and only if you 90 | ** link with -lldap_r. 91 | ** 92 | ** If you fail to define LDAP_THREAD_SAFE when linking with 93 | ** -lldap_r or define LDAP_THREAD_SAFE when linking with -lldap, 94 | ** provided header definations and declarations may be incorrect. 95 | ** 96 | */ 97 | 98 | func (self *Ldap) IsThreadSafe() bool { 99 | // fmt.Println("IsThreadSafe()") 100 | // opt, err := self.GetOption(LDAP_API_FEATURE_THREAD_SAFE) ; fmt.Println(opt, err) 101 | // opt, err = self.GetOption(LDAP_THREAD_SAFE) ; fmt.Println(opt, err) 102 | // opt, err = self.GetOption(LDAP_API_FEATURE_X_OPENLDAP_THREAD_SAFE) ; fmt.Println(opt, err) 103 | 104 | //FIXME: need to implement LDAP::GetOption(LDAP_OPT_API_FEATURE_INFO) 105 | return false 106 | } 107 | 108 | func ErrorToString(err int) string { 109 | 110 | // API: char * ldap_err2string (int err ) 111 | result := C.GoString(C.to_charptr(unsafe.Pointer(C.ldap_err2string(C.int(err))))) 112 | return result 113 | } 114 | 115 | func (self *Ldap) Errno() int { 116 | opt, _ := self.GetOption(LDAP_OPT_ERROR_NUMBER) 117 | return opt 118 | } 119 | -------------------------------------------------------------------------------- /results.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2012 - Marc Quinton. 4 | * 5 | * Use of this source code is governed by the MIT Licence : 6 | * http://opensource.org/licenses/mit-license.php 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | package openldap 29 | 30 | /*#include 31 | #include 32 | #include 33 | #include 34 | 35 | int _berval_get_len(struct berval **ber, int i){ 36 | return ber[i]->bv_len; 37 | } 38 | 39 | char* _berval_get_value(struct berval **ber, int i){ 40 | return ber[i]->bv_val; 41 | } 42 | 43 | */ 44 | // #cgo CFLAGS: -DLDAP_DEPRECATED=1 45 | // #cgo linux CFLAGS: -DLINUX=1 46 | // #cgo LDFLAGS: -lldap -llber 47 | import "C" 48 | 49 | import ( 50 | "errors" 51 | "fmt" 52 | "unsafe" 53 | ) 54 | 55 | // ------------------------------------------ RESULTS methods --------------------------------------------- 56 | /* 57 | 58 | openldap C API : 59 | 60 | int ldap_count_messages( LDAP *ld, LdapMessage *result ) 61 | LdapMessage *ldap_first_message( LDAP *ld, LdapMessage *result ) 62 | LdapMessage *ldap_next_message ( LDAP *ld, LdapMessage *message ) 63 | 64 | int ldap_count_entries( LDAP *ld, LdapMessage *result ) 65 | LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result ) 66 | LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry ) 67 | 68 | char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr ) 69 | char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber ) 70 | 71 | char **ldap_get_values(LDAP *ld, LdapMessage *entry, char *attr) 72 | struct berval **ldap_get_values_len(LDAP *ld, LdapMessage *entry,char *attr) 73 | 74 | int ldap_count_values(char **vals) 75 | int ldap_count_values_len(struct berval **vals) 76 | void ldap_value_free(char **vals) 77 | void ldap_value_free_len(struct berval **vals) 78 | 79 | */ 80 | 81 | func (self *LdapMessage) Count() int { 82 | // API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain ) 83 | // err : (count = -1) 84 | count := int(C.ldap_count_messages(self.ldap.conn, self.msg)) 85 | if count == -1 { 86 | panic("LDAP::Count() (ldap_count_messages) error (-1)") 87 | } 88 | return count 89 | 90 | } 91 | 92 | func (self *LdapMessage) FirstMessage() *LdapMessage { 93 | 94 | var msg *C.LDAPMessage 95 | msg = C.ldap_first_message(self.ldap.conn, self.msg) 96 | if msg == nil { 97 | return nil 98 | } 99 | _msg := new(LdapMessage) 100 | _msg.ldap = self.ldap 101 | _msg.errno = 0 102 | _msg.msg = msg 103 | return _msg 104 | } 105 | 106 | func (self *LdapMessage) NextMessage() *LdapMessage { 107 | var msg *C.LDAPMessage 108 | msg = C.ldap_next_message(self.ldap.conn, self.msg) 109 | 110 | if msg == nil { 111 | return nil 112 | } 113 | _msg := new(LdapMessage) 114 | _msg.ldap = self.ldap 115 | _msg.errno = 0 116 | _msg.msg = msg 117 | return _msg 118 | } 119 | 120 | /* an alias to ldap_count_message() ? */ 121 | func (self *LdapEntry) CountEntries() int { 122 | // API : int ldap_count_messages(LDAP *ld, LDAPMessage *chain ) 123 | // err : (count = -1) 124 | return int(C.ldap_count_entries(self.ldap.conn, self.entry)) 125 | } 126 | 127 | func (self *LdapMessage) FirstEntry() *LdapEntry { 128 | 129 | var msg *C.LDAPMessage 130 | // API: LdapMessage *ldap_first_entry( LDAP *ld, LdapMessage *result ) 131 | msg = C.ldap_first_entry(self.ldap.conn, self.msg) 132 | if msg == nil { 133 | return nil 134 | } 135 | _msg := new(LdapEntry) 136 | _msg.ldap = self.ldap 137 | _msg.errno = 0 138 | _msg.entry = msg 139 | return _msg 140 | } 141 | 142 | func (self *LdapEntry) NextEntry() *LdapEntry { 143 | var msg *C.LDAPMessage 144 | // API: LdapMessage *ldap_next_entry ( LDAP *ld, LdapMessage *entry ) 145 | msg = C.ldap_next_entry(self.ldap.conn, self.entry) 146 | 147 | if msg == nil { 148 | return nil 149 | } 150 | _msg := new(LdapEntry) 151 | _msg.ldap = self.ldap 152 | _msg.errno = 0 153 | _msg.entry = msg 154 | return _msg 155 | } 156 | 157 | func (self *LdapEntry) FirstAttribute() (string, error) { 158 | 159 | var ber *C.BerElement 160 | 161 | // API: char *ldap_first_attribute(LDAP *ld, LdapMessage *entry, BerElement **berptr ) 162 | rv := C.ldap_first_attribute(self.ldap.conn, self.entry, &ber) 163 | 164 | if rv == nil { 165 | // error 166 | return "", nil 167 | } 168 | self.ber = ber 169 | return C.GoString(rv), nil 170 | } 171 | 172 | func (self *LdapEntry) NextAttribute() (string, error) { 173 | 174 | // API: char *ldap_next_attribute (LDAP *ld, LdapMessage *entry, BerElement *ber ) 175 | rv := C.ldap_next_attribute(self.ldap.conn, self.entry, self.ber) 176 | 177 | if rv == nil { 178 | // error 179 | return "", nil 180 | } 181 | return C.GoString(rv), nil 182 | } 183 | 184 | // private func 185 | func sptr(p uintptr) *C.char { 186 | return *(**C.char)(unsafe.Pointer(p)) 187 | } 188 | 189 | // private func used to convert null terminated char*[] to go []string 190 | func cstrings_array(x **C.char) []string { 191 | var s []string 192 | for p := uintptr(unsafe.Pointer(x)); sptr(p) != nil; p += unsafe.Sizeof(uintptr(0)) { 193 | s = append(s, C.GoString(sptr(p))) 194 | } 195 | return s 196 | } 197 | 198 | // GetValues() return an array of string containing values for LDAP attribute "attr". 199 | // Binary data are supported. 200 | func (self *LdapEntry) GetValues(attr string) []string { 201 | var s []string 202 | 203 | _attr := C.CString(attr) 204 | defer C.free(unsafe.Pointer(_attr)) 205 | 206 | var bv **C.struct_berval 207 | 208 | //API: struct berval **ldap_get_values_len(LDAP *ld, LDAPMessage *entry, char *attr) 209 | bv = C.ldap_get_values_len(self.ldap.conn, self.entry, _attr) 210 | 211 | var i int 212 | count := int(C.ldap_count_values_len(bv)) 213 | 214 | for i = 0 ; i < count; i++ { 215 | s = append(s, C.GoStringN(C._berval_get_value(bv, C.int(i)), C._berval_get_len(bv, C.int(i)))) 216 | } 217 | 218 | // free allocated array (bv) 219 | C.ldap_value_free_len(bv) 220 | 221 | return s 222 | } 223 | 224 | // ------------------------------------------------ RESULTS ----------------------------------------------- 225 | /* 226 | int ldap_result ( LDAP *ld, int msgid, int all, struct timeval *timeout, LdapMessage **result ); 227 | int ldap_msgfree( LdapMessage *msg ); 228 | int ldap_msgtype( LdapMessage *msg ); 229 | int ldap_msgid ( LdapMessage *msg ); 230 | 231 | */ 232 | 233 | // Result() 234 | // take care to free LdapMessage result with MsgFree() 235 | // 236 | func (self *Ldap) Result() (*LdapMessage, error) { 237 | 238 | var msgid int = 1 239 | var all int = 1 240 | 241 | var tv C.struct_timeval 242 | tv.tv_sec = 30 243 | 244 | var msg *C.LDAPMessage 245 | 246 | // API: int ldap_result( LDAP *ld, int msgid, int all, struct timeval *timeout, LDAPMessage **result ); 247 | rv := C.ldap_result(self.conn, C.int(msgid), C.int(all), &tv, &msg) 248 | 249 | if rv != LDAP_OPT_SUCCESS { 250 | return nil, errors.New(fmt.Sprintf("LDAP::Result() error : %d (%s)", rv, ErrorToString(int(rv)))) 251 | } 252 | 253 | _msg := new(LdapMessage) 254 | _msg.ldap = self 255 | _msg.errno = int(rv) 256 | _msg.msg = msg 257 | 258 | return _msg, nil 259 | } 260 | 261 | // MsgFree() is used to free LDAP::Result() allocated data 262 | // 263 | // returns -1 on error. 264 | // 265 | func (self *LdapMessage) MsgFree() int{ 266 | if self.msg != nil { 267 | rv := C.ldap_msgfree(self.msg) 268 | self.msg = nil 269 | return int(rv) 270 | } 271 | return -1 272 | } 273 | 274 | 275 | // ---------------------------------------- DN Methods --------------------------------------------------- 276 | /* 277 | 278 | char *ldap_get_dn( LDAP *ld, LdapMessage *entry) 279 | int ldap_str2dn( const char *str, LDAPDN *dn, unsigned flags) 280 | void ldap_dnfree( LDAPDN dn) 281 | int ldap_dn2str( LDAPDN dn, char **str, unsigned flags) 282 | 283 | char **ldap_explode_dn( const char *dn, int notypes) 284 | char **ldap_explode_rdn( const char *rdn, int notypes) 285 | 286 | char *ldap_dn2ufn ( const char * dn ) 287 | char *ldap_dn2dcedn( const char * dn ) 288 | char *ldap_dcedn2dn( const char * dn ) 289 | char *ldap_dn2ad_canonical( const char * dn ) 290 | 291 | */ 292 | 293 | // GetDn() return the DN (Distinguish Name) for self LdapEntry 294 | func (self *LdapEntry) GetDn() string { 295 | // API: char *ldap_get_dn( LDAP *ld, LDAPMessage *entry ) 296 | rv := C.ldap_get_dn(self.ldap.conn, self.entry) 297 | defer C.free(unsafe.Pointer(rv)) 298 | 299 | if rv == nil { 300 | err := self.ldap.Errno() 301 | panic(fmt.Sprintf("LDAP::GetDn() error %d (%s)", err, ErrorToString(err))) 302 | } 303 | 304 | val := C.GoString(rv) 305 | return val 306 | } 307 | -------------------------------------------------------------------------------- /types.go: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | * Copyright (C) 2012 - Marc Quinton. 4 | * 5 | * Use of this source code is governed by the MIT Licence : 6 | * http://opensource.org/licenses/mit-license.php 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 23 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | package openldap 29 | 30 | /* 31 | # include 32 | 33 | */ 34 | // #cgo CFLAGS: -DLDAP_DEPRECATED=1 35 | // #cgo linux CFLAGS: -DLINUX=1 36 | // #cgo LDFLAGS: -lldap -llber 37 | import "C" 38 | 39 | type Ldap struct { 40 | conn *C.LDAP 41 | } 42 | 43 | type LdapMessage struct { 44 | ldap *Ldap 45 | // conn *C.LDAP 46 | msg *C.LDAPMessage 47 | errno int 48 | } 49 | 50 | type LdapAttribute struct{ 51 | name string 52 | values []string 53 | } 54 | 55 | 56 | type LdapEntry struct { 57 | ldap *Ldap 58 | // conn *C.LDAP 59 | entry *C.LDAPMessage 60 | errno int 61 | ber *C.BerElement 62 | 63 | dn string 64 | values []LdapAttribute 65 | } 66 | 67 | type LdapSearchResult struct{ 68 | ldap *Ldap 69 | 70 | scope int 71 | filter string 72 | base string 73 | attributes []string 74 | 75 | entries []LdapEntry 76 | } 77 | --------------------------------------------------------------------------------