-
Notifications
You must be signed in to change notification settings - Fork 68
/
Copy pathmagic_packet.go
149 lines (123 loc) · 3.85 KB
/
magic_packet.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
149
package wol
////////////////////////////////////////////////////////////////////////////////
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"regexp"
)
////////////////////////////////////////////////////////////////////////////////
var (
delims = ":-"
re_MAC = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`)
)
////////////////////////////////////////////////////////////////////////////////
// MACAddress represents a 6 byte network 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
}
// New returns a magic packet based on a mac address string.
func New(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 !re_MAC.MatchString(mac) {
return nil, fmt.Errorf("invalid mac-address %s", mac)
}
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 returns a `*net.UDPAddr` from a network interface name.
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, fmt.Errorf("no address associated with interface %s", 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, fmt.Errorf("no address associated with interface %s", iface)
}
// SendMagicPacket sends a magic packet UDP broadcast to the specified `macAddr`.
// The broadcast is sent to `bcastAddr` via the `iface`. An empty `iface` implies
// a nil local address to dial.
func SendMagicPacket(macAddr, bcastAddr, iface string) error {
// Construct a MagicPacket for the given MAC Address
magicPacket, err := New(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)
fmt.Printf("Attempting to send a magic packet to MAC %s\n", macAddr)
fmt.Printf("... Broadcasting to: %s\n", bcastAddr)
// Get a UDPAddr to send the broadcast to
udpAddr, err := net.ResolveUDPAddr("udp", bcastAddr)
if err != nil {
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 {
return err
}
}
// Open a UDP connection, and defer it's cleanup
connection, err := net.DialUDP("udp", localAddr, udpAddr)
if err != nil {
return err
}
defer connection.Close()
// Write the bytes of the MagicPacket to the connection
bytesWritten, err := connection.Write(buf.Bytes())
if err != nil {
return err
} else if bytesWritten != 102 {
fmt.Printf("Warning: %d bytes written, %d expected!\n", bytesWritten, 102)
}
return nil
}
////////////////////////////////////////////////////////////////////////////////