Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
View | Details | Raw Unified | Return to bug 137724 | Differences between
and this patch

Collapse All | Expand All

(-)ejabberd-1.1.1/src/ejabberd.cfg.example (+6 lines)
Lines 125-130 Link Here
125
			    {max_stanza_size, 131072}
125
			    {max_stanza_size, 131072}
126
			   ]},
126
			   ]},
127
  {5280, ejabberd_http,    [http_poll, web_admin]},
127
  {5280, ejabberd_http,    [http_poll, web_admin]},
128
  %{7777, proxy65_listener, [{shaper, c2s_shaper}]},
128
  {8888, ejabberd_service, [{access, all},
129
  {8888, ejabberd_service, [{access, all},
129
			    {hosts, ["icq.localhost", "sms.localhost"],
130
			    {hosts, ["icq.localhost", "sms.localhost"],
130
			     [{password, "secret"}]}]}
131
			     [{password, "secret"}]}]}
Lines 171-176 Link Here
171
  {mod_pubsub,     []},
172
  {mod_pubsub,     []},
172
  {mod_time,       []},
173
  {mod_time,       []},
173
  {mod_last,       []},
174
  {mod_last,       []},
175
  %% Simple configuration (hostname:7777)
176
  %%{mod_proxy65,  []},
177
  %% Several possible hostnames
178
  %%{mod_proxy65,  [{access, all},
179
  %%                {streamhosts, [{"example.com", 7777}, {"192.168.0.42", 7777}]}]},
174
  {mod_version,    []}
180
  {mod_version,    []}
175
 ]}.
181
 ]}.
176
182
(-)ejabberd-1.1.1/src/jlib.hrl (+1 lines)
Lines 34-39 Link Here
34
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
34
-define(NS_PUBSUB_OWNER, "http://jabber.org/protocol/pubsub#owner").
35
-define(NS_PUBSUB_NMI,   "http://jabber.org/protocol/pubsub#node-meta-info").
35
-define(NS_PUBSUB_NMI,   "http://jabber.org/protocol/pubsub#node-meta-info").
36
-define(NS_COMMANDS,     "http://jabber.org/protocol/commands").
36
-define(NS_COMMANDS,     "http://jabber.org/protocol/commands").
37
-define(NS_BYTESTREAMS,  "http://jabber.org/protocol/bytestreams").
37
38
38
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
39
-define(NS_EJABBERD_CONFIG, "ejabberd:config").
39
40
(-)ejabberd-1.1.1/src/mod_proxy65.erl (+189 lines)
Line 0 Link Here
1
%%%----------------------------------------------------------------------
2
%%% File    : mod_proxy65.erl
3
%%% Author  : Magnus Henoch <henoch@dtek.chalmers.se>
4
%%% Purpose : Handle Jabber communications for JEP-0065 proxy
5
%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
6
%%% Id      : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $
7
%%%----------------------------------------------------------------------
8
9
-module(mod_proxy65).
10
-author('henoch@dtek.chalmers.se').
11
-vsn('$Revision$ ').
12
13
-behaviour(gen_mod).
14
15
-export([start/2,
16
	 init/1,
17
	 stop/1]).
18
19
-include("ejabberd.hrl").
20
-include("jlib.hrl").
21
22
-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}).
23
24
-record(proxy65_options, {host, access, streamhosts}).
25
26
-define(PROCNAME, ejabberd_mod_proxy65).
27
28
start(Host, Opts) ->
29
    mnesia:create_table(proxy65_connection,
30
			[{ram_copies, [node()]},
31
			 {attributes, record_info(fields, proxy65_connection)}]),
32
    MyHost = gen_mod:get_opt(host, Opts, "proxy." ++ Host),
33
    Access = gen_mod:get_opt(access, Opts, all),
34
    Streamhosts = gen_mod:get_opt(streamhosts, Opts, [{Host, 7777}]),
35
    
36
    register(gen_mod:get_module_proc(Host, ?PROCNAME),
37
	     spawn(?MODULE, init, [#proxy65_options{host = MyHost, access = Access,
38
						    streamhosts = Streamhosts}])).
39
40
41
stop(Host) ->
42
    Proc = gen_mod:get_module_proc(Host, ?PROCNAME),
43
    Proc ! stop,
44
    {wait, Proc}.
45
46
init(#proxy65_options{host = Host} = Opts) ->
47
    ejabberd_router:register_route(Host),
48
    loop(Opts).
49
50
loop(#proxy65_options{host = Host} = Opts) ->
51
    receive
52
	{route, From, To, Packet} ->
53
	    case catch do_route(Opts, From, To, Packet) of
54
		{'EXIT', Reason} ->
55
		    ?ERROR_MSG("~p", [Reason]);
56
		_ ->
57
		    ok
58
	    end,
59
	    loop(Opts);
60
	stop ->
61
	    ejabberd_router:unregister_route(Host),
62
	    ok;
63
	_ ->
64
	    loop(Opts)
65
    end.
66
67
do_route(#proxy65_options{host = Host, access = Access} = Opts,
68
	 From, To, Packet) ->
69
    case acl:match_rule(Host, Access, From) of
70
	allow ->
71
	    do_route1(Opts, From, To, Packet);
72
	_ ->
73
	    {xmlelement, _Name, Attrs, _Els} = Packet,
74
	    Lang = xml:get_attr_s("xml:lang", Attrs),
75
	    ErrText = "Access denied by service policy",
76
	    Err = jlib:make_error_reply(Packet,
77
					?ERRT_FORBIDDEN(Lang, ErrText)),
78
	    ejabberd_router:route(To, From, Err)
79
    end.
80
81
do_route1(#proxy65_options{host = Host, streamhosts = Streamhosts}, From, To, Packet) ->
82
    {xmlelement, Name, _Attrs, _Els} = Packet,
83
    case Name of
84
	"iq" ->
85
	    case jlib:iq_query_info(Packet) of
86
		#iq{type = get, xmlns = ?NS_DISCO_INFO = XMLNS,
87
		    lang = Lang, sub_el = SubEl} = IQ ->
88
		    Node = xml:get_tag_attr_s("node", SubEl),
89
		    if Node == [] ->
90
			    Res = IQ#iq{type = result,
91
					sub_el = [{xmlelement, "query",
92
						   [{"xmlns", XMLNS}],
93
						   [{xmlelement, "identity",
94
						     [{"category", "proxy"},
95
						      {"type", "bytestreams"},
96
						      {"name", translate:translate(Lang, "SOCKS5 bytestreams proxy")}],
97
						     []},
98
						    {xmlelement, "feature",
99
						     [{"var", ?NS_BYTESTREAMS}], []}]}]};
100
		       true ->
101
			    Res = jlib:make_error_reply(Packet, ?ERR_ITEM_NOT_FOUND)
102
		    end;
103
		#iq{type = get, xmlns = ?NS_DISCO_ITEMS = XMLNS} = IQ ->
104
		    Res = IQ#iq{type = result,
105
				sub_el = [{xmlelement, "query",
106
					   [{"xmlns", XMLNS}], []}]};
107
		#iq{type = get, xmlns = ?NS_VERSION} = IQ ->
108
		    OSType = case os:type() of
109
				 {Osfamily, Osname} ->
110
				     atom_to_list(Osfamily) ++ "/" ++
111
					 atom_to_list(Osname);
112
				 Osfamily ->
113
				     atom_to_list(Osfamily)
114
			     end,
115
		    OSVersion = case os:version() of
116
				    {Major, Minor, Release} ->
117
					lists:flatten(
118
					  io_lib:format("~w.~w.~w",
119
							[Major, Minor, Release]));
120
				    VersionString ->
121
					VersionString
122
				end,
123
		    OS = OSType ++ " " ++ OSVersion,
124
		    Res = IQ#iq{type = result,
125
				sub_el = [{xmlelement, "query",
126
					   [{"xmlns", ?NS_VERSION}],
127
					   [{xmlelement, "name", [],
128
					     [{xmlcdata, "ejabberd mod_proxy65 (unofficial)"}]},
129
					    {xmlelement, "version", [],
130
					     [{xmlcdata, "0.1"}]},
131
					    {xmlelement, "os", [],
132
					     [{xmlcdata, OS}]}
133
					   ]}]};
134
		#iq{type = get, xmlns = ?NS_BYTESTREAMS = XMLNS} = IQ ->
135
		    Res = IQ#iq{type = result,
136
				sub_el = [{xmlelement, "query",
137
					   [{"xmlns", XMLNS}],
138
					   return_streamhosts(Host, Streamhosts)}]};
139
		#iq{type = set, xmlns = ?NS_BYTESTREAMS} = IQ ->
140
		    Res = activate(Packet, From, To, IQ);
141
		_ ->
142
		    Res = jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED)
143
	    end,
144
	    case Res of
145
		#iq{} ->
146
		    ejabberd_router:route(To, From, jlib:iq_to_xml(Res));
147
		_ ->
148
		    ejabberd_router:route(To, From, Res)
149
	    end;
150
	_ ->
151
	    ejabberd_router:route(To, From, jlib:make_error_reply(Packet, ?ERR_FEATURE_NOT_IMPLEMENTED))
152
    end.
153
154
return_streamhosts(_JID, []) ->
155
    [];
156
return_streamhosts(JID, [{Host, Port} | Streamhosts]) ->
157
    %% This is not tail-recursive, but it doesn't matter.
158
    [{xmlelement, "streamhost",
159
      [{"jid", JID},
160
       {"host", Host},
161
       {"port", integer_to_list(Port)}],
162
      []} | return_streamhosts(JID, Streamhosts)].
163
164
activate(Packet, From, _To, #iq{sub_el = SubEl} = IQ) ->
165
    case SubEl of
166
	{xmlelement, "query", Attrs, _SubEls} ->
167
	    Sid = xml:get_attr_s("sid", Attrs),
168
	    ActivateTag = xml:get_subtag(SubEl, "activate"),
169
	    if ActivateTag /= false ->
170
		    TargetJID = jlib:string_to_jid(xml:get_tag_cdata(ActivateTag));
171
	       true ->
172
		    TargetJID = false
173
	    end,
174
	    
175
	    if Sid /= [], TargetJID /= false, TargetJID /= error ->
176
		    case proxy65_listener:activate(From, TargetJID, Sid) of
177
			ok ->
178
			    ?INFO_MSG("Activated connection between ~s and ~s",
179
				      [jlib:jid_to_string(From), TargetJID]),
180
			    IQ#iq{type = result, sub_el = []};
181
			_ ->
182
			    jlib:make_error_reply(Packet, ?ERR_INTERNAL_SERVER_ERROR)
183
		    end;
184
	       true ->
185
		    jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST)
186
	    end;
187
	_ ->
188
	    jlib:make_error_reply(Packet, ?ERR_BAD_REQUEST)
189
    end.
(-)ejabberd-1.1.1/src/proxy65_listener.erl (+192 lines)
Line 0 Link Here
1
%%%----------------------------------------------------------------------
2
%%% File    : proxy65_listener.erl
3
%%% Author  : Magnus Henoch <henoch@dtek.chalmers.se>
4
%%% Purpose : Handle SOCKS5 connections for JEP-0065 proxy
5
%%% Created : 27 Dec 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
6
%%% Id      : $Id: ejabberd_c2s.erl 440 2005-11-22 18:00:56Z alexey $
7
%%%----------------------------------------------------------------------
8
9
-module(proxy65_listener).
10
-author('henoch@dtek.chalmers.se').
11
-vsn('$Revision$ ').
12
13
-export([start/2, handle_connection/2, activate/3]).
14
15
-include("ejabberd.hrl").
16
-include("jlib.hrl").
17
18
-record(proxy65_connection, {cookie, firstpid = none, secondpid = none}).
19
20
start({SockMod, Socket}, Opts) ->
21
    {ok, proc_lib:spawn(?MODULE, handle_connection, [{SockMod, Socket}, Opts])}.
22
23
read_bytes(_SockOpts, 0, Data) ->
24
    lists:flatten(Data);
25
read_bytes({SockMod, Socket} = SockOpts, N, Data) ->
26
    Timeout = 1000,
27
    case SockMod:recv(Socket, N, Timeout) of
28
	{error, closed} ->
29
	    %% On closed connection, return everything we have,
30
	    %% but not if we have nothing.
31
	    if Data == [] ->
32
		    erlang:error(closed);
33
	       true ->
34
		    lists:flatten(Data)
35
	    end;
36
	{ok, MoreData} ->
37
	    ?DEBUG("read ~p", [MoreData]),
38
	    DataList = binary_to_list(MoreData),
39
	    read_bytes(SockOpts, N - length(DataList), [Data, DataList])
40
    end.
41
42
handle_connection({SockMod, Socket}, Opts) ->
43
    ?DEBUG("in handle_connection", []),
44
    case catch handle_auth({SockMod, Socket}, Opts) of
45
	{'EXIT', Reason} ->
46
	    ?ERROR_MSG("~p abnormal termination:~n\t~p~n",
47
		       [?MODULE, Reason]),
48
	    SockMod:close(Socket);
49
	_ ->
50
	    ok
51
    end.
52
53
handle_auth({SockMod, Socket} = SockOpts, Opts) ->
54
    ?DEBUG("in handle_auth", []),
55
    %% SOCKS protocol stuff...
56
    [5, NAuthMethods] = read_bytes(SockOpts, 2, []),
57
    AuthMethods = read_bytes(SockOpts, NAuthMethods, []),
58
    SupportsNoAuth = lists:member(0, AuthMethods),
59
60
    %% Must support no authentication, otherwise crash
61
    true = SupportsNoAuth,
62
63
    SockMod:send(Socket, [5, 0]),
64
65
    %% And done.
66
    handle_connect(SockOpts, Opts).
67
68
handle_connect({SockMod, Socket} = SockOpts, Opts) ->
69
    ?DEBUG("in handle_connect", []),
70
    %% Expect a CONNECT command and nothing else
71
    [5, 1, _, 3, AddressLength] = read_bytes(SockOpts, 5, []),
72
    Cookie = read_bytes(SockOpts, AddressLength, []),
73
    [0, 0] = read_bytes(SockOpts, 2, []),
74
75
    %% Make sure no more than two connections claim the same cookie.
76
    F = fun() ->
77
		case mnesia:read({proxy65_connection, Cookie}) of
78
		    [] ->
79
			mnesia:write(#proxy65_connection{cookie = Cookie,
80
							 firstpid = self()}),
81
			ok;
82
		    [#proxy65_connection{secondpid = none} = C] ->
83
			mnesia:write(C#proxy65_connection{secondpid = self()}),
84
			ok
85
		end
86
	end,
87
88
    case mnesia:transaction(F) of
89
	{atomic, ok} ->
90
	    SockMod:send(Socket, [5, 0, 0, 3, AddressLength, Cookie, 0, 0]),
91
	    wait_for_activation(SockOpts, Opts);
92
	Error ->
93
	    %% conflict.  send "general SOCKS server failure".
94
	    SockMod:send(Socket, [5, 1, 0, 3, AddressLength, Cookie, 0, 0]),
95
	    erlang:error({badconnect, Error})
96
    end.
97
98
wait_for_activation(SockOpts, Opts) ->
99
    ?DEBUG("in wait_for_activation", []),
100
    receive
101
	{get_socket, ReplyTo} ->
102
	    ReplyTo ! SockOpts,
103
	    wait_for_activation(SockOpts, Opts);
104
	{activate, TargetSocket, Initiator, Target} ->
105
	    ?DEBUG("activated", []),
106
107
	    %% We have no way of knowing which connection belongs to
108
	    %% which participant, so give both the maximum traffic
109
	    %% allowed to either.
110
	    Shapers = case lists:keysearch(shaper, 1, Opts) of
111
			  {value, {_, S}} -> S;
112
			  _ -> none
113
		      end,
114
	    ?DEBUG("we have shapers: ~p", [Shapers]),
115
	    Shaper1 = acl:match_rule(global, Shapers, jlib:string_to_jid(Initiator)),
116
	    Shaper2 = acl:match_rule(global, Shapers, jlib:string_to_jid(Target)),
117
	    if Shaper1 == none; Shaper2 == none ->
118
		    MaxShaper = none;
119
	       true ->
120
		    ShaperValue1 = ejabberd_config:get_global_option({shaper, Shaper1}),
121
		    ShaperValue2 = ejabberd_config:get_global_option({shaper, Shaper2}),
122
123
		    if ShaperValue1 > ShaperValue2 ->
124
			    MaxShaper = Shaper1;
125
		       true ->
126
			    MaxShaper = Shaper2
127
		    end,
128
		    ?DEBUG("shapers have values ~p and ~p~nusing ~p", [ShaperValue1, ShaperValue2, MaxShaper]),
129
		    ok
130
	    end,
131
132
	    transfer_data(SockOpts, TargetSocket, shaper:new(MaxShaper))
133
    end.
134
135
transfer_data({SockMod, Socket} = SockOpts, {TargetSockMod, TargetSocket} = TargetSockOpts,
136
	      Shaper) ->
137
    case SockMod:recv(Socket, 0, infinity) of
138
	{ok, Data} ->
139
	    if Data /= <<>> ->
140
		    NewShaper = case Shaper of
141
				    none -> none;
142
				    _ ->
143
					shaper:update(Shaper, size(Data))
144
				end,
145
		    ok = TargetSockMod:send(TargetSocket, Data);
146
	       true ->
147
		    NewShaper = Shaper
148
	    end,
149
	    transfer_data(SockOpts, TargetSockOpts, NewShaper);
150
	{error, _} ->
151
	    TargetSockMod:shutdown(TargetSocket, read_write)
152
    end.
153
154
get_socket(PID) ->
155
    PID ! {get_socket, self()},
156
    receive
157
	{_SockMod, _Socket} = SockOpts ->
158
	    SockOpts
159
    end.
160
161
%% If any argument is a jid record, convert it to normalized string form...
162
activate(#jid{} = Initiator, Target, SessionID) ->
163
    NormalizedInitiator = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Initiator))),
164
    activate(NormalizedInitiator, Target, SessionID);
165
activate(Initiator, #jid{} = Target, SessionID) ->
166
    NormalizedTarget = jlib:jid_to_string(jlib:make_jid(jlib:jid_tolower(Target))),
167
    activate(Initiator, NormalizedTarget, SessionID);
168
%% ...and get on with the activation.
169
activate(Initiator, Target, SessionID) ->
170
    Cookie = sha:sha(SessionID ++ Initiator ++ Target),
171
    F = fun() ->
172
		case mnesia:read({proxy65_connection, Cookie}) of
173
		    [#proxy65_connection{firstpid = FirstPID,
174
					 secondpid = SecondPID}]
175
		    when is_pid(FirstPID), is_pid(SecondPID) ->
176
			mnesia:delete({proxy65_connection, Cookie}),
177
			{FirstPID, SecondPID};
178
		    _ ->
179
			error
180
		end
181
	end,
182
    case mnesia:transaction(F) of
183
	{atomic, {FirstPID, SecondPID}} ->
184
	    FirstSocket = get_socket(FirstPID),
185
	    SecondSocket = get_socket(SecondPID),
186
	    FirstPID ! {activate, SecondSocket, Initiator, Target},
187
	    SecondPID ! {activate, FirstSocket, Initiator, Target},
188
	    ok;
189
	Error ->
190
	    ?ERROR_MSG("Proxy activation failed: ~p", [Error]),
191
	    error
192
    end.

Return to bug 137724