ooni-probe-cli/internal/cmd/oohelperd/ipinfo.go
Simone Basso bb6563f363
refactor: move TH structs and definitions to model (#894)
This commit moves the TH structs and definitions to model. We don't want
oohelperd to depend on web_connectivity@v0.4.

Part of https://github.com/ooni/probe/issues/2240
2022-08-28 20:20:12 +02:00

103 lines
2.9 KiB
Go

package main
//
// Generates IP and endpoint information.
//
import (
"net"
"net/url"
"sort"
"strings"
"github.com/ooni/probe-cli/v3/internal/geoipx"
"github.com/ooni/probe-cli/v3/internal/model"
"github.com/ooni/probe-cli/v3/internal/netxlite"
)
// newIPInfo creates an IP to IPInfo mapping from addresses resolved
// by the probe (inside [creq]) or the TH (inside [addrs]).
func newIPInfo(creq *ctrlRequest, addrs []string) map[string]*model.THIPInfo {
discoveredby := make(map[string]int64)
for _, epnt := range creq.TCPConnect {
addr, _, err := net.SplitHostPort(epnt)
if err != nil || net.ParseIP(addr) == nil {
continue
}
discoveredby[addr] |= model.THIPInfoFlagResolvedByProbe
}
for _, addr := range addrs {
if net.ParseIP(addr) != nil {
discoveredby[addr] |= model.THIPInfoFlagResolvedByTH
}
}
ipinfo := make(map[string]*model.THIPInfo)
for addr, flags := range discoveredby {
if netxlite.IsBogon(addr) { // note: we already excluded non-IP addrs above
flags |= model.THIPInfoFlagIsBogon
}
asn, _, _ := geoipx.LookupASN(addr) // AS0 on failure
ipinfo[addr] = &model.THIPInfo{
ASN: int64(asn),
Flags: flags,
}
}
return ipinfo
}
// endpointInfo contains info about an endpoint to measure
type endpointInfo struct {
// Addr is the address to measure
Addr string
// Epnt is the endpoint to measure
Epnt string
// TLS indicates whether we should try using TLS
TLS bool
}
// ipInfoToEndpoints takes in input the [ipinfo] returned by newIPInfo
// and the [URL] provided by the probe to generate the list of endpoints
// to measure. We choose ports as follows:
//
// 1. if the input URL contains a port, we use such a port;
//
// 2. if the input URL scheme is "https", we choose port 443;
//
// 3. if the input URL scheme is "http", we use both 443 and 80, which
// allows us to include in the measurement information useful to determine
// whether an IP address is valid for a domain;
//
// 4. otherwise, we don't generate any endpoint to measure.
func ipInfoToEndpoints(URL *url.URL, ipinfo map[string]*model.THIPInfo) []endpointInfo {
var ports []string
if port := URL.Port(); port != "" {
ports = []string{port} // as documented
} else if URL.Scheme == "https" {
ports = []string{"443"} // as documented
} else if URL.Scheme == "http" {
ports = []string{"80", "443"} // as documented
}
out := []endpointInfo{}
for addr, info := range ipinfo {
if (info.Flags & model.THIPInfoFlagIsBogon) != 0 {
continue // as documented
}
for _, port := range ports {
epnt := net.JoinHostPort(addr, port)
out = append(out, endpointInfo{
Addr: addr,
Epnt: epnt,
TLS: port == "443",
})
}
}
// sort the output to make testing work deterministically since iterating
// a map in golang isn't guaranteed to return ordered keys
sort.SliceStable(out, func(i, j int) bool {
return strings.Compare(out[i].Epnt, out[j].Epnt) < 0
})
return out
}