Capture, decrypt, and restream toolkit with compiled-in app modules (RTE, TG4, BBC), on-device MITM proxy, streamd control plane, and www. BBC module.yaml is published (clear streams); other module values stay local.
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
utls "github.com/refraction-networking/utls"
|
|
)
|
|
|
|
// dialTLSChrome dials addr with a Chrome-like ClientHello so CDNs (Fastly)
|
|
// are less likely to HTTP 403 Go's default TLS fingerprint.
|
|
// Handshakes as HTTP/1.1 only so net/http can use the returned conn.
|
|
func dialTLSChrome(ctx context.Context, dialCtx func(context.Context, string, string) (net.Conn, error), network, addr string) (net.Conn, error) {
|
|
host := serverNameFromAddr(addr)
|
|
raw, err := dialCtx(ctx, network, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg := &utls.Config{
|
|
ServerName: host,
|
|
InsecureSkipVerify: true,
|
|
NextProtos: []string{"http/1.1"},
|
|
}
|
|
spec, err := utls.UTLSIdToSpec(utls.HelloChrome_120)
|
|
if err != nil {
|
|
_ = raw.Close()
|
|
return dialTLSStd(ctx, dialCtx, network, addr)
|
|
}
|
|
for i := range spec.Extensions {
|
|
if alpn, ok := spec.Extensions[i].(*utls.ALPNExtension); ok {
|
|
alpn.AlpnProtocols = []string{"http/1.1"}
|
|
}
|
|
}
|
|
uConn := utls.UClient(raw, cfg, utls.HelloCustom)
|
|
if err := uConn.ApplyPreset(&spec); err != nil {
|
|
_ = raw.Close()
|
|
return nil, fmt.Errorf("utls preset %s: %w", host, err)
|
|
}
|
|
deadline, ok := ctx.Deadline()
|
|
if !ok {
|
|
deadline = time.Now().Add(15 * time.Second)
|
|
}
|
|
_ = raw.SetDeadline(deadline)
|
|
if err := uConn.Handshake(); err != nil {
|
|
_ = raw.Close()
|
|
return nil, fmt.Errorf("utls handshake %s: %w", host, err)
|
|
}
|
|
_ = raw.SetDeadline(time.Time{})
|
|
return uConn, nil
|
|
}
|
|
|
|
func dialTLSStd(ctx context.Context, dialCtx func(context.Context, string, string) (net.Conn, error), network, addr string) (net.Conn, error) {
|
|
host := serverNameFromAddr(addr)
|
|
raw, err := dialCtx(ctx, network, addr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cfg := &tls.Config{ServerName: host, InsecureSkipVerify: true, NextProtos: []string{"http/1.1"}}
|
|
c := tls.Client(raw, cfg)
|
|
if err := c.HandshakeContext(ctx); err != nil {
|
|
_ = raw.Close()
|
|
return nil, err
|
|
}
|
|
return c, nil
|
|
}
|
|
|
|
func serverNameFromAddr(addr string) string {
|
|
host, _, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return strings.TrimSpace(addr)
|
|
}
|
|
return host
|
|
}
|