mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-06 21:01:56 +00:00
types/ipproto: import and test string parsing for ipproto
IPProto has been being converted to and from string formats in multiple locations with variations in behavior. TextMarshaller and JSONMarshaller implementations are now added, along with defined accepted and preferred formats to centralize the logic into a single cross compatible implementation. Updates tailscale/corp#15043 Fixes tailscale/corp#15141 Signed-off-by: James Tucker <james@tailscale.com>
This commit is contained in:
committed by
James Tucker
parent
319607625f
commit
c1ef55249a
@@ -4,7 +4,13 @@
|
||||
// Package ipproto contains IP Protocol constants.
|
||||
package ipproto
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"tailscale.com/util/nocasemaps"
|
||||
"tailscale.com/util/vizerror"
|
||||
)
|
||||
|
||||
// Version describes the IP address version.
|
||||
type Version uint8
|
||||
@@ -69,6 +75,7 @@ const (
|
||||
Fragment Proto = 0xFF
|
||||
)
|
||||
|
||||
// Deprecated: use MarshalText instead.
|
||||
func (p Proto) String() string {
|
||||
switch p {
|
||||
case Unknown:
|
||||
@@ -97,3 +104,96 @@ func (p Proto) String() string {
|
||||
return fmt.Sprintf("IPProto-%d", int(p))
|
||||
}
|
||||
}
|
||||
|
||||
// Prefer names from
|
||||
// https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
|
||||
// unless otherwise noted.
|
||||
var (
|
||||
// preferredNames is the set of protocol names that re produced by
|
||||
// MarshalText, and are the preferred representation.
|
||||
preferredNames = map[Proto]string{
|
||||
51: "ah",
|
||||
DCCP: "dccp",
|
||||
8: "egp",
|
||||
50: "esp",
|
||||
47: "gre",
|
||||
ICMPv4: "icmp",
|
||||
IGMP: "igmp",
|
||||
9: "igp",
|
||||
4: "ipv4",
|
||||
ICMPv6: "ipv6-icmp",
|
||||
SCTP: "sctp",
|
||||
TCP: "tcp",
|
||||
UDP: "udp",
|
||||
}
|
||||
|
||||
// acceptedNames is the set of protocol names that are accepted by
|
||||
// UnmarshalText.
|
||||
acceptedNames = map[string]Proto{
|
||||
"ah": 51,
|
||||
"dccp": DCCP,
|
||||
"egp": 8,
|
||||
"esp": 50,
|
||||
"gre": 47,
|
||||
"icmp": ICMPv4,
|
||||
"icmpv4": ICMPv4,
|
||||
"icmpv6": ICMPv6,
|
||||
"igmp": IGMP,
|
||||
"igp": 9,
|
||||
"ip-in-ip": 4, // IANA says "ipv4"; Wikipedia/popular use says "ip-in-ip"
|
||||
"ipv4": 4,
|
||||
"ipv6-icmp": ICMPv6,
|
||||
"sctp": SCTP,
|
||||
"tcp": TCP,
|
||||
"tsmp": TSMP,
|
||||
"udp": UDP,
|
||||
}
|
||||
)
|
||||
|
||||
// UnmarshalText implements encoding.TextUnmarshaler. If the input is empty, p
|
||||
// is set to 0. If an error occurs, p is unchanged.
|
||||
func (p *Proto) UnmarshalText(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
*p = 0
|
||||
return nil
|
||||
}
|
||||
|
||||
if u, err := strconv.ParseUint(string(b), 10, 8); err == nil {
|
||||
*p = Proto(u)
|
||||
return nil
|
||||
}
|
||||
|
||||
if newP, ok := nocasemaps.GetOk(acceptedNames, string(b)); ok {
|
||||
*p = newP
|
||||
return nil
|
||||
}
|
||||
|
||||
return vizerror.Errorf("proto name %q not known; use protocol number 0-255", b)
|
||||
}
|
||||
|
||||
// MarshalText implements encoding.TextMarshaler.
|
||||
func (p Proto) MarshalText() ([]byte, error) {
|
||||
if s, ok := preferredNames[p]; ok {
|
||||
return []byte(s), nil
|
||||
}
|
||||
return []byte(strconv.Itoa(int(p))), nil
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (p Proto) MarshalJSON() ([]byte, error) {
|
||||
return []byte(strconv.Itoa(int(p))), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler. If the input is empty, p is set to
|
||||
// 0. If an error occurs, p is unchanged. The input must be a JSON number or an
|
||||
// accepted string name.
|
||||
func (p *Proto) UnmarshalJSON(b []byte) error {
|
||||
if len(b) == 0 {
|
||||
*p = 0
|
||||
return nil
|
||||
}
|
||||
if b[0] == '"' {
|
||||
b = b[1 : len(b)-1]
|
||||
}
|
||||
return p.UnmarshalText(b)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user