荔园在线
荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀
[回到开始]
[上一篇][下一篇]
发信人: 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软件 网络书店