📄 eldap.erl
字号:
cancel_timer(Timer), Reply = check_bind_reply(Result, From), {reply, Reply, From, S#eldap{dict = New_dict}}; {OtherName, OtherResult} -> New_dict = dict:erase(Id, Dict), cancel_timer(Timer), {reply, {error, {invalid_result, OtherName, OtherResult}}, From, S#eldap{dict = New_dict}} end; Error -> Error end.check_reply(#'LDAPResult'{resultCode = success}, From) -> ok;check_reply(#'LDAPResult'{resultCode = Reason}, From) -> {error, Reason};check_reply(Other, From) -> {error, Other}.check_bind_reply(#'BindResponse'{resultCode = success}, From) -> ok;check_bind_reply(#'BindResponse'{resultCode = Reason}, From) -> {error, Reason};check_bind_reply(Other, From) -> {error, Other}.get_op_rec(Id, Dict) -> case dict:find(Id, Dict) of {ok, [{Timer, From, Name}|Res]} -> {Timer, From, Name, Res}; error -> throw({error, unkown_id}) end.%%-----------------------------------------------------------------------%% recvd_wait_bind_response packet%% Deals with incoming packets in the wait_bind_response state%% Will return one of:%% bound - Success - move to active state%% {fail_bind, Reason} - Failed%% {error, Reason}%% {'EXIT', Reason} - Broken packet%%-----------------------------------------------------------------------recvd_wait_bind_response(Pkt, S) -> check_tag(Pkt), case asn1rt:decode('ELDAPv3', 'LDAPMessage', Pkt) of {ok,Msg} -> log2("~p", [Msg], S), check_id(S#eldap.id, Msg#'LDAPMessage'.messageID), case Msg#'LDAPMessage'.protocolOp of {bindResponse, Result} -> case Result#'LDAPResult'.resultCode of success -> bound; Error -> {fail_bind, Error} end end; Else -> {fail_bind, Else} end.check_id(Id, Id) -> ok;check_id(_, _) -> throw({error, wrong_bind_id}).%%-----------------------------------------------------------------------%% General Helpers%%-----------------------------------------------------------------------cancel_timer(Timer) -> erlang:cancel_timer(Timer), receive {timeout, Timer, _} -> ok after 0 -> ok end.%%% Sanity check of received packetcheck_tag(Data) -> case asn1rt_ber_bin:decode_tag(Data) of {Tag, Data1, Rb} -> case asn1rt_ber_bin:decode_length(Data1) of {{Len,Data2}, Rb2} -> ok; _ -> throw({error,decoded_tag_length}) end; _ -> throw({error,decoded_tag}) end.close_and_retry(S) -> gen_tcp:close(S#eldap.fd), retry_connect().retry_connect() -> erlang:send_after(?RETRY_TIMEOUT, self(), {timeout, retry_connect}).%%-----------------------------------------------------------------------%% Sort out timed out commands%%-----------------------------------------------------------------------cmd_timeout(Timer, Id, S) -> Dict = S#eldap.dict, case dict:find(Id, Dict) of {ok, [{Timer, From, Name}|Res]} -> case Name of searchRequest -> {Res1, Ref1} = polish(Res), New_dict = dict:erase(Id, Dict), {reply, From, {timeout, #eldap_search_result{entries = Res1, referrals = Ref1}}, S#eldap{dict = New_dict}}; Others -> New_dict = dict:erase(Id, Dict), {reply, From, {error, timeout}, S#eldap{dict = New_dict}} end; error -> {error, timed_out_cmd_not_in_dict} end.%%-----------------------------------------------------------------------%% Common stuff for results%%-----------------------------------------------------------------------%%%%%% Polish the returned search result%%%polish(Entries) -> polish(Entries, [], []).polish([H|T], Res, Ref) when record(H, 'SearchResultEntry') -> ObjectName = H#'SearchResultEntry'.objectName, F = fun({_,A,V}) -> {A,V} end, Attrs = lists:map(F, H#'SearchResultEntry'.attributes), polish(T, [#eldap_entry{object_name = ObjectName, attributes = Attrs}|Res], Ref);polish([H|T], Res, Ref) -> % No special treatment of referrals at the moment. polish(T, Res, [H|Ref]);polish([], Res, Ref) -> {Res, Ref}.%%-----------------------------------------------------------------------%% Connect to next server in list and attempt to bind to it.%%-----------------------------------------------------------------------connect_bind(S) -> Host = next_host(S#eldap.host, S#eldap.hosts), TcpOpts = [{packet, asn1}, {active, true}, binary], case gen_tcp:connect(Host, S#eldap.port, TcpOpts) of {ok, Socket} -> case bind_request(Socket, S) of {ok, NewS} -> Timer = erlang:start_timer(?BIND_TIMEOUT, self(), {timeout, bind_timeout}), {ok, wait_bind_response, NewS#eldap{fd = Socket, host = Host, bind_timer = Timer}}; {error, Reason} -> gen_tcp:close(Socket), erlang:send_after(?RETRY_TIMEOUT, self(), {timeout, retry_connect}), {ok, connecting, S#eldap{host = Host}} end; {error, Reason} -> erlang:send_after(?RETRY_TIMEOUT, self(), {timeout, retry_connect}), {ok, connecting, S#eldap{host = Host}} end.bind_request(Socket, S) -> Id = bump_id(S), Req = #'BindRequest'{version = S#eldap.version, name = S#eldap.rootdn, authentication = {simple, S#eldap.passwd}}, Message = #'LDAPMessage'{messageID = Id, protocolOp = {bindRequest, Req}}, log2("Message:~p~n",[Message], S), {ok, Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), ok = gen_tcp:send(Socket, Bytes), {ok, S#eldap{id = Id}}.%% Given last tried Server, find next one to trynext_host(null, [H|_]) -> H; % First time, take firstnext_host(Host, Hosts) -> % Find next in turn next_host(Host, Hosts, Hosts).next_host(Host, [Host], Hosts) -> hd(Hosts); % Wrap back to firstnext_host(Host, [Host|Tail], Hosts) -> hd(Tail); % Take nextnext_host(Host, [], Hosts) -> hd(Hosts); % Never connected before? (shouldn't happen)next_host(Host, [H|T], Hosts) -> next_host(Host, T, Hosts).%%% --------------------------------------------------------------------%%% Verify the input data%%% --------------------------------------------------------------------v_filter({'and',L}) -> {'and',L};v_filter({'or', L}) -> {'or',L};v_filter({'not',L}) -> {'not',L};v_filter({equalityMatch,AV}) -> {equalityMatch,AV};v_filter({greaterOrEqual,AV}) -> {greaterOrEqual,AV};v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV};v_filter({approxMatch,AV}) -> {approxMatch,AV};v_filter({present,A}) -> {present,A};v_filter({substrings,S}) when record(S,'SubstringFilter') -> {substrings,S};v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}).v_modifications(Mods) -> F = fun({_,Op,_}) -> case lists:member(Op,[add,delete,replace]) of true -> true; _ -> throw({error,{mod_operation,Op}}) end end, lists:foreach(F, Mods).v_substr([{Key,Str}|T]) when list(Str),Key==initial;Key==any;Key==final -> [{Key,Str}|v_substr(T)];v_substr([H|T]) -> throw({error,{substring_arg,H}});v_substr([]) -> [].v_scope(baseObject) -> baseObject;v_scope(singleLevel) -> singleLevel;v_scope(wholeSubtree) -> wholeSubtree;v_scope(_Scope) -> throw({error,concat(["unknown scope: ",_Scope])}).v_bool(true) -> true;v_bool(false) -> false;v_bool(_Bool) -> throw({error,concat(["not Boolean: ",_Bool])}).v_timeout(I) when integer(I), I>=0 -> I;v_timeout(_I) -> throw({error,concat(["timeout not positive integer: ",_I])}).v_attributes(Attrs) -> F = fun(A) when list(A) -> A; (A) -> throw({error,concat(["attribute not String: ",A])}) end, lists:map(F,Attrs).%%% --------------------------------------------------------------------%%% Get and Validate the initial configuration%%% --------------------------------------------------------------------get_config() -> Priv_dir = code:priv_dir(eldap), File = filename:join(Priv_dir, "eldap.conf"), case file:consult(File) of {ok, Entries} -> case catch parse(Entries) of {ok, Hosts, Port, Rootdn, Passwd, Log} -> {ok, Hosts, Port, Rootdn, Passwd, Log}; {error, Reason} -> {error, Reason}; {'EXIT', Reason} -> {error, Reason} end; {error, Reason} -> {error, Reason} end.parse(Entries) -> {ok, get_hosts(host, Entries), get_integer(port, Entries), get_list(rootdn, Entries), get_list(passwd, Entries), get_log(log, Entries)}.get_integer(Key, List) -> case lists:keysearch(Key, 1, List) of {value, {Key, Value}} when integer(Value) -> Value; {value, {Key, Value}} -> throw({error, "Bad Value in Config for " ++ atom_to_list(Key)}); false -> throw({error, "No Entry in Config for " ++ atom_to_list(Key)}) end.get_list(Key, List) -> case lists:keysearch(Key, 1, List) of {value, {Key, Value}} when list(Value) -> Value; {value, {Key, Value}} -> throw({error, "Bad Value in Config for " ++ atom_to_list(Key)}); false -> throw({error, "No Entry in Config for " ++ atom_to_list(Key)}) end.get_log(Key, List) -> case lists:keysearch(Key, 1, List) of {value, {Key, Value}} when function(Value) -> Value; {value, {Key, Else}} -> false; false -> fun(Level, Format, Args) -> io:format("--- " ++ Format, Args) end end.get_hosts(Key, List) -> lists:map(fun({Key1, {A,B,C,D}}) when integer(A), integer(B), integer(C), integer(D), Key == Key1-> {A,B,C,D}; ({Key1, Value}) when list(Value), Key == Key1-> Value; ({Else, Value}) -> throw({error, "Bad Hostname in config"}) end, List).%%% --------------------------------------------------------------------%%% Other Stuff%%% --------------------------------------------------------------------bump_id(#eldap{id = Id}) when Id > ?MAX_TRANSACTION_ID -> ?MIN_TRANSACTION_ID;bump_id(#eldap{id = Id}) -> Id + 1.%%% --------------------------------------------------------------------%%% Log routines. Call a user provided log routine Fun.%%% --------------------------------------------------------------------log1(Str, Args, #eldap{log = Fun, debug_level = N}) -> log(Fun, Str, Args, 1, N).log2(Str, Args, #eldap{log = Fun, debug_level = N}) -> log(Fun, Str, Args, 2, N).log(Fun, Str, Args, This_level, Status) when function(Fun), This_level =< Status -> catch Fun(This_level, Str, Args);log(_, _, _, _, _) -> ok.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -