Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /api/v1/services/wol/send/ endpoint to be able to send a WOL packet #237

Merged
merged 3 commits into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
26 changes: 26 additions & 0 deletions pfSense-pkg-API/files/etc/inc/api/endpoints/APIServicesWOLSend.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
// Copyright 2022 Jared Hendrickson
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

require_once("api/framework/APIEndpoint.inc");

class APIServicesWOLSend extends APIEndpoint {
public function __construct() {
$this->url = "/api/v1/services/wol/send";
}

protected function post() {
return (new APIServicesWOLSendCreate())->call();
}
}
30 changes: 30 additions & 0 deletions pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1193,6 +1193,36 @@ function get($id, $data=[], $all=false) {
"return" => $id,
"message" => "Host overrides must be contained within an array"
],
2099 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "No interface specified"
],
2100 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Could not obtain IP address for interface"
],
2101 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "No MAC address specified"
],
2102 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Invalid MAC address specified"
],
2103 => [
"status" => "server error",
"code" => 500,
"return" => $id,
"message" => "Please check the system_logs, the WOL command did not complete successfully"
],
2999 => [
"status" => "bad request",
"code" => 400,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
// Copyright 2022 Jared Hendrickson
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

require_once("api/framework/APIModel.inc");
require_once("api/framework/APIResponse.inc");

class APIServicesWOLSendCreate extends APIModel {
# Create our method constructor
public function __construct() {
parent::__construct();
$this->privileges = ["page-all", "page-services-wakeonlan"];
$this->change_note = "Sent magic WOL packet via API";
}

public function action() {
$if = $this->validated_data["interface"];
$if_ip = $this->validated_data["interface_ip"];
$mac = $this->validated_data["mac"];

$bc_ip = $this->__get_broadcast_ip($if, $if_ip);
$response_data = array_merge($this->validated_data, ["broadcast_ip" => $bc_ip]);

if (!mwexec("/usr/local/bin/wol -i {$bc_ip} {$mac}")) {
return APIResponse\get(0, $response_data);
} else {
return APIResponse\get(2103, $response_data);
}
}

private function __get_broadcast_ip($if, $if_ip) {
return gen_subnet_max($if_ip, get_interface_subnet($if));
}

public function validate_payload() {
$this->__validate_interface();
$this->__validate_mac();
}

private function __validate_interface() {
if (isset($this->initial_data["interface"])) {
$interface_ip = get_interface_ip($this->initial_data["interface"]);
wuarmin marked this conversation as resolved.
Show resolved Hide resolved
if (is_ipaddr($interface_ip)) {
$this->validated_data["interface"] = $this->initial_data["interface"];
$this->validated_data["interface_ip"] = $interface_ip;
} else {
$this->errors[] = APIResponse\get(2100);
}
} else {
$this->errors[] = APIResponse\get(2099);
}
}

private function __validate_mac() {
if (isset($this->initial_data["mac"])) {
if (is_macaddr($this->initial_data["mac"])) {
$this->validated_data["mac"] = $this->initial_data["mac"];
} else {
$this->errors[] = APIResponse\get(2102);
}
} else {
$this->errors[] = APIResponse\get(2101);
}
}
}
29 changes: 29 additions & 0 deletions pfSense-pkg-API/files/usr/local/www/api/documentation/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7500,6 +7500,34 @@ paths:
summary: Stop DNS Resolver (Unbound) service
tags:
- Services > Unbound
/api/v1/services/wol/send:
post:
description: 'Send a magic WOL packet to a host.<br><br>

_Requires at least one of the following privileges:_ [`page-all`, `page-services-wakeonlan`]'
requestBody:
content:
application/json:
schema:
properties:
interface:
description: Interface to which the host to be woken is connected.
type: string
mac:
description: MAC address of the host to be woken.
type: string
required:
- interface
- mac
type: object
responses:
200:
$ref: '#/components/responses/Success'
401:
$ref: '#/components/responses/AuthenticationFailed'
summary: Send a magic WOL packet to a host
tags:
- Services > WOL
/api/v1/status/carp:
get:
description: 'Read the CARP (failover) status.<br><br>
Expand Down Expand Up @@ -9580,5 +9608,6 @@ tags:
- name: Services > SSHD
- name: Services > SYSLOGD
- name: Services > DDNS
- name: Services > WOL
- name: Diagnostics > Command Prompt

63 changes: 63 additions & 0 deletions tests/test_api_v1_services_wol_send.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Copyright 2022 Jared Hendrickson
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import e2e_test_framework

class APIE2ETestServicesWOLSend(e2e_test_framework.APIE2ETest):
uri = "/api/v1/services/wol/send/"
post_tests = [
{
"name": "Send magic WOL packet",
"payload": {
"interface": "lan",
"mac": "2C:54:91:88:C9:E3"
}
},
{
"name": "Send magic WOL packet with missing interface",
"payload": {
"mac": "2C:54:91:88:C9:E3",
},
"status": 400,
"return": 2099
},
{
"name": "Send magic WOL packet with invalid interface",
"payload": {
"interface": "INVALID",
"mac": "2C:54:91:88:C9:E3"
},
"status": 400,
"return": 2100
},
{
"name": "Send magic WOL packet with missing mac",
"payload": {
"interface": "lan"
},
"status": 400,
"return": 2101
},
{
"name": "Send magic WOL packet with invalid mac",
"payload": {
"interface": "lan",
"mac": "INVALID"
},
"status": 400,
"return": 2102
},
]

APIE2ETestServicesWOLSend()