package netreport
import (
"context"
"crypto/tls"
"fmt"
"net"
"net/http"
"net/url"
"time"
"github.com/tmc/go-iroh/netaddr"
)
func runHTTPSProbe(ctx context .Context , relay netaddr .RelayURL , tlsConfig *tls .Config ) (*probeReport , error ) {
probeURL , err := joinPath (relay , relayProbePath )
if err != nil {
return nil , err
}
client := newProbeClient (tlsConfig )
req , err := http .NewRequestWithContext (ctx , http .MethodGet , probeURL , nil )
if err != nil {
return nil , fmt .Errorf ("build request: %w" , err )
}
start := time .Now ()
resp , err := client .Do (req )
if err != nil {
return nil , fmt .Errorf ("https request: %w" , err )
}
defer resp .Body .Close ()
latency := time .Since (start )
if resp .StatusCode < 200 || resp .StatusCode >= 300 {
return nil , fmt .Errorf ("https probe: unexpected status %d" , resp .StatusCode )
}
drain (resp .Body , 8 <<10 )
return &probeReport {probe : ProbeHTTPS , relay : relay , latency : latency }, nil
}
func checkCaptivePortal(ctx context .Context , relay netaddr .RelayURL , tlsConfig *tls .Config ) (bool , error ) {
host := relay .Host ()
if host == "" {
return false , fmt .Errorf ("captive portal: %w" , errMissingHost )
}
challenge := "ts_" + host
authority := host
if u := relay .URL (); u != nil && u .Host != "" {
authority = u .Host
}
portalURL := "http://" + authority + captivePortalPath
client := newProbeClient (tlsConfig )
req , err := http .NewRequestWithContext (ctx , http .MethodGet , portalURL , nil )
if err != nil {
return false , fmt .Errorf ("build request: %w" , err )
}
req .Header .Set (challengeHeader , challenge )
resp , err := client .Do (req )
if err != nil {
return false , fmt .Errorf ("captive portal request: %w" , err )
}
defer resp .Body .Close ()
drain (resp .Body , 8 <<10 )
want := "response " + challenge
validResponse := resp .Header .Get (responseHeader ) == want
hasCaptive := resp .StatusCode != http .StatusNoContent || !validResponse
return hasCaptive , nil
}
var errMissingHost = fmt .Errorf ("relay url has no host" )
func joinPath(relay netaddr .RelayURL , path string ) (string , error ) {
u := relay .URL ()
if u == nil {
return "" , fmt .Errorf ("join: %w" , errMissingHost )
}
ref , err := url .Parse (path )
if err != nil {
return "" , fmt .Errorf ("join: %w" , err )
}
return u .ResolveReference (ref ).String (), nil
}
func newProbeClient(tlsConfig *tls .Config ) *http .Client {
transport := &http .Transport {}
if tlsConfig != nil {
transport .TLSClientConfig = tlsConfig
}
return &http .Client {
Transport : transport ,
CheckRedirect : func (*http .Request , []*http .Request ) error {
return http .ErrUseLastResponse
},
}
}
func drain(r interface { Read ([]byte ) (int , error ) }, limit int ) {
buf := make ([]byte , 4096 )
read := 0
for read < limit {
n , err := r .Read (buf )
read += n
if err != nil {
return
}
}
}
func lookupIPStaggered(ctx context .Context , resolver *net .Resolver , host string ) ([]net .IPAddr , error ) {
if resolver == nil {
resolver = net .DefaultResolver
}
ctx , cancel := context .WithTimeout (ctx , dnsTimeout )
defer cancel ()
type result struct {
addrs []net .IPAddr
err error
}
results := make (chan result , len (dnsStaggerMs )+1 )
delays := append ([]int {0 }, dnsStaggerMs ...)
for _ , ms := range delays {
go func (delay time .Duration ) {
if delay > 0 {
select {
case <- time .After (delay ):
case <- ctx .Done ():
return
}
}
addrs , err := resolver .LookupIPAddr (ctx , host )
select {
case results <- result {addrs , err }:
case <- ctx .Done ():
}
}(time .Duration (ms ) * time .Millisecond )
}
var lastErr error
for range delays {
select {
case r := <- results :
if r .err == nil && len (r .addrs ) > 0 {
return r .addrs , nil
}
if r .err != nil {
lastErr = r .err
}
case <- ctx .Done ():
if lastErr != nil {
return nil , lastErr
}
return nil , ctx .Err ()
}
}
if lastErr != nil {
return nil , lastErr
}
return nil , fmt .Errorf ("no addresses for %s" , host )
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .