Skip to content

Commit

Permalink
darwin: stun: support macos with wireguard-go version
Browse files Browse the repository at this point in the history
We don't support Wireguard App version currently.
Don know how to do it.

Signed-off-by: Date Huang <[email protected]>
  • Loading branch information
tjjh89017 committed Jan 29, 2025
1 parent 7a4e2ec commit b09070d
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 7 deletions.
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module github.com/tjjh89017/stunmesh-go

go 1.22
go 1.22.0

require (
github.com/cloudflare/cloudflare-go v0.114.0
github.com/google/wire v0.6.0
github.com/packetcap/go-pcap v0.0.0-20250128134747-e4098f8faef2
github.com/pion/stun v0.6.1
github.com/rs/zerolog v1.33.0
github.com/spf13/viper v1.19.0
Expand All @@ -19,6 +20,7 @@ require (
github.com/goccy/go-json v0.10.4 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/gopacket/gopacket v1.3.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
Expand All @@ -34,6 +36,7 @@ require (
github.com/pion/transport/v2 v2.2.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/wire v0.6.0 h1:HBkoIh4BdSxoyo9PveV8giw7ZsaBOvzWKfcg/6MrVwI=
github.com/google/wire v0.6.0/go.mod h1:F4QhpQ9EDIdJ1Mbop/NZBRB+5yrR6qg3BnctaoUk6NA=
github.com/gopacket/gopacket v1.3.1 h1:ZppWyLrOJNZPe5XkdjLbtuTkfQoxQ0xyMJzQCqtqaPU=
github.com/gopacket/gopacket v1.3.1/go.mod h1:3I13qcqSpB2R9fFQg866OOgzylYkZxLTmkvcXhvf6qg=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
Expand All @@ -47,6 +49,8 @@ github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE9
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/packetcap/go-pcap v0.0.0-20250128134747-e4098f8faef2 h1:yESodjSSmfhuIdhW/wh7l0r6hWV9DMAyTZROxWPND10=
github.com/packetcap/go-pcap v0.0.0-20250128134747-e4098f8faef2/go.mod h1:zIAoVKeWP0mz4zXY50UYQt6NLg2uwKRswMDcGEqOms4=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8=
Expand All @@ -70,6 +74,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
Expand All @@ -85,6 +91,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
Expand Down Expand Up @@ -135,6 +142,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down
214 changes: 208 additions & 6 deletions internal/stun/stun_darwin.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,225 @@
// build +darwin
package stun

import "context"
import (
"context"
"encoding/binary"
"errors"
"fmt"
"net"
"sync"
"syscall"
"time"

pcap "github.com/packetcap/go-pcap"
"github.com/pion/stun"
"github.com/rs/zerolog"
"golang.org/x/net/ipv4"
"golang.org/x/net/route"
)

const PacketSize = 1600

const (
etherOff = 0
ipOff = etherOff + 14
udpOff = ipOff + 5*4
payloadOff = udpOff + 2*4
)

// NOTE: This is a placeholder implementation for darwin.
type Stun struct {
port uint16
conn *ipv4.PacketConn
handle *pcap.Handle
once sync.Once
packetChan chan *stun.Message
}

func New(ctx context.Context, port uint16) (*Stun, error) {
return &Stun{}, nil

// use default route to be the interface to listen stun
defaultRouteInterface, err := getDefaultRouteInterface()
if err != nil {
return nil, err
}

handle, err := pcap.OpenLive(defaultRouteInterface, PacketSize, false, 0, pcap.DefaultSyscalls)
if err != nil {
return nil, err
}

filter := fmt.Sprintf("udp dst port %d and udp[12:4] == 0x2112A442", port)
if err := handle.SetBPFFilter(filter); err != nil {
return nil, err
}

c, err := net.ListenPacket("ip4:17", "0.0.0.0")
if err != nil {
return nil, err
}

conn := ipv4.NewPacketConn(c)

return &Stun{
port: port,
conn: conn,
handle: handle,
packetChan: make(chan *stun.Message),
}, nil
}

func (s *Stun) Stop() error {
return nil
close(s.packetChan)
s.handle.Close()
return s.conn.Close()
}

func (s *Stun) Start(ctx context.Context) {
logger := zerolog.Ctx(ctx)

logger.Info().Msgf("starting to listen stun response")
s.once.Do(func() {
go func() {
for {
select {
case <-ctx.Done():
return
case <-time.After(time.Duration(StunTimeout) * time.Second):
return
default:
var (
buf []byte
err error
)
buf, _, err = s.handle.ReadPacketData()
if err != nil {
logger.Debug().Msgf("fail to read packet data")
continue
}
// decode STUN
m := &stun.Message{
Raw: buf[payloadOff:],
}
if err := m.Decode(); err != nil {
logger.Debug().Msgf("fail to decode stun msg")
continue
}
s.packetChan <- m
return
}
}
}()
})
}

func (s *Stun) Connect(ctx context.Context, address string) (_ string, _ int, err error) {
return "", 0, nil
func (s *Stun) Connect(ctx context.Context, stunAddr string) (_ string, _ int, err error) {
logger := zerolog.Ctx(ctx)

logger.Info().Msgf("connecting to STUN server: %s", stunAddr)
addr, err := net.ResolveUDPAddr("udp4", stunAddr)
if err != nil {
return "", 0, err
}

packet, err := createStunBindingPacket(s.port, uint16(addr.Port))
if err != nil {
return "", 0, err
}

_, err = s.conn.WriteTo(packet, nil, addr)
if err != nil {
return "", 0, err
}

reply, err := s.Read(ctx)
if err != nil {
return "", 0, err
}

replyAddr := Parse(ctx, reply)

return replyAddr.IP.String(), replyAddr.Port, nil
}

func (s *Stun) Read(ctx context.Context) (*stun.Message, error) {
select {
case m := <-s.packetChan:
return m, nil
case <-time.After(time.Duration(StunTimeout) * time.Second):
return nil, ErrTimeout
case <-ctx.Done():
return nil, ctx.Err()
}
}

func createStunBindingPacket(srcPort, dstPort uint16) ([]byte, error) {
msg, err := stun.Build(stun.TransactionID, stun.BindingRequest)
if err != nil {
return nil, err
}
_ = msg.NewTransactionID()

packetLength := uint16(BindingPacketHeaderSize + len(msg.Raw))
checksum := uint16(0)

buf := make([]byte, BindingPacketHeaderSize)
binary.BigEndian.PutUint16(buf[0:], srcPort)
binary.BigEndian.PutUint16(buf[2:], dstPort)
binary.BigEndian.PutUint16(buf[4:], packetLength)
binary.BigEndian.PutUint16(buf[6:], checksum)

return append(buf, msg.Raw...), nil
}

func getDefaultRouteInterface() (string, error) {
// Fetch the routing information base (RIB) for routes.
rib, err := route.FetchRIB(syscall.AF_INET, route.RIBTypeRoute, 0)
if err != nil {
fmt.Println("Error fetching RIB:", err)
return "", err
}

msgs, err := route.ParseRIB(route.RIBTypeRoute, rib)
if err != nil {
fmt.Println("Error parsing RIB:", err)
return "", err
}

var dstIP string
var netmask string
var ifaceName string

for _, msg := range msgs {
switch m := msg.(type) {
case *route.RouteMessage:
// Extract destination address.
if len(m.Addrs) > syscall.RTAX_NETMASK {
switch addr := m.Addrs[syscall.RTAX_DST].(type) {
case *route.Inet4Addr:
dstIP = net.IPv4(addr.IP[0], addr.IP[1], addr.IP[2], addr.IP[3]).String()
}
switch addr := m.Addrs[syscall.RTAX_NETMASK].(type) {
case *route.Inet4Addr:
netmask = net.IPv4Mask(addr.IP[0], addr.IP[1], addr.IP[2], addr.IP[3]).String()
}
}

// Extract interface index and name using the Index field.
ifaceIndex := m.Index
if ifaceIndex != 0 {
iface, err := net.InterfaceByIndex(ifaceIndex)
if err == nil {
ifaceName = iface.Name
} else {
ifaceName = fmt.Sprintf("if%d", ifaceIndex)
}
}

if dstIP == "0.0.0.0" && netmask == "00000000" {
return ifaceName, nil
}
}
}

return "", errors.New("No default route found")
}

0 comments on commit b09070d

Please sign in to comment.