forked from GladysAssistant/Gladys
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexternalIntegration.wakeOnLan.js
More file actions
133 lines (107 loc) · 3.66 KB
/
Copy pathexternalIntegration.wakeOnLan.js
File metadata and controls
133 lines (107 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
const dgram = require('dgram');
const net = require('net');
const { BadParameters, ForbiddenError } = require('../../utils/coreErrors');
const DEFAULT_SOURCE_PORT = 0;
const DEFAULT_PORT = 9;
const DEFAULT_ADDRESS = '255.255.255.255';
/**
* @description Normalize a MAC address to a Buffer.
* @param {string} mac - MAC address in format xx:xx:xx:xx:xx:xx.
* @returns {Buffer<ArrayBuffer>} - MAC address as a Buffer.
* @example
* normalizeMac('00:11:22:33:44:55');
*/
function normalizeMac(mac) {
if (typeof mac !== 'string') {
throw new BadParameters('MAC address must be a string');
}
const normalized = mac.replace(/[:-]/g, '');
if (!/^[0-9a-fA-F]{12}$/.test(normalized)) {
throw new BadParameters('Invalid MAC address');
}
return Buffer.from(normalized, 'hex');
}
/**
* @description Build a Wake-on-LAN magic packet.
* @param {string} mac - MAC address in format xx:xx:xx:xx:xx:xx.
* @returns {Buffer<ArrayBuffer>} Magic packet as a Buffer.
* @example
* buildMagicPacket('00:11:22:33:44:55');
*/
function buildMagicPacket(mac) {
const macBuffer = normalizeMac(mac);
return Buffer.concat([Buffer.alloc(6, 0xff), ...Array.from({ length: 16 }, () => macBuffer)]);
}
/**
* @description Send a Wake-on-LAN magic packet to a target device.
* @param {object} service - The external integration service (plain object).
* @param {object} service.manifest - The external integration manifest.
* @param {boolean} [service.manifest.network_wake] - Whether Wake-on-LAN is allowed for this integration.
* @param {object} options - Wake-on-LAN options.
* @param {string} options.mac - Target MAC address.
* @param {string} [options.address] - Destination/broadcast address.
* @param {number} [options.port] - Destination UDP port.
* @param {number} [options.sourcePort] - Source UDP port.
* @returns {Promise<void>} Promise that resolves when the magic packet is sent.
* @example
* await gladys.externalIntegration.wakeOnLan(service, { mac: '00:11:22:33:44:55' });
*/
async function wakeOnLan(service, options) {
if (!options || typeof options !== 'object' || Array.isArray(options)) {
throw new BadParameters('Invalid Wake-on-LAN options');
}
const { mac, address = DEFAULT_ADDRESS, port = DEFAULT_PORT, sourcePort = DEFAULT_SOURCE_PORT } = options;
if (!net.isIPv4(address)) {
throw new BadParameters('Invalid IPv4 address');
}
if (!Number.isInteger(port) || port < 1 || port > 65535) {
throw new BadParameters('port: must be an integer between 1 and 65535');
}
if (!Number.isInteger(sourcePort) || sourcePort < 0 || sourcePort > 65535) {
throw new BadParameters('sourcePort: must be an integer between 0 and 65535');
}
if (!service.manifest || service.manifest.network_wake !== true) {
throw new ForbiddenError('Wake-on-LAN is not allowed for this integration');
}
const payload = buildMagicPacket(mac);
const socket = dgram.createSocket({
type: 'udp4',
reuseAddr: true,
});
await new Promise((resolve, reject) => {
let settled = false;
const fail = (error) => {
if (settled) {
return;
}
settled = true;
socket.close(() => {
reject(error);
});
};
socket.once('error', fail);
socket.bind(sourcePort, () => {
try {
socket.setBroadcast(true);
socket.send(payload, port, address, (error) => {
if (settled) {
return;
}
if (error) {
fail(error);
return;
}
settled = true;
socket.close(() => {
resolve();
});
});
} catch (error) {
fail(error);
}
});
});
}
module.exports = {
wakeOnLan,
};