mirror of
https://github.com/processone/ejabberd.git
synced 2024-12-20 17:27:00 +01:00
* src/tls/tls_drv.c: Support for "connect" method
* src/tls/tls.erl: Likewise * src/ejabberd_s2s_in.erl: Support for STARTTLS+Dialback * src/ejabberd_s2s_out.erl: Likewise * src/ejabberd_receiver.erl: Added a few hacks ({active,once} mode should be used instead of recv/3 call to avoid them) * src/ejabberd_config.erl: Added s2s_use_starttls and s2s_certfile options * src/ejabberd.cfg.example: Likewise SVN Revision: 426
This commit is contained in:
parent
6309f41b9a
commit
1433dafe6b
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
2005-10-25 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
|
* src/tls/tls_drv.c: Support for "connect" method
|
||||||
|
* src/tls/tls.erl: Likewise
|
||||||
|
|
||||||
|
* src/ejabberd_s2s_in.erl: Support for STARTTLS+Dialback
|
||||||
|
* src/ejabberd_s2s_out.erl: Likewise
|
||||||
|
* src/ejabberd_receiver.erl: Added a few hacks ({active,once} mode
|
||||||
|
should be used instead of recv/3 call to avoid them)
|
||||||
|
* src/ejabberd_config.erl: Added s2s_use_starttls and s2s_certfile
|
||||||
|
options
|
||||||
|
* src/ejabberd.cfg.example: Likewise
|
||||||
|
|
||||||
2005-10-22 Alexey Shchepin <alexey@sevcom.net>
|
2005-10-22 Alexey Shchepin <alexey@sevcom.net>
|
||||||
|
|
||||||
* src/ejabberd_app.erl: Try to load tls_drv at startup to avoid
|
* src/ejabberd_app.erl: Try to load tls_drv at startup to avoid
|
||||||
|
@ -115,6 +115,11 @@
|
|||||||
[{password, "secret"}]}]}
|
[{password, "secret"}]}]}
|
||||||
]}.
|
]}.
|
||||||
|
|
||||||
|
|
||||||
|
% Use STARTTLS+Dialback for S2S connections
|
||||||
|
{s2s_use_starttls, true}.
|
||||||
|
{s2s_certfile, "./ssl.pem"}.
|
||||||
|
|
||||||
% If SRV lookup fails, then port 5269 is used to communicate with remote server
|
% If SRV lookup fails, then port 5269 is used to communicate with remote server
|
||||||
{outgoing_s2s_port, 5269}.
|
{outgoing_s2s_port, 5269}.
|
||||||
|
|
||||||
|
@ -108,6 +108,10 @@ process_term(Term, State) ->
|
|||||||
add_option(listen, Val, State);
|
add_option(listen, Val, State);
|
||||||
{outgoing_s2s_port, Port} ->
|
{outgoing_s2s_port, Port} ->
|
||||||
add_option(outgoing_s2s_port, Port, State);
|
add_option(outgoing_s2s_port, Port, State);
|
||||||
|
{s2s_use_starttls, Port} ->
|
||||||
|
add_option(s2s_use_starttls, Port, State);
|
||||||
|
{s2s_certfile, Port} ->
|
||||||
|
add_option(s2s_certfile, Port, State);
|
||||||
{Opt, Val} ->
|
{Opt, Val} ->
|
||||||
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
lists:foldl(fun(Host, S) -> process_host_term(Term, Host, S) end,
|
||||||
State, State#state.hosts)
|
State, State#state.hosts)
|
||||||
|
@ -36,24 +36,27 @@ receiver(Socket, SockMod, Shaper, C2SPid) ->
|
|||||||
|
|
||||||
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout) ->
|
receiver(Socket, SockMod, ShaperState, C2SPid, XMLStreamState, Timeout) ->
|
||||||
Res = (catch SockMod:recv(Socket, 0, Timeout)),
|
Res = (catch SockMod:recv(Socket, 0, Timeout)),
|
||||||
case Res of
|
receive
|
||||||
{ok, Data} ->
|
{starttls, TLSSocket} ->
|
||||||
receive
|
xml_stream:close(XMLStreamState),
|
||||||
{starttls, TLSSocket} ->
|
XMLStreamState1 = xml_stream:new(C2SPid),
|
||||||
xml_stream:close(XMLStreamState),
|
TLSRes = case Res of
|
||||||
XMLStreamState1 = xml_stream:new(C2SPid),
|
{ok, Data} ->
|
||||||
TLSRes = tls:recv_data(TLSSocket, Data),
|
tls:recv_data(TLSSocket, Data);
|
||||||
receiver1(TLSSocket, tls,
|
_ ->
|
||||||
ShaperState, C2SPid, XMLStreamState1, Timeout,
|
tls:recv_data(TLSSocket, "")
|
||||||
TLSRes)
|
end,
|
||||||
after 0 ->
|
receiver1(TLSSocket, tls,
|
||||||
receiver1(Socket, SockMod,
|
ShaperState, C2SPid, XMLStreamState1, Timeout,
|
||||||
ShaperState, C2SPid, XMLStreamState, Timeout,
|
TLSRes);
|
||||||
Res)
|
{change_timeout, NewTimeout} -> % Dirty hack
|
||||||
end;
|
|
||||||
_ ->
|
|
||||||
receiver1(Socket, SockMod,
|
receiver1(Socket, SockMod,
|
||||||
ShaperState, C2SPid, XMLStreamState, Timeout, Res)
|
ShaperState, C2SPid, XMLStreamState, NewTimeout,
|
||||||
|
Res)
|
||||||
|
after 0 ->
|
||||||
|
receiver1(Socket, SockMod,
|
||||||
|
ShaperState, C2SPid, XMLStreamState, Timeout,
|
||||||
|
Res)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,13 +14,12 @@
|
|||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/2,
|
-export([start/2,
|
||||||
start_link/2,
|
start_link/2]).
|
||||||
send_text/2,
|
|
||||||
send_element/2]).
|
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
wait_for_stream/2,
|
wait_for_stream/2,
|
||||||
|
wait_for_feature_request/2,
|
||||||
stream_established/2,
|
stream_established/2,
|
||||||
handle_event/3,
|
handle_event/3,
|
||||||
handle_sync_event/4,
|
handle_sync_event/4,
|
||||||
@ -34,9 +33,13 @@
|
|||||||
-define(DICT, dict).
|
-define(DICT, dict).
|
||||||
|
|
||||||
-record(state, {socket,
|
-record(state, {socket,
|
||||||
|
sockmod,
|
||||||
receiver,
|
receiver,
|
||||||
streamid,
|
streamid,
|
||||||
shaper,
|
shaper,
|
||||||
|
tls = false,
|
||||||
|
tls_enabled = false,
|
||||||
|
tls_options = [],
|
||||||
connections = ?DICT:new(),
|
connections = ?DICT:new(),
|
||||||
timer}).
|
timer}).
|
||||||
|
|
||||||
@ -49,13 +52,13 @@
|
|||||||
-define(FSMOPTS, []).
|
-define(FSMOPTS, []).
|
||||||
-endif.
|
-endif.
|
||||||
|
|
||||||
-define(STREAM_HEADER,
|
-define(STREAM_HEADER(Version),
|
||||||
("<?xml version='1.0'?>"
|
("<?xml version='1.0'?>"
|
||||||
"<stream:stream "
|
"<stream:stream "
|
||||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||||
"xmlns='jabber:server' "
|
"xmlns='jabber:server' "
|
||||||
"xmlns:db='jabber:server:dialback' "
|
"xmlns:db='jabber:server:dialback' "
|
||||||
"id='" ++ StateData#state.streamid ++ "'>")
|
"id='" ++ StateData#state.streamid ++ "'" ++ Version ++ ">")
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(STREAM_TRAILER, "</stream:stream>").
|
-define(STREAM_TRAILER, "</stream:stream>").
|
||||||
@ -96,12 +99,28 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
{value, {_, S}} -> S;
|
{value, {_, S}} -> S;
|
||||||
_ -> none
|
_ -> none
|
||||||
end,
|
end,
|
||||||
|
StartTLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
UseStartTLS ->
|
||||||
|
UseStartTLS
|
||||||
|
end,
|
||||||
|
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||||
|
undefined ->
|
||||||
|
[];
|
||||||
|
CertFile ->
|
||||||
|
[{certfile, CertFile}]
|
||||||
|
end,
|
||||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||||
{ok, wait_for_stream,
|
{ok, wait_for_stream,
|
||||||
#state{socket = Socket,
|
#state{socket = Socket,
|
||||||
|
sockmod = SockMod,
|
||||||
receiver = ReceiverPid,
|
receiver = ReceiverPid,
|
||||||
streamid = new_id(),
|
streamid = new_id(),
|
||||||
shaper = Shaper,
|
shaper = Shaper,
|
||||||
|
tls = StartTLS,
|
||||||
|
tls_enabled = false,
|
||||||
|
tls_options = TLSOpts,
|
||||||
timer = Timer}}.
|
timer = Timer}}.
|
||||||
|
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
@ -113,18 +132,28 @@ init([{SockMod, Socket}, Opts]) ->
|
|||||||
|
|
||||||
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
wait_for_stream({xmlstreamstart, _Name, Attrs}, StateData) ->
|
||||||
% TODO
|
% TODO
|
||||||
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
|
case {xml:get_attr_s("xmlns", Attrs),
|
||||||
{"jabber:server", "jabber:server:dialback"} ->
|
xml:get_attr_s("xmlns:db", Attrs),
|
||||||
send_text(StateData#state.socket, ?STREAM_HEADER),
|
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||||
{next_state, stream_established, StateData#state{}};
|
{"jabber:server", "jabber:server:dialback", true} when
|
||||||
|
StateData#state.tls ->
|
||||||
|
send_text(StateData, ?STREAM_HEADER(" version='1.0'")),
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "stream:features", [],
|
||||||
|
[{xmlelement, "starttls",
|
||||||
|
[{"xmlns", ?NS_TLS}], []}]}),
|
||||||
|
{next_state, wait_for_feature_request, StateData};
|
||||||
|
{"jabber:server", "jabber:server:dialback", _} ->
|
||||||
|
send_text(StateData, ?STREAM_HEADER("")),
|
||||||
|
{next_state, stream_established, StateData};
|
||||||
_ ->
|
_ ->
|
||||||
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
|
send_text(StateData, ?INVALID_NAMESPACE_ERR),
|
||||||
{stop, normal, StateData}
|
{stop, normal, StateData}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||||
send_text(StateData#state.socket,
|
send_text(StateData,
|
||||||
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
?STREAM_HEADER("") ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
wait_for_stream(timeout, StateData) ->
|
wait_for_stream(timeout, StateData) ->
|
||||||
@ -133,6 +162,45 @@ wait_for_stream(timeout, StateData) ->
|
|||||||
wait_for_stream(closed, StateData) ->
|
wait_for_stream(closed, StateData) ->
|
||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_feature_request({xmlstreamelement, El}, StateData) ->
|
||||||
|
{xmlelement, Name, Attrs, Els} = El,
|
||||||
|
TLS = StateData#state.tls,
|
||||||
|
TLSEnabled = StateData#state.tls_enabled,
|
||||||
|
SockMod = StateData#state.sockmod,
|
||||||
|
case {xml:get_attr_s("xmlns", Attrs), Name} of
|
||||||
|
{?NS_TLS, "starttls"} when TLS == true,
|
||||||
|
TLSEnabled == false,
|
||||||
|
SockMod == gen_tcp ->
|
||||||
|
?INFO_MSG("starttls", []),
|
||||||
|
Socket = StateData#state.socket,
|
||||||
|
TLSOpts = StateData#state.tls_options,
|
||||||
|
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||||
|
ejabberd_receiver:starttls(StateData#state.receiver, TLSSocket),
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "proceed", [{"xmlns", ?NS_TLS}], []}),
|
||||||
|
{next_state, wait_for_stream,
|
||||||
|
StateData#state{sockmod = tls,
|
||||||
|
socket = TLSSocket,
|
||||||
|
streamid = new_id(),
|
||||||
|
tls_enabled = true
|
||||||
|
}};
|
||||||
|
_ ->
|
||||||
|
stream_established({xmlstreamelement, El}, StateData)
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_feature_request({xmlstreamend, _Name}, StateData) ->
|
||||||
|
send_text(StateData, ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_feature_request({xmlstreamerror, _}, StateData) ->
|
||||||
|
send_text(StateData, ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_feature_request(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
stream_established({xmlstreamelement, El}, StateData) ->
|
stream_established({xmlstreamelement, El}, StateData) ->
|
||||||
cancel_timer(StateData#state.timer),
|
cancel_timer(StateData#state.timer),
|
||||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||||
@ -154,7 +222,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
|||||||
StateData#state{connections = Conns,
|
StateData#state{connections = Conns,
|
||||||
timer = Timer}};
|
timer = Timer}};
|
||||||
_ ->
|
_ ->
|
||||||
send_text(StateData#state.socket, ?HOST_UNKNOWN_ERR),
|
send_text(StateData, ?HOST_UNKNOWN_ERR),
|
||||||
{stop, normal, StateData}
|
{stop, normal, StateData}
|
||||||
end;
|
end;
|
||||||
{verify, To, From, Id, Key} ->
|
{verify, To, From, Id, Key} ->
|
||||||
@ -165,7 +233,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
|||||||
Type = if Key == Key1 -> "valid";
|
Type = if Key == Key1 -> "valid";
|
||||||
true -> "invalid"
|
true -> "invalid"
|
||||||
end,
|
end,
|
||||||
send_element(StateData#state.socket,
|
send_element(StateData,
|
||||||
{xmlelement,
|
{xmlelement,
|
||||||
"db:verify",
|
"db:verify",
|
||||||
[{"from", To},
|
[{"from", To},
|
||||||
@ -204,7 +272,7 @@ stream_established({xmlstreamelement, El}, StateData) ->
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
stream_established({valid, From, To}, StateData) ->
|
stream_established({valid, From, To}, StateData) ->
|
||||||
send_element(StateData#state.socket,
|
send_element(StateData,
|
||||||
{xmlelement,
|
{xmlelement,
|
||||||
"db:result",
|
"db:result",
|
||||||
[{"from", To},
|
[{"from", To},
|
||||||
@ -219,7 +287,7 @@ stream_established({valid, From, To}, StateData) ->
|
|||||||
{next_state, stream_established, NSD};
|
{next_state, stream_established, NSD};
|
||||||
|
|
||||||
stream_established({invalid, From, To}, StateData) ->
|
stream_established({invalid, From, To}, StateData) ->
|
||||||
send_element(StateData#state.socket,
|
send_element(StateData,
|
||||||
{xmlelement,
|
{xmlelement,
|
||||||
"db:result",
|
"db:result",
|
||||||
[{"from", To},
|
[{"from", To},
|
||||||
@ -237,8 +305,8 @@ stream_established({xmlstreamend, _Name}, StateData) ->
|
|||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
stream_established({xmlstreamerror, _}, StateData) ->
|
stream_established({xmlstreamerror, _}, StateData) ->
|
||||||
send_text(StateData#state.socket,
|
send_text(StateData,
|
||||||
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
stream_established(timeout, StateData) ->
|
stream_established(timeout, StateData) ->
|
||||||
@ -294,7 +362,7 @@ code_change(_OldVsn, StateName, StateData, _Extra) ->
|
|||||||
%% {stop, Reason, NewStateData}
|
%% {stop, Reason, NewStateData}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
handle_info({send_text, Text}, StateName, StateData) ->
|
handle_info({send_text, Text}, StateName, StateData) ->
|
||||||
send_text(StateData#state.socket, Text),
|
send_text(StateData, Text),
|
||||||
{next_state, StateName, StateData};
|
{next_state, StateName, StateData};
|
||||||
|
|
||||||
handle_info({timeout, Timer, _}, StateName,
|
handle_info({timeout, Timer, _}, StateName,
|
||||||
@ -312,18 +380,18 @@ handle_info(_, StateName, StateData) ->
|
|||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
terminate(Reason, _StateName, StateData) ->
|
terminate(Reason, _StateName, StateData) ->
|
||||||
?INFO_MSG("terminated: ~p", [Reason]),
|
?INFO_MSG("terminated: ~p", [Reason]),
|
||||||
gen_tcp:close(StateData#state.socket),
|
(StateData#state.sockmod):close(StateData#state.socket),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
send_text(Socket, Text) ->
|
send_text(StateData, Text) ->
|
||||||
gen_tcp:send(Socket,Text).
|
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||||
|
|
||||||
send_element(Socket, El) ->
|
send_element(StateData, El) ->
|
||||||
send_text(Socket, xml:element_to_string(El)).
|
send_text(StateData, xml:element_to_string(El)).
|
||||||
|
|
||||||
|
|
||||||
change_shaper(StateData, Host, JID) ->
|
change_shaper(StateData, Host, JID) ->
|
||||||
|
@ -13,13 +13,15 @@
|
|||||||
-behaviour(gen_fsm).
|
-behaviour(gen_fsm).
|
||||||
|
|
||||||
%% External exports
|
%% External exports
|
||||||
-export([start/3, start_link/3, send_text/2, send_element/2]).
|
-export([start/3, start_link/3]).
|
||||||
|
|
||||||
%% gen_fsm callbacks
|
%% gen_fsm callbacks
|
||||||
-export([init/1,
|
-export([init/1,
|
||||||
open_socket/2,
|
open_socket/2,
|
||||||
wait_for_stream/2,
|
wait_for_stream/2,
|
||||||
wait_for_validation/2,
|
wait_for_validation/2,
|
||||||
|
wait_for_features/2,
|
||||||
|
wait_for_starttls_proceed/2,
|
||||||
stream_established/2,
|
stream_established/2,
|
||||||
handle_event/3,
|
handle_event/3,
|
||||||
handle_sync_event/4,
|
handle_sync_event/4,
|
||||||
@ -30,8 +32,15 @@
|
|||||||
-include("ejabberd.hrl").
|
-include("ejabberd.hrl").
|
||||||
-include("jlib.hrl").
|
-include("jlib.hrl").
|
||||||
|
|
||||||
-record(state, {socket, receiver, streamid,
|
-record(state, {socket, receiver,
|
||||||
myname, server, xmlpid, queue,
|
sockmod,
|
||||||
|
streamid,
|
||||||
|
use_v10,
|
||||||
|
tls = false,
|
||||||
|
tls_required = false,
|
||||||
|
tls_enabled = false,
|
||||||
|
tls_options = [],
|
||||||
|
myname, server, queue,
|
||||||
new = false, verify = false,
|
new = false, verify = false,
|
||||||
timer}).
|
timer}).
|
||||||
|
|
||||||
@ -49,7 +58,7 @@
|
|||||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||||
"xmlns='jabber:server' "
|
"xmlns='jabber:server' "
|
||||||
"xmlns:db='jabber:server:dialback' "
|
"xmlns:db='jabber:server:dialback' "
|
||||||
"to='~s'>"
|
"to='~s'~s>"
|
||||||
).
|
).
|
||||||
|
|
||||||
-define(STREAM_TRAILER, "</stream:stream>").
|
-define(STREAM_TRAILER, "</stream:stream>").
|
||||||
@ -86,6 +95,19 @@ start_link(From, Host, Type) ->
|
|||||||
init([From, Server, Type]) ->
|
init([From, Server, Type]) ->
|
||||||
?INFO_MSG("started: ~p", [{From, Server, Type}]),
|
?INFO_MSG("started: ~p", [{From, Server, Type}]),
|
||||||
gen_fsm:send_event(self(), init),
|
gen_fsm:send_event(self(), init),
|
||||||
|
TLS = case ejabberd_config:get_local_option(s2s_use_starttls) of
|
||||||
|
undefined ->
|
||||||
|
false;
|
||||||
|
UseStartTLS ->
|
||||||
|
UseStartTLS
|
||||||
|
end,
|
||||||
|
UseV10 = TLS,
|
||||||
|
TLSOpts = case ejabberd_config:get_local_option(s2s_certfile) of
|
||||||
|
undefined ->
|
||||||
|
[];
|
||||||
|
CertFile ->
|
||||||
|
[{certfile, CertFile}, connect]
|
||||||
|
end,
|
||||||
{New, Verify} = case Type of
|
{New, Verify} = case Type of
|
||||||
{new, Key} ->
|
{new, Key} ->
|
||||||
{Key, false};
|
{Key, false};
|
||||||
@ -93,7 +115,10 @@ init([From, Server, Type]) ->
|
|||||||
{false, {Pid, Key, SID}}
|
{false, {Pid, Key, SID}}
|
||||||
end,
|
end,
|
||||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||||
{ok, open_socket, #state{queue = queue:new(),
|
{ok, open_socket, #state{use_v10 = UseV10,
|
||||||
|
tls = TLS,
|
||||||
|
tls_options = TLSOpts,
|
||||||
|
queue = queue:new(),
|
||||||
myname = From,
|
myname = From,
|
||||||
server = Server,
|
server = Server,
|
||||||
new = New,
|
new = New,
|
||||||
@ -113,23 +138,34 @@ open_socket(init, StateData) ->
|
|||||||
ASCIIAddr ->
|
ASCIIAddr ->
|
||||||
?DEBUG("s2s_out: connecting to ~s:~p~n", [ASCIIAddr, Port]),
|
?DEBUG("s2s_out: connecting to ~s:~p~n", [ASCIIAddr, Port]),
|
||||||
case gen_tcp:connect(ASCIIAddr, Port,
|
case gen_tcp:connect(ASCIIAddr, Port,
|
||||||
[binary, {packet, 0}]) of
|
[binary, {packet, 0},
|
||||||
|
{active, false}]) of
|
||||||
{ok, _Socket} = R -> R;
|
{ok, _Socket} = R -> R;
|
||||||
{error, Reason1} ->
|
{error, Reason1} ->
|
||||||
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
|
?DEBUG("s2s_out: connect return ~p~n", [Reason1]),
|
||||||
catch gen_tcp:connect(Addr, Port,
|
catch gen_tcp:connect(Addr, Port,
|
||||||
[binary, {packet, 0}, inet6])
|
[binary, {packet, 0},
|
||||||
|
{active, false}, inet6])
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
case Res of
|
case Res of
|
||||||
{ok, Socket} ->
|
{ok, Socket} ->
|
||||||
XMLStreamPid = xml_stream:start(self()),
|
ReceiverPid = ejabberd_receiver:start(Socket, gen_tcp, none),
|
||||||
send_text(Socket, io_lib:format(?STREAM_HEADER,
|
Version = if
|
||||||
[StateData#state.server])),
|
StateData#state.use_v10 ->
|
||||||
{next_state, wait_for_stream,
|
" version='1.0'";
|
||||||
StateData#state{socket = Socket,
|
true ->
|
||||||
xmlpid = XMLStreamPid,
|
""
|
||||||
streamid = new_id()}};
|
end,
|
||||||
|
NewStateData = StateData#state{socket = Socket,
|
||||||
|
sockmod = gen_tcp,
|
||||||
|
tls_enabled = false,
|
||||||
|
receiver = ReceiverPid,
|
||||||
|
streamid = new_id()},
|
||||||
|
send_text(NewStateData, io_lib:format(?STREAM_HEADER,
|
||||||
|
[StateData#state.server,
|
||||||
|
Version])),
|
||||||
|
{next_state, wait_for_stream, NewStateData};
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
|
?DEBUG("s2s_out: inet6 connect return ~p~n", [Reason]),
|
||||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||||
@ -140,58 +176,36 @@ open_socket(init, StateData) ->
|
|||||||
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
Error = ?ERR_REMOTE_SERVER_NOT_FOUND,
|
||||||
bounce_messages(Error),
|
bounce_messages(Error),
|
||||||
{stop, normal, StateData}
|
{stop, normal, StateData}
|
||||||
end.
|
end;
|
||||||
|
open_socket(_, StateData) ->
|
||||||
|
{next_state, open_socket, StateData}.
|
||||||
|
|
||||||
|
|
||||||
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) ->
|
||||||
% TODO
|
case {xml:get_attr_s("xmlns", Attrs),
|
||||||
case {xml:get_attr_s("xmlns", Attrs), xml:get_attr_s("xmlns:db", Attrs)} of
|
xml:get_attr_s("xmlns:db", Attrs),
|
||||||
{"jabber:server", "jabber:server:dialback"} ->
|
xml:get_attr_s("version", Attrs) == "1.0"} of
|
||||||
Server = StateData#state.server,
|
{"jabber:server", "jabber:server:dialback", false} ->
|
||||||
New = case StateData#state.new of
|
send_db_request(StateData);
|
||||||
false ->
|
{"jabber:server", "jabber:server:dialback", true} when
|
||||||
case ejabberd_s2s:try_register(
|
StateData#state.use_v10 ->
|
||||||
{StateData#state.myname, Server}) of
|
{next_state, wait_for_features, StateData};
|
||||||
{key, Key} ->
|
{"jabber:server", "", true} when StateData#state.use_v10 ->
|
||||||
Key;
|
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||||
false ->
|
StateData#state.server}]),
|
||||||
false
|
% TODO: clear message queue
|
||||||
end;
|
(StateData#state.sockmod):close(StateData#state.socket),
|
||||||
Key ->
|
gen_fsm:send_event(self(), init),
|
||||||
Key
|
{next_state, open_socket, StateData#state{socket = undefined,
|
||||||
end,
|
use_v10 = false}};
|
||||||
case New of
|
|
||||||
false ->
|
|
||||||
ok;
|
|
||||||
Key1 ->
|
|
||||||
send_element(StateData#state.socket,
|
|
||||||
{xmlelement,
|
|
||||||
"db:result",
|
|
||||||
[{"from", StateData#state.myname},
|
|
||||||
{"to", Server}],
|
|
||||||
[{xmlcdata, Key1}]})
|
|
||||||
end,
|
|
||||||
case StateData#state.verify of
|
|
||||||
false ->
|
|
||||||
ok;
|
|
||||||
{Pid, Key2, SID} ->
|
|
||||||
send_element(StateData#state.socket,
|
|
||||||
{xmlelement,
|
|
||||||
"db:verify",
|
|
||||||
[{"from", StateData#state.myname},
|
|
||||||
{"to", StateData#state.server},
|
|
||||||
{"id", SID}],
|
|
||||||
[{xmlcdata, Key2}]})
|
|
||||||
end,
|
|
||||||
{next_state, wait_for_validation, StateData#state{new = New}};
|
|
||||||
_ ->
|
_ ->
|
||||||
send_text(StateData#state.socket, ?INVALID_NAMESPACE_ERR),
|
send_text(StateData, ?INVALID_NAMESPACE_ERR),
|
||||||
{stop, normal, StateData}
|
{stop, normal, StateData}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
wait_for_stream({xmlstreamerror, _}, StateData) ->
|
||||||
send_text(StateData#state.socket,
|
send_text(StateData,
|
||||||
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
wait_for_stream(timeout, StateData) ->
|
wait_for_stream(timeout, StateData) ->
|
||||||
@ -208,7 +222,7 @@ wait_for_validation({xmlstreamelement, El}, StateData) ->
|
|||||||
?INFO_MSG("recv result: ~p", [{From, To, Id, Type}]),
|
?INFO_MSG("recv result: ~p", [{From, To, Id, Type}]),
|
||||||
case Type of
|
case Type of
|
||||||
"valid" ->
|
"valid" ->
|
||||||
send_queue(StateData#state.socket, StateData#state.queue),
|
send_queue(StateData, StateData#state.queue),
|
||||||
{next_state, stream_established,
|
{next_state, stream_established,
|
||||||
StateData#state{queue = queue:new()}};
|
StateData#state{queue = queue:new()}};
|
||||||
_ ->
|
_ ->
|
||||||
@ -248,8 +262,8 @@ wait_for_validation({xmlstreamend, Name}, StateData) ->
|
|||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
wait_for_validation({xmlstreamerror, _}, StateData) ->
|
wait_for_validation({xmlstreamerror, _}, StateData) ->
|
||||||
send_text(StateData#state.socket,
|
send_text(StateData,
|
||||||
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
wait_for_validation(timeout, StateData) ->
|
wait_for_validation(timeout, StateData) ->
|
||||||
@ -259,6 +273,111 @@ wait_for_validation(closed, StateData) ->
|
|||||||
{stop, normal, StateData}.
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_features({xmlstreamelement, El}, StateData) ->
|
||||||
|
case El of
|
||||||
|
{xmlelement, "stream:features", _Attrs, Els} ->
|
||||||
|
{StartTLS, StartTLSRequired} =
|
||||||
|
lists:foldl(
|
||||||
|
fun({xmlelement, "starttls", Attrs1, Els1} = El1, Acc) ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs1) of
|
||||||
|
?NS_TLS ->
|
||||||
|
Req = case xml:get_subtag(El1, "required") of
|
||||||
|
{xmlelement, _, _, _} -> true;
|
||||||
|
false -> false
|
||||||
|
end,
|
||||||
|
{true, Req};
|
||||||
|
_ ->
|
||||||
|
Acc
|
||||||
|
end;
|
||||||
|
(_, Acc) ->
|
||||||
|
Acc
|
||||||
|
end, {false, false}, Els),
|
||||||
|
if
|
||||||
|
StartTLS and StateData#state.tls and
|
||||||
|
(not StateData#state.tls_enabled) ->
|
||||||
|
StateData#state.receiver ! {change_timeout, 100},
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement, "starttls",
|
||||||
|
[{"xmlns", ?NS_TLS}], []}),
|
||||||
|
{next_state, wait_for_starttls_proceed, StateData};
|
||||||
|
StartTLSRequired and (not StateData#state.tls) ->
|
||||||
|
?INFO_MSG("restarted: ~p", [{StateData#state.myname,
|
||||||
|
StateData#state.server}]),
|
||||||
|
(StateData#state.sockmod):close(StateData#state.socket),
|
||||||
|
gen_fsm:send_event(self(), init),
|
||||||
|
{next_state, open_socket,
|
||||||
|
StateData#state{socket = undefined,
|
||||||
|
use_v10 = false}};
|
||||||
|
true ->
|
||||||
|
send_db_request(StateData)
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
send_text(StateData,
|
||||||
|
xml:element_to_string(?SERR_BAD_FORMAT) ++
|
||||||
|
?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_features({xmlstreamend, Name}, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_features({xmlstreamerror, _}, StateData) ->
|
||||||
|
send_text(StateData,
|
||||||
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_features(timeout, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_features(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
|
wait_for_starttls_proceed({xmlstreamelement, El}, StateData) ->
|
||||||
|
case El of
|
||||||
|
{xmlelement, "proceed", Attrs, _Els} ->
|
||||||
|
case xml:get_attr_s("xmlns", Attrs) of
|
||||||
|
?NS_TLS ->
|
||||||
|
?INFO_MSG("starttls: ~p", [{StateData#state.myname,
|
||||||
|
StateData#state.server}]),
|
||||||
|
Socket = StateData#state.socket,
|
||||||
|
TLSOpts = StateData#state.tls_options,
|
||||||
|
{ok, TLSSocket} = tls:tcp_to_tls(Socket, TLSOpts),
|
||||||
|
ejabberd_receiver:starttls(
|
||||||
|
StateData#state.receiver, TLSSocket),
|
||||||
|
StateData#state.receiver ! {change_timeout, infinity},
|
||||||
|
NewStateData = StateData#state{sockmod = tls,
|
||||||
|
socket = TLSSocket,
|
||||||
|
streamid = new_id(),
|
||||||
|
tls_enabled = true
|
||||||
|
},
|
||||||
|
R = send_text(NewStateData,
|
||||||
|
io_lib:format(?STREAM_HEADER,
|
||||||
|
[StateData#state.server,
|
||||||
|
" version='1.0'"])),
|
||||||
|
{next_state, wait_for_stream, NewStateData};
|
||||||
|
_ ->
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
_ ->
|
||||||
|
{stop, normal, StateData}
|
||||||
|
end;
|
||||||
|
|
||||||
|
wait_for_starttls_proceed({xmlstreamend, Name}, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_starttls_proceed({xmlstreamerror, _}, StateData) ->
|
||||||
|
send_text(StateData,
|
||||||
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_starttls_proceed(timeout, StateData) ->
|
||||||
|
{stop, normal, StateData};
|
||||||
|
|
||||||
|
wait_for_starttls_proceed(closed, StateData) ->
|
||||||
|
{stop, normal, StateData}.
|
||||||
|
|
||||||
|
|
||||||
stream_established({xmlstreamelement, El}, StateData) ->
|
stream_established({xmlstreamelement, El}, StateData) ->
|
||||||
?INFO_MSG("stream established", []),
|
?INFO_MSG("stream established", []),
|
||||||
case is_verify_res(El) of
|
case is_verify_res(El) of
|
||||||
@ -290,8 +409,8 @@ stream_established({xmlstreamend, Name}, StateData) ->
|
|||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
stream_established({xmlstreamerror, _}, StateData) ->
|
stream_established({xmlstreamerror, _}, StateData) ->
|
||||||
send_text(StateData#state.socket,
|
send_text(StateData,
|
||||||
?STREAM_HEADER ++ ?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
?INVALID_XML_ERR ++ ?STREAM_TRAILER),
|
||||||
{stop, normal, StateData};
|
{stop, normal, StateData};
|
||||||
|
|
||||||
stream_established(timeout, StateData) ->
|
stream_established(timeout, StateData) ->
|
||||||
@ -347,7 +466,7 @@ code_change(OldVsn, StateName, StateData, Extra) ->
|
|||||||
%% {stop, Reason, NewStateData}
|
%% {stop, Reason, NewStateData}
|
||||||
%%----------------------------------------------------------------------
|
%%----------------------------------------------------------------------
|
||||||
handle_info({send_text, Text}, StateName, StateData) ->
|
handle_info({send_text, Text}, StateName, StateData) ->
|
||||||
send_text(StateData#state.socket, Text),
|
send_text(StateData, Text),
|
||||||
cancel_timer(StateData#state.timer),
|
cancel_timer(StateData#state.timer),
|
||||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||||
{next_state, StateName, StateData#state{timer = Timer}};
|
{next_state, StateName, StateData#state{timer = Timer}};
|
||||||
@ -357,7 +476,7 @@ handle_info({send_element, El}, StateName, StateData) ->
|
|||||||
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
Timer = erlang:start_timer(?S2STIMEOUT, self(), []),
|
||||||
case StateName of
|
case StateName of
|
||||||
stream_established ->
|
stream_established ->
|
||||||
send_element(StateData#state.socket, El),
|
send_element(StateData, El),
|
||||||
{next_state, StateName, StateData#state{timer = Timer}};
|
{next_state, StateName, StateData#state{timer = Timer}};
|
||||||
_ ->
|
_ ->
|
||||||
Q = queue:in(El, StateData#state.queue),
|
Q = queue:in(El, StateData#state.queue),
|
||||||
@ -365,17 +484,17 @@ handle_info({send_element, El}, StateName, StateData) ->
|
|||||||
timer = Timer}}
|
timer = Timer}}
|
||||||
end;
|
end;
|
||||||
|
|
||||||
handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
%handle_info({tcp, Socket, Data}, StateName, StateData) ->
|
||||||
xml_stream:send_text(StateData#state.xmlpid, Data),
|
% xml_stream:send_text(StateData#state.xmlpid, Data),
|
||||||
{next_state, StateName, StateData};
|
% {next_state, StateName, StateData};
|
||||||
|
%
|
||||||
handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
%handle_info({tcp_closed, Socket}, StateName, StateData) ->
|
||||||
gen_fsm:send_event(self(), closed),
|
% gen_fsm:send_event(self(), closed),
|
||||||
{next_state, StateName, StateData};
|
% {next_state, StateName, StateData};
|
||||||
|
%
|
||||||
handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
%handle_info({tcp_error, Socket, Reason}, StateName, StateData) ->
|
||||||
gen_fsm:send_event(self(), closed),
|
% gen_fsm:send_event(self(), closed),
|
||||||
{next_state, StateName, StateData};
|
% {next_state, StateName, StateData};
|
||||||
|
|
||||||
handle_info({timeout, Timer, _}, StateName,
|
handle_info({timeout, Timer, _}, StateName,
|
||||||
#state{timer = Timer} = StateData) ->
|
#state{timer = Timer} = StateData) ->
|
||||||
@ -404,8 +523,7 @@ terminate(Reason, StateName, StateData) ->
|
|||||||
undefined ->
|
undefined ->
|
||||||
ok;
|
ok;
|
||||||
Socket ->
|
Socket ->
|
||||||
gen_tcp:close(Socket),
|
(StateData#state.sockmod):close(Socket)
|
||||||
exit(StateData#state.xmlpid, closed)
|
|
||||||
end,
|
end,
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
@ -413,17 +531,17 @@ terminate(Reason, StateName, StateData) ->
|
|||||||
%%% Internal functions
|
%%% Internal functions
|
||||||
%%%----------------------------------------------------------------------
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
send_text(Socket, Text) ->
|
send_text(StateData, Text) ->
|
||||||
gen_tcp:send(Socket,Text).
|
(StateData#state.sockmod):send(StateData#state.socket, Text).
|
||||||
|
|
||||||
send_element(Socket, El) ->
|
send_element(StateData, El) ->
|
||||||
send_text(Socket, xml:element_to_string(El)).
|
send_text(StateData, xml:element_to_string(El)).
|
||||||
|
|
||||||
send_queue(Socket, Q) ->
|
send_queue(StateData, Q) ->
|
||||||
case queue:out(Q) of
|
case queue:out(Q) of
|
||||||
{{value, El}, Q1} ->
|
{{value, El}, Q1} ->
|
||||||
send_element(Socket, El),
|
send_element(StateData, El),
|
||||||
send_queue(Socket, Q1);
|
send_queue(StateData, Q1);
|
||||||
{empty, Q1} ->
|
{empty, Q1} ->
|
||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
@ -470,20 +588,46 @@ bounce_messages(Error) ->
|
|||||||
ok
|
ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
|
||||||
% {key,
|
send_db_request(StateData) ->
|
||||||
% xml:get_attr_s("to", Attrs),
|
Server = StateData#state.server,
|
||||||
% xml:get_attr_s("from", Attrs),
|
New = case StateData#state.new of
|
||||||
% xml:get_attr_s("id", Attrs),
|
false ->
|
||||||
% xml:get_cdata(Els)};
|
case ejabberd_s2s:try_register(
|
||||||
%is_key_packet({xmlelement, Name, Attrs, Els}) when Name == "db:verify" ->
|
{StateData#state.myname, Server}) of
|
||||||
% {verify,
|
{key, Key} ->
|
||||||
% xml:get_attr_s("to", Attrs),
|
Key;
|
||||||
% xml:get_attr_s("from", Attrs),
|
false ->
|
||||||
% xml:get_attr_s("id", Attrs),
|
false
|
||||||
% xml:get_cdata(Els)};
|
end;
|
||||||
%is_key_packet(_) ->
|
Key ->
|
||||||
% false.
|
Key
|
||||||
|
end,
|
||||||
|
case New of
|
||||||
|
false ->
|
||||||
|
ok;
|
||||||
|
Key1 ->
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement,
|
||||||
|
"db:result",
|
||||||
|
[{"from", StateData#state.myname},
|
||||||
|
{"to", Server}],
|
||||||
|
[{xmlcdata, Key1}]})
|
||||||
|
end,
|
||||||
|
case StateData#state.verify of
|
||||||
|
false ->
|
||||||
|
ok;
|
||||||
|
{Pid, Key2, SID} ->
|
||||||
|
send_element(StateData,
|
||||||
|
{xmlelement,
|
||||||
|
"db:verify",
|
||||||
|
[{"from", StateData#state.myname},
|
||||||
|
{"to", StateData#state.server},
|
||||||
|
{"id", SID}],
|
||||||
|
[{xmlcdata, Key2}]})
|
||||||
|
end,
|
||||||
|
{next_state, wait_for_validation, StateData#state{new = New}}.
|
||||||
|
|
||||||
|
|
||||||
is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
is_verify_res({xmlelement, Name, Attrs, Els}) when Name == "db:result" ->
|
||||||
{result,
|
{result,
|
||||||
|
@ -27,11 +27,12 @@
|
|||||||
code_change/3,
|
code_change/3,
|
||||||
terminate/2]).
|
terminate/2]).
|
||||||
|
|
||||||
-define(SET_CERTIFICATE_FILE, 1).
|
-define(SET_CERTIFICATE_FILE_ACCEPT, 1).
|
||||||
-define(SET_ENCRYPTED_INPUT, 2).
|
-define(SET_CERTIFICATE_FILE_CONNECT, 2).
|
||||||
-define(SET_DECRYPTED_OUTPUT, 3).
|
-define(SET_ENCRYPTED_INPUT, 3).
|
||||||
-define(GET_ENCRYPTED_OUTPUT, 4).
|
-define(SET_DECRYPTED_OUTPUT, 4).
|
||||||
-define(GET_DECRYPTED_INPUT, 5).
|
-define(GET_ENCRYPTED_OUTPUT, 5).
|
||||||
|
-define(GET_DECRYPTED_INPUT, 6).
|
||||||
|
|
||||||
-record(tlssock, {tcpsock, tlsport}).
|
-record(tlssock, {tcpsock, tlsport}).
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ start_link() ->
|
|||||||
init([]) ->
|
init([]) ->
|
||||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||||
Port = open_port({spawn, tls_drv}, [binary]),
|
Port = open_port({spawn, tls_drv}, [binary]),
|
||||||
Res = port_control(Port, ?SET_CERTIFICATE_FILE, "./ssl.pem" ++ [0]),
|
Res = port_control(Port, ?SET_CERTIFICATE_FILE_ACCEPT, "./ssl.pem" ++ [0]),
|
||||||
case Res of
|
case Res of
|
||||||
<<0>> ->
|
<<0>> ->
|
||||||
%ets:new(iconv_table, [set, public, named_table]),
|
%ets:new(iconv_table, [set, public, named_table]),
|
||||||
@ -86,8 +87,13 @@ tcp_to_tls(TCPSocket, Options) ->
|
|||||||
{value, {certfile, CertFile}} ->
|
{value, {certfile, CertFile}} ->
|
||||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||||
Port = open_port({spawn, tls_drv}, [binary]),
|
Port = open_port({spawn, tls_drv}, [binary]),
|
||||||
case port_control(Port, ?SET_CERTIFICATE_FILE,
|
Command = case lists:member(connect, Options) of
|
||||||
CertFile ++ [0]) of
|
true ->
|
||||||
|
?SET_CERTIFICATE_FILE_CONNECT;
|
||||||
|
false ->
|
||||||
|
?SET_CERTIFICATE_FILE_ACCEPT
|
||||||
|
end,
|
||||||
|
case port_control(Port, Command, CertFile ++ [0]) of
|
||||||
<<0>> ->
|
<<0>> ->
|
||||||
{ok, #tlssock{tcpsock = TCPSocket, tlsport = Port}};
|
{ok, #tlssock{tcpsock = TCPSocket, tlsport = Port}};
|
||||||
<<1, Error/binary>> ->
|
<<1, Error/binary>> ->
|
||||||
@ -145,7 +151,10 @@ send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet) ->
|
|||||||
{error, binary_to_list(Error)}
|
{error, binary_to_list(Error)}
|
||||||
end;
|
end;
|
||||||
<<1, Error/binary>> ->
|
<<1, Error/binary>> ->
|
||||||
{error, binary_to_list(Error)}
|
{error, binary_to_list(Error)};
|
||||||
|
<<2>> -> % Dirty hack
|
||||||
|
receive after 100 -> ok end,
|
||||||
|
send(#tlssock{tcpsock = TCPSocket, tlsport = Port}, Packet)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
|
||||||
@ -158,7 +167,8 @@ test() ->
|
|||||||
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
ok = erl_ddll:load_driver(ejabberd:get_so_path(), tls_drv),
|
||||||
Port = open_port({spawn, tls_drv}, [binary]),
|
Port = open_port({spawn, tls_drv}, [binary]),
|
||||||
io:format("open_port: ~p~n", [Port]),
|
io:format("open_port: ~p~n", [Port]),
|
||||||
PCRes = port_control(Port, ?SET_CERTIFICATE_FILE, "./ssl.pem" ++ [0]),
|
PCRes = port_control(Port, ?SET_CERTIFICATE_FILE_ACCEPT,
|
||||||
|
"./ssl.pem" ++ [0]),
|
||||||
io:format("port_control: ~p~n", [PCRes]),
|
io:format("port_control: ~p~n", [PCRes]),
|
||||||
{ok, ListenSocket} = gen_tcp:listen(1234, [binary,
|
{ok, ListenSocket} = gen_tcp:listen(1234, [binary,
|
||||||
{packet, 0},
|
{packet, 0},
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <erl_driver.h>
|
#include <erl_driver.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
|
||||||
|
|
||||||
#define BUF_SIZE 1024
|
#define BUF_SIZE 1024
|
||||||
@ -45,11 +46,12 @@ static void tls_drv_stop(ErlDrvData handle)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#define SET_CERTIFICATE_FILE 1
|
#define SET_CERTIFICATE_FILE_ACCEPT 1
|
||||||
#define SET_ENCRYPTED_INPUT 2
|
#define SET_CERTIFICATE_FILE_CONNECT 2
|
||||||
#define SET_DECRYPTED_OUTPUT 3
|
#define SET_ENCRYPTED_INPUT 3
|
||||||
#define GET_ENCRYPTED_OUTPUT 4
|
#define SET_DECRYPTED_OUTPUT 4
|
||||||
#define GET_DECRYPTED_INPUT 5
|
#define GET_ENCRYPTED_OUTPUT 5
|
||||||
|
#define GET_DECRYPTED_INPUT 6
|
||||||
|
|
||||||
|
|
||||||
#define die_unless(cond, errstr) \
|
#define die_unless(cond, errstr) \
|
||||||
@ -76,8 +78,9 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
|
|
||||||
switch (command)
|
switch (command)
|
||||||
{
|
{
|
||||||
case SET_CERTIFICATE_FILE:
|
case SET_CERTIFICATE_FILE_ACCEPT:
|
||||||
d->ctx = SSL_CTX_new(SSLv23_server_method());
|
case SET_CERTIFICATE_FILE_CONNECT:
|
||||||
|
d->ctx = SSL_CTX_new(SSLv23_method());
|
||||||
die_unless(d->ctx, "SSL_CTX_new failed");
|
die_unless(d->ctx, "SSL_CTX_new failed");
|
||||||
|
|
||||||
res = SSL_CTX_use_certificate_file(d->ctx, buf, SSL_FILETYPE_PEM);
|
res = SSL_CTX_use_certificate_file(d->ctx, buf, SSL_FILETYPE_PEM);
|
||||||
@ -97,7 +100,10 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
|
|
||||||
SSL_set_bio(d->ssl, d->bio_read, d->bio_write);
|
SSL_set_bio(d->ssl, d->bio_read, d->bio_write);
|
||||||
|
|
||||||
SSL_set_accept_state(d->ssl);
|
if (command == SET_CERTIFICATE_FILE_ACCEPT)
|
||||||
|
SSL_set_accept_state(d->ssl);
|
||||||
|
else
|
||||||
|
SSL_set_connect_state(d->ssl);
|
||||||
break;
|
break;
|
||||||
case SET_ENCRYPTED_INPUT:
|
case SET_ENCRYPTED_INPUT:
|
||||||
die_unless(d->ssl, "SSL not initialized");
|
die_unless(d->ssl, "SSL not initialized");
|
||||||
@ -106,6 +112,19 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
case SET_DECRYPTED_OUTPUT:
|
case SET_DECRYPTED_OUTPUT:
|
||||||
die_unless(d->ssl, "SSL not initialized");
|
die_unless(d->ssl, "SSL not initialized");
|
||||||
res = SSL_write(d->ssl, buf, len);
|
res = SSL_write(d->ssl, buf, len);
|
||||||
|
if (res <= 0)
|
||||||
|
{
|
||||||
|
res = SSL_get_error(d->ssl, res);
|
||||||
|
if (res == SSL_ERROR_WANT_READ || res == SSL_ERROR_WANT_WRITE)
|
||||||
|
{
|
||||||
|
b = driver_alloc_binary(1);
|
||||||
|
b->orig_bytes[0] = 2;
|
||||||
|
*rbuf = (char *)b;
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
die_unless(0, "SSL_write failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GET_ENCRYPTED_OUTPUT:
|
case GET_ENCRYPTED_OUTPUT:
|
||||||
die_unless(d->ssl, "SSL not initialized");
|
die_unless(d->ssl, "SSL not initialized");
|
||||||
@ -128,13 +147,10 @@ static int tls_drv_control(ErlDrvData handle,
|
|||||||
case GET_DECRYPTED_INPUT:
|
case GET_DECRYPTED_INPUT:
|
||||||
if (!SSL_is_init_finished(d->ssl))
|
if (!SSL_is_init_finished(d->ssl))
|
||||||
{
|
{
|
||||||
//printf("Doing SSL_accept\r\n");
|
res = SSL_do_handshake(d->ssl);
|
||||||
res = SSL_accept(d->ssl);
|
if (res <= 0)
|
||||||
//if (res == 0)
|
|
||||||
// printf("SSL_accept returned zero\r\n");
|
|
||||||
if (res < 0)
|
|
||||||
die_unless(SSL_get_error(d->ssl, res) == SSL_ERROR_WANT_READ,
|
die_unless(SSL_get_error(d->ssl, res) == SSL_ERROR_WANT_READ,
|
||||||
"SSL_accept failed");
|
"SSL_do_handshake failed");
|
||||||
} else {
|
} else {
|
||||||
size = BUF_SIZE + 1;
|
size = BUF_SIZE + 1;
|
||||||
rlen = 1;
|
rlen = 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user