背景
第一次接觸ldap時,是大三實習的那個暑假,使用java訪問AD服務器中的用戶屬性,已經基本忘記如何搞了。這次本來是想用go語言和ldap寫一個獲取AD服務器中用戶的安全組的例子,想測試一下可以達到多高的性能。但是無奈go語言下沒有好的ldap庫(只找到github中的一個go-ldap,不確定是否好--https://github.com/go-ldap/ldap,后面可以再試試)。
考慮到ldap操作,最出名的應該就是openldap,於是想先試試這個。
下載代碼,編譯
到http://www.openldap.org/software/download/OpenLDAP/openldap-release/ 下載最新的版本,lib的說明文檔:http://www.openldap.org/software/man.cgi?query=ldap ,我下的是openldap-2.4.46.tgz,剛一開始下錯了,下了2.4.9版本,一編譯直接報錯,上網查了說要用2.4.15以后的版本,於是就再次下載。
configure時,還會報缺失BDB的錯誤,由於不需要SLAPD,所以就--enable-sldap=no給跳過了。
./configure --enable-slapd=no
make depend
make
make install
上網隨便找了一個代碼,編譯試試,果然可以。
gcc t1.c -L /usr/local/lib/ -lldap -llber
測試性能:測試環境,老電腦AMD A10 6700,12G DDR3,hyper-V安裝windows2012,並配置好域控;docker啟動centos,在centos上編譯基於openldap的測試代碼。
測試代碼如下:
#include <stdio.h>
#include <ldap.h>
#include <sys/time.h>
void print_result(LDAP* ld, LDAPMessage *e)
{
BerElement *ber;
char *a;
char **vals;
int i;
for ( a = ldap_first_attribute( ld, e, &ber ); a != NULL; a = ldap_next_attribute( ld, e, ber ) )
{
if ((vals = ldap_get_values( ld, e, a)) != NULL )
{
for ( i = 0; vals[i] != NULL; i++ )
{
//printf( "%s: %s\n", a, vals[i] );
if (0 == strcmp("memberOf", a))
{
printf("get team:%s\n", vals[i]);
}
}
//printf("print one val end\n");
ldap_value_free( vals );
}
ldap_memfree( a );
}
if ( ber != NULL )
{
ber_free( ber, 0 );
}
//printf("print end\n");
}
void search_ext(LDAP *ld, char *finddn, char *filter)
{
int rc;
int msg;
LDAPMessage *result, *e;
int finish = 0;
struct timeval tm = {0};
tm.tv_sec = -1;
rc = ldap_search_ext(ld, finddn, LDAP_SCOPE_SUBTREE, filter, NULL, 0, NULL, NULL, NULL, &tm, &msg);
if (rc != LDAP_SUCCESS)
{
fprintf(stderr, "ldap_search_ext_s: rc: %d, %s\n", rc, ldap_err2string(rc));
return( 1 );
}
//printf("ldap_search_ext success\n");
int r = ldap_result(ld, msg, LDAP_MSG_ONE, NULL, &result);
if (r > 0)
{
for (e = ldap_first_message(ld, result); e != NULL; e = ldap_next_message(ld, result))
{
print_result(ld, e);
}
}
//printf("search_ext end\n");
return;
}
int main()
{
LDAP *ld;
#define HOSTNAME "192.168.1.110"
#define PORT_NUMBER 389
#define FIND_DN "dc=test,dc=com"
LDAPMessage *result, *e;
BerElement *ber;
char *a;
char **vals;
int i, rc;
int i_version = 3;
struct timeval tm = {0};
tm.tv_sec = -1;
ldap_set_option(NULL, LDAP_OPT_PROTOCOL_VERSION, &i_version);
ldap_set_option(NULL, LDAP_OPT_REFERRALS, LDAP_OPT_ON);
time_t t1 = time(NULL);
for (i = 0; i < 1000; i++)
{
if ( (ld = ldap_init( HOSTNAME, PORT_NUMBER )) == NULL )
{
perror( "ldap_init" );
return( 1 );
}
//printf( "ldap_init success\n" );
rc = ldap_simple_bind_s( ld, "cn=administrator,cn=Users,dc=test,dc=com", "Yanhong001");
if ( rc != LDAP_SUCCESS )
{
fprintf(stderr, "ldap_simple_bind_s: rc: %d, %s\n", rc, ldap_err2string(rc));
return( 1 );
}
// printf( "ldap_simple_bind_s success\n" );
search_ext(ld, "dc=test, dc=com", "(&(objectclass=person)(sAMAccountName=user001))");
//printf("searchexe end:%d\n", i);
ldap_unbind_ext(ld, NULL, NULL);
}
time_t t2 = time(NULL);
printf("time:%d\n", t2-t1);
return 0;
}
測試結果:不加打印,執行1000次認證和search為6秒,加上打印為8秒;如果只做認證,不做search,為5秒。說明這里還有優化的余地。其中只認證一次,然后再循環search,執行第二次search時會卡在ldap_result上一段時間,通過抓包看,AD服務器每次在請求后,馬上會回應,但是客戶端會一直在發送zeroWindow和KeepAlive消息,估計是openldap中自己在做等待,而不是服務器響應的問題。