NAME

Net::BitTorrent::SSRF - Server-Side Request Forgery Protection

SYNOPSIS

use Net::BitTorrent::SSRF qw[is_safe_ip is_safe_host is_safe_url resolve_and_pin];

# Check a raw IP address
die 'unsafe' unless is_safe_ip('8.8.8.8');

# Check a hostname (resolves DNS and validates all IPs)
die 'unsafe' unless is_safe_host('tracker.example.com');

# Check a full URL (extracts host, resolves, validates)
die 'unsafe' unless is_safe_url('http://tracker.example.com/announce');

# Resolve a hostname to a validated safe IP + port (DNS pinning)
my ($ip, $port) = resolve_and_pin('tracker.example.com', 80)
    or die 'could not resolve to a safe address';

DESCRIPTION

Net::BitTorrent::SSRF provides utility functions to detect and block outbound connections to RFC 1918 private ranges, loopback, link-local, multicast, cloud metadata endpoints, and other address spaces that should never be contacted by a BitTorrent client on behalf of untrusted input.

This module is used by the tracker, web seed, and DHT subsystems to prevent Server-Side Request Forgery (SSRF) attacks where a malicious torrent file, magnet link, tracker response, or DHT node tricks the client into probing internal networks.

FUNCTIONS

is_safe_ip( $ip )

Returns true if $ip is a public (non-private) IP address string. Returns false for loopback, RFC 1918 private, link-local, ULA, multicast, unspecified, and unparseable inputs.

if ( is_safe_ip( $ip ) ) {
    # safe to connect
}

Blocked ranges:

  • 127.0.0.0/8 - IPv4 loopback

  • 10.0.0.0/8 - RFC 1918 private

  • 172.16.0.0/12 - RFC 1918 private

  • 192.168.0.0/16 - RFC 1918 private

  • 169.254.0.0/16 - Link-local (includes 169.254.169.254 cloud metadata)

  • 224.0.0.0/4 - IPv4 multicast

  • 0.0.0.0 - Unspecified

  • ::1 - IPv6 loopback

  • fe80::/10 - IPv6 link-local

  • fc00::/7 - IPv6 ULA

  • ff00::/8 - IPv6 multicast

  • :: - IPv6 unspecified

is_safe_host( $host )

Returns true if $host (an IP address or hostname) resolves exclusively to public IP addresses. If $host is already a valid IP, this behaves identically to is_safe_ip. Otherwise, $host is resolved via getaddrinfo and every resulting IP is checked with is_safe_ip.

unless ( is_safe_host( $tracker_host ) ) {
    warn 'refusing to connect to ' .$tracker_host;
}

is_safe_url( $url )

Extracts the hostname from $url using URI and passes it to is_safe_host. Returns true if the URL's host resolves to only public IPs.

my $http = HTTP::Tiny->new();
if ( is_safe_url( $target_url ) ) {
    my $res = $http->get( $target_url );
}

resolve_and_pin( $host, $port = undef )

Resolves $host via getaddrinfo, validates every resolved IP with is_safe_ip, and returns ($safe_ip, $port) for the first safe result. Returns an empty list on failure (DNS error, no results, or all resolved IPs are unsafe).

my ($ip, $port) = resolve_and_pin('tracker.example.com', 80)
    or die 'could not resolve to a safe address';
# $ip is now a validated public IP, $port is the resolved or original port

If $host is already a valid safe IP (passes is_safe_ip), it is returned immediately without DNS resolution. This function is intended for DNS pinning: resolve once, validate, and use the result for the actual connection to minimize the TOCTOU window between resolution and connection.

A full TOCTOU elimination would require overriding HTTP::Tiny's internal connection logic. The residual window when using this function is sub-millisecond and requires attacker-controlled DNS.

INTEGRATION

This module is consumed by the following classes:

All tracker and DHT classes accept a ssrf_bypass constructor parameter for testing. This parameter defaults to 0 (protection enabled) and should never be set to 1 in production.

SPECIFICATIONS

  • RFC 1918 - Address Allocation for Private Internets

  • RFC 3927 - Dynamic Configuration of IPv4 Link-Local Addresses

  • RFC 4193 - Unique Local IPv6 Unicast Addresses

  • BEP 55 - Holepunching via UDP (SSRF vector via HP_CONNECT)

AUTHOR

Sanko Robinson - https://github.com/sanko

COPYRIGHT

Copyright (C) 2008-2026 by Sanko Robinson.

This library is free software; you can redistribute it and/or modify it under the terms of the Artistic License 2.0.