mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00
net: lws_wol() and lws_parse_mac()
Introduce a LWS_WITH_WOL and an api to wake a mac address, optionally with an address bind to the local interface to go out on. Add a helper to parse ascii mac addresses well, and add tests.
This commit is contained in:
parent
3454cd1f8a
commit
5bcc5ac655
10 changed files with 318 additions and 1 deletions
|
@ -326,6 +326,7 @@ option(LWS_HTTP_HEADERS_ALL "Override header reduction optimization and include
|
|||
option(LWS_WITH_SUL_DEBUGGING "Enable zombie lws_sul checking on object deletion" OFF)
|
||||
option(LWS_WITH_PLUGINS_API "Build generic lws_plugins apis (see LWS_WITH_PLUGINS to also build protocol plugins)" OFF)
|
||||
option(LWS_WITH_CONMON "Collect introspectable connection latency stats on individual client connections" ON)
|
||||
option(LWS_WITH_WOL "Wake On Lan support" ON)
|
||||
option(LWS_WITHOUT_EVENTFD "Force using pipe instead of eventfd" OFF)
|
||||
if (UNIX OR WIN32)
|
||||
option(LWS_WITH_CACHE_NSCOOKIEJAR "Build file-backed lws-cache-ttl that uses netscape cookie jar format (linux-only)" ON)
|
||||
|
|
|
@ -1250,3 +1250,15 @@ lws_minilex_parse(const uint8_t *lex, int16_t *ps, const uint8_t c,
|
|||
|
||||
LWS_VISIBLE LWS_EXTERN unsigned int
|
||||
lws_sigbits(uintptr_t u);
|
||||
|
||||
/**
|
||||
* lws_wol() - broadcast a magic WOL packet to MAC, optionally binding to if IP
|
||||
*
|
||||
* \p ctx: The lws context
|
||||
* \p ip_or_NULL: The IP address to bind to at the client side, to send the
|
||||
* magic packet on. If NULL, the system chooses, probably the
|
||||
* interface with the default route.
|
||||
* \p mac_6_bytes: Points to a 6-byte MAC address to direct the magic packet to
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_wol(struct lws_context *ctx, const char *ip_or_NULL, uint8_t *mac_6_bytes);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2020 Andy Green <andy@warmcat.com>
|
||||
* Copyright (C) 2010 - 2023 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
|
@ -246,4 +246,16 @@ lws_write_numeric_address(const uint8_t *ads, int size, char *buf, size_t len);
|
|||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_sa46_write_numeric_address(lws_sockaddr46 *sa46, char *buf, size_t len);
|
||||
|
||||
/**
|
||||
* lws_parse_mac() - convert XX:XX:XX:XX:XX:XX to 6-byte MAC address
|
||||
*
|
||||
* \param ads: mac address as XX:XX:XX:XX:XX:XX string
|
||||
* \param result_6_bytes: result buffer to take 6 bytes
|
||||
*
|
||||
* Converts a string representation of a 6-byte hex mac address to a 6-byte
|
||||
* array.
|
||||
*/
|
||||
LWS_VISIBLE LWS_EXTERN int
|
||||
lws_parse_mac(const char *ads, uint8_t *result_6_bytes);
|
||||
|
||||
///@}
|
||||
|
|
|
@ -56,6 +56,11 @@ if (LWS_WITH_LWS_DSH)
|
|||
core-net/lws-dsh.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_WOL)
|
||||
list(APPEND SOURCES
|
||||
core-net/wol.c)
|
||||
endif()
|
||||
|
||||
if (LWS_WITH_CLIENT)
|
||||
list(APPEND SOURCES
|
||||
core-net/client/client.c
|
||||
|
|
|
@ -1104,3 +1104,62 @@ lws_system_get_state_manager(struct lws_context *context)
|
|||
return &context->mgr_system;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
lws_parse_mac(const char *ads, uint8_t *result_6_bytes)
|
||||
{
|
||||
uint8_t *p = result_6_bytes;
|
||||
struct lws_tokenize ts;
|
||||
char t[3];
|
||||
size_t n;
|
||||
long u;
|
||||
|
||||
lws_tokenize_init(&ts, ads, LWS_TOKENIZE_F_NO_INTEGERS |
|
||||
LWS_TOKENIZE_F_MINUS_NONTERM);
|
||||
ts.len = strlen(ads);
|
||||
|
||||
do {
|
||||
ts.e = (int8_t)lws_tokenize(&ts);
|
||||
switch (ts.e) {
|
||||
case LWS_TOKZE_TOKEN:
|
||||
if (ts.token_len != 2)
|
||||
return -1;
|
||||
if (p - result_6_bytes == 6)
|
||||
return -2;
|
||||
t[0] = ts.token[0];
|
||||
t[1] = ts.token[1];
|
||||
t[2] = '\0';
|
||||
for (n = 0; n < 2; n++)
|
||||
if (t[n] < '0' || t[n] > 'f' ||
|
||||
(t[n] > '9' && t[n] < 'A') ||
|
||||
(t[n] > 'F' && t[n] < 'a'))
|
||||
return -1;
|
||||
u = strtol(t, NULL, 16);
|
||||
if (u > 0xff)
|
||||
return -5;
|
||||
*p++ = (uint8_t)u;
|
||||
break;
|
||||
|
||||
case LWS_TOKZE_DELIMITER:
|
||||
if (*ts.token != ':')
|
||||
return -10;
|
||||
if (p - result_6_bytes > 5)
|
||||
return -11;
|
||||
break;
|
||||
|
||||
case LWS_TOKZE_ENDED:
|
||||
if (p - result_6_bytes != 6)
|
||||
return -12;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
lwsl_err("%s: malformed mac\n", __func__);
|
||||
|
||||
return -13;
|
||||
}
|
||||
} while (ts.e > 0);
|
||||
|
||||
lwsl_err("%s: ended on e %d\n", __func__, ts.e);
|
||||
|
||||
return -14;
|
||||
}
|
||||
|
|
84
lib/core-net/wol.c
Normal file
84
lib/core-net/wol.c
Normal file
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* libwebsockets - small server side websockets and web server implementation
|
||||
*
|
||||
* Copyright (C) 2010 - 2023 Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "private-lib-core.h"
|
||||
|
||||
int
|
||||
lws_wol(struct lws_context *ctx, const char *ip_or_NULL, uint8_t *mac_6_bytes)
|
||||
{
|
||||
int n, m, ofs = 0, fd, optval = 1, ret = 1;
|
||||
uint8_t pkt[17 * IFHWADDRLEN];
|
||||
struct sockaddr_in addr;
|
||||
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0) {
|
||||
lwsl_cx_err(ctx, "failed to open UDP, errno %d\n", errno);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
|
||||
(char *)&optval, sizeof(optval)) < 0) {
|
||||
lwsl_cx_err(ctx, "failed to set broadcast, errno %d\n", errno);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lay out the magic packet
|
||||
*/
|
||||
|
||||
for (n = 0; n < IFHWADDRLEN; n++)
|
||||
pkt[ofs++] = 0xff;
|
||||
for (m = 0; m < 16; m++)
|
||||
for (n = 0; n < IFHWADDRLEN; n++)
|
||||
pkt[ofs++] = mac_6_bytes[n];
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(9);
|
||||
|
||||
if (!inet_aton(ip_or_NULL ? ip_or_NULL : "255.255.255.255",
|
||||
&addr.sin_addr)) {
|
||||
lwsl_cx_err(ctx, "failed to convert broadcast ads, errno %d\n",
|
||||
errno);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
lwsl_cx_notice(ctx, "Sending WOL to %02X:%02X:%02X:%02X:%02X:%02X %s\n",
|
||||
mac_6_bytes[0], mac_6_bytes[1], mac_6_bytes[2], mac_6_bytes[3],
|
||||
mac_6_bytes[4], mac_6_bytes[5], ip_or_NULL ? ip_or_NULL : "");
|
||||
|
||||
if (sendto(fd, pkt, sizeof(pkt), 0, (struct sockaddr *)&addr,
|
||||
sizeof(addr)) < 0) {
|
||||
lwsl_cx_err(ctx, "failed to sendto broadcast ads, errno %d\n",
|
||||
errno);
|
||||
goto bail;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
bail:
|
||||
close(fd);
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -425,6 +425,7 @@ main(int argc, const char **argv)
|
|||
{
|
||||
int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
|
||||
struct lws_context_creation_info info;
|
||||
uint8_t mac[6];
|
||||
const char *p;
|
||||
|
||||
/* fixup dynamic target addresses we're testing against */
|
||||
|
@ -517,6 +518,40 @@ main(int argc, const char **argv)
|
|||
ok++;
|
||||
}
|
||||
|
||||
/* mac address parser tests */
|
||||
|
||||
if (lws_parse_mac("11:ff:ce:CE:22:33", mac)) {
|
||||
lwsl_err("%s: mac fail 1\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
} else
|
||||
if (mac[0] != 0x11 || mac[1] != 0xff || mac[2] != 0xce ||
|
||||
mac[3] != 0xce || mac[4] != 0x22 || mac[5] != 0x33) {
|
||||
lwsl_err("%s: mac fail 2\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
}
|
||||
if (!lws_parse_mac("11:ff:ce:CE:22:3", mac)) {
|
||||
lwsl_err("%s: mac fail 3\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
}
|
||||
if (!lws_parse_mac("11:ff:ce:CE:22", mac)) {
|
||||
lwsl_err("%s: mac fail 4\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
}
|
||||
if (!lws_parse_mac("11:ff:ce:CE:22:", mac)) {
|
||||
lwsl_err("%s: mac fail 5\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
}
|
||||
if (!lws_parse_mac("11:ff:ce:CE22", mac)) {
|
||||
lwsl_err("%s: mac fail 6\n", __func__);
|
||||
lwsl_hexdump_notice(mac, 6);
|
||||
fail++;
|
||||
}
|
||||
|
||||
#if !defined(LWS_WITH_IPV6)
|
||||
_exp -= 2;
|
||||
#endif
|
||||
|
|
23
minimal-examples-lowlevel/raw/minimal-raw-wol/CMakeLists.txt
Normal file
23
minimal-examples-lowlevel/raw/minimal-raw-wol/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
project(lws-minimal-raw-wol C)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
find_package(libwebsockets CONFIG REQUIRED)
|
||||
list(APPEND CMAKE_MODULE_PATH ${LWS_CMAKE_DIR})
|
||||
include(CheckCSourceCompiles)
|
||||
include(LwsCheckRequirements)
|
||||
|
||||
set(SAMP lws-minimal-raw-wol)
|
||||
set(SRCS minimal-raw-wol.c)
|
||||
|
||||
set(requirements 1)
|
||||
require_lws_config(LWS_WITH_WOL 1 requirements)
|
||||
|
||||
if (requirements)
|
||||
add_executable(${SAMP} ${SRCS})
|
||||
|
||||
if (websockets_shared)
|
||||
target_link_libraries(${SAMP} websockets_shared ${LIBWEBSOCKETS_DEP_LIBS})
|
||||
add_dependencies(${SAMP} websockets_shared)
|
||||
else()
|
||||
target_link_libraries(${SAMP} websockets ${LIBWEBSOCKETS_DEP_LIBS})
|
||||
endif()
|
||||
endif()
|
34
minimal-examples-lowlevel/raw/minimal-raw-wol/README.md
Normal file
34
minimal-examples-lowlevel/raw/minimal-raw-wol/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# lws minimal raw wol
|
||||
|
||||
This example shows how to send a Wake On Lan magic packet to a given mac.
|
||||
|
||||
## build
|
||||
|
||||
```
|
||||
$ cmake . && make
|
||||
```
|
||||
|
||||
## usage
|
||||
|
||||
```
|
||||
$ bin/lws-minimal-raw-wol b4:2e:99:a9:22:90
|
||||
[2023/11/09 12:25:24:2255] N: lws_create_context: LWS: 4.3.99-v4.3.0-295-g60d671c7, NET CLI SRV H1 H2 WS SS-JSON-POL ConMon ASYNC_DNS IPv6-absent
|
||||
[2023/11/09 12:25:24:2256] N: __lws_lc_tag: ++ [wsi|0|pipe] (1)
|
||||
[2023/11/09 12:25:24:2256] N: __lws_lc_tag: ++ [vh|0|netlink] (1)
|
||||
[2023/11/09 12:25:24:2256] N: __lws_lc_tag: ++ [vh|1|system||-1] (2)
|
||||
[2023/11/09 12:25:24:2257] N: __lws_lc_tag: ++ [wsisrv|0|system|asyncdns] (1)
|
||||
[2023/11/09 12:25:24:2257] N: __lws_lc_tag: ++ [wsisrv|1|system|asyncdns] (2)
|
||||
[2023/11/09 12:25:24:2257] N: __lws_lc_tag: ++ [vh|2|default||0] (3)
|
||||
[2023/11/09 12:25:24:2257] N: [vh|2|default||0]: lws_socket_bind: source ads 0.0.0.0
|
||||
[2023/11/09 12:25:24:2257] N: __lws_lc_tag: ++ [wsi|1|listen|default||33749] (2)
|
||||
[2023/11/09 12:25:24:2257] N: lws_wol: Sending WOL to B4:2E:99:A9:22:90
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [wsi|0|pipe] (1) 190μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [wsi|1|listen|default||33749] (0) 80μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [wsisrv|1|system|asyncdns] (1) 118μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [wsisrv|0|system|asyncdns] (0) 155μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [vh|0|netlink] (2) 198μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [vh|1|system||-1] (1) 182μs
|
||||
[2023/11/09 12:25:24:2258] N: __lws_lc_untag: -- [vh|2|default||0] (0) 125μs
|
||||
|
||||
$
|
||||
```
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* lws-minimal-raw-wol
|
||||
*
|
||||
* Written in 2010-2023 by Andy Green <andy@warmcat.com>
|
||||
*
|
||||
* This file is made available under the Creative Commons CC0 1.0
|
||||
* Universal Public Domain Dedication.
|
||||
*
|
||||
* This demonstrates using lws_wol()
|
||||
*/
|
||||
|
||||
#include <libwebsockets.h>
|
||||
#include <net/if.h>
|
||||
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
struct lws_context_creation_info info;
|
||||
struct lws_context *ctx;
|
||||
const char *p, *ip = NULL;
|
||||
uint8_t mac[IFHWADDRLEN];
|
||||
int ret = 1;
|
||||
|
||||
memset(&info, 0, sizeof info);
|
||||
lws_cmdline_option_handle_builtin(argc, argv, &info);
|
||||
|
||||
if ((p = lws_cmdline_option(argc, argv, "-ip")))
|
||||
ip = p;
|
||||
|
||||
if (argc < 2) {
|
||||
lwsl_user("lws-minimal-raw-wol XX:XX:XX:XX:XX:XX [-ip interface IP]\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
if (lws_parse_mac(argv[1], mac)) {
|
||||
lwsl_user("Failed to parse mac '%s'\n", argv[1]);
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
ctx = lws_create_context(&info);
|
||||
if (!ctx) {
|
||||
lwsl_err("lws init failed\n");
|
||||
goto bail1;
|
||||
}
|
||||
|
||||
if (!lws_wol(ctx, ip, mac))
|
||||
ret = 0;
|
||||
|
||||
lws_context_destroy(ctx);
|
||||
|
||||
bail1:
|
||||
return ret;
|
||||
}
|
Loading…
Add table
Reference in a new issue