73 lines
2 KiB
Go
73 lines
2 KiB
Go
|
|
// © 2026 TechnoLibre (http://www.technolibre.ca)
|
||
|
|
// License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||
|
|
|
||
|
|
//go:build linux
|
||
|
|
|
||
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"net"
|
||
|
|
"syscall"
|
||
|
|
)
|
||
|
|
|
||
|
|
// SO_ORIGINAL_DST : l'option que Netfilter renseigne sur une socket
|
||
|
|
// redirigée. Elle n'est pas dans le paquet « syscall », d'où la constante.
|
||
|
|
const soOriginalDst = 80
|
||
|
|
|
||
|
|
// originalDst rend l'adresse que l'invité voulait joindre.
|
||
|
|
//
|
||
|
|
// En interception transparente le client croit parler au serveur amont : la
|
||
|
|
// socket acceptée porte l'adresse du cache, et la destination d'origine n'est
|
||
|
|
// connue que du noyau, qui la garde pour cette redirection. Sans elle, un
|
||
|
|
// tunnel opaque n'a nulle part à aller.
|
||
|
|
func originalDst(c net.Conn) (string, error) {
|
||
|
|
// La connexion peut être enveloppée par le rejeu du ClientHello : c'est
|
||
|
|
// la socket dessous qui porte l'option.
|
||
|
|
for {
|
||
|
|
if r, ok := c.(*replayed); ok {
|
||
|
|
c = r.Conn
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
break
|
||
|
|
}
|
||
|
|
tcp, ok := c.(*net.TCPConn)
|
||
|
|
if !ok {
|
||
|
|
return "", errors.New("la connexion n'est pas une socket TCP")
|
||
|
|
}
|
||
|
|
raw, err := tcp.SyscallConn()
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
var addr string
|
||
|
|
var inner error
|
||
|
|
err = raw.Control(func(fd uintptr) {
|
||
|
|
// GetsockoptIPv6Mreq rend seize octets bruts, ce qui est exactement
|
||
|
|
// la taille d'un « sockaddr_in » : famille, port, adresse. Le nom de
|
||
|
|
// l'appel ne correspond pas à l'usage, mais c'est le seul du paquet
|
||
|
|
// « syscall » qui rende un tampon de cette taille.
|
||
|
|
mreq, e := syscall.GetsockoptIPv6Mreq(int(fd), syscall.IPPROTO_IP, soOriginalDst)
|
||
|
|
if e != nil {
|
||
|
|
inner = e
|
||
|
|
return
|
||
|
|
}
|
||
|
|
port := int(mreq.Multiaddr[2])<<8 | int(mreq.Multiaddr[3])
|
||
|
|
ip := net.IPv4(
|
||
|
|
mreq.Multiaddr[4], mreq.Multiaddr[5],
|
||
|
|
mreq.Multiaddr[6], mreq.Multiaddr[7],
|
||
|
|
)
|
||
|
|
addr = fmt.Sprintf("%s:%d", ip.String(), port)
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
if inner != nil {
|
||
|
|
return "", inner
|
||
|
|
}
|
||
|
|
if addr == "" || addr == "0.0.0.0:0" {
|
||
|
|
return "", errors.New("aucune destination d'origine : la connexion n'est pas redirigée")
|
||
|
|
}
|
||
|
|
return addr, nil
|
||
|
|
}
|