clean up output; add ReleaseVersion tests
This commit is contained in:
parent
f61e698ea3
commit
6fbbc0a182
@ -1,7 +1,6 @@
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
@ -18,9 +17,13 @@ type HandshakeLog struct {
|
||||
// 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"`
|
||||
// ConnectFlags0 is the first set of ConnectFlags values that the server
|
||||
// returns in the Accept packet for the first.
|
||||
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
|
||||
// client's first Connect packet.
|
||||
@ -28,15 +31,28 @@ type HandshakeLog struct {
|
||||
|
||||
// RedirectTarget is the connect descriptor returned by the server in the
|
||||
// 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.
|
||||
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
|
||||
// Refuse packet from the server. TODO: finalize format.
|
||||
RefuseReason string `json:"refuse_reason,omitempty"`
|
||||
RefuseError Descriptor `json:"refuse_error,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
|
||||
// first Connect packet.
|
||||
@ -84,7 +100,10 @@ func (conn *Connection) readPacket() (*TNSPacket, error) {
|
||||
// Automatically handles Resend responses; the caller is responsible for
|
||||
// handling other exceptional cases.
|
||||
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 {
|
||||
return nil, err
|
||||
@ -162,13 +181,26 @@ func (conn *Connection) Connect(connectDescriptor string) (*HandshakeLog, error)
|
||||
case *TNSAccept:
|
||||
accept = resp
|
||||
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?
|
||||
return &result, nil
|
||||
case *TNSRefuse:
|
||||
result.RefuseError = string(resp.Data)
|
||||
// TODO: do better
|
||||
result.RefuseReason = fmt.Sprintf("AppReason: 0x%02x; SysReason: 0x%02x", resp.AppReason, resp.SysReason)
|
||||
result.RefuseErrorRaw = string(resp.Data)
|
||||
result.RefuseReasonApp = resp.AppReason.String()
|
||||
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
|
||||
default:
|
||||
return &result, ErrUnexpectedResponse
|
||||
@ -177,12 +209,13 @@ func (conn *Connection) Connect(connectDescriptor string) (*HandshakeLog, error)
|
||||
// TODO: Unclear what these do. Taken from my client.
|
||||
result.AcceptVersion = accept.Version
|
||||
result.GlobalServiceOptions = accept.GlobalServiceOptions.Set()
|
||||
result.ConnectFlags = [2]map[string]bool{
|
||||
accept.ConnectFlags0.Set(),
|
||||
accept.ConnectFlags1.Set(),
|
||||
}
|
||||
result.ConnectFlags0 = accept.ConnectFlags0.Set()
|
||||
result.ConnectFlags1 = accept.ConnectFlags1.Set()
|
||||
|
||||
// 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{
|
||||
0xde, 0xad, 0xbe, 0xef,
|
||||
|
@ -153,7 +153,7 @@ type TNSDriver struct {
|
||||
// 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
|
||||
// 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()
|
||||
if packet.Header == nil {
|
||||
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
|
||||
if driver.Mode == TNSModeOld {
|
||||
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)
|
||||
} else {
|
||||
@ -178,7 +178,7 @@ func (driver *TNSDriver) EncodePacket(packet *TNSPacket) []byte {
|
||||
}
|
||||
}
|
||||
header := packet.Header.Encode()
|
||||
return append(header, body...)
|
||||
return append(header, body...), nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
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
|
||||
// failed auth from TNSConnect).
|
||||
type TNSRefuse struct {
|
||||
|
@ -2,6 +2,7 @@ package oracle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
@ -482,7 +483,10 @@ func TestTNSConnect(t *testing.T) {
|
||||
driver := getTNSDriver()
|
||||
for tag, info := range validTNSConnect {
|
||||
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) {
|
||||
t.Errorf("%s: TNSConnect.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
||||
}
|
||||
@ -511,7 +515,10 @@ func TestTNSAccept(t *testing.T) {
|
||||
driver := getTNSDriver()
|
||||
for tag, info := range validTNSAccept {
|
||||
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) {
|
||||
t.Errorf("%s: TNSAccept.Encode mismatch:[\n%s\n]", tag, interleave(bin, encoded))
|
||||
}
|
||||
@ -540,7 +547,10 @@ func TestTNSData(t *testing.T) {
|
||||
driver := getTNSDriver()
|
||||
for tag, info := range validTNSData {
|
||||
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) {
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -46,17 +46,26 @@ nsn_services = [
|
||||
"Supervisor",
|
||||
]
|
||||
|
||||
parsed_descriptor = ListOf(SubRecord({
|
||||
"key": String(),
|
||||
"value": String(),
|
||||
}))
|
||||
|
||||
oracle_scan_response = SubRecord({
|
||||
"result": SubRecord({
|
||||
"handshake": SubRecord({
|
||||
"accept_version": Unsigned16BitInteger(),
|
||||
"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(),
|
||||
"redirect_target": String(),
|
||||
"refuse_error": String(),
|
||||
# TODO: Finalize format of refuse_reason
|
||||
"refuse_reason": String(),
|
||||
"redirect_target_raw": String(),
|
||||
"redirect_target": parsed_descriptor,
|
||||
"refuse_error_raw": String(),
|
||||
"refuse_error": parsed_descriptor,
|
||||
"refuse_version": String(),
|
||||
"refuse_reason_app": String(),
|
||||
"refuse_reason_sys": String(),
|
||||
"nsn_version": String(),
|
||||
"nsn_service_versions": SubRecord({
|
||||
"Authentication": String(),
|
||||
|
Loading…
Reference in New Issue
Block a user