-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
142 lines (121 loc) · 3.66 KB
/
main.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
package main
import (
"flag"
"fmt"
"log"
"net"
"net/http"
)
// MagicPacket computes the magic packet to broadcast to activate the WakeOnLan
// device. See https://www.amd.com/system/files/TechDocs/20213.pdf
// address is expected to be a MAC address composed of 6 bytes, MagicPacket
// returns nil if address isn't valid.
func MagicPacket(address net.HardwareAddr) []byte {
if len(address) != 6 {
return nil
}
payload := make([]byte, 102)
// add prefix
i := 0
for ; i < 6; i++ {
payload[i] = 255
}
// repeat 16 times
for c := 0; c < 16; c++ {
for m := 0; m < 6; m++ {
payload[i] = address[m]
i++
}
}
return payload
}
// ParseArguments checks the arguments of the program and creates an open TCP server,
// the parsed MAC address of the target and an open UDP connection. The two server
// must be closed by the caller after use. target must be a valid 6 bytes MAC address,
// addr must be a valid IP address / TCP port combo to listen to, broadcast must be a
// valid IP address / UDP port to write to. It is not checked whether broadcast actually
// correspond to the broadcast address of its network.
func ParseArguments(target, addr, broadcast string) (net.Listener, net.HardwareAddr, net.Conn, error) {
// target -> no default value
if len(target) == 0 {
return nil, nil, nil, fmt.Errorf("the MAC address of the target is required")
}
targetAddr, err := net.ParseMAC(target)
if err != nil {
return nil, nil, nil, err
}
if len(targetAddr) != 6 {
return nil, nil, nil, fmt.Errorf("unsupported MAC address: %v", targetAddr)
}
// TCP server binding
listener, err := net.Listen("tcp", addr)
if err != nil {
return nil, nil, nil, err
}
// UDP destination (broadcast)
dest, err := net.Dial("udp", broadcast)
if err != nil {
return nil, nil, nil, err
}
return listener, targetAddr, dest, nil
}
func main() {
target := flag.String("target", "", "6 bytes MAC address of the target")
addr := flag.String("address", ":0", "server binding address (with port)")
broadcast := flag.String("broadcast", "192.168.0.255:9", "UDP address to send the datagram to (with port)")
verbose := flag.Bool("verbose", false, "gives more network information at start-up")
killable := flag.Bool("killable", true, "allow the route /kill to work")
flag.Parse()
listener, targetAddr, dest, err := ParseArguments(*target, *addr, *broadcast)
payload := MagicPacket(targetAddr)
if err != nil {
log.Fatal(err)
}
defer listener.Close()
defer dest.Close()
if *verbose {
fmt.Println("waker is running, killable:", *killable)
fmt.Println("\tTCP socket", listener.Addr().String())
fmt.Println("\tMAC target", targetAddr.String())
fmt.Println("\tUDP target", dest.RemoteAddr().String())
}
mux := http.NewServeMux()
mux.HandleFunc("/wake", func(w http.ResponseWriter, _ *http.Request) {
_, err := dest.Write(payload)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
_, err := fmt.Fprintln(w, "500 - Error while sending magic packet (see logs)")
if err != nil {
log.Println(err)
}
} else {
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprintln(w, "200 - Magic packet sent")
if err != nil {
log.Println(err)
}
}
})
done := make(chan struct{})
mux.HandleFunc("/kill", func(w http.ResponseWriter, _ *http.Request) {
if *killable {
w.WriteHeader(http.StatusOK)
_, err := fmt.Fprintln(w, "200 - shutting down")
if err != nil {
log.Println(err)
}
close(done)
} else {
w.WriteHeader(http.StatusBadRequest)
_, err := fmt.Fprintln(w, "400 - server cannot be killed remotely")
if err != nil {
log.Println(err)
}
}
})
go func() {
log.Fatal(http.Serve(listener, mux))
}()
<-done
}