From e39d626119d9cf1df425b9709515c1cd2db0ed9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxi=20Quo=C3=9F?= Date: Thu, 26 Jan 2023 18:27:49 +0100 Subject: [PATCH] use wol package and support wol passwords --- README.md | 4 +- backend/go.mod | 5 + backend/go.sum | 12 ++ backend/networking/magicpacket.go | 83 +++++++++----- backend/networking/wake.go | 2 +- frontend/src/routes/settings/+page.svelte | 129 ++++++++++++---------- 6 files changed, 146 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index 28215ba3..2101d857 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ Open up [http://localhost:5173/](http://localhost:5173/) - [x] ~~make sure ping works~~ - [ ] make sure arp works - - [ ] make sure wake works + - [x] make sure wake works - [ ] remove nmap? - [ ] add shutdown command - [ ] add scheduled wake - - [ ] [#34 Add support for WOL passwords](https://github.com/seriousm4x/UpSnap/issues/34) + - [x] [#34 Add support for WOL passwords](https://github.com/seriousm4x/UpSnap/issues/34) - [x] [~~#33 Seemingly High Ram usage~~](https://github.com/seriousm4x/UpSnap/issues/33) - [x] [~~#32 API available?~~](https://github.com/seriousm4x/UpSnap/issues/32) diff --git a/backend/go.mod b/backend/go.mod index 6e9b43d4..47e5955f 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/go-ping/ping v1.1.0 github.com/labstack/echo/v5 v5.0.0-20220201181537-ed2888cfa198 + github.com/mdlayher/wol v0.0.0-20220221231636-b763a792253a github.com/pocketbase/dbx v1.8.0 github.com/pocketbase/pocketbase v0.11.2 github.com/robfig/cron/v3 v3.0.1 @@ -48,10 +49,14 @@ require ( github.com/googleapis/gax-go/v2 v2.7.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/native v1.0.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-sqlite3 v1.14.16 // indirect + github.com/mdlayher/ethernet v0.0.0-20190313224307-5b5fc417d966 // indirect + github.com/mdlayher/packet v1.0.0 // indirect + github.com/mdlayher/socket v0.2.1 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/remyoudompheng/bigfft v0.0.0-20220927061507-ef77025ab5aa // indirect github.com/spf13/cast v1.5.0 // indirect diff --git a/backend/go.sum b/backend/go.sum index c563e376..6fc33cb6 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1293,6 +1293,8 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= +github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1397,6 +1399,15 @@ github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= +github.com/mdlayher/ethernet v0.0.0-20190313224307-5b5fc417d966 h1:O3p5UmisBhl3V6lgs4Vdfg8HpjzbWJPyOfGLdwVJSmI= +github.com/mdlayher/ethernet v0.0.0-20190313224307-5b5fc417d966/go.mod h1:5s5p/sMJ6sNsFl6uCh85lkFGV8kLuIYJCRJLavVJwvg= +github.com/mdlayher/packet v1.0.0 h1:InhZJbdShQYt6XV2GPj5XHxChzOfhJJOMbvnGAmOfQ8= +github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU= +github.com/mdlayher/raw v0.0.0-20190313224157-43dbcdd7739d/go.mod h1:r1fbeITl2xL/zLbVnNHFyOzQJTgr/3fpf1lJX/cjzR8= +github.com/mdlayher/socket v0.2.1 h1:F2aaOwb53VsBE+ebRS9bLd7yPOfYUMC8lOODdCBDY6w= +github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= +github.com/mdlayher/wol v0.0.0-20220221231636-b763a792253a h1:fpQi42PM6HzbHwNg6a7OvLbh5l49Rgr7GCARozjHwio= +github.com/mdlayher/wol v0.0.0-20220221231636-b763a792253a/go.mod h1:XX6nvyQpGc8E4KaQm62FyexOYd36ilpyfYf8wjgMDV8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -1980,6 +1991,7 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= diff --git a/backend/networking/magicpacket.go b/backend/networking/magicpacket.go index 7c0fccd7..dee481f3 100644 --- a/backend/networking/magicpacket.go +++ b/backend/networking/magicpacket.go @@ -1,45 +1,74 @@ package networking import ( - "bytes" - "encoding/hex" + "encoding/binary" + "errors" "fmt" "net" - "strings" + + "github.com/mdlayher/wol" + "github.com/pocketbase/pocketbase/models" ) -func SendMagicPacket(mac, netmask string) error { - macAddr, err := hex.DecodeString(strings.ReplaceAll(mac, ":", "")) +func SendMagicPacket(device *models.Record) error { + ip := device.GetString("ip") + mac := device.GetString("mac") + netmask := device.GetString("netmask") + password := device.GetString("password") + + // parse inputs + parsedIP := net.ParseIP(ip) + if parsedIP == nil { + return fmt.Errorf("error: invalid ip address") + } + parsedMac, err := net.ParseMAC(mac) if err != nil { - return fmt.Errorf("error decoding mac address: %v", err) + return err + } + parsedNetmask := net.ParseIP(netmask) + if parsedNetmask == nil { + return fmt.Errorf("error: invalid netmask") + } + var bytePassword []byte + if len(password) == 0 || len(password) == 4 || len(password) == 6 { + bytePassword = []byte(password) + } else { + return fmt.Errorf("error: password must be 0, 4 or 6 characters long") } - // Create an address to listen on - address := net.UDPAddr{ - IP: net.ParseIP(netmask), - Port: 7, + // get target addr + ipNet := &net.IPNet{ + IP: parsedIP, + Mask: parsedNetmask.DefaultMask(), } - - // create a new UDP packet conn - conn, err := net.DialUDP("udp", nil, &address) + broadcastIp, err := getBroadcastIp(ipNet) if err != nil { - return fmt.Errorf("error creating UDP packet conn: %v", err) + return err } - defer conn.Close() + targetAddr := fmt.Sprintf("%s:%d", broadcastIp, 9) - // create a magic packet - var magicPacket bytes.Buffer - for i := 0; i < 6; i++ { - magicPacket.WriteByte(0xff) - } - for i := 0; i < 16; i++ { - magicPacket.Write(macAddr) + // send wake via udp port 9 + if err := wakeUDP(targetAddr, parsedMac, bytePassword); err != nil { + return err } - // send the magic packet - _, err = conn.Write(magicPacket.Bytes()) - if err != nil { - return fmt.Errorf("error sending magic packet: %v", err) - } return nil } + +func wakeUDP(addr string, target net.HardwareAddr, password []byte) error { + c, err := wol.NewClient() + if err != nil { + return err + } + defer c.Close() + return c.WakePassword(addr, target, password) +} + +func getBroadcastIp(n *net.IPNet) (net.IP, error) { + if n.IP.To4() == nil { + return net.IP{}, errors.New("does not support IPv6 addresses") + } + ip := make(net.IP, len(n.IP.To4())) + binary.BigEndian.PutUint32(ip, binary.BigEndian.Uint32(n.IP.To4())|^binary.BigEndian.Uint32(net.IP(n.Mask).To4())) + return ip, nil +} diff --git a/backend/networking/wake.go b/backend/networking/wake.go index d9c4e29e..5c8e78ac 100644 --- a/backend/networking/wake.go +++ b/backend/networking/wake.go @@ -8,7 +8,7 @@ import ( ) func WakeDevice(device *models.Record) bool { - err := SendMagicPacket(device.GetString("mac"), device.GetString("netmask")) + err := SendMagicPacket(device) if err != nil { logger.Error.Println(err) return false diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index 4bf5ef62..ad87cdc8 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -48,6 +48,13 @@ async function addDevice() { buttons.add_device.state = 'waiting'; try { + if ( + newDevice.password.length != 0 && + newDevice.password.length != 4 && + newDevice.password.length != 6 + ) { + throw 'Password must be 0, 4 or 6 characters long'; + } await pb.collection('devices').create(newDevice, { $autoCancel: false }); setTimeout(() => { buttons.add_device.state = 'none'; @@ -274,6 +281,7 @@ aria-describedby="addon-wrapping" type={passwordType} value={newDevice.password} + maxlength="6" on:input={onPasswordInput} /> + +

Restore