Skip to content

Commit 4b8fbfb

Browse files
committed
refactor: the tracker is responsible for assigning the IP to peers
1 parent 8e85f18 commit 4b8fbfb

File tree

4 files changed

+151
-130
lines changed

4 files changed

+151
-130
lines changed

src/http/warp_implementation/handlers.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::collections::HashMap;
22
use std::convert::Infallible;
3-
use std::net::IpAddr;
3+
use std::net::{IpAddr, SocketAddr};
44
use std::panic::Location;
55
use std::sync::Arc;
66

@@ -41,11 +41,15 @@ pub async fn handle_announce(
4141
auth_key: Option<auth::Key>,
4242
tracker: Arc<tracker::Tracker>,
4343
) -> WebResult<impl Reply> {
44+
debug!("http announce request: {:#?}", announce_request);
45+
4446
authenticate(&announce_request.info_hash, &auth_key, tracker.clone()).await?;
4547

46-
debug!("{:?}", announce_request);
48+
// build the peer
49+
let peer_ip = tracker.assign_ip_address_to_peer(&announce_request.peer_addr);
50+
let peer_socket_address = SocketAddr::new(peer_ip, announce_request.port);
51+
let peer = peer::Peer::from_http_announce_request(&announce_request, &peer_socket_address);
4752

48-
let peer = peer::Peer::from_http_announce_request(&announce_request, announce_request.peer_addr, tracker.config.get_ext_ip());
4953
let torrent_stats = tracker
5054
.update_torrent_with_peer_and_get_stats(&announce_request.info_hash, &peer)
5155
.await;

src/tracker/mod.rs

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub mod torrent;
88

99
use std::collections::btree_map::Entry;
1010
use std::collections::BTreeMap;
11-
use std::net::SocketAddr;
11+
use std::net::{IpAddr, SocketAddr};
1212
use std::panic::Location;
1313
use std::sync::Arc;
1414
use std::time::Duration;
@@ -76,6 +76,12 @@ impl Tracker {
7676
self.mode == mode::Mode::Listed || self.mode == mode::Mode::PrivateListed
7777
}
7878

79+
/// It assigns a socket address to the peer
80+
#[must_use]
81+
pub fn assign_ip_address_to_peer(&self, remote_client_ip: &IpAddr) -> IpAddr {
82+
assign_ip_address_to_peer(remote_client_ip, self.config.get_ext_ip())
83+
}
84+
7985
/// # Errors
8086
///
8187
/// Will return a `database::Error` if unable to add the `auth_key` to the database.
@@ -378,6 +384,15 @@ impl Tracker {
378384
}
379385
}
380386

387+
#[must_use]
388+
pub fn assign_ip_address_to_peer(remote_client_ip: &IpAddr, tracker_external_ip: Option<IpAddr>) -> IpAddr {
389+
if let Some(host_ip) = tracker_external_ip.filter(|_| remote_client_ip.is_loopback()) {
390+
host_ip
391+
} else {
392+
*remote_client_ip
393+
}
394+
}
395+
381396
#[cfg(test)]
382397
mod tests {
383398
use std::sync::Arc;
@@ -424,4 +439,100 @@ mod tests {
424439
}
425440
);
426441
}
442+
443+
mod the_tracker_assigning_the_ip_to_the_peer {
444+
445+
use std::net::{IpAddr, Ipv4Addr};
446+
447+
use crate::tracker::assign_ip_address_to_peer;
448+
449+
#[test]
450+
fn should_use_the_source_ip_instead_of_the_ip_in_the_announce_request() {
451+
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
452+
453+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
454+
455+
assert_eq!(peer_ip, remote_ip);
456+
}
457+
458+
mod when_the_client_ip_is_a_ipv4_loopback_ip {
459+
460+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
461+
use std::str::FromStr;
462+
463+
use crate::tracker::assign_ip_address_to_peer;
464+
465+
#[test]
466+
fn it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration() {
467+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
468+
469+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
470+
471+
assert_eq!(peer_ip, remote_ip);
472+
}
473+
474+
#[test]
475+
fn it_should_use_the_external_tracker_ip_in_tracker_configuration_if_it_is_defined() {
476+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
477+
478+
let tracker_external_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
479+
480+
let peer_ip = assign_ip_address_to_peer(&remote_ip, Some(tracker_external_ip));
481+
482+
assert_eq!(peer_ip, tracker_external_ip);
483+
}
484+
485+
#[test]
486+
fn it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv6_ip()
487+
{
488+
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
489+
490+
let tracker_external_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
491+
492+
let peer_ip = assign_ip_address_to_peer(&remote_ip, Some(tracker_external_ip));
493+
494+
assert_eq!(peer_ip, tracker_external_ip);
495+
}
496+
}
497+
498+
mod when_client_ip_is_a_ipv6_loopback_ip {
499+
500+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
501+
use std::str::FromStr;
502+
503+
use crate::tracker::assign_ip_address_to_peer;
504+
505+
#[test]
506+
fn it_should_use_the_loopback_ip_if_the_tracker_does_not_have_the_external_ip_configuration() {
507+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
508+
509+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
510+
511+
assert_eq!(peer_ip, remote_ip);
512+
}
513+
514+
#[test]
515+
fn it_should_use_the_external_ip_in_tracker_configuration_if_it_is_defined() {
516+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
517+
518+
let tracker_external_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
519+
520+
let peer_ip = assign_ip_address_to_peer(&remote_ip, Some(tracker_external_ip));
521+
522+
assert_eq!(peer_ip, tracker_external_ip);
523+
}
524+
525+
#[test]
526+
fn it_should_use_the_external_ip_in_the_tracker_configuration_if_it_is_defined_even_if_the_external_ip_is_an_ipv4_ip()
527+
{
528+
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
529+
530+
let tracker_external_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
531+
532+
let peer_ip = assign_ip_address_to_peer(&remote_ip, Some(tracker_external_ip));
533+
534+
assert_eq!(peer_ip, tracker_external_ip);
535+
}
536+
}
537+
}
427538
}

src/tracker/peer.rs

Lines changed: 25 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::net::{IpAddr, SocketAddr};
1+
use std::net::SocketAddr;
22
use std::panic::Location;
33

44
use aquatic_udp_protocol::{AnnounceEvent, NumberOfBytes};
@@ -29,16 +29,10 @@ pub struct Peer {
2929

3030
impl Peer {
3131
#[must_use]
32-
pub fn from_udp_announce_request(
33-
announce_request: &aquatic_udp_protocol::AnnounceRequest,
34-
remote_ip: IpAddr,
35-
host_opt_ip: Option<IpAddr>,
36-
) -> Self {
37-
let peer_addr = Peer::peer_addr_from_ip_and_port_and_opt_host_ip(remote_ip, host_opt_ip, announce_request.port.0);
38-
32+
pub fn from_udp_announce_request(announce_request: &aquatic_udp_protocol::AnnounceRequest, peer_addr: &SocketAddr) -> Self {
3933
Peer {
4034
peer_id: Id(announce_request.peer_id.0),
41-
peer_addr,
35+
peer_addr: *peer_addr,
4236
updated: Current::now(),
4337
uploaded: announce_request.bytes_uploaded,
4438
downloaded: announce_request.bytes_downloaded,
@@ -48,9 +42,7 @@ impl Peer {
4842
}
4943

5044
#[must_use]
51-
pub fn from_http_announce_request(announce_request: &Announce, remote_ip: IpAddr, host_opt_ip: Option<IpAddr>) -> Self {
52-
let peer_addr = Peer::peer_addr_from_ip_and_port_and_opt_host_ip(remote_ip, host_opt_ip, announce_request.port);
53-
45+
pub fn from_http_announce_request(announce_request: &Announce, peer_addr: &SocketAddr) -> Self {
5446
let event: AnnounceEvent = if let Some(event) = &announce_request.event {
5547
match event.as_ref() {
5648
"started" => AnnounceEvent::Started,
@@ -65,7 +57,7 @@ impl Peer {
6557
#[allow(clippy::cast_possible_truncation)]
6658
Peer {
6759
peer_id: announce_request.peer_id,
68-
peer_addr,
60+
peer_addr: *peer_addr,
6961
updated: Current::now(),
7062
uploaded: NumberOfBytes(i128::from(announce_request.uploaded) as i64),
7163
downloaded: NumberOfBytes(i128::from(announce_request.downloaded) as i64),
@@ -74,16 +66,6 @@ impl Peer {
7466
}
7567
}
7668

77-
// potentially substitute localhost ip with external ip
78-
#[must_use]
79-
pub fn peer_addr_from_ip_and_port_and_opt_host_ip(remote_ip: IpAddr, host_opt_ip: Option<IpAddr>, port: u16) -> SocketAddr {
80-
if let Some(host_ip) = host_opt_ip.filter(|_| remote_ip.is_loopback()) {
81-
SocketAddr::new(host_ip, port)
82-
} else {
83-
SocketAddr::new(remote_ip, port)
84-
}
85-
}
86-
8769
#[must_use]
8870
pub fn is_seeder(&self) -> bool {
8971
self.left.0 <= 0 && self.event != AnnounceEvent::Stopped
@@ -446,6 +428,7 @@ mod test {
446428
AnnounceEvent, AnnounceRequest, NumberOfBytes, NumberOfPeers, PeerId as AquaticPeerId, PeerKey, Port, TransactionId,
447429
};
448430

431+
use crate::tracker::assign_ip_address_to_peer;
449432
use crate::tracker::peer::Peer;
450433
use crate::udp::connection_cookie::{into_connection_id, make};
451434

@@ -498,7 +481,10 @@ mod test {
498481
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
499482
let announce_request = AnnounceRequestBuilder::default().into();
500483

501-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, None);
484+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
485+
let peer_socket_address = SocketAddr::new(peer_ip, announce_request.port.0);
486+
487+
let torrent_peer = Peer::from_udp_announce_request(&announce_request, &peer_socket_address);
502488

503489
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
504490
}
@@ -508,99 +494,21 @@ mod test {
508494
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
509495
let announce_request = AnnounceRequestBuilder::default().into();
510496

511-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, None);
512-
513-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
514-
}
515-
516-
mod when_source_udp_ip_is_a_ipv_4_loopback_ip {
517-
518-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
519-
use std::str::FromStr;
520-
521-
use crate::tracker::peer::test::torrent_peer_constructor_from_udp_requests::AnnounceRequestBuilder;
522-
use crate::tracker::peer::Peer;
523-
524-
#[test]
525-
fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration() {
526-
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
527-
let announce_request = AnnounceRequestBuilder::default().into();
528-
529-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, None);
530-
531-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
532-
}
533-
534-
#[test]
535-
fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined() {
536-
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
537-
let announce_request = AnnounceRequestBuilder::default().into();
538-
539-
let host_opt_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
540-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
541-
542-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
543-
}
544-
545-
#[test]
546-
fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv6_ip() {
547-
let remote_ip = IpAddr::V4(Ipv4Addr::LOCALHOST);
548-
let announce_request = AnnounceRequestBuilder::default().into();
497+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
498+
let peer_socket_address = SocketAddr::new(peer_ip, announce_request.port.0);
549499

550-
let host_opt_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
551-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
500+
let torrent_peer = Peer::from_udp_announce_request(&announce_request, &peer_socket_address);
552501

553-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
554-
}
555-
}
556-
557-
mod when_source_udp_ip_is_a_ipv6_loopback_ip {
558-
559-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
560-
use std::str::FromStr;
561-
562-
use crate::tracker::peer::test::torrent_peer_constructor_from_udp_requests::AnnounceRequestBuilder;
563-
use crate::tracker::peer::Peer;
564-
565-
#[test]
566-
fn it_should_use_the_loopback_ip_if_the_server_does_not_have_the_external_ip_configuration() {
567-
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
568-
let announce_request = AnnounceRequestBuilder::default().into();
569-
570-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, None);
571-
572-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
573-
}
574-
575-
#[test]
576-
fn it_should_use_the_external_host_ip_in_tracker_configuration_if_defined() {
577-
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
578-
let announce_request = AnnounceRequestBuilder::default().into();
579-
580-
let host_opt_ip = IpAddr::V6(Ipv6Addr::from_str("2345:0425:2CA1:0000:0000:0567:5673:23b5").unwrap());
581-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
582-
583-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
584-
}
585-
586-
#[test]
587-
fn it_should_use_the_external_ip_in_tracker_configuration_if_defined_even_if_the_external_ip_is_an_ipv4_ip() {
588-
let remote_ip = IpAddr::V6(Ipv6Addr::LOCALHOST);
589-
let announce_request = AnnounceRequestBuilder::default().into();
590-
591-
let host_opt_ip = IpAddr::V4(Ipv4Addr::from_str("126.0.0.1").unwrap());
592-
let torrent_peer = Peer::from_udp_announce_request(&announce_request, remote_ip, Some(host_opt_ip));
593-
594-
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(host_opt_ip, announce_request.port.0));
595-
}
502+
assert_eq!(torrent_peer.peer_addr, SocketAddr::new(remote_ip, announce_request.port.0));
596503
}
597504
}
598505

599506
mod torrent_peer_constructor_from_for_http_requests {
600-
use std::net::{IpAddr, Ipv4Addr};
507+
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
601508

602509
use crate::http::warp_implementation::request::Announce;
603510
use crate::protocol::info_hash::InfoHash;
511+
use crate::tracker::assign_ip_address_to_peer;
604512
use crate::tracker::peer::{self, Peer};
605513

606514
fn sample_http_announce_request(peer_addr: IpAddr, port: u16) -> Announce {
@@ -618,13 +526,16 @@ mod test {
618526
}
619527

620528
#[test]
621-
fn it_should_use_the_source_ip_in_the_udp_heder_as_the_peer_ip_address_ignoring_the_peer_ip_in_the_announce_request() {
529+
fn it_should_use_the_source_ip_in_the_udp_header_as_the_peer_ip_address_ignoring_the_peer_ip_in_the_announce_request() {
622530
let remote_ip = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 2));
623531

624532
let ip_in_announce_request = IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1));
625533
let announce_request = sample_http_announce_request(ip_in_announce_request, 8080);
626534

627-
let torrent_peer = Peer::from_http_announce_request(&announce_request, remote_ip, None);
535+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
536+
let peer_socket_address = SocketAddr::new(peer_ip, announce_request.port);
537+
538+
let torrent_peer = Peer::from_http_announce_request(&announce_request, &peer_socket_address);
628539

629540
assert_eq!(torrent_peer.peer_addr.ip(), remote_ip);
630541
assert_ne!(torrent_peer.peer_addr.ip(), ip_in_announce_request);
@@ -639,18 +550,13 @@ mod test {
639550
let announce_request =
640551
sample_http_announce_request(IpAddr::V4(Ipv4Addr::new(126, 0, 0, 1)), port_in_announce_request);
641552

642-
let torrent_peer = Peer::from_http_announce_request(&announce_request, remote_ip, None);
553+
let peer_ip = assign_ip_address_to_peer(&remote_ip, None);
554+
let peer_socket_address = SocketAddr::new(peer_ip, announce_request.port);
555+
556+
let torrent_peer = Peer::from_http_announce_request(&announce_request, &peer_socket_address);
643557

644558
assert_eq!(torrent_peer.peer_addr.port(), announce_request.port);
645559
assert_ne!(torrent_peer.peer_addr.port(), remote_port);
646560
}
647-
648-
// todo: other cases are already covered by UDP cases.
649-
// Code review:
650-
// We should extract the method "peer_addr_from_ip_and_port_and_opt_host_ip" from TorrentPeer.
651-
// It could be another service responsible for assigning the IP to the peer.
652-
// So we can test that behavior independently from where you use it.
653-
// We could also build the peer with the IP in the announce request and let the tracker decide
654-
// wether it has to change it or not depending on tracker configuration.
655561
}
656562
}

0 commit comments

Comments
 (0)