@ -9,43 +9,58 @@ import (
"github.com/subgraph/fw-daemon/socks5"
"github.com/subgraph/fw-daemon/socks5"
)
)
type S ocksChainConfig struct {
type s ocksChainConfig struct {
TargetSocksNet string
TargetSocksNet string
TargetSocksAddr string
TargetSocksAddr string
ListenSocksNet string
ListenSocksNet string
ListenSocksAddr string
ListenSocksAddr string
}
}
type session struct {
type socksChain struct {
cfg * SocksChainConfig
cfg * socksChainConfig
dbus * dbusServer
listener net . Listener
wg * sync . WaitGroup
}
type socksChainSession struct {
cfg * socksChainConfig
clientConn net . Conn
clientConn net . Conn
upstreamConn net . Conn
upstreamConn net . Conn
req * socks5 . Request
bndAddr * socks5 . Address
optData [ ] byte
}
req * socks5 . Request
func NewSocksChain ( cfg * socksChainConfig , wg * sync . WaitGroup , dbus * dbusServer ) * socksChain {
bndAddr * socks5 . Address
chain := socksChain {
optData [ ] byte
cfg : cfg ,
dbus : dbus ,
wg : wg ,
}
return & chain
}
}
// InitSocksListener initializes the SOCKS 5 server and starts
// Start initializes the SOCKS 5 server and starts
// accepting connections.
// accepting connections.
func InitSocksListener ( cfg * SocksChainConfig , wg * sync . WaitGroup ) {
func ( s * socksChain ) start ( ) {
ln , err := net . Listen ( cfg . ListenSocksNet , cfg . ListenSocksAddr )
var err error
s . listener , err = net . Listen ( s . cfg . ListenSocksNet , s . cfg . ListenSocksAddr )
if err != nil {
if err != nil {
log . Error ( "ERR/socks: Failed to listen on the socks address: %v" , err )
log . Error ( "ERR/socks: Failed to listen on the socks address: %v" , err )
os . Exit ( 1 )
os . Exit ( 1 )
}
}
wg. Add ( 1 )
s. wg. Add ( 1 )
go socksAcceptLoop ( cfg , ln , wg )
go s . socksAcceptLoop ( )
}
}
func socksAcceptLoop ( cfg * SocksChainConfig , ln net . Listener , wg * sync . WaitGroup ) error {
func ( s * socksChain ) socksAcceptLoop ( ) error {
defer wg. Done ( )
defer s. wg. Done ( )
defer ln. Close ( )
defer s. liste ner . Close ( )
for {
for {
conn , err := ln. Accept ( )
conn , err := s. liste ner . Accept ( )
if err != nil {
if err != nil {
if e , ok := err . ( net . Error ) ; ok && ! e . Temporary ( ) {
if e , ok := err . ( net . Error ) ; ok && ! e . Temporary ( ) {
log . Info ( "ERR/socks: Failed to Accept(): %v" , err )
log . Info ( "ERR/socks: Failed to Accept(): %v" , err )
@ -53,55 +68,55 @@ func socksAcceptLoop(cfg *SocksChainConfig, ln net.Listener, wg *sync.WaitGroup)
}
}
continue
continue
}
}
s := & s ession{ cfg : cfg , clientConn : conn }
s ession := & s ocksChainS ession{ cfg : s . cfg , clientConn : conn }
go s . sessionWorker ( )
go s ession . sessionWorker ( )
}
}
}
}
func ( s * s ession) sessionWorker ( ) {
func ( c * socksChainS ession) sessionWorker ( ) {
defer s . clientConn . Close ( )
defer c . clientConn . Close ( )
clientAddr := s . clientConn . RemoteAddr ( )
clientAddr := c . clientConn . RemoteAddr ( )
log . Info ( "INFO/socks: New connection from: %v" , clientAddr )
log . Info ( "INFO/socks: New connection from: %v" , clientAddr )
// Do the SOCKS handshake with the client, and read the command.
// Do the SOCKS handshake with the client, and read the command.
var err error
var err error
if s . req , err = socks5 . Handshake ( s . clientConn ) ; err != nil {
if c . req , err = socks5 . Handshake ( c . clientConn ) ; err != nil {
log . Info ( "ERR/socks: Failed SOCKS5 handshake: %v" , err )
log . Info ( "ERR/socks: Failed SOCKS5 handshake: %v" , err )
return
return
}
}
switch s . req . Cmd {
switch c . req . Cmd {
case socks5 . CommandTorResolve , socks5 . CommandTorResolvePTR :
case socks5 . CommandTorResolve , socks5 . CommandTorResolvePTR :
err = s . dispatchTorSOCKS ( )
err = c . dispatchTorSOCKS ( )
// If we reach here, the request has been dispatched and completed.
// If we reach here, the request has been dispatched and completed.
if err == nil {
if err == nil {
// Successfully even, send the response back with the addres s .
// Successfully even, send the response back with the addres c .
s . req . ReplyAddr ( socks5 . ReplySucceeded , s . bndAddr )
c . req . ReplyAddr ( socks5 . ReplySucceeded , c . bndAddr )
}
}
return
return
case socks5 . CommandConnect :
case socks5 . CommandConnect :
default :
default :
// Should *NEVER* happen, validated as part of handshake.
// Should *NEVER* happen, validated as part of handshake.
log . Info ( "BUG/socks: Unsupported SOCKS command: 0x%02x" , s . req . Cmd )
log . Info ( "BUG/socks: Unsupported SOCKS command: 0x%02x" , c . req . Cmd )
s . req . Reply ( socks5 . ReplyCommandNotSupported )
c . req . Reply ( socks5 . ReplyCommandNotSupported )
return
return
}
}
err = s . dispatchTorSOCKS ( )
err = c . dispatchTorSOCKS ( )
if err != nil {
if err != nil {
return
return
}
}
s . req . Reply ( socks5 . ReplySucceeded )
c . req . Reply ( socks5 . ReplySucceeded )
defer s . upstreamConn . Close ( )
defer c . upstreamConn . Close ( )
if s . optData != nil {
if c . optData != nil {
if _ , err = s. upstreamConn . Write ( s . optData ) ; err != nil {
if _ , err = c. upstreamConn . Write ( c . optData ) ; err != nil {
log . Info ( "ERR/socks: Failed writing OptData: %v" , err )
log . Info ( "ERR/socks: Failed writing OptData: %v" , err )
return
return
}
}
s . optData = nil
c . optData = nil
}
}
// A upstream connection has been established, push data back and forth
// A upstream connection has been established, push data back and forth
@ -115,17 +130,17 @@ func (s *session) sessionWorker() {
io . Copy ( dst , src )
io . Copy ( dst , src )
}
}
go copyLoop ( s. upstreamConn , s . clientConn )
go copyLoop ( c. upstreamConn , c . clientConn )
go copyLoop ( s. clientConn , s . upstreamConn )
go copyLoop ( c. clientConn , c . upstreamConn )
wg . Wait ( )
wg . Wait ( )
log . Info ( "INFO/socks: Closed SOCKS connection from: %v" , clientAddr )
log . Info ( "INFO/socks: Closed SOCKS connection from: %v" , clientAddr )
}
}
func ( s * s ession) dispatchTorSOCKS ( ) ( err error ) {
func ( c * socksChainS ession) dispatchTorSOCKS ( ) ( err error ) {
s. upstreamConn , s . bndAddr , err = socks5 . Redispatch ( s. cfg . TargetSocksNet , s . cfg . TargetSocksAddr , s . req )
c. upstreamConn , c . bndAddr , err = socks5 . Redispatch ( c. cfg . TargetSocksNet , c . cfg . TargetSocksAddr , c . req )
if err != nil {
if err != nil {
s . req . Reply ( socks5 . ErrorToReplyCode ( err ) )
c . req . Reply ( socks5 . ErrorToReplyCode ( err ) )
}
}
return
return
}
}