荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: tang (独孤九剑〖玄铁重剑〗), 信区: Program
标  题: OPENLDAP API 编程简述(C语言)
发信站: BBS 荔园晨风站 (Thu Apr  6 21:32:58 2000), 转信

                  OPENLDAP API 编程简述(C语言)
                      作者:汤宇清
一、 LDAP模型概览:
  1、 LDAP的数据存储在众多的Entry(条目)里;
  2、 LDAP中所有的Entry以树型结构组织在一起;
  3、 Entry由唯一的DN(Distinguished Name)标识和定位,DN就是树上到该Entry的路
      径标识;
  4、 Entry的数据以属性形式组织,每个属性可以拥有一个或多个值;
  5、 属性有各自的类型,一个Entry所能拥有的属性是由这个Entry的ObjectClass属性
      规定的;
  6、 每个Entry的Objectclass属性中必须包含top这个值,因为在top这个ObjectClass
      中定义了属性ObjectClass,Entry只有设置了该属性值才能拥有ObjectClass的属
      性(这是个递归定义,有点向pascal和c里的向前声明);
  7、 Entry能容纳的属性个数、属性值的类型、属性的名称(标识)都由该Entry的
      ObjectCalss规定(注意一个Entry的ObjectClass象它的其它属性一样是可以有多
      个值的)。以下是两个Entry的定义:
      以下两个Entry是同时由top,uidobject,person三个ObjectClass规范的Entry,注意
      它们的三个ObjectClass属性;由于这三个ObjectClass属性的规范,在增加、修改
      这两个Entry时必须要提供ObjectClass、uid、cn、sn的属性值。(关于Object参
      见下面的描述)
        dn: uid=10207, ou=People, o=VinSide, c=CN
        objectclass: top
        objectclass: uidobject
        objectclass: person
        uid: 10001
        cn: gang
        sn: liu

        dn: uid=10209, ou=People, o=VinSide, c=CN
        objectclass: top
        objectclass: uidobject
        objectclass: person
        uid: 10002
        cn: yuqing
        sn: tang
  8、 以下是ObjectClass(top,uidobject,person)的定义:
      其中在requires下的属性是设成该ObjectClass的Entry必须要设置的属性;
      其中在allow下的属性是设成该ObjectClass的Entry可以设置的属性,但不是必
      须的。
        objectclass top
                requires
                        objectClass
        objectclass uidobject
                requires
                        objectClass,
                        uid
        objectclass person
                requires
                        objectClass,
                        sn,
                        cn
                allows
                        description,
                        seeAlso,
                        telephoneNumber,
                        userPassword
  9、 Entry的DN描述这个Entry处于LDAP树型数据组织中的位置,如DN: uid=10207,
      ou=People, o=VinSide, c=CN的Entry就描述该Entry是DN: ou=People, o=VinSide,
      c=CN的Entry的子结点,故在添加DN: uid=10207, ou=People, o=VinSide, c=CN的
      Entry前DN: ou=People, o=VinSide, c=CN的Entry必须已经加入LDAP中;在删除
      结点时也存在同样的问题。

二、 使用LDAP API的步骤:
  1、 用ldap_open() API打开与LDAP服务器的连接,该API返回一个struct LDAP的
      指针,该指针将用于整个LDAP的操作过程直到结束;
  2、 用ldap_bind()族(如ldap_simple_bind、ldap_kerberos_bind等)API取得LDAP服
      务器的认证,只有获得认证(包括匿名认证)后才能对LDAP服务器提出请求;
  3、 执行具体的LDAP操作(如ldap_search()、ldap_add()、ldap_modify()、
ldap_delete()、
      ldap_first_entry()等);
  4、 关闭连接ldap_unbind();(这样整个操作过程就结束了)

三、 使用OpenLDAP API所需的头文件和连接库:
  1、 头文件(缺省安装在/usr/local/include):
       #include <lber.h>
       #include <ldap.h>
  2、 连接库(缺省安装在/usr/local/lib):
        libldap.a
        liblber.a
  3、 某些API需要额外的头文件,如<sys/time.h>,具体查看该API的man page(如man
      ldap_simple_bind_s )。

四、 LDAP API详述:
        说明:以下API中带"_s"后缀的API是同步API,即调用后会以阻塞方式运行;没有
"_s"
        后缀的是异步API,调用后会立刻返回,然后在适当的时候用ldap_result()取回结
果。
1、 ldap_open() 打开到LDAP服务器的连接:
              typedef struct ldap {
                      /* ... opaque parameters ... */
                      int     ld_deref;
                      int     ld_timelimit;
                      int     ld_sizelimit;
                      int     ld_errno;
                      char    *ld_matched;
                      char    *ld_error;
                      /* ... opaque parameters ... */
              } LDAP;

                LDAP *ldap_open( char *hostname, int portno );
        > hostname是LDAP服务器的地址,可以是IP或域名;
        > portno是LDAP服务器的端口号,缺省的端口是常数:LDAP_PORT;
        > 返回值:成功返回一个struct LDAP指针是一个连接句柄用于以后的操作,
          失败返回NULL。
2、 ldap_bind()族API获得访问目录的认证:
           int ldap_bind( LDAP *ld, char *dn, char *cred, int method );

           int ldap_bind_s( LDAP *ld, char *dn, char *cred, int method );

           int ldap_simple_bind( LDAP *ld, char *dn, char *passwd );

           int ldap_simple_bind_s( LDAP *ld, char *dn, char *passwd );

           int ldap_kerberos_bind( LDAP *ld, char *dn );

           int ldap_kerberos_bind_s( LDAP *ld, char *dn );
        > ld是连接句柄;
        > dn是要连接的Entry的DN。
          说明:该Entry会作为LDAP判断用ld句柄是否有权进行操作的依据(可
          以用帐号去理解,一般系统在配置时会设置一个rootdn可看作超级用户帐
          号)
        > passwd是对应dn的帐号密码。
        > method是验证方法:LDAP_AUTH_SIMPLE,LDAP_AUTH_KRBV41,或
          LDAP_AUTH_KRBV42
        > 一般用简单的绑定就可以了:ldap_simple_bind_s、ldap_simple_bind
3、 ldap_unbind()关闭连接:
          int ldap_unbind( LDAP *ld );
        > ld是连接句柄
4、 ldap_search()族API查询LDAP目录:
           int ldap_search(
                   LDAP    *ld,
                   char    *base,
                   int     scope,
                   char    *filter,
                   char    *attrs[],
                   int     attrsonly
           );
           int ldap_search_s(
                   LDAP            *ld,
                   char            *base,
                   int             scope,
                   char            *filter,
                   char            *attrs[],
                   int             attrsonly,
                   LDAPMessage     **res
           );
           int ldap_search_st(
                   LDAP            *ld,
                   char            *base,
                   int             scope,
                   char            *filter,
                   char            *attrs[],
                   int             attrsonly,
                   struct timeval  *timeout,
                   LDAPMessage     **res
           );
        需要了解的结构:
                   struct timeval {
                           long    tv_sec;
                        long    tv_usec;
                   };
        > ld连接句柄
        > base搜索起始处那个Entry的DN
        > scope搜索范围:
                1. LDAP_SCOPE_BASE:只搜索该base Entry
                2. LDAP_SCOPE_ONELEVEL:搜索该base Entry的所有子Entry,但
                   只限于子Entry的那一层
                3. LDAP_SCOPE_SUBTREE:搜索该base Entry的整个子树(包括base
                   Entry)
        > filter是搜索过滤串,具体的描述在RFC1558,以下是简单的BNF描述:
               <filter> ::= '(' <filtercomp> ')'
               <filtercomp> ::= <and> | <or> | <not> | <simple>
               <and> ::= '&' <filterlist>
               <or> ::= '|' <filterlist>
               <not> ::= '!' <filter>
               <filterlist> ::= <filter> | <filter> <filterlist>
               <simple> ::= <attributetype> <filtertype> <attributevalue>
               <filtertype> ::= '=' | '~=' | '<=' | '>='
                说明:filtertype中的~=是专门用于近似匹配的
        > attrs是指出搜索将要返回哪些属性,它是以NULL标志结束的字符指针数
          组,如:attrs={"uid","sn","cn",NULL}。如果把attrs设成NULL表示传回所
          有的属性。
        > attrsonly是一个布尔值,0表示回传属性类型值和属性值,非0只传回属性
          类型。
        > timeout是专门用于ldap_search_st(),指出操作的超时时间。
        > res是同步API使用的,用于得到搜索结果。
          额外说明:(在ld这个连接句柄中有三个域影响搜索的进行)
        > ld_sizelimit:搜索结果中最大的Entry数
        > ld_timelimit:搜索消耗的时间限制(单位秒)
        > ld_deref:与别名有关的处理,具体看manual!
5、 目录查找结果分析API:
 i. 遍历结果目录项集(Entries Set)(ldap_first_entry、ldap_next_entry、
    ldap_count_entries):
           LDAPMesage *ldap_first_entry( LDAP *ld, LDAPMessage *res );

           LDAPMesage *ldap_next_entry( LDAP *ld, LDAPMessage *entry );

           int ldap_count_entries( LDAP *ld, LDAPMessage *res );
        > ld连接句柄
        > res搜索结果,从ldap_search()族函数或ldap_result()返回的。
        > entry是ldap_next_entry API中使用的,它是上次调用ldap_first_entry、
          ldap_next_entry的返回值,用于遍历正个Entry结果集。
        > 如果没有更多的Entry在结果集中,这两个API返回NULL。
        > 遍历开始先调用一次ldap_first_entry取得首个entry,然后调用
ldap_next_entry
          遍历剩下的entry。
 ii. 遍历目录项中的各属性(ldap_first_attribute()、ldap_next_attribute()):
           char *ldap_first_attribute(
                   LDAP            *ld,
                   LDAPMessage     *entry,
                   void            **ptr
           );
           char *ldap_next_attribute(
                   LDAP            *ld,
                   LDAPMessage     *entry,
                   void            *ptr
           );
        > ld是连接句柄
        > entry是要遍历属性的entry
        > ptr是用于跟踪遍历Entry时的当前位置,是一个指向BerElement的指针。
        > 这两个API返回的指向当前的属性名的字符指针,这个指针指向的内存空间
          是不可修改的。
        > 更多的细节看manual。
 iii. 获得属性值(ldap_get_values()和ldap_get_values_len()):
           typedef struct berval {
                   unsigned long   bv_len;
                   char            *bv_val;
           };

           char **ldap_get_values(
                   LDAP            *ld,
                   LDAPMessage     *entry,
                   char            *attr
           );

           struct berval **ldap_get_values_len(
                   LDAP            *ld,
                   LDAPMessage     *entry,
                   char            *attr
           );

           int ldap_count_values( char **vals );

           int ldap_count_values_len( struct berval **vals );

           int ldap_value_free( char **vals );

           int ldap_value_free_len( struct berval **vals );
        > ld是连接句柄
        > entry是由ldap_first_entry()和ldap_next_entry()返回的。
        > attr是属性名,一般是由ldap_first_entry和ldap_next_entry()返回的。
        > vals由ldap_get_values和ldap_get_values_len返回的值
        > 以'_len'为后缀是取得二进制的属性值
        > 更多的细节参照manual
 iv. 获得目录项的名称(ldap_get_dn()、ldap_explode_dn()、ldap_dn2ufn()):
           char *ldap_get_dn( LDAP *ld, LDAPMessage *entry );

           char **ldap_explode_dn( char *dn, int notypes );

           char *ldap_dn2ufn( char *dn );
        > 取得entry的DN,并取得一些更可读的表示的API
        > 具体细节参看manual
6、 ldap_add()和ldap_add_s()添加目录项:
           int ldap_add( LDAP *ld, char *dn, LDAPMod *attrs[] );

           int ldap_add_s( LDAP *ld, char *dn, LDAPMod *attrs[] );
    需要注意的结构:
        typedef struct ldapmod {
                   int             mod_op;
                   char            *mod_type;
                   union {
                           char            **modv_strvals;
                           struct berval   **modv_bvals;
                   } mod_vals;
           } LDAPMod;
           #define mod_values      mod_vals.modv_strvals
           #define mod_bvalues     mod_vals.modv_bvals

        > ld是连接句柄
        > dn是要添加到目录中的Entry的DN
        > attrs是要添加的属性,是一个指向LDAPMod结构的指针数组,以NULL结
          束,如:
                LDAPMod *lists_of_mod[5];
                lists_of_mod[0]=&attribute1;
                lists_of_mod[1]=&attribute2;
                lists_of_mod[2]=&attribute3;
                lists_of_mod[3]=&attribute4;
                lists_of_mod[4]=NULL;
        > LDAPMod结构的说明:
        > mod_op:LDAP_MOD_ADD、LDAP_MOD_DELETE、
                  LDAP_MOD_REPLACE、LDAP_MOD_BVALUES。其中
          LDAP_MOD_BVALUE与其他的值位或后表示属性值的操作是二进制的
         (如LDAP_MOD_ADD|LDAP_MOD_BVALUE表示增加二进制属性值)。
        > mod_type:要变更的属性的类型(属性名)
        > mod_vals:是一个以NULL结束的字符串指针数组,或是一个以NULL
                    结束的指向berval结构的指针数组(用于二进制值的操作)。
        > 关于二进制的属性的更详细的描述参照RFC1823和manual。
        > 注意在添加Entry时,其父Enry必须存在。
7、 ldap_modify()和ldap_modify_s()API更改存在的目录项(Entry):
           int ldap_modify( LDAP *ld, char *dn, LDAPMod *mods[] );

           int ldap_modify_s( LDAP *ld, char *dn, LDAPMod *mods[] );
        > 用于更改现存的Entry。
        > 参数作用与ldap_add()的相同。
        > 更详细的说明参照manual。

8、 ldap_delete()和ldap_delete_s()API删除已存在的目录项(Entry):
           int ldap_delete( LDAP *ld, char *dn );

           int ldap_delete_s( LDAP *ld, char *dn );
        > ld是连接句柄
        > dn是要删除的Entry的DN
9、 ldap_modrdn()和ldap_modrdn_s()API更改已存在的目录项(Entry)的相对DN:
           int ldap_modrdn(
                   LDAP    *ld,
                   char    *dn,
                   char    *newrdn,
                   int     deleteoldrdn
           );
           int ldap_modrdn_s(
                   LDAP    *ld,
                   char    *dn,
                   char    *newrdn,
                   int     deleteoldrdn
           );
        > 修改相对路径名
        > 更详细的说明参照manual
10、 ldap_abandon()废弃一个进行中的操作(用于异步操作):
         int ldap_abandon( LDAP *ld, int msgid );
        > 更详细的说明参看manual
11、 ldap_result()获得此前异步操作的的结果,用ldap_msgfree释放该结果:
           int ldap_result(
                   LDAP            *ld,
                   int             msgid,
                   int             all,
                   struct timeval  *timeout,
                   LDAPMessage     **res
           );

           int ldap_msgfree( LDAPMessage *res );

        > ld是连接句柄
        > msgid是由要回传的结果的ID,由异步API(如ldap_add())返回的值;可设
          为LDAP_RES_ANY,回传所有存在的值。
        > timeout是获取返回值操作的时限
        > res用于保存返回的结果,要用ldap_msgfree()来释放!
12、 错误处理API(ldap_result2error、ldap_err2string、ldap_perror):
           int ldap_result2error(
                   LDAP            *ld,
                   LDAPMessage     *res,
                   int             freeit
           );

           char *ldap_err2string( int err );

           void ldap_perror( LDAP *ld, char *msg );

        > ld是连接句柄;
        > res是由能返回result的API(如ldap_add_s之类的操作)设置过的LDAPMessage
          结构,用于取得错误的信息;
        > freeit指出错误处理过程后是否释放相应的LDAPMessage结构,非0释放,0
          不释放;
        > err是由ldap_result2error()或其他的同步API得到的整数,用于把该整数变成
          错误信息串;
        > msg是要在LDAP的错误信息被输出前显示的信息串。
          关于ld中某些在错误处理中作用的额外说明:
        > ld_matched:在LDAP_NO_SUCH_OBJECT错误返回时,这个域会含有已匹
          配的DN;
        > ld_error:是LDAP服务器返回的错误信息;
        > ld_errno是LDAP服务器返回的操作结果的错误码,以下是可能的值:
           LDAP_SUCCESS
           LDAP_OPERATIONS_ERROR
           LDAP_PROTOCOL_ERROR
           LDAP_TIMELIMIT_EXCEEDED
           LDAP_SIZELIMIT_EXCEEDED
           LDAP_COMPARE_FALSE
           LDAP_COMPARE_TRUE
           LDAP_STRONG_AUTH_NOT_SUPPORTED
           LDAP_STRONG_AUTH_REQUIRED
           LDAP_NO_SUCH_ATTRIBUTE
           LDAP_UNDEFINED_TYPE
           LDAP_INAPPROPRIATE_MATCHING
           LDAP_CONSTRAINT_VIOLATION
           LDAP_TYPE_OR_VALUE_EXISTS
           LDAP_INVALID_SYNTAX
           LDAP_NO_SUCH_OBJECT
           LDAP_ALIAS_PROBLEM
           LDAP_INVALID_DN_SYNTAX
           LDAP_IS_LEAF
           LDAP_ALIAS_DEREF_PROBLEM
           LDAP_INAPPROPRIATE_AUTH
           LDAP_INVALID_CREDENTIALS
           LDAP_INSUFFICIENT_ACCESS
           LDAP_BUSY
           LDAP_UNAVAILABLE
           LDAP_UNWILLING_TO_PERFORM
           LDAP_LOOP_DETECT
           LDAP_NAMING_VIOLATION
           LDAP_OBJECT_CLASS_VIOLATION
           LDAP_NOT_ALLOWED_ON_NONLEAF
           LDAP_NOT_ALLOWED_ON_RDN
           LDAP_ALREADY_EXISTS
           LDAP_NO_OBJECT_CLASS_MODS
           LDAP_RESULTS_TOO_LARGE
           LDAP_OTHER
           LDAP_SERVER_DOWN
           LDAP_LOCAL_ERROR
           LDAP_ENCODING_ERROR
           LDAP_DECODING_ERROR
           LDAP_TIMEOUT
           LDAP_AUTH_UNKNOWN
           LDAP_FILTER_ERROR
           LDAP_USER_CANCELLED
           LDAP_PARAM_ERROR
           LDAP_NO_MEMORY
        > ldap_err2string是把错误码转换成错误信息
        > ldap_perro()是打印一段由msg加上错误信息的串

参考文档:
1. RFC1823 The LDAP Application Program Interface
2. RFC1558 A String Representation of LDAP Search Filters"
3. OpenLdap的在线manual
4. Netscape Directory SDK3.0 for C Programmer’s Guide

--
海到天边天作岸
山登绝顶我为峰

※ 修改:·tang 於 Apr  7 19:47:19 修改本文·[FROM: 192.168.63.246]
※ 来源:·BBS 荔园晨风站 bbs.szu.edu.cn·[FROM: 192.168.63.239]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店