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 134 135 136 137
|
#!/usr/bin/env python3
"""
Small module for use with the wake on lan protocol.
"""
import argparse
import ipaddress
import socket
from typing import List
from typing import Optional
BROADCAST_IP = "255.255.255.255"
DEFAULT_PORT = 9
def create_magic_packet(macaddress: str) -> bytes:
"""
Create a magic packet.
A magic packet is a packet that can be used with the for wake on lan
protocol to wake up a computer. The packet is constructed from the
mac address given as a parameter.
Args:
macaddress: the mac address that should be parsed into a magic packet.
"""
if len(macaddress) == 17:
sep = macaddress[2]
macaddress = macaddress.replace(sep, "")
elif len(macaddress) == 14:
sep = macaddress[4]
macaddress = macaddress.replace(sep, "")
if len(macaddress) != 12:
raise ValueError("Incorrect MAC address format")
return bytes.fromhex("F" * 12 + macaddress * 16)
def send_magic_packet(
*macs: str,
ip_address: str = BROADCAST_IP,
port: int = DEFAULT_PORT,
interface: Optional[str] = None,
address_family: Optional[socket.AddressFamily] = None
) -> None:
"""
Wake up computers having any of the given mac addresses.
Wake on lan must be enabled on the host device.
Args:
macs: One or more macaddresses of machines to wake.
Keyword Args:
ip_address: the ip address of the host to send the magic packet to.
port: the port of the host to send the magic packet to.
interface: the ip address of the network adapter to route the magic packet through.
address_family: the address family of the ip address to initiate connection with.
When not specificied, chosen automatically between IPv4 and IPv6.
"""
packets = [create_magic_packet(mac) for mac in macs]
if address_family is None:
address_family = (
socket.AF_INET6 if _is_ipv6_address(ip_address) else socket.AF_INET
)
with socket.socket(address_family, socket.SOCK_DGRAM) as sock:
if interface is not None:
sock.bind((interface, 0))
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.connect((ip_address, port))
for packet in packets:
sock.send(packet)
def _is_ipv6_address(ip_address: str) -> bool:
try:
return isinstance(ipaddress.ip_address(ip_address), ipaddress.IPv6Address)
except ValueError:
return False
def main(argv: Optional[List[str]] = None) -> None:
"""
Run wake on lan as a CLI application.
"""
parser = argparse.ArgumentParser(
description="Wake one or more computers using the wake on lan protocol.",
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"macs",
metavar="mac address",
nargs="+",
help="The mac addresses of the computers you are trying to wake.",
)
parser.add_argument(
"-6",
dest="use_ipv6",
action="store_true",
help="To indicate if ipv6 should be used by default instead of ipv4.",
)
parser.add_argument(
"-i",
metavar="ip",
default=BROADCAST_IP,
help="The ip address of the host to send the magic packet to.",
)
parser.add_argument(
"-p",
metavar="port",
type=int,
default=DEFAULT_PORT,
help="The port of the host to send the magic packet to.",
)
parser.add_argument(
"-n",
metavar="interface",
default=None,
help="The ip address of the network adapter to route the magic packet through.",
)
args = parser.parse_args(argv)
send_magic_packet(
*args.macs,
ip_address=args.i,
port=args.p,
interface=args.n,
address_family=socket.AF_INET6 if args.use_ipv6 else None
)
if __name__ == "__main__": # pragma: nocover
main()
|