9bd1c5ff20
See the explanatory newly-added comment for more information. Closes https://github.com/ooni/probe/issues/2064
108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
// Command oohelperd implements the Web Connectivity test helper.
|
|
package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/apex/log"
|
|
"github.com/ooni/probe-cli/v3/internal/atomicx"
|
|
"github.com/ooni/probe-cli/v3/internal/model"
|
|
"github.com/ooni/probe-cli/v3/internal/netxlite"
|
|
"github.com/ooni/probe-cli/v3/internal/runtimex"
|
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
|
)
|
|
|
|
const maxAcceptableBody = 1 << 24
|
|
|
|
var (
|
|
endpoint = flag.String("endpoint", "127.0.0.1:8080", "API endpoint")
|
|
srvAddr = make(chan string, 1) // with buffer
|
|
srvCancel context.CancelFunc
|
|
srvCtx context.Context
|
|
srvWg = new(sync.WaitGroup)
|
|
)
|
|
|
|
func init() {
|
|
srvCtx, srvCancel = context.WithCancel(context.Background())
|
|
}
|
|
|
|
func newResolver(logger model.Logger) model.Resolver {
|
|
// Implementation note: pin to a specific resolver so we don't depend upon the
|
|
// default resolver configured by the box. Also, use an encrypted transport thus
|
|
// we're less vulnerable to any policy implemented by the box's provider.
|
|
resolver := netxlite.NewParallelDNSOverHTTPSResolver(logger, "https://dns.google/dns-query")
|
|
return resolver
|
|
}
|
|
|
|
func shutdown(srv *http.Server) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
srv.Shutdown(ctx)
|
|
}
|
|
|
|
func main() {
|
|
logmap := map[bool]log.Level{
|
|
true: log.DebugLevel,
|
|
false: log.InfoLevel,
|
|
}
|
|
prometheus := flag.String("prometheus", "127.0.0.1:9091", "Prometheus endpoint")
|
|
debug := flag.Bool("debug", false, "Toggle debug mode")
|
|
flag.Parse()
|
|
log.SetLevel(logmap[*debug])
|
|
defer srvCancel()
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/", &handler{
|
|
BaseLogger: log.Log,
|
|
Indexer: &atomicx.Int64{},
|
|
MaxAcceptableBody: maxAcceptableBody,
|
|
NewClient: func(logger model.Logger) model.HTTPClient {
|
|
// If the DoH resolver we're using insists that a given domain maps to
|
|
// bogons, make sure we're going to fail the HTTP measurement.
|
|
//
|
|
// The TCP measurements scheduler in ipinfo.go will also refuse to
|
|
// schedule TCP measurements for bogons.
|
|
//
|
|
// While this seems theoretical, as of 2022-08-28, I see:
|
|
//
|
|
// % host polito.it
|
|
// polito.it has address 192.168.59.6
|
|
// polito.it has address 192.168.40.1
|
|
// polito.it mail is handled by 10 mx.polito.it.
|
|
//
|
|
// So, it's better to consider this as a possible corner case.
|
|
reso := netxlite.MaybeWrapWithBogonResolver(
|
|
true, // enabled
|
|
newResolver(logger),
|
|
)
|
|
return netxlite.NewHTTPClientWithResolver(logger, reso)
|
|
},
|
|
NewDialer: func(logger model.Logger) model.Dialer {
|
|
return netxlite.NewDialerWithResolver(logger, newResolver(logger))
|
|
},
|
|
NewResolver: newResolver,
|
|
NewTLSHandshaker: func(logger model.Logger) model.TLSHandshaker {
|
|
return netxlite.NewTLSHandshakerStdlib(logger)
|
|
},
|
|
})
|
|
srv := &http.Server{Addr: *endpoint, Handler: mux}
|
|
listener, err := net.Listen("tcp", *endpoint)
|
|
runtimex.PanicOnError(err, "net.Listen failed")
|
|
srvAddr <- listener.Addr().String()
|
|
srvWg.Add(1)
|
|
go srv.Serve(listener)
|
|
promMux := http.NewServeMux()
|
|
promMux.Handle("/metrics", promhttp.Handler())
|
|
promSrv := &http.Server{Addr: *prometheus, Handler: promMux}
|
|
go promSrv.ListenAndServe()
|
|
<-srvCtx.Done()
|
|
shutdown(srv)
|
|
shutdown(promSrv)
|
|
listener.Close()
|
|
srvWg.Done()
|
|
}
|