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 8b3d6b5
Show file tree
Hide file tree
Showing 13 changed files with 569 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
158 changes: 158 additions & 0 deletions ios/debugproxy/utun/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//go:build darwin

package utun

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

"github.com/google/gopacket"
"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(p gopacket.Packet, ip *layers.IPv6, 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
}
137 changes: 137 additions & 0 deletions ios/debugproxy/utun/decoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
//go:build darwin

package utun

import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"io"

dtx "github.com/danielpaulus/go-ios/ios/dtx_codec"
"github.com/danielpaulus/go-ios/ios/xpc"
log "github.com/sirupsen/logrus"
http22 "golang.org/x/net/http2"
)

type contentType int

const (
http2 = contentType(iota)
remoteXpc
remoteDtx
unknown
)

func detectType(r io.ReadSeeker) contentType {
offset, err := r.Seek(0, io.SeekCurrent)
if err != nil {
return unknown
}
defer func() {
r.Seek(offset, io.SeekStart)
}()
b := make([]byte, 4)
_, err = r.Read(b)
if err != nil {
return unknown
}
if string(b) == "PRI " {
return http2
}
i := binary.LittleEndian.Uint32(b)
if i == 0x29b00b92 {
return remoteXpc
}
if string(b[:3]) == "y[=" {
return remoteDtx
}

return unknown
}

func decodeHttp2(w io.Writer, r io.Reader, needSkip bool) error {
if needSkip {
_, err := io.CopyN(io.Discard, r, 24)
if err != nil {
return err
}
}
framer := http22.NewFramer(io.Discard, r)
for {
f, err := framer.ReadFrame()
if err != nil {
return err
}
if f.Header().Type == http22.FrameData {
dataFrame := f.(*http22.DataFrame)
if _, err := w.Write(dataFrame.Data()); err != nil {
return err
}
}
}
}

func decodeHttp2FrameHeaders(w io.Writer, r io.Reader, needSkip bool) error {
if needSkip {
_, err := io.CopyN(io.Discard, r, 24)
if err != nil {
return err
}
}
framer := http22.NewFramer(io.Discard, r)
for {
f, err := framer.ReadFrame()
if err != nil {
return err
}
_, err = w.Write(append([]byte(f.Header().String()), '\n'))
if err != nil {
return err
}
}
}

func decodeRemoteXpc(w io.Writer, r io.Reader) error {
for {
m, err := xpc.DecodeMessage(r)
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}

b, err := json.Marshal(m)
if err != nil {
return err
}
buf := bytes.NewBuffer(nil)
json.Compact(buf, b)
if _, err := io.Copy(w, buf); err != nil {
return err
}
if m.IsFileOpen() {
log.Info("file transfer started, skipping remaining data ")
return nil
}
}
}

func decodeRemoteDtx(w io.Writer, r io.Reader) error {
for {
m, err := dtx.ReadMessage(r)
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}

buf := bytes.NewBufferString(m.StringDebug() + "\n")
if _, err := io.Copy(w, buf); err != nil {
return err
}
}
}
Loading

0 comments on commit 8b3d6b5

Please sign in to comment.