1 %%%-------------------------------------------------------------------
2 %%% File : acceptor.erl
3 %%% Author : Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
4 %%% Description : Accept new connections from the network.
6 %%% Created : 30 Jul 2007 by Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
7 %%%-------------------------------------------------------------------
8 -module(etorrent_acceptor
).
10 -behaviour(gen_server
).
12 -include("etorrent_mnesia_table.hrl").
15 -export([start_link
/1]).
17 %% gen_server callbacks
18 -export([init
/1, handle_call
/3, handle_cast
/2, handle_info
/2,
19 terminate
/2, code_change
/3]).
21 -record(state
, { listen_socket
= none
,
24 %%====================================================================
26 %%====================================================================
27 %%--------------------------------------------------------------------
28 %% Function: start_link(OurPeerId) -> {ok,Pid} | ignore | {error,Error}
29 %% Description: Starts the server.
30 %%--------------------------------------------------------------------
31 start_link(OurPeerId
) ->
32 gen_server:start_link(?MODULE
, [OurPeerId
], []).
34 %%====================================================================
35 %% gen_server callbacks
36 %%====================================================================
38 %%--------------------------------------------------------------------
39 %% Function: init(Args) -> {ok, State} |
40 %% {ok, State, Timeout} |
43 %% Description: Initiates the server
44 %%--------------------------------------------------------------------
46 {ok
, ListenSocket
} = etorrent_listener:get_socket(),
47 {ok
, #state
{ listen_socket
= ListenSocket
,
48 our_peer_id
= OurPeerId
}, 0}.
50 %%--------------------------------------------------------------------
51 %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
52 %% {reply, Reply, State, Timeout} |
54 %% {noreply, State, Timeout} |
55 %% {stop, Reason, Reply, State} |
56 %% {stop, Reason, State}
57 %% Description: Handling call messages
58 %%--------------------------------------------------------------------
59 handle_call(_Request
, _From
, State
) ->
61 {reply
, Reply
, State
}.
63 %%--------------------------------------------------------------------
64 %% Function: handle_cast(Msg, State) -> {noreply, State} |
65 %% {noreply, State, Timeout} |
66 %% {stop, Reason, State}
67 %% Description: Handling cast messages
68 %%--------------------------------------------------------------------
69 handle_cast(_Msg
, State
) ->
72 %%--------------------------------------------------------------------
73 %% Function: handle_info(Info, State) -> {noreply, State} |
74 %% {noreply, State, Timeout} |
75 %% {stop, Reason, State}
76 %% Description: Handling all non call/cast messages
77 %%--------------------------------------------------------------------
78 handle_info(timeout
, S
) ->
79 case gen_tcp:accept(S#state
.listen_socket
) of
80 {ok
, Socket
} -> handshake(Socket
, S
),
82 {error
, closed
} -> {noreply
, S
, 0};
83 {error
, econnaborted
} -> {noreply
, S
, 0};
84 {error
, enotconn
} -> {noreply
, S
, 0};
85 {error
, E
} -> {stop
, E
, S
}
88 %%--------------------------------------------------------------------
89 %% Function: terminate(Reason, State) -> void()
90 %% Description: This function is called by a gen_server when it is about to
91 %% terminate. It should be the opposite of Module:init/1 and do any necessary
92 %% cleaning up. When it returns, the gen_server terminates with Reason.
93 %% The return value is ignored.
94 %%--------------------------------------------------------------------
95 terminate(_Reason
, _State
) ->
98 %%--------------------------------------------------------------------
99 %% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
100 %% Description: Convert process state when code is changed
101 %%--------------------------------------------------------------------
102 code_change(_OldVsn
, State
, _Extra
) ->
105 %%--------------------------------------------------------------------
106 %%% Internal functions
107 %%--------------------------------------------------------------------
109 handshake(Socket
, S
) ->
110 case etorrent_peer_communication:receive_handshake(Socket
) of
111 {ok
, ReservedBytes
, InfoHash
, PeerId
} ->
112 lookup_infohash(Socket
, ReservedBytes
, InfoHash
, PeerId
, S
);
114 gen_tcp:close(Socket
),
118 lookup_infohash(Socket
, ReservedBytes
, InfoHash
, PeerId
, S
) ->
119 case etorrent_tracking_map:select({infohash
, InfoHash
}) of
120 {atomic
, [#tracking_map
{ _
= _
}]} ->
121 start_peer(Socket
, ReservedBytes
, PeerId
, InfoHash
, S
);
123 gen_tcp:close(Socket
),
127 start_peer(Socket
, ReservedBytes
, PeerId
, InfoHash
, S
) ->
128 {ok
, {Address
, Port
}} = inet:peername(Socket
),
129 case new_incoming_peer(Address
, Port
, InfoHash
, PeerId
, S
) of
130 {ok
, PeerProcessPid
} ->
131 case gen_tcp:controlling_process(Socket
, PeerProcessPid
) of
132 ok
-> etorrent_t_peer_recv:complete_handshake(PeerProcessPid
,
138 etorrent_t_peer_recv:stop(PeerProcessPid
),
141 already_enough_connections
->
143 connect_to_ourselves
->
144 gen_tcp:close(Socket
),
147 error_logger:info_report([peer_id_is_bad
, PeerId
]),
148 gen_tcp:close(Socket
),
152 new_incoming_peer(_IP
, _Port
, _InfoHash
, PeerId
, S
) when S#state
.our_peer_id
== PeerId
->
153 connect_to_ourselves
;
154 new_incoming_peer(IP
, Port
, InfoHash
, _PeerId
, S
) ->
155 {atomic
, [TM
]} = etorrent_tracking_map:select({infohash
, InfoHash
}),
156 case etorrent_peer_mgr:bad_peer(IP
, Port
, TM#tracking_map
.id
) of
160 start_new_incoming_peer(IP
, Port
, InfoHash
, S
)
164 start_new_incoming_peer(IP
, Port
, InfoHash
, S
) ->
165 case etorrent_counters:obtain_peer_slot() of
166 full
-> already_enough_connections
;
168 {atomic
, [T
]} = etorrent_tracking_map:select({infohash
, InfoHash
}),
169 try
etorrent_t_sup:add_peer(
170 T#tracking_map
.supervisor_pid
,
176 _
-> etorrent_counters:release_peer_slot()