TUN-813: Clean up cloudflared dependencies

This commit is contained in:
Areg Harutyunyan
2018-07-24 18:04:33 -05:00
parent d06fc520c7
commit 0468866626
3310 changed files with 993 additions and 1223303 deletions

View File

@@ -1,53 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd netbsd openbsd
package nettest
import (
"runtime"
"strconv"
"strings"
"syscall"
)
var darwinVersion int
func init() {
if runtime.GOOS == "darwin" {
// See http://support.apple.com/kb/HT1633.
s, err := syscall.Sysctl("kern.osrelease")
if err != nil {
return
}
ss := strings.Split(s, ".")
if len(ss) == 0 {
return
}
darwinVersion, _ = strconv.Atoi(ss[0])
}
}
func supportsIPv6MulticastDeliveryOnLoopback() bool {
switch runtime.GOOS {
case "freebsd":
// See http://www.freebsd.org/cgi/query-pr.cgi?pr=180065.
// Even after the fix, it looks like the latest
// kernels don't deliver link-local scoped multicast
// packets correctly.
return false
case "darwin":
return !causesIPv6Crash()
default:
return true
}
}
func causesIPv6Crash() bool {
// We see some kernel crash when running IPv6 with IP-level
// options on Darwin kernel version 12 or below.
// See golang.org/issues/17015.
return darwinVersion < 13
}

View File

@@ -1,15 +0,0 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux solaris
package nettest
func supportsIPv6MulticastDeliveryOnLoopback() bool {
return true
}
func causesIPv6Crash() bool {
return false
}

View File

@@ -1,31 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package nettest
import (
"os"
"syscall"
)
func protocolNotSupported(err error) bool {
switch err := err.(type) {
case syscall.Errno:
switch err {
case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
return true
}
case *os.SyscallError:
switch err := err.Err.(type) {
case syscall.Errno:
switch err {
case syscall.EPROTONOSUPPORT, syscall.ENOPROTOOPT:
return true
}
}
}
return false
}

View File

@@ -1,32 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js,wasm nacl plan9
package nettest
import (
"fmt"
"runtime"
)
func maxOpenFiles() int {
return defaultMaxOpenFiles
}
func supportsRawIPSocket() (string, bool) {
return fmt.Sprintf("not supported on %s", runtime.GOOS), false
}
func supportsIPv6MulticastDeliveryOnLoopback() bool {
return false
}
func causesIPv6Crash() bool {
return false
}
func protocolNotSupported(err error) bool {
return false
}

View File

@@ -1,29 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package nettest
import (
"fmt"
"os"
"runtime"
"syscall"
)
func maxOpenFiles() int {
var rlim syscall.Rlimit
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil {
return defaultMaxOpenFiles
}
return int(rlim.Cur)
}
func supportsRawIPSocket() (string, bool) {
if os.Getuid() != 0 {
return fmt.Sprintf("must be root on %s", runtime.GOOS), false
}
return "", true
}

View File

@@ -1,42 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nettest
import (
"fmt"
"runtime"
"syscall"
)
func maxOpenFiles() int {
return 4 * defaultMaxOpenFiles /* actually it's 16581375 */
}
func supportsRawIPSocket() (string, bool) {
// From http://msdn.microsoft.com/en-us/library/windows/desktop/ms740548.aspx:
// Note: To use a socket of type SOCK_RAW requires administrative privileges.
// Users running Winsock applications that use raw sockets must be a member of
// the Administrators group on the local computer, otherwise raw socket calls
// will fail with an error code of WSAEACCES. On Windows Vista and later, access
// for raw sockets is enforced at socket creation. In earlier versions of Windows,
// access for raw sockets is enforced during other socket operations.
s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, 0)
if err == syscall.WSAEACCES {
return fmt.Sprintf("no access to raw socket allowed on %s", runtime.GOOS), false
}
if err != nil {
return err.Error(), false
}
syscall.Closesocket(s)
return "", true
}
func supportsIPv6MulticastDeliveryOnLoopback() bool {
return true
}
func causesIPv6Crash() bool {
return false
}

View File

@@ -1,94 +0,0 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nettest
import "net"
// IsMulticastCapable reports whether ifi is an IP multicast-capable
// network interface. Network must be "ip", "ip4" or "ip6".
func IsMulticastCapable(network string, ifi *net.Interface) (net.IP, bool) {
switch network {
case "ip", "ip4", "ip6":
default:
return nil, false
}
if ifi == nil || ifi.Flags&net.FlagUp == 0 || ifi.Flags&net.FlagMulticast == 0 {
return nil, false
}
return hasRoutableIP(network, ifi)
}
// RoutedInterface returns a network interface that can route IP
// traffic and satisfies flags. It returns nil when an appropriate
// network interface is not found. Network must be "ip", "ip4" or
// "ip6".
func RoutedInterface(network string, flags net.Flags) *net.Interface {
switch network {
case "ip", "ip4", "ip6":
default:
return nil
}
ift, err := net.Interfaces()
if err != nil {
return nil
}
for _, ifi := range ift {
if ifi.Flags&flags != flags {
continue
}
if _, ok := hasRoutableIP(network, &ifi); !ok {
continue
}
return &ifi
}
return nil
}
func hasRoutableIP(network string, ifi *net.Interface) (net.IP, bool) {
ifat, err := ifi.Addrs()
if err != nil {
return nil, false
}
for _, ifa := range ifat {
switch ifa := ifa.(type) {
case *net.IPAddr:
if ip := routableIP(network, ifa.IP); ip != nil {
return ip, true
}
case *net.IPNet:
if ip := routableIP(network, ifa.IP); ip != nil {
return ip, true
}
}
}
return nil, false
}
func routableIP(network string, ip net.IP) net.IP {
if !ip.IsLoopback() && !ip.IsLinkLocalUnicast() && !ip.IsGlobalUnicast() {
return nil
}
switch network {
case "ip4":
if ip := ip.To4(); ip != nil {
return ip
}
case "ip6":
if ip.IsLoopback() { // addressing scope of the loopback address depends on each implementation
return nil
}
if ip := ip.To16(); ip != nil && ip.To4() == nil {
return ip
}
default:
if ip := ip.To4(); ip != nil {
return ip
}
if ip := ip.To16(); ip != nil {
return ip
}
}
return nil
}

View File

@@ -1,11 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package nettest
const defaultMaxOpenFiles = 256
// MaxOpenFiles returns the maximum number of open files for the
// caller's process.
func MaxOpenFiles() int { return maxOpenFiles() }

View File

@@ -1,152 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package nettest provides utilities for network testing.
package nettest // import "golang.org/x/net/internal/nettest"
import (
"fmt"
"io/ioutil"
"net"
"os"
"runtime"
)
var (
supportsIPv4 bool
supportsIPv6 bool
)
func init() {
if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
ln.Close()
supportsIPv4 = true
}
if ln, err := net.Listen("tcp6", "[::1]:0"); err == nil {
ln.Close()
supportsIPv6 = true
}
}
// SupportsIPv4 reports whether the platform supports IPv4 networking
// functionality.
func SupportsIPv4() bool { return supportsIPv4 }
// SupportsIPv6 reports whether the platform supports IPv6 networking
// functionality.
func SupportsIPv6() bool { return supportsIPv6 }
// SupportsRawIPSocket reports whether the platform supports raw IP
// sockets.
func SupportsRawIPSocket() (string, bool) {
return supportsRawIPSocket()
}
// SupportsIPv6MulticastDeliveryOnLoopback reports whether the
// platform supports IPv6 multicast packet delivery on software
// loopback interface.
func SupportsIPv6MulticastDeliveryOnLoopback() bool {
return supportsIPv6MulticastDeliveryOnLoopback()
}
// ProtocolNotSupported reports whether err is a protocol not
// supported error.
func ProtocolNotSupported(err error) bool {
return protocolNotSupported(err)
}
// TestableNetwork reports whether network is testable on the current
// platform configuration.
func TestableNetwork(network string) bool {
// This is based on logic from standard library's
// net/platform_test.go.
switch network {
case "unix", "unixgram":
switch runtime.GOOS {
case "android", "js", "nacl", "plan9", "windows":
return false
}
if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
return false
}
case "unixpacket":
switch runtime.GOOS {
case "android", "darwin", "freebsd", "js", "nacl", "plan9", "windows":
return false
case "netbsd":
// It passes on amd64 at least. 386 fails (Issue 22927). arm is unknown.
if runtime.GOARCH == "386" {
return false
}
}
}
return true
}
// NewLocalListener returns a listener which listens to a loopback IP
// address or local file system path.
// Network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
func NewLocalListener(network string) (net.Listener, error) {
switch network {
case "tcp":
if supportsIPv4 {
if ln, err := net.Listen("tcp4", "127.0.0.1:0"); err == nil {
return ln, nil
}
}
if supportsIPv6 {
return net.Listen("tcp6", "[::1]:0")
}
case "tcp4":
if supportsIPv4 {
return net.Listen("tcp4", "127.0.0.1:0")
}
case "tcp6":
if supportsIPv6 {
return net.Listen("tcp6", "[::1]:0")
}
case "unix", "unixpacket":
return net.Listen(network, localPath())
}
return nil, fmt.Errorf("%s is not supported", network)
}
// NewLocalPacketListener returns a packet listener which listens to a
// loopback IP address or local file system path.
// Network must be "udp", "udp4", "udp6" or "unixgram".
func NewLocalPacketListener(network string) (net.PacketConn, error) {
switch network {
case "udp":
if supportsIPv4 {
if c, err := net.ListenPacket("udp4", "127.0.0.1:0"); err == nil {
return c, nil
}
}
if supportsIPv6 {
return net.ListenPacket("udp6", "[::1]:0")
}
case "udp4":
if supportsIPv4 {
return net.ListenPacket("udp4", "127.0.0.1:0")
}
case "udp6":
if supportsIPv6 {
return net.ListenPacket("udp6", "[::1]:0")
}
case "unixgram":
return net.ListenPacket(network, localPath())
}
return nil, fmt.Errorf("%s is not supported", network)
}
func localPath() string {
f, err := ioutil.TempFile("", "nettest")
if err != nil {
panic(err)
}
path := f.Name()
f.Close()
os.Remove(path)
return path
}

View File

@@ -1,259 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build go1.9
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package socket_test
import (
"bytes"
"fmt"
"net"
"runtime"
"testing"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/internal/socket"
)
type mockControl struct {
Level int
Type int
Data []byte
}
func TestControlMessage(t *testing.T) {
for _, tt := range []struct {
cs []mockControl
}{
{
[]mockControl{
{Level: 1, Type: 1},
},
},
{
[]mockControl{
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
{
[]mockControl{
{Level: 3, Type: 3, Data: []byte{0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
},
},
{
[]mockControl{
{Level: 4, Type: 4, Data: []byte{0xfe, 0xff, 0xff, 0xfe, 0xfe, 0xff, 0xff, 0xfe}},
{Level: 2, Type: 2, Data: []byte{0xfe}},
},
},
} {
var w []byte
var tailPadLen int
mm := socket.NewControlMessage([]int{0})
for i, c := range tt.cs {
m := socket.NewControlMessage([]int{len(c.Data)})
l := len(m) - len(mm)
if i == len(tt.cs)-1 && l > len(c.Data) {
tailPadLen = l - len(c.Data)
}
w = append(w, m...)
}
var err error
ww := make([]byte, len(w))
copy(ww, w)
m := socket.ControlMessage(ww)
for _, c := range tt.cs {
if err = m.MarshalHeader(c.Level, c.Type, len(c.Data)); err != nil {
t.Fatalf("(%v).MarshalHeader() = %v", tt.cs, err)
}
copy(m.Data(len(c.Data)), c.Data)
m = m.Next(len(c.Data))
}
m = socket.ControlMessage(w)
for _, c := range tt.cs {
m, err = m.Marshal(c.Level, c.Type, c.Data)
if err != nil {
t.Fatalf("(%v).Marshal() = %v", tt.cs, err)
}
}
if !bytes.Equal(ww, w) {
t.Fatalf("got %#v; want %#v", ww, w)
}
ws := [][]byte{w}
if tailPadLen > 0 {
// Test a message with no tail padding.
nopad := w[:len(w)-tailPadLen]
ws = append(ws, [][]byte{nopad}...)
}
for _, w := range ws {
ms, err := socket.ControlMessage(w).Parse()
if err != nil {
t.Fatalf("(%v).Parse() = %v", tt.cs, err)
}
for i, m := range ms {
lvl, typ, dataLen, err := m.ParseHeader()
if err != nil {
t.Fatalf("(%v).ParseHeader() = %v", tt.cs, err)
}
if lvl != tt.cs[i].Level || typ != tt.cs[i].Type || dataLen != len(tt.cs[i].Data) {
t.Fatalf("%v: got %d, %d, %d; want %d, %d, %d", tt.cs[i], lvl, typ, dataLen, tt.cs[i].Level, tt.cs[i].Type, len(tt.cs[i].Data))
}
}
}
}
}
func TestUDP(t *testing.T) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
cc, err := socket.NewConn(c.(net.Conn))
if err != nil {
t.Fatal(err)
}
t.Run("Message", func(t *testing.T) {
data := []byte("HELLO-R-U-THERE")
wm := socket.Message{
Buffers: bytes.SplitAfter(data, []byte("-")),
Addr: c.LocalAddr(),
}
if err := cc.SendMsg(&wm, 0); err != nil {
t.Fatal(err)
}
b := make([]byte, 32)
rm := socket.Message{
Buffers: [][]byte{b[:1], b[1:3], b[3:7], b[7:11], b[11:]},
}
if err := cc.RecvMsg(&rm, 0); err != nil {
t.Fatal(err)
}
if !bytes.Equal(b[:rm.N], data) {
t.Fatalf("got %#v; want %#v", b[:rm.N], data)
}
})
switch runtime.GOOS {
case "android", "linux":
t.Run("Messages", func(t *testing.T) {
data := []byte("HELLO-R-U-THERE")
wmbs := bytes.SplitAfter(data, []byte("-"))
wms := []socket.Message{
{Buffers: wmbs[:1], Addr: c.LocalAddr()},
{Buffers: wmbs[1:], Addr: c.LocalAddr()},
}
n, err := cc.SendMsgs(wms, 0)
if err != nil {
t.Fatal(err)
}
if n != len(wms) {
t.Fatalf("got %d; want %d", n, len(wms))
}
b := make([]byte, 32)
rmbs := [][][]byte{{b[:len(wmbs[0])]}, {b[len(wmbs[0]):]}}
rms := []socket.Message{
{Buffers: rmbs[0]},
{Buffers: rmbs[1]},
}
n, err = cc.RecvMsgs(rms, 0)
if err != nil {
t.Fatal(err)
}
if n != len(rms) {
t.Fatalf("got %d; want %d", n, len(rms))
}
nn := 0
for i := 0; i < n; i++ {
nn += rms[i].N
}
if !bytes.Equal(b[:nn], data) {
t.Fatalf("got %#v; want %#v", b[:nn], data)
}
})
}
// The behavior of transmission for zero byte paylaod depends
// on each platform implementation. Some may transmit only
// protocol header and options, other may transmit nothing.
// We test only that SendMsg and SendMsgs will not crash with
// empty buffers.
wm := socket.Message{
Buffers: [][]byte{{}},
Addr: c.LocalAddr(),
}
cc.SendMsg(&wm, 0)
wms := []socket.Message{
{Buffers: [][]byte{{}}, Addr: c.LocalAddr()},
}
cc.SendMsgs(wms, 0)
}
func BenchmarkUDP(b *testing.B) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
b.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
cc, err := socket.NewConn(c.(net.Conn))
if err != nil {
b.Fatal(err)
}
data := []byte("HELLO-R-U-THERE")
wm := socket.Message{
Buffers: [][]byte{data},
Addr: c.LocalAddr(),
}
rm := socket.Message{
Buffers: [][]byte{make([]byte, 128)},
OOB: make([]byte, 128),
}
for M := 1; M <= 1<<9; M = M << 1 {
b.Run(fmt.Sprintf("Iter-%d", M), func(b *testing.B) {
for i := 0; i < b.N; i++ {
for j := 0; j < M; j++ {
if err := cc.SendMsg(&wm, 0); err != nil {
b.Fatal(err)
}
if err := cc.RecvMsg(&rm, 0); err != nil {
b.Fatal(err)
}
}
}
})
switch runtime.GOOS {
case "android", "linux":
wms := make([]socket.Message, M)
for i := range wms {
wms[i].Buffers = [][]byte{data}
wms[i].Addr = c.LocalAddr()
}
rms := make([]socket.Message, M)
for i := range rms {
rms[i].Buffers = [][]byte{make([]byte, 128)}
rms[i].OOB = make([]byte, 128)
}
b.Run(fmt.Sprintf("Batch-%d", M), func(b *testing.B) {
for i := 0; i < b.N; i++ {
if _, err := cc.SendMsgs(wms, 0); err != nil {
b.Fatal(err)
}
if _, err := cc.RecvMsgs(rms, 0); err != nil {
b.Fatal(err)
}
}
})
}
}
}

View File

@@ -1,46 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris windows
package socket_test
import (
"net"
"runtime"
"syscall"
"testing"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/internal/socket"
)
func TestSocket(t *testing.T) {
t.Run("Option", func(t *testing.T) {
testSocketOption(t, &socket.Option{Level: syscall.SOL_SOCKET, Name: syscall.SO_RCVBUF, Len: 4})
})
}
func testSocketOption(t *testing.T, so *socket.Option) {
c, err := nettest.NewLocalPacketListener("udp")
if err != nil {
t.Skipf("not supported on %s/%s: %v", runtime.GOOS, runtime.GOARCH, err)
}
defer c.Close()
cc, err := socket.NewConn(c.(net.Conn))
if err != nil {
t.Fatal(err)
}
const N = 2048
if err := so.SetInt(cc, N); err != nil {
t.Fatal(err)
}
n, err := so.GetInt(cc)
if err != nil {
t.Fatal(err)
}
if n < N {
t.Fatalf("got %d; want greater than or equal to %d", n, N)
}
}

View File

@@ -1,168 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socks
import (
"context"
"errors"
"io"
"net"
"strconv"
"time"
)
var (
noDeadline = time.Time{}
aLongTimeAgo = time.Unix(1, 0)
)
func (d *Dialer) connect(ctx context.Context, c net.Conn, address string) (_ net.Addr, ctxErr error) {
host, port, err := splitHostPort(address)
if err != nil {
return nil, err
}
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
c.SetDeadline(deadline)
defer c.SetDeadline(noDeadline)
}
if ctx != context.Background() {
errCh := make(chan error, 1)
done := make(chan struct{})
defer func() {
close(done)
if ctxErr == nil {
ctxErr = <-errCh
}
}()
go func() {
select {
case <-ctx.Done():
c.SetDeadline(aLongTimeAgo)
errCh <- ctx.Err()
case <-done:
errCh <- nil
}
}()
}
b := make([]byte, 0, 6+len(host)) // the size here is just an estimate
b = append(b, Version5)
if len(d.AuthMethods) == 0 || d.Authenticate == nil {
b = append(b, 1, byte(AuthMethodNotRequired))
} else {
ams := d.AuthMethods
if len(ams) > 255 {
return nil, errors.New("too many authentication methods")
}
b = append(b, byte(len(ams)))
for _, am := range ams {
b = append(b, byte(am))
}
}
if _, ctxErr = c.Write(b); ctxErr != nil {
return
}
if _, ctxErr = io.ReadFull(c, b[:2]); ctxErr != nil {
return
}
if b[0] != Version5 {
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
}
am := AuthMethod(b[1])
if am == AuthMethodNoAcceptableMethods {
return nil, errors.New("no acceptable authentication methods")
}
if d.Authenticate != nil {
if ctxErr = d.Authenticate(ctx, c, am); ctxErr != nil {
return
}
}
b = b[:0]
b = append(b, Version5, byte(d.cmd), 0)
if ip := net.ParseIP(host); ip != nil {
if ip4 := ip.To4(); ip4 != nil {
b = append(b, AddrTypeIPv4)
b = append(b, ip4...)
} else if ip6 := ip.To16(); ip6 != nil {
b = append(b, AddrTypeIPv6)
b = append(b, ip6...)
} else {
return nil, errors.New("unknown address type")
}
} else {
if len(host) > 255 {
return nil, errors.New("FQDN too long")
}
b = append(b, AddrTypeFQDN)
b = append(b, byte(len(host)))
b = append(b, host...)
}
b = append(b, byte(port>>8), byte(port))
if _, ctxErr = c.Write(b); ctxErr != nil {
return
}
if _, ctxErr = io.ReadFull(c, b[:4]); ctxErr != nil {
return
}
if b[0] != Version5 {
return nil, errors.New("unexpected protocol version " + strconv.Itoa(int(b[0])))
}
if cmdErr := Reply(b[1]); cmdErr != StatusSucceeded {
return nil, errors.New("unknown error " + cmdErr.String())
}
if b[2] != 0 {
return nil, errors.New("non-zero reserved field")
}
l := 2
var a Addr
switch b[3] {
case AddrTypeIPv4:
l += net.IPv4len
a.IP = make(net.IP, net.IPv4len)
case AddrTypeIPv6:
l += net.IPv6len
a.IP = make(net.IP, net.IPv6len)
case AddrTypeFQDN:
if _, err := io.ReadFull(c, b[:1]); err != nil {
return nil, err
}
l += int(b[0])
default:
return nil, errors.New("unknown address type " + strconv.Itoa(int(b[3])))
}
if cap(b) < l {
b = make([]byte, l)
} else {
b = b[:l]
}
if _, ctxErr = io.ReadFull(c, b); ctxErr != nil {
return
}
if a.IP != nil {
copy(a.IP, b)
} else {
a.Name = string(b[:len(b)-2])
}
a.Port = int(b[len(b)-2])<<8 | int(b[len(b)-1])
return &a, nil
}
func splitHostPort(address string) (string, int, error) {
host, port, err := net.SplitHostPort(address)
if err != nil {
return "", 0, err
}
portnum, err := strconv.Atoi(port)
if err != nil {
return "", 0, err
}
if 1 > portnum || portnum > 0xffff {
return "", 0, errors.New("port number out of range " + port)
}
return host, portnum, nil
}

View File

@@ -1,170 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package socks_test
import (
"context"
"io"
"math/rand"
"net"
"os"
"testing"
"time"
"golang.org/x/net/internal/socks"
"golang.org/x/net/internal/sockstest"
)
func TestDial(t *testing.T) {
t.Run("Connect", func(t *testing.T) {
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
d.AuthMethods = []socks.AuthMethod{
socks.AuthMethodNotRequired,
socks.AuthMethodUsernamePassword,
}
d.Authenticate = (&socks.UsernamePassword{
Username: "username",
Password: "password",
}).Authenticate
c, err := d.DialContext(context.Background(), ss.TargetAddr().Network(), ss.TargetAddr().String())
if err != nil {
t.Fatal(err)
}
c.(*socks.Conn).BoundAddr()
c.Close()
})
t.Run("ConnectWithConn", func(t *testing.T) {
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, sockstest.NoProxyRequired)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
c, err := net.Dial(ss.Addr().Network(), ss.Addr().String())
if err != nil {
t.Fatal(err)
}
defer c.Close()
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
d.AuthMethods = []socks.AuthMethod{
socks.AuthMethodNotRequired,
socks.AuthMethodUsernamePassword,
}
d.Authenticate = (&socks.UsernamePassword{
Username: "username",
Password: "password",
}).Authenticate
a, err := d.DialWithConn(context.Background(), c, ss.TargetAddr().Network(), ss.TargetAddr().String())
if err != nil {
t.Fatal(err)
}
if _, ok := a.(*socks.Addr); !ok {
t.Fatalf("got %+v; want socks.Addr", a)
}
})
t.Run("Cancel", func(t *testing.T) {
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
dialErr := make(chan error)
go func() {
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
if err == nil {
c.Close()
}
dialErr <- err
}()
time.Sleep(100 * time.Millisecond)
cancel()
err = <-dialErr
if perr, nerr := parseDialError(err); perr != context.Canceled && nerr == nil {
t.Fatalf("got %v; want context.Canceled or equivalent", err)
}
})
t.Run("Deadline", func(t *testing.T) {
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, blackholeCmdFunc)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))
defer cancel()
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
if err == nil {
c.Close()
}
if perr, nerr := parseDialError(err); perr != context.DeadlineExceeded && nerr == nil {
t.Fatalf("got %v; want context.DeadlineExceeded or equivalent", err)
}
})
t.Run("WithRogueServer", func(t *testing.T) {
ss, err := sockstest.NewServer(sockstest.NoAuthRequired, rogueCmdFunc)
if err != nil {
t.Fatal(err)
}
defer ss.Close()
d := socks.NewDialer(ss.Addr().Network(), ss.Addr().String())
for i := 0; i < 2*len(rogueCmdList); i++ {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(100*time.Millisecond))
defer cancel()
c, err := d.DialContext(ctx, ss.TargetAddr().Network(), ss.TargetAddr().String())
if err == nil {
t.Log(c.(*socks.Conn).BoundAddr())
c.Close()
t.Error("should fail")
}
}
})
}
func blackholeCmdFunc(rw io.ReadWriter, b []byte) error {
if _, err := sockstest.ParseCmdRequest(b); err != nil {
return err
}
var bb [1]byte
for {
if _, err := rw.Read(bb[:]); err != nil {
return err
}
}
}
func rogueCmdFunc(rw io.ReadWriter, b []byte) error {
if _, err := sockstest.ParseCmdRequest(b); err != nil {
return err
}
rw.Write(rogueCmdList[rand.Intn(len(rogueCmdList))])
return nil
}
var rogueCmdList = [][]byte{
{0x05},
{0x06, 0x00, 0x00, 0x01, 192, 0, 2, 1, 0x17, 0x4b},
{0x05, 0x00, 0xff, 0x01, 192, 0, 2, 2, 0x17, 0x4b},
{0x05, 0x00, 0x00, 0x01, 192, 0, 2, 3},
{0x05, 0x00, 0x00, 0x03, 0x04, 'F', 'Q', 'D', 'N'},
}
func parseDialError(err error) (perr, nerr error) {
if e, ok := err.(*net.OpError); ok {
err = e.Err
nerr = e
}
if e, ok := err.(*os.SyscallError); ok {
err = e.Err
}
perr = err
return
}

View File

@@ -1,316 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package socks provides a SOCKS version 5 client implementation.
//
// SOCKS protocol version 5 is defined in RFC 1928.
// Username/Password authentication for SOCKS version 5 is defined in
// RFC 1929.
package socks
import (
"context"
"errors"
"io"
"net"
"strconv"
)
// A Command represents a SOCKS command.
type Command int
func (cmd Command) String() string {
switch cmd {
case CmdConnect:
return "socks connect"
case cmdBind:
return "socks bind"
default:
return "socks " + strconv.Itoa(int(cmd))
}
}
// An AuthMethod represents a SOCKS authentication method.
type AuthMethod int
// A Reply represents a SOCKS command reply code.
type Reply int
func (code Reply) String() string {
switch code {
case StatusSucceeded:
return "succeeded"
case 0x01:
return "general SOCKS server failure"
case 0x02:
return "connection not allowed by ruleset"
case 0x03:
return "network unreachable"
case 0x04:
return "host unreachable"
case 0x05:
return "connection refused"
case 0x06:
return "TTL expired"
case 0x07:
return "command not supported"
case 0x08:
return "address type not supported"
default:
return "unknown code: " + strconv.Itoa(int(code))
}
}
// Wire protocol constants.
const (
Version5 = 0x05
AddrTypeIPv4 = 0x01
AddrTypeFQDN = 0x03
AddrTypeIPv6 = 0x04
CmdConnect Command = 0x01 // establishes an active-open forward proxy connection
cmdBind Command = 0x02 // establishes a passive-open forward proxy connection
AuthMethodNotRequired AuthMethod = 0x00 // no authentication required
AuthMethodUsernamePassword AuthMethod = 0x02 // use username/password
AuthMethodNoAcceptableMethods AuthMethod = 0xff // no acceptable authentication methods
StatusSucceeded Reply = 0x00
)
// An Addr represents a SOCKS-specific address.
// Either Name or IP is used exclusively.
type Addr struct {
Name string // fully-qualified domain name
IP net.IP
Port int
}
func (a *Addr) Network() string { return "socks" }
func (a *Addr) String() string {
if a == nil {
return "<nil>"
}
port := strconv.Itoa(a.Port)
if a.IP == nil {
return net.JoinHostPort(a.Name, port)
}
return net.JoinHostPort(a.IP.String(), port)
}
// A Conn represents a forward proxy connection.
type Conn struct {
net.Conn
boundAddr net.Addr
}
// BoundAddr returns the address assigned by the proxy server for
// connecting to the command target address from the proxy server.
func (c *Conn) BoundAddr() net.Addr {
if c == nil {
return nil
}
return c.boundAddr
}
// A Dialer holds SOCKS-specific options.
type Dialer struct {
cmd Command // either CmdConnect or cmdBind
proxyNetwork string // network between a proxy server and a client
proxyAddress string // proxy server address
// ProxyDial specifies the optional dial function for
// establishing the transport connection.
ProxyDial func(context.Context, string, string) (net.Conn, error)
// AuthMethods specifies the list of request authention
// methods.
// If empty, SOCKS client requests only AuthMethodNotRequired.
AuthMethods []AuthMethod
// Authenticate specifies the optional authentication
// function. It must be non-nil when AuthMethods is not empty.
// It must return an error when the authentication is failed.
Authenticate func(context.Context, io.ReadWriter, AuthMethod) error
}
// DialContext connects to the provided address on the provided
// network.
//
// The returned error value may be a net.OpError. When the Op field of
// net.OpError contains "socks", the Source field contains a proxy
// server address and the Addr field contains a command target
// address.
//
// See func Dial of the net package of standard library for a
// description of the network and address parameters.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if ctx == nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
}
var err error
var c net.Conn
if d.ProxyDial != nil {
c, err = d.ProxyDial(ctx, d.proxyNetwork, d.proxyAddress)
} else {
var dd net.Dialer
c, err = dd.DialContext(ctx, d.proxyNetwork, d.proxyAddress)
}
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
a, err := d.connect(ctx, c, address)
if err != nil {
c.Close()
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
return &Conn{Conn: c, boundAddr: a}, nil
}
// DialWithConn initiates a connection from SOCKS server to the target
// network and address using the connection c that is already
// connected to the SOCKS server.
//
// It returns the connection's local address assigned by the SOCKS
// server.
func (d *Dialer) DialWithConn(ctx context.Context, c net.Conn, network, address string) (net.Addr, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if ctx == nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: errors.New("nil context")}
}
a, err := d.connect(ctx, c, address)
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
return a, nil
}
// Dial connects to the provided address on the provided network.
//
// Unlike DialContext, it returns a raw transport connection instead
// of a forward proxy connection.
//
// Deprecated: Use DialContext or DialWithConn instead.
func (d *Dialer) Dial(network, address string) (net.Conn, error) {
if err := d.validateTarget(network, address); err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
var err error
var c net.Conn
if d.ProxyDial != nil {
c, err = d.ProxyDial(context.Background(), d.proxyNetwork, d.proxyAddress)
} else {
c, err = net.Dial(d.proxyNetwork, d.proxyAddress)
}
if err != nil {
proxy, dst, _ := d.pathAddrs(address)
return nil, &net.OpError{Op: d.cmd.String(), Net: network, Source: proxy, Addr: dst, Err: err}
}
if _, err := d.DialWithConn(context.Background(), c, network, address); err != nil {
return nil, err
}
return c, nil
}
func (d *Dialer) validateTarget(network, address string) error {
switch network {
case "tcp", "tcp6", "tcp4":
default:
return errors.New("network not implemented")
}
switch d.cmd {
case CmdConnect, cmdBind:
default:
return errors.New("command not implemented")
}
return nil
}
func (d *Dialer) pathAddrs(address string) (proxy, dst net.Addr, err error) {
for i, s := range []string{d.proxyAddress, address} {
host, port, err := splitHostPort(s)
if err != nil {
return nil, nil, err
}
a := &Addr{Port: port}
a.IP = net.ParseIP(host)
if a.IP == nil {
a.Name = host
}
if i == 0 {
proxy = a
} else {
dst = a
}
}
return
}
// NewDialer returns a new Dialer that dials through the provided
// proxy server's network and address.
func NewDialer(network, address string) *Dialer {
return &Dialer{proxyNetwork: network, proxyAddress: address, cmd: CmdConnect}
}
const (
authUsernamePasswordVersion = 0x01
authStatusSucceeded = 0x00
)
// UsernamePassword are the credentials for the username/password
// authentication method.
type UsernamePassword struct {
Username string
Password string
}
// Authenticate authenticates a pair of username and password with the
// proxy server.
func (up *UsernamePassword) Authenticate(ctx context.Context, rw io.ReadWriter, auth AuthMethod) error {
switch auth {
case AuthMethodNotRequired:
return nil
case AuthMethodUsernamePassword:
if len(up.Username) == 0 || len(up.Username) > 255 || len(up.Password) == 0 || len(up.Password) > 255 {
return errors.New("invalid username/password")
}
b := []byte{authUsernamePasswordVersion}
b = append(b, byte(len(up.Username)))
b = append(b, up.Username...)
b = append(b, byte(len(up.Password)))
b = append(b, up.Password...)
// TODO(mikio): handle IO deadlines and cancelation if
// necessary
if _, err := rw.Write(b); err != nil {
return err
}
if _, err := io.ReadFull(rw, b[:2]); err != nil {
return err
}
if b[0] != authUsernamePasswordVersion {
return errors.New("invalid username/password version")
}
if b[1] != authStatusSucceeded {
return errors.New("username/password authentication failed")
}
return nil
}
return errors.New("unsupported authentication method " + strconv.Itoa(int(auth)))
}

View File

@@ -1,241 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sockstest provides utilities for SOCKS testing.
package sockstest
import (
"errors"
"io"
"net"
"golang.org/x/net/internal/nettest"
"golang.org/x/net/internal/socks"
)
// An AuthRequest represents an authentication request.
type AuthRequest struct {
Version int
Methods []socks.AuthMethod
}
// ParseAuthRequest parses an authentication request.
func ParseAuthRequest(b []byte) (*AuthRequest, error) {
if len(b) < 2 {
return nil, errors.New("short auth request")
}
if b[0] != socks.Version5 {
return nil, errors.New("unexpected protocol version")
}
if len(b)-2 < int(b[1]) {
return nil, errors.New("short auth request")
}
req := &AuthRequest{Version: int(b[0])}
if b[1] > 0 {
req.Methods = make([]socks.AuthMethod, b[1])
for i, m := range b[2 : 2+b[1]] {
req.Methods[i] = socks.AuthMethod(m)
}
}
return req, nil
}
// MarshalAuthReply returns an authentication reply in wire format.
func MarshalAuthReply(ver int, m socks.AuthMethod) ([]byte, error) {
return []byte{byte(ver), byte(m)}, nil
}
// A CmdRequest repesents a command request.
type CmdRequest struct {
Version int
Cmd socks.Command
Addr socks.Addr
}
// ParseCmdRequest parses a command request.
func ParseCmdRequest(b []byte) (*CmdRequest, error) {
if len(b) < 7 {
return nil, errors.New("short cmd request")
}
if b[0] != socks.Version5 {
return nil, errors.New("unexpected protocol version")
}
if socks.Command(b[1]) != socks.CmdConnect {
return nil, errors.New("unexpected command")
}
if b[2] != 0 {
return nil, errors.New("non-zero reserved field")
}
req := &CmdRequest{Version: int(b[0]), Cmd: socks.Command(b[1])}
l := 2
off := 4
switch b[3] {
case socks.AddrTypeIPv4:
l += net.IPv4len
req.Addr.IP = make(net.IP, net.IPv4len)
case socks.AddrTypeIPv6:
l += net.IPv6len
req.Addr.IP = make(net.IP, net.IPv6len)
case socks.AddrTypeFQDN:
l += int(b[4])
off = 5
default:
return nil, errors.New("unknown address type")
}
if len(b[off:]) < l {
return nil, errors.New("short cmd request")
}
if req.Addr.IP != nil {
copy(req.Addr.IP, b[off:])
} else {
req.Addr.Name = string(b[off : off+l-2])
}
req.Addr.Port = int(b[off+l-2])<<8 | int(b[off+l-1])
return req, nil
}
// MarshalCmdReply returns a command reply in wire format.
func MarshalCmdReply(ver int, reply socks.Reply, a *socks.Addr) ([]byte, error) {
b := make([]byte, 4)
b[0] = byte(ver)
b[1] = byte(reply)
if a.Name != "" {
if len(a.Name) > 255 {
return nil, errors.New("fqdn too long")
}
b[3] = socks.AddrTypeFQDN
b = append(b, byte(len(a.Name)))
b = append(b, a.Name...)
} else if ip4 := a.IP.To4(); ip4 != nil {
b[3] = socks.AddrTypeIPv4
b = append(b, ip4...)
} else if ip6 := a.IP.To16(); ip6 != nil {
b[3] = socks.AddrTypeIPv6
b = append(b, ip6...)
} else {
return nil, errors.New("unknown address type")
}
b = append(b, byte(a.Port>>8), byte(a.Port))
return b, nil
}
// A Server repesents a server for handshake testing.
type Server struct {
ln net.Listener
}
// Addr rerurns a server address.
func (s *Server) Addr() net.Addr {
return s.ln.Addr()
}
// TargetAddr returns a fake final destination address.
//
// The returned address is only valid for testing with Server.
func (s *Server) TargetAddr() net.Addr {
a := s.ln.Addr()
switch a := a.(type) {
case *net.TCPAddr:
if a.IP.To4() != nil {
return &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 5963}
}
if a.IP.To16() != nil && a.IP.To4() == nil {
return &net.TCPAddr{IP: net.IPv6loopback, Port: 5963}
}
}
return nil
}
// Close closes the server.
func (s *Server) Close() error {
return s.ln.Close()
}
func (s *Server) serve(authFunc, cmdFunc func(io.ReadWriter, []byte) error) {
c, err := s.ln.Accept()
if err != nil {
return
}
defer c.Close()
go s.serve(authFunc, cmdFunc)
b := make([]byte, 512)
n, err := c.Read(b)
if err != nil {
return
}
if err := authFunc(c, b[:n]); err != nil {
return
}
n, err = c.Read(b)
if err != nil {
return
}
if err := cmdFunc(c, b[:n]); err != nil {
return
}
}
// NewServer returns a new server.
//
// The provided authFunc and cmdFunc must parse requests and return
// appropriate replies to clients.
func NewServer(authFunc, cmdFunc func(io.ReadWriter, []byte) error) (*Server, error) {
var err error
s := new(Server)
s.ln, err = nettest.NewLocalListener("tcp")
if err != nil {
return nil, err
}
go s.serve(authFunc, cmdFunc)
return s, nil
}
// NoAuthRequired handles a no-authentication-required signaling.
func NoAuthRequired(rw io.ReadWriter, b []byte) error {
req, err := ParseAuthRequest(b)
if err != nil {
return err
}
b, err = MarshalAuthReply(req.Version, socks.AuthMethodNotRequired)
if err != nil {
return err
}
n, err := rw.Write(b)
if err != nil {
return err
}
if n != len(b) {
return errors.New("short write")
}
return nil
}
// NoProxyRequired handles a command signaling without constructing a
// proxy connection to the final destination.
func NoProxyRequired(rw io.ReadWriter, b []byte) error {
req, err := ParseCmdRequest(b)
if err != nil {
return err
}
req.Addr.Port += 1
if req.Addr.Name != "" {
req.Addr.Name = "boundaddr.doesnotexist"
} else if req.Addr.IP.To4() != nil {
req.Addr.IP = net.IPv4(127, 0, 0, 1)
} else {
req.Addr.IP = net.IPv6loopback
}
b, err = MarshalCmdReply(socks.Version5, socks.StatusSucceeded, &req.Addr)
if err != nil {
return err
}
n, err := rw.Write(b)
if err != nil {
return err
}
if n != len(b) {
return errors.New("short write")
}
return nil
}

View File

@@ -1,103 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sockstest
import (
"net"
"reflect"
"testing"
"golang.org/x/net/internal/socks"
)
func TestParseAuthRequest(t *testing.T) {
for i, tt := range []struct {
wire []byte
req *AuthRequest
}{
{
[]byte{0x05, 0x00},
&AuthRequest{
socks.Version5,
nil,
},
},
{
[]byte{0x05, 0x01, 0xff},
&AuthRequest{
socks.Version5,
[]socks.AuthMethod{
socks.AuthMethodNoAcceptableMethods,
},
},
},
{
[]byte{0x05, 0x02, 0x00, 0xff},
&AuthRequest{
socks.Version5,
[]socks.AuthMethod{
socks.AuthMethodNotRequired,
socks.AuthMethodNoAcceptableMethods,
},
},
},
// corrupted requests
{nil, nil},
{[]byte{0x00, 0x01}, nil},
{[]byte{0x06, 0x00}, nil},
{[]byte{0x05, 0x02, 0x00}, nil},
} {
req, err := ParseAuthRequest(tt.wire)
if !reflect.DeepEqual(req, tt.req) {
t.Errorf("#%d: got %v, %v; want %v", i, req, err, tt.req)
continue
}
}
}
func TestParseCmdRequest(t *testing.T) {
for i, tt := range []struct {
wire []byte
req *CmdRequest
}{
{
[]byte{0x05, 0x01, 0x00, 0x01, 192, 0, 2, 1, 0x17, 0x4b},
&CmdRequest{
socks.Version5,
socks.CmdConnect,
socks.Addr{
IP: net.IP{192, 0, 2, 1},
Port: 5963,
},
},
},
{
[]byte{0x05, 0x01, 0x00, 0x03, 0x04, 'F', 'Q', 'D', 'N', 0x17, 0x4b},
&CmdRequest{
socks.Version5,
socks.CmdConnect,
socks.Addr{
Name: "FQDN",
Port: 5963,
},
},
},
// corrupted requests
{nil, nil},
{[]byte{0x05}, nil},
{[]byte{0x06, 0x01, 0x00, 0x01, 192, 0, 2, 2, 0x17, 0x4b}, nil},
{[]byte{0x05, 0x01, 0xff, 0x01, 192, 0, 2, 3}, nil},
{[]byte{0x05, 0x01, 0x00, 0x01, 192, 0, 2, 4}, nil},
{[]byte{0x05, 0x01, 0x00, 0x03, 0x04, 'F', 'Q', 'D', 'N'}, nil},
} {
req, err := ParseCmdRequest(tt.wire)
if !reflect.DeepEqual(req, tt.req) {
t.Errorf("#%d: got %v, %v; want %v", i, req, err, tt.req)
continue
}
}
}

View File

@@ -1,170 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package timeseries
import (
"math"
"testing"
"time"
)
func isNear(x *Float, y float64, tolerance float64) bool {
return math.Abs(x.Value()-y) < tolerance
}
func isApproximate(x *Float, y float64) bool {
return isNear(x, y, 1e-2)
}
func checkApproximate(t *testing.T, o Observable, y float64) {
x := o.(*Float)
if !isApproximate(x, y) {
t.Errorf("Wanted %g, got %g", y, x.Value())
}
}
func checkNear(t *testing.T, o Observable, y, tolerance float64) {
x := o.(*Float)
if !isNear(x, y, tolerance) {
t.Errorf("Wanted %g +- %g, got %g", y, tolerance, x.Value())
}
}
var baseTime = time.Date(2013, 1, 1, 0, 0, 0, 0, time.UTC)
func tu(s int64) time.Time {
return baseTime.Add(time.Duration(s) * time.Second)
}
func tu2(s int64, ns int64) time.Time {
return baseTime.Add(time.Duration(s)*time.Second + time.Duration(ns)*time.Nanosecond)
}
func TestBasicTimeSeries(t *testing.T) {
ts := NewTimeSeries(NewFloat)
fo := new(Float)
*fo = Float(10)
ts.AddWithTime(fo, tu(1))
ts.AddWithTime(fo, tu(1))
ts.AddWithTime(fo, tu(1))
ts.AddWithTime(fo, tu(1))
checkApproximate(t, ts.Range(tu(0), tu(1)), 40)
checkApproximate(t, ts.Total(), 40)
ts.AddWithTime(fo, tu(3))
ts.AddWithTime(fo, tu(3))
ts.AddWithTime(fo, tu(3))
checkApproximate(t, ts.Range(tu(0), tu(2)), 40)
checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
checkApproximate(t, ts.Total(), 70)
ts.AddWithTime(fo, tu(1))
ts.AddWithTime(fo, tu(1))
checkApproximate(t, ts.Range(tu(0), tu(2)), 60)
checkApproximate(t, ts.Range(tu(2), tu(4)), 30)
checkApproximate(t, ts.Total(), 90)
*fo = Float(100)
ts.AddWithTime(fo, tu(100))
checkApproximate(t, ts.Range(tu(99), tu(100)), 100)
checkApproximate(t, ts.Range(tu(0), tu(4)), 36)
checkApproximate(t, ts.Total(), 190)
*fo = Float(10)
ts.AddWithTime(fo, tu(1))
ts.AddWithTime(fo, tu(1))
checkApproximate(t, ts.Range(tu(0), tu(4)), 44)
checkApproximate(t, ts.Range(tu(37), tu2(100, 100e6)), 100)
checkApproximate(t, ts.Range(tu(50), tu2(100, 100e6)), 100)
checkApproximate(t, ts.Range(tu(99), tu2(100, 100e6)), 100)
checkApproximate(t, ts.Total(), 210)
for i, l := range ts.ComputeRange(tu(36), tu(100), 64) {
if i == 63 {
checkApproximate(t, l, 100)
} else {
checkApproximate(t, l, 0)
}
}
checkApproximate(t, ts.Range(tu(0), tu(100)), 210)
checkApproximate(t, ts.Range(tu(10), tu(100)), 100)
for i, l := range ts.ComputeRange(tu(0), tu(100), 100) {
if i < 10 {
checkApproximate(t, l, 11)
} else if i >= 90 {
checkApproximate(t, l, 10)
} else {
checkApproximate(t, l, 0)
}
}
}
func TestFloat(t *testing.T) {
f := Float(1)
if g, w := f.String(), "1"; g != w {
t.Errorf("Float(1).String = %q; want %q", g, w)
}
f2 := Float(2)
var o Observable = &f2
f.Add(o)
if g, w := f.Value(), 3.0; g != w {
t.Errorf("Float post-add = %v; want %v", g, w)
}
f.Multiply(2)
if g, w := f.Value(), 6.0; g != w {
t.Errorf("Float post-multiply = %v; want %v", g, w)
}
f.Clear()
if g, w := f.Value(), 0.0; g != w {
t.Errorf("Float post-clear = %v; want %v", g, w)
}
f.CopyFrom(&f2)
if g, w := f.Value(), 2.0; g != w {
t.Errorf("Float post-CopyFrom = %v; want %v", g, w)
}
}
type mockClock struct {
time time.Time
}
func (m *mockClock) Time() time.Time { return m.time }
func (m *mockClock) Set(t time.Time) { m.time = t }
const buckets = 6
var testResolutions = []time.Duration{
10 * time.Second, // level holds one minute of observations
100 * time.Second, // level holds ten minutes of observations
10 * time.Minute, // level holds one hour of observations
}
// TestTimeSeries uses a small number of buckets to force a higher
// error rate on approximations from the timeseries.
type TestTimeSeries struct {
timeSeries
}
func TestExpectedErrorRate(t *testing.T) {
ts := new(TestTimeSeries)
fake := new(mockClock)
fake.Set(time.Now())
ts.timeSeries.init(testResolutions, NewFloat, buckets, fake)
for i := 1; i <= 61*61; i++ {
fake.Set(fake.Time().Add(1 * time.Second))
ob := Float(1)
ts.AddWithTime(&ob, fake.Time())
// The results should be accurate within one missing bucket (1/6) of the observations recorded.
checkNear(t, ts.Latest(0, buckets), min(float64(i), 60), 10)
checkNear(t, ts.Latest(1, buckets), min(float64(i), 600), 100)
checkNear(t, ts.Latest(2, buckets), min(float64(i), 3600), 600)
}
}
func min(a, b float64) float64 {
if a < b {
return a
}
return b
}