fix server example

This commit is contained in:
zhengrui
2018-11-03 18:52:23 +08:00
parent 9e981f6c6b
commit afb424193e
6 changed files with 424 additions and 95 deletions
+24
View File
@@ -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)
+52
View File
@@ -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
View File
@@ -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))
}
+195
View File
@@ -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),
}
}
+70
View File
@@ -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
}
+66
View File
@@ -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)