mirror of
https://github.com/coalaura/up.git
synced 2025-07-18 21:53:23 +00:00
http3 wip
This commit is contained in:
@ -8,13 +8,11 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PinnedCertificate struct {
|
||||
@ -135,31 +133,31 @@ func CertificateFingerprint(certificate *x509.Certificate) string {
|
||||
return fmt.Sprintf("%s-%s", algo, hex.EncodeToString(sum[:]))
|
||||
}
|
||||
|
||||
func PreFetchServerCertificate(store *CertificateStore, addr string) error {
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}, "tcp", addr, &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
})
|
||||
func PreFetchServerCertificate(store *CertificateStore, hostname string, useHttp3 bool) error {
|
||||
addr, err := EnsurePort(hostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
name string
|
||||
certificate *x509.Certificate
|
||||
)
|
||||
|
||||
if useHttp3 {
|
||||
certificate, name, err = ResolveTLSCertificateHttp3(addr)
|
||||
} else {
|
||||
certificate, name, err = ResolveTLSCertificateHttp2(addr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return fmt.Errorf("no peer certificates")
|
||||
}
|
||||
|
||||
certificate := state.PeerCertificates[0]
|
||||
|
||||
if certificate.Subject.CommonName != "up" {
|
||||
return errors.New("invalid certificate subject")
|
||||
}
|
||||
|
||||
name := state.ServerName
|
||||
fingerprint := CertificateFingerprint(certificate)
|
||||
|
||||
if store.IsPinned(name, fingerprint) {
|
||||
@ -180,12 +178,8 @@ func PreFetchServerCertificate(store *CertificateStore, addr string) error {
|
||||
return store.Pin(name, fingerprint)
|
||||
}
|
||||
|
||||
func NewPinnedClient(store *CertificateStore) *http.Client {
|
||||
config := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
|
||||
config.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||
func NewPinnedClient(store *CertificateStore, useHttp3 bool) *http.Client {
|
||||
return NewHttpClient(func(cs tls.ConnectionState) error {
|
||||
if len(cs.PeerCertificates) == 0 {
|
||||
return errors.New("missing certificate")
|
||||
}
|
||||
@ -204,16 +198,5 @@ func NewPinnedClient(store *CertificateStore) *http.Client {
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Transport: &http.Transport{
|
||||
TLSClientConfig: config,
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
IdleConnTimeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}, useHttp3)
|
||||
}
|
||||
|
@ -75,10 +75,9 @@ func Run(_ context.Context, cmd *cli.Command) error {
|
||||
}
|
||||
|
||||
if found, _ := cfg.Get(hostname, "HostName"); found != "" {
|
||||
hostname = found
|
||||
|
||||
if index := strings.Index(hostname, ":"); index != -1 {
|
||||
hostname = hostname[:index]
|
||||
hostname, err = StripPort(found)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,11 +118,19 @@ func Run(_ context.Context, cmd *cli.Command) error {
|
||||
return fmt.Errorf("failed to load certificate store: %v", err)
|
||||
}
|
||||
|
||||
if err = PreFetchServerCertificate(store, hostname); err != nil {
|
||||
useHttp3 := cmd.Bool("http3")
|
||||
|
||||
if useHttp3 {
|
||||
log.Println("Using http3 over udp")
|
||||
} else {
|
||||
log.Println("Using http2 over tcp")
|
||||
}
|
||||
|
||||
if err = PreFetchServerCertificate(store, hostname, useHttp3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client := NewPinnedClient(store)
|
||||
client := NewPinnedClient(store, useHttp3)
|
||||
|
||||
log.Println("Requesting challenge...")
|
||||
|
||||
|
118
client/http.go
Normal file
118
client/http.go
Normal file
@ -0,0 +1,118 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
)
|
||||
|
||||
func GetHttp2Transport(verify func(tls.ConnectionState) error) *http.Transport {
|
||||
return &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS13,
|
||||
NextProtos: []string{"h2"},
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: verify,
|
||||
},
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}).Dial,
|
||||
TLSHandshakeTimeout: 5 * time.Second,
|
||||
IdleConnTimeout: 10 * time.Second,
|
||||
ForceAttemptHTTP2: true,
|
||||
}
|
||||
}
|
||||
|
||||
func GetHttp3Transport(verify func(tls.ConnectionState) error) *http3.Transport {
|
||||
return &http3.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
MinVersion: tls.VersionTLS13,
|
||||
NextProtos: []string{http3.NextProtoH3},
|
||||
InsecureSkipVerify: true,
|
||||
VerifyConnection: verify,
|
||||
},
|
||||
QUICConfig: &quic.Config{
|
||||
HandshakeIdleTimeout: 5 * time.Second,
|
||||
MaxIdleTimeout: 10 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewHttpClient(verify func(tls.ConnectionState) error, useHttp3 bool) *http.Client {
|
||||
var transport http.RoundTripper
|
||||
|
||||
if useHttp3 {
|
||||
transport = GetHttp2Transport(verify)
|
||||
} else {
|
||||
transport = GetHttp3Transport(verify)
|
||||
}
|
||||
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
}
|
||||
}
|
||||
|
||||
func ResolveTLSCertificateHttp2(addr string) (*x509.Certificate, string, error) {
|
||||
transport := GetHttp2Transport(nil)
|
||||
|
||||
conn, err := tls.DialWithDialer(&net.Dialer{
|
||||
Timeout: 5 * time.Second,
|
||||
}, "tcp", addr, transport.TLSClientConfig)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
defer conn.Close()
|
||||
|
||||
state := conn.ConnectionState()
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return nil, "", errors.New("no peer certificates")
|
||||
}
|
||||
|
||||
return state.PeerCertificates[0], state.ServerName, nil
|
||||
}
|
||||
|
||||
func ResolveTLSCertificateHttp3(addr string) (*x509.Certificate, string, error) {
|
||||
transport := GetHttp3Transport(nil)
|
||||
|
||||
conn, err := quic.DialAddr(context.Background(), addr, transport.TLSClientConfig, transport.QUICConfig)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
defer conn.CloseWithError(quic.ApplicationErrorCode(0), "")
|
||||
|
||||
state := conn.ConnectionState().TLS
|
||||
if len(state.PeerCertificates) == 0 {
|
||||
return nil, "", errors.New("no peer certificates")
|
||||
}
|
||||
|
||||
return state.PeerCertificates[0], state.ServerName, nil
|
||||
}
|
||||
|
||||
func StripPort(hostname string) (string, error) {
|
||||
host, _, err := net.SplitHostPort(hostname)
|
||||
|
||||
return host, err
|
||||
}
|
||||
|
||||
func EnsurePort(hostname string) (string, error) {
|
||||
host, port, err := net.SplitHostPort(hostname)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if port == "" {
|
||||
port = "443"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%s", host, port), err
|
||||
}
|
Reference in New Issue
Block a user