clean up output; add ReleaseVersion tests
This commit is contained in:
parent
f61e698ea3
commit
6fbbc0a182
@ -1,7 +1,6 @@
|
|||||||
package oracle
|
package oracle
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
@ -18,9 +17,13 @@ type HandshakeLog struct {
|
|||||||
// server returns in the Accept packet.
|
// server returns in the Accept packet.
|
||||||
GlobalServiceOptions map[string]bool `json:"global_service_options,omitempty"`
|
GlobalServiceOptions map[string]bool `json:"global_service_options,omitempty"`
|
||||||
|
|
||||||
// ConnectFlags is the ste of ConnectFlags values that the server returns
|
// ConnectFlags0 is the first set of ConnectFlags values that the server
|
||||||
// in the Accept packet. Both are included in the array.
|
// returns in the Accept packet for the first.
|
||||||
ConnectFlags [2]map[string]bool `json:"connect_flags,omitempty"`
|
ConnectFlags0 map[string]bool `json:"connect_flags0,omitempty"`
|
||||||
|
|
||||||
|
// ConnectFlags1 is the second set of ConnectFlags values that the server
|
||||||
|
// returns in the Accept packet for the first.
|
||||||
|
ConnectFlags1 map[string]bool `json:"connect_flags1,omitempty"`
|
||||||
|
|
||||||
// DidResend is true if the server sent a Resend packet in response to the
|
// DidResend is true if the server sent a Resend packet in response to the
|
||||||
// client's first Connect packet.
|
// client's first Connect packet.
|
||||||
@ -28,15 +31,28 @@ type HandshakeLog struct {
|
|||||||
|
|
||||||
// RedirectTarget is the connect descriptor returned by the server in the
|
// RedirectTarget is the connect descriptor returned by the server in the
|
||||||
// Redirect packet, if one is sent. Otherwise it is empty/omitted.
|
// Redirect packet, if one is sent. Otherwise it is empty/omitted.
|
||||||
RedirectTarget string `json:"redirect_target,omitempty"`
|
RedirectTargetRaw string `json:"redirect_target_raw,omitempty"`
|
||||||
|
|
||||||
// RefuseError is the Data from the Refuse packet returned by the server;
|
RedirectTarget Descriptor `json:"redirect_target,omitempty"`
|
||||||
|
|
||||||
|
// RefuseErrorRaW is the Data from the Refuse packet returned by the server;
|
||||||
// it is empty if the server does not return a Refuse packet.
|
// it is empty if the server does not return a Refuse packet.
|
||||||
RefuseError string `json:"refuse_error,omitempty"`
|
RefuseErrorRaw string `json:"refuse_error_raw,omitempty"`
|
||||||
|
|
||||||
// RefuseReason describes the reason the request was refused as given in the
|
RefuseError Descriptor `json:"refuse_error,omitempty"`
|
||||||
// Refuse packet from the server. TODO: finalize format.
|
|
||||||
RefuseReason string `json:"refuse_reason,omitempty"`
|
// RefuseReasonApp is the "AppReason" returned by the server in a Refused
|
||||||
|
// response packet.
|
||||||
|
RefuseReasonApp string `json:"refuse_reason_app,omitempty"`
|
||||||
|
|
||||||
|
// RefuseReasonSys is the "SysReason" returned by the server in a Refused
|
||||||
|
// response packet.
|
||||||
|
RefuseReasonSys string `json:"refuse_reason_sys,omitempty"`
|
||||||
|
|
||||||
|
// RefuseVersion is the parsed DESCRIPTION.VSNNUM field from the RefuseError
|
||||||
|
// string returned by the server in the Refuse packet, in dotted-decimal
|
||||||
|
// format.
|
||||||
|
RefuseVersion string `json:"refuse_version,omitempty"`
|
||||||
|
|
||||||
// DidResend is set to true if the server sent a Resend packet after the
|
// DidResend is set to true if the server sent a Resend packet after the
|
||||||
// first Connect packet.
|
// first Connect packet.
|
||||||
@ -84,7 +100,10 @@ func (conn *Connection) readPacket() (*TNSPacket, error) {
|
|||||||
// Automatically handles Resend responses; the caller is responsible for
|
// Automatically handles Resend responses; the caller is responsible for
|
||||||
// handling other exceptional cases.
|
// handling other exceptional cases.
|
||||||
func (conn *Connection) SendPacket(packet TNSPacketBody) (TNSPacketBody, error) {
|
func (conn *Connection) SendPacket(packet TNSPacketBody) (TNSPacketBody, error) {
|
||||||
toSend := conn.tnsDriver.EncodePacket(&TNSPacket{Body: packet})
|
toSend, err := conn.tnsDriver.EncodePacket(&TNSPacket{Body: packet})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := conn.send(toSend); err != nil {
|
if err := conn.send(toSend); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -162,13 +181,26 @@ func (conn *Connection) Connect(connectDescriptor string) (*HandshakeLog, error)
|
|||||||
case *TNSAccept:
|
case *TNSAccept:
|
||||||
accept = resp
|
accept = resp
|
||||||
case *TNSRedirect:
|
case *TNSRedirect:
|
||||||
result.RedirectTarget = string(resp.Data)
|
result.RedirectTargetRaw = string(resp.Data)
|
||||||
|
if parsed, err := DecodeDescriptor(result.RedirectTargetRaw); err != nil {
|
||||||
|
result.RedirectTarget = parsed
|
||||||
|
}
|
||||||
// TODO: Follow redirects?
|
// TODO: Follow redirects?
|
||||||
return &result, nil
|
return &result, nil
|
||||||
case *TNSRefuse:
|
case *TNSRefuse:
|
||||||
result.RefuseError = string(resp.Data)
|
result.RefuseErrorRaw = string(resp.Data)
|
||||||
// TODO: do better
|
result.RefuseReasonApp = resp.AppReason.String()
|
||||||
result.RefuseReason = fmt.Sprintf("AppReason: 0x%02x; SysReason: 0x%02x", resp.AppReason, resp.SysReason)
|
result.RefuseReasonSys = resp.SysReason.String()
|
||||||
|
if desc, err := DecodeDescriptor(result.RefuseErrorRaw); err == nil {
|
||||||
|
result.RefuseError = desc
|
||||||
|
if versions := desc.GetValues("DESCRIPTION.VSNNUM"); len(versions) > 0 {
|
||||||
|
// If there are multiple VSNNUMs, we only care about the first.
|
||||||
|
decVersion := versions[0]
|
||||||
|
if intVersion, err := strconv.ParseUint(decVersion, 10, 32); err == nil {
|
||||||
|
result.RefuseVersion = ReleaseVersion(intVersion).String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return &result, nil
|
return &result, nil
|
||||||
default:
|
default:
|
||||||
return &result, ErrUnexpectedResponse
|
return &result, ErrUnexpectedResponse
|
||||||
@ -177,12 +209,13 @@ func (conn *Connection) Connect(connectDescriptor string) (*HandshakeLog, error)
|
|||||||
// TODO: Unclear what these do. Taken from my client.
|
// TODO: Unclear what these do. Taken from my client.
|
||||||
result.AcceptVersion = accept.Version
|
result.AcceptVersion = accept.Version
|
||||||
result.GlobalServiceOptions = accept.GlobalServiceOptions.Set()
|
result.GlobalServiceOptions = accept.GlobalServiceOptions.Set()
|
||||||
result.ConnectFlags = [2]map[string]bool{
|
result.ConnectFlags0 = accept.ConnectFlags0.Set()
|
||||||
accept.ConnectFlags0.Set(),
|
result.ConnectFlags1 = accept.ConnectFlags1.Set()
|
||||||
accept.ConnectFlags1.Set(),
|
|
||||||
}
|
|
||||||
// uint32 PID + uint32 ??
|
// uint32 PID + uint32 ??
|
||||||
supervisorBytes0 := []byte{0x00, 0x00, 0x04, 0xec, 0x19, 0x2c, 0x7b, 0x4c}
|
// In real clients, seems to be a small u32 followed by some kind of u32
|
||||||
|
// counter/timestamp.
|
||||||
|
supervisorBytes0 := []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
supervisorBytes1 := []byte{
|
supervisorBytes1 := []byte{
|
||||||
0xde, 0xad, 0xbe, 0xef,
|
0xde, 0xad, 0xbe, 0xef,
|
||||||
|
@ -153,7 +153,7 @@ type TNSDriver struct {
|
|||||||
// EncodePacket encodes the packet (header + body). If header is nil, create one
|
// EncodePacket encodes 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
|
// 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).
|
// it to the appropriate value (length of encoded body + 8).
|
||||||
func (driver *TNSDriver) EncodePacket(packet *TNSPacket) []byte {
|
func (driver *TNSDriver) EncodePacket(packet *TNSPacket) ([]byte, error) {
|
||||||
body := packet.Body.Encode()
|
body := packet.Body.Encode()
|
||||||
if packet.Header == nil {
|
if packet.Header == nil {
|
||||||
packet.Header = &TNSHeader{
|
packet.Header = &TNSHeader{
|
||||||
@ -170,7 +170,7 @@ func (driver *TNSDriver) EncodePacket(packet *TNSPacket) []byte {
|
|||||||
// It is up to the user to check the body length for overflows before calling Encode
|
// It is up to the user to check the body length for overflows before calling Encode
|
||||||
if driver.Mode == TNSModeOld {
|
if driver.Mode == TNSModeOld {
|
||||||
if (len(body) + 8) > 0xffff {
|
if (len(body) + 8) > 0xffff {
|
||||||
panic(fmt.Errorf("Body too large to fit into 16-bit length (%d bytes)", len(body)))
|
return nil, ErrInvalidInput
|
||||||
}
|
}
|
||||||
packet.Header.Length = uint32(len(body) + 8)
|
packet.Header.Length = uint32(len(body) + 8)
|
||||||
} else {
|
} else {
|
||||||
@ -178,7 +178,7 @@ func (driver *TNSDriver) EncodePacket(packet *TNSPacket) []byte {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
header := packet.Header.Encode()
|
header := packet.Header.Encode()
|
||||||
return append(header, body...)
|
return append(header, body...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TNSFlags is the type for the TNS header's flags.
|
// TNSFlags is the type for the TNS header's flags.
|
||||||
@ -831,6 +831,11 @@ func ReadTNSAccept(reader io.Reader, header *TNSHeader) (*TNSAccept, error) {
|
|||||||
// TODO: details.
|
// TODO: details.
|
||||||
type RefuseReason uint8
|
type RefuseReason uint8
|
||||||
|
|
||||||
|
func (reason RefuseReason) String() string {
|
||||||
|
// TODO: Get better const error mappings. AppReason = 0x22 = syntax error?
|
||||||
|
return fmt.Sprintf("0x%02x", uint8(reason))
|
||||||
|
}
|
||||||
|
|
||||||
// TNSRefuse is returned by the server when (...TODO: details -- not returned on
|
// TNSRefuse is returned by the server when (...TODO: details -- not returned on
|
||||||
// failed auth from TNSConnect).
|
// failed auth from TNSConnect).
|
||||||
type TNSRefuse struct {
|
type TNSRefuse struct {
|
||||||
|
@ -2,6 +2,7 @@ package oracle
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -482,7 +483,10 @@ func TestTNSConnect(t *testing.T) {
|
|||||||
driver := getTNSDriver()
|
driver := getTNSDriver()
|
||||||
for tag, info := range validTNSConnect {
|
for tag, info := range validTNSConnect {
|
||||||
bin := fromHex(info.Encoding)
|
bin := fromHex(info.Encoding)
|
||||||
encoded := driver.EncodePacket(info.Value)
|
encoded, err := driver.EncodePacket(info.Value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: TNSConnect Error encoding packet: %v", tag, err)
|
||||||
|
}
|
||||||
if !bytes.Equal(bin, encoded) {
|
if !bytes.Equal(bin, encoded) {
|
||||||
t.Errorf("%s: TNSConnect.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
t.Errorf("%s: TNSConnect.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
||||||
}
|
}
|
||||||
@ -511,7 +515,10 @@ func TestTNSAccept(t *testing.T) {
|
|||||||
driver := getTNSDriver()
|
driver := getTNSDriver()
|
||||||
for tag, info := range validTNSAccept {
|
for tag, info := range validTNSAccept {
|
||||||
bin := fromHex(info.Encoding)
|
bin := fromHex(info.Encoding)
|
||||||
encoded := driver.EncodePacket(info.Value)
|
encoded, err := driver.EncodePacket(info.Value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: TNSAccept Error encoding packet: %v", tag, err)
|
||||||
|
}
|
||||||
if !bytes.Equal(bin, encoded) {
|
if !bytes.Equal(bin, encoded) {
|
||||||
t.Errorf("%s: TNSAccept.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
t.Errorf("%s: TNSAccept.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
||||||
}
|
}
|
||||||
@ -540,7 +547,10 @@ func TestTNSData(t *testing.T) {
|
|||||||
driver := getTNSDriver()
|
driver := getTNSDriver()
|
||||||
for tag, info := range validTNSData {
|
for tag, info := range validTNSData {
|
||||||
bin := fromHex(info.Encoding)
|
bin := fromHex(info.Encoding)
|
||||||
encoded := driver.EncodePacket(info.Value)
|
encoded, err := driver.EncodePacket(info.Value)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: TNSData Error encoding packet: %v", tag, err)
|
||||||
|
}
|
||||||
if !bytes.Equal(bin, encoded) {
|
if !bytes.Equal(bin, encoded) {
|
||||||
t.Errorf("%s: TNSData.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
t.Errorf("%s: TNSData.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
||||||
}
|
}
|
||||||
@ -753,3 +763,52 @@ func TestDecodeDescriptor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var releaseVersions = map[string]ReleaseVersion{
|
||||||
|
"1.2.3.4.5": ReleaseVersion(0x01230405),
|
||||||
|
"0.0.0.0.0": ReleaseVersion(0),
|
||||||
|
"255.15.15.255.255": ReleaseVersion(0xFFFFFFFF),
|
||||||
|
}
|
||||||
|
|
||||||
|
var badReleaseVersions = []string{
|
||||||
|
"",
|
||||||
|
"1",
|
||||||
|
"1.2",
|
||||||
|
"1.2.3",
|
||||||
|
"1.2.3.4",
|
||||||
|
"256.0.0.0.0",
|
||||||
|
"0.16.0.0.0",
|
||||||
|
"0.0.16.0.0",
|
||||||
|
"0.0.0.256.0",
|
||||||
|
"0.0.0.0.256",
|
||||||
|
"a.b.c.d.e",
|
||||||
|
"A.B.C.D.E",
|
||||||
|
"p.q.r.s.t",
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReleaseVersion(t *testing.T) {
|
||||||
|
expectedBytes := make([]byte, 4)
|
||||||
|
for stringValue, version := range releaseVersions {
|
||||||
|
actualString := version.String()
|
||||||
|
if stringValue != actualString {
|
||||||
|
t.Errorf("ReleaseVersion.String() failed: 0x%08x gave %s, expected %s", uint32(version), actualString, stringValue)
|
||||||
|
}
|
||||||
|
binary.BigEndian.PutUint32(expectedBytes, uint32(version))
|
||||||
|
actualBytes := version.Bytes()
|
||||||
|
if !bytes.Equal(expectedBytes, actualBytes) {
|
||||||
|
t.Errorf("ReleaseVersion.Bytes() failed: 0x%08x gave %v, expected %v", uint32(version), actualBytes, expectedBytes)
|
||||||
|
}
|
||||||
|
encoded, err := EncodeReleaseVersion(stringValue)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("EncodeReleaseVersion(%s) failed: %v", stringValue, err)
|
||||||
|
}
|
||||||
|
if encoded != version {
|
||||||
|
t.Errorf("EncodeReleaseVersion(%s) failed: got 0x%08x, expected 0x%08x", stringValue, uint32(encoded), uint32(version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, bad := range badReleaseVersions {
|
||||||
|
if ret, err := EncodeReleaseVersion(bad); err == nil {
|
||||||
|
t.Errorf("Successfully encoded bad ReleaseVersion %s: 0x%08x", bad, uint32(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -40,23 +40,32 @@ connect_flags = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
nsn_services = [
|
nsn_services = [
|
||||||
"Authentication",
|
"Authentication",
|
||||||
"Encryption",
|
"Encryption",
|
||||||
"DataIntegrity",
|
"DataIntegrity",
|
||||||
"Supervisor",
|
"Supervisor",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
parsed_descriptor = ListOf(SubRecord({
|
||||||
|
"key": String(),
|
||||||
|
"value": String(),
|
||||||
|
}))
|
||||||
|
|
||||||
oracle_scan_response = SubRecord({
|
oracle_scan_response = SubRecord({
|
||||||
"result": SubRecord({
|
"result": SubRecord({
|
||||||
"handshake": SubRecord({
|
"handshake": SubRecord({
|
||||||
"accept_version": Unsigned16BitInteger(),
|
"accept_version": Unsigned16BitInteger(),
|
||||||
"global_service_options": flagsSet(global_service_options),
|
"global_service_options": flagsSet(global_service_options),
|
||||||
"connect_flags": List(flagsSet(connect_flags)),
|
"connect_flags0": flagsSet(connect_flags),
|
||||||
|
"connect_flags1": flagsSet(connect_flags),
|
||||||
"did_resend": Boolean(),
|
"did_resend": Boolean(),
|
||||||
"redirect_target": String(),
|
"redirect_target_raw": String(),
|
||||||
"refuse_error": String(),
|
"redirect_target": parsed_descriptor,
|
||||||
# TODO: Finalize format of refuse_reason
|
"refuse_error_raw": String(),
|
||||||
"refuse_reason": String(),
|
"refuse_error": parsed_descriptor,
|
||||||
|
"refuse_version": String(),
|
||||||
|
"refuse_reason_app": String(),
|
||||||
|
"refuse_reason_sys": String(),
|
||||||
"nsn_version": String(),
|
"nsn_version": String(),
|
||||||
"nsn_service_versions": SubRecord({
|
"nsn_service_versions": SubRecord({
|
||||||
"Authentication": String(),
|
"Authentication": String(),
|
||||||
|
Loading…
Reference in New Issue
Block a user