Skip to content

Commit 9e9c009

Browse files
committed
debug proxy for tunnel interfaces
1 parent 72af7ea commit 9e9c009

File tree

13 files changed

+562
-16
lines changed

13 files changed

+562
-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/connection.go

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

ios/debugproxy/utun/decoding.go

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
//go:build darwin
2+
3+
package utun
4+
5+
import (
6+
"bytes"
7+
"encoding/binary"
8+
"encoding/json"
9+
"errors"
10+
"io"
11+
12+
dtx "github.com/danielpaulus/go-ios/ios/dtx_codec"
13+
"github.com/danielpaulus/go-ios/ios/xpc"
14+
log "github.com/sirupsen/logrus"
15+
http22 "golang.org/x/net/http2"
16+
)
17+
18+
type contentType int
19+
20+
const (
21+
http2 = contentType(iota)
22+
remoteXpc
23+
remoteDtx
24+
unknown
25+
)
26+
27+
func detectType(r io.ReadSeeker) contentType {
28+
offset, err := r.Seek(0, io.SeekCurrent)
29+
if err != nil {
30+
return unknown
31+
}
32+
defer func() {
33+
r.Seek(offset, io.SeekStart)
34+
}()
35+
b := make([]byte, 4)
36+
_, err = r.Read(b)
37+
if err != nil {
38+
return unknown
39+
}
40+
if string(b) == "PRI " {
41+
return http2
42+
}
43+
i := binary.LittleEndian.Uint32(b)
44+
if i == 0x29b00b92 {
45+
return remoteXpc
46+
}
47+
if string(b[:3]) == "y[=" {
48+
return remoteDtx
49+
}
50+
51+
return unknown
52+
}
53+
54+
func decodeHttp2(w io.Writer, r io.Reader, needSkip bool) error {
55+
if needSkip {
56+
_, err := io.CopyN(io.Discard, r, 24)
57+
if err != nil {
58+
return err
59+
}
60+
}
61+
framer := http22.NewFramer(io.Discard, r)
62+
for {
63+
f, err := framer.ReadFrame()
64+
if err != nil {
65+
return err
66+
}
67+
if f.Header().Type == http22.FrameData {
68+
dataFrame := f.(*http22.DataFrame)
69+
if _, err := w.Write(dataFrame.Data()); err != nil {
70+
return err
71+
}
72+
}
73+
}
74+
}
75+
76+
func decodeHttp2FrameHeaders(w io.Writer, r io.Reader, needSkip bool) error {
77+
if needSkip {
78+
_, err := io.CopyN(io.Discard, r, 24)
79+
if err != nil {
80+
return err
81+
}
82+
}
83+
framer := http22.NewFramer(io.Discard, r)
84+
for {
85+
f, err := framer.ReadFrame()
86+
if err != nil {
87+
return err
88+
}
89+
_, err = w.Write(append([]byte(f.Header().String()), '\n'))
90+
if err != nil {
91+
return err
92+
}
93+
}
94+
}
95+
96+
func decodeRemoteXpc(w io.Writer, r io.Reader) error {
97+
for {
98+
m, err := xpc.DecodeMessage(r)
99+
if err != nil {
100+
if errors.Is(err, io.EOF) {
101+
return nil
102+
}
103+
return err
104+
}
105+
106+
b, err := json.Marshal(m)
107+
if err != nil {
108+
return err
109+
}
110+
buf := bytes.NewBuffer(nil)
111+
json.Compact(buf, b)
112+
if _, err := io.Copy(w, buf); err != nil {
113+
return err
114+
}
115+
if m.IsFileOpen() {
116+
log.Info("file transfer started, skipping remaining data ")
117+
return nil
118+
}
119+
}
120+
}
121+
122+
func decodeRemoteDtx(w io.Writer, r io.Reader) error {
123+
for {
124+
m, err := dtx.ReadMessage(r)
125+
if err != nil {
126+
if errors.Is(err, io.EOF) {
127+
return nil
128+
}
129+
return err
130+
}
131+
132+
buf := bytes.NewBufferString(m.StringDebug() + "\n")
133+
if _, err := io.Copy(w, buf); err != nil {
134+
return err
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)