1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%%----------------------------------------------------------------------
23%% Purpose: The supervisor for tcpip-forwarding acceptor
24%%----------------------------------------------------------------------
25
26-module(ssh_tcpip_forward_acceptor).
27
28-export([supervised_start/6,
29         start_link/6]).
30
31-include("ssh.hrl").
32
33%%%----------------------------------------------------------------
34supervised_start(FwdSup, {ListenAddrStr, ListenPort}, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
35    case get_fwd_listen_opts(ListenAddrStr) of
36        {ok,Opts} ->
37            %% start listening on Addr:BoundPort
38            case gen_tcp:listen(ListenPort, [binary,
39                                             {reuseaddr,true},
40                                             {active,false} | Opts]) of
41                {ok,LSock} ->
42                    {ok,{_, TrueListenPort}} = inet:sockname(LSock),
43                    ssh_tcpip_forward_acceptor_sup:start_child(FwdSup,
44                                                               LSock,
45                                                               {ListenAddrStr,TrueListenPort},
46                                                               ConnectToAddr,
47                                                               ChanType,
48                                                               ChanCB,
49                                                               ConnPid),
50                    {ok, TrueListenPort};
51
52                {error,Error} ->
53                    {error,Error}
54            end;
55
56        {error,Error} ->
57            {error,Error}
58    end.
59
60
61%%%----------------------------------------------------------------
62start_link(LSock, {ListenAddrStr,ListenPort}, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
63    Pid = proc_lib:spawn_link(
64            fun() ->
65                    acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid)
66            end),
67    {ok, Pid}.
68
69%%%================================================================
70%%%
71%%% Internal
72%%%
73acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid) ->
74    case gen_tcp:accept(LSock) of
75        {ok, Sock} ->
76            {ok, {RemHost,RemPort}} = inet:peername(Sock),
77            RemHostBin = list_to_binary(encode_ip(RemHost)),
78            Data =
79                case ConnectToAddr of
80                    undefined ->
81                        <<?STRING(ListenAddrStr), ?UINT32(ListenPort),
82                          ?STRING(RemHostBin), ?UINT32(RemPort)>>;
83                    {ConnectToHost, ConnectToPort} ->
84                        <<?STRING(ConnectToHost), ?UINT32(ConnectToPort),
85                          ?STRING(RemHostBin), ?UINT32(RemPort)>>
86                end,
87            case ssh_connection:open_channel(ConnPid, ChanType, Data, infinity) of
88                {ok,ChId} ->
89                    gen_tcp:controlling_process(Sock, ConnPid),
90                    ConnPid ! {fwd_connect_received, Sock, ChId, ChanCB};
91                _ ->
92                    gen_tcp:close(Sock)
93            end,
94            acceptor_loop(LSock, ListenAddrStr, ListenPort, ConnectToAddr, ChanType, ChanCB, ConnPid);
95
96        {error,closed} ->
97            ok
98    end.
99
100%%%----------------------------------------------------------------
101get_fwd_listen_opts(<<"">>         ) -> {ok, []};
102get_fwd_listen_opts(<<"0.0.0.0">>  ) -> {ok, [inet]};
103get_fwd_listen_opts(<<"::">>       ) -> {ok, [inet6]};
104get_fwd_listen_opts(<<"localhost">>) -> {ok, [{ip,loopback}]};
105get_fwd_listen_opts(AddrStr) ->
106    case inet:getaddr(binary_to_list(AddrStr), inet) of
107        {ok, Addr} -> {ok, [{ip,Addr}]};
108        {error,Error} -> {error,Error}
109    end.
110
111%%%----------------------------------------------------------------
112encode_ip(Addr) when is_tuple(Addr) ->
113    case catch inet_parse:ntoa(Addr) of
114	{'EXIT',_} -> false;
115	A -> A
116    end.
117