[Go] Wake on LAN

程式語言:Go
Package:
"bytes"
"encoding/binary"
"fmt"
"log"
"net"
"regexp"
"github.com/pkg/errors"
GitHub 網址

功能:建立魔法封包

格式說明

魔術封包 包含 6 bytes 的 0xFF 與 16 次重複的 MAC 地址
由於只要資料是以上格式即可,因此可以作為任何網絡和傳輸層協議發送
通常做法為 UDP 發送到端口 0, 7, 9 之一
TCP 不太適合,因目標通常為待機狀態,無法進行 Handshake 的動作

程式碼
magicPacket.go
  1. package magicPacket
  2.  
  3. ////////////////////////////////////////////////////////////////////////////////
  4.  
  5. import (
  6. "bytes"
  7. "encoding/binary"
  8. "fmt"
  9. "log"
  10. "net"
  11. "regexp"
  12.  
  13. "github.com/pkg/errors"
  14. )
  15.  
  16. ////////////////////////////////////////////////////////////////////////////////
  17.  
  18. var (
  19. delims = ":-"
  20. reMAC = regexp.MustCompile(`^([0-9a-fA-F]{2}[` + delims + `]){5}([0-9a-fA-F]{2})$`)
  21. )
  22.  
  23. ////////////////////////////////////////////////////////////////////////////////
  24.  
  25. // MACAddress represents a 6 byte network mac address.
  26. type MACAddress [6]byte
  27.  
  28. // A MagicPacket is constituted of 6 bytes of 0xFF followed by 16-groups of the
  29. // destination MAC address.
  30. type MagicPacket struct {
  31. header [6]byte
  32. payload [16]MACAddress
  33. }
  34.  
  35. // New returns a magic packet based on a mac address string.
  36. func New(mac string) (*MagicPacket, error) {
  37. var packet MagicPacket
  38. var macAddr MACAddress
  39.  
  40. hwAddr, err := net.ParseMAC(mac)
  41. if err != nil {
  42. return nil, errors.Wrap(err, "net.ParseMAC")
  43. }
  44.  
  45. // We only support 6 byte MAC addresses since it is much harder to use the
  46. // binary.Write(...) interface when the size of the MagicPacket is dynamic.
  47. if !reMAC.MatchString(mac) {
  48. return nil, errors.Errorf("%s is not a IEEE 802 MAC-48 address", mac)
  49. }
  50.  
  51. // Copy bytes from the returned HardwareAddr -> a fixed size MACAddress.
  52. for idx := range macAddr {
  53. macAddr[idx] = hwAddr[idx]
  54. }
  55.  
  56. // Setup the header which is 6 repetitions of 0xFF.
  57. for idx := range packet.header {
  58. packet.header[idx] = 0xFF
  59. }
  60.  
  61. // Setup the payload which is 16 repetitions of the MAC addr.
  62. for idx := range packet.payload {
  63. packet.payload[idx] = macAddr
  64. }
  65.  
  66. return &packet, nil
  67. }
  68.  
  69. // Marshal serializes the magic packet structure into a 102 byte slice.
  70. func (mp *MagicPacket) marshal() ([]byte, error) {
  71. var buf bytes.Buffer
  72. if err := binary.Write(&buf, binary.BigEndian, mp); err != nil {
  73. return nil, err
  74. }
  75.  
  76. return buf.Bytes(), nil
  77. }
  78.  
  79. // Send MagicPacket by UDP
  80. func (mp *MagicPacket) SendUDP(ip string, port int) error {
  81. bcastAddr := fmt.Sprintf("%s:%d", ip, port)
  82. udpAddr, err := net.ResolveUDPAddr("udp", bcastAddr)
  83. if err != nil {
  84. return errors.Wrap(err, "net.ResolveUDPAddr")
  85. }
  86.  
  87. conn, err := net.DialUDP("udp", nil, udpAddr)
  88. if err != nil {
  89. return errors.Wrap(err, "net.DialUDP")
  90. }
  91. defer conn.Close()
  92.  
  93. log.Printf("Attempting to send a magic packet to MAC %x\n", mp.payload[0])
  94. log.Printf("... Broadcasting to: %s\n", bcastAddr)
  95.  
  96. bs, err := mp.marshal()
  97. if err != nil {
  98. return errors.Wrap(err, "mp.marshal")
  99. }
  100.  
  101. _, err = conn.Write(bs)
  102. return errors.Wrap(err, "conn.Write")
  103. }

magicPacket_test.go
  1. package magicPacket
  2.  
  3. import (
  4. "testing"
  5. )
  6.  
  7. func TestSendUDP(t *testing.T) {
  8. mp, err := New("11:22:33:44:55:66")
  9. if err != nil {
  10. t.Error(err)
  11. }
  12. err = mp.SendUDP("192.168.1.255", 9)
  13. if err != nil {
  14. t.Error(err)
  15. }
  16. }

參考

Wake-on-LAN
github.com/linde12/gowol
github.com/ghthor/gowol
github.com/sabhiram/go-wol

留言