add head file

This commit is contained in:
mo 2020-04-19 21:37:05 +08:00
parent f87199c59e
commit fef547fd67
5 changed files with 249 additions and 75 deletions

19
_example/main.go Normal file

@ -0,0 +1,19 @@
package main
import (
"github.com/thinkgos/socks5"
)
func main() {
// Create a SOCKS5 server
conf := &socks5.Config{}
server, err := socks5.New(conf)
if err != nil {
panic(err)
}
// Create SOCKS5 proxy on localhost port 8000
if err := server.ListenAndServe("tcp", "127.0.0.1:1080"); err != nil {
panic(err)
}
}

0
go.sum Normal file

202
header.go Normal file

@ -0,0 +1,202 @@
package socks5
import (
"bufio"
"context"
"fmt"
"io"
"log"
"net"
"strconv"
)
const (
// protocol version
socks4Version = uint8(4)
socks5Version = uint8(5)
// request command
ConnectCommand = uint8(1)
BindCommand = uint8(2)
AssociateCommand = uint8(3) // udp associate
// address type
ipv4Address = uint8(1)
fqdnAddress = uint8(3) // domain
ipv6Address = uint8(4)
)
//
const (
successReply uint8 = iota
serverFailure
ruleFailure
networkUnreachable
hostUnreachable
connectionRefused
ttlExpired
commandNotSupported
addrTypeNotSupported
// 0x09 - 0xff unassigned
)
const (
// common fields
reqProtocolVersionBytePos = uint8(0) // proto version pos
reqCommandBytePos = uint8(1)
reqAddrBytePos = uint8(4)
reqStartLen = uint8(4)
reqVersionLen = 1
reqCommandLen = 1
reqPortLen = 2
reqReservedLen = 1
reqAddrTypeLen = 1
reqIPv4Addr = 4
reqIPv6Addr = 8
reqFQDNAddr = 249
//position settings for socks4
req4PortBytePos = uint8(2)
)
// AddressRewriter is used to rewrite a destination transparently
type AddressRewriter interface {
Rewrite(ctx context.Context, request *Request) (context.Context, *AddrSpec)
}
// AddrSpec is used to return the target AddrSpec
// which may be specified as IPv4, IPv6, or a FQDN
type AddrSpec struct {
FQDN string
IP net.IP
Port int
}
func (a *AddrSpec) String() string {
if a.FQDN != "" {
return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
}
return fmt.Sprintf("%s:%d", a.IP, a.Port)
}
// Address returns a string suitable to dial; prefer returning IP-based
// address, fallback to FQDN
func (a AddrSpec) Address() string {
if 0 != len(a.IP) {
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}
return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port))
}
// Header represents the SOCKS5/SOCKS4 header, it contains everything that is not payload
type Header struct {
// Version of socks protocol for message
Version uint8
// Socks Command "connect","bind","associate"
Command uint8
// Reserved byte
Reserved uint8 // only socks5 support
// Address in socks message
Address AddrSpec
// private stuff set when Header parsed
addrType uint8
addrLen int
headerLen int
}
func (h Header) Bytes() (b []byte) {
b = append(b, h.Version)
b = append(b, h.Command)
hiPort, loPort := breakPort(h.Address.Port)
if h.Version == socks4Version {
b = append(b, hiPort, loPort)
b = append(b, h.Address.IP...)
} else if h.Version == socks5Version {
b = append(b, h.Reserved)
b = append(b, h.addrType)
if h.addrType == fqdnAddress {
b = append(b, []byte(h.Address.FQDN)...)
} else {
b = append(b, h.Address.IP...)
}
b = append(b, hiPort, loPort)
}
return b
}
func Parse(r io.Reader) (hd Header, err error) {
h := make([]byte, 5)
bufConn := bufio.NewReader(r)
if h, err = bufConn.Peek(5); err != nil {
return hd, fmt.Errorf("failed to get header: %v", err)
}
hd.Version = h[0]
hd.Command = h[1]
if hd.Version != socks5Version && hd.Version != socks4Version {
return hd, fmt.Errorf("unrecognized SOCKS version")
}
if hd.Command != ConnectCommand && hd.Command != BindCommand && hd.Command != AssociateCommand {
return hd, fmt.Errorf("unrecognized command")
}
if hd.Version == socks4Version && hd.Command == AssociateCommand {
return hd, fmt.Errorf("wrong version for command")
}
hd.headerLen = reqVersionLen + reqCommandLen + reqPortLen
if hd.Version == socks4Version {
hd.addrLen = reqIPv4Addr
} else if hd.Version == socks5Version {
hd.Reserved = h[2]
hd.addrType = h[3]
hd.headerLen += reqReservedLen + reqAddrTypeLen
switch hd.addrType {
case fqdnAddress:
hd.headerLen += 1
hd.addrLen = int(h[4])
case ipv4Address:
hd.addrLen = reqIPv4Addr
case ipv6Address:
hd.addrLen = reqIPv6Addr
default:
return hd, unrecognizedAddrType
}
}
hd.headerLen += hd.addrLen
bHeader := make([]byte, hd.headerLen)
if _, err = io.ReadAtLeast(bufConn, bHeader, hd.headerLen); err != nil {
return hd, fmt.Errorf("failed to get header address: %v", err)
}
switch hd.addrType {
case ipv4Address:
hd.Address.IP = bHeader[reqAddrBytePos : reqAddrBytePos+reqIPv4Addr]
if hd.Version == socks4Version {
hd.Address.Port = buildPort(bHeader[req4PortBytePos], bHeader[req4PortBytePos+1])
} else if hd.Version == socks5Version {
hd.Address.Port = buildPort(bHeader[hd.headerLen-2], bHeader[hd.headerLen-1])
}
case ipv6Address:
hd.Address.IP = bHeader[reqAddrBytePos : reqAddrBytePos+reqIPv6Addr]
hd.Address.Port = buildPort(bHeader[hd.headerLen-2], bHeader[hd.headerLen-1])
case fqdnAddress:
hd.Address.FQDN = string(bHeader[reqAddrBytePos : hd.headerLen-reqPortLen])
hd.Address.Port = buildPort(bHeader[hd.headerLen-2], bHeader[hd.headerLen-1])
}
log.Printf("%+v", hd)
return hd, nil
//payload = make([]byte, 4)
//if _, err := bufConn.Read(payload); err != nil {
// return hd, payload, fmt.Errorf("failed read payload: %v", err)
//}
//return hd, payload, nil
}
func buildPort(hi, lo byte) int {
return (int(hi) << 8) | int(lo)
}
func breakPort(port int) (hi, lo byte) {
return byte(port >> 8), byte(port)
}

@ -5,66 +5,16 @@ import (
"fmt"
"io"
"net"
"strconv"
"strings"
)
const (
ConnectCommand = uint8(1)
BindCommand = uint8(2)
AssociateCommand = uint8(3)
ipv4Address = uint8(1)
fqdnAddress = uint8(3)
ipv6Address = uint8(4)
)
const (
successReply uint8 = iota
serverFailure
ruleFailure
networkUnreachable
hostUnreachable
connectionRefused
ttlExpired
commandNotSupported
addrTypeNotSupported
)
var (
unrecognizedAddrType = fmt.Errorf("Unrecognized address type")
)
// AddressRewriter is used to rewrite a destination transparently
type AddressRewriter interface {
Rewrite(ctx context.Context, request *Request) (context.Context, *AddrSpec)
}
// AddrSpec is used to return the target AddrSpec
// which may be specified as IPv4, IPv6, or a FQDN
type AddrSpec struct {
FQDN string
IP net.IP
Port int
}
func (a *AddrSpec) String() string {
if a.FQDN != "" {
return fmt.Sprintf("%s (%s):%d", a.FQDN, a.IP, a.Port)
}
return fmt.Sprintf("%s:%d", a.IP, a.Port)
}
// Address returns a string suitable to dial; prefer returning IP-based
// address, fallback to FQDN
func (a AddrSpec) Address() string {
if 0 != len(a.IP) {
return net.JoinHostPort(a.IP.String(), strconv.Itoa(a.Port))
}
return net.JoinHostPort(a.FQDN, strconv.Itoa(a.Port))
}
// A Request represents request received by a server
type Request struct {
Header
// Protocol version
Version uint8
// Requested command
@ -103,15 +53,22 @@ func NewRequest(bufConn io.Reader) (*Request, error) {
if err != nil {
return nil, err
}
request := &Request{
return &Request{
Version: socks5Version,
Command: header[1],
DestAddr: dest,
bufConn: bufConn,
}
return request, nil
}, nil
//hd, err := Parse(bufConn)
//if err != nil {
// return nil, err
//}
//return &Request{
// Version: hd.Version,
// Command: hd.Command,
// DestAddr: &hd.Address,
// bufConn: bufConn,
//}, nil
}
// handleRequest is used for request processing after authentication
@ -309,29 +266,33 @@ func addrSpecFromNetAddr(addr net.Addr) *AddrSpec {
// sendReply is used to send a reply message
func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
var head Header
// Format the address
var addrType uint8
var addrBody []byte
var addrPort uint16
head.Version = socks5Version
head.Command = resp
head.Reserved = 0
switch {
case addr == nil:
addrType = ipv4Address
head.addrType = ipv4Address
addrBody = []byte{0, 0, 0, 0}
addrPort = 0
case addr.FQDN != "":
addrType = fqdnAddress
head.addrType = fqdnAddress
addrBody = append([]byte{byte(len(addr.FQDN))}, addr.FQDN...)
addrPort = uint16(addr.Port)
case addr.IP.To4() != nil:
addrType = ipv4Address
addrBody = []byte(addr.IP.To4())
head.addrType = ipv4Address
addrBody = addr.IP.To4()
addrPort = uint16(addr.Port)
case addr.IP.To16() != nil:
addrType = ipv6Address
addrBody = []byte(addr.IP.To16())
head.addrType = ipv6Address
addrBody = addr.IP.To16()
addrPort = uint16(addr.Port)
default:
@ -339,14 +300,10 @@ func sendReply(w io.Writer, resp uint8, addr *AddrSpec) error {
}
// Format the message
msg := make([]byte, 6+len(addrBody))
msg[0] = socks5Version
msg[1] = resp
msg[2] = 0 // Reserved
msg[3] = addrType
copy(msg[4:], addrBody)
msg[4+len(addrBody)] = byte(addrPort >> 8)
msg[4+len(addrBody)+1] = byte(addrPort & 0xff)
msg := make([]byte, 0, 6+len(addrBody))
msg = append(msg, head.Version, head.Command, head.Reserved, head.addrType)
msg = append(msg, addrBody...)
msg = append(msg, byte(addrPort>>8), byte(addrPort&0xff))
// Send the message
_, err := w.Write(msg)

@ -9,10 +9,6 @@ import (
"net"
)
const (
socks5Version = uint8(5)
)
// Config is used to setup and configure a Server
type Config struct {
// AuthMethods can be provided to implement custom authentication