-
Notifications
You must be signed in to change notification settings - Fork 74
/
wol.go
148 lines (126 loc) · 4.12 KB
/
wol.go
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
138
139
140
141
142
143
144
145
146
147
148
package main
// Taken from https://github.com/sabhiram/go-wol
import (
"bytes"
"encoding/binary"
"errors"
"log"
"net"
"regexp"
)
// Define globals for the MacAddress parsing
var (
delims = ":-"
reMAC = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`)
)
// MACAddress define construct for MAC Address
type MACAddress [6]byte
// A MagicPacket is constituted of 6 bytes of 0xFF followed by
// 16 groups of the destination MAC address.
type MagicPacket struct {
header [6]byte
payload [16]MACAddress
}
// NewMagicPacket function accepts a MAC Address string, and returns a pointer to
// a MagicPacket object. A Magic Packet is a broadcast frame which
// contains 6 bytes of 0xFF followed by 16 repetitions of a given mac address.
func NewMagicPacket(mac string) (*MagicPacket, error) {
var packet MagicPacket
var macAddr MACAddress
// We only support 6 byte MAC addresses since it is much harder to use
// the binary.Write(...) interface when the size of the MagicPacket is
// dynamic.
if !reMAC.MatchString(mac) {
return nil, errors.New("MAC address " + mac + " is not valid.")
}
hwAddr, err := net.ParseMAC(mac)
if err != nil {
return nil, err
}
// Copy bytes from the returned HardwareAddr -> a fixed size
// MACAddress
for idx := range macAddr {
macAddr[idx] = hwAddr[idx]
}
// Setup the header which is 6 repetitions of 0xFF
for idx := range packet.header {
packet.header[idx] = 0xFF
}
// Setup the payload which is 16 repetitions of the MAC addr
for idx := range packet.payload {
packet.payload[idx] = macAddr
}
return &packet, nil
}
// GetIPFromInterface function get IP Address of interface
func GetIPFromInterface(iface string) (*net.UDPAddr, error) {
ief, err := net.InterfaceByName(iface)
if err != nil {
return nil, err
}
addrs, err := ief.Addrs()
if err != nil {
return nil, err
} else if len(addrs) <= 0 {
return nil, errors.New("No address associated with interface " + iface)
}
// Validate that one of the addr's is a valid network IP address
for _, addr := range addrs {
switch ip := addr.(type) {
case *net.IPNet:
// Verify that the DefaultMask for the address we want to use exists
if ip.IP.DefaultMask() != nil {
return &net.UDPAddr{
IP: ip.IP,
}, nil
}
}
}
return nil, errors.New("Unable to find valid IP addr for interface " + iface)
}
// SendMagicPacket to send a magic packet to a given mac address, and optionally
// receives an iface to broadcast on. An iface of "" implies a nil net.UDPAddr
func SendMagicPacket(macAddr, bcastAddr, iface string) error {
// Construct a MagicPacket for the given MAC Address
magicPacket, err := NewMagicPacket(macAddr)
if err != nil {
return err
}
// Fill our byte buffer with the bytes in our MagicPacket
var buf bytes.Buffer
binary.Write(&buf, binary.BigEndian, magicPacket)
log.Printf("Attempting to send a magic packet to MAC %s\n", macAddr)
log.Printf("... Broadcasting to: %s\n", bcastAddr)
// Get a UDPAddr to send the broadcast to
udpAddr, err := net.ResolveUDPAddr("udp", bcastAddr)
if err != nil {
log.Printf("Unable to get a UDP address for %s\n", bcastAddr)
return err
}
// If an interface was specified, get the address associated with it
var localAddr *net.UDPAddr
if iface != "" {
var err error
localAddr, err = GetIPFromInterface(iface)
if err != nil {
log.Printf("ERROR: %s\n", err.Error())
return errors.New("Unable to get address for interface " + iface)
}
}
// Open a UDP connection, and defer it's cleanup
connection, err := net.DialUDP("udp", localAddr, udpAddr)
if err != nil {
log.Printf("ERROR: %s\n", err.Error())
return errors.New("unable to dial UDP address")
}
defer connection.Close()
// Write the bytes of the MagicPacket to the connection
bytesWritten, err := connection.Write(buf.Bytes())
if err != nil {
log.Printf("Unable to write packet to connection\n")
return err
} else if bytesWritten != 102 {
log.Printf("Warning: %d bytes written, %d expected!\n", bytesWritten, 102)
}
return nil
}