/
hardwareaddr.go
84 lines (70 loc) · 2.61 KB
/
hardwareaddr.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
// Copyright 2018 Dan Jacques. All rights reserved.
// Use of this source code is governed under the MIT License
// that can be found in the LICENSE file.
package proxy
import (
"bytes"
"crypto/sha256"
"net"
"github.com/danjacques/gopushpixels/support/fmtutil"
"github.com/pkg/errors"
)
// hardwareAddrSize is the number of bytes in a generated hardware address.
const hardwareAddrSize = 6
// AddressRegistry generates a series of hardware addresses, ensuring that
// each successive address is unique within its Prefix space.
//
// AddressRegistry is not safe for concurrent use.
type AddressRegistry struct {
// Prefix is the sequence of bytes to prefix to a generated address.
//
// If empty, no consistent prefix will be applied.
Prefix []byte
// used tracks the generated hardware addresses and the seeds that generated
// them. This (poorly) addresses the possibility that two seeds generate the
// same address.
used map[string]string
}
// Generate generates an address for the specified device ID.
//
// Addresses are generated by prepending a fixded Prefix to a hash generated
// from the ID.
func (reg *AddressRegistry) Generate(id string) (net.HardwareAddr, error) {
genBytes := hardwareAddrSize - len(reg.Prefix)
if genBytes <= 0 {
return nil, errors.Errorf("prefix (%s) is too large", fmtutil.HexSlice(reg.Prefix))
}
hash := sha256.Sum256([]byte(id))
if len(hash) < genBytes {
panic("violated assumption: hash does not provide enough bytes")
}
// Generate an address from the hash.
addr := append(append(make(net.HardwareAddr, 0, hardwareAddrSize), reg.Prefix...), hash[:genBytes]...)
// Ensure that this address is not used by a different ID.
//
// It's possible that a device will unregister and re-register, so we need to
// allow a duplicate address if its ID matches.
key := string(addr)
existingSeed, ok := reg.used[key]
if ok && existingSeed != id {
return nil, errors.Errorf("address for ID %s collides with ID %s: %v",
fmtutil.HexSlice(existingSeed), fmtutil.HexSlice(id), key)
}
// Register the generated address with its source ID.
if !ok {
// This is a unique address, and is the first time we've seen this ID.
if reg.used == nil {
reg.used = make(map[string]string)
}
reg.used[key] = id
}
return addr, nil
}
// IsProxyDeviceAddr examines a hardware address and returns true if it begins
// with the prefix used by our generated proxy devices.
//
// If the prefix is nil, all devices will be considered proxy devices, which
// is probably not what you want.
func (reg *AddressRegistry) IsProxyDeviceAddr(addr net.HardwareAddr) bool {
return bytes.HasPrefix(addr, reg.Prefix)
}