Packages
ejabberd
16.4.0
26.4.0
26.3.0
26.2.0
26.1.0
25.10.0
25.8.0
25.7.0
25.4.0
25.3.0
24.12.0
24.10.0
24.7.0
24.6.0
24.2.6
23.10.0
23.4.0
23.1.0
22.10.0
22.5.0
21.12.0
21.7.0
21.4.0
21.1.0
20.12.0
20.7.0
20.4.0
20.3.0
20.2.0
20.1.0
19.9.1
19.9.0
19.8.0
19.5.0
19.2.0
18.12.1
18.12.0
18.6.0
18.4.0
18.3.0
18.1.0
17.11.0
17.9.0
17.6.0
17.3.0
17.1.0
16.12.0-beta1
16.9.0
16.8.0
16.6.2
16.6.1
16.6.0
16.4.1
16.4.0
16.3.0
16.2.0
16.1.0-beta1
Robust, Ubiquitous and Massively Scalable Messaging Platform (XMPP, MQTT, SIP Server)
Current section
Files
Jump to
Current section
Files
src/mod_proxy65_service.erl
%%%----------------------------------------------------------------------
%%% File : mod_proxy65_service.erl
%%% Author : Evgeniy Khramtsov <xram@jabber.ru>
%%% Purpose : SOCKS5 Bytestreams XMPP service.
%%% Created : 12 Oct 2006 by Evgeniy Khramtsov <xram@jabber.ru>
%%%
%%%
%%% ejabberd, Copyright (C) 2002-2016 ProcessOne
%%%
%%% This program is free software; you can redistribute it and/or
%%% modify it under the terms of the GNU General Public License as
%%% published by the Free Software Foundation; either version 2 of the
%%% License, or (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful,
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License along
%%% with this program; if not, write to the Free Software Foundation, Inc.,
%%% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
%%%
%%%----------------------------------------------------------------------
-module(mod_proxy65_service).
-author('xram@jabber.ru').
-behaviour(gen_server).
%% gen_server callbacks.
-export([init/1, handle_info/2, handle_call/3,
handle_cast/2, terminate/2, code_change/3]).
-export([start_link/2, add_listener/2,
transform_module_options/1, delete_listener/1]).
-include("ejabberd.hrl").
-include("logger.hrl").
-include("jlib.hrl").
-define(PROCNAME, ejabberd_mod_proxy65_service).
-record(state,
{myhost = <<"">> :: binary(),
serverhost = <<"">> :: binary(),
name = <<"">> :: binary(),
stream_addr = [] :: [attr()],
port = 0 :: inet:port_number(),
ip = {127,0,0,1} :: inet:ip_address(),
acl = none :: atom()}).
%%%------------------------
%%% gen_server callbacks
%%%------------------------
start_link(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
gen_server:start_link({local, Proc}, ?MODULE,
[Host, Opts], []).
init([Host, Opts]) ->
State = parse_options(Host, Opts),
ejabberd_router:register_route(State#state.myhost, Host),
{ok, State}.
terminate(_Reason, #state{myhost = MyHost}) ->
ejabberd_router:unregister_route(MyHost), ok.
handle_info({route, From, To,
#xmlel{name = <<"iq">>} = Packet},
State) ->
IQ = jlib:iq_query_info(Packet),
case catch process_iq(From, IQ, State) of
Result when is_record(Result, iq) ->
ejabberd_router:route(To, From, jlib:iq_to_xml(Result));
{'EXIT', Reason} ->
?ERROR_MSG("Error when processing IQ stanza: ~p",
[Reason]),
Err = jlib:make_error_reply(Packet,
?ERR_INTERNAL_SERVER_ERROR),
ejabberd_router:route(To, From, Err);
_ -> ok
end,
{noreply, State};
handle_info(_Info, State) -> {noreply, State}.
handle_call(get_port_ip, _From, State) ->
{reply, {port_ip, State#state.port, State#state.ip},
State};
handle_call(_Request, _From, State) ->
{reply, ok, State}.
handle_cast(_Request, State) -> {noreply, State}.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%%------------------------
%%% Listener management
%%%------------------------
add_listener(Host, Opts) ->
State = parse_options(Host, Opts),
NewOpts = [Host | Opts],
ejabberd_listener:add_listener({State#state.port,
State#state.ip},
mod_proxy65_stream, NewOpts).
delete_listener(Host) ->
Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
{port_ip, Port, IP} = gen_server:call(Proc,
get_port_ip),
catch ejabberd_listener:delete_listener({Port, IP},
mod_proxy65_stream).
%%%------------------------
%%% IQ Processing
%%%------------------------
%% disco#info request
process_iq(_,
#iq{type = get, xmlns = ?NS_DISCO_INFO, lang = Lang} =
IQ,
#state{name = Name, serverhost = ServerHost}) ->
Info = ejabberd_hooks:run_fold(disco_info, ServerHost,
[], [ServerHost, ?MODULE, <<"">>, <<"">>]),
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_INFO}],
children = iq_disco_info(Lang, Name) ++ Info}]};
%% disco#items request
process_iq(_,
#iq{type = get, xmlns = ?NS_DISCO_ITEMS} = IQ, _) ->
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_DISCO_ITEMS}],
children = []}]};
%% vCard request
process_iq(_,
#iq{type = get, xmlns = ?NS_VCARD, lang = Lang} = IQ,
_) ->
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"vCard">>,
attrs = [{<<"xmlns">>, ?NS_VCARD}],
children = iq_vcard(Lang)}]};
%% bytestreams info request
process_iq(JID,
#iq{type = get, sub_el = SubEl, lang = Lang,
xmlns = ?NS_BYTESTREAMS} =
IQ,
#state{acl = ACL, stream_addr = StreamAddr,
serverhost = ServerHost}) ->
case acl:match_rule(ServerHost, ACL, JID) of
allow ->
StreamHostEl = [#xmlel{name = <<"streamhost">>,
attrs = StreamAddr, children = []}],
IQ#iq{type = result,
sub_el =
[#xmlel{name = <<"query">>,
attrs = [{<<"xmlns">>, ?NS_BYTESTREAMS}],
children = StreamHostEl}]};
deny ->
Txt = <<"Denied by ACL">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_FORBIDDEN(Lang, Txt)]}
end;
%% bytestream activation request
process_iq(InitiatorJID,
#iq{type = set, sub_el = SubEl, lang = Lang,
xmlns = ?NS_BYTESTREAMS} =
IQ,
#state{acl = ACL, serverhost = ServerHost}) ->
case acl:match_rule(ServerHost, ACL, InitiatorJID) of
allow ->
ActivateEl = fxml:get_path_s(SubEl,
[{elem, <<"activate">>}]),
SID = fxml:get_tag_attr_s(<<"sid">>, SubEl),
case catch
jid:from_string(fxml:get_tag_cdata(ActivateEl))
of
TargetJID
when is_record(TargetJID, jid), SID /= <<"">>,
byte_size(SID) =< 128, TargetJID /= InitiatorJID ->
Target =
jid:to_string(jid:tolower(TargetJID)),
Initiator =
jid:to_string(jid:tolower(InitiatorJID)),
SHA1 = p1_sha:sha(<<SID/binary, Initiator/binary, Target/binary>>),
case mod_proxy65_sm:activate_stream(SHA1, InitiatorJID,
TargetJID, ServerHost)
of
ok -> IQ#iq{type = result, sub_el = []};
false ->
Txt = <<"Failed to activate bytestream">>,
IQ#iq{type = error,
sub_el = [SubEl, ?ERRT_ITEM_NOT_FOUND(Lang, Txt)]};
limit ->
Txt = <<"Too many active bytestreams">>,
IQ#iq{type = error,
sub_el = [SubEl, ?ERRT_RESOURCE_CONSTRAINT(Lang, Txt)]};
conflict ->
Txt = <<"Bytestream already activated">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_CONFLICT(Lang, Txt)]};
_ ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_INTERNAL_SERVER_ERROR]}
end;
_ ->
Txt = <<"Malformed JID">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_BAD_REQUEST(Lang, Txt)]}
end;
deny ->
Txt = <<"Denied by ACL">>,
IQ#iq{type = error, sub_el = [SubEl, ?ERRT_FORBIDDEN(Lang, Txt)]}
end;
%% Unknown "set" or "get" request
process_iq(_, #iq{type = Type, sub_el = SubEl} = IQ, _)
when Type == get; Type == set ->
IQ#iq{type = error,
sub_el = [SubEl, ?ERR_SERVICE_UNAVAILABLE]};
%% IQ "result" or "error".
process_iq(_, _, _) -> ok.
%%%-------------------------
%%% Auxiliary functions.
%%%-------------------------
-define(FEATURE(Feat),
#xmlel{name = <<"feature">>,
attrs = [{<<"var">>, Feat}], children = []}).
iq_disco_info(Lang, Name) ->
[#xmlel{name = <<"identity">>,
attrs =
[{<<"category">>, <<"proxy">>},
{<<"type">>, <<"bytestreams">>},
{<<"name">>, translate:translate(Lang, Name)}],
children = []},
?FEATURE((?NS_DISCO_INFO)), ?FEATURE((?NS_VCARD)),
?FEATURE((?NS_BYTESTREAMS))].
iq_vcard(Lang) ->
[#xmlel{name = <<"FN">>, attrs = [],
children = [{xmlcdata, <<"ejabberd/mod_proxy65">>}]},
#xmlel{name = <<"URL">>, attrs = [],
children = [{xmlcdata, ?EJABBERD_URI}]},
#xmlel{name = <<"DESC">>, attrs = [],
children =
[{xmlcdata,
<<(translate:translate(Lang,
<<"ejabberd SOCKS5 Bytestreams module">>))/binary,
"\nCopyright (c) 2003-2016 ProcessOne">>}]}].
parse_options(ServerHost, Opts) ->
MyHost = gen_mod:get_opt_host(ServerHost, Opts,
<<"proxy.@HOST@">>),
Port = gen_mod:get_opt(port, Opts,
fun(P) when is_integer(P), P>0, P<65536 -> P end,
7777),
ACL = gen_mod:get_opt(access, Opts, fun(A) when is_atom(A) -> A end,
all),
Name = gen_mod:get_opt(name, Opts, fun iolist_to_binary/1,
<<"SOCKS5 Bytestreams">>),
IP = gen_mod:get_opt(ip, Opts,
fun(S) ->
{ok, Addr} = inet_parse:address(
binary_to_list(
iolist_to_binary(S))),
Addr
end, get_my_ip()),
HostName = gen_mod:get_opt(hostname, Opts,
fun iolist_to_binary/1,
jlib:ip_to_list(IP)),
StreamAddr = [{<<"jid">>, MyHost},
{<<"host">>, HostName},
{<<"port">>, jlib:integer_to_binary(Port)}],
#state{myhost = MyHost, serverhost = ServerHost,
name = Name, port = Port, ip = IP,
stream_addr = StreamAddr, acl = ACL}.
transform_module_options(Opts) ->
lists:map(
fun({ip, IP}) when is_tuple(IP) ->
{ip, jlib:ip_to_list(IP)};
({hostname, IP}) when is_tuple(IP) ->
{hostname, jlib:ip_to_list(IP)};
(Opt) ->
Opt
end, Opts).
get_my_ip() ->
{ok, MyHostName} = inet:gethostname(),
case inet:getaddr(MyHostName, inet) of
{ok, Addr} -> Addr;
{error, _} -> {127, 0, 0, 1}
end.