Skip to content

Commit

Permalink
Allow specifying ipv6 addresses (#26)
Browse files Browse the repository at this point in the history
  • Loading branch information
alimg authored Nov 17, 2023
1 parent aa98922 commit 1a006fc
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 4 deletions.
142 changes: 141 additions & 1 deletion test_wakeonlan.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,131 @@ def test_send_magic_packet_interface(sock: Mock) -> None:
]


@patch("socket.socket")
def test_send_correct_af_chosen_with_ipv6_address(sock: Mock) -> None:
"""
Test whether AF_INET6 automatically chosen when the `address_family` argument is not given.
"""
send_magic_packet(
"133713371337",
"00-00-00-00-00-00",
ip_address="fc00::",
port=7,
)
assert sock.mock_calls == [
call(socket.AF_INET6, socket.SOCK_DGRAM),
call().__enter__(),
call().__enter__().setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1),
call().__enter__().connect(("fc00::", 7)),
call()
.__enter__()
.send(
b"\xff\xff\xff\xff\xff\xff"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
),
call()
.__enter__()
.send(
b"\xff\xff\xff\xff\xff\xff"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
),
call().__exit__(None, None, None),
]


@patch("socket.socket")
def test_send_with_explicit_ipv6_address(sock: Mock) -> None:
"""
Test whether the given address family is used instead automatically it automatically.
"""
send_magic_packet(
"133713371337",
"00-00-00-00-00-00",
ip_address="example.com",
port=7,
address_family=socket.AF_INET6,
)
assert sock.mock_calls == [
call(socket.AF_INET6, socket.SOCK_DGRAM),
call().__enter__(),
call().__enter__().setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1),
call().__enter__().connect(("example.com", 7)),
call()
.__enter__()
.send(
b"\xff\xff\xff\xff\xff\xff"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
b"\x137\x137\x137"
),
call()
.__enter__()
.send(
b"\xff\xff\xff\xff\xff\xff"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
b"\x00\x00\x00\x00\x00\x00"
),
call().__exit__(None, None, None),
]


@patch("wakeonlan.send_magic_packet")
def test_main(send_magic_packet: Mock) -> None:
"""
Expand All @@ -301,12 +426,27 @@ def test_main(send_magic_packet: Mock) -> None:
"""
main(["00:11:22:33:44:55", "-i", "host.example", "-p", "1337"])
main(["00:11:22:33:44:55", "-i", "host.example", "-p", "1337", "-n", "192.168.0.2"])
main(["00:11:22:33:44:55", "-i", "host.example", "-p", "1337", "-6"])
assert send_magic_packet.mock_calls == [
call("00:11:22:33:44:55", ip_address="host.example", port=1337, interface=None),
call(
"00:11:22:33:44:55",
ip_address="host.example",
port=1337,
interface=None,
address_family=None,
),
call(
"00:11:22:33:44:55",
ip_address="host.example",
port=1337,
interface="192.168.0.2",
address_family=None,
),
call(
"00:11:22:33:44:55",
ip_address="host.example",
port=1337,
interface=None,
address_family=socket.AF_INET6,
),
]
34 changes: 31 additions & 3 deletions wakeonlan/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""
import argparse
import ipaddress
import socket
from typing import List
from typing import Optional
Expand Down Expand Up @@ -40,7 +41,8 @@ def send_magic_packet(
*macs: str,
ip_address: str = BROADCAST_IP,
port: int = DEFAULT_PORT,
interface: Optional[str] = None
interface: Optional[str] = None,
address_family: Optional[socket.AddressFamily] = None
) -> None:
"""
Wake up computers having any of the given mac addresses.
Expand All @@ -54,11 +56,18 @@ def send_magic_packet(
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]

with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
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)
Expand All @@ -67,6 +76,13 @@ def send_magic_packet(
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.
Expand All @@ -82,6 +98,12 @@ def main(argv: Optional[List[str]] = None) -> None:
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",
Expand All @@ -102,7 +124,13 @@ def main(argv: Optional[List[str]] = None) -> 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)
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
Expand Down

0 comments on commit 1a006fc

Please sign in to comment.