ooni-probe-cli/internal/cmd/oohelperd/main.go
Simone Basso 9bd1c5ff20
fix(oohelperd): refuse to measure bogons (#898)
See the explanatory newly-added comment for more information.

Closes https://github.com/ooni/probe/issues/2064
2022-08-29 00:06:57 +02:00

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()
}