Squash integration of the reviewed Go core, frontend audit fixes, release tooling, and deployment hygiene. CI passed all Server/Client jobs.
82 lines
2.7 KiB
Go
82 lines
2.7 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/TNTcraftHIM/Screener/internal/server/protocol"
|
|
)
|
|
|
|
// Ported from src/server/ice.ts.
|
|
const natPredictionBasePort = 3478
|
|
|
|
// natPredictionAuxiliaryPorts are NAT_PREDICTION_AUXILIARY_PORTS: the two
|
|
// listeners a NAT-prediction STUN deployment binds beside the base port, whose
|
|
// answers reveal the peer's port-allocation stride.
|
|
var natPredictionAuxiliaryPorts = [...]int{natPredictionBasePort + 1, natPredictionBasePort + 2}
|
|
|
|
// NATPredictionStunURLs ports natPredictionStunUrls: the two auxiliary
|
|
// listeners derived from the first ordinary STUN authority on UDP 3478.
|
|
func NATPredictionStunURLs(stunURLs []string) []string {
|
|
for _, stunURL := range stunURLs {
|
|
// TS: stunUrl.slice(stunUrl.indexOf(":") + 1) — a URL without a colon
|
|
// keeps its whole text, which then fails to yield a host below.
|
|
authorityText := stunURL[strings.IndexByte(stunURL, ':')+1:]
|
|
authority, err := url.Parse("http://" + authorityText)
|
|
if err != nil || authority.Hostname() == "" {
|
|
continue
|
|
}
|
|
port := authority.Port()
|
|
// The WHATWG parser drops a port equal to the scheme default, so
|
|
// "stun:host:80" reaches the TypeScript's `: NAT_PREDICTION_BASE_PORT`
|
|
// fallback and counts as a base listener.
|
|
if port == defaultSchemePort("http") {
|
|
port = ""
|
|
}
|
|
if port != "" {
|
|
number, err := strconv.Atoi(port)
|
|
if err != nil || number != natPredictionBasePort {
|
|
continue
|
|
}
|
|
}
|
|
// WHATWG lowercases the host and keeps an IPv6 literal bracketed;
|
|
// url.URL.Hostname strips the brackets, so put them back.
|
|
hostname := strings.ToLower(authority.Hostname())
|
|
if strings.Contains(hostname, ":") {
|
|
hostname = "[" + hostname + "]"
|
|
}
|
|
derived := make([]string, 0, len(natPredictionAuxiliaryPorts))
|
|
for _, auxiliaryPort := range natPredictionAuxiliaryPorts {
|
|
derived = append(derived, fmt.Sprintf("stun:%s:%d", hostname, auxiliaryPort))
|
|
}
|
|
return derived
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// IceConfig ports createIceConfig.
|
|
func IceConfig(c Config) protocol.IceConfig {
|
|
// TS: options.natPredictionStunUrls ?? natPredictionStunUrls(stunUrls). A
|
|
// configured empty list (Local without prediction) is kept as-is; only an
|
|
// absent one is derived, so nil and empty differ here.
|
|
prediction := c.NATPredictionSTUNURLs
|
|
if prediction == nil {
|
|
prediction = NATPredictionStunURLs(c.STUNURLs)
|
|
}
|
|
config := protocol.IceConfig{
|
|
IceServers: []protocol.IceServer{},
|
|
NatPredictionStunURLs: []string{},
|
|
}
|
|
if len(c.STUNURLs) > 0 {
|
|
config.IceServers = []protocol.IceServer{
|
|
{URLs: protocol.IceServerURLList(append([]string{}, c.STUNURLs...)...)},
|
|
}
|
|
}
|
|
if c.NATPredictionEnabled {
|
|
config.NatPredictionStunURLs = append([]string{}, prediction...)
|
|
}
|
|
return config
|
|
}
|