Last active
July 9, 2017 17:05
-
-
Save korc/6538214e8de2e1dcc4cd491b8fc36356 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This program is intented to use with iptables REDIRECT directive | |
// for example if your provider is blocking outgoing port 25 connections | |
// 1) connect to server in datacenter which allows connections | |
// ssh -D 1080 user@example.com | |
// 2) redirect all outgoing SMTP connections through this here | |
// iptables -t nat -A OUTPUT -p tcp --dport 25 -j REDIRECT --to-ports 4567 | |
// *Note* for router configuration (iptables PREROUTING chain) you have to listen on other IP than 127.0.0.1 | |
package main | |
import ( | |
"flag" | |
"fmt" | |
"io" | |
"log" | |
"net" | |
"sync" | |
"syscall" | |
"unsafe" | |
"golang.org/x/net/proxy" | |
) | |
const SO_ORIGINAL_DST = 80 | |
func getsockopt(s int, level int, name int, val uintptr, vallen *uint32) (err error) { | |
_, _, errno := syscall.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen)), 0) | |
if errno != 0 { | |
err = errno | |
} | |
return | |
} | |
func getOrigDest(conn net.Conn) (ret *net.TCPAddr, err error) { | |
f, err := conn.(*net.TCPConn).File() | |
if err != nil { | |
return | |
} | |
fd := f.Fd() | |
var addr syscall.RawSockaddrAny | |
size := uint32(unsafe.Sizeof(addr)) | |
if err = getsockopt(int(fd), syscall.SOL_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), &size); err != nil { | |
return | |
} | |
f.Close() | |
switch addr.Addr.Family { | |
case syscall.AF_INET: | |
pp := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&addr)) | |
pa := (*[2]byte)(unsafe.Pointer(&pp.Port)) | |
ret = &net.TCPAddr{Port: (int(pa[0]) << 8) + int(pa[1]), | |
IP: net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3])} | |
default: | |
err = fmt.Errorf("Unknown address family: %d", addr.Addr.Family) | |
return | |
} | |
return | |
} | |
func copyTcp(w, r net.Conn, wg sync.WaitGroup) { | |
defer wg.Done() | |
defer w.(*net.TCPConn).CloseWrite() | |
io.Copy(w, r) | |
} | |
func handleClient(conn net.Conn, origDst *net.TCPAddr, dialer proxy.Dialer) { | |
defer conn.Close() | |
if remote, err := dialer.Dial("tcp", origDst.String()); err != nil { | |
log.Print("error dialing to remote: ", err) | |
} else { | |
defer remote.Close() | |
var wg sync.WaitGroup | |
wg.Add(2) | |
go copyTcp(remote, conn, wg) | |
go copyTcp(conn, remote, wg) | |
wg.Wait() | |
} | |
} | |
func main() { | |
listenAddr := flag.String("listen", "127.0.0.1:4567", "Listen ip:port") | |
socksAddr := flag.String("socks", "localhost:1080", "Socks5 server address") | |
flag.Parse() | |
dialer, err := proxy.SOCKS5("tcp", *socksAddr, nil, proxy.Direct) | |
if err != nil { | |
log.Fatal("creating dialer: ", err) | |
} | |
ls, err := net.Listen("tcp", *listenAddr) | |
if err != nil { | |
log.Fatal("Listening on socket: ", err) | |
} | |
defer ls.Close() | |
for { | |
cl, err := ls.Accept() | |
if err != nil { | |
log.Fatal("Accepting connection: ", err) | |
} | |
origDst, err := getOrigDest(cl) | |
if err != nil { | |
log.Printf("Cannot get original destination: %v", err) | |
cl.Close() | |
continue | |
} | |
log.Printf("Connecting %s -> [socks5:%s] -> %s\n", cl.RemoteAddr(), *socksAddr, origDst.String()) | |
go handleClient(cl, origDst, dialer) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment