Handle HAVE_ALL and HAVE_NONE. Cleanup the BITFIELD message.
[etorrent.git] / lib / etorrent-1.0 / src / etorrent_acceptor.erl
blob60e63b18932c87980992d037b7211c643ace8665
1 %%%-------------------------------------------------------------------
2 %%% File : acceptor.erl
3 %%% Author : Jesper Louis Andersen <jesper.louis.andersen@gmail.com>
4 %%% Description : Accept new connections from the network.
5 %%%
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").
14 %% API
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,
22 our_peer_id}).
24 %%====================================================================
25 %% API
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} |
41 %% ignore |
42 %% {stop, Reason}
43 %% Description: Initiates the server
44 %%--------------------------------------------------------------------
45 init([OurPeerId]) ->
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} |
53 %% {noreply, State} |
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) ->
60 Reply = ok,
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) ->
70 {noreply, 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),
81 {noreply, S, 0};
82 {error, closed} -> {noreply, S, 0};
83 {error, econnaborted} -> {noreply, S, 0};
84 {error, enotconn} -> {noreply, S, 0};
85 {error, E} -> {stop, E, S}
86 end.
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) ->
96 ok.
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) ->
103 {ok, State}.
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);
113 {error, _Reason} ->
114 gen_tcp:close(Socket),
116 end.
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);
122 {atomic, []} ->
123 gen_tcp:close(Socket),
125 end.
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,
133 ReservedBytes,
134 Socket,
135 PeerId),
137 {error, enotconn} ->
138 etorrent_t_peer_recv:stop(PeerProcessPid),
140 end;
141 already_enough_connections ->
143 connect_to_ourselves ->
144 gen_tcp:close(Socket),
146 bad_peer ->
147 error_logger:info_report([peer_id_is_bad, PeerId]),
148 gen_tcp:close(Socket),
150 end.
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
157 true ->
158 bad_peer;
159 false ->
160 start_new_incoming_peer(IP, Port, InfoHash, S)
161 end.
164 start_new_incoming_peer(IP, Port, InfoHash, S) ->
165 case etorrent_counters:obtain_peer_slot() of
166 full -> already_enough_connections;
167 ok ->
168 {atomic, [T]} = etorrent_tracking_map:select({infohash, InfoHash}),
169 try etorrent_t_sup:add_peer(
170 T#tracking_map.supervisor_pid,
171 S#state.our_peer_id,
172 InfoHash,
173 T#tracking_map.id,
174 {IP, Port})
175 catch
176 _ -> etorrent_counters:release_peer_slot()
178 end.