1 %%%-------------------------------------------------------------------
2 %%% File : peer_communication.erl
3 %%% Author : Jesper Louis Andersen <jlouis@succubus>
4 %%% License : See COPYING
5 %%% Description : Various pieces of the peer protocol that takes a bit to
8 %%% Created : 26 Jan 2007 by Jesper Louis Andersen <jlouis@succubus>
9 %%%-------------------------------------------------------------------
10 -module(etorrent_peer_communication
).
13 -export([initiate_handshake
/3, receive_handshake
/1,
14 complete_handshake
/3]).
15 -export([send_message
/3, recv_message
/2,
16 construct_bitfield
/2, destruct_bitfield
/2]).
18 -define(DEFAULT_HANDSHAKE_TIMEOUT
, 120000).
19 -define(HANDSHAKE_SIZE
, 68).
20 -define(PROTOCOL_STRING
, "BitTorrent protocol").
23 -define(EXT_BASIS
, 0). % The protocol basis
24 -define(EXT_FAST
, 4). % The Fast Extension
28 -define(UNCHOKE
, 1:8).
29 -define(INTERESTED
, 2:8).
30 -define(NOT_INTERESTED
, 3:8).
32 -define(BITFIELD
, 5:8).
33 -define(REQUEST
, 6:8).
38 %% FAST EXTENSION Packet types
39 -define(SUGGEST
, 13:8).
40 -define(HAVE_ALL
, 14:8).
41 -define(HAVE_NONE
, 15:8).
42 -define(REJECT_REQUEST
, 16:8).
43 -define(ALLOWED_FAST
, 17:8).
45 %%====================================================================
47 %%====================================================================
48 %%--------------------------------------------------------------------
49 %% Function: recv_message(Message) -> keep_alive | choke | unchoke |
50 %% interested | not_interested | {have, integer()} | ...
51 %% Description: Receive a message from a peer and decode it
52 %%--------------------------------------------------------------------
53 recv_message(Rate
, Message
) ->
54 MSize
= size(Message
),
65 <<?NOT_INTERESTED
>> ->
67 <<?HAVE
, PieceNum:32/big
>> ->
69 <<?BITFIELD
, BitField
/binary>> ->
71 <<?REQUEST
, Index:32/big
, Begin:32/big
, Len:32/big
>> ->
72 {request
, Index
, Begin
, Len
};
73 <<?PIECE
, Index:32/big
, Begin:32/big
, Data
/binary>> ->
74 {piece
, Index
, Begin
, Data
};
75 <<?CANCEL
, Index:32/big
, Begin:32/big
, Len:32/big
>> ->
76 {cancel
, Index
, Begin
, Len
};
77 <<?PORT
, Port:16/big
>> ->
79 %% FAST EXTENSION MESSAGES
80 <<?SUGGEST
, Index:32/big
>> ->
86 <<?REJECT_REQUEST
, Index:32, Offset:32, Len:32>> ->
87 {reject_request
, Index
, Offset
, Len
};
88 <<?ALLOWED_FAST
, FastSet
/binary>> ->
89 {allowed_fast
, decode_allowed_fast(FastSet
)}
91 {Decoded
, etorrent_rate:update(Rate
, MSize
), MSize
}.
93 %%--------------------------------------------------------------------
94 %% Function: send_message(Socket, Message)
95 %% Description: Send a message on a socket
96 %%--------------------------------------------------------------------
97 send_message(Rate
, Socket
, Message
) ->
111 <<?HAVE
, PieceNum:32/big
>>;
112 {bitfield
, BitField
} ->
113 <<?BITFIELD
, BitField
/binary>>;
114 {request
, Index
, Begin
, Len
} ->
115 <<?REQUEST
, Index:32/big
, Begin:32/big
, Len:32/big
>>;
116 {piece
, Index
, Begin
, Data
} ->
118 Index:32/big
, Begin:32/big
, Data
/binary>>;
119 {cancel
, Index
, Begin
, Len
} ->
120 <<?CANCEL
, Index:32/big
, Begin:32/big
, Len:32/big
>>;
122 <<?PORT
, PortNum:16/big
>>;
125 <<?SUGGEST
, Index:32>>;
130 {reject_request
, Index
, Offset
, Len
} ->
131 <<?REJECT_REQUEST
, Index
, Offset
, Len
>>;
132 {allowed_fast
, FastSet
} ->
133 BinFastSet
= encode_fastset(FastSet
),
134 <<?ALLOWED_FAST
, BinFastSet
>>
137 Res
= gen_tcp:send(Socket
, <<Sz:32/big
, Datagram
/binary>>),
138 {Res
, etorrent_rate:update(Rate
, Sz
), Sz
}.
140 %%--------------------------------------------------------------------
141 %% Function: receive_handshake(Socket) -> {ok, protocol_version,
142 %% remote_peer_id()} |
143 %% {ok, proto_version(),
145 %% remote_peer_id()} |
147 %% Description: Receive a handshake from another peer. In the receive,
148 %% we don't send the info_hash, but expect the initiator to send what
149 %% he thinks is the correct hash. For the return value, see the
150 %% function receive_header()
151 %%--------------------------------------------------------------------
152 receive_handshake(Socket
) ->
153 Header
= build_peer_protocol_header(),
154 case gen_tcp:send(Socket
, Header
) of
156 receive_header(Socket
, await
);
161 %%--------------------------------------------------------------------
162 %% Function: initiate_handshake(socket(), peer_id(), info_hash()) ->
163 %% {ok, protocol_version()} |
165 %% Description: Handshake with a peer where we have initiated with him.
166 %% This call is used if we are the initiator of a torrent handshake as
167 %% we then know the peer_id completely.
168 %%--------------------------------------------------------------------
169 initiate_handshake(Socket
, LocalPeerId
, InfoHash
) ->
170 % Since we are the initiator, send out this handshake
171 Header
= build_peer_protocol_header(),
173 ok
= gen_tcp:send(Socket
, Header
),
174 ok
= gen_tcp:send(Socket
, InfoHash
),
175 ok
= gen_tcp:send(Socket
, LocalPeerId
),
176 receive_header(Socket
, InfoHash
)
178 error:_
-> {error
, stop
}
181 %%--------------------------------------------------------------------
182 %% Function: complete_handshake/3
183 %% Args: Socket ::= socket()
184 %% InfoHash ::= binary()
185 %% LocalPeerId ::= binary()
186 %% Description: Complete a handshake.
187 %%--------------------------------------------------------------------
188 complete_handshake(Socket
, InfoHash
, LocalPeerId
) ->
189 Header
= build_peer_protocol_header(),
191 ok
= gen_tcp:send(Socket
, Header
),
192 ok
= gen_tcp:send(Socket
, InfoHash
),
193 ok
= gen_tcp:send(Socket
, LocalPeerId
),
196 error:_
-> {error
, stop
}
200 %%====================================================================
201 %% Internal functions
202 %%====================================================================
204 %%--------------------------------------------------------------------
205 %% Function: build_peer_protocol_header() -> binary()
206 %% Description: Returns the Peer Protocol header.
207 %%--------------------------------------------------------------------
208 build_peer_protocol_header() ->
209 PSSize
= length(?PROTOCOL_STRING
),
210 ReservedBytes
= protocol_capabilities(),
211 <<PSSize:8, ?PROTOCOL_STRING
, ReservedBytes
/binary>>.
213 protocol_capabilities() ->
214 ProtoSpec
= lists:sum([%?EXT_FAST,
216 <<ProtoSpec:64/big
>>.
218 %%--------------------------------------------------------------------
219 %% Function: receive_header(socket()) -> {ok, proto_version(),
220 %% remote_peer_id()} |
221 %% {ok, proto_version(),
223 %% remote_peer_id()} |
225 %% Description: Receive the full header from a peer. The function
226 %% returns either with an error or successfully with a
227 %% protocol_version string, the infohash the remote sent us and his
229 %% --------------------------------------------------------------------
230 receive_header(Socket
, InfoHash
) ->
231 %% Last thing we do on the socket, catch an error here.
232 case gen_tcp:recv(Socket
, ?HANDSHAKE_SIZE
, ?DEFAULT_HANDSHAKE_TIMEOUT
) of
233 %% Fail if the header length is wrong
234 {ok
, <<PSL:8/integer, ?PROTOCOL_STRING
, _:8/binary,
235 _IH:20/binary, _PI:20/binary>>}
236 when PSL
/= length(?PROTOCOL_STRING
) ->
237 {error
, packet_size_mismatch
};
238 %% If the infohash is await, return the infohash along.
239 {ok
, <<_PSL:8/integer, ?PROTOCOL_STRING
, ReservedBytes:8/binary,
240 IH:20/binary, PI:20/binary>>}
241 when InfoHash
=:= await
->
242 {ok
, ReservedBytes
, IH
, PI
};
243 %% Infohash mismatches. Error it.
244 {ok
, <<_PSL:8/integer, ?PROTOCOL_STRING
, _ReservedBytes:8/binary,
245 IH:20/binary, _PI:20/binary>>}
246 when IH
/= InfoHash
->
247 {error
, infohash_mismatch
};
249 {ok
, <<_PSL:8/integer, ?PROTOCOL_STRING
, ReservedBytes:8/binary,
250 _IH:20/binary, PI:20/binary>>} ->
251 {ok
, ReservedBytes
, PI
};
252 %% This is not even a header!
253 {ok
, X
} when is_binary(X
) ->
254 {error
, {bad_header
, X
}};
255 %% Propagate errors upwards, most importantly, {error, closed}
260 %%--------------------------------------------------------------------
261 %% Function: construct_bitfield
262 %% Description: Construct a BitField for sending to the peer
263 %%--------------------------------------------------------------------
264 construct_bitfield(Size
, PieceSet
) ->
265 PadBits
= 8 - (Size rem
8),
267 case gb_sets:is_element(N
, PieceSet
) of
272 Bits
= lists:append([F(N
) || N
<- lists:seq(0, Size
-1)],
273 [0 || _N
<- lists:seq(1,PadBits
)]),
274 0 = length(Bits
) rem
8,
275 list_to_binary(build_bytes(Bits
)).
277 build_bytes(BitField
) ->
278 build_bytes(BitField
, []).
280 build_bytes([], Acc
) ->
282 build_bytes(L
, Acc
) ->
283 {Byte
, Rest
} = lists:split(8, L
),
284 build_bytes(Rest
, [bytify(Byte
) | Acc
]).
286 bytify([B1
, B2
, B3
, B4
, B5
, B6
, B7
, B8
]) ->
287 <<B1:1/integer, B2:1/integer, B3:1/integer, B4:1/integer,
288 B5:1/integer, B6:1/integer, B7:1/integer, B8:1/integer>>.
290 destruct_bitfield(Size
, BinaryLump
) ->
291 ByteList
= binary_to_list(BinaryLump
),
292 Numbers
= decode_bytes(0, ByteList
),
293 PieceSet
= gb_sets:from_list(lists:flatten(Numbers
)),
294 case max_element(PieceSet
) < Size
of
298 {error
, bitfield_had_wrong_padding
}
302 gb_sets:fold(fun(E
, Max
) ->
311 decode_byte(B
, Add
) ->
312 <<B1:1/integer, B2:1/integer, B3:1/integer, B4:1/integer,
313 B5:1/integer, B6:1/integer, B7:1/integer, B8:1/integer>> = <<B
>>,
314 Bytes
= [{B1
, 0}, {B2
, 1}, {B3
, 2}, {B4
, 3},
315 {B5
, 4}, {B6
, 5}, {B7
, 6}, {B8
, 7}],
316 [N
+Add
|| {K
, N
} <- Bytes
, K
=:= 1].
318 decode_bytes(_SoFar
, []) -> [];
319 decode_bytes(SoFar
, [B
| Rest
]) ->
320 [decode_byte(B
, SoFar
) | decode_bytes(SoFar
+ 8, Rest
)].
323 decode_allowed_fast(<<>>) -> [];
324 decode_allowed_fast(<<Index:32, Rest
/binary>>) ->
325 [Index
| decode_allowed_fast(Rest
)].
327 encode_fastset([]) -> <<>>;
328 encode_fastset([Idx
| Rest
]) ->
329 R
= encode_fastset(Rest
),
330 <<R
/binary, Idx:32>>.