summaryrefslogtreecommitdiff
path: root/utils/net.go
blob: c69d90f137634f5b4be4fe1409b7a4be912596b2 (plain) (blame)
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
package utils

import (
	"fmt"
	"strings"
)

// Mac is a 48-bit hardware address.
type Mac struct {
	Addr [6]byte
}

// Format renders the canonical lowercase, zero-padded form aa:bb:cc:dd:ee:ff.
func (m *Mac) Format() string {
	return fmt.Sprintf("%02x:%02x:%02x:%02x:%02x:%02x",
		m.Addr[0], m.Addr[1], m.Addr[2], m.Addr[3], m.Addr[4], m.Addr[5])
}

// Compact renders the MAC as 12 lowercase hex digits with no separators, the
// form used as a DNS label (e.g. aabbccddeeff).
func (m *Mac) Compact() string {
	return fmt.Sprintf("%02x%02x%02x%02x%02x%02x",
		m.Addr[0], m.Addr[1], m.Addr[2], m.Addr[3], m.Addr[4], m.Addr[5])
}

// ParseMac reads a colon-separated hex MAC. It tolerates uppercase and the
// zero-stripped octets macOS prints (e.g. "8:0:27:ab:cd:ef").
func ParseMac(s string) (*Mac, error) {
	mac := &Mac{}
	t := strings.TrimSpace(s)
	n, err := fmt.Sscanf(t, "%x:%x:%x:%x:%x:%x",
		&mac.Addr[0], &mac.Addr[1], &mac.Addr[2], &mac.Addr[3], &mac.Addr[4], &mac.Addr[5])
	if err != nil {
		return nil, err
	}
	if n != 6 {
		return nil, fmt.Errorf("invalid mac %q: parsed %d of 6 octets", s, n)
	}
	return mac, nil
}

// ParseMacCompact reads the 12-hex-digit, separator-less form used as a DNS
// label (e.g. aabbccddeeff) back into a Mac.
func ParseMacCompact(s string) (*Mac, error) {
	mac := &Mac{}
	t := strings.TrimSpace(s)
	if len(t) != 12 {
		return nil, fmt.Errorf("invalid compact mac %q: want 12 hex digits, got %d", s, len(t))
	}
	n, err := fmt.Sscanf(t, "%2x%2x%2x%2x%2x%2x",
		&mac.Addr[0], &mac.Addr[1], &mac.Addr[2], &mac.Addr[3], &mac.Addr[4], &mac.Addr[5])
	if err != nil {
		return nil, err
	}
	if n != 6 {
		return nil, fmt.Errorf("invalid compact mac %q: parsed %d of 6 octets", s, n)
	}
	return mac, nil
}

// INet4 is an IPv4 address.
type INet4 struct {
	Addr [4]byte
}

// Format renders the dotted-decimal form a.b.c.d.
func (n *INet4) Format() string {
	return fmt.Sprintf("%d.%d.%d.%d", n.Addr[0], n.Addr[1], n.Addr[2], n.Addr[3])
}

// ParseINet4 reads a dotted-decimal IPv4 address. Out-of-range octets (>255)
// overflow the byte and are rejected.
func ParseINet4(s string) (*INet4, error) {
	addr := &INet4{}
	t := strings.TrimSpace(s)
	n, err := fmt.Sscanf(t, "%d.%d.%d.%d",
		&addr.Addr[0], &addr.Addr[1], &addr.Addr[2], &addr.Addr[3])
	if err != nil {
		return nil, err
	}
	if n != 4 {
		return nil, fmt.Errorf("invalid ipv4 %q: parsed %d of 4 octets", s, n)
	}
	return addr, nil
}