1 | /* |
---|
2 | Copyright (c) 2010 by Johannes Lieder |
---|
3 | |
---|
4 | Permission is hereby granted, free of charge, to any person obtaining a copy |
---|
5 | of this software and associated documentation files (the "Software"), to deal |
---|
6 | in the Software without restriction, including without limitation the rights |
---|
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
---|
8 | copies of the Software, and to permit persons to whom the Software is |
---|
9 | furnished to do so, subject to the following conditions: |
---|
10 | |
---|
11 | The above copyright notice and this permission notice shall be included in |
---|
12 | all copies or substantial portions of the Software. |
---|
13 | |
---|
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
---|
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
---|
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
---|
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
---|
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
---|
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
---|
20 | THE SOFTWARE. |
---|
21 | */ |
---|
22 | |
---|
23 | /* ansi */ |
---|
24 | #include <errno.h> |
---|
25 | #include <stdio.h> |
---|
26 | #include <string.h> /* strlen (), strncpy (), strstr (), memset () */ |
---|
27 | |
---|
28 | /* posix */ |
---|
29 | #include <signal.h> /* sig_atomic_t */ |
---|
30 | #include <sys/time.h> |
---|
31 | #include <unistd.h> /* close () */ |
---|
32 | #include <ctype.h> /* toupper () */ |
---|
33 | #ifdef _WIN32 |
---|
34 | #include <w32api.h> |
---|
35 | #define WINDOWS WindowsXP /* freeaddrinfo (),getaddrinfo (),getnameinfo () */ |
---|
36 | #include <inttypes.h> |
---|
37 | #include <ws2tcpip.h> |
---|
38 | typedef uint16_t in_port_t; /* all missing */ |
---|
39 | #else |
---|
40 | #include <sys/types.h> |
---|
41 | #include <sys/socket.h> /* socket (), bind () */ |
---|
42 | #include <netinet/in.h> /* sockaddr_in */ |
---|
43 | #endif |
---|
44 | |
---|
45 | /* third party */ |
---|
46 | #include <event2/event.h> |
---|
47 | #include <event2/util.h> |
---|
48 | |
---|
49 | /* libT */ |
---|
50 | #include "transmission.h" |
---|
51 | #include "log.h" |
---|
52 | #include "net.h" |
---|
53 | #include "peer-mgr.h" /* tr_peerMgrAddPex () */ |
---|
54 | #include "session.h" |
---|
55 | #include "torrent.h" /* tr_torrentFindFromHash () */ |
---|
56 | #include "tr-lpd.h" |
---|
57 | #include "utils.h" |
---|
58 | #include "version.h" |
---|
59 | |
---|
60 | /** |
---|
61 | * @brief Local Peer Discovery |
---|
62 | * @file tr-lpd.c |
---|
63 | * |
---|
64 | * This module implements the Local Peer Discovery (LPD) protocol as supported by the |
---|
65 | * uTorrent client application. A typical LPD datagram is 119 bytes long. |
---|
66 | * |
---|
67 | * $Id: tr-lpd.c 14321 2014-07-08 00:15:12Z jordan $ |
---|
68 | */ |
---|
69 | |
---|
70 | static void event_callback (evutil_socket_t, short, void*); |
---|
71 | |
---|
72 | enum { |
---|
73 | UPKEEP_INTERVAL_SECS = 5 |
---|
74 | }; |
---|
75 | static struct event * upkeep_timer = NULL; |
---|
76 | |
---|
77 | static int lpd_socket; /**<separate multicast receive socket */ |
---|
78 | static int lpd_socket2; /**<and multicast send socket */ |
---|
79 | static struct event * lpd_event = NULL; |
---|
80 | static tr_port lpd_port; |
---|
81 | |
---|
82 | static tr_torrent* lpd_torStaticType UNUSED; /* just a helper for static type analysis */ |
---|
83 | static tr_session* session; |
---|
84 | |
---|
85 | enum { lpd_maxDatagramLength = 200 }; /**<the size an LPD datagram must not exceed */ |
---|
86 | const char lpd_mcastGroup[] = "239.192.152.143"; /**<LPD multicast group */ |
---|
87 | const int lpd_mcastPort = 6771; /**<LPD source and destination UPD port */ |
---|
88 | static struct sockaddr_in lpd_mcastAddr; /**<initialized from the above constants in tr_lpdInit */ |
---|
89 | |
---|
90 | /** |
---|
91 | * @brief Protocol-related information carried by a Local Peer Discovery packet */ |
---|
92 | struct lpd_protocolVersion |
---|
93 | { |
---|
94 | int major, minor; |
---|
95 | }; |
---|
96 | |
---|
97 | enum lpd_enumTimeToLive { |
---|
98 | lpd_ttlSameSubnet = 1, |
---|
99 | lpd_ttlSameSite = 32, |
---|
100 | lpd_ttlSameRegion = 64, |
---|
101 | lpd_ttlSameContinent = 128, |
---|
102 | lpd_ttlUnrestricted = 255 |
---|
103 | }; |
---|
104 | |
---|
105 | enum { |
---|
106 | lpd_announceInterval = 4 * 60, /**<4 min announce interval per torrent */ |
---|
107 | lpd_announceScope = lpd_ttlSameSubnet /**<the maximum scope for LPD datagrams */ |
---|
108 | }; |
---|
109 | |
---|
110 | |
---|
111 | /** |
---|
112 | * @defgroup DoS Message Flood Protection |
---|
113 | * @{ |
---|
114 | * We want to have a means to protect the libtransmission backend against message |
---|
115 | * flooding: the strategy is to cap event processing once more than ten messages |
---|
116 | * per second (that is, taking the average over one of our housekeeping intervals) |
---|
117 | * got into our processing handler. |
---|
118 | * If we'd really hit the limit and start discarding events, we either joined an |
---|
119 | * extremely crowded multicast group or a malevolent host is sending bogus data to |
---|
120 | * our socket. In this situation, we rather miss some announcements than blocking |
---|
121 | * the actual task. |
---|
122 | * @} |
---|
123 | */ |
---|
124 | |
---|
125 | /** |
---|
126 | * @ingroup DoS |
---|
127 | * @brief allow at most ten messages per second (interval average) |
---|
128 | * @note this constraint is only enforced once per housekeeping interval */ |
---|
129 | enum { lpd_announceCapFactor = 10 }; |
---|
130 | |
---|
131 | /** |
---|
132 | * @ingroup DoS |
---|
133 | * @brief number of unsolicited messages during the last HK interval |
---|
134 | * @remark counts downwards */ |
---|
135 | static int lpd_unsolicitedMsgCounter; |
---|
136 | |
---|
137 | /** |
---|
138 | * @def CRLF |
---|
139 | * @brief a line-feed, as understood by the LPD protocol */ |
---|
140 | #define CRLF "\r\n" |
---|
141 | |
---|
142 | |
---|
143 | /** |
---|
144 | * @defgroup HttpReqProc HTTP-style request handling |
---|
145 | * @{ |
---|
146 | */ |
---|
147 | |
---|
148 | /** |
---|
149 | * @brief Checks for BT-SEARCH method and separates the parameter section |
---|
150 | * @param[in] s The request string |
---|
151 | * @param[out] ver If non-NULL, gets filled with protocol info from the request |
---|
152 | * @return Returns a relative pointer to the beginning of the parameter section; |
---|
153 | * if result is NULL, s was invalid and no information will be returned |
---|
154 | * @remark Note that the returned pointer is only usable as long as the given |
---|
155 | * pointer s is valid; that is, return storage is temporary. |
---|
156 | * |
---|
157 | * Determines whether the given string checks out to be a valid BT-SEARCH message. |
---|
158 | * If so, the return value points to the beginning of the parameter section (note: |
---|
159 | * in this case the function returns a character sequence beginning with CRLF). |
---|
160 | * If parameter is not NULL, the declared protocol version is returned as part of |
---|
161 | * the lpd_protocolVersion structure. |
---|
162 | */ |
---|
163 | static const char* lpd_extractHeader (const char* s, struct lpd_protocolVersion* const ver) |
---|
164 | { |
---|
165 | int major = -1, minor = -1; |
---|
166 | size_t len; |
---|
167 | |
---|
168 | assert (s != NULL); |
---|
169 | len = strlen (s); |
---|
170 | |
---|
171 | /* something might be rotten with this chunk of data */ |
---|
172 | if (len == 0 || len > lpd_maxDatagramLength) |
---|
173 | return NULL; |
---|
174 | |
---|
175 | /* now we can attempt to look up the BT-SEARCH header */ |
---|
176 | if (sscanf (s, "BT-SEARCH * HTTP/%d.%d" CRLF, &major, &minor) != 2) |
---|
177 | return NULL; |
---|
178 | |
---|
179 | if (major < 0 || minor < 0) |
---|
180 | return NULL; |
---|
181 | |
---|
182 | { |
---|
183 | /* a pair of blank lines at the end of the string, no place else */ |
---|
184 | const char* const two_blank = CRLF CRLF CRLF; |
---|
185 | const char* const end = strstr (s, two_blank); |
---|
186 | |
---|
187 | if (end == NULL || strlen (end) > strlen (two_blank)) |
---|
188 | return NULL; |
---|
189 | } |
---|
190 | |
---|
191 | if (ver != NULL) |
---|
192 | { |
---|
193 | ver->major = major; |
---|
194 | ver->minor = minor; |
---|
195 | } |
---|
196 | |
---|
197 | /* separate the header, begins with CRLF */ |
---|
198 | return strstr (s, CRLF); |
---|
199 | } |
---|
200 | |
---|
201 | /** |
---|
202 | * @brief Return the value of a named parameter |
---|
203 | * |
---|
204 | * @param[in] str Input string of "\r\nName: Value" pairs without HTTP-style method part |
---|
205 | * @param[in] name Name of parameter to extract |
---|
206 | * @param[in] n Maximum available storage for value to return |
---|
207 | * @param[out] val Output parameter for the actual value |
---|
208 | * @return Returns 1 if value could be copied successfully |
---|
209 | * |
---|
210 | * Extracts the associated value of a named parameter from a HTTP-style header by |
---|
211 | * performing the following steps: |
---|
212 | * - assemble search string "\r\nName: " and locate position |
---|
213 | * - copy back value from end to next "\r\n" |
---|
214 | */ |
---|
215 | static int lpd_extractParam (const char* const str, const char* const name, int n, char* const val) |
---|
216 | { |
---|
217 | /* configure maximum length of search string here */ |
---|
218 | enum { maxLength = 30 }; |
---|
219 | char sstr[maxLength] = { 0 }; |
---|
220 | const char* pos; |
---|
221 | |
---|
222 | assert (str != NULL && name != NULL); |
---|
223 | assert (val != NULL); |
---|
224 | |
---|
225 | if (strlen (name) > maxLength - strlen (CRLF ": ")) |
---|
226 | return 0; |
---|
227 | |
---|
228 | /* compose the string token to search for */ |
---|
229 | tr_snprintf (sstr, maxLength, CRLF "%s: ", name); |
---|
230 | |
---|
231 | pos = strstr (str, sstr); |
---|
232 | if (pos == NULL) |
---|
233 | return 0; /* search was not successful */ |
---|
234 | |
---|
235 | { |
---|
236 | const char* const beg = pos + strlen (sstr); |
---|
237 | const char* const new_line = strstr (beg, CRLF); |
---|
238 | |
---|
239 | /* the value is delimited by the next CRLF */ |
---|
240 | int len = new_line - beg; |
---|
241 | |
---|
242 | /* if value string hits the length limit n, |
---|
243 | * leave space for a trailing '\0' character */ |
---|
244 | if (len < n--) |
---|
245 | n = len; |
---|
246 | |
---|
247 | strncpy (val, beg, n); |
---|
248 | val[n] = 0; |
---|
249 | } |
---|
250 | |
---|
251 | /* we successfully returned the value string */ |
---|
252 | return 1; |
---|
253 | } |
---|
254 | |
---|
255 | /** |
---|
256 | * @} */ |
---|
257 | |
---|
258 | static void on_upkeep_timer (evutil_socket_t, short, void *); |
---|
259 | |
---|
260 | /** |
---|
261 | * @brief Initializes Local Peer Discovery for this node |
---|
262 | * |
---|
263 | * For the most part, this means setting up an appropriately configured multicast socket |
---|
264 | * and event-based message handling. |
---|
265 | * |
---|
266 | * @remark Since the LPD service does not use another protocol family yet, this code is |
---|
267 | * IPv4 only for the time being. |
---|
268 | */ |
---|
269 | int tr_lpdInit (tr_session* ss, tr_address* tr_addr UNUSED) |
---|
270 | { |
---|
271 | struct ip_mreq mcastReq; |
---|
272 | const int opt_on = 1, opt_off = 0; |
---|
273 | |
---|
274 | if (session) /* already initialized */ |
---|
275 | return -1; |
---|
276 | |
---|
277 | assert (lpd_announceInterval > 0); |
---|
278 | assert (lpd_announceScope > 0); |
---|
279 | |
---|
280 | lpd_port = tr_sessionGetPeerPort (ss); |
---|
281 | if (lpd_port <= 0) |
---|
282 | return -1; |
---|
283 | |
---|
284 | tr_logAddNamedDbg ("LPD", "Initialising Local Peer Discovery"); |
---|
285 | |
---|
286 | /* setup datagram socket (receive) */ |
---|
287 | { |
---|
288 | lpd_socket = socket (PF_INET, SOCK_DGRAM, 0); |
---|
289 | if (lpd_socket < 0) |
---|
290 | goto fail; |
---|
291 | |
---|
292 | if (evutil_make_socket_nonblocking (lpd_socket) < 0) |
---|
293 | goto fail; |
---|
294 | |
---|
295 | if (setsockopt (lpd_socket, SOL_SOCKET, SO_REUSEADDR, |
---|
296 | &opt_on, sizeof opt_on) < 0) |
---|
297 | goto fail; |
---|
298 | |
---|
299 | memset (&lpd_mcastAddr, 0, sizeof lpd_mcastAddr); |
---|
300 | lpd_mcastAddr.sin_family = AF_INET; |
---|
301 | lpd_mcastAddr.sin_port = htons (lpd_mcastPort); |
---|
302 | if (evutil_inet_pton (lpd_mcastAddr.sin_family, lpd_mcastGroup, |
---|
303 | &lpd_mcastAddr.sin_addr) < 0) |
---|
304 | goto fail; |
---|
305 | |
---|
306 | if (bind (lpd_socket, (struct sockaddr*) &lpd_mcastAddr, |
---|
307 | sizeof lpd_mcastAddr) < 0) |
---|
308 | goto fail; |
---|
309 | |
---|
310 | /* we want to join that LPD multicast group */ |
---|
311 | memset (&mcastReq, 0, sizeof mcastReq); |
---|
312 | mcastReq.imr_multiaddr = lpd_mcastAddr.sin_addr; |
---|
313 | mcastReq.imr_interface.s_addr = htonl (INADDR_ANY); |
---|
314 | if (setsockopt (lpd_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
---|
315 | &mcastReq, sizeof mcastReq) < 0) |
---|
316 | goto fail; |
---|
317 | |
---|
318 | if (setsockopt (lpd_socket, IPPROTO_IP, IP_MULTICAST_LOOP, |
---|
319 | &opt_off, sizeof opt_off) < 0) |
---|
320 | goto fail; |
---|
321 | } |
---|
322 | |
---|
323 | /* setup datagram socket (send) */ |
---|
324 | { |
---|
325 | const unsigned char scope = lpd_announceScope; |
---|
326 | |
---|
327 | lpd_socket2 = socket (PF_INET, SOCK_DGRAM, 0); |
---|
328 | if (lpd_socket2 < 0) |
---|
329 | goto fail; |
---|
330 | |
---|
331 | if (evutil_make_socket_nonblocking (lpd_socket2) < 0) |
---|
332 | goto fail; |
---|
333 | |
---|
334 | /* configure outbound multicast TTL */ |
---|
335 | if (setsockopt (lpd_socket2, IPPROTO_IP, IP_MULTICAST_TTL, |
---|
336 | &scope, sizeof scope) < 0) |
---|
337 | goto fail; |
---|
338 | |
---|
339 | if (setsockopt (lpd_socket2, IPPROTO_IP, IP_MULTICAST_LOOP, |
---|
340 | &opt_off, sizeof opt_off) < 0) |
---|
341 | goto fail; |
---|
342 | } |
---|
343 | |
---|
344 | session = ss; |
---|
345 | |
---|
346 | /* Note: lpd_unsolicitedMsgCounter remains 0 until the first timeout event, thus |
---|
347 | * any announcement received during the initial interval will be discarded. */ |
---|
348 | |
---|
349 | lpd_event = event_new (ss->event_base, lpd_socket, EV_READ | EV_PERSIST, event_callback, NULL); |
---|
350 | event_add (lpd_event, NULL); |
---|
351 | |
---|
352 | upkeep_timer = evtimer_new (ss->event_base, on_upkeep_timer, ss); |
---|
353 | tr_timerAdd (upkeep_timer, UPKEEP_INTERVAL_SECS, 0); |
---|
354 | |
---|
355 | tr_logAddNamedDbg ("LPD", "Local Peer Discovery initialised"); |
---|
356 | |
---|
357 | return 1; |
---|
358 | |
---|
359 | fail: |
---|
360 | { |
---|
361 | const int save = errno; |
---|
362 | close (lpd_socket); |
---|
363 | close (lpd_socket2); |
---|
364 | lpd_socket = lpd_socket2 = -1; |
---|
365 | session = NULL; |
---|
366 | tr_logAddNamedDbg ("LPD", "LPD initialisation failed (errno = %d)", save); |
---|
367 | errno = save; |
---|
368 | } |
---|
369 | |
---|
370 | |
---|
371 | return -1; |
---|
372 | } |
---|
373 | |
---|
374 | void tr_lpdUninit (tr_session* ss) |
---|
375 | { |
---|
376 | if (session != ss) |
---|
377 | return; |
---|
378 | |
---|
379 | tr_logAddNamedDbg ("LPD", "Uninitialising Local Peer Discovery"); |
---|
380 | |
---|
381 | event_free (lpd_event); |
---|
382 | lpd_event = NULL; |
---|
383 | |
---|
384 | evtimer_del (upkeep_timer); |
---|
385 | upkeep_timer = NULL; |
---|
386 | |
---|
387 | /* just shut down, we won't remember any former nodes */ |
---|
388 | evutil_closesocket (lpd_socket); |
---|
389 | evutil_closesocket (lpd_socket2); |
---|
390 | tr_logAddNamedDbg ("LPD", "Done uninitialising Local Peer Discovery"); |
---|
391 | |
---|
392 | session = NULL; |
---|
393 | } |
---|
394 | |
---|
395 | bool |
---|
396 | tr_lpdEnabled (const tr_session* ss) |
---|
397 | { |
---|
398 | return ss && (ss == session); |
---|
399 | } |
---|
400 | |
---|
401 | |
---|
402 | /** |
---|
403 | * @cond |
---|
404 | * @brief Performs some (internal) software consistency checks at compile time. |
---|
405 | * @remark Declared inline for the compiler not to allege us of feeding unused |
---|
406 | * functions. In any other respect, lpd_consistencyCheck is an orphaned function. |
---|
407 | */ |
---|
408 | static inline void lpd_consistencyCheck (void) |
---|
409 | { |
---|
410 | /* if the following check fails, the definition of a hash string has changed |
---|
411 | * without our knowledge; revise string handling in functions tr_lpdSendAnnounce |
---|
412 | * and tr_lpdConsiderAnnounce. However, the code is designed to function as long |
---|
413 | * as interfaces to the rest of the lib remain compatible with char* strings. */ |
---|
414 | TR_STATIC_ASSERT (sizeof (lpd_torStaticType->info.hashString[0]) == sizeof (char), ""); |
---|
415 | } |
---|
416 | /** |
---|
417 | * @endcond */ |
---|
418 | |
---|
419 | |
---|
420 | /** |
---|
421 | * @defgroup LdsProto LPD announcement processing |
---|
422 | * @{ |
---|
423 | */ |
---|
424 | |
---|
425 | /** |
---|
426 | * @brief Announce the given torrent on the local network |
---|
427 | * |
---|
428 | * @param[in] t Torrent to announce |
---|
429 | * @return Returns true on success |
---|
430 | * |
---|
431 | * Send a query for torrent t out to the LPD multicast group (or the LAN, for that |
---|
432 | * matter). A listening client on the same network might react by adding us to his |
---|
433 | * peer pool for torrent t. |
---|
434 | */ |
---|
435 | bool |
---|
436 | tr_lpdSendAnnounce (const tr_torrent* t) |
---|
437 | { |
---|
438 | size_t i; |
---|
439 | const char fmt[] = |
---|
440 | "BT-SEARCH * HTTP/%u.%u" CRLF |
---|
441 | "Host: %s:%u" CRLF |
---|
442 | "Port: %u" CRLF |
---|
443 | "Infohash: %s" CRLF |
---|
444 | CRLF |
---|
445 | CRLF; |
---|
446 | |
---|
447 | char hashString[lengthof (t->info.hashString)]; |
---|
448 | char query[lpd_maxDatagramLength + 1] = { 0 }; |
---|
449 | |
---|
450 | if (t == NULL) |
---|
451 | return false; |
---|
452 | |
---|
453 | /* make sure the hash string is normalized, just in case */ |
---|
454 | for (i = 0; i < sizeof hashString; i++) |
---|
455 | hashString[i] = toupper (t->info.hashString[i]); |
---|
456 | |
---|
457 | /* prepare a zero-terminated announce message */ |
---|
458 | tr_snprintf (query, lpd_maxDatagramLength + 1, fmt, 1, 1, |
---|
459 | lpd_mcastGroup, lpd_mcastPort, lpd_port, hashString); |
---|
460 | |
---|
461 | /* actually send the query out using [lpd_socket2] */ |
---|
462 | { |
---|
463 | const int len = strlen (query); |
---|
464 | |
---|
465 | /* destination address info has already been set up in tr_lpdInit (), |
---|
466 | * so we refrain from preparing another sockaddr_in here */ |
---|
467 | int res = sendto (lpd_socket2, query, len, 0, |
---|
468 | (const struct sockaddr*) &lpd_mcastAddr, sizeof lpd_mcastAddr); |
---|
469 | |
---|
470 | if (res != len) |
---|
471 | return false; |
---|
472 | } |
---|
473 | |
---|
474 | tr_logAddTorDbg (t, "LPD announce message away"); |
---|
475 | |
---|
476 | return true; |
---|
477 | } |
---|
478 | |
---|
479 | /** |
---|
480 | * @brief Process incoming unsolicited messages and add the peer to the announced |
---|
481 | * torrent if all checks are passed. |
---|
482 | * |
---|
483 | * @param[in,out] peer Adress information of the peer to add |
---|
484 | * @param[in] msg The announcement message to consider |
---|
485 | * @return Returns 0 if any input parameter or the announce was invalid, 1 if the peer |
---|
486 | * was successfully added, -1 if not; a non-null return value indicates a side-effect to |
---|
487 | * the peer in/out parameter. |
---|
488 | * |
---|
489 | * @note The port information gets added to the peer structure if tr_lpdConsiderAnnounce |
---|
490 | * is able to extract the necessary information from the announce message. That is, if |
---|
491 | * return != 0, the caller may retrieve the value from the passed structure. |
---|
492 | */ |
---|
493 | static int tr_lpdConsiderAnnounce (tr_pex* peer, const char* const msg) |
---|
494 | { |
---|
495 | enum |
---|
496 | { |
---|
497 | maxValueLen = 25, |
---|
498 | maxHashLen = lengthof (lpd_torStaticType->info.hashString) |
---|
499 | }; |
---|
500 | |
---|
501 | struct lpd_protocolVersion ver = { -1, -1 }; |
---|
502 | char value[maxValueLen] = { 0 }; |
---|
503 | char hashString[maxHashLen] = { 0 }; |
---|
504 | int res = 0, peerPort = 0; |
---|
505 | |
---|
506 | if (peer != NULL && msg != NULL) |
---|
507 | { |
---|
508 | tr_torrent* tor = NULL; |
---|
509 | |
---|
510 | const char* params = lpd_extractHeader (msg, &ver); |
---|
511 | if (params == NULL || ver.major != 1) /* allow messages of protocol v1 */ |
---|
512 | return 0; |
---|
513 | |
---|
514 | /* save the effort to check Host, which seems to be optional anyway */ |
---|
515 | |
---|
516 | if (lpd_extractParam (params, "Port", maxValueLen, value) == 0) |
---|
517 | return 0; |
---|
518 | |
---|
519 | /* determine announced peer port, refuse if value too large */ |
---|
520 | if (sscanf (value, "%d", &peerPort) != 1 || peerPort > (in_port_t)-1) |
---|
521 | return 0; |
---|
522 | |
---|
523 | peer->port = htons (peerPort); |
---|
524 | res = -1; /* signal caller side-effect to peer->port via return != 0 */ |
---|
525 | |
---|
526 | if (lpd_extractParam (params, "Infohash", maxHashLen, hashString) == 0) |
---|
527 | return res; |
---|
528 | |
---|
529 | tor = tr_torrentFindFromHashString (session, hashString); |
---|
530 | |
---|
531 | if (tr_isTorrent (tor) && tr_torrentAllowsLPD (tor)) |
---|
532 | { |
---|
533 | /* we found a suitable peer, add it to the torrent */ |
---|
534 | tr_peerMgrAddPex (tor, TR_PEER_FROM_LPD, peer, -1); |
---|
535 | tr_logAddTorDbg (tor, "Learned %d local peer from LPD (%s:%u)", |
---|
536 | 1, tr_address_to_string (&peer->addr), peerPort); |
---|
537 | |
---|
538 | /* periodic reconnectPulse () deals with the rest... */ |
---|
539 | |
---|
540 | return 1; |
---|
541 | } |
---|
542 | else |
---|
543 | tr_logAddNamedDbg ("LPD", "Cannot serve torrent #%s", hashString); |
---|
544 | } |
---|
545 | |
---|
546 | return res; |
---|
547 | } |
---|
548 | |
---|
549 | /** |
---|
550 | * @} */ |
---|
551 | |
---|
552 | /** |
---|
553 | * @note Since it possible for tr_lpdAnnounceMore to get called from outside the LPD module, |
---|
554 | * the function needs to be informed of the externally employed housekeeping interval. |
---|
555 | * Further, by setting interval to zero (or negative) the caller may actually disable LPD |
---|
556 | * announces on a per-interval basis. |
---|
557 | * |
---|
558 | * FIXME: since this function's been made private and is called by a periodic timer, |
---|
559 | * most of the previous paragraph isn't true anymore... we weren't using that functionality |
---|
560 | * before. are there cases where we should? if not, should we remove the bells & whistles? |
---|
561 | */ |
---|
562 | static int |
---|
563 | tr_lpdAnnounceMore (const time_t now, const int interval) |
---|
564 | { |
---|
565 | tr_torrent* tor = NULL; |
---|
566 | int announcesSent = 0; |
---|
567 | |
---|
568 | if (!tr_isSession (session)) |
---|
569 | return -1; |
---|
570 | |
---|
571 | while ((tor = tr_torrentNext (session, tor)) |
---|
572 | && tr_sessionAllowsLPD (session)) |
---|
573 | { |
---|
574 | if (tr_isTorrent (tor)) |
---|
575 | { |
---|
576 | int announcePrio = 0; |
---|
577 | |
---|
578 | if (!tr_torrentAllowsLPD (tor)) |
---|
579 | continue; |
---|
580 | |
---|
581 | /* issue #3208: prioritize downloads before seeds */ |
---|
582 | switch (tr_torrentGetActivity (tor)) |
---|
583 | { |
---|
584 | case TR_STATUS_DOWNLOAD: |
---|
585 | announcePrio = 1; |
---|
586 | break; |
---|
587 | case TR_STATUS_SEED: |
---|
588 | announcePrio = 2; |
---|
589 | break; |
---|
590 | default: /* fall through */ |
---|
591 | break; |
---|
592 | } |
---|
593 | |
---|
594 | if (announcePrio > 0 && tor->lpdAnnounceAt <= now) |
---|
595 | { |
---|
596 | if (tr_lpdSendAnnounce (tor)) |
---|
597 | announcesSent++; |
---|
598 | |
---|
599 | tor->lpdAnnounceAt = now + |
---|
600 | lpd_announceInterval * announcePrio; |
---|
601 | |
---|
602 | break; /* that's enough; for this interval */ |
---|
603 | } |
---|
604 | } |
---|
605 | } |
---|
606 | |
---|
607 | /* perform housekeeping for the flood protection mechanism */ |
---|
608 | { |
---|
609 | const int maxAnnounceCap = interval * lpd_announceCapFactor; |
---|
610 | |
---|
611 | if (lpd_unsolicitedMsgCounter < 0) |
---|
612 | tr_logAddNamedInfo ("LPD", "Dropped %d announces in the last interval (max. %d " |
---|
613 | "allowed)", -lpd_unsolicitedMsgCounter, maxAnnounceCap); |
---|
614 | |
---|
615 | lpd_unsolicitedMsgCounter = maxAnnounceCap; |
---|
616 | } |
---|
617 | |
---|
618 | return announcesSent; |
---|
619 | } |
---|
620 | |
---|
621 | static void |
---|
622 | on_upkeep_timer (evutil_socket_t foo UNUSED, short bar UNUSED, void * vsession UNUSED) |
---|
623 | { |
---|
624 | const time_t now = tr_time (); |
---|
625 | tr_lpdAnnounceMore (now, UPKEEP_INTERVAL_SECS); |
---|
626 | tr_timerAdd (upkeep_timer, UPKEEP_INTERVAL_SECS, 0); |
---|
627 | } |
---|
628 | |
---|
629 | /** |
---|
630 | * @brief Processing of timeout notifications and incoming data on the socket |
---|
631 | * @note maximum rate of read events is limited according to @a lpd_maxAnnounceCap |
---|
632 | * @see DoS */ |
---|
633 | static void event_callback (evutil_socket_t s UNUSED, short type, void* ignore UNUSED) |
---|
634 | { |
---|
635 | assert (tr_isSession (session)); |
---|
636 | |
---|
637 | /* do not allow announces to be processed if LPD is disabled */ |
---|
638 | if (!tr_sessionAllowsLPD (session)) |
---|
639 | return; |
---|
640 | |
---|
641 | if ((type & EV_READ) != 0) |
---|
642 | { |
---|
643 | struct sockaddr_in foreignAddr; |
---|
644 | int addrLen = sizeof foreignAddr; |
---|
645 | |
---|
646 | /* be paranoid enough about zero terminating the foreign string */ |
---|
647 | char foreignMsg[lpd_maxDatagramLength + 1] = { 0 }; |
---|
648 | |
---|
649 | /* process local announcement from foreign peer */ |
---|
650 | int res = recvfrom (lpd_socket, foreignMsg, lpd_maxDatagramLength, |
---|
651 | 0, (struct sockaddr*) &foreignAddr, (socklen_t*) &addrLen); |
---|
652 | |
---|
653 | /* besides, do we get flooded? then bail out! */ |
---|
654 | if (--lpd_unsolicitedMsgCounter < 0) |
---|
655 | return; |
---|
656 | |
---|
657 | if (res > 0 && res <= lpd_maxDatagramLength) |
---|
658 | { |
---|
659 | struct tr_pex foreignPeer = |
---|
660 | { |
---|
661 | .port = 0, /* the peer-to-peer port is yet unknown */ |
---|
662 | .flags = 0 |
---|
663 | }; |
---|
664 | |
---|
665 | foreignPeer.addr.addr.addr4 = foreignAddr.sin_addr; |
---|
666 | if (tr_lpdConsiderAnnounce (&foreignPeer, foreignMsg) != 0) |
---|
667 | return; /* OK so far, no log message */ |
---|
668 | } |
---|
669 | |
---|
670 | tr_logAddNamedDbg ("LPD", "Discarded invalid multicast message"); |
---|
671 | } |
---|
672 | } |
---|
673 | |
---|