Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@
"manual_list" = True/False for creating your own JSON list of devices as device.json. See device.example.json.
"manual_ip" = True/False for adding the IP to the device.json or to allow the script to find IPs.
"use_ios_deploy" = True/False for using ios-deploy instead of cfgutil for querying devices (disables the profile command).
"allow_cmds" = True/False for allowing the execution of local commands that are defined in "cmds".
"cmds" = An array of objects that contain the "alias" (required), "command" (required), and "description" (optional).
"alias" is the name that you can send to the listener to execute a command without typing the full command.
"command" is a string used to hold the command and its parameters. Note that you should use nohup if the command's error output is larger than 200kb like Redux.
"description" is a string that you can use to keep notes about your command.
Note that passing parameters or using sudo are not supported as these open your system to attacks.

```

## Android support
Expand Down
15 changes: 14 additions & 1 deletion config.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,18 @@
"domain": "http://YOUR-DOMAIN.TLD",
"manual_list": false,
"manual_ip": false,
"use_ios_deploy": false
"use_ios_deploy": false,
"allow_cmds": false,
"cmds": [
{
"alias": "1",
"command": "pwd",
"description": "The pwd command prints the working directory"
},
{
"alias": "redux_1",
"command": "cd ~/redux/ && nohup ./redux.js -f",
"description": "This command changes to the Redux directory and executes Redux with the full auto parameter. Output is sent to nohup.out so the listener can avoid buffer overflows"
}
]
}
115 changes: 91 additions & 24 deletions listener.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const Request = require("request");
const express = require("express");
const { exec } = require("child_process");
const config = require("./config.json");
const cmds = config.cmds;

// DEFINE THE EXPRESS SERVER
var server = express().use(express.json({ limit: "1mb" }));
Expand Down Expand Up @@ -40,12 +41,13 @@ else if (config.use_ios_deploy) {
server.post("/", (payload, res) => {
console.log("[DCM] [listener.js] [" + getTime("log") + "] Received a Payload:", payload.body);
let target = payload.body;
// GO THROUGH DEVICE ARRAY TO FIND A MATCH
devices.forEach(async (device, i) => {
switch (target.type) {
// CHECK IF THE PAYLOAD IS FOR DEVICES OR COMMANDS
// IF NOT A COMMAND, GO THROUGH DEVICE ARRAY TO FIND A MATCH
switch (target.type) {

// RESTART A DEVICE
case "restart":
// RESTART A DEVICE
case "restart":
devices.forEach(async (device, i) => {
if (device.name == target.device) {
if (device.reboot_cmd) {
var command = device.reboot_cmd
Expand Down Expand Up @@ -75,10 +77,12 @@ server.post("/", (payload, res) => {
});
}
}
break;
});
break;

// REOPEN THE GAME
case "reopen":
// REOPEN THE GAME
case "reopen":
devices.forEach(async (device, i) => {
if (device.name == target.device) {
var ipaddr = '';
if (config.manual_ip) {
Expand Down Expand Up @@ -127,14 +131,26 @@ server.post("/", (payload, res) => {
});
}
}
break;
});
break;

// REAPPLY THE SAM PROFILE
case "profile":
if (!config.use_ios_deploy && device.name == target.device) {
// REAPPLY THE SAM PROFILE
case "profile":
if (config.use_ios_deploy) {
res.json({
status: 'error',
node: config.name,
error: 'Listener set to only use iOS_Deploy.'
});
break;
}
devices.forEach(async (device, i) => {
if (device.name == target.device) {
// REMOVE THE SAM PROFILE
var errored = false;
var profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt remove-profile com.apple.configurator.singleappmode", "device_command");
if (profile.hasError) {
errored = true;
console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to remove the SAM1 profile from " + device.name + " : " + device.uuid + ".", profile.error);
res.json({
status: 'error',
Expand All @@ -146,6 +162,7 @@ server.post("/", (payload, res) => {
// APPLY THE CLOCK PROFILE TO FORCE THE GAME CLOSED
profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt install-profile sam_clock.mobileconfig", "device_command");
if (profile.hasError) {
errored = true;
console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to add the SAM_CLOCK profile to " + device.name + " : " + device.uuid + ".", profile.error);
res.json({
status: 'error',
Expand All @@ -157,6 +174,7 @@ server.post("/", (payload, res) => {
// REMOVE THE SAM PROFILE AGAIN
profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt remove-profile com.apple.configurator.singleappmode", "device_command");
if (profile.hasError) {
errored = true;
console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to remove the SAM2 profile from " + device.name + " : " + device.uuid + ".", profile.error);
res.json({
status: 'error',
Expand All @@ -168,26 +186,30 @@ server.post("/", (payload, res) => {
// APPLY THE POGO PROFILE TO RELAUNCH THE GAME
profile = await cli_exec("cfgutil -e " + device.ecid + " -K org.der -C org.crt install-profile sam_pogo.mobileconfig", "device_command");
if (profile.hasError) {
errored = true;
console.error("[DCM] [listener.js] [" + getTime("log") + "] Failed to add the SAM_CALC profile to " + device.name + " : " + device.uuid + ".", profile.error);
res.json({
status: 'error',
node: config.name,
error: 'Failed to add the SAM_POGO profile.'
});
break;
}

// REAPPLICATION WAS SUCCESSFUL
console.log("[DCM] [listener.js] [" + getTime("log") + "] Reapplied the SAM profile to " + device.name + " : " + device.uuid + ".");
// SEND CONFIRMATION TO DCM
res.json({
status: 'ok'
});
if (!errored) {
// REAPPLICATION WAS SUCCESSFUL
console.log("[DCM] [listener.js] [" + getTime("log") + "] Reapplied the SAM profile to " + device.name + " : " + device.uuid + ".");
// SEND CONFIRMATION TO DCM
res.json({
status: 'ok'
});
}
}
break;
});
break;

// CHANGE DEVICE BRIGHTNESS
case "brightness":
// CHANGE DEVICE BRIGHTNESS
case "brightness":
devices.forEach(async (device, i) => {
if (device.name == target.device) {
let ipaddr = '';
if (config.manual_ip) {
Expand Down Expand Up @@ -237,9 +259,51 @@ server.post("/", (payload, res) => {
});
}
}
});
break;

// INCOMING COMMAND
case "cmd":
if(!config.allow_cmds) {
res.json({
status: 'error',
node: config.name,
error: 'Listener is not configured to execute local commands.'
});
break;
}
});
}
// CHECK IF COMMAND IS IN THE CONFIG
cmds.forEach(async (cmd, i) => {
if(cmd.alias == target.cmdID) {
var cmd_string = cmd.command.toString();
var cmd_results = await cli_exec(cmd_string, 'local_command');
if(cmd_results.hasError) {
res.json({
status: 'error',
node: config.name,
message: 'Failed to execute command: ' + cmd.alias + '\n' + cmd_results.error
});
}
else {
res.json({
status: 'ok',
node: config.name,
message: 'Listener ran command `'+cmd.alias+'` and output the following:\n'+cmd_results.result
});
}
}
});
break;

// DEFAULT
default:
res.json({
status: 'error',
node: config.name,
error: 'Listener received an unhandled request type: ' + target.type
});
break;
}
});

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -343,6 +407,9 @@ function cli_exec(command, type) {
}
return resolve(ipaddr);

// BLOCK IN CASE WE NEED DIFFERENT INFO FOR LOCAL COMMANDS
case 'local_command':

// GENERAL IDEVICEDIAGNOSTICS COMMAND
case 'device_command':
response.hasError = false;
Expand Down