Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
2.6 kB · 101 lines
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102package aqhttp
import ( "context" "fmt" "net" "net/http" "time")
// TrustedTransport is a basic transport that adds User-Agent headers.// Use this for trusted infrastructure endpoints where SSRF is not a concern.type TrustedTransport struct { Base http.RoundTripper}
func (t *TrustedTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("User-Agent", UserAgent) return t.Base.RoundTrip(req)}
// NewTrustedTransport creates a transport for trusted endpoints.func NewTrustedTransport() *TrustedTransport { return &TrustedTransport{ Base: &http.Transport{ MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, }, }}
// UntrustedTransport validates destination IPs using DNS-over-HTTPS before connecting.// Prevents SSRF attacks by blocking private, loopback, and bogon IP ranges.type UntrustedTransport struct { Base http.RoundTripper resolver *DoHResolver}
func (t *UntrustedTransport) RoundTrip(req *http.Request) (*http.Response, error) { req.Header.Add("User-Agent", UserAgent) return t.Base.RoundTrip(req)}
// NewUntrustedTransport creates a transport that validates all destination IPs.func NewUntrustedTransport() *UntrustedTransport { resolver := NewDoHResolver("")
dialer := &net.Dialer{ Timeout: 30 * time.Second, KeepAlive: 30 * time.Second, }
transport := &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { host, port, err := net.SplitHostPort(addr) if err != nil { return nil, fmt.Errorf("failed to parse address: %w", err) }
// Resolve IPv4 addresses using DoH ipv4Addrs, _ := resolver.Resolve(host, TypeA) var validIP string
// Check IPv4 addresses first for _, ip := range ipv4Addrs { if !resolver.IsInvalidIP(ip) { validIP = ip break } }
// Fall back to IPv6 if no valid IPv4 if validIP == "" { ipv6Addrs, _ := resolver.Resolve(host, TypeAAAA) for _, ip := range ipv6Addrs { if !resolver.IsInvalidIP(ip) { validIP = ip break } } }
if validIP == "" { return nil, fmt.Errorf("all resolved IPs for %s are private/invalid", host) }
// Dial using the validated IP targetAddr := net.JoinHostPort(validIP, port) return dialer.DialContext(ctx, network, targetAddr) }, MaxIdleConns: 100, IdleConnTimeout: 90 * time.Second, TLSHandshakeTimeout: 10 * time.Second, }
return &UntrustedTransport{ Base: transport, resolver: resolver, }}