mirror of
https://github.com/ginuerzh/gosocks5.git
synced 2024-08-11 17:54:31 +00:00
fix server example
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
selector gosocks5.Selector
|
||||
}
|
||||
|
||||
func (c *Client) Dial(addr string, options ...DialOption) (net.Conn, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// DialOptions describes the options for Transporter.Dial.
|
||||
type DialOptions struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// DialOption allows a common way to set dial options.
|
||||
type DialOption func(opts *DialOptions)
|
||||
@@ -0,0 +1,52 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
)
|
||||
|
||||
type clientSelector struct {
|
||||
methods []uint8
|
||||
User *url.Userinfo
|
||||
}
|
||||
|
||||
func (selector *clientSelector) Methods() []uint8 {
|
||||
return selector.methods
|
||||
}
|
||||
|
||||
func (selector *clientSelector) AddMethod(methods ...uint8) {
|
||||
selector.methods = append(selector.methods, methods...)
|
||||
}
|
||||
|
||||
func (selector *clientSelector) Select(methods ...uint8) (method uint8) {
|
||||
return
|
||||
}
|
||||
|
||||
func (selector *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||
switch method {
|
||||
case gosocks5.MethodUserPass:
|
||||
var username, password string
|
||||
if selector.User != nil {
|
||||
username = selector.User.Username()
|
||||
password, _ = selector.User.Password()
|
||||
}
|
||||
|
||||
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
|
||||
if err := req.Write(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := gosocks5.ReadUserPassResponse(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.Status != gosocks5.Succeeded {
|
||||
return nil, gosocks5.ErrAuthFailure
|
||||
}
|
||||
case gosocks5.MethodNoAcceptable:
|
||||
return nil, gosocks5.ErrBadMethod
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
+17
-95
@@ -1,110 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
"io"
|
||||
"flag"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/ginuerzh/gosocks5/server"
|
||||
)
|
||||
|
||||
var (
|
||||
laddr string
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&laddr, "l", ":1080", "SOCKS5 server address")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
log.SetFlags(log.Lshortfile | log.LstdFlags)
|
||||
|
||||
server := &gosocks5.Server{
|
||||
Addr: ":9999",
|
||||
Handle: handle,
|
||||
}
|
||||
|
||||
server.ListenAndServe()
|
||||
}
|
||||
|
||||
func handle(conn net.Conn, method uint8) error {
|
||||
defer conn.Close()
|
||||
|
||||
req, err := gosocks5.ReadRequest(conn)
|
||||
ln, err := net.Listen("tcp", laddr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
log.Fatal(err)
|
||||
}
|
||||
tconn, err := Connect(req.Addr.String())
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return err
|
||||
}
|
||||
defer tconn.Close()
|
||||
|
||||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
||||
if err := rep.Write(conn); err != nil {
|
||||
return err
|
||||
srv := &server.Server{
|
||||
Listener: ln,
|
||||
}
|
||||
|
||||
if err := Transport(conn, tconn); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Connect(addr string) (net.Conn, error) {
|
||||
taddr, err := net.ResolveTCPAddr("tcp", addr)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
return net.DialTCP("tcp", nil, taddr)
|
||||
}
|
||||
|
||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
buf := make([]byte, 32*1024)
|
||||
for {
|
||||
nr, er := src.Read(buf)
|
||||
//log.Println("cp r", nr, er)
|
||||
if nr > 0 {
|
||||
nw, ew := dst.Write(buf[:nr])
|
||||
//log.Println("cp w", nw, ew)
|
||||
if nw > 0 {
|
||||
written += int64(nw)
|
||||
}
|
||||
if ew != nil {
|
||||
err = ew
|
||||
break
|
||||
}
|
||||
/*
|
||||
if nr != nw {
|
||||
err = io.ErrShortWrite
|
||||
break
|
||||
}
|
||||
*/
|
||||
}
|
||||
if er == io.EOF {
|
||||
break
|
||||
}
|
||||
if er != nil {
|
||||
err = er
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Pipe(src io.Reader, dst io.Writer, c chan<- error) {
|
||||
_, err := Copy(dst, src)
|
||||
c <- err
|
||||
}
|
||||
|
||||
func Transport(conn, conn2 net.Conn) (err error) {
|
||||
rChan := make(chan error, 1)
|
||||
wChan := make(chan error, 1)
|
||||
|
||||
go Pipe(conn, conn2, wChan)
|
||||
go Pipe(conn2, conn, rChan)
|
||||
|
||||
select {
|
||||
case err = <-wChan:
|
||||
//log.Println("w exit", err)
|
||||
case err = <-rChan:
|
||||
//log.Println("r exit", err)
|
||||
}
|
||||
|
||||
return
|
||||
log.Fatal(srv.Serve(server.DefaultHandler))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultHandler is the default server handler.
|
||||
DefaultHandler Handler
|
||||
)
|
||||
|
||||
func init() {
|
||||
DefaultHandler = &serverHandler{
|
||||
selector: &serverSelector{},
|
||||
}
|
||||
}
|
||||
|
||||
// Handler is interface for server handler.
|
||||
type Handler interface {
|
||||
Handle(conn net.Conn) error
|
||||
}
|
||||
|
||||
type serverHandler struct {
|
||||
selector gosocks5.Selector
|
||||
}
|
||||
|
||||
func (h *serverHandler) Handle(conn net.Conn) error {
|
||||
conn = gosocks5.ServerConn(conn, h.selector)
|
||||
req, err := gosocks5.ReadRequest(conn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch req.Cmd {
|
||||
case gosocks5.CmdConnect:
|
||||
return h.handleConnect(conn, req)
|
||||
|
||||
case gosocks5.CmdBind:
|
||||
return h.handleBind(conn, req)
|
||||
|
||||
// case gosocks5.CmdUdp:
|
||||
// h.handleUDPRelay(conn, req)
|
||||
|
||||
default:
|
||||
return fmt.Errorf("%d: unsupported command", gosocks5.CmdUnsupported)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *serverHandler) handleConnect(conn net.Conn, req *gosocks5.Request) error {
|
||||
cc, err := net.Dial("tcp", req.Addr.String())
|
||||
if err != nil {
|
||||
rep := gosocks5.NewReply(gosocks5.HostUnreachable, nil)
|
||||
rep.Write(conn)
|
||||
return err
|
||||
}
|
||||
defer cc.Close()
|
||||
|
||||
rep := gosocks5.NewReply(gosocks5.Succeeded, nil)
|
||||
if err := rep.Write(conn); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return transport(conn, cc)
|
||||
}
|
||||
|
||||
var (
|
||||
trPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 1500)
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
func (h *serverHandler) handleBind(conn net.Conn, req *gosocks5.Request) error {
|
||||
addr := req.Addr.String()
|
||||
bindAddr, _ := net.ResolveTCPAddr("tcp", addr)
|
||||
ln, err := net.ListenTCP("tcp", bindAddr) // strict mode: if the port already in use, it will return error
|
||||
if err != nil {
|
||||
gosocks5.NewReply(gosocks5.Failure, nil).Write(conn)
|
||||
return err
|
||||
}
|
||||
|
||||
socksAddr := toSocksAddr(ln.Addr())
|
||||
// Issue: may not reachable when host has multi-interface
|
||||
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, socksAddr)
|
||||
if err := reply.Write(conn); err != nil {
|
||||
ln.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
var pconn net.Conn
|
||||
accept := func() <-chan error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(errc)
|
||||
defer ln.Close()
|
||||
|
||||
c, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
errc <- err
|
||||
return
|
||||
}
|
||||
pconn = c
|
||||
}()
|
||||
|
||||
return errc
|
||||
}
|
||||
|
||||
pc1, pc2 := net.Pipe()
|
||||
pipe := func() <-chan error {
|
||||
errc := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
defer close(errc)
|
||||
defer pc1.Close()
|
||||
|
||||
errc <- transport(conn, pc1)
|
||||
}()
|
||||
|
||||
return errc
|
||||
}
|
||||
|
||||
defer pc2.Close()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-accept():
|
||||
if err != nil || pconn == nil {
|
||||
return err
|
||||
}
|
||||
defer pconn.Close()
|
||||
|
||||
reply := gosocks5.NewReply(gosocks5.Succeeded, toSocksAddr(pconn.RemoteAddr()))
|
||||
if err := reply.Write(pc2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = transport(pc2, pconn); err != nil {
|
||||
}
|
||||
|
||||
return err
|
||||
case err := <-pipe():
|
||||
ln.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func transport(rw1, rw2 io.ReadWriter) error {
|
||||
errc := make(chan error, 1)
|
||||
go func() {
|
||||
buf := trPool.Get().([]byte)
|
||||
defer trPool.Put(buf)
|
||||
|
||||
_, err := io.CopyBuffer(rw1, rw2, buf)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
go func() {
|
||||
buf := trPool.Get().([]byte)
|
||||
defer trPool.Put(buf)
|
||||
|
||||
_, err := io.CopyBuffer(rw2, rw1, buf)
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
err := <-errc
|
||||
if err != nil && err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func toSocksAddr(addr net.Addr) *gosocks5.Addr {
|
||||
host := "0.0.0.0"
|
||||
port := 0
|
||||
if addr != nil {
|
||||
h, p, _ := net.SplitHostPort(addr.String())
|
||||
host = h
|
||||
port, _ = strconv.Atoi(p)
|
||||
}
|
||||
return &gosocks5.Addr{
|
||||
Type: gosocks5.AddrIPv4,
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/ginuerzh/gosocks5"
|
||||
)
|
||||
|
||||
type serverSelector struct {
|
||||
methods []uint8
|
||||
Users []*url.Userinfo
|
||||
}
|
||||
|
||||
func (selector *serverSelector) Methods() []uint8 {
|
||||
return selector.methods
|
||||
}
|
||||
|
||||
func (selector *serverSelector) AddMethod(methods ...uint8) {
|
||||
selector.methods = append(selector.methods, methods...)
|
||||
}
|
||||
|
||||
func (selector *serverSelector) Select(methods ...uint8) (method uint8) {
|
||||
method = gosocks5.MethodNoAuth
|
||||
|
||||
// when user/pass is set, auth is mandatory
|
||||
if len(selector.Users) > 0 {
|
||||
method = gosocks5.MethodUserPass
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (selector *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
|
||||
switch method {
|
||||
case gosocks5.MethodUserPass:
|
||||
req, err := gosocks5.ReadUserPassRequest(conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
valid := false
|
||||
for _, user := range selector.Users {
|
||||
username := user.Username()
|
||||
password, _ := user.Password()
|
||||
if (req.Username == username && req.Password == password) ||
|
||||
(req.Username == username && password == "") ||
|
||||
(username == "" && req.Password == password) {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(selector.Users) > 0 && !valid {
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, gosocks5.ErrAuthFailure
|
||||
}
|
||||
|
||||
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
|
||||
if err := resp.Write(conn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case gosocks5.MethodNoAcceptable:
|
||||
return nil, gosocks5.ErrBadMethod
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Server is a SOCKS5 server.
|
||||
type Server struct {
|
||||
Listener net.Listener
|
||||
}
|
||||
|
||||
// Addr returns the address of the server
|
||||
func (s *Server) Addr() net.Addr {
|
||||
return s.Listener.Addr()
|
||||
}
|
||||
|
||||
// Serve serves incoming requests.
|
||||
func (s *Server) Serve(h Handler, options ...ServerOption) error {
|
||||
if s.Listener == nil {
|
||||
ln, err := net.ListenTCP("tcp", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Listener = ln
|
||||
}
|
||||
if h == nil {
|
||||
h = DefaultHandler
|
||||
}
|
||||
|
||||
l := s.Listener
|
||||
var tempDelay time.Duration
|
||||
for {
|
||||
conn, e := l.Accept()
|
||||
if e != nil {
|
||||
if ne, ok := e.(net.Error); ok && ne.Temporary() {
|
||||
if tempDelay == 0 {
|
||||
tempDelay = 5 * time.Millisecond
|
||||
} else {
|
||||
tempDelay *= 2
|
||||
}
|
||||
if max := 1 * time.Second; tempDelay > max {
|
||||
tempDelay = max
|
||||
}
|
||||
time.Sleep(tempDelay)
|
||||
continue
|
||||
}
|
||||
return e
|
||||
}
|
||||
tempDelay = 0
|
||||
|
||||
go h.Handle(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the socks5 server
|
||||
func (s *Server) Close() error {
|
||||
return s.Listener.Close()
|
||||
}
|
||||
|
||||
// ServerOptions is options for server.
|
||||
type ServerOptions struct {
|
||||
}
|
||||
|
||||
// ServerOption allows a common way to set server options.
|
||||
type ServerOption func(opts *ServerOptions)
|
||||
Reference in New Issue
Block a user