Skip to content

Instantly share code, notes, and snippets.

@mwpcheung
Last active January 12, 2021 05:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mwpcheung/a8a1e4b86c39578cbbbbddf1f12300bd to your computer and use it in GitHub Desktop.
Save mwpcheung/a8a1e4b86c39578cbbbbddf1f12300bd to your computer and use it in GitHub Desktop.
golang tcp forward idevice
package main
import (
"log"
"sync"
)
type USBController struct {
DeviceID map[string]int
sync.RWMutex
usbStatus int
}
func NewUSBController() *USBController {
kls := new(USBController)
kls.DeviceID = make(map[string]int)
kls.usbStatus = 1
go listenDevice(usbController)
return kls
}
func (kls *USBController) Close() {
kls.usbStatus = 2
}
func (kls *USBController) SetUSBStatus(status int) {
kls.usbStatus = status
}
func (kls *USBController) IsRunning() int {
return kls.usbStatus
}
func (kls *USBController) DeviceList() []string {
r := make([]string, 0)
kls.RLock()
for k := range kls.DeviceID {
r = append(r, k)
}
kls.RUnlock()
return r
}
func (kls *USBController) LookupDevice(udid string) (int, bool) {
deviceID := 0
var exsit bool
kls.RLock()
deviceID, exsit = kls.DeviceID[udid]
kls.RUnlock()
return deviceID, exsit
}
func (kls *USBController) USBDeviceDidPlug(frame *USBDeviceAttachedDetachedFrame) {
if frame != nil {
kls.Lock()
kls.DeviceID[frame.Properties.SerialNumber] = frame.DeviceID
kls.Unlock()
}
}
func (kls *USBController) USBDeviceDidUnPlug(frame *USBDeviceAttachedDetachedFrame) {
if frame == nil {
return
}
kls.RLock()
_, ok := kls.DeviceID[frame.Properties.SerialNumber]
kls.RUnlock()
if ok {
kls.Lock()
delete(kls.DeviceID, frame.Properties.SerialNumber)
kls.Unlock()
}
}
func (kls *USBController) USBDidReceiveErrorWhilePluggingOrUnplugging(err error, desc string) {
log.Printf("插拔手机出错 %s %v", desc, err)
}
package main
import (
"bytes"
"encoding/binary"
"errors"
"fmt"
"io"
"log"
"net"
"runtime"
"time"
"howett.net/plist"
)
//USBDeviceDelegate usb interface
type USBDeviceDelegate interface {
USBDeviceDidPlug(*USBDeviceAttachedDetachedFrame)
USBDeviceDidUnPlug(*USBDeviceAttachedDetachedFrame)
USBDidReceiveErrorWhilePluggingOrUnplugging(error, string)
IsRunning() int
Close()
SetUSBStatus(status int) // 0 ok, 1 runnning 2 close
}
var ErrDeviceDisconnected = errors.New("device has disconnected")
var ErrDevicePortUnavailable = errors.New("Port you're requesting is unavailable")
var ErrDevicePortUnknow = errors.New("[IDK]: Malformed request received in the device")
//USBListenRequestFrame When we want to listen for any new USB device or device removed
type USBListenRequestFrame struct {
MessageType string `plist:"MessageType"`
ClientVersionString string `plist:"ClientVersionString"`
ProgName string `plist:"ProgName"`
}
//USBGenericACKFrame Its a frame model for generic response after we send listen or connect
//Number == 0 {OK}, Number == 1 {Device not connected anymore}, Number == 2 {Port not available}, Number == 5 {IDK}
type USBGenericACKFrame struct {
MessageType string `plist:"MessageType"`
Number int `plist:"Number"`
}
//USBDeviceAttachedDetachedFrame Model for USB connect or disconnect frame
type USBDeviceAttachedDetachedFrame struct {
MessageType string `plist:"MessageType"`
DeviceID int `plist:"DeviceID"`
Properties USBDeviceAttachedPropertiesDictFrame `plist:"Properties"`
}
//USBDeviceAttachedPropertiesDictFrame Model for USB attach properties
type USBDeviceAttachedPropertiesDictFrame struct {
ConnectionSpeed int `plist:"ConnectionSpeed"`
ConnectionType string `plist:"ConnectionType"`
DeviceID int `plist:"DeviceID"`
LocationID int `plist:"LocationID"`
ProductID int `plit:"ProductID"`
SerialNumber string `plit:"SerialNumber"`
}
//USBConnectRequestFrame Model for connect frame to a specific port in a connected device
type USBConnectRequestFrame struct {
MessageType string `plist:"MessageType"`
ClientVersionString string `plist:"ClientVersionString"`
ProgName string `plist:"ProgName"`
DeviceID int `plist:"DeviceID"`
PortNumber int `plist:"PortNumber"`
}
type usbmuxdHeader struct {
Length uint32 // length of the header + plist (16 + plist.length)
Version uint32 // 0 for binary version, 1 for plist version
Request uint32 // always 8 (taken from tcprelay.py)
Tag uint32 // always 1 (taken from tcprelay.py)
}
func (header *usbmuxdHeader) Bytes(data []byte) []byte {
header.Length = uint32(16 + len(data))
buffer := make([]byte, header.Length)
binary.LittleEndian.PutUint32(buffer, header.Length)
binary.LittleEndian.PutUint32(buffer[4:], header.Version)
binary.LittleEndian.PutUint32(buffer[8:], header.Request)
binary.LittleEndian.PutUint32(buffer[12:], header.Tag)
copy(buffer[16:], data)
return buffer
}
func (header *usbmuxdHeader) Command(frame interface{}) ([]byte, error) {
data := &bytes.Buffer{}
encoder := plist.NewEncoder(data)
if err := encoder.Encode(frame); err != nil {
return nil, err
}
return header.Bytes(data.Bytes()), nil
}
func (header *usbmuxdHeader) Parser(data []byte, frame interface{}) error {
header.Version = binary.LittleEndian.Uint32(data[0:4])
header.Request = binary.LittleEndian.Uint32(data[4:8])
header.Tag = binary.LittleEndian.Uint32(data[8:12])
decoder := plist.NewDecoder(bytes.NewReader(data[12:]))
return decoder.Decode(frame)
}
func createHeader() *usbmuxdHeader {
return &usbmuxdHeader{Version: 1, Request: 8, Tag: 1}
}
func tunnel(d time.Duration) (net.Conn, error) {
if runtime.GOOS == "windows" {
return net.Dial("tcp", "127.0.0.1:27015")
}
return net.Dial("unix", "/var/run/usbmuxd")
// return net.DialTimeout("unix", "/var/run/usbmuxd", d)
}
func listenDevice(listener USBDeviceDelegate) {
for listener.IsRunning() == 1 {
if conn, err := tunnel(5 * time.Second); err != nil {
log.Printf("open usbmuxd tunnel error: %v", err)
time.Sleep(5 * time.Second)
} else {
header := createHeader()
if buf, err := header.Command(&USBListenRequestFrame{
MessageType: "Listen",
ProgName: "go-usbmuxd",
ClientVersionString: "1.0.0",
}); err != nil {
panic(fmt.Errorf("create header buffer error: %v", err))
} else if _, err = conn.Write(buf); err != nil {
log.Printf("write listen header buffer error: %v", err)
time.Sleep(5 * time.Second)
} else {
header := &usbmuxdHeader{}
var frame USBGenericACKFrame
lenBuf := make([]byte, 4)
devices := make(map[int]*USBDeviceAttachedDetachedFrame)
for listener.IsRunning() == 1 {
if _, err := io.ReadFull(conn, lenBuf); err != nil {
log.Printf("read buffer len error: %v", err)
break
}
pbuf := make([]byte, binary.LittleEndian.Uint32(lenBuf)-4)
if _, err := io.ReadFull(conn, pbuf); err != nil {
log.Printf("read buffer error: %v", err)
break
}
if err := header.Parser(pbuf, &frame); err != nil {
listener.USBDidReceiveErrorWhilePluggingOrUnplugging(err, string(pbuf))
} else if frame.MessageType == "Result" {
if frame.Number != 0 {
listener.USBDidReceiveErrorWhilePluggingOrUnplugging(errors.New("Illegal response received"), string(pbuf))
}
} else {
data := &USBDeviceAttachedDetachedFrame{}
if err := header.Parser(pbuf, data); err != nil {
listener.USBDidReceiveErrorWhilePluggingOrUnplugging(err, string(pbuf))
} else if data.MessageType == "Attached" {
devices[data.DeviceID] = data
listener.USBDeviceDidPlug(data)
} else if data.MessageType == "Detached" {
listener.USBDeviceDidUnPlug(data)
delete(devices, data.DeviceID)
} else {
listener.USBDidReceiveErrorWhilePluggingOrUnplugging(errors.New("Unable to parse the response"), string(pbuf))
}
}
}
for _, data := range devices {
listener.USBDeviceDidUnPlug(data)
}
time.Sleep(5 * time.Second)
}
conn.Close()
}
}
listener.SetUSBStatus(0)
}
func byteSwap(val int) int {
return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF)
}
func ConnectDevice(deviceID int, port int) (net.Conn, error) {
var err error
var conn net.Conn
if conn, err = tunnel(time.Second * 3); err != nil {
return nil, err
}
hasError := true
defer func() {
if hasError {
conn.Close()
}
}()
header := createHeader()
var buf []byte
cmdS := &USBConnectRequestFrame{
DeviceID: deviceID,
PortNumber: byteSwap(port),
MessageType: "Connect",
ClientVersionString: "1.0.0",
ProgName: "go-usbmuxd",
}
if buf, err = header.Command(cmdS); err != nil {
return nil, err
}
if _, err = conn.Write(buf); err != nil {
return nil, err
}
lenBuf := make([]byte, 4)
if _, err = io.ReadFull(conn, lenBuf); err != nil {
return nil, err
}
pbuf := make([]byte, binary.LittleEndian.Uint32(lenBuf)-4)
if _, err = io.ReadFull(conn, pbuf); err != nil {
return nil, err
}
var frame USBGenericACKFrame
if err = header.Parser(pbuf, &frame); err != nil {
return nil, err
} else if frame.MessageType != "Result" {
return nil, fmt.Errorf("unknow message type: %s", frame.MessageType)
} else {
switch frame.Number {
case 0:
hasError = false
return conn, nil
case 2:
// Device Disconnected
return nil, ErrDeviceDisconnected
case 3:
// Port isn't available/ busy
return nil, ErrDevicePortUnavailable
case 5:
// UNKNOWN Error
return nil, ErrDevicePortUnknow
default:
return nil, fmt.Errorf("Unknow error code: %d", frame.Number)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment