@irdk/usbmux 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/README.md +3 -3
- package/dist/usbmux.cjs +317 -0
- package/dist/usbmux.cjs.map +1 -0
- package/dist/usbmux.d.cts +196 -0
- package/dist/usbmux.d.ts +196 -0
- package/dist/usbmux.js +275 -0
- package/dist/usbmux.js.map +1 -0
- package/package.json +12 -6
- package/test/tests.cjs +0 -328
package/LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2015 Sterling DeMille <sterlingdemille@gmail.com>
|
3
|
+
Copyright (c) 2015-2025 Sterling DeMille <sterlingdemille@gmail.com>, Maksim Alzhanov <me@alzhanov.ru>
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
@@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
17
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
18
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
19
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
CHANGED
@@ -52,7 +52,7 @@ irelay --help
|
|
52
52
|
## Module Usage
|
53
53
|
|
54
54
|
```javascript
|
55
|
-
|
55
|
+
import * as usbmux from 'usbmux';
|
56
56
|
|
57
57
|
// usbmux.Relay()
|
58
58
|
// usbmux.createListener()
|
@@ -278,7 +278,7 @@ The `Number` field indicates status. 0 is success, other numbers indicate an err
|
|
278
278
|
|
279
279
|
The MIT License (MIT)
|
280
280
|
|
281
|
-
Copyright (c) 2015 Sterling DeMille <sterlingdemille@gmail.com>
|
281
|
+
Copyright (c) 2015-2025 Sterling DeMille <sterlingdemille@gmail.com>, Maksim Alzhanov <me@alzhanov.ru>
|
282
282
|
|
283
283
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
284
284
|
this software and associated documentation files (the "Software"), to deal in
|
@@ -295,4 +295,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
295
295
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
296
296
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
297
297
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
298
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
298
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/usbmux.cjs
ADDED
@@ -0,0 +1,317 @@
|
|
1
|
+
var __create = Object.create;
|
2
|
+
var __defProp = Object.defineProperty;
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
7
|
+
var __export = (target, all) => {
|
8
|
+
for (var name in all)
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
10
|
+
};
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
13
|
+
for (let key of __getOwnPropNames(from))
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
16
|
+
}
|
17
|
+
return to;
|
18
|
+
};
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
25
|
+
mod
|
26
|
+
));
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
28
|
+
|
29
|
+
// lib/usbmux.ts
|
30
|
+
var usbmux_exports = {};
|
31
|
+
__export(usbmux_exports, {
|
32
|
+
Relay: () => Relay,
|
33
|
+
UsbmuxdError: () => UsbmuxdError,
|
34
|
+
address: () => address,
|
35
|
+
connect: () => connect,
|
36
|
+
createListener: () => createListener,
|
37
|
+
devices: () => devices,
|
38
|
+
findDevice: () => findDevice,
|
39
|
+
getTunnel: () => getTunnel,
|
40
|
+
protocol: () => protocol
|
41
|
+
});
|
42
|
+
module.exports = __toCommonJS(usbmux_exports);
|
43
|
+
var import_node_net = __toESM(require("net"), 1);
|
44
|
+
var import_events = require("events");
|
45
|
+
var import_plist = __toESM(require("plist"), 1);
|
46
|
+
var import_debug = __toESM(require("debug"), 1);
|
47
|
+
var debug = {
|
48
|
+
relay: (0, import_debug.default)("usbmux:relay"),
|
49
|
+
listen: (0, import_debug.default)("usbmux:listen"),
|
50
|
+
connect: (0, import_debug.default)("usbmux:connect")
|
51
|
+
};
|
52
|
+
var devices = {};
|
53
|
+
var address = process.platform === "win32" ? { port: 27015, family: 4 } : { path: "/var/run/usbmuxd" };
|
54
|
+
var protocol = function() {
|
55
|
+
function pack(payload_obj) {
|
56
|
+
const payload_plist = import_plist.default.build(payload_obj);
|
57
|
+
const payload_buf = Buffer.from(payload_plist);
|
58
|
+
var header = {
|
59
|
+
len: payload_buf.length + 16,
|
60
|
+
version: 1,
|
61
|
+
request: 8,
|
62
|
+
tag: 1
|
63
|
+
};
|
64
|
+
const header_buf = Buffer.alloc(16);
|
65
|
+
header_buf.fill(0);
|
66
|
+
header_buf.writeUInt32LE(header.len, 0);
|
67
|
+
header_buf.writeUInt32LE(header.version, 4);
|
68
|
+
header_buf.writeUInt32LE(header.request, 8);
|
69
|
+
header_buf.writeUInt32LE(header.tag, 12);
|
70
|
+
return Buffer.concat([header_buf, payload_buf]);
|
71
|
+
}
|
72
|
+
function byteSwap16(val) {
|
73
|
+
return (val & 255) << 8 | val >> 8 & 255;
|
74
|
+
}
|
75
|
+
const listen = pack({
|
76
|
+
MessageType: "Listen",
|
77
|
+
ClientVersionString: "node-usbmux",
|
78
|
+
ProgName: "node-usbmux"
|
79
|
+
});
|
80
|
+
function connect2(deviceID, port) {
|
81
|
+
return pack({
|
82
|
+
MessageType: "Connect",
|
83
|
+
ClientVersionString: "node-usbmux",
|
84
|
+
ProgName: "node-usbmux",
|
85
|
+
DeviceID: deviceID,
|
86
|
+
PortNumber: byteSwap16(port)
|
87
|
+
});
|
88
|
+
}
|
89
|
+
function makeParser(onComplete) {
|
90
|
+
let len, msg;
|
91
|
+
return function parse(data) {
|
92
|
+
if (!len) {
|
93
|
+
len = data.readUInt32LE(0) - 16;
|
94
|
+
msg = "";
|
95
|
+
data = data.subarray(16);
|
96
|
+
if (!data.length) return;
|
97
|
+
}
|
98
|
+
var body = data.subarray(0, len);
|
99
|
+
msg += body;
|
100
|
+
len -= body.length;
|
101
|
+
if (len === 0) onComplete(import_plist.default.parse(msg));
|
102
|
+
data = data.subarray(body.length);
|
103
|
+
if (data.length) parse(data);
|
104
|
+
};
|
105
|
+
}
|
106
|
+
return {
|
107
|
+
listen,
|
108
|
+
connect: connect2,
|
109
|
+
makeParser
|
110
|
+
};
|
111
|
+
}();
|
112
|
+
var UsbmuxdError = class extends Error {
|
113
|
+
constructor(message, number) {
|
114
|
+
if (number) {
|
115
|
+
message += ", Err #" + number;
|
116
|
+
}
|
117
|
+
if (number === 2) message += ": Device isn't connected";
|
118
|
+
if (number === 3) message += ": Port isn't available or open";
|
119
|
+
if (number === 5) message += ": Malformed request";
|
120
|
+
super(message);
|
121
|
+
if (number) {
|
122
|
+
this.number = number;
|
123
|
+
}
|
124
|
+
}
|
125
|
+
};
|
126
|
+
function createListener() {
|
127
|
+
const conn = import_node_net.default.connect(address);
|
128
|
+
const req = protocol.listen;
|
129
|
+
const parse = protocol.makeParser(function onMsgComplete(msg) {
|
130
|
+
debug.listen("Response: \n%o", msg);
|
131
|
+
if (msg.MessageType === "Result" && msg.Number !== 0) {
|
132
|
+
conn.emit("error", new UsbmuxdError("Listen failed", msg.Number));
|
133
|
+
conn.end();
|
134
|
+
}
|
135
|
+
if (msg.MessageType === "Attached") {
|
136
|
+
devices[msg.Properties.SerialNumber] = msg.Properties;
|
137
|
+
conn.emit("attached", msg.Properties.SerialNumber);
|
138
|
+
}
|
139
|
+
if (msg.MessageType === "Detached") {
|
140
|
+
Object.keys(devices).forEach(function(key) {
|
141
|
+
if (devices[key].DeviceID === msg.DeviceID) {
|
142
|
+
conn.emit("detached", devices[key].SerialNumber);
|
143
|
+
delete devices[key];
|
144
|
+
}
|
145
|
+
});
|
146
|
+
}
|
147
|
+
});
|
148
|
+
debug.listen("Request: \n%s", req.subarray(16).toString());
|
149
|
+
conn.on("data", parse);
|
150
|
+
process.nextTick(function() {
|
151
|
+
conn.write(req);
|
152
|
+
});
|
153
|
+
return conn;
|
154
|
+
}
|
155
|
+
function connect(deviceID, devicePort) {
|
156
|
+
return new Promise(function(resolve, reject) {
|
157
|
+
const conn = import_node_net.default.connect(address), req = protocol.connect(deviceID, devicePort);
|
158
|
+
var parse = protocol.makeParser(function onMsgComplete(msg) {
|
159
|
+
debug.connect("Response: \n%o", msg);
|
160
|
+
if (msg.MessageType === "Result" && msg.Number === 0) {
|
161
|
+
conn.removeListener("data", parse);
|
162
|
+
resolve(conn);
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
reject(new UsbmuxdError("Tunnel failed", msg.Number));
|
166
|
+
conn.end();
|
167
|
+
});
|
168
|
+
debug.connect("Request: \n%s", req.subarray(16).toString());
|
169
|
+
conn.on("data", parse);
|
170
|
+
process.nextTick(function() {
|
171
|
+
conn.write(req);
|
172
|
+
});
|
173
|
+
});
|
174
|
+
}
|
175
|
+
var Relay = class extends import_events.EventEmitter {
|
176
|
+
constructor(devicePort, relayPort, opts) {
|
177
|
+
super();
|
178
|
+
/**
|
179
|
+
* Stops the relay
|
180
|
+
*/
|
181
|
+
this.stop = function() {
|
182
|
+
this._listener.end();
|
183
|
+
this._server.close();
|
184
|
+
};
|
185
|
+
/**
|
186
|
+
* Debugging wrapper for emits
|
187
|
+
*/
|
188
|
+
this._emit = function(event, data) {
|
189
|
+
debug.relay("Emit: %s", event + (data ? ", Data: " + data : ""));
|
190
|
+
this.emit(event, data);
|
191
|
+
};
|
192
|
+
/**
|
193
|
+
* Starts a usbmuxd listener
|
194
|
+
*
|
195
|
+
* Relay will start searching for connected devices and issue a warning if a
|
196
|
+
* device is not found within the timeout. If/when a device is found, it will
|
197
|
+
* emit a ready event.
|
198
|
+
*
|
199
|
+
* Listener events (attach, detach, error) are passed through as relay events.
|
200
|
+
*/
|
201
|
+
this._startListener = function(timeout) {
|
202
|
+
var _this = this;
|
203
|
+
var timer = setTimeout(function() {
|
204
|
+
if (!_this._udid && !Object.keys(devices).length) {
|
205
|
+
_this._emit("warning", new Error("No devices connected"));
|
206
|
+
}
|
207
|
+
if (_this._udid && !devices[_this._udid]) {
|
208
|
+
_this._emit("warning", new Error("Requested device not connected"));
|
209
|
+
}
|
210
|
+
}, timeout || 1e3);
|
211
|
+
function readyCheck(udid) {
|
212
|
+
if (_this._udid && _this._udid !== udid) return;
|
213
|
+
_this._emit("ready", udid);
|
214
|
+
_this._listener.removeListener("attached", readyCheck);
|
215
|
+
clearTimeout(timer);
|
216
|
+
}
|
217
|
+
this._listener = createListener().on("attached", readyCheck).on("attached", _this._emit.bind(this, "attached")).on("detached", _this._emit.bind(this, "detached")).on("error", _this._emit.bind(this, "error"));
|
218
|
+
};
|
219
|
+
/**
|
220
|
+
* Start local TCP server that will pipe to the usbmuxd tunnel
|
221
|
+
*
|
222
|
+
* Server events (close and error) are passed through as relay events.
|
223
|
+
*/
|
224
|
+
this._startServer = function() {
|
225
|
+
var _this = this;
|
226
|
+
this._server = import_node_net.default.createServer(this._handler.bind(this)).on("close", _this._emit.bind(this, "close")).on("error", function(err) {
|
227
|
+
_this._listener.end();
|
228
|
+
_this._emit("error", err);
|
229
|
+
}).listen(this._relayPort);
|
230
|
+
};
|
231
|
+
/**
|
232
|
+
* Handle & pipe connections from local server
|
233
|
+
*
|
234
|
+
* Fires error events and connection begin / disconnect events
|
235
|
+
*/
|
236
|
+
this._handler = function(conn) {
|
237
|
+
if (!Object.keys(devices).length) {
|
238
|
+
this._emit("error", new Error("No devices connected"));
|
239
|
+
conn.end();
|
240
|
+
return;
|
241
|
+
}
|
242
|
+
if (this._udid && !devices[this._udid]) {
|
243
|
+
this._emit("error", new Error("Requested device not connected"));
|
244
|
+
conn.end();
|
245
|
+
return;
|
246
|
+
}
|
247
|
+
var _this = this, udid = this._udid || Object.keys(devices)[0], deviceID = devices[udid].DeviceID;
|
248
|
+
connect(deviceID, this._devicePort).then(function(tunnel) {
|
249
|
+
conn.pipe(tunnel).pipe(conn);
|
250
|
+
_this._emit("connect");
|
251
|
+
conn.on("end", function() {
|
252
|
+
_this._emit("disconnect");
|
253
|
+
tunnel.end();
|
254
|
+
conn.end();
|
255
|
+
});
|
256
|
+
conn.on("error", function() {
|
257
|
+
tunnel.end();
|
258
|
+
conn.end();
|
259
|
+
});
|
260
|
+
}).catch(function(err) {
|
261
|
+
_this._emit("error", err);
|
262
|
+
conn.end();
|
263
|
+
});
|
264
|
+
};
|
265
|
+
this._devicePort = devicePort;
|
266
|
+
this._relayPort = relayPort;
|
267
|
+
opts = opts || {};
|
268
|
+
this._udid = opts.udid;
|
269
|
+
this._startListener(opts.timeout || 1e3);
|
270
|
+
this._startServer();
|
271
|
+
}
|
272
|
+
};
|
273
|
+
function findDevice(opts) {
|
274
|
+
return new Promise(function(resolve, reject) {
|
275
|
+
var listener = createListener();
|
276
|
+
opts = opts || {};
|
277
|
+
var timer = setTimeout(function() {
|
278
|
+
listener.end();
|
279
|
+
opts.udid ? reject(new Error("Requested device not connected")) : reject(new Error("No devices connected"));
|
280
|
+
}, opts.timeout || 1e3);
|
281
|
+
listener.on("attached", function(udid) {
|
282
|
+
if (opts.udid && opts.udid !== udid) return;
|
283
|
+
listener.end();
|
284
|
+
clearTimeout(timer);
|
285
|
+
resolve(devices[udid].DeviceID);
|
286
|
+
});
|
287
|
+
});
|
288
|
+
}
|
289
|
+
async function getTunnel(devicePort, opts) {
|
290
|
+
opts = opts || {};
|
291
|
+
let udid;
|
292
|
+
let deviceID;
|
293
|
+
if (opts.udid && devices[opts.udid]) {
|
294
|
+
deviceID = devices[opts.udid].DeviceID;
|
295
|
+
return connect(deviceID, devicePort);
|
296
|
+
}
|
297
|
+
if (!opts.udid && Object.keys(devices).length) {
|
298
|
+
udid = Object.keys(devices)[0];
|
299
|
+
deviceID = devices[udid].DeviceID;
|
300
|
+
return connect(deviceID, devicePort);
|
301
|
+
}
|
302
|
+
const deviceID_2 = await findDevice(opts);
|
303
|
+
return await connect(deviceID_2, devicePort);
|
304
|
+
}
|
305
|
+
// Annotate the CommonJS export names for ESM import in node:
|
306
|
+
0 && (module.exports = {
|
307
|
+
Relay,
|
308
|
+
UsbmuxdError,
|
309
|
+
address,
|
310
|
+
connect,
|
311
|
+
createListener,
|
312
|
+
devices,
|
313
|
+
findDevice,
|
314
|
+
getTunnel,
|
315
|
+
protocol
|
316
|
+
});
|
317
|
+
//# sourceMappingURL=usbmux.cjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../lib/usbmux.ts"],"sourcesContent":["import net from \"node:net\";\nimport { EventEmitter } from \"events\";\n\nimport plist from \"plist\";\n\n/**\n * Debugging\n * set with DEBUG=usbmux:* env variable\n *\n * on windows cmd set with: cmd /C \"SET DEBUG=usbmux:* && node script.js\"\n */\nimport bug from \"debug\";\nconst debug = {\n relay: bug(\"usbmux:relay\"),\n listen: bug(\"usbmux:listen\"),\n connect: bug(\"usbmux:connect\"),\n};\n\ntype Device = {\n ConnectionType: string;\n DeviceID: DeviceId;\n LocationID: number;\n ProductID: number;\n SerialNumber: string;\n};\n/**\n * Keep track of connected devices\n *\n * Maps device UDID to device properties, ie:\n * '22226dd59aaac687f555f8521f8ffddac32d394b': {\n * ConnectionType: 'USB',\n * DeviceID: 19,\n * LocationID: 0,\n * ProductID: 4776,\n * SerialNumber: '22226dd59aaac687f555f8521f8ffddac32d394b'\n * }\n *\n * Devices are added and removed to this obj only by createListener()\n *\n */\nexport const devices: Record<string, Device> = {};\n\n/**\n * usbmuxd address\n *\n * OSX usbmuxd listens on a unix socket at /var/run/usbmuxd\n * Windows usbmuxd listens on port 27015\n *\n * libimobiledevice[1] looks like it operates at /var/run/usbmuxd too, but if\n * your usbmuxd is listening somewhere else you'll need to set this manually.\n *\n * [1] github.com/libimobiledevice/usbmuxd\n */\nexport const address =\n process.platform === \"win32\"\n ? { port: 27015, family: 4 }\n : { path: \"/var/run/usbmuxd\" };\n\n/**\n * Exposes methods for dealing with usbmuxd protocol messages (send/receive)\n *\n * The usbmuxd message protocol has 2 versions. V1 doesn't look like its used\n * anymore. V2 is a header + plist format like this:\n *\n * Header:\n * UInt32LE Length - is the length of the header + plist (16 + plist.length)\n * UInt32LE Version - is 0 for binary version, 1 for plist version\n * UInt32LE Request - is always 8, for plist? from rcg4u/iphonessh\n * UInt32LE Tag - is always 1, ? from rcg4u/iphonessh\n *\n * Plist:\n * <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\"\n * \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n * <plist version=\"1.0\">\n * <dict>\n * <key>MessageType</key>\n * <string>Listen</string>\n * <key>ClientVersionString</key>\n * <string>node-usbmux</string>\n * <key>ProgName</key>\n * <string>node-usbmux</string>\n * </dict>\n * </plist>\n *\n * References:\n * - https://github.com/rcg4u/iphonessh\n * - https://www.theiphonewiki.com/wiki/Usbmux (binary protocol)\n */\n\ntype DeviceId = number;\ntype UsbMuxPlist =\n | {\n MessageType: \"Result\";\n Number: number;\n }\n | {\n MessageType: \"Attached\";\n Number: number;\n Properties: Device;\n }\n | { MessageType: \"Detached\"; Number: number; DeviceID: DeviceId };\n\nexport const protocol = (function () {\n /**\n * Pack a request object into a buffer for usbmuxd\n */\n function pack(payload_obj: plist.PlistValue): Buffer {\n const payload_plist = plist.build(payload_obj);\n const payload_buf = Buffer.from(payload_plist);\n\n var header = {\n len: payload_buf.length + 16,\n version: 1,\n request: 8,\n tag: 1,\n };\n\n const header_buf = Buffer.alloc(16);\n header_buf.fill(0);\n header_buf.writeUInt32LE(header.len, 0);\n header_buf.writeUInt32LE(header.version, 4);\n header_buf.writeUInt32LE(header.request, 8);\n header_buf.writeUInt32LE(header.tag, 12);\n\n return Buffer.concat([header_buf, payload_buf]);\n }\n\n /**\n * Swap endianness of a 16bit value\n */\n function byteSwap16(val: number) {\n return ((val & 0xff) << 8) | ((val >> 8) & 0xff);\n }\n\n /**\n * Listen request\n */\n const listen: Buffer = pack({\n MessageType: \"Listen\",\n ClientVersionString: \"node-usbmux\",\n ProgName: \"node-usbmux\",\n });\n\n /**\n * Connect request\n *\n * Note: PortNumber must be network-endian, so it gets byte swapped here\n */\n function connect(deviceID: DeviceId, port: number): Buffer {\n return pack({\n MessageType: \"Connect\",\n ClientVersionString: \"node-usbmux\",\n ProgName: \"node-usbmux\",\n DeviceID: deviceID,\n PortNumber: byteSwap16(port),\n });\n }\n\n /**\n * Creates a function that will parse messages from data events\n *\n * net.Socket data events sometimes break up the incoming message across\n * multiple events, making it necessary to combine them. This parser function\n * assembles messages using the length given in the message header and calls\n * the onComplete callback as new messages are assembled. Sometime multiple\n * messages will be within a single data buffer too.\n */\n function makeParser(\n onComplete: (msg: UsbMuxPlist) => void,\n ): (data: Buffer) => void {\n // Store status (remaining message length & msg text) of partial messages\n // across multiple calls to the parse function\n let len: number, msg: string;\n\n return function parse(data: Buffer) {\n // Check if this data represents a new incoming message or is part of an\n // existing partially completed message\n if (!len) {\n // The length of the message's body is the total length (the first\n // UInt32LE in the header) minus the length of header itself (16)\n len = data.readUInt32LE(0) - 16;\n msg = \"\";\n\n // If there is data beyond the header then continue adding data to msg\n data = data.subarray(16);\n if (!data.length) return;\n }\n\n // Add in data until our remaining length is used up\n var body = data.subarray(0, len);\n msg += body;\n len -= body.length;\n\n // If msg is finished, convert plist to obj and run callback\n if (len === 0) onComplete(plist.parse(msg) as UsbMuxPlist);\n\n // If there is any data left over that means there is another message\n // so we need to run this parse fct again using the rest of the data\n data = data.subarray(body.length);\n if (data.length) parse(data);\n };\n }\n\n // Exposed methods\n return {\n listen: listen,\n connect: connect,\n makeParser: makeParser,\n };\n})();\n\n/**\n * Custom usbmuxd error\n *\n * There's no documentation for usbmuxd responses, but I think I've figured\n * out these result numbers:\n * 0 - Success\n * 2 - Device requested isn't connected\n * 3 - Port requested isn't available \\ open\n * 5 - Malformed request\n */\nexport class UsbmuxdError extends Error {\n number: number;\n\n constructor(message: string, number: number) {\n if (number) {\n message += \", Err #\" + number;\n }\n if (number === 2) message += \": Device isn't connected\";\n if (number === 3) message += \": Port isn't available or open\";\n if (number === 5) message += \": Malformed request\";\n super(message);\n if (number) {\n this.number = number;\n }\n }\n}\n\n/**\n * Connects to usbmuxd and listens for ios devices\n *\n * This connection stays open, listening as devices are plugged/unplugged and\n * cant be upgraded into a tcp tunnel. You have to start a second connection\n * with connect() to actually make tunnel.\n *\n * @return {net.Socket} - Socket with 2 bolted on events, attached & detached:\n *\n * Fires when devices are plugged in or first found by the listener\n * @event net.Socket#attached\n * @type {string} - UDID\n *\n * Fires when devices are unplugged\n * @event net.Socket#detached\n * @type {string} - UDID\n */\nexport function createListener(): net.Socket {\n const conn = net.connect(address);\n const req = protocol.listen;\n\n /**\n * Handle complete messages from usbmuxd\n * @function\n */\n const parse = protocol.makeParser(function onMsgComplete(msg) {\n debug.listen(\"Response: \\n%o\", msg);\n\n // first response always acknowledges / denies the request:\n if (msg.MessageType === \"Result\" && msg.Number !== 0) {\n conn.emit(\"error\", new UsbmuxdError(\"Listen failed\", msg.Number));\n conn.end();\n }\n\n // subsequent responses report on connected device status:\n if (msg.MessageType === \"Attached\") {\n devices[msg.Properties.SerialNumber] = msg.Properties;\n conn.emit(\"attached\", msg.Properties.SerialNumber);\n }\n\n if (msg.MessageType === \"Detached\") {\n // given msg.DeviceID, find matching device and remove it\n Object.keys(devices).forEach(function (key) {\n if (devices[key].DeviceID === msg.DeviceID) {\n conn.emit(\"detached\", devices[key].SerialNumber);\n delete devices[key];\n }\n });\n }\n });\n\n debug.listen(\"Request: \\n%s\", req.subarray(16).toString());\n\n conn.on(\"data\", parse);\n process.nextTick(function () {\n conn.write(req);\n });\n\n return conn;\n}\n\n/**\n * Connects to a device through usbmuxd for a tunneled tcp connection\n */\nexport function connect(\n deviceID: DeviceId,\n devicePort: number,\n): Promise<net.Socket> {\n return new Promise(function (resolve, reject) {\n const conn = net.connect(address),\n req = protocol.connect(deviceID, devicePort);\n\n /**\n * Handle complete messages from usbmuxd\n * @function\n */\n var parse = protocol.makeParser(function onMsgComplete(msg) {\n debug.connect(\"Response: \\n%o\", msg);\n\n if (msg.MessageType === \"Result\" && msg.Number === 0) {\n conn.removeListener(\"data\", parse);\n resolve(conn);\n return;\n }\n\n // anything other response means it failed\n reject(new UsbmuxdError(\"Tunnel failed\", msg.Number));\n conn.end();\n });\n\n debug.connect(\"Request: \\n%s\", req.subarray(16).toString());\n\n conn.on(\"data\", parse);\n process.nextTick(function () {\n conn.write(req);\n });\n });\n}\n\ntype RelayOpts = {\n timeout?: number;\n udid?: string;\n};\n/**\n * Creates a new tcp relay to a port on connected usb device\n *\n * @constructor\n * @param {integer} devicePort - Port to connect to on device\n * @param {integer} relayPort - Local port that will listen as relay\n * @param {object} [opts] - Options\n * @param {integer} [opts.timeout=1000] - Search time (ms) before warning\n * @param {string} [opts.udid] - UDID of specific device to connect to\n *\n * @public\n */\nexport class Relay extends EventEmitter {\n _devicePort: number;\n _relayPort: number;\n _udid?: string;\n constructor(devicePort: number, relayPort: number, opts: RelayOpts) {\n super();\n this._devicePort = devicePort;\n this._relayPort = relayPort;\n\n opts = opts || {};\n this._udid = opts.udid;\n\n this._startListener(opts.timeout || 1000);\n this._startServer();\n }\n /**\n * Stops the relay\n */\n stop = function () {\n this._listener.end();\n this._server.close();\n };\n /**\n * Debugging wrapper for emits\n */\n _emit = function (event: string, data: any) {\n debug.relay(\"Emit: %s\", event + (data ? \", Data: \" + data : \"\"));\n this.emit(event, data);\n };\n /**\n * Starts a usbmuxd listener\n *\n * Relay will start searching for connected devices and issue a warning if a\n * device is not found within the timeout. If/when a device is found, it will\n * emit a ready event.\n *\n * Listener events (attach, detach, error) are passed through as relay events.\n */\n _startListener = function (timeout: number) {\n var _this = this;\n\n var timer = setTimeout(function () {\n // no UDID was given and no devices found yet\n if (!_this._udid && !Object.keys(devices).length) {\n _this._emit(\"warning\", new Error(\"No devices connected\"));\n }\n // UDID was given, but that device is not connected\n if (_this._udid && !devices[_this._udid]) {\n _this._emit(\"warning\", new Error(\"Requested device not connected\"));\n }\n }, timeout || 1000);\n\n function readyCheck(udid: string) {\n if (_this._udid && _this._udid !== udid) return;\n _this._emit(\"ready\", udid);\n _this._listener.removeListener(\"attached\", readyCheck);\n clearTimeout(timer);\n }\n\n this._listener = createListener()\n .on(\"attached\", readyCheck)\n .on(\"attached\", _this._emit.bind(this, \"attached\"))\n .on(\"detached\", _this._emit.bind(this, \"detached\"))\n .on(\"error\", _this._emit.bind(this, \"error\"));\n };\n /**\n * Start local TCP server that will pipe to the usbmuxd tunnel\n *\n * Server events (close and error) are passed through as relay events.\n */\n _startServer = function () {\n var _this = this;\n this._server = net\n .createServer(this._handler.bind(this))\n .on(\"close\", _this._emit.bind(this, \"close\"))\n .on(\"error\", function (err) {\n _this._listener.end();\n _this._emit(\"error\", err);\n })\n .listen(this._relayPort);\n };\n /**\n * Handle & pipe connections from local server\n *\n * Fires error events and connection begin / disconnect events\n */\n _handler = function (conn: net.Socket) {\n // emit error if there are no devices connected\n if (!Object.keys(devices).length) {\n this._emit(\"error\", new Error(\"No devices connected\"));\n conn.end();\n return;\n }\n\n // emit error if a udid was specified but that device isn't connected\n if (this._udid && !devices[this._udid]) {\n this._emit(\"error\", new Error(\"Requested device not connected\"));\n conn.end();\n return;\n }\n\n // Use specified device or choose one from available devices\n var _this = this,\n udid = this._udid || Object.keys(devices)[0],\n deviceID = devices[udid].DeviceID;\n\n connect(deviceID, this._devicePort)\n .then(function (tunnel) {\n // pipe connection & tunnel together\n conn.pipe(tunnel).pipe(conn);\n\n _this._emit(\"connect\");\n\n conn.on(\"end\", function () {\n _this._emit(\"disconnect\");\n tunnel.end();\n conn.end();\n });\n\n conn.on(\"error\", function () {\n tunnel.end();\n conn.end();\n });\n })\n .catch(function (err) {\n _this._emit(\"error\", err);\n conn.end();\n });\n };\n}\n\n/**\n * Find a device (specified or not) within a timeout\n *\n * Usbmuxd has IDs it assigned to devices as they are plugged in. The IDs\n * change as devices are unpplugged and plugged back in, so even if we have a\n * UDID we need to get the current ID from usbmuxd before we can connect.\n */\nexport function findDevice(opts: RelayOpts): Promise<number> {\n return new Promise(function (resolve, reject) {\n var listener = createListener();\n opts = opts || {};\n\n var timer = setTimeout(function () {\n listener.end();\n opts.udid\n ? reject(new Error(\"Requested device not connected\"))\n : reject(new Error(\"No devices connected\"));\n }, opts.timeout || 1000);\n\n listener.on(\"attached\", function (udid) {\n if (opts.udid && opts.udid !== udid) return;\n listener.end();\n clearTimeout(timer);\n resolve(devices[udid].DeviceID);\n });\n });\n}\n\n/**\n * Get a tunneled connection to a device (specified or not) within a timeout\n */\nexport async function getTunnel(\n devicePort: number,\n opts: RelayOpts,\n): Promise<net.Socket> {\n opts = opts || {};\n let udid: string;\n let deviceID: DeviceId;\n\n // If UDID was specified and that device's DeviceID is known, connect to it\n if (opts.udid && devices[opts.udid]) {\n deviceID = devices[opts.udid].DeviceID;\n return connect(deviceID, devicePort);\n }\n\n // If no UDID given, connect to any known device\n // (random because no key order, but there's probably only 1 option anyways)\n if (!opts.udid && Object.keys(devices).length) {\n udid = Object.keys(devices)[0];\n deviceID = devices[udid].DeviceID;\n return connect(deviceID, devicePort);\n }\n\n // - Try to find and connect to requested the device (given opts.UDID),\n // - or find and connect to any device (no opts.UDID given)\n const deviceID_2 = await findDevice(opts);\n return await connect(deviceID_2, devicePort);\n}\n"],"mappings":"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","names":["bug","plist","connect","net"]}
|
@@ -0,0 +1,196 @@
|
|
1
|
+
import net from 'node:net';
|
2
|
+
import { EventEmitter } from 'events';
|
3
|
+
|
4
|
+
type Device = {
|
5
|
+
ConnectionType: string;
|
6
|
+
DeviceID: DeviceId;
|
7
|
+
LocationID: number;
|
8
|
+
ProductID: number;
|
9
|
+
SerialNumber: string;
|
10
|
+
};
|
11
|
+
/**
|
12
|
+
* Keep track of connected devices
|
13
|
+
*
|
14
|
+
* Maps device UDID to device properties, ie:
|
15
|
+
* '22226dd59aaac687f555f8521f8ffddac32d394b': {
|
16
|
+
* ConnectionType: 'USB',
|
17
|
+
* DeviceID: 19,
|
18
|
+
* LocationID: 0,
|
19
|
+
* ProductID: 4776,
|
20
|
+
* SerialNumber: '22226dd59aaac687f555f8521f8ffddac32d394b'
|
21
|
+
* }
|
22
|
+
*
|
23
|
+
* Devices are added and removed to this obj only by createListener()
|
24
|
+
*
|
25
|
+
*/
|
26
|
+
declare const devices: Record<string, Device>;
|
27
|
+
/**
|
28
|
+
* usbmuxd address
|
29
|
+
*
|
30
|
+
* OSX usbmuxd listens on a unix socket at /var/run/usbmuxd
|
31
|
+
* Windows usbmuxd listens on port 27015
|
32
|
+
*
|
33
|
+
* libimobiledevice[1] looks like it operates at /var/run/usbmuxd too, but if
|
34
|
+
* your usbmuxd is listening somewhere else you'll need to set this manually.
|
35
|
+
*
|
36
|
+
* [1] github.com/libimobiledevice/usbmuxd
|
37
|
+
*/
|
38
|
+
declare const address: {
|
39
|
+
port: number;
|
40
|
+
family: number;
|
41
|
+
path?: undefined;
|
42
|
+
} | {
|
43
|
+
path: string;
|
44
|
+
port?: undefined;
|
45
|
+
family?: undefined;
|
46
|
+
};
|
47
|
+
/**
|
48
|
+
* Exposes methods for dealing with usbmuxd protocol messages (send/receive)
|
49
|
+
*
|
50
|
+
* The usbmuxd message protocol has 2 versions. V1 doesn't look like its used
|
51
|
+
* anymore. V2 is a header + plist format like this:
|
52
|
+
*
|
53
|
+
* Header:
|
54
|
+
* UInt32LE Length - is the length of the header + plist (16 + plist.length)
|
55
|
+
* UInt32LE Version - is 0 for binary version, 1 for plist version
|
56
|
+
* UInt32LE Request - is always 8, for plist? from rcg4u/iphonessh
|
57
|
+
* UInt32LE Tag - is always 1, ? from rcg4u/iphonessh
|
58
|
+
*
|
59
|
+
* Plist:
|
60
|
+
* <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
|
61
|
+
* "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
62
|
+
* <plist version="1.0">
|
63
|
+
* <dict>
|
64
|
+
* <key>MessageType</key>
|
65
|
+
* <string>Listen</string>
|
66
|
+
* <key>ClientVersionString</key>
|
67
|
+
* <string>node-usbmux</string>
|
68
|
+
* <key>ProgName</key>
|
69
|
+
* <string>node-usbmux</string>
|
70
|
+
* </dict>
|
71
|
+
* </plist>
|
72
|
+
*
|
73
|
+
* References:
|
74
|
+
* - https://github.com/rcg4u/iphonessh
|
75
|
+
* - https://www.theiphonewiki.com/wiki/Usbmux (binary protocol)
|
76
|
+
*/
|
77
|
+
type DeviceId = number;
|
78
|
+
type UsbMuxPlist = {
|
79
|
+
MessageType: "Result";
|
80
|
+
Number: number;
|
81
|
+
} | {
|
82
|
+
MessageType: "Attached";
|
83
|
+
Number: number;
|
84
|
+
Properties: Device;
|
85
|
+
} | {
|
86
|
+
MessageType: "Detached";
|
87
|
+
Number: number;
|
88
|
+
DeviceID: DeviceId;
|
89
|
+
};
|
90
|
+
declare const protocol: {
|
91
|
+
listen: Buffer<ArrayBufferLike>;
|
92
|
+
connect: (deviceID: DeviceId, port: number) => Buffer;
|
93
|
+
makeParser: (onComplete: (msg: UsbMuxPlist) => void) => (data: Buffer) => void;
|
94
|
+
};
|
95
|
+
/**
|
96
|
+
* Custom usbmuxd error
|
97
|
+
*
|
98
|
+
* There's no documentation for usbmuxd responses, but I think I've figured
|
99
|
+
* out these result numbers:
|
100
|
+
* 0 - Success
|
101
|
+
* 2 - Device requested isn't connected
|
102
|
+
* 3 - Port requested isn't available \ open
|
103
|
+
* 5 - Malformed request
|
104
|
+
*/
|
105
|
+
declare class UsbmuxdError extends Error {
|
106
|
+
number: number;
|
107
|
+
constructor(message: string, number: number);
|
108
|
+
}
|
109
|
+
/**
|
110
|
+
* Connects to usbmuxd and listens for ios devices
|
111
|
+
*
|
112
|
+
* This connection stays open, listening as devices are plugged/unplugged and
|
113
|
+
* cant be upgraded into a tcp tunnel. You have to start a second connection
|
114
|
+
* with connect() to actually make tunnel.
|
115
|
+
*
|
116
|
+
* @return {net.Socket} - Socket with 2 bolted on events, attached & detached:
|
117
|
+
*
|
118
|
+
* Fires when devices are plugged in or first found by the listener
|
119
|
+
* @event net.Socket#attached
|
120
|
+
* @type {string} - UDID
|
121
|
+
*
|
122
|
+
* Fires when devices are unplugged
|
123
|
+
* @event net.Socket#detached
|
124
|
+
* @type {string} - UDID
|
125
|
+
*/
|
126
|
+
declare function createListener(): net.Socket;
|
127
|
+
/**
|
128
|
+
* Connects to a device through usbmuxd for a tunneled tcp connection
|
129
|
+
*/
|
130
|
+
declare function connect(deviceID: DeviceId, devicePort: number): Promise<net.Socket>;
|
131
|
+
type RelayOpts = {
|
132
|
+
timeout?: number;
|
133
|
+
udid?: string;
|
134
|
+
};
|
135
|
+
/**
|
136
|
+
* Creates a new tcp relay to a port on connected usb device
|
137
|
+
*
|
138
|
+
* @constructor
|
139
|
+
* @param {integer} devicePort - Port to connect to on device
|
140
|
+
* @param {integer} relayPort - Local port that will listen as relay
|
141
|
+
* @param {object} [opts] - Options
|
142
|
+
* @param {integer} [opts.timeout=1000] - Search time (ms) before warning
|
143
|
+
* @param {string} [opts.udid] - UDID of specific device to connect to
|
144
|
+
*
|
145
|
+
* @public
|
146
|
+
*/
|
147
|
+
declare class Relay extends EventEmitter {
|
148
|
+
_devicePort: number;
|
149
|
+
_relayPort: number;
|
150
|
+
_udid?: string;
|
151
|
+
constructor(devicePort: number, relayPort: number, opts: RelayOpts);
|
152
|
+
/**
|
153
|
+
* Stops the relay
|
154
|
+
*/
|
155
|
+
stop: () => void;
|
156
|
+
/**
|
157
|
+
* Debugging wrapper for emits
|
158
|
+
*/
|
159
|
+
_emit: (event: string, data: any) => void;
|
160
|
+
/**
|
161
|
+
* Starts a usbmuxd listener
|
162
|
+
*
|
163
|
+
* Relay will start searching for connected devices and issue a warning if a
|
164
|
+
* device is not found within the timeout. If/when a device is found, it will
|
165
|
+
* emit a ready event.
|
166
|
+
*
|
167
|
+
* Listener events (attach, detach, error) are passed through as relay events.
|
168
|
+
*/
|
169
|
+
_startListener: (timeout: number) => void;
|
170
|
+
/**
|
171
|
+
* Start local TCP server that will pipe to the usbmuxd tunnel
|
172
|
+
*
|
173
|
+
* Server events (close and error) are passed through as relay events.
|
174
|
+
*/
|
175
|
+
_startServer: () => void;
|
176
|
+
/**
|
177
|
+
* Handle & pipe connections from local server
|
178
|
+
*
|
179
|
+
* Fires error events and connection begin / disconnect events
|
180
|
+
*/
|
181
|
+
_handler: (conn: net.Socket) => void;
|
182
|
+
}
|
183
|
+
/**
|
184
|
+
* Find a device (specified or not) within a timeout
|
185
|
+
*
|
186
|
+
* Usbmuxd has IDs it assigned to devices as they are plugged in. The IDs
|
187
|
+
* change as devices are unpplugged and plugged back in, so even if we have a
|
188
|
+
* UDID we need to get the current ID from usbmuxd before we can connect.
|
189
|
+
*/
|
190
|
+
declare function findDevice(opts: RelayOpts): Promise<number>;
|
191
|
+
/**
|
192
|
+
* Get a tunneled connection to a device (specified or not) within a timeout
|
193
|
+
*/
|
194
|
+
declare function getTunnel(devicePort: number, opts: RelayOpts): Promise<net.Socket>;
|
195
|
+
|
196
|
+
export { Relay, UsbmuxdError, address, connect, createListener, devices, findDevice, getTunnel, protocol };
|