Skip to content

Commit 6efc49e

Browse files
committed
debug proxy for tunnel interfaces
1 parent 72af7ea commit 6efc49e

File tree

13 files changed

+572
-16
lines changed

13 files changed

+572
-16
lines changed

ios/debugproxy/binforward.go renamed to ios/debugproxy/usbmuxd/binforward.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"encoding/hex"

ios/debugproxy/debugproxy.go renamed to ios/debugproxy/usbmuxd/debugproxy.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"encoding/json"
@@ -77,8 +77,8 @@ func (d *DebugProxy) retrieveServiceInfoByPort(port uint16) (PhoneServiceInforma
7777
}
7878

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

8484
// Launch moves the original /var/run/usbmuxd to /var/run/usbmuxd.real and starts the server at /var/run/usbmuxd
@@ -104,7 +104,6 @@ func (d *DebugProxy) Launch(device ios.DeviceEntry, binaryMode bool) error {
104104
log.WithFields(log.Fields{"error": err, "socket": ios.GetUsbmuxdSocket()}).Error("Unable to move, lacking permissions?")
105105
return err
106106
}
107-
d.setupDirectory()
108107
listener, err := net.Listen("unix", ios.ToUnixSocketPath(ios.GetUsbmuxdSocket()))
109108
if err != nil {
110109
log.Error("Could not listen on usbmuxd socket, do I have access permissions?", err)

ios/debugproxy/decoders.go renamed to ios/debugproxy/usbmuxd/decoders.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"bytes"

ios/debugproxy/dumpingconn.go renamed to ios/debugproxy/usbmuxd/dumpingconn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"encoding/hex"

ios/debugproxy/lockdownhandler.go renamed to ios/debugproxy/usbmuxd/lockdownhandler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"bytes"

ios/debugproxy/muxhandler.go renamed to ios/debugproxy/usbmuxd/muxhandler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"bytes"

ios/debugproxy/socket_mover.go renamed to ios/debugproxy/usbmuxd/socket_mover.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package debugproxy
1+
package usbmuxd
22

33
import (
44
"fmt"

ios/debugproxy/utun/capture.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//go:build darwin
2+
3+
package utun
4+
5+
import (
6+
"context"
7+
"errors"
8+
"fmt"
9+
"io"
10+
"net"
11+
12+
"github.com/danielpaulus/go-ios/ios"
13+
"github.com/google/gopacket"
14+
"github.com/google/gopacket/layers"
15+
"github.com/google/gopacket/pcap"
16+
log "github.com/sirupsen/logrus"
17+
)
18+
19+
type direction uint8
20+
21+
const (
22+
outgoing = iota
23+
incoming
24+
)
25+
26+
type connections map[connectionId]*connection
27+
28+
type connectionId struct {
29+
localPort layers.TCPPort
30+
remotePort layers.TCPPort
31+
}
32+
33+
func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error {
34+
addr, err := ifaceAddr(iface)
35+
if err != nil {
36+
return err
37+
}
38+
log.Infof("Capture traffice for iface %s with address %s", iface, addr)
39+
if handle, err := pcap.OpenLive(iface, 64*1024, true, pcap.BlockForever); err != nil {
40+
return fmt.Errorf("Live: failed to connect to iface %s. %w", iface, err)
41+
} else {
42+
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
43+
s := newSession(packetSource.Packets(), addr, provider, dumpDir)
44+
s.readPackets(ctx)
45+
}
46+
return nil
47+
}
48+
49+
func ifaceAddr(name string) (net.IP, error) {
50+
ifaces, err := pcap.FindAllDevs()
51+
if err != nil {
52+
return nil, err
53+
}
54+
for _, iface := range ifaces {
55+
if iface.Name == name {
56+
return iface.Addresses[1].IP, nil
57+
}
58+
}
59+
return nil, fmt.Errorf("ifaceAddr: could not find iface with name %s", name)
60+
}
61+
62+
func (s *session) connectionIdentifier(ip *layers.IPv6, tcp *layers.TCP) connectionId {
63+
if ip.SrcIP.String() == s.localAddr.String() {
64+
return connectionId{
65+
localPort: tcp.SrcPort,
66+
remotePort: tcp.DstPort,
67+
}
68+
} else {
69+
return connectionId{
70+
localPort: tcp.DstPort,
71+
remotePort: tcp.SrcPort,
72+
}
73+
}
74+
}
75+
76+
type payloadWriter struct {
77+
incoming io.WriteCloser
78+
outgoing io.WriteCloser
79+
}
80+
81+
func (p payloadWriter) Close() error {
82+
inErr := p.incoming.Close()
83+
outErr := p.outgoing.Close()
84+
return errors.Join(inErr, outErr)
85+
}
86+
87+
func (p payloadWriter) Write(d direction, b []byte) (int, error) {
88+
switch d {
89+
case outgoing:
90+
return p.outgoing.Write(b)
91+
case incoming:
92+
return p.incoming.Write(b)
93+
default:
94+
return 0, fmt.Errorf("Write: unknown direction %d", d)
95+
}
96+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//go:build !darwin
2+
3+
package utun
4+
5+
import (
6+
"context"
7+
"errors"
8+
9+
"github.com/danielpaulus/go-ios/ios"
10+
)
11+
12+
func Live(ctx context.Context, iface string, provider ios.RsdPortProvider, dumpDir string) error {
13+
return errors.New("capturing traffic on the utun interface is only supported on MacOS")
14+
}

ios/debugproxy/utun/connection.go

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
//go:build darwin
2+
3+
package utun
4+
5+
import (
6+
"errors"
7+
"fmt"
8+
"io"
9+
"os"
10+
"path"
11+
12+
"github.com/google/gopacket/layers"
13+
"github.com/sirupsen/logrus"
14+
)
15+
16+
type connection struct {
17+
id connectionId
18+
w payloadWriter
19+
outPath string
20+
inPath string
21+
service string
22+
}
23+
24+
func newConnection(id connectionId, p string, service string) (*connection, error) {
25+
inPath := path.Join(p, "incoming")
26+
incoming, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
27+
if err != nil {
28+
return nil, fmt.Errorf("newConnection: could not open file for incoming connection dump: %w", err)
29+
}
30+
outPath := path.Join(p, "outgoing")
31+
outgoing, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
32+
if err != nil {
33+
return nil, fmt.Errorf("newConnection: could not open file for outgoing connection dump: %w", err)
34+
}
35+
pw := payloadWriter{
36+
incoming: incoming,
37+
outgoing: outgoing,
38+
}
39+
return &connection{
40+
id: id,
41+
w: pw,
42+
outPath: outPath,
43+
inPath: inPath,
44+
service: service,
45+
}, nil
46+
}
47+
48+
func (c connection) handlePacket(tcp *layers.TCP) {
49+
if tcp.SYN && tcp.SrcPort == c.id.localPort {
50+
logrus.Infof("new connection %s", c.id)
51+
}
52+
if len(tcp.Payload) > 0 {
53+
c.w.Write(c.direction(tcp), tcp.Payload)
54+
}
55+
}
56+
57+
func (c connection) direction(tcp *layers.TCP) direction {
58+
if c.id.localPort == tcp.SrcPort {
59+
return outgoing
60+
} else {
61+
return incoming
62+
}
63+
}
64+
65+
func (c connection) Close() error {
66+
_ = c.w.Close()
67+
logrus.WithField("connection", c.id.String()).WithField("service", c.service).Info("closing connection")
68+
err := parseConnectionData(c.outPath, c.inPath)
69+
if err != nil {
70+
logrus.WithField("connection", c.id.String()).
71+
WithField("service", c.service).
72+
WithError(err).
73+
Warn("failed parsing data")
74+
}
75+
return nil
76+
}
77+
78+
func (c connectionId) String() string {
79+
return fmt.Sprintf("%d-%d", c.localPort, c.remotePort)
80+
}
81+
82+
func parseConnectionData(outgoing string, incoming string) error {
83+
dir := path.Dir(outgoing)
84+
85+
outFile, err := os.OpenFile(outgoing, os.O_RDONLY, os.ModePerm)
86+
if err != nil {
87+
return err
88+
}
89+
defer outFile.Close()
90+
inFile, err := os.OpenFile(incoming, os.O_RDONLY, os.ModePerm)
91+
if err != nil {
92+
return err
93+
}
94+
defer inFile.Close()
95+
96+
t := detectType(outFile)
97+
98+
switch t {
99+
case http2:
100+
_ = createDecodingFiles(dir, "http.frames", func(outgoing, incoming pair) error {
101+
outErr := decodeHttp2FrameHeaders(outgoing.w, outFile, true)
102+
inErr := decodeHttp2FrameHeaders(incoming.w, inFile, false)
103+
return errors.Join(outErr, inErr)
104+
})
105+
_, _ = outFile.Seek(0, io.SeekStart)
106+
_, _ = inFile.Seek(0, io.SeekStart)
107+
return createDecodingFiles(dir, "http.bin", func(outgoing, incoming pair) error {
108+
outErr := decodeHttp2(outgoing.w, outFile, true)
109+
inErr := decodeHttp2(incoming.w, inFile, false)
110+
if err := errors.Join(outErr, inErr); err != nil {
111+
//return err
112+
}
113+
return parseConnectionData(outgoing.p, incoming.p)
114+
})
115+
case remoteXpc:
116+
return createDecodingFiles(dir, "xpc.jsonl", func(outgoing, incoming pair) error {
117+
outErr := decodeRemoteXpc(outgoing.w, outFile)
118+
inErr := decodeRemoteXpc(incoming.w, inFile)
119+
return errors.Join(outErr, inErr)
120+
})
121+
case remoteDtx:
122+
return createDecodingFiles(dir, "dtx", func(outgoing, incoming pair) error {
123+
outErr := decodeRemoteDtx(outgoing.w, outFile)
124+
inErr := decodeRemoteDtx(incoming.w, inFile)
125+
return errors.Join(outErr, inErr)
126+
})
127+
default:
128+
stat, _ := os.Stat(outgoing)
129+
if stat.Size() == 0 {
130+
return nil
131+
}
132+
return fmt.Errorf("unknown content type: %s/%s", outgoing, incoming)
133+
}
134+
}
135+
136+
func createDecodingFiles(dir, suffix string, consumer func(outgoing, incoming pair) error) error {
137+
outPath := path.Join(dir, fmt.Sprintf("outgoing.%s", suffix))
138+
inPath := path.Join(dir, fmt.Sprintf("incoming.%s", suffix))
139+
140+
outFile, err := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
141+
if err != nil {
142+
return err
143+
}
144+
defer outFile.Close()
145+
inFile, err := os.OpenFile(inPath, os.O_CREATE|os.O_WRONLY, os.ModePerm)
146+
if err != nil {
147+
return err
148+
}
149+
defer inFile.Close()
150+
151+
return consumer(pair{outPath, outFile}, pair{inPath, inFile})
152+
}
153+
154+
type pair struct {
155+
p string
156+
w io.Writer
157+
}

0 commit comments

Comments
 (0)