func getOriginalDst(clientConn *net.TCPConn) (ipv4 string, port uint16, newTCPConn *net.TCPConn) {
srcipport := fmt.Sprintf("%v", clientConn.RemoteAddr())
newTCPConn = nil
// net.TCPConn.File() will cause the receiver's (clientConn) socket to be placed in blocking mode.
// The workaround is to take the File returned by .File(), do getsockopt() to get the original
// destination, then create a new *net.TCPConn by calling net.Conn.FileConn(). The new TCPConn
// will be in non-blocking mode. What a pain.
clientConnFile, err := clientConn.File()
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|ERR: could not get a copy of the client connection's file object", srcipport)
return
} else {
clientConn.Close()
}
// Get original destination
// this is the only syscall in the Golang libs that I can find that returns 16 bytes
// Example result: &{Multiaddr:[2 0 31 144 206 190 36 45 0 0 0 0 0 0 0 0] Interface:0}
// port starts at the 3rd byte and is 2 bytes long (31 144 = port 8080)
// IPv4 address starts at the 5th byte, 4 bytes long (206 190 36 45)
addr, err := syscall.GetsockoptIPv6Mreq(int(clientConnFile.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
log.Debugf("getOriginalDst(): SO_ORIGINAL_DST=%+v\n", addr)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->FAILEDTOBEDETERMINED|getsocketopt(SO_ORIGINAL_DST) failed: %v", srcipport, err)
return
}
newConn, err := net.FileConn(clientConnFile)
if err != nil {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: could not create a FileConn fron clientConnFile=%+v: %v", srcipport, addr, clientConnFile, err)
return
}
if _, ok := newConn.(*net.TCPConn); ok {
newTCPConn = newConn.(*net.TCPConn)
clientConnFile.Close()
} else {
log.Infof("GETORIGINALDST|%v->?->%v|ERR: newConn is not a *net.TCPConn, instead it is: %T (%v)", srcipport, addr, newConn, newConn)
return
}
ipv4 = itod(uint(addr.Multiaddr[4])) + "." +
itod(uint(addr.Multiaddr[5])) + "." +
itod(uint(addr.Multiaddr[6])) + "." +
itod(uint(addr.Multiaddr[7]))
port = uint16(addr.Multiaddr[2]) << 8 + uint16(addr.Multiaddr[3])
return
}