package nettools import ( "crypto/tls" "fmt" "golang.org/x/net/context" "net" "strconv" "time" ) type TlsPingResult struct { ConnectionTime int HandshakeTime int TLSVersion uint16 Err error IP net.IP } func (tlsR *TlsPingResult) Result() int { return tlsR.ConnectionTime + tlsR.HandshakeTime } func (tlsR *TlsPingResult) Error() error { return tlsR.Err } func (tlsR *TlsPingResult) String() string { if tlsR.Err != nil { return fmt.Sprintf("%s", tlsR.Err) } else { return fmt.Sprintf("%s: protocol=%s, connection=%d ms, handshake=%d ms, time=%d ms", tlsR.IP.String(), tlsVersionToString(tlsR.TLSVersion), tlsR.ConnectionTime, tlsR.HandshakeTime, tlsR.Result()) } } type TlsPing struct { Host string Port uint16 ConnectionTimeout time.Duration HandshakeTimeout time.Duration // 以下为可选参数 TlsVersion uint16 Insecure bool IP net.IP } func (tslC *TlsPing) Ping() IPingResult { return tslC.PingContext(context.Background()) } func (tslC *TlsPing) PingContext(ctx context.Context) IPingResult { ip := cloneIP(tslC.IP) if ip == nil { var err error ip, err = LookupFunc(tslC.Host) if err != nil { return tslC.errorResult(err) } } dialer := &net.Dialer{ Timeout: tslC.ConnectionTimeout, KeepAlive: -1, } t0 := time.Now() conn, err := dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), strconv.FormatUint(uint64(tslC.Port), 10))) if err != nil { return tslC.errorResult(err) } defer conn.Close() t1 := time.Now() config := &tls.Config{ ServerName: tslC.Host, MinVersion: tslC.TlsVersion, MaxVersion: tslC.TlsVersion, InsecureSkipVerify: tslC.Insecure, } client := tls.Client(conn, config) client.SetDeadline(time.Now().Add(tslC.HandshakeTimeout)) err = client.Handshake() if err != nil { return tslC.errorResult(err) } defer client.Close() t2 := time.Now() return &TlsPingResult{int(t1.Sub(t0).Milliseconds()), int(t2.Sub(t1).Milliseconds()), client.ConnectionState().Version, nil, ip} } func NewTlsPing(host string, port uint16, ct, ht time.Duration) *TlsPing { return &TlsPing{ Host: host, Port: port, ConnectionTimeout: ct, HandshakeTimeout: ht, } } func (tslC *TlsPing) errorResult(err error) *TlsPingResult { r := &TlsPingResult{} r.Err = err return r } var ( _ IPing = (*TlsPing)(nil) _ IPingResult = (*TlsPingResult)(nil) )