parent
880274af1f
commit
2fcdcb8a43
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkgos/go-socks5"
|
||||
"github.com/thinkgos/go-socks5/client_socks5"
|
||||
)
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
locIP := net.ParseIP("127.0.0.1")
|
||||
// Create a local listener
|
||||
lAddr := &net.UDPAddr{IP: locIP, Port: 12398}
|
||||
l, err := net.ListenUDP("udp", lAddr)
|
||||
handleErr(err)
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
buf := make([]byte, 2048)
|
||||
for {
|
||||
n, remote, err := l.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.Printf("server: %v", err)
|
||||
return
|
||||
}
|
||||
log.Printf("server: %+v", string(buf[:n]))
|
||||
|
||||
l.WriteTo([]byte("pong"), remote) // nolint: errcheck
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Second)
|
||||
c, err := client_socks5.NewClient("127.0.0.1:10809")
|
||||
handleErr(err)
|
||||
con, err := c.Dial("udp", lAddr.String())
|
||||
handleErr(err)
|
||||
for {
|
||||
con.Write([]byte("ping")) // nolint: errcheck
|
||||
out := make([]byte, 4)
|
||||
con.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(con, out)
|
||||
con.SetDeadline(time.Time{}) // nolint: errcheck
|
||||
if err != nil {
|
||||
log.Printf("client: %v", err)
|
||||
} else {
|
||||
log.Printf("client: %+v", string(out))
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
}()
|
||||
|
||||
// Create a SOCKS5 server
|
||||
server := socks5.NewServer(socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))))
|
||||
|
||||
// Create SOCKS5 proxy on localhost port 8000
|
||||
if err := server.ListenAndServe("tcp", "127.0.0.1:10809"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkgos/go-socks5"
|
||||
"github.com/thinkgos/go-socks5/client_socks5"
|
||||
)
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
handleErr(err)
|
||||
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
handleErr(err)
|
||||
defer conn.Close()
|
||||
for {
|
||||
buf := make([]byte, 4)
|
||||
_, err = io.ReadAtLeast(conn, buf, 4)
|
||||
handleErr(err)
|
||||
log.Printf("server: %+v", string(buf))
|
||||
conn.Write([]byte("pong")) // nolint: errcheck
|
||||
}
|
||||
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Second * 1)
|
||||
c, err := client_socks5.NewClient("127.0.0.1:10808")
|
||||
handleErr(err)
|
||||
con, err := c.Dial("tcp", lAddr.String())
|
||||
handleErr(err)
|
||||
|
||||
for {
|
||||
con.Write([]byte("ping")) // nolint: errcheck
|
||||
out := make([]byte, 4)
|
||||
con.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(con, out)
|
||||
con.SetDeadline(time.Time{}) // nolint: errcheck
|
||||
if err != nil {
|
||||
log.Printf("client: %+v", err)
|
||||
} else {
|
||||
log.Printf("client: %+v", string(out))
|
||||
}
|
||||
time.Sleep(time.Second * 2)
|
||||
}
|
||||
|
||||
}()
|
||||
|
||||
// Create a SOCKS5 server
|
||||
server := socks5.NewServer(socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))))
|
||||
|
||||
// Create SOCKS5 proxy on localhost port 8000
|
||||
if err := server.ListenAndServe("tcp", "127.0.0.1:10808"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/thinkgos/go-socks5"
|
||||
"github.com/thinkgos/go-socks5/client"
|
||||
)
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create a local listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
handleErr(err)
|
||||
|
||||
go func() {
|
||||
conn, err := l.Accept()
|
||||
handleErr(err)
|
||||
defer conn.Close()
|
||||
|
||||
buf := make([]byte, 4)
|
||||
_, err = io.ReadAtLeast(conn, buf, 4)
|
||||
handleErr(err)
|
||||
log.Printf("server: %+v", string(buf))
|
||||
conn.Write([]byte("pong"))
|
||||
|
||||
}()
|
||||
lAddr := l.Addr().(*net.TCPAddr)
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Second * 1)
|
||||
c, err := client.NewClient("127.0.0.1:10809")
|
||||
handleErr(err)
|
||||
con, err := c.Dial("tcp", lAddr.String())
|
||||
handleErr(err)
|
||||
|
||||
con.Write([]byte("ping"))
|
||||
out := make([]byte, 4)
|
||||
_ = con.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(con, out)
|
||||
log.Printf("client: %+v", string(out))
|
||||
}()
|
||||
|
||||
// Create a SOCKS5 server
|
||||
server := socks5.NewServer(socks5.WithLogger(socks5.NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags))))
|
||||
|
||||
// Create SOCKS5 proxy on localhost port 8000
|
||||
if err := server.ListenAndServe("tcp", "127.0.0.1:10809"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
package socks5
|
||||
package bufferpool
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A BufferPool is an interface for getting and returning temporary
|
||||
// BufPool is an interface for getting and returning temporary
|
||||
// byte slices for use by io.CopyBuffer.
|
||||
type BufferPool interface {
|
||||
type BufPool interface {
|
||||
Get() []byte
|
||||
Put([]byte)
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ type pool struct {
|
|||
|
||||
// NewPool new buffer pool for getting and returning temporary
|
||||
// byte slices for use by io.CopyBuffer.
|
||||
func NewPool(size int) BufferPool {
|
||||
func NewPool(size int) BufPool {
|
||||
return &pool{
|
||||
size,
|
||||
&sync.Pool{
|
||||
|
@ -26,12 +26,12 @@ func NewPool(size int) BufferPool {
|
|||
}
|
||||
}
|
||||
|
||||
// Get implement interface BufferPool
|
||||
// Get implement interface BufPool
|
||||
func (sf *pool) Get() []byte {
|
||||
return sf.pool.Get().([]byte)
|
||||
}
|
||||
|
||||
// Put implement interface BufferPool
|
||||
// Put implement interface BufPool
|
||||
func (sf *pool) Put(b []byte) {
|
||||
if cap(b) != sf.size {
|
||||
panic("invalid buffer size that's put into leaky buffer")
|
|
@ -1,4 +1,4 @@
|
|||
package socks5
|
||||
package bufferpool
|
||||
|
||||
import (
|
||||
"sync"
|
261
client/client.go
261
client/client.go
|
@ -1,261 +0,0 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/thinkgos/go-socks5/statute"
|
||||
)
|
||||
|
||||
// Client is socks5 client wrapper
|
||||
type Client struct {
|
||||
Server string
|
||||
Auth *proxy.Auth
|
||||
// On command UDP, let server control the tcp and udp connection relationship
|
||||
TCPConn *net.TCPConn
|
||||
UDPConn *net.UDPConn
|
||||
RemoteAddress net.Addr
|
||||
TCPDeadline time.Duration
|
||||
TCPTimeout time.Duration
|
||||
UDPDeadline time.Duration
|
||||
}
|
||||
|
||||
// This is just create a client, you need to use Dial to create conn
|
||||
func NewClient(addr string, opts ...Option) (*Client, error) {
|
||||
c := &Client{
|
||||
Server: addr,
|
||||
TCPTimeout: time.Second,
|
||||
TCPDeadline: time.Second,
|
||||
UDPDeadline: time.Second,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Client) Close() error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Close()
|
||||
}
|
||||
if c.TCPConn != nil {
|
||||
c.TCPConn.Close()
|
||||
}
|
||||
return c.UDPConn.Close()
|
||||
}
|
||||
|
||||
func (c *Client) LocalAddr() net.Addr {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.LocalAddr()
|
||||
}
|
||||
return c.UDPConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *Client) RemoteAddr() net.Addr {
|
||||
return c.RemoteAddress
|
||||
}
|
||||
|
||||
func (c *Client) SetDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) SetReadDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetReadDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) SetWriteDeadline(t time.Time) error {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.SetWriteDeadline(t)
|
||||
}
|
||||
return c.UDPConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (c *Client) Read(b []byte) (int, error) {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Read(b)
|
||||
}
|
||||
b1 := make([]byte, 65535)
|
||||
n, err := c.UDPConn.Read(b1)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
pkt, err := statute.ParseDatagram(b1[:n])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = copy(b, pkt.Data)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (c *Client) Write(b []byte) (int, error) {
|
||||
if c.UDPConn == nil {
|
||||
return c.TCPConn.Write(b)
|
||||
}
|
||||
pkt, err := statute.NewDatagram(c.RemoteAddress.String(), b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return c.UDPConn.Write(pkt.Bytes())
|
||||
}
|
||||
|
||||
func (c *Client) Dial(network, addr string) (net.Conn, error) {
|
||||
var err error
|
||||
|
||||
conn := *c // clone a client
|
||||
if network == "tcp" {
|
||||
conn.RemoteAddress, err = net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conn.TCPConn, err = conn.dialServer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conn.handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, err := statute.ParseAddrSpec(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
head := statute.Request{
|
||||
Version: statute.VersionSocks5,
|
||||
Command: statute.CommandConnect,
|
||||
DstAddress: a,
|
||||
}
|
||||
if _, err := conn.Write(head.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rspHead, err := statute.ParseRequest(conn.TCPConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rspHead.Command != statute.RepSuccess {
|
||||
return nil, errors.New("host unreachable")
|
||||
}
|
||||
return &conn, nil
|
||||
}
|
||||
|
||||
if network == "udp" {
|
||||
conn.RemoteAddress, err = net.ResolveUDPAddr("udp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.TCPConn, err = conn.dialServer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := conn.handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
laddr := &net.UDPAddr{
|
||||
IP: conn.TCPConn.LocalAddr().(*net.TCPAddr).IP,
|
||||
Port: conn.TCPConn.LocalAddr().(*net.TCPAddr).Port,
|
||||
Zone: conn.TCPConn.LocalAddr().(*net.TCPAddr).Zone,
|
||||
}
|
||||
a, err := statute.ParseAddrSpec(laddr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
head := statute.Request{
|
||||
Version: statute.VersionSocks5,
|
||||
Command: statute.CommandConnect,
|
||||
DstAddress: a,
|
||||
}
|
||||
if _, err := conn.Write(head.Bytes()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rspHead, err := statute.ParseRequest(conn.TCPConn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if rspHead.Command != statute.RepSuccess {
|
||||
return nil, errors.New("host unreachable")
|
||||
}
|
||||
|
||||
raddr, err := net.ResolveUDPAddr("udp", rspHead.DstAddress.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conn.UDPConn, err = net.DialUDP("udp", laddr, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &conn, nil
|
||||
}
|
||||
|
||||
return nil, errors.New("not support network")
|
||||
}
|
||||
|
||||
func (c *Client) handshake() error {
|
||||
methods := statute.MethodNoAuth
|
||||
if c.Auth != nil {
|
||||
methods = statute.MethodUserPassAuth
|
||||
}
|
||||
|
||||
_, err := c.TCPConn.Write(statute.NewMethodRequest(statute.VersionSocks5, []byte{methods}).Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
reply, err := statute.ParseMethodReply(c.TCPConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reply.Ver != statute.VersionSocks5 {
|
||||
return statute.ErrNotSupportVersion
|
||||
}
|
||||
if reply.Method != methods {
|
||||
return statute.ErrNotSupportMethod
|
||||
}
|
||||
|
||||
if methods == statute.MethodUserPassAuth {
|
||||
_, err = c.TCPConn.Write(statute.NewUserPassRequest(statute.UserPassAuthVersion, []byte(c.Auth.User), []byte(c.Auth.Password)).Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rsp, err := statute.ParseUserPassReply(c.TCPConn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rsp.Ver != statute.UserPassAuthVersion {
|
||||
return statute.ErrNotSupportMethod
|
||||
}
|
||||
if rsp.Status != statute.RepSuccess {
|
||||
return statute.ErrUserAuthFailed
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) dialServer() (*net.TCPConn, error) {
|
||||
conn, err := net.Dial("tcp", c.Server)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
TCPConn := conn.(*net.TCPConn)
|
||||
if c.TCPTimeout != 0 {
|
||||
if err := TCPConn.SetKeepAlivePeriod(c.TCPTimeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if c.TCPDeadline != 0 {
|
||||
if err := TCPConn.SetDeadline(time.Now().Add(c.TCPTimeout)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return TCPConn, nil
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package client_socks5
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Connect struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
func (sf *Connect) SetReadBuffer(bytes int) error {
|
||||
return sf.tcpConn.SetReadBuffer(bytes)
|
||||
}
|
||||
|
||||
func (sf *Connect) SetWriteBuffer(bytes int) error {
|
||||
return sf.tcpConn.SetWriteBuffer(bytes)
|
||||
}
|
||||
|
||||
func (sf *Connect) SetKeepAlive(keepalive bool) error {
|
||||
return sf.tcpConn.SetKeepAlive(keepalive)
|
||||
}
|
||||
|
||||
func (sf *Connect) SetKeepAlivePeriod(d time.Duration) error {
|
||||
return sf.tcpConn.SetKeepAlivePeriod(d)
|
||||
}
|
||||
|
||||
func (sf *Connect) SetLinger(sec int) error {
|
||||
return sf.tcpConn.SetLinger(sec)
|
||||
}
|
||||
|
||||
func (sf *Connect) SetNoDelay(noDelay bool) error {
|
||||
return sf.tcpConn.SetNoDelay(noDelay)
|
||||
}
|
||||
|
||||
func (sf *Connect) ReadFrom(r io.Reader) (int64, error) {
|
||||
return sf.tcpConn.ReadFrom(r)
|
||||
}
|
||||
|
||||
func (sf *Connect) CloseRead() error {
|
||||
return sf.tcpConn.CloseRead()
|
||||
}
|
||||
|
||||
func (sf *Connect) CloseWrite() error {
|
||||
return sf.tcpConn.CloseWrite()
|
||||
}
|
||||
|
||||
func (sf *Connect) File() (f *os.File, err error) {
|
||||
return sf.tcpConn.File()
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package client_socks5
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Associate struct {
|
||||
*Client
|
||||
}
|
||||
|
||||
func (sf *Associate) getUDPConn() *net.UDPConn {
|
||||
return sf.underConn.(*underUDPConn).udpConn
|
||||
}
|
||||
|
||||
func (sf *Associate) SetReadBuffer(bytes int) error {
|
||||
return sf.getUDPConn().SetReadBuffer(bytes)
|
||||
}
|
||||
|
||||
func (sf *Associate) SetWriteBuffer(bytes int) error {
|
||||
return sf.getUDPConn().SetWriteBuffer(bytes)
|
||||
}
|
||||
func (sf *Associate) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return sf.getUDPConn().ReadFrom(b)
|
||||
}
|
||||
func (sf *Associate) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
return sf.getUDPConn().ReadFromUDP(b)
|
||||
}
|
||||
func (sf *Associate) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
|
||||
return sf.getUDPConn().ReadMsgUDP(b, oob)
|
||||
}
|
||||
func (sf *Associate) WriteTo(b []byte, addr net.Addr) (int, error) {
|
||||
return sf.getUDPConn().WriteTo(b, addr)
|
||||
}
|
||||
func (sf *Associate) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
||||
return sf.getUDPConn().WriteToUDP(b, addr)
|
||||
}
|
||||
func (sf *Associate) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
|
||||
return sf.getUDPConn().WriteMsgUDP(b, oob, addr)
|
||||
}
|
||||
func (sf *Associate) File() (f *os.File, err error) {
|
||||
return sf.getUDPConn().File()
|
||||
}
|
|
@ -0,0 +1,229 @@
|
|||
package client_socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
"github.com/thinkgos/go-socks5/statute"
|
||||
)
|
||||
|
||||
// Client is socks5 client wrapper
|
||||
type Client struct {
|
||||
proxyAddr string
|
||||
auth *proxy.Auth
|
||||
// On command UDP, let server control the tcp and udp connection relationship
|
||||
tcpConn *net.TCPConn
|
||||
underConn net.Conn
|
||||
TCPDeadline time.Duration
|
||||
KeepAlivePeriod time.Duration
|
||||
UDPDeadline time.Duration
|
||||
bufferPool bufferpool.BufPool
|
||||
}
|
||||
|
||||
// This is just create a client, you need to use Dial to create conn
|
||||
func NewClient(proxyAddr string, opts ...Option) (*Client, error) {
|
||||
c := &Client{
|
||||
proxyAddr: proxyAddr,
|
||||
KeepAlivePeriod: time.Second,
|
||||
TCPDeadline: time.Second,
|
||||
UDPDeadline: time.Second,
|
||||
bufferPool: bufferpool.NewPool(32 * 1024),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (sf *Client) Read(b []byte) (int, error) {
|
||||
return sf.underConn.Read(b)
|
||||
}
|
||||
|
||||
func (sf *Client) Write(b []byte) (int, error) {
|
||||
return sf.underConn.Write(b)
|
||||
}
|
||||
|
||||
func (sf *Client) Close() (err error) {
|
||||
err = sf.tcpConn.Close()
|
||||
if sf.underConn != nil {
|
||||
err = sf.underConn.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (sf *Client) LocalAddr() net.Addr {
|
||||
return sf.underConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (sf *Client) RemoteAddr() net.Addr {
|
||||
return sf.underConn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (sf *Client) SetDeadline(t time.Time) error {
|
||||
return sf.underConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (sf *Client) SetReadDeadline(t time.Time) error {
|
||||
return sf.underConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (sf *Client) SetWriteDeadline(t time.Time) error {
|
||||
return sf.underConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (sf *Client) Dial(network, addr string) (net.Conn, error) {
|
||||
if network == "tcp" {
|
||||
return sf.DialTCP(network, addr)
|
||||
}
|
||||
if network == "udp" {
|
||||
return sf.DialUDP(network, nil, addr)
|
||||
}
|
||||
return nil, errors.New("not support network")
|
||||
}
|
||||
|
||||
func (sf *Client) DialTCP(network, addr string) (*Connect, error) {
|
||||
conn := *sf // clone a client
|
||||
|
||||
_, err := net.ResolveTCPAddr(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.dialProxyServer(network)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := conn.handshake(statute.CommandConnect, addr); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn.underConn = conn.tcpConn
|
||||
return &Connect{&conn}, nil
|
||||
}
|
||||
|
||||
func (sf *Client) DialUDP(network string, laddr *net.UDPAddr, raddr string) (*Associate, error) {
|
||||
conn := *sf // clone a client
|
||||
|
||||
remoteAddress, err := net.ResolveUDPAddr(network, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = conn.dialProxyServer("tcp")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bndAddress, err := conn.handshake(statute.CommandAssociate, raddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ra, err := net.ResolveUDPAddr(network, bndAddress)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if laddr == nil {
|
||||
laddr = &net.UDPAddr{
|
||||
IP: conn.tcpConn.LocalAddr().(*net.TCPAddr).IP,
|
||||
Port: conn.tcpConn.LocalAddr().(*net.TCPAddr).Port,
|
||||
Zone: conn.tcpConn.LocalAddr().(*net.TCPAddr).Zone,
|
||||
}
|
||||
}
|
||||
|
||||
udpConn, err := net.DialUDP(network, laddr, ra)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
conn.underConn = &underUDPConn{
|
||||
udpConn,
|
||||
conn.bufferPool,
|
||||
remoteAddress,
|
||||
}
|
||||
return &Associate{&conn}, nil
|
||||
}
|
||||
|
||||
func (sf *Client) handshake(command byte, addr string) (string, error) {
|
||||
methods := statute.MethodNoAuth
|
||||
if sf.auth != nil {
|
||||
methods = statute.MethodUserPassAuth
|
||||
}
|
||||
|
||||
_, err := sf.tcpConn.Write(statute.NewMethodRequest(statute.VersionSocks5, []byte{methods}).Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
reply, err := statute.ParseMethodReply(sf.tcpConn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if reply.Ver != statute.VersionSocks5 {
|
||||
return "", statute.ErrNotSupportVersion
|
||||
}
|
||||
if reply.Method != methods {
|
||||
return "", statute.ErrNotSupportMethod
|
||||
}
|
||||
|
||||
if methods == statute.MethodUserPassAuth {
|
||||
_, err = sf.tcpConn.Write(statute.NewUserPassRequest(statute.UserPassAuthVersion, []byte(sf.auth.User), []byte(sf.auth.Password)).Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rsp, err := statute.ParseUserPassReply(sf.tcpConn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rsp.Ver != statute.UserPassAuthVersion {
|
||||
return "", statute.ErrNotSupportMethod
|
||||
}
|
||||
if rsp.Status != statute.RepSuccess {
|
||||
return "", statute.ErrUserAuthFailed
|
||||
}
|
||||
}
|
||||
|
||||
a, err := statute.ParseAddrSpec(addr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
reqHead := statute.Request{
|
||||
Version: statute.VersionSocks5,
|
||||
Command: command,
|
||||
DstAddress: a,
|
||||
}
|
||||
if _, err := sf.tcpConn.Write(reqHead.Bytes()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
rspHead, err := statute.ParseReply(sf.tcpConn)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rspHead.Response != statute.RepSuccess {
|
||||
return "", errors.New("host unreachable")
|
||||
}
|
||||
return rspHead.BndAddress.String(), nil
|
||||
}
|
||||
|
||||
func (sf *Client) dialProxyServer(network string) error {
|
||||
conn, err := net.Dial(network, sf.proxyAddr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sf.tcpConn = conn.(*net.TCPConn)
|
||||
|
||||
if sf.KeepAlivePeriod != 0 {
|
||||
err = sf.tcpConn.SetKeepAlivePeriod(sf.KeepAlivePeriod)
|
||||
}
|
||||
if err != nil {
|
||||
sf.tcpConn.Close()
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,22 +1,30 @@
|
|||
package client
|
||||
package client_socks5
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/proxy"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
)
|
||||
|
||||
type Option func(c *Client)
|
||||
|
||||
func WithAuth(auth *proxy.Auth) Option {
|
||||
return func(c *Client) {
|
||||
c.Auth = auth
|
||||
c.auth = auth
|
||||
}
|
||||
}
|
||||
|
||||
func WithTCPTimeout(t time.Duration) Option {
|
||||
func WithBufferPool(p bufferpool.BufPool) Option {
|
||||
return func(c *Client) {
|
||||
c.TCPTimeout = t
|
||||
c.bufferPool = p
|
||||
}
|
||||
}
|
||||
|
||||
func WithKeepAlivePeriod(t time.Duration) Option {
|
||||
return func(c *Client) {
|
||||
c.KeepAlivePeriod = t
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
package client_socks5
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
"github.com/thinkgos/go-socks5/statute"
|
||||
)
|
||||
|
||||
type underUDPConn struct {
|
||||
udpConn *net.UDPConn
|
||||
bufferPool bufferpool.BufPool
|
||||
remoteAddress net.Addr
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) Read(b []byte) (int, error) {
|
||||
b1 := sf.bufferPool.Get()
|
||||
defer sf.bufferPool.Put(b1)
|
||||
|
||||
n, err := sf.udpConn.Read(b1[:cap(b1)])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
datagram, err := statute.ParseDatagram(b1[:n])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = copy(b, datagram.Data)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) Write(b []byte) (int, error) {
|
||||
datagram, err := statute.NewDatagram(sf.remoteAddress.String(), b)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return sf.udpConn.Write(datagram.Bytes())
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) Close() error {
|
||||
return sf.udpConn.Close()
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) LocalAddr() net.Addr {
|
||||
return sf.udpConn.LocalAddr()
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) RemoteAddr() net.Addr {
|
||||
return sf.remoteAddress
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) SetDeadline(t time.Time) error {
|
||||
return sf.udpConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) SetReadDeadline(t time.Time) error {
|
||||
return sf.udpConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (sf *underUDPConn) SetWriteDeadline(t time.Time) error {
|
||||
return sf.udpConn.SetWriteDeadline(t)
|
||||
}
|
|
@ -10,6 +10,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
"github.com/thinkgos/go-socks5/statute"
|
||||
)
|
||||
|
||||
|
@ -49,7 +50,7 @@ func TestRequest_Connect(t *testing.T) {
|
|||
rules: NewPermitAll(),
|
||||
resolver: DNSResolver{},
|
||||
logger: NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags)),
|
||||
bufferPool: NewPool(32 * 1024),
|
||||
bufferPool: bufferpool.NewPool(32 * 1024),
|
||||
}
|
||||
|
||||
// Create the connect request
|
||||
|
@ -106,7 +107,7 @@ func TestRequest_Connect_RuleFail(t *testing.T) {
|
|||
rules: NewPermitNone(),
|
||||
resolver: DNSResolver{},
|
||||
logger: NewLogger(log.New(os.Stdout, "socks5: ", log.LstdFlags)),
|
||||
bufferPool: NewPool(32 * 1024),
|
||||
bufferPool: bufferpool.NewPool(32 * 1024),
|
||||
}
|
||||
|
||||
// Create the connect request
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
)
|
||||
|
||||
// Option user's option
|
||||
|
@ -11,7 +13,7 @@ type Option func(s *Server)
|
|||
|
||||
// WithBufferPool can be provided to implement custom buffer pool
|
||||
// By default, buffer pool use size is 32k
|
||||
func WithBufferPool(bufferPool BufferPool) Option {
|
||||
func WithBufferPool(bufferPool bufferpool.BufPool) Option {
|
||||
return func(s *Server) {
|
||||
s.bufferPool = bufferPool
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/thinkgos/go-socks5/bufferpool"
|
||||
"github.com/thinkgos/go-socks5/statute"
|
||||
)
|
||||
|
||||
|
@ -48,7 +49,7 @@ type Server struct {
|
|||
// Optional function for dialing out
|
||||
dial func(ctx context.Context, network, addr string) (net.Conn, error)
|
||||
// buffer pool
|
||||
bufferPool BufferPool
|
||||
bufferPool bufferpool.BufPool
|
||||
// goroutine pool
|
||||
gPool GPool
|
||||
// user's handle
|
||||
|
@ -62,7 +63,7 @@ func NewServer(opts ...Option) *Server {
|
|||
server := &Server{
|
||||
authMethods: make(map[uint8]Authenticator),
|
||||
authCustomMethods: []Authenticator{&NoAuthAuthenticator{}},
|
||||
bufferPool: NewPool(32 * 1024),
|
||||
bufferPool: bufferpool.NewPool(32 * 1024),
|
||||
resolver: DNSResolver{},
|
||||
rules: NewPermitAll(),
|
||||
logger: NewLogger(log.New(ioutil.Discard, "socks5: ", log.LstdFlags)),
|
||||
|
|
|
@ -100,6 +100,7 @@ func TestSOCKS5_Connect(t *testing.T) {
|
|||
out := make([]byte, len(expected))
|
||||
conn.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(conn, out)
|
||||
conn.SetDeadline(time.Time{}) // nolint: errcheck
|
||||
require.NoError(t, err)
|
||||
// Ignore the port
|
||||
out[12] = 0
|
||||
|
@ -110,7 +111,7 @@ func TestSOCKS5_Connect(t *testing.T) {
|
|||
func TestSOCKS5_Associate(t *testing.T) {
|
||||
locIP := net.ParseIP("127.0.0.1")
|
||||
// Create a local listener
|
||||
lAddr := &net.UDPAddr{IP: locIP, Port: 12398}
|
||||
lAddr := &net.UDPAddr{IP: locIP, Port: 12399}
|
||||
l, err := net.ListenUDP("udp", lAddr)
|
||||
require.NoError(t, err)
|
||||
defer l.Close()
|
||||
|
@ -173,8 +174,9 @@ func TestSOCKS5_Associate(t *testing.T) {
|
|||
}
|
||||
|
||||
out := make([]byte, len(expected))
|
||||
_ = conn.SetDeadline(time.Now().Add(time.Second))
|
||||
conn.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(conn, out)
|
||||
conn.SetDeadline(time.Time{}) // nolint: errcheck
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, out)
|
||||
|
||||
|
@ -183,7 +185,7 @@ func TestSOCKS5_Associate(t *testing.T) {
|
|||
require.Equal(t, statute.VersionSocks5, rspHead.Version)
|
||||
require.Equal(t, statute.RepSuccess, rspHead.Response)
|
||||
|
||||
t.Logf("proxy bind listen port: %d", rspHead.BndAddress.Port)
|
||||
// t.Logf("proxy bind listen port: %d", rspHead.BndAddress.Port)
|
||||
|
||||
udpConn, err := net.DialUDP("udp", nil, &net.UDPAddr{
|
||||
IP: locIP,
|
||||
|
@ -246,6 +248,7 @@ func Test_SocksWithProxy(t *testing.T) {
|
|||
out := make([]byte, 4)
|
||||
conn.SetDeadline(time.Now().Add(time.Second)) // nolint: errcheck
|
||||
_, err = io.ReadFull(conn, out)
|
||||
conn.SetDeadline(time.Time{}) // nolint: errcheck
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []byte("pong"), out)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue