๐Ÿ“ฆ dsymonds / plugmon

๐Ÿ“„ ping.go ยท 107 lines
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
107package main

import (
	"bytes"
	"fmt"
	"log"
	"net"
	"strings"
)

func main() {
	conn, err := net.ListenUDP("udp4", &net.UDPAddr{})
	if err != nil {
		log.Fatalf("net.ListenUDP: %v", err)
	}
	laddr := conn.LocalAddr().(*net.UDPAddr)
	msg := []byte{
		// These first 32 bytes will be echoed back.
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x0a, 0x00, 0x00, 0x00, 0xe1, 0x07, 0x05, 0x0c,
		0x01, 0x06, 0x1d, 0x04, 0x00, 0x00, 0x00, 0x00,
		laddr.IP[0], laddr.IP[1], laddr.IP[2], laddr.IP[3],
		byte(laddr.Port & 0xff), byte(laddr.Port >> 8),
		0x00, 0x00,

		// dunno what the rest is for
		0x6c, 0xc1, 0x00, 0x00, 0x00, 0x00, // 6c -> 108 (dec)
		0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	}
	dst := &net.UDPAddr{
		IP:   net.IPv4(255, 255, 255, 255),
		Port: 80,
	}
	log.Printf("sending %d byte message: %x", len(msg), msg)
	if _, err := conn.WriteToUDP(msg, dst); err != nil {
		log.Fatalf("conn.WriteToUDP: %v", err)
	}
	var scratch [1 << 10]byte
	n, raddr, err := conn.ReadFrom(scratch[:])
	if err != nil {
		log.Fatalf("conn.ReadFrom: %v", err)
	}
	b := scratch[:n]
	log.Printf("got back %d bytes from %s: %x", n, raddr, b)

	d := decode(msg, b)
	log.Printf("* %q (MAC %s, IP %s, ID %q)", d.name, d.mac, d.ip, d.id)
	log.Printf("  unknown1: %x", d.unknown1)
}

func decode(req, b []byte) *data {
	if len(b) != 128 {
		log.Fatalf("bad response length %d (want 128)", len(b))
	}
	if !bytes.Equal(req[:32], b[:32]) {
		log.Fatalf("first 32 bytes of response isn't an echo\n req[:32] = %x\nresp[:32] = %x", req[:32], b[:32])
	}

	// next returns the next n bytes from b.
	next := func(n int) []byte {
		x := b[:n]
		b = b[n:]
		return x
	}
	rev := func(b []byte) []byte {
		for i, j := 0, len(b)-1; i < len(b)/2; i, j = i+1, j-1 {
			b[i], b[j] = b[j], b[i]
		}
		return b
	}

	d := new(data)

	// Drop the first 32 bytes. They echo the request.
	next(32)

	d.unknown1 = next(20)

	// Next 2 are the device ID.
	switch x := fmt.Sprintf("0x%X", next(2)); x {
	case "0x1627":
		d.id = "Neo Power/INPLUG"
	default:
		d.id = x
	}

	// Next 4 are the reversed IP of the switch.
	d.ip = rev(next(4))

	// Next 6 are the reversed MAC.
	d.mac = rev(next(6))

	// The remainder is the switch's name,
	// padded with zero bytes.
	d.name = strings.TrimRight(string(b), "\x00")

	return d
}

type data struct {
	unknown1 []byte
	id       string
	ip       net.IP
	mac      net.HardwareAddr
	name     string
}