moved
[gscan_quic.git] / quic.go
blobc742166d12284feff3d89377ee6fc1039d4c8680
1 package main
3 import (
4 "bytes"
5 "crypto/tls"
6 "io"
7 "io/ioutil"
8 "math/rand"
9 "net"
10 "net/http"
11 "strings"
12 "time"
14 quic "github.com/ipsn/go-ipfs/gxlibs/github.com/lucas-clemente/quic-go"
15 "github.com/ipsn/go-ipfs/gxlibs/github.com/lucas-clemente/quic-go/h2quic"
18 var errNoSuchBucket = []byte("<?xml version='1.0' encoding='UTF-8'?><Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist.</Message></Error>")
20 func testQuic(ip string, config *ScanConfig, record *ScanRecord) bool {
21 start := time.Now()
23 udpConn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
24 if err != nil {
25 return false
27 udpConn.SetDeadline(time.Now().Add(config.ScanMaxRTT))
28 defer udpConn.Close()
30 quicCfg := &quic.Config{
31 HandshakeTimeout: config.HandshakeTimeout,
32 KeepAlive: false,
35 var serverName string
36 if len(config.ServerName) == 0 {
37 serverName = randomHost()
38 } else {
39 serverName = config.ServerName[rand.Intn(len(config.ServerName))]
42 tlsCfg := &tls.Config{
43 InsecureSkipVerify: true,
44 ServerName: serverName,
47 udpAddr := &net.UDPAddr{IP: net.ParseIP(ip), Port: 443}
48 addr := net.JoinHostPort(serverName, "443")
49 quicSessn, err := quic.Dial(udpConn, udpAddr, addr, tlsCfg, quicCfg)
50 if err != nil {
51 return false
53 defer quicSessn.Close()
55 // lv1 只会验证证书是否存在
56 cs := quicSessn.ConnectionState()
57 if !cs.HandshakeComplete {
58 return false
60 pcs := cs.PeerCertificates
61 if len(pcs) < 2 {
62 return false
65 // lv2 验证证书是否正确
66 if config.Level > 1 {
67 pkp := pcs[1].RawSubjectPublicKeyInfo
68 if !bytes.Equal(g2pkp, pkp) && !bytes.Equal(g3pkp, pkp) { // && !bytes.Equal(g3ecc, pkp[:]) {
69 return false
73 // lv3 使用 http 访问来验证
74 if config.Level > 2 {
75 tr := &h2quic.RoundTripper{DisableCompression: true}
76 defer tr.Close()
78 tr.Dial = func(network, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.Session, error) {
79 return quicSessn, err
81 // 设置超时
82 hclient := &http.Client{
83 Transport: tr,
84 CheckRedirect: func(req *http.Request, via []*http.Request) error {
85 return http.ErrUseLastResponse
87 Timeout: config.ScanMaxRTT - time.Since(start),
89 url := "https://" + config.HTTPVerifyHosts[rand.Intn(len(config.HTTPVerifyHosts))]
90 req, _ := http.NewRequest(http.MethodGet, url, nil)
91 req.Close = true
92 resp, _ := hclient.Do(req)
93 if resp == nil || (resp.StatusCode < 200 || resp.StatusCode >= 400) || !strings.Contains(resp.Header.Get("Alt-Svc"), `quic=":443"`) {
94 return false
96 if resp.Body != nil {
97 defer resp.Body.Close()
98 // lv4 验证是否是 NoSuchBucket 错误
99 if config.Level > 3 && resp.Header.Get("Content-Type") == "application/xml; charset=UTF-8" { // 也许条件改为 || 更好
100 body, err := ioutil.ReadAll(resp.Body)
101 if err != nil || bytes.Equal(body, errNoSuchBucket) {
102 return false
104 } else {
105 io.Copy(ioutil.Discard, resp.Body)
110 if rtt := time.Since(start); rtt > config.ScanMinRTT {
111 record.RTT += rtt
112 return true
114 return false