Skip to content

Commit

Permalink
debug proxy for tunnel interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
dmissmann committed Mar 6, 2024
1 parent 72af7ea commit 6efc49e
Show file tree
Hide file tree
Showing 13 changed files with 572 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"encoding/hex"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"encoding/json"
Expand Down Expand Up @@ -77,8 +77,8 @@ func (d *DebugProxy) retrieveServiceInfoByPort(port uint16) (PhoneServiceInforma
}

// NewDebugProxy creates a new Default proxy
func NewDebugProxy() *DebugProxy {
return &DebugProxy{mux: sync.Mutex{}, serviceList: []PhoneServiceInformation{}}
func NewDebugProxy(workdir string) *DebugProxy {
return &DebugProxy{mux: sync.Mutex{}, serviceList: []PhoneServiceInformation{}, WorkingDir: workdir}
}

// Launch moves the original /var/run/usbmuxd to /var/run/usbmuxd.real and starts the server at /var/run/usbmuxd
Expand All @@ -104,7 +104,6 @@ func (d *DebugProxy) Launch(device ios.DeviceEntry, binaryMode bool) error {
log.WithFields(log.Fields{"error": err, "socket": ios.GetUsbmuxdSocket()}).Error("Unable to move, lacking permissions?")
return err
}
d.setupDirectory()
listener, err := net.Listen("unix", ios.ToUnixSocketPath(ios.GetUsbmuxdSocket()))
if err != nil {
log.Error("Could not listen on usbmuxd socket, do I have access permissions?", err)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"bytes"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"encoding/hex"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"bytes"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"bytes"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package debugproxy
package usbmuxd

import (
"fmt"
Expand Down
96 changes: 96 additions & 0 deletions ios/debugproxy/utun/capture.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//go:build darwin

package utun

import (
"context"
"errors"
"fmt"
"io"
"net"

"github.com/danielpaulus/go-ios/ios"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
log "github.com/sirupsen/logrus"
)

type direction uint8

const (
outgoing = iota
incoming
)

type connections map[connectionId]*connection

type connectionId struct {
localPort layers.TCPPort
remotePort layers.TCPPort
}

func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error {
addr, err := ifaceAddr(iface)
if err != nil {
return err
}
log.Infof("Capture traffice for iface %s with address %s", iface, addr)
if handle, err := pcap.OpenLive(iface, 64*1024, true, pcap.BlockForever); err != nil {
return fmt.Errorf("Live: failed to connect to iface %s. %w", iface, err)
} else {
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
s := newSession(packetSource.Packets(), addr, provider, dumpDir)
s.readPackets(ctx)
}
return nil
}

func ifaceAddr(name string) (net.IP, error) {
ifaces, err := pcap.FindAllDevs()
if err != nil {
return nil, err
}
for _, iface := range ifaces {
if iface.Name == name {
return iface.Addresses[1].IP, nil
}
}
return nil, fmt.Errorf("ifaceAddr: could not find iface with name %s", name)
}

func (s *session) connectionIdentifier(ip *layers.IPv6, tcp *layers.TCP) connectionId {
if ip.SrcIP.String() == s.localAddr.String() {
return connectionId{
localPort: tcp.SrcPort,
remotePort: tcp.DstPort,
}
} else {
return connectionId{
localPort: tcp.DstPort,
remotePort: tcp.SrcPort,
}
}
}

type payloadWriter struct {
incoming io.WriteCloser
outgoing io.WriteCloser
}

func (p payloadWriter) Close() error {
inErr := p.incoming.Close()
outErr := p.outgoing.Close()
return errors.Join(inErr, outErr)
}

func (p payloadWriter) Write(d direction, b []byte) (int, error) {
switch d {
case outgoing:
return p.outgoing.Write(b)
case incoming:
return p.incoming.Write(b)
default:
return 0, fmt.Errorf("Write: unknown direction %d", d)
}
}
14 changes: 14 additions & 0 deletions ios/debugproxy/utun/capture_noop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//go:build !darwin

package utun

import (
"context"
"errors"

"github.com/danielpaulus/go-ios/ios"
)

func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error {
return errors.New("capturing traffic on the utun interface is only supported on MacOS")
}
157 changes: 157 additions & 0 deletions ios/debugproxy/utun/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//go:build darwin

package utun

import (
"errors"
"fmt"
"io"
"os"
"path"

"github.com/google/gopacket/layers"
"github.com/sirupsen/logrus"
)

type connection struct {
id connectionId
w payloadWriter
outPath string
inPath string
service string
}

func newConnection(id connectionId, p string, service string) (*connection, error) {
inPath := path.Join(p, "incoming")
incoming, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return nil, fmt.Errorf("newConnection: could not open file for incoming connection dump: %w", err)
}
outPath := path.Join(p, "outgoing")
outgoing, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return nil, fmt.Errorf("newConnection: could not open file for outgoing connection dump: %w", err)
}
pw := payloadWriter{
incoming: incoming,
outgoing: outgoing,
}
return &connection{
id: id,
w: pw,
outPath: outPath,
inPath: inPath,
service: service,
}, nil
}

func (c connection) handlePacket(tcp *layers.TCP) {
if tcp.SYN && tcp.SrcPort == c.id.localPort {
logrus.Infof("new connection %s", c.id)
}
if len(tcp.Payload) > 0 {
c.w.Write(c.direction(tcp), tcp.Payload)
}
}

func (c connection) direction(tcp *layers.TCP) direction {
if c.id.localPort == tcp.SrcPort {
return outgoing
} else {
return incoming
}
}

func (c connection) Close() error {
_ = c.w.Close()
logrus.WithField("connection", c.id.String()).WithField("service", c.service).Info("closing connection")
err := parseConnectionData(c.outPath, c.inPath)
if err != nil {
logrus.WithField("connection", c.id.String()).
WithField("service", c.service).
WithError(err).
Warn("failed parsing data")
}
return nil
}

func (c connectionId) String() string {
return fmt.Sprintf("%d-%d", c.localPort, c.remotePort)
}

func parseConnectionData(outgoing string, incoming string) error {
dir := path.Dir(outgoing)

outFile, err := os.OpenFile(outgoing, os.O_RDONLY, os.ModePerm)
if err != nil {
return err
}
defer outFile.Close()
inFile, err := os.OpenFile(incoming, os.O_RDONLY, os.ModePerm)
if err != nil {
return err
}
defer inFile.Close()

t := detectType(outFile)

switch t {
case http2:
_ = createDecodingFiles(dir, "http.frames", func(outgoing, incoming pair) error {
outErr := decodeHttp2FrameHeaders(outgoing.w, outFile, true)
inErr := decodeHttp2FrameHeaders(incoming.w, inFile, false)
return errors.Join(outErr, inErr)
})
_, _ = outFile.Seek(0, io.SeekStart)
_, _ = inFile.Seek(0, io.SeekStart)
return createDecodingFiles(dir, "http.bin", func(outgoing, incoming pair) error {
outErr := decodeHttp2(outgoing.w, outFile, true)
inErr := decodeHttp2(incoming.w, inFile, false)
if err := errors.Join(outErr, inErr); err != nil {
//return err
}
return parseConnectionData(outgoing.p, incoming.p)
})
case remoteXpc:
return createDecodingFiles(dir, "xpc.jsonl", func(outgoing, incoming pair) error {
outErr := decodeRemoteXpc(outgoing.w, outFile)
inErr := decodeRemoteXpc(incoming.w, inFile)
return errors.Join(outErr, inErr)
})
case remoteDtx:
return createDecodingFiles(dir, "dtx", func(outgoing, incoming pair) error {
outErr := decodeRemoteDtx(outgoing.w, outFile)
inErr := decodeRemoteDtx(incoming.w, inFile)
return errors.Join(outErr, inErr)
})
default:
stat, _ := os.Stat(outgoing)
if stat.Size() == 0 {
return nil
}
return fmt.Errorf("unknown content type: %s/%s", outgoing, incoming)
}
}

func createDecodingFiles(dir, suffix string, consumer func(outgoing, incoming pair) error) error {
outPath := path.Join(dir, fmt.Sprintf("outgoing.%s", suffix))
inPath := path.Join(dir, fmt.Sprintf("incoming.%s", suffix))

outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return err
}
defer outFile.Close()
inFile, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
if err != nil {
return err
}
defer inFile.Close()

return consumer(pair{outPath, outFile}, pair{inPath, inFile})
}

type pair struct {
p string
w io.Writer
}
Loading

0 comments on commit 6efc49e

Please sign in to comment.