From 9a5549940f474872539c7fee71c1ca75136db7a0 Mon Sep 17 00:00:00 2001 From: Justin Bastress Date: Thu, 22 Feb 2018 17:21:57 -0500 Subject: [PATCH] add redirect; godoc for connection.go; remove debugging from tests --- modules/oracle/connection.go | 133 +++---- modules/oracle/types.go | 655 ++++++++++++++++++++++------------- modules/oracle/types_test.go | 457 ++++-------------------- 3 files changed, 527 insertions(+), 718 deletions(-) diff --git a/modules/oracle/connection.go b/modules/oracle/connection.go index b9121bc..ef93c57 100644 --- a/modules/oracle/connection.go +++ b/modules/oracle/connection.go @@ -1,8 +1,6 @@ package oracle import ( - "fmt" - "io" "net" "strconv" @@ -16,67 +14,53 @@ const ( logIndexNSNResponse = 3 ) +// HandshakeLog gives the results of the initial connection handshake in a form +// suitable for zgrab2 output. type HandshakeLog struct { - RawAccept []byte `json:"raw_accept,omitempty" zgrab:"debug"` - AcceptVersion uint16 `json:"accept_version"` - GlobalServiceOptions map[string]bool `json:"global_service_options,omitempty"` - ConnectFlags [2]map[string]bool `json:"connect_flags,omitempty"` + // AcceptVersion is the protocol version value from the Accept packet. + AcceptVersion uint16 `json:"accept_version"` + // GlobalServiceOptions is the set of GlobalServiceOptions flags that the + // server returns in the Accept packet. + GlobalServiceOptions map[string]bool `json:"global_service_options,omitempty"` + + // ConnectFlags is the ste of ConnectFlags values that the server returns + // in the Accept packet. Both are included in the array. + ConnectFlags [2]map[string]bool `json:"connect_flags,omitempty"` + + // DidResend is true if the server sent a Resend packet in response to the + // client's first Connect packet. DidResend bool `json:"did_resend"` - RawNSN []byte `json:"raw_nsn,omitempty" zgrab:"debug"` - NSNVersion string `json:"nsn_version,omitempty"` + // RedirectTarget is the connection string returned by the server in the + // Redirect packet, if one is sent. Otherwise it is empty/omitted. + RedirectTarget string `json:"redirect_target,omitempty"` + + // DidResend is set to true if the server sent a Resend packet after the + // first Connect packet. + + // NSNVersion is the ReleaseVersion string (in dotted decimal format) in the + // root of the Native Service Negotiation packet. + NSNVersion string `json:"nsn_version,omitempty"` + + // NSNServiceVersions is a map from the Native Service Negotiation service + // name to the ReleaseVersion in that service packet. NSNServiceVersions map[string]string `json:"nsn_service_versions,omitempty"` } -type PacketLogEntry struct { - Outgoing bool - Data []byte -} - -type loggedReader struct { - reader io.Reader - data []byte -} - -func logReader(reader io.Reader) *loggedReader { - return &loggedReader{reader: reader} -} - -func (reader *loggedReader) Read(data []byte) (int, error) { - n, err := reader.reader.Read(data) - if err != nil { - reader.data = append(reader.data, data[0:n]...) - } - return n, err -} - +// Connection holds the state for a scan connection to the Oracle server. type Connection struct { - conn net.Conn - target *zgrab2.ScanTarget - scanner *Scanner - resent bool - packetLog []PacketLogEntry - reader *loggedReader -} - -func (conn *Connection) getLastPacket(outgoing bool) []byte { - for i := range conn.packetLog { - r := len(conn.packetLog) - i - 1 - v := conn.packetLog[r] - if v.Outgoing == outgoing { - return v.Data - } - } - return nil + conn net.Conn + target *zgrab2.ScanTarget + scanner *Scanner + resent bool + redirect string } func (conn *Connection) send(data []byte) error { - conn.appendLogEntry(true, data) rest := data n := 0 for n < len(rest) { - fmt.Println("n=", n, "rest=", len(rest)) n, err := conn.conn.Write(rest) if err != nil { return err @@ -86,18 +70,8 @@ func (conn *Connection) send(data []byte) error { return nil } -func (conn *Connection) appendLogEntry(outgoing bool, data []byte) { - conn.packetLog = append(conn.packetLog, PacketLogEntry{ - Outgoing: outgoing, - Data: data, - }) -} - func (conn *Connection) readPacket() (*TNSPacket, error) { - wrapped := logReader(conn.conn) - ret, err := ReadTNSPacket(wrapped) - conn.appendLogEntry(false, wrapped.data) - return ret, err + return ReadTNSPacket(conn.conn) } func (conn *Connection) SendPacket(packet TNSPacketBody) (TNSPacketBody, error) { @@ -135,21 +109,13 @@ func u16Flag(v string) uint16 { return uint16(ret) } -/* -func dump(tag string, val interface{}) { - j, err := json.MarshalIndent(val, " ", " ") - if err != nil { - panic(err) - } - fmt.Println(tag + "=[[" + string(j) + "]]") - -} -*/ func (conn *Connection) Connect(connectionString string) (*HandshakeLog, error) { result := HandshakeLog{} - if len(connectionString) > 0x7fff { + extraData := []byte{} + if len(connectionString)+len(extraData)+0x3A > 0x7fff { return nil, ErrInvalidInput } + // TODO: Variable fields in the connection string (e.g. host?) connectPacket := &TNSConnect{ Version: conn.scanner.config.Version, @@ -159,9 +125,9 @@ func (conn *Connection) Connect(connectionString string) (*HandshakeLog, error) TDU: u16Flag(conn.scanner.config.TDU), ProtocolCharacteristics: NTProtocolCharacteristics(u16Flag(conn.scanner.config.ProtocolCharacterisics)), MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: uint16(len(connectionString)), - DataOffset: 0x003A, + DataOffset: uint16(0x003A + len(extraData)), MaxResponseSize: 0x00000800, ConnectFlags0: ConnectFlags(u16Flag(conn.scanner.config.ConnectFlags) & 0xff), ConnectFlags1: ConnectFlags(u16Flag(conn.scanner.config.ConnectFlags) >> 8), @@ -169,22 +135,30 @@ func (conn *Connection) Connect(connectionString string) (*HandshakeLog, error) CrossFacility1: 0, ConnectionID0: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{}, + Unknown3A: extraData, ConnectionString: connectionString, } response, err := conn.SendPacket(connectPacket) + // TODO: handle redirect if err != nil { return nil, err } if conn.resent { result.DidResend = true } - result.RawAccept = conn.getLastPacket(false) - - accept, ok := response.(*TNSAccept) - if !ok { + var accept *TNSAccept + switch resp := response.(type) { + case *TNSAccept: + accept = resp + break + case *TNSRedirect: + result.RedirectTarget = string(resp.Data) + // TODO: Follow redirects? + return &result, nil + default: return &result, ErrUnexpectedResponse } + // TODO: Unclear what these do. Taken from my client. result.AcceptVersion = accept.Version result.GlobalServiceOptions = accept.GlobalServiceOptions.Set() @@ -221,6 +195,7 @@ func (conn *Connection) Connect(connectionString string) (*HandshakeLog, error) nsnRequest := &TNSData{ DataFlags: 0, Data: (&TNSDataNSN{ + ID: 0xdeadbeef, Version: EncodeReleaseVersion(conn.scanner.config.ReleaseVersion), Options: NSNOptions(0), Services: []NSNService{ @@ -268,8 +243,6 @@ func (conn *Connection) Connect(connectionString string) (*HandshakeLog, error) return &result, err } - result.RawNSN = conn.getLastPacket(false) - wrappedNSNResponse, ok := response.(*TNSData) if !ok { return &result, ErrUnexpectedResponse diff --git a/modules/oracle/types.go b/modules/oracle/types.go index d74f8c4..7f5da21 100644 --- a/modules/oracle/types.go +++ b/modules/oracle/types.go @@ -164,42 +164,33 @@ func (header *TNSHeader) Encode() []byte { return ret } -// DecodeTNSHeader reads the header from the first 8 bytes of buf. If no header -// is provided, a new one is allocated. +// DecodeTNSHeader reads the header from the first 8 bytes of buf. // The decoded header is returned as well as a slice pointing past the end of // the header in buf. On failure, returns nil/nil/error. -func DecodeTNSHeader(ret *TNSHeader, buf []byte) (*TNSHeader, []byte, error) { +func DecodeTNSHeader(buf []byte) (*TNSHeader, []byte, error) { if len(buf) < 8 { return nil, nil, ErrBufferTooSmall } - if ret == nil { - ret = new(TNSHeader) + ret, err := ReadTNSHeader(getSliceReader(buf)) + if err != nil { + return nil, nil, err } - var u8 uint8 - rest := buf - ret.Length, rest = popU16(rest) - ret.PacketChecksum, rest = popU16(rest) - u8, rest = popU8(rest) - ret.Type = PacketType(u8) - u8, rest = popU8(rest) - ret.Flags = TNSFlags(u8) - ret.HeaderChecksum, rest = popU16(rest) - return ret, rest, nil + return ret, buf[8:], nil } -// ReadTNSHeader reads / decodes a TNSHeader from the first 8 bytes of the -// stream. +// ReadTNSHeader reads/decodes a TNSHeader from the first 8 bytes of the stream. func ReadTNSHeader(reader io.Reader) (*TNSHeader, error) { - buf := make([]byte, 8) - n, err := reader.Read(buf) - if err != nil { + ret := TNSHeader{} + next := startReading(reader) + next.read(&ret.Length) + next.read(&ret.PacketChecksum) + next.read(&ret.Type) + next.read(&ret.Flags) + next.read(&ret.HeaderChecksum) + if err := next.Error(); err != nil { return nil, err } - if n != len(buf) { - return nil, ErrInvalidData - } - ret, _, err := DecodeTNSHeader(nil, buf) - return ret, err + return &ret, nil } // ServiceOptions are flags used by the client and server in negotiating the @@ -456,27 +447,9 @@ func (buf *outputBuffer) pushU32(v uint32) *outputBuffer { return buf } -// Copy data to dest, return the byte immediately following dest -func push(dest []byte, data []byte) []byte { - copy(dest[0:len(data)], data) - return dest[len(data):] -} - -func pushU16(dest []byte, v uint16) []byte { - binary.BigEndian.PutUint16(dest[0:2], v) - return dest[2:] -} - -func pushU32(dest []byte, v uint32) []byte { - binary.BigEndian.PutUint32(dest[0:4], v) - return dest[4:] -} - -func pushU8(dest []byte, v uint8) []byte { - dest[0] = v - return dest[1:] -} - +// Encode the TNSConnect packet body into a newly-allocated buffer. If the +// packet would be longer than 255 bytes, the data is empty and the connection +// string immediately follows. func (packet *TNSConnect) Encode() []byte { length := 0x3A + len(packet.Unknown3A) + len(packet.ConnectionString) if length > 255 { @@ -513,18 +486,93 @@ func (packet *TNSConnect) Encode() []byte { return ret } -func (header *TNSConnect) String() string { - ret, err := json.Marshal(*header) - if err != nil { - return fmt.Sprintf("(error encoding %v: %v)", header, err) - } - return string(ret) +// chainedReader is a helper for decoding binary data from a stream, primarily +// to remove the need to check for an error after each read. If an error occurs, +// subsequent calls are all noops. +type chainedReader struct { + reader io.Reader + byteOrder binary.ByteOrder + err error } +// startReading returns a new BigEndian chainedReader for the given io.Reader. +func startReading(reader io.Reader) *chainedReader { + return &chainedReader{reader: reader, byteOrder: binary.BigEndian} +} + +// read the value from the stream, unless there was a previous error on the +// reader. Uses binary.Read() to decode the data. dest must be a pointer. +func (reader *chainedReader) read(dest interface{}) *chainedReader { + if reader.err != nil { + return reader + } + reader.err = binary.Read(reader.reader, binary.BigEndian, dest) + return reader +} + +// readNew allocates a new buffer to read size bytes from the stream and stores +// the buffer in *dest, unless there was a previous error on the reader. +func (reader *chainedReader) readNew(dest *[]byte, size int) *chainedReader { + if reader.err != nil { + return reader + } + ret := make([]byte, size) + _, err := io.ReadFull(reader.reader, ret) + reader.err = err + *dest = ret + return reader +} + +// readNew allocates a new buffer to read size bytes from the stream and stores +// the buffer in *dest as a string, unless there was a previous error on the +// reader. +func (reader *chainedReader) readNewString(dest *string, size int) *chainedReader { + if reader.err != nil { + return reader + } + var data []byte + reader.readNew(&data, size) + *dest = string(data) + return reader +} + +// Error returns nil if there were no errors during reading, otherwise, it +// returns the error. +func (reader *chainedReader) Error() error { + return reader.err +} + +// readU16 reads and returns an unsigned 16-bit integer, unless there was an +// error, in which case the error is returned. +func (reader *chainedReader) readU16() (uint16, error) { + var ret uint16 + reader.read(&ret) + return ret, reader.err +} + +// Read implements the io.Reader interface for the chainedReader; forwards the +// call to the underlying reader, unless there was a previous error, in which +// case the error is returned immediately. +// If the underlying reader.Read call fails, that error is returned to the +// caller and also stored in the stream's err property. +func (reader *chainedReader) Read(buf []byte) (int, error) { + if reader.err != nil { + return 0, reader.err + } + n, err := reader.reader.Read(buf) + reader.err = err + return n, err +} + +// readError is a special error type to distinguish read errors (which should be +// converted to a return value) from other panics. +type readError error + +// Helper to convert readErrors into return values. func unpanic() error { if rerr := recover(); rerr != nil { switch err := rerr.(type) { - case error: + case readError: return err default: panic(rerr) @@ -533,115 +581,112 @@ func unpanic() error { return nil } -func ReadTNSConnect(reader io.Reader, header *TNSHeader) (ret *TNSConnect, thrown error) { - defer func() { - if err := unpanic(); err != nil { - thrown = err - } - }() - ret = new(TNSConnect) - ret.Version = readU16(reader) - ret.MinVersion = readU16(reader) - ret.GlobalServiceOptions = ServiceOptions(readU16(reader)) - ret.SDU = readU16(reader) - ret.TDU = readU16(reader) - ret.ProtocolCharacteristics = NTProtocolCharacteristics(readU16(reader)) - ret.MaxBeforeAck = readU16(reader) - if _, err := io.ReadFull(reader, ret.ByteOrder[:]); err != nil { - return nil, err - } - ret.DataLength = readU16(reader) - ret.DataOffset = readU16(reader) - ret.MaxResponseSize = readU32(reader) - ret.ConnectFlags0 = ConnectFlags(readU8(reader)) - ret.ConnectFlags1 = ConnectFlags(readU8(reader)) - ret.CrossFacility0 = readU32(reader) - ret.CrossFacility1 = readU32(reader) - if _, err := io.ReadFull(reader, ret.ConnectionID0[:]); err != nil { - return nil, err - } - if _, err := io.ReadFull(reader, ret.ConnectionID1[:]); err != nil { - return nil, err - } +// ReadTNSConnect reads a TNSConnect packet from the reader, which should point +// to the first byte after the end of the TNSHeader. +func ReadTNSConnect(reader io.Reader, header *TNSHeader) (*TNSConnect, error) { + ret := new(TNSConnect) + next := startReading(reader) + next.read(&ret.Version) + next.read(&ret.MinVersion) + next.read(&ret.GlobalServiceOptions) + next.read(&ret.SDU) + next.read(&ret.TDU) + next.read(&ret.ProtocolCharacteristics) + next.read(&ret.MaxBeforeAck) + next.read(&ret.ByteOrder) + next.read(&ret.DataLength) + next.read(&ret.DataOffset) + next.read(&ret.MaxResponseSize) + next.read(&ret.ConnectFlags0) + next.read(&ret.ConnectFlags1) + next.read(&ret.CrossFacility0) + next.read(&ret.CrossFacility1) + next.read(&ret.ConnectionID0) + next.read(&ret.ConnectionID1) unknownLen := ret.DataOffset - 0x3A - ret.Unknown3A = make([]byte, unknownLen) - if _, err := io.ReadFull(reader, ret.Unknown3A); err != nil { + next.readNew(&ret.Unknown3A, int(unknownLen)) + next.readNewString(&ret.ConnectionString, int(ret.DataLength)) + if err := next.Error(); err != nil { return nil, err } - data := make([]byte, ret.DataLength) - if _, err := io.ReadFull(reader, data); err != nil { - return nil, err - } - ret.ConnectionString = string(data) return ret, nil } +// GetType identifies the packet as a PacketTypeConnect. func (packet *TNSConnect) GetType() PacketType { return PacketTypeConnect } -// TNSResend is just a header with type = PacketTypeResend (0x0b == 11) +// TNSResend is empty -- the entire packet is just a header with a type of +// PacketTypeResend (0x0b == 11). type TNSResend struct { } +// Encode the packet body (which for a Resend packet just means returning an +// empty byte slice). func (packet *TNSResend) Encode() []byte { return []byte{} } +// GetType identifies the packet as a PacketTypeResend. func (packet *TNSResend) GetType() PacketType { return PacketTypeResend } +// ReadTNSResend reads a TNSResend packet from the reader, which should point +// to the first byte after the end of the header -- so in this case, it reads +// nothing and returns an empty TNSResend{} instance. func ReadTNSResend(reader io.Reader, header *TNSHeader) (*TNSResend, error) { ret := TNSResend{} return &ret, nil } -// TODO: TNSConnect.Decode() - +// TNSAccept is the server's response to a successful TNSConnect request from +// the client. type TNSAccept struct { - // 08..09: 0x0136 / 0x0134? + // Version is the protocol version the server is using. TODO: find the + // actual format. Version uint16 - // 0A..0B: 0x0801 + + // GlobalServiceOptions specify connection settings (TODO: details). GlobalServiceOptions ServiceOptions - // 0C..0D: 0x0800 + + // SDU gives the Session Data Unit size for this connection. SDU uint16 - // 0E..0F: 0x7fff + + // TDU gives the Transfer Data Unit size for this connection. TDU uint16 - // 10..11: 01 00 + + // ByteOrder gives the encoding of the integer 1 as a 16-bit integer + // (NOTE: clients and servers seem to routinely send a little-endian 1, + // while clearly using big-endian encoding for integers, at least at the + // TNS layer...?) ByteOrder [2]byte - // 12..13: 0x0000 + + // DataLength is the length of the AcceptData payload. DataLength uint16 - // 14..15: 0x0020 + + // DataOffset is the offset from the start of the packet (including the + // 8 bytes of the header) of the AcceptData. Always (?) 0x20. DataOffset uint16 - // 16..17: 0x0101 + + // ConnectFlags0 specifies connection settings (TODO: details). ConnectFlags0 ConnectFlags + + // ConnectFlags1 specifies connection settings (TODO: details). ConnectFlags1 ConnectFlags // Unknown18 provides support for case like TNSConnect, where there is // "data" after the end of the known packet but before the start of the // AcceptData pointed to by DataOffset. // Currently this is always 8 bytes. - Unknown18 []byte + Unknown18 []byte + + // AcceptData is the packet payload (TODO: details). AcceptData []byte } -func popU8(buf []byte) (uint8, []byte) { - return uint8(buf[0]), buf[1:] -} - -func popU16(buf []byte) (uint16, []byte) { - return binary.BigEndian.Uint16(buf[0:2]), buf[2:] -} - -func popU32(buf []byte) (uint32, []byte) { - return binary.BigEndian.Uint32(buf[0:4]), buf[4:] -} - -func popN(buf []byte, n int) ([]byte, []byte) { - return buf[0:n], buf[n:] -} - +// Encode the TNSAccept packet body into a newly-allocated byte slice. func (packet *TNSAccept) Encode() []byte { length := 16 + len(packet.Unknown18) + len(packet.AcceptData) ret := make([]byte, length) @@ -651,8 +696,6 @@ func (packet *TNSAccept) Encode() []byte { next.pushU16(packet.SDU) next.pushU16(packet.TDU) next.push(packet.ByteOrder[:]) - // packet.DataLength = len(packet.AcceptData) - // packet.DataOffset = 8 + 16 + len(packet.Unknown18) // TNSHeader + accept header + unknown next.pushU16(packet.DataLength) next.pushU16(packet.DataOffset) next.pushU8(uint8(packet.ConnectFlags0)) @@ -662,6 +705,7 @@ func (packet *TNSAccept) Encode() []byte { return ret } +// GetType identifies the packet as a PacketTypeAccept. func (packet *TNSAccept) GetType() PacketType { return PacketTypeAccept } @@ -670,7 +714,7 @@ func readU8(reader io.Reader) uint8 { buf := make([]byte, 1) _, err := io.ReadFull(reader, buf) if err != nil { - panic(err) + panic(readError(err)) } return buf[0] } @@ -679,7 +723,7 @@ func readU16(reader io.Reader) uint16 { buf := make([]byte, 2) _, err := io.ReadFull(reader, buf) if err != nil { - panic(err) + panic(readError(err)) } return binary.BigEndian.Uint16(buf) } @@ -688,73 +732,119 @@ func readU32(reader io.Reader) uint32 { buf := make([]byte, 4) _, err := io.ReadFull(reader, buf) if err != nil { - panic(err) + panic(readError(err)) } return binary.BigEndian.Uint32(buf) } -func ReadTNSAccept(reader io.Reader, header *TNSHeader) (ret *TNSAccept, thrown error) { - defer func() { - if err := unpanic(); err != nil { - thrown = err - } - }() - ret = new(TNSAccept) - ret.Version = readU16(reader) - ret.GlobalServiceOptions = ServiceOptions(readU16(reader)) - ret.SDU = readU16(reader) - ret.TDU = readU16(reader) - if _, err := io.ReadFull(reader, ret.ByteOrder[:]); err != nil { - return nil, err - } - ret.DataLength = readU16(reader) - ret.DataOffset = readU16(reader) - ret.ConnectFlags0 = ConnectFlags(readU8(reader)) - ret.ConnectFlags1 = ConnectFlags(readU8(reader)) +// ReadTNSAccept reads a TNSAccept packet body from the stream. reader should +// point to the first byte after the TNSHeader. +func ReadTNSAccept(reader io.Reader, header *TNSHeader) (*TNSAccept, error) { + ret := new(TNSAccept) + next := startReading(reader) + next.read(&ret.Version) + next.read(&ret.GlobalServiceOptions) + next.read(&ret.SDU) + next.read(&ret.TDU) + next.read(&ret.ByteOrder) + next.read(&ret.DataLength) + next.read(&ret.DataOffset) + next.read(&ret.ConnectFlags0) + next.read(&ret.ConnectFlags1) unknownLen := ret.DataOffset - 16 - 8 - ret.Unknown18 = make([]byte, unknownLen) - if _, err := io.ReadFull(reader, ret.Unknown18); err != nil { - return nil, err - } - ret.AcceptData = make([]byte, ret.DataLength) - if _, err := io.ReadFull(reader, ret.AcceptData); err != nil { + next.readNew(&ret.Unknown18, int(unknownLen)) + next.readNew(&ret.AcceptData, int(ret.DataLength)) + if err := next.Error(); err != nil { return nil, err } return ret, nil } +// RefuseReason is an enumeration describing the reason the request was refused. +// TODO: details. type RefuseReason uint8 +// TNSRefuse is returned by the server when (...TODO: details -- not returned on +// failed auth from TNSConnect). type TNSRefuse struct { - // 08: 01 + // TODO: details AppReason RefuseReason - // 09: 00 + + // TODO: details SysReason RefuseReason - // 0A..0B: 0010 + + // DataLength is the length of the packet's Data payload DataLength uint16 - // 0C... + + // Data is the packet's payload. TODO: details Data []byte } +// TNSRedirect is returned by the server in response to a TNSConnect when it +// needs to direct the caller elsewhere. Its Data is a new connection string +// for the caller to use. type TNSRedirect struct { + // DataLength is the length of the packet's Data payload. DataLength uint16 - Data []byte + + // Data is the TNSRedirect's payload -- it contains a new connection string + // for the client to use in a subsequent TNSConnect call. + Data []byte } +// Encode the TNSRedirect packet body into a newly-allocated buffer. +func (packet *TNSRedirect) Encode() []byte { + ret := make([]byte, len(packet.Data)+2) + next := outputBuffer(ret) + next.pushU16(uint16(packet.DataLength)) + next.push(packet.Data) + return ret +} + +// GetType identifies the packet as PacketTypeRedirect. +func (packet *TNSRedirect) GetType() PacketType { + return PacketTypeRedirect +} + +// ReadTNSRedirect reads a TNSRedirect packet from the stream, which should +// point to the first byte after the TNSHeader. +func ReadTNSRedirect(reader io.Reader, header *TNSHeader) (*TNSRedirect, error) { + ret := new(TNSRedirect) + next := startReading(reader) + next.read(&ret.DataLength) + next.readNew(&ret.Data, int(header.Length-8-2)) + if err := next.Error(); err != nil { + return nil, err + } + if len(ret.Data) != int(ret.DataLength) { + return nil, ErrInvalidData + } + return ret, nil +} + +// ReleaseVersion is a packed version number describing the release version of +// a specific (sub-)component. Logically it has five components, described at +// https://docs.oracle.com/cd/B28359_01/server.111/b28310/dba004.htm: +// major.maintenance.appserver.component.platform. The number of bits allocated +// to each are respectively 8.4.4.8.8, so 0x01230405 would denote "1.2.3.4.5". type ReleaseVersion uint32 +// String returns the dotted-decimal representation of the release version: +// major.maintenance.appserver.component.platform. func (v ReleaseVersion) String() string { - // 0xAAbcddee -> A.b.c.d.e, major.maintenance.appserver.component.platform - // See https://docs.oracle.com/cd/B28359_01/server.111/b28310/dba004.htm return fmt.Sprintf("%d.%d.%d.%d.%d", v>>24, v>>20&0x0F, v>>16&0x0F, v>>8&0xFF, v&0xFF) } +// Bytes returns the big-endian binary encoding of the release version. func (v ReleaseVersion) Bytes() []byte { buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, uint32(v)) return buf } +// EncodeReleaseVersion gets a ReleaseVersion instance from its dotted-decimal +// representation, e.g.: +// EncodeReleaseVersion("64.3.2.1.0") = ReleaseVersion(0x40320100). func EncodeReleaseVersion(value string) ReleaseVersion { parts := strings.Split(value, ".") if len(parts) != 5 { @@ -781,8 +871,10 @@ func EncodeReleaseVersion(value string) ReleaseVersion { return ReleaseVersion((numbers[0] << 24) | (numbers[1] << 20) | (numbers[2] << 16) | (numbers[3] << 8) | numbers[4]) } +// DataFlags is a 16-bit flags field used in the TNSData packet. type DataFlags uint16 +// TODO: details const ( DFSendToken DataFlags = 0x0001 DFRequestConfirmation = 0x0002 @@ -821,6 +913,7 @@ var dfNames = map[DataFlags]string{ DFUnknown8000: "UNKNOWN_8000", } +// Set gets a set representation of the DataFlags. func (flags DataFlags) Set() map[string]bool { ret, _ := zgrab2.MapFlagsToSet(uint64(flags), func(bit uint64) (string, error) { return dfNames[DataFlags(bit)], nil @@ -829,24 +922,25 @@ func (flags DataFlags) Set() map[string]bool { return ret } +// TNSData is the packet type used to send (more or less) arbitrary data between +// client and server. All packets have the DataFlags, and for many, the first +// four bytes of the data have a 32-bit value identifying the type of data. type TNSData struct { - // 08..09 + // DataFlags gives information on the data (TODO: details) DataFlags DataFlags - // 0A... + + // Data is the packet's payload. Its length is equal to the header's length + // less 10 bytes (8 for the header itself, 2 for the DataFlags). Data []byte } -type DataType uint8 - -const ( - DataTypeSetProtocol DataType = 0x01 - DataTypeSecureNetworkServices = 0x06 -) - const ( + // DataIDNSN identifies a Native Security Negotiation data payload. DataIDNSN uint32 = 0xdeadbeef ) +// GetID returns a TNSData's ID (the first four bytes of the data), if available +// otherwise returns 0. func (packet *TNSData) GetID() uint32 { if len(packet.Data) < 4 { return 0 @@ -854,6 +948,7 @@ func (packet *TNSData) GetID() uint32 { return binary.BigEndian.Uint32(packet.Data[0:4]) } +// Encode the TNSData packet body into a newly-allocated buffer. func (packet *TNSData) Encode() []byte { ret := make([]byte, len(packet.Data)+2) next := outputBuffer(ret) @@ -862,36 +957,42 @@ func (packet *TNSData) Encode() []byte { return ret } +// GetType identifies the packet as PacketTypeData. func (packet *TNSData) GetType() PacketType { return PacketTypeData } -func ReadTNSData(reader io.Reader, header *TNSHeader) (ret *TNSData, thrown error) { - defer func() { - if err := unpanic(); err != nil { - thrown = err - } - }() - ret = new(TNSData) - ret.DataFlags = DataFlags(readU16(reader)) - ret.Data = make([]byte, header.Length-8-2) - n, err := reader.Read(ret.Data) - if err != nil { +// ReadTNSData reads a TNSData packet from the stream, which should point to the +// first byte after the TNSHeader. +func ReadTNSData(reader io.Reader, header *TNSHeader) (*TNSData, error) { + ret := new(TNSData) + next := startReading(reader) + next.read(&ret.DataFlags) + next.readNew(&ret.Data, int(header.Length-8-2)) + if err := next.Error(); err != nil { return nil, err } - if n != len(ret.Data) { - return nil, ErrInvalidData - } return ret, nil } +// NSNServiceType is an enumerated type identifying the "service" types inside +// a NSN packet. type NSNServiceType uint16 const ( + // NSNServiceAuthentication identifies an Authentication service + // (TODO: details). NSNServiceAuthentication NSNServiceType = 1 - NSNServiceEncryption = 2 - NSNServiceDataIntegrity = 3 - NSNServiceSupervisor = 4 + + // NSNServiceEncryption identifies an Encryption service (TODO: details). + NSNServiceEncryption = 2 + + // NSNServiceDataIntegrity identifies a Data Integrity service + // (TODO: details). + NSNServiceDataIntegrity = 3 + + // NSNServiceSupervisor identifies a Supervisor service (TODO: details). + NSNServiceSupervisor = 4 ) var nsnServiceTypeToName = map[NSNServiceType]string{ @@ -901,6 +1002,7 @@ var nsnServiceTypeToName = map[NSNServiceType]string{ NSNServiceSupervisor: "Supervisor", } +// String gives the string representation of the service type. func (typ NSNServiceType) String() string { ret, ok := nsnServiceTypeToName[typ] if !ok { @@ -909,18 +1011,25 @@ func (typ NSNServiceType) String() string { return ret } +// IsUnknown returns true iff the service type value is not one of the +// recognized enum values. func (typ NSNServiceType) IsUnknown() bool { _, ok := nsnServiceTypeToName[typ] return !ok } -// NSN packets somewhat described here: https://docs.oracle.com/cd/B19306_01/network.102/b14212/troublestng.htm +// NSNService is an individual "packet" inside the NSN data payload; it consists +// in an identifier and a list of values or "sub-packets" giving configuration +// settings for that service type. These are somewhat described here: +// https://docs.oracle.com/cd/B19306_01/network.102/b14212/troublestng.htm type NSNService struct { Type NSNServiceType Values []NSNValue Marker uint32 } +// GetSize returns the encoded size of the NSNService. Rather than overflowing, +// causes a panic if this is larger than 16 bits. func (service *NSNService) GetSize() uint16 { ret := uint32(8) // uint16(Type) + uint16(#values) + uint32(marker) for _, v := range service.Values { @@ -934,6 +1043,8 @@ func (service *NSNService) GetSize() uint16 { return uint16(ret) } +// Encode returns the encoded NSNService in a newly allocated buffer. +// If the length of the encoded value would be larger than 16 bits, panics. func (service *NSNService) Encode() []byte { // Absolute minimum, if each value had zero length ret := make([]byte, service.GetSize()) @@ -952,6 +1063,9 @@ func (service *NSNService) Encode() []byte { return ret } +// ReadNSNService reads an NSNService packet from the stream. On failure to +// read a service, returns nil + an error (though the stream will be in a bad +// state). func ReadNSNService(reader io.Reader, ret *NSNService) (*NSNService, error) { if ret == nil { ret = &NSNService{} @@ -975,23 +1089,44 @@ func ReadNSNService(reader io.Reader, ret *NSNService) (*NSNService, error) { return ret, nil } +// NSNValueType is a 16-bit enumerated value identifying the different data +// types of the values or "sub-packets" in the NSNService packets. NOTE: this +// list may not be comprehensive. type NSNValueType uint16 const ( - NSNValueTypeString NSNValueType = 0 - NSNValueTypeBytes = 1 - NSNValueTypeUB1 = 2 - NSNValueTypeUB2 = 3 - NSNValueTypeUB4 = 4 - NSNValueTypeVersion = 5 - NSNValueTypeStatus = 6 + // NSNValueTypeString identifies a string value type. + NSNValueTypeString NSNValueType = 0 + + // NSNValueTypeBytes identifies a binary value type (an array of bytes). + NSNValueTypeBytes = 1 + + // NSNValueTypeUB1 identifies an unsigned 8-bit integer. + NSNValueTypeUB1 = 2 + + // NSNValueTypeUB2 identifies an unsigned 16-bit big-endian integer. + NSNValueTypeUB2 = 3 + + // NSNValueTypeUB2 identifies an unsigned 32-bit big-endian integer. + NSNValueTypeUB4 = 4 + + // NSNValueTypeVersion identifies a 32-bit ReleaseVersion value. + NSNValueTypeVersion = 5 + + // NSNValueTypeStatus identifies a 16-bit status value. + NSNValueTypeStatus = 6 ) +// NSNValue represents a single value or "sub-packet" within an NSNService. It +// consists of a type identifier and the value itself. type NSNValue struct { Type NSNValueType Value []byte } +// String gives the friendly encoding of the sub-packet value; integers are +// given in decimal, versions in dotted decimal format, binary data as base64, +// strings as strings. func (value *NSNValue) String() string { switch value.Type { case NSNValueTypeString: @@ -1013,10 +1148,11 @@ func (value *NSNValue) String() string { } } +// MarshalJSON encodes the NSNValue as a JSON object: a type/value pair. func (value *NSNValue) MarshalJSON() ([]byte, error) { type Aux struct { - Type NSNValueType - Value interface{} + Type NSNValueType `json:"type"` + Value interface{} `json:"value"` } ret := Aux{ Type: value.Type, @@ -1042,42 +1178,49 @@ func (value *NSNValue) MarshalJSON() ([]byte, error) { return json.Marshal(ret) } +// NSNValueVersion returns a NSNValue of type Version whose value is given in +// dotted-decimal format. func NSNValueVersion(v string) *NSNValue { return &NSNValue{ - Type: 5, + Type: NSNValueTypeVersion, Value: EncodeReleaseVersion(v).Bytes(), } } +// NSNValueBytes returns a NSNValue of type Bytes with the given value. func NSNValueBytes(bytes []byte) *NSNValue { return &NSNValue{ - Type: 1, + Type: NSNValueTypeBytes, Value: bytes, } } +// NSNValueUB1 returns a NSNValue of type UB1 with the given value. func NSNValueUB1(val uint8) *NSNValue { return &NSNValue{ - Type: 2, + Type: NSNValueTypeUB1, Value: []byte{val}, } } +// NSNValueUB2 returns a NSNValue of type UB2 with the given value. func NSNValueUB2(val uint16) *NSNValue { ret := make([]byte, 2) binary.BigEndian.PutUint16(ret, val) return &NSNValue{ - Type: 3, + Type: NSNValueTypeUB2, Value: ret, } } +// NSNValueStatus returns a NSNValue of type Status with the given value. func NSNValueStatus(val uint16) *NSNValue { ret := NSNValueUB2(val) - ret.Type = 6 + ret.Type = NSNValueTypeStatus return ret } +// NSNValueString returns a NSNValue of type String with the given value. func NSNValueString(val string) *NSNValue { return &NSNValue{ Type: 0, @@ -1085,6 +1228,8 @@ func NSNValueString(val string) *NSNValue { } } +// Encode returns the encoding of the NSNValue in a newly-allocated byte slice. +// Causes a panic if the length of the value would be longer than 16 bits. func (value *NSNValue) Encode() []byte { if len(value.Value) > 0xffff { panic(ErrInvalidInput) @@ -1100,6 +1245,8 @@ func (value *NSNValue) Encode() []byte { return ret } +// ReadNSNValue reads a NSNValue from the stream, returns nil/error if one +// cannot be read (leaving the stream in a bad state). func ReadNSNValue(reader io.Reader, ret *NSNValue) (*NSNValue, error) { if ret == nil { ret = &NSNValue{} @@ -1117,16 +1264,32 @@ func ReadNSNValue(reader io.Reader, ret *NSNValue) (*NSNValue, error) { return ret, nil } +// NSNOptions is an 8-bit flags value describing the Native Security Negotiation +// options (TODO: details). type NSNOptions uint8 +// TNSDataNSN represents the decoded body of a TNSData packet for a Native +// Security Negotiation payload. type TNSDataNSN struct { - Version ReleaseVersion - Options NSNOptions + // ID is the TNSData identifier for NSN (0xdeadbeef) + ID uint32 + + // Version is the ReleaseVersion, which seems to often be 0 in practice. + Version ReleaseVersion + + // Options is an 8-bit flags value giving options for the connection (TODO: + // details). + Options NSNOptions + + // Services is an array of NSNService values, giving the configuration for + // that service type. Services []NSNService } +// GetSize returns the encoded size of the TNSDataNSN body. Causes a panic if +// the data length would be longer than 16 bits. func (packet *TNSDataNSN) GetSize() uint16 { - ret := uint32(13) // uint32(id) + uint16(len) + uint32(version) + uint16(#services) + uint8(options) + ret := uint32(13) // uint32(ID) + uint16(len) + uint32(version) + uint16(#services) + uint8(options) for _, v := range packet.Services { ret += uint32(v.GetSize()) } @@ -1138,11 +1301,12 @@ func (packet *TNSDataNSN) GetSize() uint16 { return uint16(ret) } +// Encode returns the encoded TNSDataNSN data in a newly-allocated buffer. func (packet *TNSDataNSN) Encode() []byte { size := packet.GetSize() ret := make([]byte, size) next := outputBuffer(ret) - next.pushU32(uint32(DataIDNSN)) + next.pushU32(uint32(packet.ID)) next.pushU16(size) next.pushU32(uint32(packet.Version)) if len(packet.Services) > 0xffff { @@ -1156,76 +1320,85 @@ func (packet *TNSDataNSN) Encode() []byte { return ret } +// DecodeTNSDataNSN reads a TNSDataNSN packet from a TNSData body. func DecodeTNSDataNSN(data []byte) (*TNSDataNSN, error) { reader := getSliceReader(data) + + ret, err := ReadTNSDataNSN(reader) + if err != nil { + return nil, err + } + if len(reader.Data) > 0 { + // there should be no leftover data + return nil, ErrInvalidData + } + return ret, nil +} + +// ReadTNSDataNSN reads a TNSDataNSN packet from a stream pointing to the start +// of the NSN data. +func ReadTNSDataNSN(reader io.Reader) (*TNSDataNSN, error) { ret := TNSDataNSN{} - tag := readU32(reader) - if tag != DataIDNSN { + next := startReading(reader) + + next.read(&ret.ID) + if ret.ID != DataIDNSN { return nil, ErrUnexpectedResponse } - length := readU16(reader) - if len(data) != int(length) { - // if we have 0xdeadbeef, but the length is incorrect, that would - // indicate truncation or corruption + + length, err := next.readU16() + if err != nil { + return nil, err + } + + if length < 4+2+4+2 { + // length covers the entire data field, so it should cover 0xdeadbeef, + // the length, the version and the number of services, at a minimum. return nil, ErrInvalidData } - ret.Version = ReleaseVersion(readU32(reader)) - n := int(readU16(reader)) - if n > 0x0400 { - // arbitrary but certainly sufficiently-high value. + next.read(&ret.Version) + n, err := next.readU16() + if n >= 0x0100 { + // arbitrary but certainly sufficiently-high value -- n here is the + // number of "services", which is typically 4. return nil, ErrInvalidData } - ret.Options = NSNOptions(readU8(reader)) + next.read(&ret.Options) // TODO: Check for valid options? + + if err := next.Error(); err != nil { + return nil, err + } + ret.Services = make([]NSNService, n) - for i := 0; i < n; i++ { + for i := 0; i < int(n); i++ { _, err := ReadNSNService(reader, &ret.Services[i]) if err != nil { return nil, err } } + if length != ret.GetSize() { + return nil, ErrInvalidData + } return &ret, nil } -type TNSDataSetProtocolRequest struct { - // 08..09 - DataFlags DataFlags - // 0A - DataType DataType - // 0B...(null) - AcceptedVersions []byte - // ... - ClientPlatform string -} - -type TNSDataSetProtocolResponse struct { - // 08..09 - DataFlags DataFlags - // 0A - DataType DataType - // 0B...(null) - AcceptedVersions []byte - // ...(null) - ServerBanner string - // ... - Data []byte -} - +// TNSPacketBody is the interface for the "body" of a TNSPacket (that is, +// everything after the header). type TNSPacketBody interface { GetType() PacketType Encode() []byte } +// TNSPacket is a TNSHeader + a body. type TNSPacket struct { Header *TNSHeader Body TNSPacketBody } -type InputTNSPacket struct { - TNSPacket - Raw []byte -} - +// Encode the packet (header + body). If header is nil, create one with no flags +// and the type set to the body's type. If header.Length == 0, set it to the +// appropriate value (length of encoded body + 8). func (packet *TNSPacket) Encode() []byte { body := packet.Body.Encode() if packet.Header == nil { @@ -1246,6 +1419,8 @@ func (packet *TNSPacket) Encode() []byte { return append(header, body...) } +// ReadTNSPacket reads a TNSPacket from the stream, or returns nil + an error +// if one cannot be read. func ReadTNSPacket(reader io.Reader) (*TNSPacket, error) { var body TNSPacketBody var err error diff --git a/modules/oracle/types_test.go b/modules/oracle/types_test.go index e72b2d2..58f7ff6 100644 --- a/modules/oracle/types_test.go +++ b/modules/oracle/types_test.go @@ -4,13 +4,9 @@ import ( "bytes" "encoding/hex" "encoding/json" - "fmt" - "math" "strconv" "strings" "testing" - - "github.com/sirupsen/logrus" ) func max(a, b int) int { @@ -20,12 +16,13 @@ func max(a, b int) int { return b } +// debugging -- interleave hex dumps of expected/actual, highlight differences func interleave(expected, actual []byte) string { ret := make([]string, 0) e := strings.Split(string(hex.Dump(expected)), "\n") a := strings.Split(string(hex.Dump(actual)), "\n") n := max(len(e), len(a)) - for i := 0; i < n; i++ { + for i := 0; i < n-1; i++ { var ei, ai string = "", "" if i < len(e) { ei = e[i] @@ -54,15 +51,6 @@ func interleave(expected, actual []byte) string { return strings.Join(ret, "\n") } -type Buffer struct { - Data []byte -} - -func (buf *Buffer) Write(data []byte) (int, error) { - buf.Data = append(buf.Data, data...) - return len(data), nil -} - func fromHex(h string) []byte { bytes := strings.Fields(h) ret := make([]byte, len(bytes)) @@ -154,6 +142,7 @@ var validTNSData = map[string]TestCase{ Body: &TNSData{ DataFlags: 0, Data: (&TNSDataNSN{ + ID: 0xdeadbeef, Version: EncodeReleaseVersion("10.2.0.3.0"), Options: NSNOptions(0), Services: []NSNService{ @@ -217,15 +206,6 @@ var validTNSData = map[string]TestCase{ }, } -type TestCaseOld struct { - ConnectEncoding string - ConnectHeader *TNSHeader - ConnectValue *TNSConnect - AcceptEncoding string - AcceptHeader *TNSHeader - AcceptValue *TNSAccept -} - var validTNSConnect = map[string]TestCase{ "01. 013A-0139": TestCase{ Encoding: "00 ca 00 00 01 00 00 00 01 3a 01 2c 0c 41 20 00 " + /* .........:.,.A . */ @@ -251,7 +231,7 @@ var validTNSConnect = map[string]TestCase{ TDU: 0xffff, ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: 0x0090, DataOffset: 0x003A, MaxResponseSize: 0x00000800, @@ -293,7 +273,7 @@ var validTNSConnect = map[string]TestCase{ TDU: 0x7fff, ProtocolCharacteristics: NTPCHangon | NTPCCallbackIO | NTPCAsyncIO | NTPCGenerateSIGURG | NTPCUrgentIO | NTPCFullDuplex, // 0x860e MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: 0x00c6, DataOffset: 0x003a, MaxResponseSize: 0x00000200, @@ -334,7 +314,7 @@ var validTNSConnect = map[string]TestCase{ TDU: 0x7fff, ProtocolCharacteristics: NTPCHangon | NTPCCallbackIO | NTPCAsyncIO | NTPCGenerateSIGURG | NTPCUrgentIO | NTPCFullDuplex, // 0x860e MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: 0x00b2, DataOffset: 0x003a, MaxResponseSize: 0x00000200, @@ -374,7 +354,7 @@ var validTNSConnect = map[string]TestCase{ TDU: 0xffff, ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: 0x0091, DataOffset: 0x0046, // points past the 12 bytes of \x00/\x20 at the start MaxResponseSize: 0x00000800, @@ -389,6 +369,46 @@ var validTNSConnect = map[string]TestCase{ }, }, }, + "unknown3a": TestCase{ + Encoding: "00 d7 00 00 01 00 00 00 01 3b 01 2c 0c 41 20 00 " + /* .........;.,.A . */ + "ff ff 7f 08 00 00 01 00 00 91 00 46 00 00 08 00 " + /* ...........F.... */ + "41 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + /* AA.............. */ + "00 00 00 00 00 00 00 00 00 00 12 34 56 78 9a bc " + /* ............ .. */ + "de fe dc ba 98 76 28 44 45 53 43 52 49 50 54 49 " + /* ......(DESCRIPTI */ + "4f 4e 3d 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 " + /* ON=(CONNECT_DATA */ + "3d 28 53 49 44 3d 6f 72 63 6c 31 31 67 29 28 43 " + /* =(SID=orcl11g)(C */ + "49 44 3d 28 50 52 4f 47 52 41 4d 3d 73 71 6c 70 " + /* ID=(PROGRAM=sqlp */ + "6c 75 73 40 6b 61 6c 69 29 28 48 4f 53 54 3d 6b " + /* lus@kali)(HOST=k */ + "61 6c 69 29 28 55 53 45 52 3d 72 6f 6f 74 29 29 " + /* ali)(USER=root)) */ + "29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f " + /* )(ADDRESS=(PROTO */ + "43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 31 30 " + /* COL=TCP)(HOST=10 */ + "2e 30 2e 37 32 2e 31 31 33 29 28 50 4f 52 54 3d " + /* .0.72.113)(PORT= */ + "31 35 32 31 29 29 29 ", + Value: &TNSPacket{ + Header: &TNSHeader{Length: 0x00d7, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0, HeaderChecksum: 0}, + Body: &TNSConnect{ + Version: 0x013b, + MinVersion: 0x012c, + GlobalServiceOptions: SOHeaderChecksum | SOFullDuplex | SOUnknown0040 | SOUnknown0001, // (0x0c41) + SDU: 0x2000, + TDU: 0xffff, + ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 + MaxBeforeAck: 0, + ByteOrder: defaultByteOrder, + DataLength: 0x0091, + DataOffset: 0x0046, + MaxResponseSize: 0x00000800, + ConnectFlags0: CFUnknown40 | CFServicesWanted, + ConnectFlags1: CFUnknown40 | CFServicesWanted, + CrossFacility0: 0, + CrossFacility1: 0, + ConnectionID0: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, + ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, + Unknown3A: []byte{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xfe, 0xdc, 0xba, 0x98, 0x76}, + ConnectionString: "(DESCRIPTION=(CONNECT_DATA=(SID=orcl11g)(CID=(PROGRAM=sqlplus@kali)(HOST=kali)(USER=root)))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.72.113)(PORT=1521)))", + }, + }, + }, } var validTNSAccept = map[string]TestCase{ @@ -402,7 +422,7 @@ var validTNSAccept = map[string]TestCase{ GlobalServiceOptions: 0, SDU: 0x0800, TDU: 0x7fff, - ByteOrder: DefaultByteOrder, + ByteOrder: defaultByteOrder, DataLength: 0, DataOffset: 0x20, ConnectFlags0: CFUnknown40 | CFUnknown20 | CFServicesWanted, @@ -414,243 +434,6 @@ var validTNSAccept = map[string]TestCase{ }, } -var old1ValidTNSConnect = map[string]TestCaseOld{ - "01. 013A-0139": TestCaseOld{ - ConnectEncoding: "00 ca 00 00 01 00 00 00 01 3a 01 2c 0c 41 20 00 " + /* .........:.,.A . */ - "ff ff 7f 08 00 00 01 00 00 90 00 3a 00 00 08 00 " + /* ...........:.... */ - "41 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + /* AA.............. */ - "00 00 00 00 00 00 00 00 00 00 28 44 45 53 43 52 " + /* ..........(DESCR */ - "49 50 54 49 4f 4e 3d 28 43 4f 4e 4e 45 43 54 5f " + /* IPTION=(CONNECT_ */ - "44 41 54 41 3d 28 53 45 52 56 49 43 45 5f 4e 41 " + /* DATA=(SERVICE_NA */ - "4d 45 3d 63 6b 64 62 29 28 43 49 44 3d 28 50 52 " + /* ME=ckdb)(CID=(PR */ - "4f 47 52 41 4d 3d 67 73 71 6c 29 28 48 4f 53 54 " + /* OGRAM=gsql)(HOST */ - "3d 4d 63 41 66 65 65 29 28 55 53 45 52 3d 72 6f " + /* =McAfee)(USER=ro */ - "6f 74 29 29 29 28 41 44 44 52 45 53 53 3d 28 50 " + /* ot)))(ADDRESS=(P */ - "52 4f 54 4f 43 4f 4c 3d 54 43 50 29 28 48 4f 53 " + /* ROTOCOL=TCP)(HOS */ - "54 3d 31 30 2e 31 2e 35 30 2e 31 34 29 28 50 4f " + /* T=10.1.50.14)(PO */ - "52 54 3d 31 35 32 31 29 29 29 ", /* RT=1521))) */ - ConnectHeader: &TNSHeader{Length: 0x00ca, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0, HeaderChecksum: 0}, - ConnectValue: &TNSConnect{ - Version: 0x013a, - MinVersion: 0x012c, - GlobalServiceOptions: SOHeaderChecksum | SOFullDuplex | SOUnknown0040 | SOUnknown0001, // (0x0c41) - SDU: 0x2000, - TDU: 0xffff, - ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 - MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, - DataLength: 0x0090, - DataOffset: 0x003A, - MaxResponseSize: 0x00000800, - ConnectFlags0: CFUnknown40 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFServicesWanted, - CrossFacility0: 0, - CrossFacility1: 0, - ConnectionID0: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{}, - ConnectionString: "(DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=ckdb)(CID=(PROGRAM=gsql)(HOST=McAfee)(USER=root)))(ADDRESS=(PROTOCOL=TCP)(HOST=10.1.50.14)(PORT=1521)))", - }, - AcceptEncoding: "00 20 00 00 02 00 00 00 01 39 00 00 08 00 7f ff " + /* . .......9...... */ - "01 00 00 00 00 20 61 61 00 00 00 00 00 00 00 00 ", /* ..... aa........ */ - AcceptHeader: &TNSHeader{Length: 0x0020, PacketChecksum: 0, Type: PacketTypeAccept, Flags: 0, HeaderChecksum: 0}, - AcceptValue: &TNSAccept{ - Version: 0x0139, - GlobalServiceOptions: 0, - SDU: 0x0800, - TDU: 0x7fff, - ByteOrder: DefaultByteOrder, - DataLength: 0, - DataOffset: 0x20, - ConnectFlags0: CFUnknown40 | CFUnknown20 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFUnknown20 | CFServicesWanted, - Unknown18: []byte{0, 0, 0, 0, 0, 0, 0, 0}, - AcceptData: []byte{}, - }, - }, - "02. 138-138": TestCaseOld{ - ConnectEncoding: "01 00 00 00 01 04 00 00 01 38 01 2c 00 00 08 00 " + /* .........8.,.... */ - "7f ff 86 0e 00 00 01 00 00 c6 00 3a 00 00 02 00 " + /* ...........:.... */ - "61 61 00 00 00 00 00 00 00 00 00 00 04 10 00 00 " + /* aa.............. */ - "00 03 00 00 00 00 00 00 00 00 28 44 45 53 43 52 " + /* ..........(DESCR */ - "49 50 54 49 4f 4e 3d 28 41 44 44 52 45 53 53 3d " + /* IPTION=(ADDRESS= */ - "28 50 52 4f 54 4f 43 4f 4c 3d 54 43 50 29 28 48 " + /* (PROTOCOL=TCP)(H */ - "4f 53 54 3d 31 39 32 2e 31 36 38 2e 31 2e 32 32 " + /* OST=192.168.1.22 */ - "31 29 28 50 4f 52 54 3d 31 35 32 31 29 29 28 43 " + /* 1)(PORT=1521))(C */ - "4f 4e 4e 45 43 54 5f 44 41 54 41 3d 28 53 49 44 " + /* ONNECT_DATA=(SID */ - "3d 76 6f 69 64 29 28 53 45 52 56 45 52 3d 44 45 " + /* =void)(SERVER=DE */ - "44 49 43 41 54 45 44 29 28 43 49 44 3d 28 50 52 " + /* DICATED)(CID=(PR */ - "4f 47 52 41 4d 3d 46 3a 5c 6f 72 61 63 6c 65 5c " + /* OGRAM=F:\oracle\ */ - "6f 72 61 39 32 5c 62 69 6e 5c 73 71 6c 70 6c 75 " + /* ora92\bin\sqlplu */ - "73 2e 65 78 65 29 28 48 4f 53 54 3d 46 41 4e 47 " + /* s.exe)(HOST=FANG */ - "48 4f 4e 47 5a 48 41 4f 29 28 55 53 45 52 3d 41 " + /* HONGZHAO)(USER=A */ - "64 6d 69 6e 69 73 74 72 61 74 6f 72 29 29 29 29 ", /* dministrator)))) */ - ConnectHeader: &TNSHeader{Length: 0x0100, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0x04, HeaderChecksum: 0}, - ConnectValue: &TNSConnect{ - Version: 0x0138, - MinVersion: 0x012c, - GlobalServiceOptions: 0, - SDU: 0x0800, - TDU: 0x7fff, - ProtocolCharacteristics: NTPCHangon | NTPCCallbackIO | NTPCAsyncIO | NTPCGenerateSIGURG | NTPCUrgentIO | NTPCFullDuplex, // 0x860e - MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, - DataLength: 0x00c6, - DataOffset: 0x003a, - MaxResponseSize: 0x00000200, - ConnectFlags0: CFUnknown40 | CFUnknown20 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFUnknown20 | CFServicesWanted, - CrossFacility0: 0, - CrossFacility1: 0, - ConnectionID0: [8]byte{0x00, 0x00, 0x04, 0x10, 0x00, 0x00, 0x00, 0x03}, - ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{}, - ConnectionString: "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.1.221)(PORT=1521))(CONNECT_DATA=(SID=void)(SERVER=DEDICATED)(CID=(PROGRAM=F:\\oracle\\ora92\\bin\\sqlplus.exe)(HOST=FANGHONGZHAO)(USER=Administrator))))", - }, - }, - "03. 138-138": TestCaseOld{ - ConnectEncoding: "00 ec 00 00 01 04 00 00 01 38 01 2c 00 00 08 00 " + /* .........8.,.... */ - "7f ff 86 0e 00 00 01 00 00 b2 00 3a 00 00 02 00 " + /* ...........:.... */ - "61 61 00 00 00 00 00 00 00 00 00 00 10 ec 00 00 " + /* aa.............. */ - "00 05 00 00 00 00 00 00 00 00 28 44 45 53 43 52 " + /* ..........(DESCR */ - "49 50 54 49 4f 4e 3d 28 41 44 44 52 45 53 53 3d " + /* IPTION=(ADDRESS= */ - "28 50 52 4f 54 4f 43 4f 4c 3d 54 43 50 29 28 48 " + /* (PROTOCOL=TCP)(H */ - "4f 53 54 3d 41 41 29 28 50 4f 52 54 3d 31 35 32 " + /* OST=AA)(PORT=152 */ - "31 29 29 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 " + /* 1))(CONNECT_DATA */ - "3d 28 53 49 44 3d 76 6f 69 64 29 28 53 45 52 56 " + /* =(SID=void)(SERV */ - "45 52 3d 44 45 44 49 43 41 54 45 44 29 28 43 49 " + /* ER=DEDICATED)(CI */ - "44 3d 28 50 52 4f 47 52 41 4d 3d 44 3a 5c 6f 72 " + /* D=(PROGRAM=D:\or */ - "61 63 6c 65 5c 6f 72 61 39 32 5c 62 69 6e 5c 73 " + /* acle\ora92\bin\s */ - "71 6c 70 6c 75 73 2e 65 78 65 29 28 48 4f 53 54 " + /* qlplus.exe)(HOST */ - "3d 48 49 4e 47 45 2d 48 41 4e 59 46 29 28 55 53 " + /* =HINGE-HANYF)(US */ - "45 52 3d 68 61 6e 79 66 29 29 29 29 ", /* ER=hanyf)))) */ - ConnectHeader: &TNSHeader{Length: 0x00EC, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0x04, HeaderChecksum: 0}, - ConnectValue: &TNSConnect{ - Version: 0x0138, - MinVersion: 0x012c, - GlobalServiceOptions: 0, - SDU: 0x0800, - TDU: 0x7fff, - ProtocolCharacteristics: NTPCHangon | NTPCCallbackIO | NTPCAsyncIO | NTPCGenerateSIGURG | NTPCUrgentIO | NTPCFullDuplex, // 0x860e - MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, - DataLength: 0x00b2, - DataOffset: 0x003a, - MaxResponseSize: 0x00000200, - ConnectFlags0: CFUnknown40 | CFUnknown20 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFUnknown20 | CFServicesWanted, - CrossFacility0: 0, - CrossFacility1: 0, - ConnectionID0: [8]byte{0x00, 0x00, 0x10, 0xec, 0x00, 0x00, 0x00, 0x05}, - ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{}, - ConnectionString: "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=AA)(PORT=1521))(CONNECT_DATA=(SID=void)(SERVER=DEDICATED)(CID=(PROGRAM=D:\\oracle\\ora92\\bin\\sqlplus.exe)(HOST=HINGE-HANYF)(USER=hanyf))))", - }, - }, - "unknown": TestCaseOld{ - ConnectEncoding: "00 d7 00 00 01 00 00 00 01 3b 01 2c 0c 41 20 00 " + /* .........;.,.A . */ - "ff ff 7f 08 00 00 01 00 00 91 00 46 00 00 08 00 " + /* ...........F.... */ - "41 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + /* AA.............. */ - "00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 20 " + /* ............ .. */ - "00 00 00 00 00 00 28 44 45 53 43 52 49 50 54 49 " + /* ......(DESCRIPTI */ - "4f 4e 3d 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 " + /* ON=(CONNECT_DATA */ - "3d 28 53 49 44 3d 6f 72 63 6c 31 31 67 29 28 43 " + /* =(SID=orcl11g)(C */ - "49 44 3d 28 50 52 4f 47 52 41 4d 3d 73 71 6c 70 " + /* ID=(PROGRAM=sqlp */ - "6c 75 73 40 6b 61 6c 69 29 28 48 4f 53 54 3d 6b " + /* lus@kali)(HOST=k */ - "61 6c 69 29 28 55 53 45 52 3d 72 6f 6f 74 29 29 " + /* ali)(USER=root)) */ - "29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f " + /* )(ADDRESS=(PROTO */ - "43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 31 30 " + /* COL=TCP)(HOST=10 */ - "2e 30 2e 37 32 2e 31 31 33 29 28 50 4f 52 54 3d " + /* .0.72.113)(PORT= */ - "31 35 32 31 29 29 29 ", - ConnectHeader: &TNSHeader{Length: 0x00d7, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0, HeaderChecksum: 0}, - ConnectValue: &TNSConnect{ - Version: 0x013b, - MinVersion: 0x012c, - GlobalServiceOptions: SOHeaderChecksum | SOFullDuplex | SOUnknown0040 | SOUnknown0001, // (0x0c41) - SDU: 0x2000, - TDU: 0xffff, - ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 - MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, - DataLength: 0x0091, - DataOffset: 0x0046, // points past the 12 bytes of \x00/\x20 at the start - MaxResponseSize: 0x00000800, - ConnectFlags0: CFUnknown40 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFServicesWanted, - CrossFacility0: 0, - CrossFacility1: 0, - ConnectionID0: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - ConnectionString: "(DESCRIPTION=(CONNECT_DATA=(SID=orcl11g)(CID=(PROGRAM=sqlplus@kali)(HOST=kali)(USER=root)))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.72.113)(PORT=1521)))", - }, - }, -} - -var oldValidTNSHeader = map[string]TNSHeader{ - "old1": TNSHeader{Length: 0x00d7, PacketChecksum: 0, Type: PacketTypeConnect, Flags: 0, HeaderChecksum: 0}, -} -var oldValidTNSConnect = map[string]TNSConnect{ - - "00 d7 00 00 01 00 00 00 01 3b 01 2c 0c 41 20 00 " + /* .........;.,.A . */ - "ff ff 7f 08 00 00 01 00 00 91 00 46 00 00 08 00 " + /* ...........F.... */ - "41 41 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " + /* AA.............. */ - "00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 20 " + /* ............ .. */ - "00 00 00 00 00 00 28 44 45 53 43 52 49 50 54 49 " + /* ......(DESCRIPTI */ - "4f 4e 3d 28 43 4f 4e 4e 45 43 54 5f 44 41 54 41 " + /* ON=(CONNECT_DATA */ - "3d 28 53 49 44 3d 6f 72 63 6c 31 31 67 29 28 43 " + /* =(SID=orcl11g)(C */ - "49 44 3d 28 50 52 4f 47 52 41 4d 3d 73 71 6c 70 " + /* ID=(PROGRAM=sqlp */ - "6c 75 73 40 6b 61 6c 69 29 28 48 4f 53 54 3d 6b " + /* lus@kali)(HOST=k */ - "61 6c 69 29 28 55 53 45 52 3d 72 6f 6f 74 29 29 " + /* ali)(USER=root)) */ - "29 28 41 44 44 52 45 53 53 3d 28 50 52 4f 54 4f " + /* )(ADDRESS=(PROTO */ - "43 4f 4c 3d 54 43 50 29 28 48 4f 53 54 3d 31 30 " + /* COL=TCP)(HOST=10 */ - "2e 30 2e 37 32 2e 31 31 33 29 28 50 4f 52 54 3d " + /* .0.72.113)(PORT= */ - "31 35 32 31 29 29 29 ": TNSConnect{ - Version: 0x013b, - MinVersion: 0x012c, - GlobalServiceOptions: SOHeaderChecksum | SOFullDuplex | SOUnknown0040 | SOUnknown0001, // (0x0c41) - SDU: 0x2000, - TDU: 0xffff, - ProtocolCharacteristics: NTPCConfirmedRelease | NTPCTDUBasedIO | NTPCSpawnerRunning | NTPCDataTest | NTPCCallbackIO | NTPCAsyncIO | NTPCPacketIO | NTPCGenerateSIGURG, // 0x7F08 - MaxBeforeAck: 0, - ByteOrder: DefaultByteOrder, - DataLength: 0x0091, - DataOffset: 0x0046, // points past the 12 bytes of \x00/\x20 at the start - MaxResponseSize: 0x00000800, - ConnectFlags0: CFUnknown40 | CFServicesWanted, - ConnectFlags1: CFUnknown40 | CFServicesWanted, - CrossFacility0: 0, - CrossFacility1: 0, - ConnectionID0: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - ConnectionID1: [8]byte{0, 0, 0, 0, 0, 0, 0, 0}, - Unknown3A: []byte{0x00, 0x00, 0x20, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - ConnectionString: "(DESCRIPTION=(CONNECT_DATA=(SID=orcl11g)(CID=(PROGRAM=sqlplus@kali)(HOST=kali)(USER=root)))(ADDRESS=(PROTOCOL=TCP)(HOST=10.0.72.113)(PORT=1521)))", - }, -} - -func str(a interface{}) string { - temp := "" - switch v := a.(type) { - case []byte: - temp = string(v) - case string: - temp = v - default: - temp = string(serialize(a)) - } - - if len(temp) > 23 { - return temp[0:10] + "..." + temp[len(temp)-10:] - } - return temp -} - -func assertEqualBytes(t *testing.T, expected []byte, actual []byte) { - if !bytes.Equal(expected, actual) { - t.Errorf("Mismatch: expected %s, got %s", str(expected), str(actual)) - } -} - func serialize(val interface{}) []byte { ret, err := json.Marshal(val) if err != nil { @@ -659,10 +442,6 @@ func serialize(val interface{}) []byte { return ret } -func assertEqualElementwise(t *testing.T, expected interface{}, actual interface{}) { - assertEqualBytes(t, serialize(expected), serialize(actual)) -} - func min(a, b int) int { if a < b { return a @@ -670,46 +449,17 @@ func min(a, b int) int { return b } -func getExpectedActual(expected string, actual string) string { - lex := len(expected) - lac := len(actual) - - if lex < 20 && lac < 20 { - return fmt.Sprintf("Expected [%s], got [%s]", expected, actual) - } - if math.Abs(float64(lex-lac)/float64(min(lex, lac))) > 0.5 { - return fmt.Sprintf("Expected [%s] - %d bytes, got [%s] - %d bytes", str(expected), lex, str(actual), lac) - } - - firstDiff := lex - for i := 0; i < min(lex, lac); i++ { - ex := byte(0) - ac := byte(0) - if i < lac { - ac = actual[i] - } - if i < lac { - ex = expected[i] - } - if ex != ac { - firstDiff = i - break - } - } - if firstDiff < 10 || (firstDiff > lac-10) || (firstDiff > lex-10) { - return fmt.Sprintf("Expected [%s...%s], got [%s...%s]", str(expected[0:10]), str(expected[lex-10:]), str(actual[0:10]), str(actual[lac-10:])) - } - return fmt.Sprintf("Expected [%s...%s...%s], got [%s...%s...%s] (first diff @ %d)", expected[0:10], expected[firstDiff-50:firstDiff+50], expected[lex-10:], actual[0:10], actual[firstDiff-50:firstDiff+50], actual[lac-10:], firstDiff) -} +// TODO: TNSRedirect +// TODO: Invalid cases func TestTNSHeaderEncode(t *testing.T) { for hex, header := range validHeaders { bin := fromHex(hex) encoded := header.Encode() if !bytes.Equal(bin, encoded) { - t.Errorf("TNSHeader.Encode mismatch: %s", getExpectedActual(string(bin), string(encoded))) + t.Errorf("TNSHeader.Encode mismatch:[\n%s\n]", interleave(bin, encoded)) } - decoded, rest, err := DecodeTNSHeader(nil, bin) + decoded, rest, err := DecodeTNSHeader(bin) if err != nil { t.Fatalf("Decode error: %v", err) } @@ -719,7 +469,7 @@ func TestTNSHeaderEncode(t *testing.T) { jsonHeader := serialize(header) jsonDecoded := serialize(decoded) if !bytes.Equal(jsonHeader, jsonDecoded) { - t.Errorf("TNSHeader.Read mismatch: %s", getExpectedActual(string(jsonHeader), string(jsonDecoded))) + t.Errorf("TNSHeader.Read mismatch:[\n%s\n]", interleave(jsonHeader, jsonDecoded)) } } } @@ -729,7 +479,7 @@ func TestTNSConnect(t *testing.T) { bin := fromHex(info.Encoding) encoded := info.Value.Encode() if !bytes.Equal(bin, encoded) { - t.Errorf("%s: TNSConnect.Encode mismatch: %s", tag, getExpectedActual(string(bin), string(encoded))) + t.Errorf("%s: TNSConnect.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded)) } reader := getSliceReader(bin) response, err := ReadTNSPacket(reader) @@ -744,7 +494,7 @@ func TestTNSConnect(t *testing.T) { jsonPacket := serialize(info.Value.Body) jsonDecoded := serialize(decoded) if !bytes.Equal(jsonPacket, jsonDecoded) { - t.Errorf("%s: TNSConnect.Read mismatch: %s", tag, getExpectedActual(string(jsonPacket), string(jsonDecoded))) + t.Errorf("%s: TNSConnect.Read mismatch:[\n%s\n]", tag, interleave(jsonPacket, jsonDecoded)) } if len(reader.Data) > 0 { t.Errorf("%s: TNSConnect.Read: %d bytes left over", tag, len(reader.Data)) @@ -757,7 +507,7 @@ func TestTNSAccept(t *testing.T) { bin := fromHex(info.Encoding) encoded := info.Value.Encode() if !bytes.Equal(bin, encoded) { - t.Errorf("%s: TNSAccept.Encode mismatch: %s", tag, getExpectedActual(string(bin), string(encoded))) + t.Errorf("%s: TNSAccept.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded)) } reader := getSliceReader(bin) response, err := ReadTNSPacket(reader) @@ -772,7 +522,7 @@ func TestTNSAccept(t *testing.T) { jsonPacket := serialize(info.Value.Body) jsonDecoded := serialize(decoded) if !bytes.Equal(jsonPacket, jsonDecoded) { - t.Errorf("%s: TNSAccept.Read mismatch: %s", tag, getExpectedActual(string(jsonPacket), string(jsonDecoded))) + t.Errorf("%s: TNSAccept.Read mismatch:[\n%s\n]", tag, interleave(jsonPacket, jsonDecoded)) } if len(reader.Data) > 0 { t.Errorf("%s: TNSAccept.Read: %d bytes left over", tag, len(reader.Data)) @@ -785,12 +535,7 @@ func TestTNSData(t *testing.T) { bin := fromHex(info.Encoding) encoded := info.Value.Encode() if !bytes.Equal(bin, encoded) { - expected := hex.Dump(bin) - actual := hex.Dump(encoded) - logrus.Warnf("Expected: {[\n%s\n]}\n", expected) - logrus.Warnf("Actual: {[\n%s\n]}\n", actual) - logrus.Warnf("{[\n%s\n]}\n", interleave(bin, encoded)) - t.Errorf("%s: TNSData.Encode mismatch: %s", tag, getExpectedActual(string(bin), string(encoded))) + t.Errorf("%s: TNSData.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded)) } reader := getSliceReader(bin) response, err := ReadTNSPacket(reader) @@ -805,94 +550,10 @@ func TestTNSData(t *testing.T) { jsonPacket := serialize(info.Value.Body) jsonDecoded := serialize(decoded) if !bytes.Equal(jsonPacket, jsonDecoded) { - t.Errorf("%s: TNSData.Read mismatch: %s", tag, getExpectedActual(string(jsonPacket), string(jsonDecoded))) + t.Errorf("%s: TNSData.Read mismatch:[\n%s\n]", tag, interleave(jsonPacket, jsonDecoded)) } if len(reader.Data) > 0 { t.Errorf("%s: TNSData.Read: %d bytes left over", tag, len(reader.Data)) } } } - -func TestTNSConnect2(t *testing.T) { - for tag, info := range old1ValidTNSConnect { - bin := fromHex(info.ConnectEncoding) - if info.ConnectValue != nil { - packet := &TNSPacket{ - Header: info.ConnectHeader, - Body: info.ConnectValue, - } - encoded := packet.Encode() - if !bytes.Equal(bin, encoded) { - t.Errorf("%s: TNSConnect.Encode mismatch: %s", tag, getExpectedActual(string(bin), string(encoded))) - } - reader := getSliceReader(bin) - response, err := ReadTNSPacket(reader) - if err != nil { - t.Fatalf("%s: Error reading TNSConnect packet: %v", tag, err) - } - // TODO: check header - decoded, ok := response.Body.(*TNSConnect) - if !ok { - t.Fatalf("%s: Read wrong packet: %v", tag, response.Body) - } - jsonPacket := serialize(packet.Body) - jsonDecoded := serialize(decoded) - if !bytes.Equal(jsonPacket, jsonDecoded) { - t.Errorf("%s: TNSConnect.Read mismatch: %s", tag, getExpectedActual(string(jsonPacket), string(jsonDecoded))) - } - if len(reader.Data) > 0 { - t.Errorf("%s: TNSConnect.Read: %d bytes left over", tag, len(reader.Data)) - } - } - } -} - -func TestTNSAccept2(t *testing.T) { - for tag, info := range old1ValidTNSConnect { - if info.AcceptValue == nil { - fmt.Println("skipping ", tag) - } else { - fmt.Println("testing ", tag) - bin := fromHex(info.AcceptEncoding) - packet := &TNSPacket{ - Header: info.AcceptHeader, - Body: info.AcceptValue, - } - encoded := packet.Encode() - if !bytes.Equal(bin, encoded) { - fmt.Println("expected=[\n", string(hex.Dump(bin)), "]") - fmt.Println(" actual=[\n", string(hex.Dump(encoded)), "]") - t.Errorf("%s: TNSAccept.Encode mismatch: %s", tag, getExpectedActual(string(bin), string(encoded))) - } - reader := getSliceReader(bin) - response, err := ReadTNSPacket(reader) - if err != nil { - t.Fatalf("%s: Error reading TNSAccept packet: %v", tag, err) - } - // TODO: check header - decoded, ok := response.Body.(*TNSAccept) - if !ok { - t.Fatalf("%s: Read wrong packet: %v", tag, response.Body) - } - jsonPacket := serialize(packet.Body) - jsonDecoded := serialize(decoded) - if !bytes.Equal(jsonPacket, jsonDecoded) { - dump(tag+":expected", packet.Body) - dump(tag+":actual", decoded) - t.Errorf("%s: TNSAccept.Read mismatch: %s", tag, getExpectedActual(string(jsonPacket), string(jsonDecoded))) - } - if len(reader.Data) > 0 { - t.Errorf("%s: TNSAccept.Read: %d bytes left over", tag, len(reader.Data)) - } - } - } -} - -func dump(tag string, a interface{}) { - j, err := json.MarshalIndent(a, "", " ") - if err != nil { - fmt.Println("Error encoding:", err) - return - } - fmt.Println(tag + " [[[" + string(j) + "]]]") -}