package rtrlib import ( "bytes" "encoding/binary" "encoding/hex" "errors" "fmt" "io" "net" ) type Logger interface { Debugf(string, ...interface{}) Printf(string, ...interface{}) Warnf(string, ...interface{}) Errorf(string, ...interface{}) Infof(string, ...interface{}) } const ( messageMaxSize = 2048 PROTOCOL_VERSION_0 = 0 PROTOCOL_VERSION_1 = 1 PDU_ID_SERIAL_NOTIFY = 0 PDU_ID_SERIAL_QUERY = 1 PDU_ID_RESET_QUERY = 2 PDU_ID_CACHE_RESPONSE = 3 PDU_ID_IPV4_PREFIX = 4 PDU_ID_IPV6_PREFIX = 6 PDU_ID_END_OF_DATA = 7 PDU_ID_CACHE_RESET = 8 PDU_ID_ROUTER_KEY = 9 PDU_ID_ERROR_REPORT = 10 FLAG_ADDED = 1 FLAG_REMOVED = 0 PDU_ERROR_CORRUPTDATA = 0 PDU_ERROR_INTERNALERR = 1 PDU_ERROR_NODATA = 2 PDU_ERROR_INVALIDREQUEST = 3 PDU_ERROR_BADPROTOVERSION = 4 PDU_ERROR_BADPDUTYPE = 5 PDU_ERROR_WITHDRAWUNKNOWN = 6 PDU_ERROR_DUPANNOUNCE = 7 TYPE_UNKNOWN = iota TYPE_PLAIN TYPE_TLS TYPE_SSH ) type PDU interface { Bytes() []byte Write(io.Writer) String() string SetVersion(uint8) GetVersion() uint8 GetType() uint8 } func TypeToString(t uint8) string { switch t { case PDU_ID_SERIAL_NOTIFY: return "Serial Notify" case PDU_ID_SERIAL_QUERY: return "Serial Query" case PDU_ID_RESET_QUERY: return "Reset Query" case PDU_ID_CACHE_RESPONSE: return "Cache Response" case PDU_ID_IPV4_PREFIX: return "IPv4 Prefix" case PDU_ID_IPV6_PREFIX: return "IPv6 Prefix" case PDU_ID_END_OF_DATA: return "End of Data" case PDU_ID_CACHE_RESET: return "Cache Reset" case PDU_ID_ROUTER_KEY: return "Router Key" case PDU_ID_ERROR_REPORT: return "Error Report" default: return fmt.Sprintf("Unknown type %d", t) } } func IsCorrectPDUVersion(pdu PDU, version uint8) bool { if version > 1 { return false } switch pdu.(type) { case *PDURouterKey: if version == 0 { return false } } return true } type PDUSerialNotify struct { Version uint8 SessionId uint16 SerialNumber uint32 } func (pdu *PDUSerialNotify) String() string { return fmt.Sprintf("PDU Serial Notify v%d (session: %d): serial: %d", pdu.Version, pdu.SessionId, pdu.SerialNumber) } func (pdu *PDUSerialNotify) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUSerialNotify) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUSerialNotify) GetVersion() uint8 { return pdu.Version } func (pdu *PDUSerialNotify) GetType() uint8 { return PDU_ID_SERIAL_NOTIFY } func (pdu *PDUSerialNotify) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_SERIAL_NOTIFY)) binary.Write(wr, binary.BigEndian, pdu.SessionId) binary.Write(wr, binary.BigEndian, uint32(12)) binary.Write(wr, binary.BigEndian, uint32(pdu.SerialNumber)) } type PDUSerialQuery struct { Version uint8 SessionId uint16 SerialNumber uint32 } func (pdu *PDUSerialQuery) String() string { return fmt.Sprintf("PDU Serial Query v%d (session: %d): serial: %d", pdu.Version, pdu.SessionId, pdu.SerialNumber) } func (pdu *PDUSerialQuery) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUSerialQuery) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUSerialQuery) GetVersion() uint8 { return pdu.Version } func (pdu *PDUSerialQuery) GetType() uint8 { return PDU_ID_SERIAL_QUERY } func (pdu *PDUSerialQuery) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_SERIAL_QUERY)) binary.Write(wr, binary.BigEndian, pdu.SessionId) binary.Write(wr, binary.BigEndian, uint32(12)) binary.Write(wr, binary.BigEndian, uint32(pdu.SerialNumber)) } type PDUResetQuery struct { Version uint8 } func (pdu *PDUResetQuery) String() string { return fmt.Sprintf("PDU Reset Query v%d", pdu.Version) } func (pdu *PDUResetQuery) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUResetQuery) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUResetQuery) GetVersion() uint8 { return pdu.Version } func (pdu *PDUResetQuery) GetType() uint8 { return PDU_ID_RESET_QUERY } func (pdu *PDUResetQuery) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_RESET_QUERY)) binary.Write(wr, binary.BigEndian, uint16(0)) binary.Write(wr, binary.BigEndian, uint32(8)) } type PDUCacheResponse struct { Version uint8 SessionId uint16 } func (pdu *PDUCacheResponse) String() string { return fmt.Sprintf("PDU Cache Response v%d (session: %d)", pdu.Version, pdu.SessionId) } func (pdu *PDUCacheResponse) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUCacheResponse) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUCacheResponse) GetVersion() uint8 { return pdu.Version } func (pdu *PDUCacheResponse) GetType() uint8 { return PDU_ID_CACHE_RESPONSE } func (pdu *PDUCacheResponse) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_CACHE_RESPONSE)) binary.Write(wr, binary.BigEndian, pdu.SessionId) binary.Write(wr, binary.BigEndian, uint32(8)) } type PDUIPv4Prefix struct { Version uint8 Prefix net.IPNet MaxLen uint8 ASN uint32 Flags uint8 } func (pdu *PDUIPv4Prefix) String() string { mask, _ := pdu.Prefix.Mask.Size() return fmt.Sprintf("PDU IPv4 Prefix v%d %s/%d(->/%d), origin: AS%d, flags: %d", pdu.Version, pdu.Prefix.IP.String(), mask, pdu.MaxLen, pdu.ASN, pdu.Flags) } func (pdu *PDUIPv4Prefix) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUIPv4Prefix) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUIPv4Prefix) GetVersion() uint8 { return pdu.Version } func (pdu *PDUIPv4Prefix) GetType() uint8 { return PDU_ID_IPV4_PREFIX } func (pdu *PDUIPv4Prefix) Write(wr io.Writer) { mask, _ := pdu.Prefix.Mask.Size() binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_IPV4_PREFIX)) binary.Write(wr, binary.BigEndian, uint16(0)) binary.Write(wr, binary.BigEndian, uint32(20)) binary.Write(wr, binary.BigEndian, pdu.Flags) binary.Write(wr, binary.BigEndian, uint8(mask)) binary.Write(wr, binary.BigEndian, pdu.MaxLen) binary.Write(wr, binary.BigEndian, uint8(0)) binary.Write(wr, binary.BigEndian, pdu.Prefix.IP.To4()) binary.Write(wr, binary.BigEndian, pdu.ASN) } type PDUIPv6Prefix struct { Version uint8 Prefix net.IPNet MaxLen uint8 ASN uint32 Flags uint8 } func (pdu *PDUIPv6Prefix) String() string { mask, _ := pdu.Prefix.Mask.Size() return fmt.Sprintf("PDU IPv6 Prefix v%d %s/%d(->/%d), origin: AS%d, flags: %d", pdu.Version, pdu.Prefix.IP.String(), mask, pdu.MaxLen, pdu.ASN, pdu.Flags) } func (pdu *PDUIPv6Prefix) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUIPv6Prefix) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUIPv6Prefix) GetVersion() uint8 { return pdu.Version } func (pdu *PDUIPv6Prefix) GetType() uint8 { return PDU_ID_IPV6_PREFIX } func (pdu *PDUIPv6Prefix) Write(wr io.Writer) { mask, _ := pdu.Prefix.Mask.Size() binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_IPV6_PREFIX)) binary.Write(wr, binary.BigEndian, uint16(0)) binary.Write(wr, binary.BigEndian, uint32(32)) binary.Write(wr, binary.BigEndian, pdu.Flags) binary.Write(wr, binary.BigEndian, uint8(mask)) binary.Write(wr, binary.BigEndian, pdu.MaxLen) binary.Write(wr, binary.BigEndian, uint8(0)) binary.Write(wr, binary.BigEndian, pdu.Prefix.IP.To16()) binary.Write(wr, binary.BigEndian, pdu.ASN) } type PDUEndOfData struct { Version uint8 SessionId uint16 SerialNumber uint32 RefreshInterval uint32 RetryInterval uint32 ExpireInterval uint32 } func (pdu *PDUEndOfData) String() string { return fmt.Sprintf("PDU End of Data v%d (session: %d): serial: %d, refresh: %d, retry: %d, expire: %d", pdu.Version, pdu.SessionId, pdu.SerialNumber, pdu.RefreshInterval, pdu.RetryInterval, pdu.ExpireInterval) } func (pdu *PDUEndOfData) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUEndOfData) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUEndOfData) GetVersion() uint8 { return pdu.Version } func (pdu *PDUEndOfData) GetType() uint8 { return PDU_ID_END_OF_DATA } func (pdu *PDUEndOfData) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_END_OF_DATA)) binary.Write(wr, binary.BigEndian, pdu.SessionId) if pdu.Version == PROTOCOL_VERSION_0 { binary.Write(wr, binary.BigEndian, uint32(12)) binary.Write(wr, binary.BigEndian, pdu.SerialNumber) } else if pdu.Version == PROTOCOL_VERSION_1 { binary.Write(wr, binary.BigEndian, uint32(24)) binary.Write(wr, binary.BigEndian, pdu.SerialNumber) binary.Write(wr, binary.BigEndian, pdu.RefreshInterval) binary.Write(wr, binary.BigEndian, pdu.RetryInterval) binary.Write(wr, binary.BigEndian, pdu.ExpireInterval) } } type PDUCacheReset struct { Version uint8 } func (pdu *PDUCacheReset) String() string { return fmt.Sprintf("PDU Cache Reset v%d", pdu.Version) } func (pdu *PDUCacheReset) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUCacheReset) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUCacheReset) GetVersion() uint8 { return pdu.Version } func (pdu *PDUCacheReset) GetType() uint8 { return PDU_ID_CACHE_RESET } func (pdu *PDUCacheReset) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_CACHE_RESET)) binary.Write(wr, binary.BigEndian, uint16(0)) binary.Write(wr, binary.BigEndian, uint32(8)) } type PDURouterKey struct { Version uint8 Flags uint8 SubjectKeyIdentifier [20]byte ASN uint32 SubjectPublicKeyInfo uint32 } func (pdu *PDURouterKey) String() string { return "PDU Router Key" } func (pdu *PDURouterKey) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDURouterKey) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDURouterKey) GetVersion() uint8 { return pdu.Version } func (pdu *PDURouterKey) GetType() uint8 { return PDU_ID_ROUTER_KEY } func (pdu *PDURouterKey) Write(wr io.Writer) { binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_ROUTER_KEY)) binary.Write(wr, binary.BigEndian, uint8(pdu.Flags)) binary.Write(wr, binary.BigEndian, uint8(0)) binary.Write(wr, binary.BigEndian, uint32(36)) binary.Write(wr, binary.BigEndian, pdu.SubjectKeyIdentifier) binary.Write(wr, binary.BigEndian, pdu.ASN) binary.Write(wr, binary.BigEndian, pdu.SubjectPublicKeyInfo) } type PDUErrorReport struct { Version uint8 ErrorCode uint16 PDUCopy []byte ErrorMsg string } func (pdu *PDUErrorReport) String() string { return fmt.Sprintf("PDU Error report v%d (error code: %d): bytes PDU copy (%d): %s. Message: %s", pdu.Version, pdu.ErrorCode, len(pdu.PDUCopy), hex.EncodeToString(pdu.PDUCopy), pdu.ErrorMsg) } func (pdu *PDUErrorReport) Bytes() []byte { b := bytes.NewBuffer([]byte{}) pdu.Write(b) return b.Bytes() } func (pdu *PDUErrorReport) SetVersion(version uint8) { pdu.Version = version } func (pdu *PDUErrorReport) GetVersion() uint8 { return pdu.Version } func (pdu *PDUErrorReport) GetType() uint8 { return PDU_ID_ERROR_REPORT } func (pdu *PDUErrorReport) Write(wr io.Writer) { nonnull := (pdu.ErrorMsg != "") addlen := 0 if nonnull { addlen = 1 } binary.Write(wr, binary.BigEndian, uint8(pdu.Version)) binary.Write(wr, binary.BigEndian, uint8(PDU_ID_ERROR_REPORT)) binary.Write(wr, binary.BigEndian, pdu.ErrorCode) binary.Write(wr, binary.BigEndian, uint32(12+len(pdu.PDUCopy)+4+len(pdu.ErrorMsg)+addlen)) binary.Write(wr, binary.BigEndian, uint32(len(pdu.PDUCopy))) binary.Write(wr, binary.BigEndian, pdu.PDUCopy) binary.Write(wr, binary.BigEndian, uint32(len(pdu.ErrorMsg)+addlen)) if nonnull { binary.Write(wr, binary.BigEndian, []byte(pdu.ErrorMsg)) binary.Write(wr, binary.BigEndian, uint8(0)) // Some clients require null-terminated strings } } func DecodeBytes(b []byte) (PDU, error) { buf := bytes.NewBuffer(b) return Decode(buf) } func Decode(rdr io.Reader) (PDU, error) { if rdr == nil { return nil, errors.New("Reader for decoding is nil") } var pver uint8 var pduType uint8 var sessionId uint16 var length uint32 err := binary.Read(rdr, binary.BigEndian, &pver) if err != nil { return nil, err } err = binary.Read(rdr, binary.BigEndian, &pduType) if err != nil { return nil, err } err = binary.Read(rdr, binary.BigEndian, &sessionId) if err != nil { return nil, err } err = binary.Read(rdr, binary.BigEndian, &length) if err != nil { return nil, err } if length < 8 { return nil, fmt.Errorf("Wrong length: %d < 8", length) } if length > messageMaxSize { return nil, fmt.Errorf("Wrong length: %d > %d", length, messageMaxSize) } toread := make([]byte, length-8) err = binary.Read(rdr, binary.BigEndian, toread) if err != nil { return nil, err } switch pduType { case PDU_ID_SERIAL_NOTIFY: if len(toread) != 4 { return nil, fmt.Errorf("Wrong length for Serial Notify PDU: %d != 4", len(toread)) } serial := binary.BigEndian.Uint32(toread) return &PDUSerialNotify{ Version: pver, SessionId: sessionId, SerialNumber: serial, }, nil case PDU_ID_SERIAL_QUERY: if len(toread) != 4 { return nil, fmt.Errorf("Wrong length for Serial Query PDU: %d != 4", len(toread)) } serial := binary.BigEndian.Uint32(toread) return &PDUSerialQuery{ Version: pver, SessionId: sessionId, SerialNumber: serial, }, nil case PDU_ID_RESET_QUERY: if len(toread) != 0 { return nil, fmt.Errorf("Wrong length for Reset Query PDU: %d != 0", len(toread)) } return &PDUResetQuery{ Version: pver, }, nil case PDU_ID_CACHE_RESPONSE: if len(toread) != 0 { return nil, fmt.Errorf("Wrong length for Cache Response PDU: %d != 0", len(toread)) } return &PDUCacheResponse{ Version: pver, SessionId: sessionId, }, nil case PDU_ID_IPV4_PREFIX: if len(toread) != 12 { return nil, fmt.Errorf("Wrong length for IPv4 Prefix PDU: %d != 12", len(toread)) } prefixLen := int(toread[1]) ip := toread[4:8] ipnet := net.IPNet{ IP: ip, Mask: net.CIDRMask(prefixLen, 32), } asn := binary.BigEndian.Uint32(toread[8:]) return &PDUIPv4Prefix{ Version: pver, Flags: uint8(toread[0]), MaxLen: uint8(toread[2]), ASN: asn, Prefix: ipnet, }, nil case PDU_ID_IPV6_PREFIX: if len(toread) != 24 { return nil, fmt.Errorf("Wrong length for IPv6 Prefix PDU: %d != 24", len(toread)) } prefixLen := int(toread[1]) ip := toread[4:20] ipnet := net.IPNet{ IP: ip, Mask: net.CIDRMask(prefixLen, 128), } asn := binary.BigEndian.Uint32(toread[20:]) return &PDUIPv6Prefix{ Version: pver, Flags: uint8(toread[0]), MaxLen: uint8(toread[2]), ASN: asn, Prefix: ipnet, }, nil case PDU_ID_END_OF_DATA: if len(toread) != 4 && len(toread) != 16 { return nil, fmt.Errorf("Wrong length for End of Data PDU: %d != 4 or != 16", len(toread)) } var serial uint32 var refreshInterval uint32 var retryInterval uint32 var expireInterval uint32 if len(toread) == 4 { serial = binary.BigEndian.Uint32(toread) } else if len(toread) == 16 { serial = binary.BigEndian.Uint32(toread[0:4]) refreshInterval = binary.BigEndian.Uint32(toread[4:8]) retryInterval = binary.BigEndian.Uint32(toread[8:12]) expireInterval = binary.BigEndian.Uint32(toread[12:16]) } return &PDUEndOfData{ Version: pver, SessionId: sessionId, SerialNumber: serial, RefreshInterval: refreshInterval, RetryInterval: retryInterval, ExpireInterval: expireInterval, }, nil case PDU_ID_CACHE_RESET: if len(toread) != 0 { return nil, fmt.Errorf("Wrong length for Cache Reset PDU: %d != 0", len(toread)) } return &PDUCacheReset{ Version: pver, }, nil case PDU_ID_ROUTER_KEY: if len(toread) != 28 { return nil, fmt.Errorf("Wrong length for Router Key PDU: %d < 8", len(toread)) } asn := binary.BigEndian.Uint32(toread[20:24]) spki := binary.BigEndian.Uint32(toread[24:28]) ski := [20]byte{} copy(ski[:], toread[0:20]) return &PDURouterKey{ Version: pver, SubjectKeyIdentifier: ski, ASN: asn, SubjectPublicKeyInfo: spki, }, nil case PDU_ID_ERROR_REPORT: if len(toread) < 8 { return nil, fmt.Errorf("Wrong length for Error Report PDU: %d < 8", len(toread)) } lenPdu := binary.BigEndian.Uint32(toread[0:4]) if len(toread) < int(lenPdu)+8 { return nil, fmt.Errorf("Wrong length for Error Report PDU: %d < %d", len(toread), lenPdu+4) } errPdu := toread[4 : lenPdu+4] lenErrText := binary.BigEndian.Uint32(toread[lenPdu+4 : lenPdu+8]) // int casting for each value is needed here to prevent an uint32 overflow that could result in // upper bound being lower than lower bound causing a crash if len(toread) < int(lenPdu)+8+int(lenErrText) { return nil, fmt.Errorf("Wrong length for Error Report PDU: %d < %d", len(toread), lenPdu+8+lenErrText) } errMsg := string(toread[lenPdu+8 : lenPdu+8+lenErrText]) return &PDUErrorReport{ Version: pver, ErrorCode: sessionId, PDUCopy: errPdu, ErrorMsg: errMsg, }, nil default: return nil, errors.New("Could not decode packet") } }