add head file
This commit is contained in:
parent
f87199c59e
commit
fef547fd67
19
_example/main.go
Normal file
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
0
go.sum
Normal file
202
header.go
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)
|
||||
}
|
99
request.go
99
request.go
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user