blob: 1ec77773c114abad0901c6e836b103c60d32e36e [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package wol
import (
"errors"
"fmt"
"net"
"regexp"
)
var (
macAddrRegex = regexp.MustCompile(`(?i)^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$`)
// Magic Packet header is 0xFF repeated 6 times.
magicPacketHeader = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
)
const (
magicPacketLength = 102
)
// Reboot sends a WakeOnLAN magic packet {magicPacketHeader + macAddr x 16}
// using the specified network interface to the broadcast address
func Reboot(broadcastAddr, interfaceName, macAddr string) error {
if !macAddrRegex.Match([]byte(macAddr)) {
return fmt.Errorf("Invalid MAC: %s", macAddr)
}
remoteHwAddr, err := net.ParseMAC(macAddr)
if err != nil {
return err
}
localAddr, err := getUDPAddrFromIFace(interfaceName)
if err != nil {
return err
}
remoteAddr, err := net.ResolveUDPAddr("udp", broadcastAddr)
if err != nil {
return err
}
return sendMagicPacket(localAddr, remoteAddr, remoteHwAddr)
}
func getUDPAddrFromIFace(ifaceName string) (*net.UDPAddr, error) {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return nil, err
}
addrs, err := iface.Addrs()
if err != nil {
return nil, err
}
for _, addr := range addrs {
if ipAddr, ok := addr.(*net.IPNet); ok {
// Need an IPv4, non-loopback address to send on
if !ipAddr.IP.IsLoopback() && ipAddr.IP.To4() != nil {
return &net.UDPAddr{
IP: ipAddr.IP,
}, nil
}
}
}
return nil, errors.New("No UDPAddr found on interface")
}
func sendMagicPacket(localAddr, remoteAddr *net.UDPAddr, remoteHwAddr net.HardwareAddr) error {
packet := magicPacketHeader
for i := 0; i < 16; i++ {
packet = append(packet, remoteHwAddr...)
}
if len(packet) != magicPacketLength {
return fmt.Errorf("Wake-On-LAN packet incorrect length: %d", len(packet))
}
conn, err := net.DialUDP("udp", localAddr, remoteAddr)
if err != nil {
return err
}
defer conn.Close()
n, err := conn.Write(packet)
if n != magicPacketLength {
return errors.New("Failed to send correct Wake-On-LAN packet length")
}
return err
}