Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions linux-tool/glue/glue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package glue

import (
"encoding/binary"
"log"
"syscall"
"unsafe"

"github.com/celzero/firestack/intra"
"github.com/celzero/firestack/intra/backend"
)

type MyBridge struct {
PIDCSV string
TIDCSV string
}

// implement intra.log.Console
func (bridge *MyBridge) Log(level int32, msg string) {
log.Printf("log level %d, %s", level, msg)
}

// implement intra.backend.Controller
func (bridge *MyBridge) Bind4(who, addrport string, fd int) {}
func (bridge *MyBridge) Bind6(who, addrport string, fd int) {}
func (bridge *MyBridge) Protect(who string, fd int) {}

// implement intra.SocketListener
func (bridge *MyBridge) Preflow(protocol, uid int32, src, dst, domains string) *intra.PreMark {
return &intra.PreMark{
UID: "-1",
IsUidSelf: false,
}
}
func (bridge *MyBridge) Flow(protocol, uid int32, src, dst, origdsts, domains, probableDomains, blocklists string) *intra.Mark {
return &intra.Mark{
PIDCSV: bridge.PIDCSV,
CID: "0",
UID: "-1",
}
}
func (bridge *MyBridge) Inflow(protocol, uid int32, src, dst string) *intra.Mark {
return &intra.Mark{
PIDCSV: bridge.PIDCSV,
CID: "0",
UID: "-1",
}
}
func (bridge *MyBridge) OnSocketClosed(*intra.SocketSummary) {}

// implement intra.backend.ResolverListener
func (bridge *MyBridge) OnDNSAdded(id string) {}
func (bridge *MyBridge) OnDNSRemoved(id string) {}
func (bridge *MyBridge) OnDNSStopped() {}

// implement intra.backend.DNSListener
func (bridge *MyBridge) OnQuery(uid, domain string, qtyp int) *backend.DNSOpts {
return &backend.DNSOpts{
PIDCSV: bridge.PIDCSV,
IPCSV: "",
TIDCSV: bridge.TIDCSV,
TIDSECCSV: "",
NOBLOCK: false,
}
}
func (bridge *MyBridge) OnResponse(*backend.DNSSummary) {}

// implement intra.backend.ServerListener
func (bridge *MyBridge) SvcRoute(sid, pid, network, sipport, dipport string) *backend.Tab {
return &backend.Tab{
CID: "0",
Block: false,
}
}
func (bridge *MyBridge) OnSvcComplete(*backend.ServerSummary) {}

// implement intra.backend.ProxyListener
func (bridge *MyBridge) OnProxyAdded(id string) {}
func (bridge *MyBridge) OnProxyRemoved(id string) {}
func (bridge *MyBridge) OnProxyStopped(id string) {}
func (bridge *MyBridge) OnProxiesStopped() {}

func Ioctl(fd int, req uint64, data []byte) (int, error) {
p := unsafe.Pointer(&data[0])
r1, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(req), uintptr(p))
if errno == 0 {
return int(r1), nil
} else {
return int(r1), errno
}
}
func OpenTUN(name string) (int, error) {
fd, err := syscall.Open("/dev/net/tun", syscall.O_RDWR, 600)
if err != nil {
return fd, err
}
var req [64]byte
copy(req[:15], []byte(name))
flags := uint16(syscall.IFF_TUN | syscall.IFF_NO_PI)
binary.NativeEndian.PutUint16(req[16:], flags)
_, err = Ioctl(fd, syscall.TUNSETIFF, req[:])
if err != nil {
defer syscall.Close(fd)
return -1, err
}
return fd, nil
}
func GenFDCmsg(fds []uint32) []byte {
cmsgLen := 16 + len(fds)*4
cmsg := make([]byte, cmsgLen)
binary.NativeEndian.PutUint64(cmsg[:], uint64(cmsgLen))
binary.NativeEndian.PutUint32(cmsg[8:], syscall.SOL_SOCKET)
binary.NativeEndian.PutUint32(cmsg[12:], syscall.SCM_RIGHTS)

offset := 16
for _, v := range fds {
binary.NativeEndian.PutUint32(cmsg[offset:], v)
offset += 4
}
return cmsg
}
107 changes: 107 additions & 0 deletions linux-tool/toy/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package main

Check failure on line 1 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

: # github.com/celzero/firestack/linux-tool/toy

import (
"encoding/binary"
"flag"
"fmt"
"net"
"os"
"os/exec"
"os/signal"
"syscall"

"github.com/celzero/firestack/intra"
"github.com/celzero/firestack/intra/backend"
"github.com/celzero/firestack/intra/settings"
"github.com/celzero/firestack/linux-tool/glue"
)

func p(e error) {
if e != nil {
panic(e)
}
}
func sendTUN(name string) {
sock := int(3)
defer syscall.Close(sock)
syscall.SetNonblock(sock, true)
tun, err := glue.OpenTUN(name)
p(err)
defer syscall.Close(tun)

ifInfo, err := net.InterfaceByName(name)
p(err)
var msg [4]byte
binary.NativeEndian.PutUint32(msg[:], uint32(ifInfo.MTU))
cmsg := glue.GenFDCmsg([]uint32{uint32(tun)})
err = syscall.Sendmsg(sock, msg[:], cmsg, nil, 0)
p(err)
}
func demo(targetPid int, tunName string, fakeDNS string) {
pair, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_SEQPACKET, 0)
p(err)
defer func() {
syscall.Close(pair[0])
syscall.Close(pair[1])
}()
syscall.SetNonblock(pair[0], true)

elfPath, err := os.Executable()
p(err)
c1 := exec.Command("/usr/bin/nsenter", "--target", fmt.Sprintf("%d", targetPid), "--user", "--net",
"--preserve-credentials", "--keep-caps",
elfPath, "-mode", "sendfd", "-tun", tunName)
c1.ExtraFiles = []*os.File{os.NewFile(uintptr(pair[1]), "")}
fmt.Println("starting child...")
output, err := c1.CombinedOutput()
if len(output) > 0 {
fmt.Printf("child's output:\n%s\n", string(output))
}
p(err)

var buf [4]byte
var cmsgBuf [20]byte
n, cmsgN, msgFlag, _, err := syscall.Recvmsg(pair[0], buf[:], cmsgBuf[:], 0)
p(err)
fmt.Printf("msg: %v\ncmsg: %v\nflag: %d\n", buf[:n], cmsgBuf[:cmsgN], msgFlag)
tun := int(binary.NativeEndian.Uint32(cmsgBuf[16:]))
defer syscall.Close(tun)
mtu := binary.NativeEndian.Uint32(buf[:])

settings.SetDialerOpts(settings.SplitDesync, settings.RetryNever, 0, false)
bridge := glue.MyBridge{
PIDCSV: backend.Base,
TIDCSV: backend.Preferred,
}
tunnel, err := intra.NewTunnel(tun, int(mtu), fakeDNS, nil, &bridge)

Check failure on line 76 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use &bridge (value of type *glue.MyBridge) as intra.Bridge value in argument to intra.NewTunnel: *glue.MyBridge does not implement intra.Bridge (wrong type for method Flow)

Check failure on line 76 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use &bridge (value of type *glue.MyBridge) as intra.Bridge value in argument to intra.NewTunnel: *glue.MyBridge does not implement intra.Bridge (wrong type for method Flow)
p(err)
defer tunnel.Disconnect()

err = intra.AddDoHTransport(tunnel, backend.Preferred, "https://[2620:fe::12]/dns-query", "2620:fe::12")

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use "2620:fe::12" (untyped string constant) as *backend.Gostr value in argument to intra.AddDoHTransport (compile)

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use "https://[2620:fe::12]/dns-query" (untyped string constant) as *backend.Gostr value in argument to intra.AddDoHTransport

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use backend.Preferred (untyped string constant "Preferred") as *backend.Gostr value in argument to intra.AddDoHTransport

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use "2620:fe::12" (untyped string constant) as *backend.Gostr value in argument to intra.AddDoHTransport (typecheck)

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use "https://[2620:fe::12]/dns-query" (untyped string constant) as *backend.Gostr value in argument to intra.AddDoHTransport

Check failure on line 80 in linux-tool/toy/main.go

View workflow job for this annotation

GitHub Actions / 🧭 Lint

cannot use backend.Preferred (untyped string constant "Preferred") as *backend.Gostr value in argument to intra.AddDoHTransport
p(err)

ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
wait1 := <-ch
fmt.Printf("received %v signal, exiting\n", wait1)
}
func main() {
var mode string
flag.StringVar(&mode, "mode", "main", "main or sendfd")
var tunName string
flag.StringVar(&tunName, "tun", "tun0", "")
var pid int
flag.IntVar(&pid, "target", -1, "target process to get namespaces from")
var fakeDNS string
flag.StringVar(&fakeDNS, "dns", "10.0.2.3:53", "DNS passed to intra.NewTunnel()")
flag.Parse()

switch mode {
case "main":
demo(pid, tunName, fakeDNS)
case "sendfd":
sendTUN(tunName)
default:
fmt.Printf("unknown mode: %s\n", mode)
}
}
Loading