Skip to content

Commit 7eccc4c

Browse files
authored
Merge pull request #1272 from TheManticoreProject/enhancement-qmcomm2-qmmgmt-hresult
Route qmcomm2 and qmmgmt status reporting through hresult (Refs #1219)
2 parents 67c8fd3 + d569c75 commit 7eccc4c

7 files changed

Lines changed: 148 additions & 48 deletions

File tree

network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0/functions/00_R_QMMgmtGetInfo.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
qmmgmt "github.com/TheManticoreProject/Manticore/network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0"
1212
"github.com/TheManticoreProject/Manticore/network/dcerpc/ndr"
13+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
1314
msmqmq "github.com/TheManticoreProject/Manticore/windows/protocols/ms-mqmq"
1415
msmqmr "github.com/TheManticoreProject/Manticore/windows/protocols/ms-mqmr"
1516
)
@@ -51,8 +52,8 @@ func R_QMMgmtGetInfo(rpc ndr.Invoker, pObjectFormat msmqmr.MGMT_OBJECT, cp ndr.D
5152
return
5253
}
5354
ApVar = resp.ApVar
54-
if uint32(resp.Status) != qmmgmt.StatusSuccess {
55-
err = fmt.Errorf("R_QMMgmtGetInfo failed: %s", qmmgmt.StatusString(uint32(resp.Status)))
55+
if status := hresult.HRESULT(resp.Status); status != hresult.S_OK {
56+
err = fmt.Errorf("R_QMMgmtGetInfo failed: %s", qmmgmt.StatusString(uint32(status)))
5657
}
5758
return
5859
}

network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0/functions/01_R_QMMgmtAction.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
qmmgmt "github.com/TheManticoreProject/Manticore/network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0"
1212
"github.com/TheManticoreProject/Manticore/network/dcerpc/ndr"
13+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
1314
msmqmr "github.com/TheManticoreProject/Manticore/windows/protocols/ms-mqmr"
1415
)
1516

@@ -42,8 +43,8 @@ func R_QMMgmtAction(rpc ndr.Invoker, pObjectFormat msmqmr.MGMT_OBJECT, action st
4243
if err := rpc.Invoke(req, &resp); err != nil {
4344
return fmt.Errorf("R_QMMgmtAction: %w", err)
4445
}
45-
if uint32(resp.Status) != qmmgmt.StatusSuccess {
46-
return fmt.Errorf("R_QMMgmtAction failed: %s", qmmgmt.StatusString(uint32(resp.Status)))
46+
if status := hresult.HRESULT(resp.Status); status != hresult.S_OK {
47+
return fmt.Errorf("R_QMMgmtAction failed: %s", qmmgmt.StatusString(uint32(status)))
4748
}
4849
return nil
4950
}

network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0/interface.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ package rpcinterface_41208ee0e97011d19b9e00e02c064c39_1_0
1212
// A fetched copy is kept at ms-mqmq.idl in the interface directory.
1313

1414
import (
15-
"fmt"
16-
1715
"github.com/TheManticoreProject/Manticore/network/dcerpc/syntax"
16+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
1817
"github.com/TheManticoreProject/Manticore/windows/guid"
1918
)
2019

@@ -29,13 +28,25 @@ const (
2928
OpnumR_QMMgmtAction uint16 = 1
3029
)
3130

32-
// Status codes returned by this interface. Both qmmgmt methods return an HRESULT; the
33-
// values are Message Queuing result codes ([MS-MQMQ] 2.4, mqerror.h). [MS-MQMR] 3.1.4.1
34-
// and 3.1.4.2 name only the codes below; StatusString falls back to the hex value for
35-
// any other failure HRESULT.
31+
// Status codes returned by this interface. Both qmmgmt methods return an HRESULT, and
32+
// the whole of [MS-ERREF] section 2.1.1 lives in
33+
// github.com/TheManticoreProject/Manticore/windows/errors/hresult as the HRESULT type,
34+
// so no code that table names is repeated here. MQ_OK, the success value, is the
35+
// HRESULT S_OK and is spelled hresult.S_OK.
36+
//
37+
// The three Message Queuing result codes [MS-MQMR] 3.1.4.1 and 3.1.4.2 name are the
38+
// exception. They are MQ_* values of [MS-MQMQ] 2.4 (mqerror.h) living in FACILITY_MSMQ
39+
// (0x00E): each is of the form 0xC00E____, and [MS-ERREF] 2.1.1 carries no row in that
40+
// facility at all — not one of its 2928 values has a code whose facility field is
41+
// 0x00E. The shared table names none of these, so they stay declared here and
42+
// StatusString decodes them before deferring to the shared table for everything else.
43+
//
44+
// MQ_ERROR_INVALID_PARAMETER is the one with server behaviour attached, and that
45+
// knowledge is why it stays spelled out: the server returns it when pObjectFormat names
46+
// the MGMT_SESSION object type rather than MGMT_MACHINE or MGMT_QUEUE, and, for
47+
// R_QMMgmtAction, when lpwszAction is not one of the documented verbs (see the Action*
48+
// constants below).
3649
const (
37-
StatusSuccess uint32 = 0x00000000 // MQ_OK
38-
3950
MQ_ERROR uint32 = 0xC00E0001 // generic error
4051
MQ_ERROR_INVALID_PARAMETER uint32 = 0xC00E0006 // e.g. MGMT_SESSION type or bad lpwszAction
4152
MQ_ERROR_ILLEGAL_PROPID uint32 = 0xC00E0039 // a property id in aProp is not valid for the object
@@ -62,20 +73,22 @@ func SyntaxID() syntax.SyntaxID {
6273
}
6374
}
6475

65-
// StatusString returns a mnemonic for the documented status codes, otherwise the
66-
// hex value.
76+
// StatusString names the Message Queuing result codes of [MS-MQMQ] 2.4, which live in
77+
// the FACILITY_MSMQ facility that [MS-ERREF] 2.1.1 does not cover, and defers every
78+
// other status to the shared [MS-ERREF] 2.1.1 table, which names each value the
79+
// specification defines, derives the FACILITY_WIN32 values it wraps, and renders hex
80+
// only for values it does not know. A zero status renders as S_OK, the name the shared
81+
// table gives the value MSMQ also spells MQ_OK.
6782
func StatusString(status uint32) string {
6883
switch status {
69-
case StatusSuccess:
70-
return "MQ_OK"
7184
case MQ_ERROR:
7285
return "MQ_ERROR"
7386
case MQ_ERROR_INVALID_PARAMETER:
7487
return "MQ_ERROR_INVALID_PARAMETER"
7588
case MQ_ERROR_ILLEGAL_PROPID:
7689
return "MQ_ERROR_ILLEGAL_PROPID"
7790
default:
78-
return fmt.Sprintf("0x%08x", status)
91+
return hresult.HRESULT(status).String()
7992
}
8093
}
8194

network/dcerpc/interfaces/41208ee0-e970-11d1-9b9e-00e02c064c39/1.0/interface_test.go

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package rpcinterface_41208ee0e97011d19b9e00e02c064c39_1_0
22

3-
import "testing"
3+
import (
4+
"testing"
5+
6+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
7+
)
48

59
// TestSyntaxID verifies the abstract syntax identity (UUID + version) of the qmmgmt interface.
610
func TestSyntaxID(t *testing.T) {
@@ -36,15 +40,44 @@ func TestOpnums(t *testing.T) {
3640
}
3741
}
3842

39-
// TestStatusString verifies mnemonic rendering and the hex fallback.
40-
func TestStatusString(t *testing.T) {
41-
if got := StatusString(StatusSuccess); got != "MQ_OK" {
42-
t.Fatalf("StatusString(StatusSuccess) = %s, want MQ_OK", got)
43+
// TestStatusStringSharedTable verifies that the statuses this interface no longer
44+
// declares resolve through the shared [MS-ERREF] 2.1.1 table: MQ_OK is the HRESULT S_OK,
45+
// and a generic HRESULT the interface never enumerated is named rather than rendered as
46+
// hex.
47+
func TestStatusStringSharedTable(t *testing.T) {
48+
if got := StatusString(uint32(hresult.S_OK)); got != "S_OK" {
49+
t.Errorf("StatusString(S_OK) = %s, want S_OK", got)
4350
}
44-
if got := StatusString(MQ_ERROR_INVALID_PARAMETER); got != "MQ_ERROR_INVALID_PARAMETER" {
45-
t.Fatalf("StatusString(MQ_ERROR_INVALID_PARAMETER) = %s, want MQ_ERROR_INVALID_PARAMETER", got)
51+
const eFail = 0x80004005
52+
if got := StatusString(eFail); got != "E_FAIL" {
53+
t.Errorf("StatusString(0x%08x) = %s, want E_FAIL", uint32(eFail), got)
4654
}
4755
if got := StatusString(0xDEADBEEF); got != "0xdeadbeef" {
48-
t.Fatalf("StatusString(unknown) = %s, want 0xdeadbeef", got)
56+
t.Errorf("StatusString(unknown) = %s, want 0xdeadbeef", got)
57+
}
58+
}
59+
60+
// TestStatusStringMSMQFacility verifies that every retained code still renders under its
61+
// Message Queuing name, and that the shared table genuinely does not know it — [MS-ERREF]
62+
// 2.1.1 carries no row in FACILITY_MSMQ, which is why these stay declared locally.
63+
func TestStatusStringMSMQFacility(t *testing.T) {
64+
retained := map[uint32]string{
65+
MQ_ERROR: "MQ_ERROR",
66+
MQ_ERROR_INVALID_PARAMETER: "MQ_ERROR_INVALID_PARAMETER",
67+
MQ_ERROR_ILLEGAL_PROPID: "MQ_ERROR_ILLEGAL_PROPID",
68+
}
69+
if len(retained) != 3 {
70+
t.Fatalf("retained code count = %d, want 3", len(retained))
71+
}
72+
for value, name := range retained {
73+
if got := StatusString(value); got != name {
74+
t.Errorf("StatusString(0x%08x) = %s, want %s", value, got, name)
75+
}
76+
if value>>16&0x07FF != 0x00E {
77+
t.Errorf("%s (0x%08x) is not in FACILITY_MSMQ", name, value)
78+
}
79+
if entry, defined := hresult.Lookup(hresult.HRESULT(value)); defined {
80+
t.Errorf("hresult.Lookup(0x%08x) = %s, want undefined", value, entry.Name)
81+
}
4982
}
5083
}

network/dcerpc/interfaces/76d12b80-3467-11d3-91ff-0090272f9ea3/1.0/functions/03_rpc_ACCreateCursorEx.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
qmcomm2 "github.com/TheManticoreProject/Manticore/network/dcerpc/interfaces/76d12b80-3467-11d3-91ff-0090272f9ea3/1.0"
1212
"github.com/TheManticoreProject/Manticore/network/dcerpc/ndr"
13+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
1314
msmqmp "github.com/TheManticoreProject/Manticore/windows/protocols/ms-mqmp"
1415
)
1516

@@ -41,8 +42,8 @@ func Rpc_ACCreateCursorEx(rpc ndr.Invoker, hQueue msmqmp.RPC_QUEUE_HANDLE, pcc m
4142
return
4243
}
4344
Pcc = resp.Pcc
44-
if uint32(resp.Status) != qmcomm2.StatusSuccess {
45-
err = fmt.Errorf("rpc_ACCreateCursorEx failed: %s", qmcomm2.StatusString(uint32(resp.Status)))
45+
if status := hresult.HRESULT(resp.Status); status != hresult.S_OK {
46+
err = fmt.Errorf("rpc_ACCreateCursorEx failed: %s", qmcomm2.StatusString(uint32(status)))
4647
}
4748
return
4849
}

network/dcerpc/interfaces/76d12b80-3467-11d3-91ff-0090272f9ea3/1.0/interface.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@ package rpcinterface_76d12b80346711d391ff0090272f9ea3_1_0
1212
// A fetched copy is kept at ms-mqmp.idl in the interface directory.
1313

1414
import (
15-
"fmt"
16-
1715
"github.com/TheManticoreProject/Manticore/network/dcerpc/syntax"
16+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
1817
"github.com/TheManticoreProject/Manticore/windows/guid"
1918
)
2019

@@ -33,13 +32,20 @@ const (
3332
Opnumrpc_ACCreateCursorEx uint16 = 3
3433
)
3534

36-
// Status codes returned by this interface. Every qmcomm2 method returns an HRESULT; the
37-
// values are the Message Queuing result codes ([MS-MQMQ] 2.4, mqerror.h). Only the codes
38-
// most relevant to the send/receive operations are enumerated here; StatusString falls
39-
// back to the hex value for any other HRESULT.
35+
// Status codes returned by this interface. Every qmcomm2 method returns an HRESULT, and
36+
// the whole of [MS-ERREF] section 2.1.1 lives in
37+
// github.com/TheManticoreProject/Manticore/windows/errors/hresult as the HRESULT type,
38+
// so no code that table names is repeated here. MQ_OK, the success value, is the
39+
// HRESULT S_OK and is spelled hresult.S_OK.
40+
//
41+
// The Message Queuing result codes below are the exception. They are the MQ_* values of
42+
// [MS-MQMQ] 2.4 (mqerror.h), which live in FACILITY_MSMQ (0x00E): every one of them is
43+
// of the form 0xC00E____, and [MS-ERREF] 2.1.1 carries no row in that facility at all —
44+
// not one of its 2928 values has a code whose facility field is 0x00E. The shared table
45+
// therefore names none of these, and cannot be made to without the specification
46+
// listing them. They stay declared here, and StatusString decodes them before deferring
47+
// to the shared table for everything else.
4048
const (
41-
StatusSuccess uint32 = 0x00000000 // MQ_OK
42-
4349
MQ_ERROR uint32 = 0xC00E0001
4450
MQ_ERROR_QUEUE_NOT_FOUND uint32 = 0xC00E0003
4551
MQ_ERROR_QUEUE_NOT_ACTIVE uint32 = 0xC00E0004
@@ -65,12 +71,14 @@ func SyntaxID() syntax.SyntaxID {
6571
}
6672
}
6773

68-
// StatusString returns a mnemonic for the documented status codes, otherwise the
69-
// hex value.
74+
// StatusString names the Message Queuing result codes of [MS-MQMQ] 2.4, which live in
75+
// the FACILITY_MSMQ facility that [MS-ERREF] 2.1.1 does not cover, and defers every
76+
// other status to the shared [MS-ERREF] 2.1.1 table, which names each value the
77+
// specification defines, derives the FACILITY_WIN32 values it wraps, and renders hex
78+
// only for values it does not know. A zero status renders as S_OK, the name the shared
79+
// table gives the value MSMQ also spells MQ_OK.
7080
func StatusString(status uint32) string {
7181
switch status {
72-
case StatusSuccess:
73-
return "MQ_OK"
7482
case MQ_ERROR:
7583
return "MQ_ERROR"
7684
case MQ_ERROR_QUEUE_NOT_FOUND:
@@ -98,7 +106,7 @@ func StatusString(status uint32) string {
98106
case MQ_ERROR_TRANSACTION_USAGE:
99107
return "MQ_ERROR_TRANSACTION_USAGE"
100108
default:
101-
return fmt.Sprintf("0x%08x", status)
109+
return hresult.HRESULT(status).String()
102110
}
103111
}
104112

network/dcerpc/interfaces/76d12b80-3467-11d3-91ff-0090272f9ea3/1.0/interface_test.go

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package rpcinterface_76d12b80346711d391ff0090272f9ea3_1_0
22

3-
import "testing"
3+
import (
4+
"testing"
5+
6+
"github.com/TheManticoreProject/Manticore/windows/errors/hresult"
7+
)
48

59
// TestSyntaxID verifies the abstract syntax identity (UUID + version) of the qmcomm2 interface.
610
func TestSyntaxID(t *testing.T) {
@@ -29,15 +33,54 @@ func TestOpnums(t *testing.T) {
2933
}
3034
}
3135

32-
// TestStatusString verifies mnemonic rendering and the hex fallback.
33-
func TestStatusString(t *testing.T) {
34-
if got := StatusString(StatusSuccess); got != "MQ_OK" {
35-
t.Fatalf("StatusString(StatusSuccess) = %s, want MQ_OK", got)
36+
// TestStatusStringSharedTable verifies that the statuses this interface no longer
37+
// declares resolve through the shared [MS-ERREF] 2.1.1 table: MQ_OK is the HRESULT S_OK,
38+
// and a generic HRESULT the interface never enumerated is named rather than rendered as
39+
// hex.
40+
func TestStatusStringSharedTable(t *testing.T) {
41+
if got := StatusString(uint32(hresult.S_OK)); got != "S_OK" {
42+
t.Errorf("StatusString(S_OK) = %s, want S_OK", got)
3643
}
37-
if got := StatusString(MQ_ERROR_INVALID_HANDLE); got != "MQ_ERROR_INVALID_HANDLE" {
38-
t.Fatalf("StatusString(MQ_ERROR_INVALID_HANDLE) = %s, want MQ_ERROR_INVALID_HANDLE", got)
44+
const eFail = 0x80004005
45+
if got := StatusString(eFail); got != "E_FAIL" {
46+
t.Errorf("StatusString(0x%08x) = %s, want E_FAIL", uint32(eFail), got)
3947
}
4048
if got := StatusString(0xDEADBEEF); got != "0xdeadbeef" {
41-
t.Fatalf("StatusString(unknown) = %s, want 0xdeadbeef", got)
49+
t.Errorf("StatusString(unknown) = %s, want 0xdeadbeef", got)
50+
}
51+
}
52+
53+
// TestStatusStringMSMQFacility verifies that every retained code still renders under its
54+
// Message Queuing name, and that the shared table genuinely does not know it — [MS-ERREF]
55+
// 2.1.1 carries no row in FACILITY_MSMQ, which is why these stay declared locally.
56+
func TestStatusStringMSMQFacility(t *testing.T) {
57+
retained := map[uint32]string{
58+
MQ_ERROR: "MQ_ERROR",
59+
MQ_ERROR_QUEUE_NOT_FOUND: "MQ_ERROR_QUEUE_NOT_FOUND",
60+
MQ_ERROR_QUEUE_NOT_ACTIVE: "MQ_ERROR_QUEUE_NOT_ACTIVE",
61+
MQ_ERROR_INVALID_PARAMETER: "MQ_ERROR_INVALID_PARAMETER",
62+
MQ_ERROR_INVALID_HANDLE: "MQ_ERROR_INVALID_HANDLE",
63+
MQ_ERROR_OPERATION_CANCELLED: "MQ_ERROR_OPERATION_CANCELLED",
64+
MQ_ERROR_SHARING_VIOLATION: "MQ_ERROR_SHARING_VIOLATION",
65+
MQ_ERROR_SERVICE_NOT_AVAILABLE: "MQ_ERROR_SERVICE_NOT_AVAILABLE",
66+
MQ_ERROR_MESSAGE_ALREADY_RECEIVED: "MQ_ERROR_MESSAGE_ALREADY_RECEIVED",
67+
MQ_ERROR_ACCESS_DENIED: "MQ_ERROR_ACCESS_DENIED",
68+
MQ_ERROR_INSUFFICIENT_RESOURCES: "MQ_ERROR_INSUFFICIENT_RESOURCES",
69+
MQ_ERROR_IO_TIMEOUT: "MQ_ERROR_IO_TIMEOUT",
70+
MQ_ERROR_TRANSACTION_USAGE: "MQ_ERROR_TRANSACTION_USAGE",
71+
}
72+
if len(retained) != 13 {
73+
t.Fatalf("retained code count = %d, want 13", len(retained))
74+
}
75+
for value, name := range retained {
76+
if got := StatusString(value); got != name {
77+
t.Errorf("StatusString(0x%08x) = %s, want %s", value, got, name)
78+
}
79+
if value>>16&0x07FF != 0x00E {
80+
t.Errorf("%s (0x%08x) is not in FACILITY_MSMQ", name, value)
81+
}
82+
if entry, defined := hresult.Lookup(hresult.HRESULT(value)); defined {
83+
t.Errorf("hresult.Lookup(0x%08x) = %s, want undefined", value, entry.Name)
84+
}
4285
}
4386
}

0 commit comments

Comments
 (0)