Last active
January 12, 2021 05:08
-
-
Save mwpcheung/a8a1e4b86c39578cbbbbddf1f12300bd to your computer and use it in GitHub Desktop.
golang tcp forward idevice
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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