pax_global_header00006660000000000000000000000064137532435630014525gustar00rootroot0000000000000052 comment=7f482067a47ba5c8f25f2bcae9ab99b3fed820e9 rpc-2.0.3/000077500000000000000000000000001375324356300123135ustar00rootroot00000000000000rpc-2.0.3/.github/000077500000000000000000000000001375324356300136535ustar00rootroot00000000000000rpc-2.0.3/.github/workflows/000077500000000000000000000000001375324356300157105ustar00rootroot00000000000000rpc-2.0.3/.github/workflows/testing.yml000066400000000000000000000036011375324356300201100ustar00rootroot00000000000000name: v1 on: push: paths-ignore: - 'v[0-9]+/**' pull_request: paths-ignore: - 'v[0-9]+/**' jobs: build: name: Tests runs-on: ubuntu-latest strategy: matrix: go: [ '1.13.x', '1.14.x', '1.15.x' ] steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v1 with: go-version: ${{ matrix.go }} - name: Checkout uses: actions/checkout@v2 with: ref: ${{ github.ref }} - name: Test well formatted with gofmt run: | # Remove major version sub directories find . -maxdepth 1 -type d -regex '\./v[0-9]+' | xargs -i rm -rf {} GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/) test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g') id: gofmt - name: Copy into GOPATH run: | # Default GOPATH=${HOME}/go mkdir -p ${HOME}/go/src/github.com/${GITHUB_REPOSITORY} cp -r $GITHUB_WORKSPACE/* /home/runner/go/src/github.com/${GITHUB_REPOSITORY} id: copyToGOPATH - name: Get dependencies run: | cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY} go get -v -t -d ./... id: goGet - name: Go vet run: | cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY} go vet $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/) id: govet - name: Unit tests run: | cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY} go test -race $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/) id: unitTests - name: Unit tests (32bit) run: | cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY} go test $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/) env: GOARCH: 386 id: unitTest32rpc-2.0.3/.github/workflows/testingv2.yml000066400000000000000000000022551375324356300203640ustar00rootroot00000000000000# Name of the workflow needs to match the name of the major version directory name: v2 on: push: paths: - 'v2/**' pull_request: paths: - 'v2/**' jobs: build: name: Tests runs-on: ubuntu-latest strategy: matrix: go: [ '1.13.x', '1.14.x', '1.15.x' ] steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v1 with: go-version: ${{ matrix.go }} - name: Checkout uses: actions/checkout@v2 with: ref: ${{ github.ref }} - name: Test well formatted with gofmt run: | GO_FILES=$(find ${GITHUB_WORKFLOW} -iname '*.go' -type f | grep -v /vendor/) test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g') id: gofmt - name: Go vet run: | cd ${GITHUB_WORKFLOW} go vet ./... id: govet - name: Unit tests run: | cd ${GITHUB_WORKFLOW} go test -race ./... id: unitTests - name: Unit tests (32bit) run: | cd ${GITHUB_WORKFLOW} go test ./... env: GOARCH: 386 id: unitTest32rpc-2.0.3/.gitignore000066400000000000000000000003001375324356300142740ustar00rootroot00000000000000# Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out rpc-2.0.3/LICENSE000066400000000000000000000261351375324356300133270ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rpc-2.0.3/README.md000066400000000000000000000054651375324356300136040ustar00rootroot00000000000000# RPC This project has now been converted to use Go modules. Please refer to the latest major version sub directory. This follows the practice outlines at https://blog.golang.org/v2-go-modules [![Version](https://img.shields.io/github/v/release/jcmturner/rpc?label=Version&sort=semver)](https://github.com/jcmturner/rpc/releases) This project relates to [CDE 1.1: Remote Procedure Call](http://pubs.opengroup.org/onlinepubs/9629399/) It is a partial implementation that mainly focuses on unmarshaling NDR encoded byte streams into Go structures. ## Help Wanted **Reference test vectors needed**: It has been difficult to implement due to a lack of reference test byte streams in the standards documentation. Test driven development has been extremely challenging without these. If you are aware of and reference test vector sources for NDR encoding please let me know by raising an issue with the details. Thanks! ## References * [Open Group RPC Publication](http://pubs.opengroup.org/onlinepubs/9629399/) * [Microsoft RPC Documentation](https://docs.microsoft.com/en-us/windows/desktop/Rpc/rpc-start-page) ## NDR Decode Capability Checklist - [x] Format label - [x] Boolean - [x] Character - [x] Unsigned small integer - [x] Unsigned short integer - [x] Unsigned long integer - [x] Unsigned hyper integer - [x] Signed small integer - [x] Signed short integer - [x] Signed long integer - [x] Signed hyper integer - [x] Single float - [x] Double float - [x] Uni-dimensional fixed array - [x] Multi-dimensional fixed array - [x] Uni-dimensional conformant array - [x] Multi-dimensional conformant array - [x] Uni-dimensional conformant varying array - [x] Multi-dimensional conformant varying array - [x] Varying string - [x] Conformant varying string - [x] Array of strings - [x] Union - [x] Pipe ## Structs from IDL [Interface Definition Language (IDL)](http://pubs.opengroup.org/onlinepubs/9629399/chap4.htm) ### Is a field a pointer? ### Is an array conformant and/or varying? An array is conformant if the IDL definition includes one of the following attributes: * min_is * max_is * size_is An array is varying if the IDL definition includes one of the following attributes: * last_is * first_is * length_is #### Examples: SubAuthority[] is conformant in the example below: ``` typedef struct _RPC_SID { unsigned char Revision; unsigned char SubAuthorityCount; RPC_SID_IDENTIFIER_AUTHORITY IdentifierAuthority; [size_is(SubAuthorityCount)] unsigned long SubAuthority[]; } RPC_SID, *PRPC_SID, *PSID; ``` Buffer is a pointer to a conformant varying array in the example below: ``` typedef struct _RPC_UNICODE_STRING { unsigned short Length; unsigned short MaximumLength; [size_is(MaximumLength/2), length_is(Length/2)] WCHAR* Buffer; } RPC_UNICODE_STRING, *PRPC_UNICODE_STRING; ``` ### Is a union encapsulated? rpc-2.0.3/examples/000077500000000000000000000000001375324356300141315ustar00rootroot00000000000000rpc-2.0.3/examples/examples.go000066400000000000000000000033161375324356300163010ustar00rootroot00000000000000// Package examples provides example decoding of NDR byte streams package examples import "github.com/jcmturner/rpc/mstypes" // KerbValidationInfo type KerbValidationInfo struct { LogOnTime mstypes.FileTime LogOffTime mstypes.FileTime KickOffTime mstypes.FileTime PasswordLastSet mstypes.FileTime PasswordCanChange mstypes.FileTime PasswordMustChange mstypes.FileTime EffectiveName mstypes.RPCUnicodeString FullName mstypes.RPCUnicodeString LogonScript mstypes.RPCUnicodeString ProfilePath mstypes.RPCUnicodeString HomeDirectory mstypes.RPCUnicodeString HomeDirectoryDrive mstypes.RPCUnicodeString LogonCount uint16 BadPasswordCount uint16 UserID uint32 PrimaryGroupID uint32 GroupCount uint32 GroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"` UserFlags uint32 UserSessionKey mstypes.UserSessionKey LogonServer mstypes.RPCUnicodeString LogonDomainName mstypes.RPCUnicodeString LogonDomainID mstypes.RPCSID `ndr:"pointer"` Reserved1 [2]uint32 // Has 2 elements UserAccountControl uint32 SubAuthStatus uint32 LastSuccessfulILogon mstypes.FileTime LastFailedILogon mstypes.FileTime FailedILogonCount uint32 Reserved3 uint32 SIDCount uint32 ExtraSIDs []mstypes.KerbSidAndAttributes `ndr:"pointer,conformant"` ResourceGroupDomainSID mstypes.RPCSID `ndr:"pointer"` ResourceGroupCount uint32 ResourceGroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"` } rpc-2.0.3/examples/examples_test.go000066400000000000000000000443451375324356300173470ustar00rootroot00000000000000package examples import ( "bytes" "encoding/hex" "testing" "time" "github.com/jcmturner/rpc/mstypes" "github.com/jcmturner/rpc/ndr" "github.com/stretchr/testify/assert" ) const ( KerbValidationInfoMS = "01100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e0000000000" KerbValidationInfoGoKRB5 = "01100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000" KerbValidationInfoTrust = "01100800cccccccc000200000000000000000200c30bcc79e444d301ffffffffffffff7fffffffffffffff7fc764125a0842d301c7247c84d142d301ffffffffffffff7f12001200040002001600160008000200000000000c0002000000000010000200000000001400020000000000180002002e0000005204000001020000030000001c0002002002000000000000000000000000000000000000060008002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000010000002c00020034000200020000003800020009000000000000000900000074006500730074007500730065007200310000000b000000000000000b0000005400650073007400310020005500730065007200310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000056040000070000000102000007000000550400000700000004000000000000000300000055004400430000000500000000000000040000005500530045005200040000000104000000000005150000002057308834e7d1d0a2fb0444010000003000020007000000010000000101000000000012010000000400000001040000000000051500000062dc8db6c8705249b5459e75020000005304000007000020540400000700002000000000" ) func TestExample_KerbValidationInfo(t *testing.T) { b, _ := hex.DecodeString(KerbValidationInfoMS) k := new(KerbValidationInfo) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(k) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2006, 4, 28, 1, 42, 50, 925640100, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2006, 3, 18, 10, 44, 54, 837147900, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2006, 3, 19, 10, 44, 54, 837147900, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "lzhu", k.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Liqiang(Larry) Zhu", k.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "ntds2.bat", k.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(4180), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(2914711), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(26), k.GroupCount, "GroupCount not as expected") gids := []mstypes.GroupMembership{ {RelativeID: 3392609, Attributes: 7}, {RelativeID: 2999049, Attributes: 7}, {RelativeID: 3322974, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 2931095, Attributes: 7}, {RelativeID: 3338539, Attributes: 7}, {RelativeID: 3354830, Attributes: 7}, {RelativeID: 3026599, Attributes: 7}, {RelativeID: 3338538, Attributes: 7}, {RelativeID: 2931096, Attributes: 7}, {RelativeID: 3392610, Attributes: 7}, {RelativeID: 3342740, Attributes: 7}, {RelativeID: 3392630, Attributes: 7}, {RelativeID: 3014318, Attributes: 7}, {RelativeID: 2937394, Attributes: 7}, {RelativeID: 3278870, Attributes: 7}, {RelativeID: 3038018, Attributes: 7}, {RelativeID: 3322975, Attributes: 7}, {RelativeID: 3513546, Attributes: 7}, {RelativeID: 2966661, Attributes: 7}, {RelativeID: 3338434, Attributes: 7}, {RelativeID: 3271401, Attributes: 7}, {RelativeID: 3051245, Attributes: 7}, {RelativeID: 3271606, Attributes: 7}, {RelativeID: 3026603, Attributes: 7}, {RelativeID: 3018354, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "NTDEV-DC-05", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "NTDEV", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-397955417-626881126-188441444", k.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(16), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(13), k.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es = []struct { sid string attr uint32 }{ {"S-1-5-21-773533881-1816936887-355810188-513", uint32(7)}, {"S-1-5-21-397955417-626881126-188441444-3101812", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291368", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291341", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3322973", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3479105", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3271400", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3283393", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3338537", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3038991", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3037999", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3248111", uint32(536870919)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint8(0), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, 0, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") b, _ = hex.DecodeString(KerbValidationInfoGoKRB5) k2 := new(KerbValidationInfo) dec = ndr.NewDecoder(bytes.NewReader(b)) err = dec.Decode(k2) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 825766900, time.UTC), k2.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 5, 6, 7, 23, 8, 968750000, time.UTC), k2.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 5, 7, 7, 23, 8, 968750000, time.UTC), k2.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k2.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k2.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(216), k2.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k2.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1105), k2.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k2.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(5), k2.GroupCount, "GroupCount not as expected") gids = []mstypes.GroupMembership{ {RelativeID: 513, Attributes: 7}, {RelativeID: 1108, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, {RelativeID: 1115, Attributes: 7}, {RelativeID: 1116, Attributes: 7}, } assert.Equal(t, gids, k2.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k2.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k2.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "ADDC", k2.LogonServer.String(), "LogonServer not as expected") assert.Equal(t, "TEST", k2.LogonDomainName.String(), "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-3167651404-3865080224-2280184895", k2.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k2.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k2.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k2.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(2), k2.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k2.SIDCount), len(k2.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es2 = []struct { sid string attr uint32 }{ {"S-1-5-21-3167651404-3865080224-2280184895-1114", uint32(536870919)}, {"S-1-5-21-3167651404-3865080224-2280184895-1111", uint32(536870919)}, } for i, s := range es2 { assert.Equal(t, s.sid, k2.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as expected") assert.Equal(t, s.attr, k2.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as expected") } assert.Equal(t, uint8(0), k2.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, 0, len(k2.ResourceGroupIDs), "ResourceGroupIDs not as expected") b, _ = hex.DecodeString(KerbValidationInfoTrust) k = new(KerbValidationInfo) dec = ndr.NewDecoder(bytes.NewReader(b)) err = dec.Decode(k) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2017, 10, 14, 12, 03, 41, 52409900, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 10, 10, 20, 42, 56, 220282300, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 10, 11, 20, 42, 56, 220282300, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "", k.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(46), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1106), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(3), k.GroupCount, "GroupCount not as expected") gids = []mstypes.GroupMembership{ {RelativeID: 1110, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(544), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "UDC", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "USER", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-2284869408-3503417140-1141177250", k.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(1), k.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") es = []struct { sid string attr uint32 }{ {"S-1-18-1", uint32(7)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint8(4), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, "S-1-5-21-3062750306-1230139592-1973306805", k.ResourceGroupDomainSID.String(), "ResourceGroupDomainSID value not as expected") assert.Equal(t, 2, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") rgids := []mstypes.GroupMembership{ {RelativeID: 1107, Attributes: 536870919}, {RelativeID: 1108, Attributes: 536870919}, } assert.Equal(t, rgids, k.ResourceGroupIDs, "ResourceGroupIDs not as expected") //groupSids := []string{"S-1-5-21-2284869408-3503417140-1141177250-1110", // "S-1-5-21-2284869408-3503417140-1141177250-513", // "S-1-5-21-2284869408-3503417140-1141177250-1109", // "S-1-18-1", // "S-1-5-21-3062750306-1230139592-1973306805-1107", // "S-1-5-21-3062750306-1230139592-1973306805-1108"} //assert.Equal(t, groupSids, k.GetGroupMembershipSIDs(), "GroupMembershipSIDs not as expected") } rpc-2.0.3/mstypes/000077500000000000000000000000001375324356300140175ustar00rootroot00000000000000rpc-2.0.3/mstypes/claims.go000066400000000000000000000110351375324356300156160ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "errors" "fmt" "github.com/jcmturner/rpc/ndr" "golang.org/x/net/http2/hpack" ) // Compression format assigned numbers. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/a8b7cb0a-92a6-4187-a23b-5e14273b96f8 const ( CompressionFormatNone uint16 = 0 CompressionFormatLZNT1 uint16 = 2 // LZNT1 aka ntfs compression CompressionFormatXPress uint16 = 3 // plain LZ77 CompressionFormatXPressHuff uint16 = 4 // LZ77+Huffman - The Huffman variant of the XPRESS compression format uses LZ77-style dictionary compression combined with Huffman coding. ) // ClaimsSourceTypeAD https://msdn.microsoft.com/en-us/library/hh553809.aspx const ClaimsSourceTypeAD uint16 = 1 // Claim Type assigned numbers const ( ClaimTypeIDInt64 uint16 = 1 ClaimTypeIDUInt64 uint16 = 2 ClaimTypeIDString uint16 = 3 ClaimsTypeIDBoolean uint16 = 6 ) // ClaimsBlob implements https://msdn.microsoft.com/en-us/library/hh554119.aspx type ClaimsBlob struct { Size uint32 EncodedBlob EncodedBlob } // EncodedBlob are the bytes of the encoded Claims type EncodedBlob []byte // Size returns the size of the bytes of the encoded Claims func (b EncodedBlob) Size(c interface{}) int { cb := c.(ClaimsBlob) return int(cb.Size) } // ClaimsSetMetadata implements https://msdn.microsoft.com/en-us/library/hh554073.aspx type ClaimsSetMetadata struct { ClaimsSetSize uint32 ClaimsSetBytes []byte `ndr:"pointer,conformant"` CompressionFormat uint16 // Enum see constants for options UncompressedClaimsSetSize uint32 ReservedType uint16 ReservedFieldSize uint32 ReservedField []byte `ndr:"pointer,conformant"` } // ClaimsSet reads the ClaimsSet type from the NDR encoded ClaimsSetBytes in the ClaimsSetMetadata func (m *ClaimsSetMetadata) ClaimsSet() (c ClaimsSet, err error) { if len(m.ClaimsSetBytes) < 1 { err = errors.New("no bytes available for ClaimsSet") return } // TODO switch statement to decompress ClaimsSetBytes switch m.CompressionFormat { case CompressionFormatLZNT1: s := hex.EncodeToString(m.ClaimsSetBytes) err = fmt.Errorf("ClaimsSet compressed, format LZNT1 not currently supported: %s", s) return case CompressionFormatXPress: s := hex.EncodeToString(m.ClaimsSetBytes) err = fmt.Errorf("ClaimsSet compressed, format XPress not currently supported: %s", s) return case CompressionFormatXPressHuff: var b []byte buff := bytes.NewBuffer(b) _, e := hpack.HuffmanDecode(buff, m.ClaimsSetBytes) if e != nil { err = fmt.Errorf("error deflating: %v", e) return } m.ClaimsSetBytes = buff.Bytes() } dec := ndr.NewDecoder(bytes.NewReader(m.ClaimsSetBytes)) err = dec.Decode(&c) return } // ClaimsSet implements https://msdn.microsoft.com/en-us/library/hh554122.aspx type ClaimsSet struct { ClaimsArrayCount uint32 ClaimsArrays []ClaimsArray `ndr:"pointer,conformant"` ReservedType uint16 ReservedFieldSize uint32 ReservedField []byte `ndr:"pointer,conformant"` } // ClaimsArray implements https://msdn.microsoft.com/en-us/library/hh536458.aspx type ClaimsArray struct { ClaimsSourceType uint16 ClaimsCount uint32 ClaimEntries []ClaimEntry `ndr:"pointer,conformant"` } // ClaimEntry is a NDR union that implements https://msdn.microsoft.com/en-us/library/hh536374.aspx type ClaimEntry struct { ID string `ndr:"pointer,conformant,varying"` Type uint16 `ndr:"unionTag"` TypeInt64 ClaimTypeInt64 `ndr:"unionField"` TypeUInt64 ClaimTypeUInt64 `ndr:"unionField"` TypeString ClaimTypeString `ndr:"unionField"` TypeBool ClaimTypeBoolean `ndr:"unionField"` } // SwitchFunc is the ClaimEntry union field selection function func (u ClaimEntry) SwitchFunc(_ interface{}) string { switch u.Type { case ClaimTypeIDInt64: return "TypeInt64" case ClaimTypeIDUInt64: return "TypeUInt64" case ClaimTypeIDString: return "TypeString" case ClaimsTypeIDBoolean: return "TypeBool" } return "" } // ClaimTypeInt64 is a claim of type int64 type ClaimTypeInt64 struct { ValueCount uint32 Value []int64 `ndr:"pointer,conformant"` } // ClaimTypeUInt64 is a claim of type uint64 type ClaimTypeUInt64 struct { ValueCount uint32 Value []uint64 `ndr:"pointer,conformant"` } // ClaimTypeString is a claim of type string type ClaimTypeString struct { ValueCount uint32 Value []LPWSTR `ndr:"pointer,conformant"` } // ClaimTypeBoolean is a claim of type bool type ClaimTypeBoolean struct { ValueCount uint32 Value []bool `ndr:"pointer,conformant"` } rpc-2.0.3/mstypes/claims_test.go000066400000000000000000000307521375324356300166640ustar00rootroot00000000000000package mstypes import ( "bytes" "compress/flate" "encoding/hex" "io/ioutil" "testing" "unicode/utf8" "github.com/jcmturner/rpc/ndr" "github.com/stretchr/testify/assert" ) const ( ClientClaimsInfoStr = "01100800cccccccc000100000000000000000200d80000000400020000000000d8000000000000000000000000000000d800000001100800ccccccccc80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000100000010000200290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a0038003800640035006400390030003800350065006100350063003000630030000000000001000000140002000a000000000000000a00000074006500730074007500730065007200310000000000000000000000" ClientClaimsInfoInt = "01100800cccccccce00000000000000000000200b80000000400020000000000b8000000000000000000000000000000b800000001100800cccccccca80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c0002000100010001000000100002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c0000000000000000000000" ClientClaimsInfoMulti = "01100800cccccccc780100000000000000000200500100000400020000000000500100000000000000000000000000005001000001100800cccccccc400100000000000000000200010000000400020000000000000000000000000001000000010000000200000008000200020000000c000200010001000100000010000200140002000300030001000000180002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c00000000000000290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a00380038006400350064003900300038003500650061003500630030006300300000000000010000001c0002000a000000000000000a000000740065007300740075007300650072003100000000000000" ClientClaimsInfoMultiUint = "01100800ccccccccf00000000000000000000200c80000000400020000000000c8000000000000000000000000000000c800000001100800ccccccccb80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200020002000400000010000200260000000000000026000000610064003a002f002f006500780074002f006f0062006a0065006300740043006c006100730073003a00380038006400350064006500370039003100650037006200320037006500360000000400000009000a000000000007000100000000000600010000000000000001000000000000000000" ClientClaimsInfoMultiStr = "01100800cccccccc480100000000000000000200200100000400020000000000200100000000000000000000000000002001000001100800cccccccc100100000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000400000010000200270000000000000027000000610064003a002f002f006500780074002f006f00740068006500720049007000500068006f006e0065003a003800380064003500640065003900660036006200340061006600390038003500000000000400000014000200180002001c000200200002000500000000000000050000007300740072003100000000000500000000000000050000007300740072003200000000000500000000000000050000007300740072003300000000000500000000000000050000007300740072003400000000000000000000000000" ClaimsEntryIDStr = "ad://ext/sAMAccountName:88d5d9085ea5c0c0" ClaimsEntryValueStr = "testuser1" ClaimsEntryIDInt64 = "ad://ext/msDS-SupportedE:88d5dea8f1af5f19" ClaimsEntryValueInt64 int64 = 28 ClaimsEntryIDUInt64 = "ad://ext/objectClass:88d5de791e7b27e6" ClaimsSetBytesCompressionFormatXPressHuff = "738788888708080007000800080007000880088808088880886687888607080000808800800000000880000000000000806667080808787707767800080000000000000000000000000000000000000000000000080000000000000000000000000000000000080000000000000000000000000000000000000000000000000057000800800000007500000000000000050700000000000064760800080000008587007700000080650808000000000075888700000000700788000000000060677000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e91f150e1ad792412496411f3904ff6027871529ef12043e4e79ab23c9f03aea65c0aca41e842b8d46f0321354538afe9f8c413b6e1a37377bca410ac8bc3b35398e51c0a290929e3ca764addf84e5ada9caa43c80c38de74d75cd0289a202641d26a950284dea25479c4376c3100720db619b9066d13c506c88a858a3305007490a40d7015a7528382a7c9ae54ab58204f01e1d8e044fee01925cbc46ad28cfa8d67c28e0216ce1de315aaaf43e4c88409002793b33a3823683680ce7d6606eca05f0cff9d06c88a0588dd5500d51de514570286fa148c007c699838d635b0b87ed420749011c94696fa202b002b0000" ) func Test_TMP(t *testing.T) { blz77huff, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8dc0000ff2601") //b, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050555555555555555555554544040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8523ed794115be9195ff9d67cdf8d0400000000") r := bytes.NewReader(blz77huff) dr := flate.NewReader(r) s, errr := ioutil.ReadAll(dr) t.Logf("test: %s %v\n", string(s), errr) ru, size := utf8.DecodeRuneInString("`") t.Logf("%d %v\n", ru, size) } func Test_ClientClaimsInfoStr_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoStr) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(3), k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{ClaimsEntryValueStr}}, k.ClaimsArrays[0].ClaimEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsMultiValueUint_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMultiUint) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDUInt64, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.ClaimsArrays[0].ClaimEntries[0].TypeUInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDUInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []uint64{655369, 65543, 65542, 65536}, k.ClaimsArrays[0].ClaimEntries[0].TypeUInt64.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsInt_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoInt) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDInt64, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{ClaimsEntryValueInt64}, k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsMultiValueStr_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMultiStr) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDString, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.ClaimsArrays[0].ClaimEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, "ad://ext/otherIpPhone:88d5de9f6b4af985", k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{"str1"}, {"str2"}, {"str3"}, {"str4"}}, k.ClaimsArrays[0].ClaimEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsInfoMultiEntry_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMulti) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(2), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(1), k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{int64(28)}, k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, uint16(3), k.ClaimsArrays[0].ClaimEntries[1].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[1].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.ClaimsArrays[0].ClaimEntries[1].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{ClaimsEntryValueStr}}, k.ClaimsArrays[0].ClaimEntries[1].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } rpc-2.0.3/mstypes/common.go000066400000000000000000000006441375324356300156420ustar00rootroot00000000000000// Package mstypes provides implemnations of some Microsoft data types [MS-DTYP] https://msdn.microsoft.com/en-us/library/cc230283.aspx package mstypes // LPWSTR implements https://msdn.microsoft.com/en-us/library/cc230355.aspx type LPWSTR struct { Value string `ndr:"pointer,conformant,varying"` } // String returns the string representation of LPWSTR data type. func (s *LPWSTR) String() string { return s.Value } rpc-2.0.3/mstypes/filetime.go000066400000000000000000000030371375324356300161470ustar00rootroot00000000000000// Package mstypes implements representations of Microsoft types package mstypes import ( "time" ) /* FILETIME is a windows data structure. Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx It contains two parts that are 32bit integers: dwLowDateTime dwHighDateTime We need to combine these two into one 64bit integer. This gives the number of 100 nano second period from January 1, 1601, Coordinated Universal Time (UTC) */ const unixEpochDiff = 116444736000000000 // FileTime implements the Microsoft FILETIME type https://msdn.microsoft.com/en-us/library/cc230324.aspx type FileTime struct { LowDateTime uint32 HighDateTime uint32 } // Time return a golang Time type from the FileTime func (ft FileTime) Time() time.Time { ns := (ft.MSEpoch() - unixEpochDiff) * 100 return time.Unix(0, int64(ns)).UTC() } // MSEpoch returns the FileTime as a Microsoft epoch, the number of 100 nano second periods elapsed from January 1, 1601 UTC. func (ft FileTime) MSEpoch() int64 { return (int64(ft.HighDateTime) << 32) + int64(ft.LowDateTime) } // Unix returns the FileTime as a Unix time, the number of seconds elapsed since January 1, 1970 UTC. func (ft FileTime) Unix() int64 { return (ft.MSEpoch() - unixEpochDiff) / 10000000 } // GetFileTime returns a FileTime type from the provided Golang Time type. func GetFileTime(t time.Time) FileTime { ns := t.UnixNano() fp := (ns / 100) + unixEpochDiff hd := fp >> 32 ld := fp - (hd << 32) return FileTime{ LowDateTime: uint32(ld), HighDateTime: uint32(hd), } } rpc-2.0.3/mstypes/filetime_test.go000066400000000000000000000030041375324356300172000ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "github.com/jcmturner/rpc/ndr" "github.com/stretchr/testify/assert" "testing" "time" ) const TestNDRHeader = "01100800cccccccca00400000000000000000200" func TestFileTime(t *testing.T) { t.Parallel() //2007-02-22 17:00:01.6382155 tt := time.Date(2007, 2, 22, 17, 0, 1, 638215500, time.UTC) ft := GetFileTime(tt) assert.Equal(t, tt.Unix(), ft.Unix(), "Unix epoch time not as expected") assert.Equal(t, int64(128166372016382155), ft.MSEpoch(), "MSEpoch not as expected") assert.Equal(t, tt, ft.Time(), "Golang time object returned from FileTime not as expected") } func TestDecodeFileTime(t *testing.T) { var tests = []struct { Hex string UnixNano int64 }{ {"d186660f656ac601", 1146188570925640100}, {"17d439fe784ac601", 1142678694837147900}, {"1794a328424bc601", 1142765094837147900}, {"175424977a81c601", 1148726694837147900}, {"058e4fdd80c6d201", 1494085991825766900}, {"cc27969c39c6d201", 1494055388968750000}, {"cce7ffc602c7d201", 1494141788968750000}, {"c30bcc79e444d301", 1507982621052409900}, {"c764125a0842d301", 1507668176220282300}, {"c7247c84d142d301", 1507754576220282300}, } for i, test := range tests { a := new(FileTime) hexStr := TestNDRHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.UnixNano, a.Time().UnixNano(), "Time value not as expected for test: %d", i+1) } } rpc-2.0.3/mstypes/group_membership.go000066400000000000000000000021421375324356300177140ustar00rootroot00000000000000package mstypes // GroupMembership implements https://msdn.microsoft.com/en-us/library/cc237945.aspx // RelativeID : A 32-bit unsigned integer that contains the RID of a particular group. // The possible values for the Attributes flags are identical to those specified in KERB_SID_AND_ATTRIBUTES type GroupMembership struct { RelativeID uint32 Attributes uint32 } // DomainGroupMembership implements https://msdn.microsoft.com/en-us/library/hh536344.aspx // DomainId: A SID structure that contains the SID for the domain.This member is used in conjunction with the GroupIds members to create group SIDs for the device. // GroupCount: A 32-bit unsigned integer that contains the number of groups within the domain to which the account belongs. // GroupIds: A pointer to a list of GROUP_MEMBERSHIP structures that contain the groups to which the account belongs in the domain. The number of groups in this list MUST be equal to GroupCount. type DomainGroupMembership struct { DomainID RPCSID `ndr:"pointer"` GroupCount uint32 GroupIDs []GroupMembership `ndr:"pointer,conformant"` // Size is value of GroupCount } rpc-2.0.3/mstypes/kerb_sid_and_attributes.go000066400000000000000000000014461375324356300212250ustar00rootroot00000000000000package mstypes // Attributes of a security group membership and can be combined by using the bitwise OR operation. // They are used by an access check mechanism to specify whether the membership is to be used in an access check decision. const ( SEGroupMandatory = 31 SEGroupEnabledByDefault = 30 SEGroupEnabled = 29 SEGroupOwner = 28 SEGroupResource = 2 //All other bits MUST be set to zero and MUST be ignored on receipt. ) // KerbSidAndAttributes implements https://msdn.microsoft.com/en-us/library/cc237947.aspx type KerbSidAndAttributes struct { SID RPCSID `ndr:"pointer"` // A pointer to an RPC_SID structure. Attributes uint32 } // SetFlag sets a flag in a uint32 attribute value. func SetFlag(a *uint32, i uint) { *a = *a | (1 << (31 - i)) } rpc-2.0.3/mstypes/reader.go000066400000000000000000000043161375324356300156140ustar00rootroot00000000000000package mstypes import ( "bufio" "encoding/binary" "fmt" "io" ) // Byte sizes of primitive types const ( SizeBool = 1 SizeChar = 1 SizeUint8 = 1 SizeUint16 = 2 SizeUint32 = 4 SizeUint64 = 8 SizeEnum = 2 SizeSingle = 4 SizeDouble = 8 SizePtr = 4 ) // Reader reads simple byte stream data into a Go representations type Reader struct { r *bufio.Reader // source of the data } // NewReader creates a new instance of a simple Reader. func NewReader(r io.Reader) *Reader { reader := new(Reader) reader.r = bufio.NewReader(r) return reader } func (r *Reader) Read(p []byte) (n int, err error) { return r.r.Read(p) } func (r *Reader) Uint8() (uint8, error) { b, err := r.r.ReadByte() if err != nil { return uint8(0), err } return uint8(b), nil } func (r *Reader) Uint16() (uint16, error) { b, err := r.ReadBytes(SizeUint16) if err != nil { return uint16(0), err } return binary.LittleEndian.Uint16(b), nil } func (r *Reader) Uint32() (uint32, error) { b, err := r.ReadBytes(SizeUint32) if err != nil { return uint32(0), err } return binary.LittleEndian.Uint32(b), nil } func (r *Reader) Uint64() (uint64, error) { b, err := r.ReadBytes(SizeUint64) if err != nil { return uint64(0), err } return binary.LittleEndian.Uint64(b), nil } func (r *Reader) FileTime() (f FileTime, err error) { f.LowDateTime, err = r.Uint32() if err != nil { return } f.HighDateTime, err = r.Uint32() if err != nil { return } return } // UTF16String returns a string that is UTF16 encoded in a byte slice. n is the number of bytes representing the string func (r *Reader) UTF16String(n int) (str string, err error) { //Length divided by 2 as each run is 16bits = 2bytes s := make([]rune, n/2, n/2) for i := 0; i < len(s); i++ { var u uint16 u, err = r.Uint16() if err != nil { return } s[i] = rune(u) } str = string(s) return } // readBytes returns a number of bytes from the NDR byte stream. func (r *Reader) ReadBytes(n int) ([]byte, error) { //TODO make this take an int64 as input to allow for larger values on all systems? b := make([]byte, n, n) m, err := r.r.Read(b) if err != nil || m != n { return b, fmt.Errorf("error reading bytes from stream: %v", err) } return b, nil } rpc-2.0.3/mstypes/rpc_unicode_string.go000066400000000000000000000016761375324356300202400ustar00rootroot00000000000000package mstypes // RPCUnicodeString implements https://msdn.microsoft.com/en-us/library/cc230365.aspx type RPCUnicodeString struct { Length uint16 // The length, in bytes, of the string pointed to by the Buffer member, not including the terminating null character if any. The length MUST be a multiple of 2. The length SHOULD equal the entire size of the Buffer, in which case there is no terminating null character. Any method that accesses this structure MUST use the Length specified instead of relying on the presence or absence of a null character. MaximumLength uint16 // The maximum size, in bytes, of the string pointed to by Buffer. The size MUST be a multiple of 2. If not, the size MUST be decremented by 1 prior to use. This value MUST not be less than Length. Value string `ndr:"pointer,conformant,varying"` } // String returns the RPCUnicodeString string value func (r *RPCUnicodeString) String() string { return r.Value } rpc-2.0.3/mstypes/rpc_unicode_string_test.go000066400000000000000000000013701375324356300212660ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "testing" "github.com/jcmturner/rpc/ndr" "github.com/stretchr/testify/assert" ) const ( TestRPCUnicodeStringBytes = "1200120004000200" + "01000000" + "0900000000000000090000007400650073007400750073006500720031000000" TestRPCUnicodeStringValue = "testuser1" ) type TestRPCUnicodeString struct { RPCStr RPCUnicodeString OtherValue uint32 } func Test_RPCUnicodeString(t *testing.T) { a := new(TestRPCUnicodeString) hexStr := TestNDRHeader + TestRPCUnicodeStringBytes b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatal(err) } assert.Equal(t, TestRPCUnicodeStringValue, a.RPCStr.Value, "String value not as expected") } rpc-2.0.3/mstypes/sid.go000066400000000000000000000027701375324356300151330ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "encoding/hex" "fmt" ) // RPCSID implements https://msdn.microsoft.com/en-us/library/cc230364.aspx type RPCSID struct { Revision uint8 // An 8-bit unsigned integer that specifies the revision level of the SID. This value MUST be set to 0x01. SubAuthorityCount uint8 // An 8-bit unsigned integer that specifies the number of elements in the SubAuthority array. The maximum number of elements allowed is 15. IdentifierAuthority [6]byte // An RPC_SID_IDENTIFIER_AUTHORITY structure that indicates the authority under which the SID was created. It describes the entity that created the SID. The Identifier Authority value {0,0,0,0,0,5} denotes SIDs created by the NT SID authority. SubAuthority []uint32 `ndr:"conformant"` // A variable length array of unsigned 32-bit integers that uniquely identifies a principal relative to the IdentifierAuthority. Its length is determined by SubAuthorityCount. } // String returns the string representation of the RPC_SID. func (s *RPCSID) String() string { var str string b := append(make([]byte, 2, 2), s.IdentifierAuthority[:]...) // For a strange reason this is read big endian: https://msdn.microsoft.com/en-us/library/dd302645.aspx i := binary.BigEndian.Uint64(b) if i >= 4294967296 { str = fmt.Sprintf("S-1-0x%s", hex.EncodeToString(s.IdentifierAuthority[:])) } else { str = fmt.Sprintf("S-1-%d", i) } for _, sub := range s.SubAuthority { str = fmt.Sprintf("%s-%d", str, sub) } return str } rpc-2.0.3/mstypes/sid_test.go000066400000000000000000000051001375324356300161600ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "testing" "github.com/jcmturner/rpc/ndr" "github.com/stretchr/testify/assert" ) type testSIDStruct struct { SID RPCSID `ndr:"pointer"` } func Test_RPCSIDDecode(t *testing.T) { var tests = []struct { Hex string SID string }{ {"040000000104000000000005150000005951b81766725d2564633b0b", "S-1-5-21-397955417-626881126-188441444"}, {"05000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000", "S-1-5-21-773533881-1816936887-355810188-513"}, {"050000000105000000000005150000005951b81766725d2564633b0b74542f00", "S-1-5-21-397955417-626881126-188441444-3101812"}, {"050000000105000000000005150000005951b81766725d2564633b0be8383200", "S-1-5-21-397955417-626881126-188441444-3291368"}, {"050000000105000000000005150000005951b81766725d2564633b0b5db43200", "S-1-5-21-397955417-626881126-188441444-3322973"}, {"050000000105000000000005150000005951b81766725d2564633b0b41163500", "S-1-5-21-397955417-626881126-188441444-3479105"}, {"050000000105000000000005150000005951b81766725d2564633b0be8ea3100", "S-1-5-21-397955417-626881126-188441444-3271400"}, {"050000000105000000000005150000005951b81766725d2564633b0bc1193200", "S-1-5-21-397955417-626881126-188441444-3283393"}, {"050000000105000000000005150000005951b81766725d2564633b0b29f13200", "S-1-5-21-397955417-626881126-188441444-3338537"}, {"050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00", "S-1-5-21-397955417-626881126-188441444-3038991"}, {"050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00", "S-1-5-21-397955417-626881126-188441444-3037999"}, {"050000000105000000000005150000005951b81766725d2564633b0bef8f3100", "S-1-5-21-397955417-626881126-188441444-3248111"}, {"050000000105000000000005150000005951b81766725d2564633b0b075f2e00", "S-1-5-21-397955417-626881126-188441444-3038983"}, {"040000000104000000000005150000004c86cebca07160e63fdce887", "S-1-5-21-3167651404-3865080224-2280184895"}, {"050000000105000000000005150000004c86cebca07160e63fdce8875a040000", "S-1-5-21-3167651404-3865080224-2280184895-1114"}, {"050000000105000000000005150000004c86cebca07160e63fdce88757040000", "S-1-5-21-3167651404-3865080224-2280184895-1111"}, } for i, test := range tests { a := new(testSIDStruct) hexStr := TestNDRHeader + "01020304" + test.Hex //The 01000000 is a dumby value for the pointer uint32 b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.SID, a.SID.String(), "SID not as expected for test %d", i+1) } } rpc-2.0.3/mstypes/user_session_key.go000066400000000000000000000004641375324356300177430ustar00rootroot00000000000000package mstypes // CypherBlock implements https://msdn.microsoft.com/en-us/library/cc237040.aspx type CypherBlock struct { Data [8]byte // size = 8 } // UserSessionKey implements https://msdn.microsoft.com/en-us/library/cc237080.aspx type UserSessionKey struct { CypherBlock [2]CypherBlock // size = 2 } rpc-2.0.3/ndr/000077500000000000000000000000001375324356300130765ustar00rootroot00000000000000rpc-2.0.3/ndr/arrays.go000066400000000000000000000307621375324356300147360ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" "strconv" ) // intFromTag returns an int that is a value in a struct tag key/value pair func intFromTag(tag reflect.StructTag, key string) (int, error) { ndrTag := parseTags(tag) d := 1 if n, ok := ndrTag.Map[key]; ok { i, err := strconv.Atoi(n) if err != nil { return d, fmt.Errorf("invalid dimensions tag [%s]: %v", n, err) } d = i } return d, nil } // parseDimensions returns the a slice of the size of each dimension and type of the member at the deepest level. func parseDimensions(v reflect.Value) (l []int, tb reflect.Type) { if v.Kind() == reflect.Ptr { v = v.Elem() } t := v.Type() if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { return } l = append(l, v.Len()) if t.Elem().Kind() == reflect.Array || t.Elem().Kind() == reflect.Slice { // contains array or slice var m []int m, tb = parseDimensions(v.Index(0)) l = append(l, m...) } else { tb = t.Elem() } return } // sliceDimensions returns the count of dimensions a slice has. func sliceDimensions(t reflect.Type) (d int, tb reflect.Type) { if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() == reflect.Slice { d++ var n int n, tb = sliceDimensions(t.Elem()) d += n } else { tb = t } return } // makeSubSlices is a deep recursive creation/initialisation of multi-dimensional slices. // Takes the reflect.Value of the 1st dimension and a slice of the lengths of the sub dimensions func makeSubSlices(v reflect.Value, l []int) { ty := v.Type().Elem() if ty.Kind() != reflect.Slice { return } for i := 0; i < v.Len(); i++ { s := reflect.MakeSlice(ty, l[0], l[0]) v.Index(i).Set(s) // Are there more sub dimensions? if len(l) > 1 { makeSubSlices(v.Index(i), l[1:]) } } return } // multiDimensionalIndexPermutations returns all the permutations of the indexes of a multi-dimensional slice. // The input is a slice of integers that indicates the max size/length of each dimension func multiDimensionalIndexPermutations(l []int) (ps [][]int) { z := make([]int, len(l), len(l)) // The zeros permutation ps = append(ps, z) // for each dimension, in reverse for i := len(l) - 1; i >= 0; i-- { ws := make([][]int, len(ps)) copy(ws, ps) //create a permutation for each of the iterations of the current dimension for j := 1; j <= l[i]-1; j++ { // For each existing permutation for _, p := range ws { np := make([]int, len(p), len(p)) copy(np, p) np[i] = j ps = append(ps, np) } } } return } // precedingMax reads off the next conformant max value func (dec *Decoder) precedingMax() uint32 { m := dec.conformantMax[0] dec.conformantMax = dec.conformantMax[1:] return m } // fillFixedArray establishes if the fixed array is uni or multi dimensional and then fills it. func (dec *Decoder) fillFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { l, t := parseDimensions(v) if t.Kind() == reflect.String { tag = reflect.StructTag(subStringArrayTag) } if len(l) < 1 { return errors.New("could not establish dimensions of fixed array") } if len(l) == 1 { err := dec.fillUniDimensionalFixedArray(v, tag, def) if err != nil { return fmt.Errorf("could not fill uni-dimensional fixed array: %v", err) } return nil } // Fixed array is multidimensional ps := multiDimensionalIndexPermutations(l[:len(l)-1]) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } // fill with the last dimension array err := dec.fillUniDimensionalFixedArray(a, tag, def) if err != nil { return fmt.Errorf("could not fill dimension %v of multi-dimensional fixed array: %v", p, err) } } return nil } // readUniDimensionalFixedArray reads an array (not slice) from the byte stream. func (dec *Decoder) fillUniDimensionalFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { for i := 0; i < v.Len(); i++ { err := dec.fill(v.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of fixed array: %v", i, err) } } return nil } // fillConformantArray establishes if the conformant array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, _ := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantArray(v, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() n := int(m) a := reflect.MakeSlice(v.Type(), n, n) for i := 0; i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantArray fills the multi-dimensional slice value provided from conformant array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantArray(v reflect.Value, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the max size of each dimensions from the ndr stream l := make([]int, d, d) for i := range l { l[i] = int(dec.precedingMax()) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional varying array: %v", err) } t := v.Type() // Total size of the array is the offset in the index being passed plus the actual count of elements being passed. n := int(s + o) a := reflect.MakeSlice(t, n, n) // Populate the array starting at the offset specified for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalVaryingArray fills the multi-dimensional slice value provided from varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read size of dimension %d: %v", i+1, err) } l[i] = int(s) + int(off) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions? for i, j := range p { if j < o[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillConformantVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional conformant varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional conformant varying array: %v", err) } if m < o+s { return errors.New("max count is less than the offset plus actual count") } t := v.Type() n := int(s) a := reflect.MakeSlice(t, n, n) for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantVaryingArray fills the multi-dimensional slice value provided from conformant varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream m := make([]int, d, d) for i := range m { m[i] = int(dec.precedingMax()) } o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read actual count of dimension %d: %v", i+1, err) } if m[i] < int(s)+int(off) { m[i] = int(s) + int(off) } l[i] = int(s) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, m[0], m[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, m[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(m) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions or max is higher than the actual count being passed for i, j := range p { if j < o[i] || j >= l[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } rpc-2.0.3/ndr/arrays_test.go000066400000000000000000000150501375324356300157660ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" ) const TestHeader = "01100800cccccccca00400000000000000000200" func TestParseDimensions(t *testing.T) { a := [2][2][2][]SimpleTest{} l, ta := parseDimensions(reflect.ValueOf(a)) assert.Equal(t, 4, len(l), "dimension count not as expected") assert.Equal(t, []int{2, 2, 2, 0}, l, "lengths list not as expected") assert.Equal(t, "SimpleTest", ta.Name(), "type within array not as expected") } func TestMakeSubSlices(t *testing.T) { l := []int{2, 5, 3, 1} a := new([][][][]uint32) v := reflect.ValueOf(a) v = v.Elem() ty := v.Type() s := reflect.MakeSlice(ty, l[0], l[0]) v.Set(s) makeSubSlices(v, l[1:]) assert.Equal(t, "[[[[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]]] [[[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]]]]", fmt.Sprintf("%v", *a)) } func TestDimensionCountFromTag(t *testing.T) { var a StructWithMultiDimensionalConformantSlice v := reflect.ValueOf(a) d, err := intFromTag(v.Type().Field(0).Tag, "test") if err != nil { t.Errorf("error getting dimensions from tag: %v", err) } assert.Equal(t, 3, d, "number of dimensions not as expected") } type StructWithArray struct { A [4]uint32 } type StructWithMultiDimArray struct { A [2][3][2]uint32 } type StructWithConformantSlice struct { A []uint32 `ndr:"conformant"` } type StructWithVaryingSlice struct { A []uint32 `ndr:"varying"` } type StructWithConformantVaryingSlice struct { A []uint32 `ndr:"conformant,varying"` } type StructWithMultiDimensionalConformantSlice struct { A [][][]uint32 `ndr:"conformant,test:3"` } type StructWithMultiDimensionalVaryingSlice struct { A [][][]uint32 `ndr:"varying"` } type StructWithMultiDimensionalConformantVaryingSlice struct { A [][][]uint32 `ndr:"conformant,varying"` } func TestReadUniDimensionalFixedArray(t *testing.T) { hexStr := TestHeader + "01000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalFixedArray(t *testing.T) { hexStr := TestHeader + "0100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [2][3][2]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional fixed array not as expected") } func TestReadUniDimensionalConformantArray(t *testing.T) { hexStr := TestHeader + "0400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithConformantSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalConformantArray(t *testing.T) { hexStr := TestHeader + "0200000003000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalConformantSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant array not as expected") } func TestReadUniDimensionalVaryingArray(t *testing.T) { hexStr := TestHeader + "000000000400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalVaryingArray(t *testing.T) { hexStr := TestHeader + "0000000002000000000000000300000000000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant varying array not as expected") } func TestReadUniDimensionalConformantVaryingArray(t *testing.T) { hexStr := TestHeader + "04000000000000000400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithConformantVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalConformantVaryingArray(t *testing.T) { hexStr := TestHeader + "0200000003000000020000000000000002000000000000000300000000000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalConformantVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant varying array not as expected") } rpc-2.0.3/ndr/decoder.go000066400000000000000000000277331375324356300150460ustar00rootroot00000000000000// Package ndr provides the ability to unmarshal NDR encoded byte steams into Go data structures package ndr import ( "bufio" "fmt" "io" "reflect" "strings" ) // Struct tag values const ( TagConformant = "conformant" TagVarying = "varying" TagPointer = "pointer" TagPipe = "pipe" ) // Decoder unmarshals NDR byte stream data into a Go struct representation type Decoder struct { r *bufio.Reader // source of the data size int // initial size of bytes in buffer ch CommonHeader // NDR common header ph PrivateHeader // NDR private header conformantMax []uint32 // conformant max values that were moved to the beginning of the structure s interface{} // pointer to the structure being populated current []string // keeps track of the current field being populated } type deferedPtr struct { v reflect.Value tag reflect.StructTag } // NewDecoder creates a new instance of a NDR Decoder. func NewDecoder(r io.Reader) *Decoder { dec := new(Decoder) dec.r = bufio.NewReader(r) dec.r.Peek(int(commonHeaderBytes)) // For some reason an operation is needed on the buffer to initialise it so Buffered() != 0 dec.size = dec.r.Buffered() return dec } // Decode unmarshals the NDR encoded bytes into the pointer of a struct provided. func (dec *Decoder) Decode(s interface{}) error { dec.s = s err := dec.readCommonHeader() if err != nil { return err } err = dec.readPrivateHeader() if err != nil { return err } _, err = dec.r.Discard(4) //The next 4 bytes are an RPC unique pointer referent. We just skip these. if err != nil { return Errorf("unable to process byte stream: %v", err) } return dec.process(s, reflect.StructTag("")) } func (dec *Decoder) process(s interface{}, tag reflect.StructTag) error { // Scan for conformant fields as their max counts are moved to the beginning // http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_37 err := dec.scanConformantArrays(s, tag) if err != nil { return err } // Recursively fill the struct fields var localDef []deferedPtr err = dec.fill(s, tag, &localDef) if err != nil { return Errorf("could not decode: %v", err) } // Read any deferred referents associated with pointers for _, p := range localDef { err = dec.process(p.v, p.tag) if err != nil { return fmt.Errorf("could not decode deferred referent: %v", err) } } return nil } // scanConformantArrays scans the structure for embedded conformant fields and captures the maximum element counts for // dimensions of the array that are moved to the beginning of the structure. func (dec *Decoder) scanConformantArrays(s interface{}, tag reflect.StructTag) error { err := dec.conformantScan(s, tag) if err != nil { return fmt.Errorf("failed to scan for embedded conformant arrays: %v", err) } for i := range dec.conformantMax { dec.conformantMax[i], err = dec.readUint32() if err != nil { return fmt.Errorf("could not read preceding conformant max count index %d: %v", i, err) } } return nil } // conformantScan inspects the structure's fields for whether they are conformant. func (dec *Decoder) conformantScan(s interface{}, tag reflect.StructTag) error { ndrTag := parseTags(tag) if ndrTag.HasValue(TagPointer) { return nil } v := getReflectValue(s) switch v.Kind() { case reflect.Struct: for i := 0; i < v.NumField(); i++ { err := dec.conformantScan(v.Field(i), v.Type().Field(i).Tag) if err != nil { return err } } case reflect.String: if !ndrTag.HasValue(TagConformant) { break } dec.conformantMax = append(dec.conformantMax, uint32(0)) case reflect.Slice: if !ndrTag.HasValue(TagConformant) { break } d, t := sliceDimensions(v.Type()) for i := 0; i < d; i++ { dec.conformantMax = append(dec.conformantMax, uint32(0)) } // For string arrays there is a common max for the strings within the array. if t.Kind() == reflect.String { dec.conformantMax = append(dec.conformantMax, uint32(0)) } } return nil } func (dec *Decoder) isPointer(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) (bool, error) { // Pointer so defer filling the referent ndrTag := parseTags(tag) if ndrTag.HasValue(TagPointer) { p, err := dec.readUint32() if err != nil { return true, fmt.Errorf("could not read pointer: %v", err) } ndrTag.delete(TagPointer) if p != 0 { // if pointer is not zero add to the deferred items at end of stream *def = append(*def, deferedPtr{v, ndrTag.StructTag()}) } return true, nil } return false, nil } func getReflectValue(s interface{}) (v reflect.Value) { if r, ok := s.(reflect.Value); ok { v = r } else { if reflect.ValueOf(s).Kind() == reflect.Ptr { v = reflect.ValueOf(s).Elem() } } return } // fill populates fields with values from the NDR byte stream. func (dec *Decoder) fill(s interface{}, tag reflect.StructTag, localDef *[]deferedPtr) error { v := getReflectValue(s) //// Pointer so defer filling the referent ptr, err := dec.isPointer(v, tag, localDef) if err != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(dec.current, "/"), err) } if ptr { return nil } // Populate the value from the byte stream switch v.Kind() { case reflect.Struct: dec.current = append(dec.current, v.Type().Name()) //Track the current field being filled // in case struct is a union, track this and the selected union field for efficiency var unionTag reflect.Value var unionField string // field to fill if struct is a union // Go through each field in the struct and recursively fill for i := 0; i < v.NumField(); i++ { fieldName := v.Type().Field(i).Name dec.current = append(dec.current, fieldName) //Track the current field being filled //fmt.Fprintf(os.Stderr, "DEBUG Decoding: %s\n", strings.Join(dec.current, "/")) structTag := v.Type().Field(i).Tag ndrTag := parseTags(structTag) // Union handling if !unionTag.IsValid() { // Is this field a union tag? unionTag = dec.isUnion(v.Field(i), structTag) } else { // What is the selected field value of the union if we don't already know if unionField == "" { unionField, err = unionSelectedField(v, unionTag) if err != nil { return fmt.Errorf("could not determine selected union value field for %s with discriminat"+ " tag %s: %v", v.Type().Name(), unionTag, err) } } if ndrTag.HasValue(TagUnionField) && fieldName != unionField { // is a union and this field has not been selected so will skip it. dec.current = dec.current[:len(dec.current)-1] //This field has been skipped so remove it from the current field tracker continue } } // Check if field is a pointer if v.Field(i).Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && v.Field(i).Type().Kind() == reflect.Slice && v.Field(i).Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes structTag, err = addSizeToTag(v, v.Field(i), structTag) if err != nil { return fmt.Errorf("could not get rawbytes field(%s) size: %v", strings.Join(dec.current, "/"), err) } ptr, err := dec.isPointer(v.Field(i), structTag, localDef) if err != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(dec.current, "/"), err) } if !ptr { err := dec.readRawBytes(v.Field(i), structTag) if err != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(dec.current, "/"), err) } } } else { err := dec.fill(v.Field(i), structTag, localDef) if err != nil { return fmt.Errorf("could not fill struct field(%s): %v", strings.Join(dec.current, "/"), err) } } dec.current = dec.current[:len(dec.current)-1] //This field has been filled so remove it from the current field tracker } dec.current = dec.current[:len(dec.current)-1] //This field has been filled so remove it from the current field tracker case reflect.Bool: i, err := dec.readBool() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint8: i, err := dec.readUint8() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint16: i, err := dec.readUint16() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint32: i, err := dec.readUint32() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint64: i, err := dec.readUint64() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int8: i, err := dec.readInt8() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int16: i, err := dec.readInt16() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int32: i, err := dec.readInt32() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int64: i, err := dec.readInt64() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.String: ndrTag := parseTags(tag) conformant := ndrTag.HasValue(TagConformant) // strings are always varying so this is assumed without an explicit tag var s string var err error if conformant { s, err = dec.readConformantVaryingString(localDef) if err != nil { return fmt.Errorf("could not fill with conformant varying string: %v", err) } } else { s, err = dec.readVaryingString(localDef) if err != nil { return fmt.Errorf("could not fill with varying string: %v", err) } } v.Set(reflect.ValueOf(s)) case reflect.Float32: i, err := dec.readFloat32() if err != nil { return fmt.Errorf("could not fill %v: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Float64: i, err := dec.readFloat64() if err != nil { return fmt.Errorf("could not fill %v: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Array: err := dec.fillFixedArray(v, tag, localDef) if err != nil { return err } case reflect.Slice: if v.Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && v.Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes err := dec.readRawBytes(v, tag) if err != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(dec.current, "/"), err) } break } ndrTag := parseTags(tag) conformant := ndrTag.HasValue(TagConformant) varying := ndrTag.HasValue(TagVarying) if ndrTag.HasValue(TagPipe) { err := dec.fillPipe(v, tag) if err != nil { return err } break } _, t := sliceDimensions(v.Type()) if t.Kind() == reflect.String && !ndrTag.HasValue(subStringArrayValue) { // String array err := dec.readStringsArray(v, tag, localDef) if err != nil { return err } break } // varying is assumed as fixed arrays use the Go array type rather than slice if conformant && varying { err := dec.fillConformantVaryingArray(v, tag, localDef) if err != nil { return err } } else if !conformant && varying { err := dec.fillVaryingArray(v, tag, localDef) if err != nil { return err } } else { //default to conformant and not varying err := dec.fillConformantArray(v, tag, localDef) if err != nil { return err } } default: return fmt.Errorf("unsupported type") } return nil } // readBytes returns a number of bytes from the NDR byte stream. func (dec *Decoder) readBytes(n int) ([]byte, error) { //TODO make this take an int64 as input to allow for larger values on all systems? b := make([]byte, n, n) m, err := dec.r.Read(b) if err != nil || m != n { return b, fmt.Errorf("error reading bytes from stream: %v", err) } return b, nil } rpc-2.0.3/ndr/decoder_test.go000066400000000000000000000077751375324356300161110ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func TestReadCommonHeader(t *testing.T) { var tests = []struct { EncodedHex string ExpectFail bool }{ {"01100800cccccccc", false}, // Little Endian {"01000008cccccccc", false}, // Big Endian have to change the bytes for the header size? This test vector was artificially created. Need proper test vector //{"01100800cccccccc1802000000000000", false}, //{"01100800cccccccc0002000000000000", false}, //{"01100800cccccccc0001000000000000", false}, //{"01100800cccccccce000000000000000", false}, //{"01100800ccccccccf000000000000000", false}, //{"01100800cccccccc7801000000000000", false}, //{"01100800cccccccc4801000000000000", false}, //{"01100800ccccccccd001000000000000", false}, {"02100800cccccccc", true}, // Incorrect version {"02100900cccccccc", true}, // Incorrect length } for i, test := range tests { b, _ := hex.DecodeString(test.EncodedHex) dec := NewDecoder(bytes.NewReader(b)) err := dec.readCommonHeader() if err != nil && !test.ExpectFail { t.Errorf("error reading common header of test %d: %v", i, err) } if err == nil && test.ExpectFail { t.Errorf("expected failure on reading common header of test %d: %v", i, err) } } } func TestReadPrivateHeader(t *testing.T) { var tests = []struct { EncodedHex string ExpectFail bool Length int }{ {"01100800cccccccc1802000000000000", false, 536}, {"01100800cccccccc0002000000000000", false, 512}, {"01100800cccccccc0001000000000000", false, 256}, {"01100800ccccccccFF00000000000000", true, 255}, // Length not multiple of 8 {"01100800cccccccc00010000000000", true, 256}, // Too short } for i, test := range tests { b, _ := hex.DecodeString(test.EncodedHex) dec := NewDecoder(bytes.NewReader(b)) err := dec.readCommonHeader() if err != nil { t.Errorf("error reading common header of test %d: %v", i, err) } err = dec.readPrivateHeader() if err != nil && !test.ExpectFail { t.Errorf("error reading private header of test %d: %v", i, err) } if err == nil && test.ExpectFail { t.Errorf("expected failure on reading private header of test %d: %v", i, err) } if dec.ph.ObjectBufferLength != uint32(test.Length) { t.Errorf("Objectbuffer length expected %d actual %d", test.Length, dec.ph.ObjectBufferLength) } } } type SimpleTest struct { A uint32 B uint32 } func TestBasicDecode(t *testing.T) { hexStr := "01100800cccccccca00400000000000000000200d186660f656ac601" b, _ := hex.DecodeString(hexStr) ft := new(SimpleTest) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err != nil { t.Fatalf("error decoding: %v", err) } assert.Equal(t, uint32(258377425), ft.A, "Value of field A not as expected") assert.Equal(t, uint32(29780581), ft.B, "Value of field B not as expected %d") } func TestBasicDecodeOverRun(t *testing.T) { hexStr := "01100800cccccccca00400000000000000000200d186660f" b, _ := hex.DecodeString(hexStr) ft := new(SimpleTest) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err == nil { t.Errorf("Expected error for trying to read more than the bytes we have") } } type testEmbeddingPointer struct { A testEmbeddedPointer `ndr:"pointer"` B uint32 // 1 } type testEmbeddedPointer struct { C testEmbeddedPointer2 `ndr:"pointer"` D uint32 `ndr:"pointer"` // 2 E uint32 // 3 } type testEmbeddedPointer2 struct { F uint32 `ndr:"pointer"` // 4 G uint32 // 5 } func Test_EmbeddedPointers(t *testing.T) { hexStr := TestHeader + "00040002" + "01000000" + "00040002" + "00040002" + "03000000" + "00040002" + "05000000" + "04000000" + "02000000" b, _ := hex.DecodeString(hexStr) ft := new(testEmbeddingPointer) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err != nil { t.Fatalf("error decoding: %v", err) } assert.Equal(t, uint32(1), ft.B) assert.Equal(t, uint32(2), ft.A.D) assert.Equal(t, uint32(3), ft.A.E) assert.Equal(t, uint32(4), ft.A.C.F) assert.Equal(t, uint32(5), ft.A.C.G) } rpc-2.0.3/ndr/error.go000066400000000000000000000007421375324356300145610ustar00rootroot00000000000000package ndr import "fmt" // Malformed implements the error interface for malformed NDR encoding errors. type Malformed struct { EText string } // Error implements the error interface on the Malformed struct. func (e Malformed) Error() string { return fmt.Sprintf("malformed NDR stream: %s", e.EText) } // Errorf formats an error message into a malformed NDR error. func Errorf(format string, a ...interface{}) Malformed { return Malformed{EText: fmt.Sprintf(format, a...)} } rpc-2.0.3/ndr/header.go000066400000000000000000000076541375324356300146710ustar00rootroot00000000000000package ndr import ( "encoding/binary" "fmt" ) /* Serialization Version 1 https://msdn.microsoft.com/en-us/library/cc243563.aspx Common Header - https://msdn.microsoft.com/en-us/library/cc243890.aspx 8 bytes in total: - First byte - Version: Must equal 1 - Second byte - 1st 4 bits: Endianess (0=Big; 1=Little); 2nd 4 bits: Character Encoding (0=ASCII; 1=EBCDIC) - 3rd - Floating point representation (This does not seem to be the case in examples for Microsoft test sources) - 4th - Common Header Length: Must equal 8 - 5th - 8th - Filler: MUST be set to 0xcccccccc on marshaling, and SHOULD be ignored during unmarshaling. Private Header - https://msdn.microsoft.com/en-us/library/cc243919.aspx 8 bytes in total: - First 4 bytes - Indicates the length of a serialized top-level type in the octet stream. It MUST include the padding length and exclude the header itself. - Second 4 bytes - Filler: MUST be set to 0 (zero) during marshaling, and SHOULD be ignored during unmarshaling. */ const ( protocolVersion uint8 = 1 commonHeaderBytes uint16 = 8 bigEndian = 0 littleEndian = 1 ascii uint8 = 0 ebcdic uint8 = 1 ieee uint8 = 0 vax uint8 = 1 cray uint8 = 2 ibm uint8 = 3 ) // CommonHeader implements the NDR common header: https://msdn.microsoft.com/en-us/library/cc243889.aspx type CommonHeader struct { Version uint8 Endianness binary.ByteOrder CharacterEncoding uint8 FloatRepresentation uint8 HeaderLength uint16 Filler []byte } // PrivateHeader implements the NDR private header: https://msdn.microsoft.com/en-us/library/cc243919.aspx type PrivateHeader struct { ObjectBufferLength uint32 Filler []byte } func (dec *Decoder) readCommonHeader() error { // Version vb, err := dec.r.ReadByte() if err != nil { return Malformed{EText: "could not read first byte of common header for version"} } dec.ch.Version = uint8(vb) if dec.ch.Version != protocolVersion { return Malformed{EText: fmt.Sprintf("byte stream does not indicate a RPC Type serialization of version %v", protocolVersion)} } // Read Endianness & Character Encoding eb, err := dec.r.ReadByte() if err != nil { return Malformed{EText: "could not read second byte of common header for endianness"} } endian := int(eb >> 4 & 0xF) if endian != 0 && endian != 1 { return Malformed{EText: "common header does not indicate a valid endianness"} } dec.ch.CharacterEncoding = uint8(vb & 0xF) if dec.ch.CharacterEncoding != 0 && dec.ch.CharacterEncoding != 1 { return Malformed{EText: "common header does not indicate a valid character encoding"} } switch endian { case littleEndian: dec.ch.Endianness = binary.LittleEndian case bigEndian: dec.ch.Endianness = binary.BigEndian } // Common header length lb, err := dec.readBytes(2) if err != nil { return Malformed{EText: fmt.Sprintf("could not read common header length: %v", err)} } dec.ch.HeaderLength = dec.ch.Endianness.Uint16(lb) if dec.ch.HeaderLength != commonHeaderBytes { return Malformed{EText: "common header does not indicate a valid length"} } // Filler bytes dec.ch.Filler, err = dec.readBytes(4) if err != nil { return Malformed{EText: fmt.Sprintf("could not read common header filler: %v", err)} } return nil } func (dec *Decoder) readPrivateHeader() error { // The next 8 bytes after the common header comprise the RPC type marshalling private header for constructed types. err := binary.Read(dec.r, dec.ch.Endianness, &dec.ph.ObjectBufferLength) if err != nil { return Malformed{EText: "could not read private header object buffer length"} } if dec.ph.ObjectBufferLength%8 != 0 { return Malformed{EText: "object buffer length not a multiple of 8"} } // Filler bytes dec.ph.Filler, err = dec.readBytes(4) if err != nil { return Malformed{EText: fmt.Sprintf("could not read private header filler: %v", err)} } return nil } rpc-2.0.3/ndr/pipe.go000066400000000000000000000013141375324356300143610ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" ) func (dec *Decoder) fillPipe(v reflect.Value, tag reflect.StructTag) error { s, err := dec.readUint32() // read element count of first chunk if err != nil { return err } a := reflect.MakeSlice(v.Type(), 0, 0) c := reflect.MakeSlice(v.Type(), int(s), int(s)) for s != 0 { for i := 0; i < int(s); i++ { err := dec.fill(c.Index(i), tag, &[]deferedPtr{}) if err != nil { return fmt.Errorf("could not fill element %d of pipe: %v", i, err) } } s, err = dec.readUint32() // read element count of first chunk if err != nil { return err } a = reflect.AppendSlice(a, c) c = reflect.MakeSlice(v.Type(), int(s), int(s)) } v.Set(a) return nil } rpc-2.0.3/ndr/pipe_test.go000066400000000000000000000011121375324356300154140ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const testPipe = "04000000010000000200000003000000040000000300000001000000020000000300000000000000" type structWithPipe struct { A []uint32 `ndr:"pipe"` } func TestFillPipe(t *testing.T) { hexStr := TestHeader + testPipe b, _ := hex.DecodeString(hexStr) a := new(structWithPipe) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } tp := []uint32{1, 2, 3, 4, 1, 2, 3} assert.Equal(t, tp, a.A, "Value of pipe not as expected") } rpc-2.0.3/ndr/primitives.go000066400000000000000000000123711375324356300156240ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/binary" "math" ) // Byte sizes of primitive types const ( SizeBool = 1 SizeChar = 1 SizeUint8 = 1 SizeUint16 = 2 SizeUint32 = 4 SizeUint64 = 8 SizeEnum = 2 SizeSingle = 4 SizeDouble = 8 SizePtr = 4 ) // Bool is an NDR Boolean which is a logical quantity that assumes one of two values: TRUE or FALSE. // NDR represents a Boolean as one octet. // It represents a value of FALSE as a zero octet, an octet in which every bit is reset. // It represents a value of TRUE as a non-zero octet, an octet in which one or more bits are set. // Char is an NDR character. // NDR represents a character as one octet. // Characters have two representation formats: ASCII and EBCDIC. // USmall is an unsigned 8 bit integer // UShort is an unsigned 16 bit integer // ULong is an unsigned 32 bit integer // UHyper is an unsigned 64 bit integer // Small is an signed 8 bit integer // Short is an signed 16 bit integer // Long is an signed 32 bit integer // Hyper is an signed 64 bit integer // Enum is the NDR representation of enumerated types as signed short integers (2 octets) // Single is an NDR defined single-precision floating-point data type // Double is an NDR defined double-precision floating-point data type // readBool reads a byte representing a boolean. // NDR represents a Boolean as one octet. // It represents a value of FALSE as a zero octet, an octet in which every bit is reset. // It represents a value of TRUE as a non-zero octet, an octet in which one or more bits are set. func (dec *Decoder) readBool() (bool, error) { i, err := dec.readUint8() if err != nil { return false, err } if i != 0 { return true, nil } return false, nil } // readChar reads bytes representing a 8bit ASCII integer cast to a rune. func (dec *Decoder) readChar() (rune, error) { var r rune a, err := dec.readUint8() if err != nil { return r, err } return rune(a), nil } // readUint8 reads bytes representing a 8bit unsigned integer. func (dec *Decoder) readUint8() (uint8, error) { b, err := dec.r.ReadByte() if err != nil { return uint8(0), err } return uint8(b), nil } // readUint16 reads bytes representing a 16bit unsigned integer. func (dec *Decoder) readUint16() (uint16, error) { dec.ensureAlignment(SizeUint16) b, err := dec.readBytes(SizeUint16) if err != nil { return uint16(0), err } return dec.ch.Endianness.Uint16(b), nil } // readUint32 reads bytes representing a 32bit unsigned integer. func (dec *Decoder) readUint32() (uint32, error) { dec.ensureAlignment(SizeUint32) b, err := dec.readBytes(SizeUint32) if err != nil { return uint32(0), err } return dec.ch.Endianness.Uint32(b), nil } // readUint32 reads bytes representing a 32bit unsigned integer. func (dec *Decoder) readUint64() (uint64, error) { dec.ensureAlignment(SizeUint64) b, err := dec.readBytes(SizeUint64) if err != nil { return uint64(0), err } return dec.ch.Endianness.Uint64(b), nil } func (dec *Decoder) readInt8() (int8, error) { dec.ensureAlignment(SizeUint8) b, err := dec.readBytes(SizeUint8) if err != nil { return 0, err } var i int8 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt16() (int16, error) { dec.ensureAlignment(SizeUint16) b, err := dec.readBytes(SizeUint16) if err != nil { return 0, err } var i int16 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt32() (int32, error) { dec.ensureAlignment(SizeUint32) b, err := dec.readBytes(SizeUint32) if err != nil { return 0, err } var i int32 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt64() (int64, error) { dec.ensureAlignment(SizeUint64) b, err := dec.readBytes(SizeUint64) if err != nil { return 0, err } var i int64 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } // https://en.wikipedia.org/wiki/IEEE_754-1985 func (dec *Decoder) readFloat32() (f float32, err error) { dec.ensureAlignment(SizeSingle) b, err := dec.readBytes(SizeSingle) if err != nil { return } bits := dec.ch.Endianness.Uint32(b) f = math.Float32frombits(bits) return } func (dec *Decoder) readFloat64() (f float64, err error) { dec.ensureAlignment(SizeDouble) b, err := dec.readBytes(SizeDouble) if err != nil { return } bits := dec.ch.Endianness.Uint64(b) f = math.Float64frombits(bits) return } // NDR enforces NDR alignment of primitive data; that is, any primitive of size n octets is aligned at a octet stream // index that is a multiple of n. (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates // the number of an octet in an octet stream when octets are numbered, beginning with 0, from the first octet in the // stream. Where necessary, an alignment gap, consisting of octets of unspecified value, precedes the representation // of a primitive. The gap is of the smallest size sufficient to align the primitive. func (dec *Decoder) ensureAlignment(n int) { p := dec.size - dec.r.Buffered() if s := p % n; s != 0 { dec.r.Discard(n - s) } } rpc-2.0.3/ndr/primitives_test.go000066400000000000000000000021511375324356300166560ustar00rootroot00000000000000package ndr import ( "bufio" "bytes" "encoding/binary" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func TestReadFloat32(t *testing.T) { tests := []struct { hexStr string value float32 order binary.ByteOrder }{ {"3E200000", 0.15625, binary.BigEndian}, {"00000000", 0.0, binary.BigEndian}, {"3F800000", 1.0, binary.BigEndian}, {"BF800000", -1.0, binary.BigEndian}, {"00000001", 1.4e-45, binary.BigEndian}, {"00400000", 5.877472e-39, binary.BigEndian}, {"007FFFFF", 1.1754942e-38, binary.BigEndian}, {"00800000", 1.1754944e-38, binary.BigEndian}, {"7F7FFFFF", 3.4028235e38, binary.BigEndian}, //TODO need some littleendian test vectors } for i, test := range tests { b, _ := hex.DecodeString(test.hexStr) //t.Logf("%s %08b\n", test.hexStr,b) r := bufio.NewReader(bytes.NewReader(b)) dec := Decoder{ r: r, ch: CommonHeader{Endianness: test.order}, } f, err := dec.readFloat32() if err != nil { t.Errorf("could not read float32 test %d: %v", i, err) } assert.Equal(t, test.value, f, "float32 not as expect for test %d: %s", i, test.hexStr) } } rpc-2.0.3/ndr/rawbytes.go000066400000000000000000000026471375324356300152760ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" "strconv" ) // type MyBytes []byte // implement RawBytes interface const ( sizeMethod = "Size" ) // RawBytes interface should be implemented if reading just a number of bytes from the NDR stream type RawBytes interface { Size(interface{}) int } func rawBytesSize(parent reflect.Value, v reflect.Value) (int, error) { sf := v.MethodByName(sizeMethod) if !sf.IsValid() { return 0, fmt.Errorf("could not find a method called %s on the implementation of RawBytes", sizeMethod) } in := []reflect.Value{parent} f := sf.Call(in) if f[0].Kind() != reflect.Int { return 0, errors.New("the RawBytes size function did not return an integer") } return int(f[0].Int()), nil } func addSizeToTag(parent reflect.Value, v reflect.Value, tag reflect.StructTag) (reflect.StructTag, error) { size, err := rawBytesSize(parent, v) if err != nil { return tag, err } ndrTag := parseTags(tag) ndrTag.Map["size"] = strconv.Itoa(size) return ndrTag.StructTag(), nil } func (dec *Decoder) readRawBytes(v reflect.Value, tag reflect.StructTag) error { ndrTag := parseTags(tag) sizeStr, ok := ndrTag.Map["size"] if !ok { return errors.New("size tag not available") } size, err := strconv.Atoi(sizeStr) if err != nil { return fmt.Errorf("size not valid: %v", err) } b, err := dec.readBytes(size) if err != nil { return err } v.Set(reflect.ValueOf(b).Convert(v.Type())) return nil } rpc-2.0.3/ndr/strings.go000066400000000000000000000030171375324356300151170ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" ) const ( subStringArrayTag = `ndr:"varying,X-subStringArray"` subStringArrayValue = "X-subStringArray" ) func uint16SliceToString(a []uint16) string { s := make([]rune, len(a), len(a)) for i := range s { s[i] = rune(a[i]) } if len(s) > 0 { // Remove any null terminator if s[len(s)-1] == rune(0) { s = s[:len(s)-1] } } return string(s) } func (dec *Decoder) readVaryingString(def *[]deferedPtr) (string, error) { a := new([]uint16) v := reflect.ValueOf(a) var t reflect.StructTag err := dec.fillUniDimensionalVaryingArray(v.Elem(), t, def) if err != nil { return "", err } s := uint16SliceToString(*a) return s, nil } func (dec *Decoder) readConformantVaryingString(def *[]deferedPtr) (string, error) { a := new([]uint16) v := reflect.ValueOf(a) var t reflect.StructTag err := dec.fillUniDimensionalConformantVaryingArray(v.Elem(), t, def) if err != nil { return "", err } s := uint16SliceToString(*a) return s, nil } func (dec *Decoder) readStringsArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, _ := sliceDimensions(v.Type()) ndrTag := parseTags(tag) var m []int //var ms int if ndrTag.HasValue(TagConformant) { for i := 0; i < d; i++ { m = append(m, int(dec.precedingMax())) } //common max size _ = dec.precedingMax() //ms = int(n) } tag = reflect.StructTag(subStringArrayTag) err := dec.fillVaryingArray(v, tag, def) if err != nil { return fmt.Errorf("could not read string array: %v", err) } return nil } rpc-2.0.3/ndr/strings_test.go000066400000000000000000000203231375324356300161550ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/binary" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const ( TestStr = "hello world!" TestStrUTF16Hex = "680065006c006c006f00200077006f0072006c00640021000000" // little endian format ) type TestStructWithVaryingString struct { A string `ndr:"varying"` } type TestStructWithConformantVaryingString struct { A string `ndr:"conformant,varying"` } type TestStructWithConformantVaryingStringUniArray struct { A []string `ndr:"conformant,varying"` } // Should not have to specify varying tag type TestStructWithNonConformantStringUniArray struct { A []string } type TestStructWithConformantVaryingStringMultiArray struct { A [][][]string `ndr:"conformant,varying"` } // Should not have to specify varying tag type TestStructWithNonConformantStringMultiArray struct { A [][][]string } // Strings are always varying but the array may not be type TestStructWithFixedStringUniArray struct { A [4]string } type TestStructWithFixedStringMultiArray struct { A [2][3][2]string } func Test_uint16SliceToString(t *testing.T) { b, _ := hex.DecodeString(TestStrUTF16Hex) var u []uint16 for i := 0; i < len(b); i += 2 { u = append(u, binary.LittleEndian.Uint16(b[i:i+2])) } s := uint16SliceToString(u) assert.Equal(t, TestStr, s, "uint16SliceToString did not return as expected") } func Test_readVaryingString(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := TestHeader + "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // header:offset(0):actual count:data b, _ := hex.DecodeString(hexStr) a := new(TestStructWithVaryingString) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, TestStr, a.A, "value of decoded varying string not as expected") } func Test_readConformantVaryingString(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := TestHeader + hex.EncodeToString(ac) + "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // header:max:offset(0):actual count:data b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingString) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, TestStr, a.A, "value of decoded varying string not as expected") } func Test_readConformantStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + "04000000" + hex.EncodeToString(ac) + "0000000004000000" + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:1st dimension count(4):max for all strings:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, 4, len(a.A), "length of string array not as expected") for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readConformantStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + "02000000" + "03000000" + "02000000" + hex.EncodeToString(ac) + "0000000002000000" + "0000000003000000" + "0000000002000000" + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } func Test_readNonConformantStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + "0000000004000000" + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithNonConformantStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, 4, len(a.A), "length of string array not as expected") for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readNonConformantStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + "0000000002000000" + "0000000003000000" + "0000000002000000" + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithNonConformantStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } func Test_readFixedStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithFixedStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readFixedStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithFixedStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [2][3][2]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } rpc-2.0.3/ndr/tags.go000066400000000000000000000025111375324356300143620ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" "strings" ) const ndrNameSpace = "ndr" type tags struct { Values []string Map map[string]string } // parse the struct field tags and extract the ndr related ones. // format of tag ndr:"value,key:value1,value2" func parseTags(st reflect.StructTag) tags { s := st.Get(ndrNameSpace) t := tags{ Values: []string{}, Map: make(map[string]string), } if s != "" { ndrTags := strings.Trim(s, `"`) for _, tag := range strings.Split(ndrTags, ",") { if strings.Contains(tag, ":") { m := strings.SplitN(tag, ":", 2) t.Map[m[0]] = m[1] } else { t.Values = append(t.Values, tag) } } } return t } func appendTag(t reflect.StructTag, s string) reflect.StructTag { ts := t.Get(ndrNameSpace) ts = fmt.Sprintf(`%s"%s,%s"`, ndrNameSpace, ts, s) return reflect.StructTag(ts) } func (t *tags) StructTag() reflect.StructTag { mv := t.Values for key, val := range t.Map { mv = append(mv, key+":"+val) } s := ndrNameSpace + ":" + `"` + strings.Join(mv, ",") + `"` return reflect.StructTag(s) } func (t *tags) delete(s string) { for i, x := range t.Values { if x == s { t.Values = append(t.Values[:i], t.Values[i+1:]...) } } delete(t.Map, s) } func (t *tags) HasValue(s string) bool { for _, v := range t.Values { if v == s { return true } } return false } rpc-2.0.3/ndr/tags_test.go000066400000000000000000000023411375324356300154220ustar00rootroot00000000000000package ndr import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) type Test struct { A int `ndr:"value"` B int `ndr:"key:value"` C int `ndr:"value1,key:value2"` D int `dr:"value"` } func TestParseTags(t *testing.T) { var test Test tag0 := reflect.TypeOf(test).Field(0).Tag tag1 := reflect.TypeOf(test).Field(1).Tag tag2 := reflect.TypeOf(test).Field(2).Tag tag3 := reflect.TypeOf(test).Field(3).Tag tg0 := parseTags(tag0) tg1 := parseTags(tag1) tg2 := parseTags(tag2) tg3 := parseTags(tag3) assert.Equal(t, []string{"value"}, tg0.Values, "Values not as expected for test %d", 0) assert.Equal(t, make(map[string]string), tg0.Map, "Map not as expected for test %d", 0) assert.Equal(t, []string{}, tg1.Values, "Values not as expected for test %d", 1) assert.Equal(t, map[string]string{"key": "value"}, tg1.Map, "Map not as expected for test %d", 1) assert.Equal(t, []string{"value1"}, tg2.Values, "Values not as expected for test %d", 2) assert.Equal(t, map[string]string{"key": "value2"}, tg2.Map, "Map not as expected for test %d", 2) assert.Equal(t, []string{}, tg3.Values, "Values not as expected for test %d", 3) assert.Equal(t, make(map[string]string), tg3.Map, "Map not as expected for test %d", 3) } rpc-2.0.3/ndr/union.go000066400000000000000000000042771375324356300145670ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" ) // Union interface must be implemented by structs that will be unmarshaled into from the NDR byte stream union representation. // The union's discriminating tag will be passed to the SwitchFunc method. // The discriminating tag field must have the struct tag: `ndr:"unionTag"` // If the union is encapsulated the discriminating tag field must have the struct tag: `ndr:"encapsulated"` // The possible value fields that can be selected from must have the struct tag: `ndr:"unionField"` type Union interface { SwitchFunc(t interface{}) string } // Union related constants such as struct tag values const ( unionSelectionFuncName = "SwitchFunc" TagEncapsulated = "encapsulated" TagUnionTag = "unionTag" TagUnionField = "unionField" ) func (dec *Decoder) isUnion(field reflect.Value, tag reflect.StructTag) (r reflect.Value) { ndrTag := parseTags(tag) if !ndrTag.HasValue(TagUnionTag) { return } r = field // For a non-encapsulated union, the discriminant is marshalled into the transmitted data stream twice: once as the // field or parameter, which is referenced by the switch_is construct, in the procedure argument list; and once as // the first part of the union representation. if !ndrTag.HasValue(TagEncapsulated) { dec.r.Discard(int(r.Type().Size())) } return } // unionSelectedField returns the field name of which of the union values to fill func unionSelectedField(union, discriminant reflect.Value) (string, error) { if !union.Type().Implements(reflect.TypeOf(new(Union)).Elem()) { return "", errors.New("struct does not implement union interface") } args := []reflect.Value{discriminant} // Call the SelectFunc of the union struct to find the name of the field to fill with the value selected. sf := union.MethodByName(unionSelectionFuncName) if !sf.IsValid() { return "", fmt.Errorf("could not find a selection function called %s in the unions struct representation", unionSelectionFuncName) } f := sf.Call(args) if f[0].Kind() != reflect.String || f[0].String() == "" { return "", fmt.Errorf("the union select function did not return a string for the name of the field to fill") } return f[0].String(), nil } rpc-2.0.3/ndr/union_test.go000066400000000000000000000046411375324356300156210ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const ( testUnionSelected1Enc = "0100000001" testUnionSelected2Enc = "020000000200" testUnionSelected1NonEnc = "010000000100000001" testUnionSelected2NonEnc = "02000000020000000200" ) type testUnionEncapsulated struct { Tag uint32 `ndr:"unionTag,encapsulated"` Value1 uint8 `ndr:"unionField"` Value2 uint16 `ndr:"unionField"` } type testUnionNonEncapsulated struct { Tag uint32 `ndr:"unionTag"` Value1 uint8 `ndr:"unionField"` Value2 uint16 `ndr:"unionField"` } func (u testUnionEncapsulated) SwitchFunc(tag interface{}) string { t := tag.(uint32) switch t { case 1: return "Value1" case 2: return "Value2" } return "" } func (u testUnionNonEncapsulated) SwitchFunc(tag interface{}) string { t := tag.(uint32) switch t { case 1: return "Value1" case 2: return "Value2" } return "" } func Test_readUnionEncapsulated(t *testing.T) { var tests = []struct { Hex string Tag uint32 V1 uint8 V2 uint16 }{ {testUnionSelected1Enc, uint32(1), uint8(1), uint16(0)}, {testUnionSelected2Enc, uint32(2), uint8(0), uint16(2)}, } for i, test := range tests { a := new(testUnionEncapsulated) hexStr := TestHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.Tag, a.Tag, "Tag value not as expected for test: %d", i+1) assert.Equal(t, test.V1, a.Value1, "Value1 not as expected for test: %d", i+1) assert.Equal(t, test.V2, a.Value2, "Value2 value not as expected for test: %d", i+1) } } func Test_readUnionNonEncapsulated(t *testing.T) { var tests = []struct { Hex string Tag uint32 V1 uint8 V2 uint16 }{ {testUnionSelected1NonEnc, uint32(1), uint8(1), uint16(0)}, {testUnionSelected2NonEnc, uint32(2), uint8(0), uint16(2)}, } for i, test := range tests { a := new(testUnionNonEncapsulated) hexStr := TestHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.Tag, a.Tag, "Tag value not as expected for test: %d", i+1) assert.Equal(t, test.V1, a.Value1, "Value1 not as expected for test: %d", i+1) assert.Equal(t, test.V2, a.Value2, "Value2 value not as expected for test: %d", i+1) } } rpc-2.0.3/v2/000077500000000000000000000000001375324356300126425ustar00rootroot00000000000000rpc-2.0.3/v2/README.md000066400000000000000000000011451375324356300141220ustar00rootroot00000000000000# RPC [![GoDoc](https://godoc.org/github.com/jcmturner/rpc/v2?status.svg)](https://godoc.org/github.com/jcmturner/rpc/v2) [![Go Report Card](https://goreportcard.com/badge/github.com/jcmturner/rpc/v2)](https://goreportcard.com/report/github.com/jcmturner/rpc/v2) This project relates to [CDE 1.1: Remote Procedure Call](http://pubs.opengroup.org/onlinepubs/9629399/) It is a partial implementation that mainly focuses on unmarshaling NDR encoded byte streams into Go structures. v2 has been released to make use of Go modules Please import as below ``` import "github.com/jcmturner/rpc/v2/" ``` rpc-2.0.3/v2/examples/000077500000000000000000000000001375324356300144605ustar00rootroot00000000000000rpc-2.0.3/v2/examples/examples.go000066400000000000000000000033211375324356300166240ustar00rootroot00000000000000// Package examples provides example decoding of NDR byte streams package examples import "github.com/jcmturner/rpc/v2/mstypes" // KerbValidationInfo type KerbValidationInfo struct { LogOnTime mstypes.FileTime LogOffTime mstypes.FileTime KickOffTime mstypes.FileTime PasswordLastSet mstypes.FileTime PasswordCanChange mstypes.FileTime PasswordMustChange mstypes.FileTime EffectiveName mstypes.RPCUnicodeString FullName mstypes.RPCUnicodeString LogonScript mstypes.RPCUnicodeString ProfilePath mstypes.RPCUnicodeString HomeDirectory mstypes.RPCUnicodeString HomeDirectoryDrive mstypes.RPCUnicodeString LogonCount uint16 BadPasswordCount uint16 UserID uint32 PrimaryGroupID uint32 GroupCount uint32 GroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"` UserFlags uint32 UserSessionKey mstypes.UserSessionKey LogonServer mstypes.RPCUnicodeString LogonDomainName mstypes.RPCUnicodeString LogonDomainID mstypes.RPCSID `ndr:"pointer"` Reserved1 [2]uint32 // Has 2 elements UserAccountControl uint32 SubAuthStatus uint32 LastSuccessfulILogon mstypes.FileTime LastFailedILogon mstypes.FileTime FailedILogonCount uint32 Reserved3 uint32 SIDCount uint32 ExtraSIDs []mstypes.KerbSidAndAttributes `ndr:"pointer,conformant"` ResourceGroupDomainSID mstypes.RPCSID `ndr:"pointer"` ResourceGroupCount uint32 ResourceGroupIDs []mstypes.GroupMembership `ndr:"pointer,conformant"` } rpc-2.0.3/v2/examples/examples_test.go000066400000000000000000000443531375324356300176750ustar00rootroot00000000000000package examples import ( "bytes" "encoding/hex" "testing" "time" "github.com/jcmturner/rpc/v2/mstypes" "github.com/jcmturner/rpc/v2/ndr" "github.com/stretchr/testify/assert" ) const ( KerbValidationInfoMS = "01100800cccccccca00400000000000000000200d186660f656ac601ffffffffffffff7fffffffffffffff7f17d439fe784ac6011794a328424bc601175424977a81c60108000800040002002400240008000200120012000c0002000000000010000200000000001400020000000000180002005410000097792c00010200001a0000001c000200200000000000000000000000000000000000000016001800200002000a000c002400020028000200000000000000000010000000000000000000000000000000000000000000000000000000000000000d0000002c0002000000000000000000000000000400000000000000040000006c007a00680075001200000000000000120000004c0069007100690061006e00670028004c006100720072007900290020005a00680075000900000000000000090000006e0074006400730032002e0062006100740000000000000000000000000000000000000000000000000000000000000000000000000000001a00000061c433000700000009c32d00070000005eb4320007000000010200000700000097b92c00070000002bf1320007000000ce30330007000000a72e2e00070000002af132000700000098b92c000700000062c4330007000000940133000700000076c4330007000000aefe2d000700000032d22c00070000001608320007000000425b2e00070000005fb4320007000000ca9c35000700000085442d0007000000c2f0320007000000e9ea310007000000ed8e2e0007000000b6eb310007000000ab2e2e0007000000720e2e00070000000c000000000000000b0000004e0054004400450056002d00440043002d003000350000000600000000000000050000004e0054004400450056000000040000000104000000000005150000005951b81766725d2564633b0b0d0000003000020007000000340002000700002038000200070000203c000200070000204000020007000020440002000700002048000200070000204c000200070000205000020007000020540002000700002058000200070000205c00020007000020600002000700002005000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000050000000105000000000005150000005951b81766725d2564633b0b74542f00050000000105000000000005150000005951b81766725d2564633b0be8383200050000000105000000000005150000005951b81766725d2564633b0bcd383200050000000105000000000005150000005951b81766725d2564633b0b5db43200050000000105000000000005150000005951b81766725d2564633b0b41163500050000000105000000000005150000005951b81766725d2564633b0be8ea3100050000000105000000000005150000005951b81766725d2564633b0bc1193200050000000105000000000005150000005951b81766725d2564633b0b29f13200050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00050000000105000000000005150000005951b81766725d2564633b0bef8f3100050000000105000000000005150000005951b81766725d2564633b0b075f2e0000000000" KerbValidationInfoGoKRB5 = "01100800cccccccc180200000000000000000200058e4fdd80c6d201ffffffffffffff7fffffffffffffff7fcc27969c39c6d201cce7ffc602c7d201ffffffffffffff7f12001200040002001600160008000200000000000c000200000000001000020000000000140002000000000018000200d80000005104000001020000050000001c000200200000000000000000000000000000000000000008000a002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000020000002c00020000000000000000000000000009000000000000000900000074006500730074007500730065007200310000000b000000000000000b000000540065007300740031002000550073006500720031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050000000102000007000000540400000700000055040000070000005b040000070000005c0400000700000005000000000000000400000041004400440043000500000000000000040000005400450053005400040000000104000000000005150000004c86cebca07160e63fdce8870200000030000200070000203400020007000020050000000105000000000005150000004c86cebca07160e63fdce8875a040000050000000105000000000005150000004c86cebca07160e63fdce8875704000000000000" KerbValidationInfoTrust = "01100800cccccccc000200000000000000000200c30bcc79e444d301ffffffffffffff7fffffffffffffff7fc764125a0842d301c7247c84d142d301ffffffffffffff7f12001200040002001600160008000200000000000c0002000000000010000200000000001400020000000000180002002e0000005204000001020000030000001c0002002002000000000000000000000000000000000000060008002000020008000a00240002002800020000000000000000001002000000000000000000000000000000000000000000000000000000000000010000002c00020034000200020000003800020009000000000000000900000074006500730074007500730065007200310000000b000000000000000b0000005400650073007400310020005500730065007200310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000056040000070000000102000007000000550400000700000004000000000000000300000055004400430000000500000000000000040000005500530045005200040000000104000000000005150000002057308834e7d1d0a2fb0444010000003000020007000000010000000101000000000012010000000400000001040000000000051500000062dc8db6c8705249b5459e75020000005304000007000020540400000700002000000000" ) func TestExample_KerbValidationInfo(t *testing.T) { b, _ := hex.DecodeString(KerbValidationInfoMS) k := new(KerbValidationInfo) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(k) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2006, 4, 28, 1, 42, 50, 925640100, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2006, 3, 18, 10, 44, 54, 837147900, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2006, 3, 19, 10, 44, 54, 837147900, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "lzhu", k.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Liqiang(Larry) Zhu", k.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "ntds2.bat", k.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(4180), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(2914711), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(26), k.GroupCount, "GroupCount not as expected") gids := []mstypes.GroupMembership{ {RelativeID: 3392609, Attributes: 7}, {RelativeID: 2999049, Attributes: 7}, {RelativeID: 3322974, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 2931095, Attributes: 7}, {RelativeID: 3338539, Attributes: 7}, {RelativeID: 3354830, Attributes: 7}, {RelativeID: 3026599, Attributes: 7}, {RelativeID: 3338538, Attributes: 7}, {RelativeID: 2931096, Attributes: 7}, {RelativeID: 3392610, Attributes: 7}, {RelativeID: 3342740, Attributes: 7}, {RelativeID: 3392630, Attributes: 7}, {RelativeID: 3014318, Attributes: 7}, {RelativeID: 2937394, Attributes: 7}, {RelativeID: 3278870, Attributes: 7}, {RelativeID: 3038018, Attributes: 7}, {RelativeID: 3322975, Attributes: 7}, {RelativeID: 3513546, Attributes: 7}, {RelativeID: 2966661, Attributes: 7}, {RelativeID: 3338434, Attributes: 7}, {RelativeID: 3271401, Attributes: 7}, {RelativeID: 3051245, Attributes: 7}, {RelativeID: 3271606, Attributes: 7}, {RelativeID: 3026603, Attributes: 7}, {RelativeID: 3018354, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "NTDEV-DC-05", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "NTDEV", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-397955417-626881126-188441444", k.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(16), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(13), k.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es = []struct { sid string attr uint32 }{ {"S-1-5-21-773533881-1816936887-355810188-513", uint32(7)}, {"S-1-5-21-397955417-626881126-188441444-3101812", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291368", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3291341", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3322973", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3479105", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3271400", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3283393", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3338537", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3038991", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3037999", uint32(536870919)}, {"S-1-5-21-397955417-626881126-188441444-3248111", uint32(536870919)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint8(0), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, 0, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") b, _ = hex.DecodeString(KerbValidationInfoGoKRB5) k2 := new(KerbValidationInfo) dec = ndr.NewDecoder(bytes.NewReader(b)) err = dec.Decode(k2) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2017, 5, 6, 15, 53, 11, 825766900, time.UTC), k2.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k2.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 5, 6, 7, 23, 8, 968750000, time.UTC), k2.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 5, 7, 7, 23, 8, 968750000, time.UTC), k2.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k2.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k2.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k2.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(216), k2.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k2.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1105), k2.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k2.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(5), k2.GroupCount, "GroupCount not as expected") gids = []mstypes.GroupMembership{ {RelativeID: 513, Attributes: 7}, {RelativeID: 1108, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, {RelativeID: 1115, Attributes: 7}, {RelativeID: 1116, Attributes: 7}, } assert.Equal(t, gids, k2.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(32), k2.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k2.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "ADDC", k2.LogonServer.String(), "LogonServer not as expected") assert.Equal(t, "TEST", k2.LogonDomainName.String(), "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-3167651404-3865080224-2280184895", k2.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k2.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k2.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k2.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k2.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(2), k2.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k2.SIDCount), len(k2.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") var es2 = []struct { sid string attr uint32 }{ {"S-1-5-21-3167651404-3865080224-2280184895-1114", uint32(536870919)}, {"S-1-5-21-3167651404-3865080224-2280184895-1111", uint32(536870919)}, } for i, s := range es2 { assert.Equal(t, s.sid, k2.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as expected") assert.Equal(t, s.attr, k2.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as expected") } assert.Equal(t, uint8(0), k2.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, 0, len(k2.ResourceGroupIDs), "ResourceGroupIDs not as expected") b, _ = hex.DecodeString(KerbValidationInfoTrust) k = new(KerbValidationInfo) dec = ndr.NewDecoder(bytes.NewReader(b)) err = dec.Decode(k) if err != nil { t.Errorf("%v", err) } assert.Equal(t, time.Date(2017, 10, 14, 12, 03, 41, 52409900, time.UTC), k.LogOnTime.Time(), "LogOnTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.LogOffTime.Time(), "LogOffTime not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551516, time.UTC), k.KickOffTime.Time(), "KickOffTime not as expected") assert.Equal(t, time.Date(2017, 10, 10, 20, 42, 56, 220282300, time.UTC), k.PasswordLastSet.Time(), "PasswordLastSet not as expected") assert.Equal(t, time.Date(2017, 10, 11, 20, 42, 56, 220282300, time.UTC), k.PasswordCanChange.Time(), "PasswordCanChange not as expected") assert.Equal(t, "testuser1", k.EffectiveName.String(), "EffectiveName not as expected") assert.Equal(t, "Test1 User1", k.FullName.String(), "EffectiveName not as expected") assert.Equal(t, "", k.LogonScript.String(), "EffectiveName not as expected") assert.Equal(t, "", k.ProfilePath.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectory.String(), "EffectiveName not as expected") assert.Equal(t, "", k.HomeDirectoryDrive.String(), "EffectiveName not as expected") assert.Equal(t, uint16(46), k.LogonCount, "LogonCount not as expected") assert.Equal(t, uint16(0), k.BadPasswordCount, "BadPasswordCount not as expected") assert.Equal(t, uint32(1106), k.UserID, "UserID not as expected") assert.Equal(t, uint32(513), k.PrimaryGroupID, "PrimaryGroupID not as expected") assert.Equal(t, uint32(3), k.GroupCount, "GroupCount not as expected") gids = []mstypes.GroupMembership{ {RelativeID: 1110, Attributes: 7}, {RelativeID: 513, Attributes: 7}, {RelativeID: 1109, Attributes: 7}, } assert.Equal(t, gids, k.GroupIDs, "GroupIDs not as expected") assert.Equal(t, uint32(544), k.UserFlags, "UserFlags not as expected") assert.Equal(t, mstypes.UserSessionKey{CypherBlock: [2]mstypes.CypherBlock{{Data: [8]byte{}}, {Data: [8]byte{}}}}, k.UserSessionKey, "UserSessionKey not as expected") assert.Equal(t, "UDC", k.LogonServer.Value, "LogonServer not as expected") assert.Equal(t, "USER", k.LogonDomainName.Value, "LogonDomainName not as expected") assert.Equal(t, "S-1-5-21-2284869408-3503417140-1141177250", k.LogonDomainID.String(), "LogonDomainID not as expected") assert.Equal(t, uint32(528), k.UserAccountControl, "UserAccountControl not as expected") assert.Equal(t, uint32(0), k.SubAuthStatus, "SubAuthStatus not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastSuccessfulILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, time.Date(2185, 7, 21, 23, 34, 33, 709551616, time.UTC), k.LastFailedILogon.Time(), "LastSuccessfulILogon not as expected") assert.Equal(t, uint32(0), k.FailedILogonCount, "FailedILogonCount not as expected") assert.Equal(t, uint32(1), k.SIDCount, "SIDCount not as expected") assert.Equal(t, int(k.SIDCount), len(k.ExtraSIDs), "SIDCount and size of ExtraSIDs list are not the same") es = []struct { sid string attr uint32 }{ {"S-1-18-1", uint32(7)}, } for i, s := range es { assert.Equal(t, s.sid, k.ExtraSIDs[i].SID.String(), "ExtraSID SID value not as epxected") assert.Equal(t, s.attr, k.ExtraSIDs[i].Attributes, "ExtraSID Attributes value not as epxected") } assert.Equal(t, uint8(4), k.ResourceGroupDomainSID.SubAuthorityCount, "ResourceGroupDomainSID not as expected") assert.Equal(t, "S-1-5-21-3062750306-1230139592-1973306805", k.ResourceGroupDomainSID.String(), "ResourceGroupDomainSID value not as expected") assert.Equal(t, 2, len(k.ResourceGroupIDs), "ResourceGroupIDs not as expected") rgids := []mstypes.GroupMembership{ {RelativeID: 1107, Attributes: 536870919}, {RelativeID: 1108, Attributes: 536870919}, } assert.Equal(t, rgids, k.ResourceGroupIDs, "ResourceGroupIDs not as expected") //groupSids := []string{"S-1-5-21-2284869408-3503417140-1141177250-1110", // "S-1-5-21-2284869408-3503417140-1141177250-513", // "S-1-5-21-2284869408-3503417140-1141177250-1109", // "S-1-18-1", // "S-1-5-21-3062750306-1230139592-1973306805-1107", // "S-1-5-21-3062750306-1230139592-1973306805-1108"} //assert.Equal(t, groupSids, k.GetGroupMembershipSIDs(), "GroupMembershipSIDs not as expected") } rpc-2.0.3/v2/go.mod000066400000000000000000000002221375324356300137440ustar00rootroot00000000000000module github.com/jcmturner/rpc/v2 go 1.13 require ( github.com/stretchr/testify v1.4.0 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa ) rpc-2.0.3/v2/go.sum000066400000000000000000000025331375324356300140000ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= rpc-2.0.3/v2/mstypes/000077500000000000000000000000001375324356300143465ustar00rootroot00000000000000rpc-2.0.3/v2/mstypes/claims.go000066400000000000000000000110401375324356300161410ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "errors" "fmt" "github.com/jcmturner/rpc/v2/ndr" "golang.org/x/net/http2/hpack" ) // Compression format assigned numbers. https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/a8b7cb0a-92a6-4187-a23b-5e14273b96f8 const ( CompressionFormatNone uint16 = 0 CompressionFormatLZNT1 uint16 = 2 // LZNT1 aka ntfs compression CompressionFormatXPress uint16 = 3 // plain LZ77 CompressionFormatXPressHuff uint16 = 4 // LZ77+Huffman - The Huffman variant of the XPRESS compression format uses LZ77-style dictionary compression combined with Huffman coding. ) // ClaimsSourceTypeAD https://msdn.microsoft.com/en-us/library/hh553809.aspx const ClaimsSourceTypeAD uint16 = 1 // Claim Type assigned numbers const ( ClaimTypeIDInt64 uint16 = 1 ClaimTypeIDUInt64 uint16 = 2 ClaimTypeIDString uint16 = 3 ClaimsTypeIDBoolean uint16 = 6 ) // ClaimsBlob implements https://msdn.microsoft.com/en-us/library/hh554119.aspx type ClaimsBlob struct { Size uint32 EncodedBlob EncodedBlob } // EncodedBlob are the bytes of the encoded Claims type EncodedBlob []byte // Size returns the size of the bytes of the encoded Claims func (b EncodedBlob) Size(c interface{}) int { cb := c.(ClaimsBlob) return int(cb.Size) } // ClaimsSetMetadata implements https://msdn.microsoft.com/en-us/library/hh554073.aspx type ClaimsSetMetadata struct { ClaimsSetSize uint32 ClaimsSetBytes []byte `ndr:"pointer,conformant"` CompressionFormat uint16 // Enum see constants for options UncompressedClaimsSetSize uint32 ReservedType uint16 ReservedFieldSize uint32 ReservedField []byte `ndr:"pointer,conformant"` } // ClaimsSet reads the ClaimsSet type from the NDR encoded ClaimsSetBytes in the ClaimsSetMetadata func (m *ClaimsSetMetadata) ClaimsSet() (c ClaimsSet, err error) { if len(m.ClaimsSetBytes) < 1 { err = errors.New("no bytes available for ClaimsSet") return } // TODO switch statement to decompress ClaimsSetBytes switch m.CompressionFormat { case CompressionFormatLZNT1: s := hex.EncodeToString(m.ClaimsSetBytes) err = fmt.Errorf("ClaimsSet compressed, format LZNT1 not currently supported: %s", s) return case CompressionFormatXPress: s := hex.EncodeToString(m.ClaimsSetBytes) err = fmt.Errorf("ClaimsSet compressed, format XPress not currently supported: %s", s) return case CompressionFormatXPressHuff: var b []byte buff := bytes.NewBuffer(b) _, e := hpack.HuffmanDecode(buff, m.ClaimsSetBytes) if e != nil { err = fmt.Errorf("error deflating: %v", e) return } m.ClaimsSetBytes = buff.Bytes() } dec := ndr.NewDecoder(bytes.NewReader(m.ClaimsSetBytes)) err = dec.Decode(&c) return } // ClaimsSet implements https://msdn.microsoft.com/en-us/library/hh554122.aspx type ClaimsSet struct { ClaimsArrayCount uint32 ClaimsArrays []ClaimsArray `ndr:"pointer,conformant"` ReservedType uint16 ReservedFieldSize uint32 ReservedField []byte `ndr:"pointer,conformant"` } // ClaimsArray implements https://msdn.microsoft.com/en-us/library/hh536458.aspx type ClaimsArray struct { ClaimsSourceType uint16 ClaimsCount uint32 ClaimEntries []ClaimEntry `ndr:"pointer,conformant"` } // ClaimEntry is a NDR union that implements https://msdn.microsoft.com/en-us/library/hh536374.aspx type ClaimEntry struct { ID string `ndr:"pointer,conformant,varying"` Type uint16 `ndr:"unionTag"` TypeInt64 ClaimTypeInt64 `ndr:"unionField"` TypeUInt64 ClaimTypeUInt64 `ndr:"unionField"` TypeString ClaimTypeString `ndr:"unionField"` TypeBool ClaimTypeBoolean `ndr:"unionField"` } // SwitchFunc is the ClaimEntry union field selection function func (u ClaimEntry) SwitchFunc(_ interface{}) string { switch u.Type { case ClaimTypeIDInt64: return "TypeInt64" case ClaimTypeIDUInt64: return "TypeUInt64" case ClaimTypeIDString: return "TypeString" case ClaimsTypeIDBoolean: return "TypeBool" } return "" } // ClaimTypeInt64 is a claim of type int64 type ClaimTypeInt64 struct { ValueCount uint32 Value []int64 `ndr:"pointer,conformant"` } // ClaimTypeUInt64 is a claim of type uint64 type ClaimTypeUInt64 struct { ValueCount uint32 Value []uint64 `ndr:"pointer,conformant"` } // ClaimTypeString is a claim of type string type ClaimTypeString struct { ValueCount uint32 Value []LPWSTR `ndr:"pointer,conformant"` } // ClaimTypeBoolean is a claim of type bool type ClaimTypeBoolean struct { ValueCount uint32 Value []bool `ndr:"pointer,conformant"` } rpc-2.0.3/v2/mstypes/claims_test.go000066400000000000000000000307551375324356300172160ustar00rootroot00000000000000package mstypes import ( "bytes" "compress/flate" "encoding/hex" "io/ioutil" "testing" "unicode/utf8" "github.com/jcmturner/rpc/v2/ndr" "github.com/stretchr/testify/assert" ) const ( ClientClaimsInfoStr = "01100800cccccccc000100000000000000000200d80000000400020000000000d8000000000000000000000000000000d800000001100800ccccccccc80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000100000010000200290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a0038003800640035006400390030003800350065006100350063003000630030000000000001000000140002000a000000000000000a00000074006500730074007500730065007200310000000000000000000000" ClientClaimsInfoInt = "01100800cccccccce00000000000000000000200b80000000400020000000000b8000000000000000000000000000000b800000001100800cccccccca80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c0002000100010001000000100002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c0000000000000000000000" ClientClaimsInfoMulti = "01100800cccccccc780100000000000000000200500100000400020000000000500100000000000000000000000000005001000001100800cccccccc400100000000000000000200010000000400020000000000000000000000000001000000010000000200000008000200020000000c000200010001000100000010000200140002000300030001000000180002002a000000000000002a000000610064003a002f002f006500780074002f006d007300440053002d0053007500700070006f00720074006500640045003a0038003800640035006400650061003800660031006100660035006600310039000000010000001c00000000000000290000000000000029000000610064003a002f002f006500780074002f00730041004d004100630063006f0075006e0074004e0061006d0065003a00380038006400350064003900300038003500650061003500630030006300300000000000010000001c0002000a000000000000000a000000740065007300740075007300650072003100000000000000" ClientClaimsInfoMultiUint = "01100800ccccccccf00000000000000000000200c80000000400020000000000c8000000000000000000000000000000c800000001100800ccccccccb80000000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200020002000400000010000200260000000000000026000000610064003a002f002f006500780074002f006f0062006a0065006300740043006c006100730073003a00380038006400350064006500370039003100650037006200320037006500360000000400000009000a000000000007000100000000000600010000000000000001000000000000000000" ClientClaimsInfoMultiStr = "01100800cccccccc480100000000000000000200200100000400020000000000200100000000000000000000000000002001000001100800cccccccc100100000000000000000200010000000400020000000000000000000000000001000000010000000100000008000200010000000c000200030003000400000010000200270000000000000027000000610064003a002f002f006500780074002f006f00740068006500720049007000500068006f006e0065003a003800380064003500640065003900660036006200340061006600390038003500000000000400000014000200180002001c000200200002000500000000000000050000007300740072003100000000000500000000000000050000007300740072003200000000000500000000000000050000007300740072003300000000000500000000000000050000007300740072003400000000000000000000000000" ClaimsEntryIDStr = "ad://ext/sAMAccountName:88d5d9085ea5c0c0" ClaimsEntryValueStr = "testuser1" ClaimsEntryIDInt64 = "ad://ext/msDS-SupportedE:88d5dea8f1af5f19" ClaimsEntryValueInt64 int64 = 28 ClaimsEntryIDUInt64 = "ad://ext/objectClass:88d5de791e7b27e6" ClaimsSetBytesCompressionFormatXPressHuff = "738788888708080007000800080007000880088808088880886687888607080000808800800000000880000000000000806667080808787707767800080000000000000000000000000000000000000000000000080000000000000000000000000000000000080000000000000000000000000000000000000000000000000057000800800000007500000000000000050700000000000064760800080000008587007700000080650808000000000075888700000000700788000000000060677000000000007000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002e91f150e1ad792412496411f3904ff6027871529ef12043e4e79ab23c9f03aea65c0aca41e842b8d46f0321354538afe9f8c413b6e1a37377bca410ac8bc3b35398e51c0a290929e3ca764addf84e5ada9caa43c80c38de74d75cd0289a202641d26a950284dea25479c4376c3100720db619b9066d13c506c88a858a3305007490a40d7015a7528382a7c9ae54ab58204f01e1d8e044fee01925cbc46ad28cfa8d67c28e0216ce1de315aaaf43e4c88409002793b33a3823683680ce7d6606eca05f0cff9d06c88a0588dd5500d51de514570286fa148c007c699838d635b0b87ed420749011c94696fa202b002b0000" ) func Test_TMP(t *testing.T) { blz77huff, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a8dc0000ff2601") //b, _ := hex.DecodeString("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000050555555555555555555554544040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d8523ed794115be9195ff9d67cdf8d0400000000") r := bytes.NewReader(blz77huff) dr := flate.NewReader(r) s, errr := ioutil.ReadAll(dr) t.Logf("test: %s %v\n", string(s), errr) ru, size := utf8.DecodeRuneInString("`") t.Logf("%d %v\n", ru, size) } func Test_ClientClaimsInfoStr_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoStr) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(3), k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{ClaimsEntryValueStr}}, k.ClaimsArrays[0].ClaimEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsMultiValueUint_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMultiUint) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDUInt64, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.ClaimsArrays[0].ClaimEntries[0].TypeUInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDUInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []uint64{655369, 65543, 65542, 65536}, k.ClaimsArrays[0].ClaimEntries[0].TypeUInt64.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsInt_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoInt) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDInt64, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{ClaimsEntryValueInt64}, k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsMultiValueStr_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMultiStr) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, ClaimTypeIDString, k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(4), k.ClaimsArrays[0].ClaimEntries[0].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, "ad://ext/otherIpPhone:88d5de9f6b4af985", k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{"str1"}, {"str2"}, {"str3"}, {"str4"}}, k.ClaimsArrays[0].ClaimEntries[0].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } func Test_ClientClaimsInfoMultiEntry_Unmarshal(t *testing.T) { b, _ := hex.DecodeString(ClientClaimsInfoMulti) m := new(ClaimsSetMetadata) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(m) if err != nil { t.Errorf("error decoding ClaimsSetMetadata %v", err) } k, err := m.ClaimsSet() if err != nil { t.Errorf("error retrieving ClaimsSet %v", err) } assert.Equal(t, uint32(1), k.ClaimsArrayCount, "claims array count not as expected") assert.Equal(t, ClaimsSourceTypeAD, k.ClaimsArrays[0].ClaimsSourceType, "claims source type not as expected") assert.Equal(t, uint32(2), k.ClaimsArrays[0].ClaimsCount, "claims count not as expected") assert.Equal(t, uint16(1), k.ClaimsArrays[0].ClaimEntries[0].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDInt64, k.ClaimsArrays[0].ClaimEntries[0].ID, "claims entry ID not as expected") assert.Equal(t, []int64{int64(28)}, k.ClaimsArrays[0].ClaimEntries[0].TypeInt64.Value, "claims value not as expected") assert.Equal(t, uint16(3), k.ClaimsArrays[0].ClaimEntries[1].Type, "claims entry type not as expected") assert.Equal(t, uint32(1), k.ClaimsArrays[0].ClaimEntries[1].TypeString.ValueCount, "claims value count not as expected") assert.Equal(t, ClaimsEntryIDStr, k.ClaimsArrays[0].ClaimEntries[1].ID, "claims entry ID not as expected") assert.Equal(t, []LPWSTR{{ClaimsEntryValueStr}}, k.ClaimsArrays[0].ClaimEntries[1].TypeString.Value, "claims value not as expected") assert.Equal(t, CompressionFormatNone, m.CompressionFormat, "compression format not as expected") } rpc-2.0.3/v2/mstypes/common.go000066400000000000000000000006441375324356300161710ustar00rootroot00000000000000// Package mstypes provides implemnations of some Microsoft data types [MS-DTYP] https://msdn.microsoft.com/en-us/library/cc230283.aspx package mstypes // LPWSTR implements https://msdn.microsoft.com/en-us/library/cc230355.aspx type LPWSTR struct { Value string `ndr:"pointer,conformant,varying"` } // String returns the string representation of LPWSTR data type. func (s *LPWSTR) String() string { return s.Value } rpc-2.0.3/v2/mstypes/filetime.go000066400000000000000000000030371375324356300164760ustar00rootroot00000000000000// Package mstypes implements representations of Microsoft types package mstypes import ( "time" ) /* FILETIME is a windows data structure. Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms724284%28v=vs.85%29.aspx It contains two parts that are 32bit integers: dwLowDateTime dwHighDateTime We need to combine these two into one 64bit integer. This gives the number of 100 nano second period from January 1, 1601, Coordinated Universal Time (UTC) */ const unixEpochDiff = 116444736000000000 // FileTime implements the Microsoft FILETIME type https://msdn.microsoft.com/en-us/library/cc230324.aspx type FileTime struct { LowDateTime uint32 HighDateTime uint32 } // Time return a golang Time type from the FileTime func (ft FileTime) Time() time.Time { ns := (ft.MSEpoch() - unixEpochDiff) * 100 return time.Unix(0, int64(ns)).UTC() } // MSEpoch returns the FileTime as a Microsoft epoch, the number of 100 nano second periods elapsed from January 1, 1601 UTC. func (ft FileTime) MSEpoch() int64 { return (int64(ft.HighDateTime) << 32) + int64(ft.LowDateTime) } // Unix returns the FileTime as a Unix time, the number of seconds elapsed since January 1, 1970 UTC. func (ft FileTime) Unix() int64 { return (ft.MSEpoch() - unixEpochDiff) / 10000000 } // GetFileTime returns a FileTime type from the provided Golang Time type. func GetFileTime(t time.Time) FileTime { ns := t.UnixNano() fp := (ns / 100) + unixEpochDiff hd := fp >> 32 ld := fp - (hd << 32) return FileTime{ LowDateTime: uint32(ld), HighDateTime: uint32(hd), } } rpc-2.0.3/v2/mstypes/filetime_test.go000066400000000000000000000030071375324356300175320ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "github.com/jcmturner/rpc/v2/ndr" "github.com/stretchr/testify/assert" "testing" "time" ) const TestNDRHeader = "01100800cccccccca00400000000000000000200" func TestFileTime(t *testing.T) { t.Parallel() //2007-02-22 17:00:01.6382155 tt := time.Date(2007, 2, 22, 17, 0, 1, 638215500, time.UTC) ft := GetFileTime(tt) assert.Equal(t, tt.Unix(), ft.Unix(), "Unix epoch time not as expected") assert.Equal(t, int64(128166372016382155), ft.MSEpoch(), "MSEpoch not as expected") assert.Equal(t, tt, ft.Time(), "Golang time object returned from FileTime not as expected") } func TestDecodeFileTime(t *testing.T) { var tests = []struct { Hex string UnixNano int64 }{ {"d186660f656ac601", 1146188570925640100}, {"17d439fe784ac601", 1142678694837147900}, {"1794a328424bc601", 1142765094837147900}, {"175424977a81c601", 1148726694837147900}, {"058e4fdd80c6d201", 1494085991825766900}, {"cc27969c39c6d201", 1494055388968750000}, {"cce7ffc602c7d201", 1494141788968750000}, {"c30bcc79e444d301", 1507982621052409900}, {"c764125a0842d301", 1507668176220282300}, {"c7247c84d142d301", 1507754576220282300}, } for i, test := range tests { a := new(FileTime) hexStr := TestNDRHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.UnixNano, a.Time().UnixNano(), "Time value not as expected for test: %d", i+1) } } rpc-2.0.3/v2/mstypes/group_membership.go000066400000000000000000000021421375324356300202430ustar00rootroot00000000000000package mstypes // GroupMembership implements https://msdn.microsoft.com/en-us/library/cc237945.aspx // RelativeID : A 32-bit unsigned integer that contains the RID of a particular group. // The possible values for the Attributes flags are identical to those specified in KERB_SID_AND_ATTRIBUTES type GroupMembership struct { RelativeID uint32 Attributes uint32 } // DomainGroupMembership implements https://msdn.microsoft.com/en-us/library/hh536344.aspx // DomainId: A SID structure that contains the SID for the domain.This member is used in conjunction with the GroupIds members to create group SIDs for the device. // GroupCount: A 32-bit unsigned integer that contains the number of groups within the domain to which the account belongs. // GroupIds: A pointer to a list of GROUP_MEMBERSHIP structures that contain the groups to which the account belongs in the domain. The number of groups in this list MUST be equal to GroupCount. type DomainGroupMembership struct { DomainID RPCSID `ndr:"pointer"` GroupCount uint32 GroupIDs []GroupMembership `ndr:"pointer,conformant"` // Size is value of GroupCount } rpc-2.0.3/v2/mstypes/kerb_sid_and_attributes.go000066400000000000000000000014461375324356300215540ustar00rootroot00000000000000package mstypes // Attributes of a security group membership and can be combined by using the bitwise OR operation. // They are used by an access check mechanism to specify whether the membership is to be used in an access check decision. const ( SEGroupMandatory = 31 SEGroupEnabledByDefault = 30 SEGroupEnabled = 29 SEGroupOwner = 28 SEGroupResource = 2 //All other bits MUST be set to zero and MUST be ignored on receipt. ) // KerbSidAndAttributes implements https://msdn.microsoft.com/en-us/library/cc237947.aspx type KerbSidAndAttributes struct { SID RPCSID `ndr:"pointer"` // A pointer to an RPC_SID structure. Attributes uint32 } // SetFlag sets a flag in a uint32 attribute value. func SetFlag(a *uint32, i uint) { *a = *a | (1 << (31 - i)) } rpc-2.0.3/v2/mstypes/reader.go000066400000000000000000000043161375324356300161430ustar00rootroot00000000000000package mstypes import ( "bufio" "encoding/binary" "fmt" "io" ) // Byte sizes of primitive types const ( SizeBool = 1 SizeChar = 1 SizeUint8 = 1 SizeUint16 = 2 SizeUint32 = 4 SizeUint64 = 8 SizeEnum = 2 SizeSingle = 4 SizeDouble = 8 SizePtr = 4 ) // Reader reads simple byte stream data into a Go representations type Reader struct { r *bufio.Reader // source of the data } // NewReader creates a new instance of a simple Reader. func NewReader(r io.Reader) *Reader { reader := new(Reader) reader.r = bufio.NewReader(r) return reader } func (r *Reader) Read(p []byte) (n int, err error) { return r.r.Read(p) } func (r *Reader) Uint8() (uint8, error) { b, err := r.r.ReadByte() if err != nil { return uint8(0), err } return uint8(b), nil } func (r *Reader) Uint16() (uint16, error) { b, err := r.ReadBytes(SizeUint16) if err != nil { return uint16(0), err } return binary.LittleEndian.Uint16(b), nil } func (r *Reader) Uint32() (uint32, error) { b, err := r.ReadBytes(SizeUint32) if err != nil { return uint32(0), err } return binary.LittleEndian.Uint32(b), nil } func (r *Reader) Uint64() (uint64, error) { b, err := r.ReadBytes(SizeUint64) if err != nil { return uint64(0), err } return binary.LittleEndian.Uint64(b), nil } func (r *Reader) FileTime() (f FileTime, err error) { f.LowDateTime, err = r.Uint32() if err != nil { return } f.HighDateTime, err = r.Uint32() if err != nil { return } return } // UTF16String returns a string that is UTF16 encoded in a byte slice. n is the number of bytes representing the string func (r *Reader) UTF16String(n int) (str string, err error) { //Length divided by 2 as each run is 16bits = 2bytes s := make([]rune, n/2, n/2) for i := 0; i < len(s); i++ { var u uint16 u, err = r.Uint16() if err != nil { return } s[i] = rune(u) } str = string(s) return } // readBytes returns a number of bytes from the NDR byte stream. func (r *Reader) ReadBytes(n int) ([]byte, error) { //TODO make this take an int64 as input to allow for larger values on all systems? b := make([]byte, n, n) m, err := r.r.Read(b) if err != nil || m != n { return b, fmt.Errorf("error reading bytes from stream: %v", err) } return b, nil } rpc-2.0.3/v2/mstypes/rpc_unicode_string.go000066400000000000000000000016761375324356300205670ustar00rootroot00000000000000package mstypes // RPCUnicodeString implements https://msdn.microsoft.com/en-us/library/cc230365.aspx type RPCUnicodeString struct { Length uint16 // The length, in bytes, of the string pointed to by the Buffer member, not including the terminating null character if any. The length MUST be a multiple of 2. The length SHOULD equal the entire size of the Buffer, in which case there is no terminating null character. Any method that accesses this structure MUST use the Length specified instead of relying on the presence or absence of a null character. MaximumLength uint16 // The maximum size, in bytes, of the string pointed to by Buffer. The size MUST be a multiple of 2. If not, the size MUST be decremented by 1 prior to use. This value MUST not be less than Length. Value string `ndr:"pointer,conformant,varying"` } // String returns the RPCUnicodeString string value func (r *RPCUnicodeString) String() string { return r.Value } rpc-2.0.3/v2/mstypes/rpc_unicode_string_test.go000066400000000000000000000013731375324356300216200ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "testing" "github.com/jcmturner/rpc/v2/ndr" "github.com/stretchr/testify/assert" ) const ( TestRPCUnicodeStringBytes = "1200120004000200" + "01000000" + "0900000000000000090000007400650073007400750073006500720031000000" TestRPCUnicodeStringValue = "testuser1" ) type TestRPCUnicodeString struct { RPCStr RPCUnicodeString OtherValue uint32 } func Test_RPCUnicodeString(t *testing.T) { a := new(TestRPCUnicodeString) hexStr := TestNDRHeader + TestRPCUnicodeStringBytes b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatal(err) } assert.Equal(t, TestRPCUnicodeStringValue, a.RPCStr.Value, "String value not as expected") } rpc-2.0.3/v2/mstypes/sid.go000066400000000000000000000030611375324356300154540ustar00rootroot00000000000000package mstypes import ( "encoding/binary" "encoding/hex" "fmt" "math" "strings" ) // RPCSID implements https://msdn.microsoft.com/en-us/library/cc230364.aspx type RPCSID struct { Revision uint8 // An 8-bit unsigned integer that specifies the revision level of the SID. This value MUST be set to 0x01. SubAuthorityCount uint8 // An 8-bit unsigned integer that specifies the number of elements in the SubAuthority array. The maximum number of elements allowed is 15. IdentifierAuthority [6]byte // An RPC_SID_IDENTIFIER_AUTHORITY structure that indicates the authority under which the SID was created. It describes the entity that created the SID. The Identifier Authority value {0,0,0,0,0,5} denotes SIDs created by the NT SID authority. SubAuthority []uint32 `ndr:"conformant"` // A variable length array of unsigned 32-bit integers that uniquely identifies a principal relative to the IdentifierAuthority. Its length is determined by SubAuthorityCount. } // String returns the string representation of the RPC_SID. func (s *RPCSID) String() string { var strb strings.Builder strb.WriteString("S-1-") b := append(make([]byte, 2, 2), s.IdentifierAuthority[:]...) // For a strange reason this is read big endian: https://msdn.microsoft.com/en-us/library/dd302645.aspx i := binary.BigEndian.Uint64(b) if i > math.MaxUint32 { fmt.Fprintf(&strb, "0x%s", hex.EncodeToString(s.IdentifierAuthority[:])) } else { fmt.Fprintf(&strb, "%d", i) } for _, sub := range s.SubAuthority { fmt.Fprintf(&strb, "-%d", sub) } return strb.String() } rpc-2.0.3/v2/mstypes/sid_test.go000066400000000000000000000051031375324356300165120ustar00rootroot00000000000000package mstypes import ( "bytes" "encoding/hex" "testing" "github.com/jcmturner/rpc/v2/ndr" "github.com/stretchr/testify/assert" ) type testSIDStruct struct { SID RPCSID `ndr:"pointer"` } func Test_RPCSIDDecode(t *testing.T) { var tests = []struct { Hex string SID string }{ {"040000000104000000000005150000005951b81766725d2564633b0b", "S-1-5-21-397955417-626881126-188441444"}, {"05000000010500000000000515000000b9301b2eb7414c6c8c3b351501020000", "S-1-5-21-773533881-1816936887-355810188-513"}, {"050000000105000000000005150000005951b81766725d2564633b0b74542f00", "S-1-5-21-397955417-626881126-188441444-3101812"}, {"050000000105000000000005150000005951b81766725d2564633b0be8383200", "S-1-5-21-397955417-626881126-188441444-3291368"}, {"050000000105000000000005150000005951b81766725d2564633b0b5db43200", "S-1-5-21-397955417-626881126-188441444-3322973"}, {"050000000105000000000005150000005951b81766725d2564633b0b41163500", "S-1-5-21-397955417-626881126-188441444-3479105"}, {"050000000105000000000005150000005951b81766725d2564633b0be8ea3100", "S-1-5-21-397955417-626881126-188441444-3271400"}, {"050000000105000000000005150000005951b81766725d2564633b0bc1193200", "S-1-5-21-397955417-626881126-188441444-3283393"}, {"050000000105000000000005150000005951b81766725d2564633b0b29f13200", "S-1-5-21-397955417-626881126-188441444-3338537"}, {"050000000105000000000005150000005951b81766725d2564633b0b0f5f2e00", "S-1-5-21-397955417-626881126-188441444-3038991"}, {"050000000105000000000005150000005951b81766725d2564633b0b2f5b2e00", "S-1-5-21-397955417-626881126-188441444-3037999"}, {"050000000105000000000005150000005951b81766725d2564633b0bef8f3100", "S-1-5-21-397955417-626881126-188441444-3248111"}, {"050000000105000000000005150000005951b81766725d2564633b0b075f2e00", "S-1-5-21-397955417-626881126-188441444-3038983"}, {"040000000104000000000005150000004c86cebca07160e63fdce887", "S-1-5-21-3167651404-3865080224-2280184895"}, {"050000000105000000000005150000004c86cebca07160e63fdce8875a040000", "S-1-5-21-3167651404-3865080224-2280184895-1114"}, {"050000000105000000000005150000004c86cebca07160e63fdce88757040000", "S-1-5-21-3167651404-3865080224-2280184895-1111"}, } for i, test := range tests { a := new(testSIDStruct) hexStr := TestNDRHeader + "01020304" + test.Hex //The 01000000 is a dumby value for the pointer uint32 b, _ := hex.DecodeString(hexStr) dec := ndr.NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.SID, a.SID.String(), "SID not as expected for test %d", i+1) } } rpc-2.0.3/v2/mstypes/user_session_key.go000066400000000000000000000004641375324356300202720ustar00rootroot00000000000000package mstypes // CypherBlock implements https://msdn.microsoft.com/en-us/library/cc237040.aspx type CypherBlock struct { Data [8]byte // size = 8 } // UserSessionKey implements https://msdn.microsoft.com/en-us/library/cc237080.aspx type UserSessionKey struct { CypherBlock [2]CypherBlock // size = 2 } rpc-2.0.3/v2/ndr/000077500000000000000000000000001375324356300134255ustar00rootroot00000000000000rpc-2.0.3/v2/ndr/arrays.go000066400000000000000000000307621375324356300152650ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" "strconv" ) // intFromTag returns an int that is a value in a struct tag key/value pair func intFromTag(tag reflect.StructTag, key string) (int, error) { ndrTag := parseTags(tag) d := 1 if n, ok := ndrTag.Map[key]; ok { i, err := strconv.Atoi(n) if err != nil { return d, fmt.Errorf("invalid dimensions tag [%s]: %v", n, err) } d = i } return d, nil } // parseDimensions returns the a slice of the size of each dimension and type of the member at the deepest level. func parseDimensions(v reflect.Value) (l []int, tb reflect.Type) { if v.Kind() == reflect.Ptr { v = v.Elem() } t := v.Type() if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Array && t.Kind() != reflect.Slice { return } l = append(l, v.Len()) if t.Elem().Kind() == reflect.Array || t.Elem().Kind() == reflect.Slice { // contains array or slice var m []int m, tb = parseDimensions(v.Index(0)) l = append(l, m...) } else { tb = t.Elem() } return } // sliceDimensions returns the count of dimensions a slice has. func sliceDimensions(t reflect.Type) (d int, tb reflect.Type) { if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() == reflect.Slice { d++ var n int n, tb = sliceDimensions(t.Elem()) d += n } else { tb = t } return } // makeSubSlices is a deep recursive creation/initialisation of multi-dimensional slices. // Takes the reflect.Value of the 1st dimension and a slice of the lengths of the sub dimensions func makeSubSlices(v reflect.Value, l []int) { ty := v.Type().Elem() if ty.Kind() != reflect.Slice { return } for i := 0; i < v.Len(); i++ { s := reflect.MakeSlice(ty, l[0], l[0]) v.Index(i).Set(s) // Are there more sub dimensions? if len(l) > 1 { makeSubSlices(v.Index(i), l[1:]) } } return } // multiDimensionalIndexPermutations returns all the permutations of the indexes of a multi-dimensional slice. // The input is a slice of integers that indicates the max size/length of each dimension func multiDimensionalIndexPermutations(l []int) (ps [][]int) { z := make([]int, len(l), len(l)) // The zeros permutation ps = append(ps, z) // for each dimension, in reverse for i := len(l) - 1; i >= 0; i-- { ws := make([][]int, len(ps)) copy(ws, ps) //create a permutation for each of the iterations of the current dimension for j := 1; j <= l[i]-1; j++ { // For each existing permutation for _, p := range ws { np := make([]int, len(p), len(p)) copy(np, p) np[i] = j ps = append(ps, np) } } } return } // precedingMax reads off the next conformant max value func (dec *Decoder) precedingMax() uint32 { m := dec.conformantMax[0] dec.conformantMax = dec.conformantMax[1:] return m } // fillFixedArray establishes if the fixed array is uni or multi dimensional and then fills it. func (dec *Decoder) fillFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { l, t := parseDimensions(v) if t.Kind() == reflect.String { tag = reflect.StructTag(subStringArrayTag) } if len(l) < 1 { return errors.New("could not establish dimensions of fixed array") } if len(l) == 1 { err := dec.fillUniDimensionalFixedArray(v, tag, def) if err != nil { return fmt.Errorf("could not fill uni-dimensional fixed array: %v", err) } return nil } // Fixed array is multidimensional ps := multiDimensionalIndexPermutations(l[:len(l)-1]) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } // fill with the last dimension array err := dec.fillUniDimensionalFixedArray(a, tag, def) if err != nil { return fmt.Errorf("could not fill dimension %v of multi-dimensional fixed array: %v", p, err) } } return nil } // readUniDimensionalFixedArray reads an array (not slice) from the byte stream. func (dec *Decoder) fillUniDimensionalFixedArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { for i := 0; i < v.Len(); i++ { err := dec.fill(v.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of fixed array: %v", i, err) } } return nil } // fillConformantArray establishes if the conformant array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, _ := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantArray(v, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() n := int(m) a := reflect.MakeSlice(v.Type(), n, n) for i := 0; i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantArray fills the multi-dimensional slice value provided from conformant array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantArray(v reflect.Value, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the max size of each dimensions from the ndr stream l := make([]int, d, d) for i := range l { l[i] = int(dec.precedingMax()) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v for _, i := range p { a = a.Index(i) } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional varying array: %v", err) } t := v.Type() // Total size of the array is the offset in the index being passed plus the actual count of elements being passed. n := int(s + o) a := reflect.MakeSlice(t, n, n) // Populate the array starting at the offset specified for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalVaryingArray fills the multi-dimensional slice value provided from varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read size of dimension %d: %v", i+1, err) } l[i] = int(s) + int(off) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, l[0], l[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, l[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(l) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions? for i, j := range p { if j < o[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } // fillConformantVaryingArray establishes if the varying array is uni or multi dimensional and then fills the slice. func (dec *Decoder) fillConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, t := sliceDimensions(v.Type()) if d > 1 { err := dec.fillMultiDimensionalConformantVaryingArray(v, t, d, tag, def) if err != nil { return err } } else { err := dec.fillUniDimensionalConformantVaryingArray(v, tag, def) if err != nil { return err } } return nil } // fillUniDimensionalConformantVaryingArray fills the uni-dimensional slice value. func (dec *Decoder) fillUniDimensionalConformantVaryingArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { m := dec.precedingMax() o, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of uni-dimensional conformant varying array: %v", err) } s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not establish actual count of uni-dimensional conformant varying array: %v", err) } if m < o+s { return errors.New("max count is less than the offset plus actual count") } t := v.Type() n := int(s) a := reflect.MakeSlice(t, n, n) for i := int(o); i < n; i++ { err := dec.fill(a.Index(i), tag, def) if err != nil { return fmt.Errorf("could not fill index %d of uni-dimensional conformant varying array: %v", i, err) } } v.Set(a) return nil } // fillMultiDimensionalConformantVaryingArray fills the multi-dimensional slice value provided from conformant varying array data. // The number of dimensions must be specified. This must be less than or equal to the dimensions in the slice for this // method not to panic. func (dec *Decoder) fillMultiDimensionalConformantVaryingArray(v reflect.Value, t reflect.Type, d int, tag reflect.StructTag, def *[]deferedPtr) error { // Read the offset and actual count of each dimensions from the ndr stream m := make([]int, d, d) for i := range m { m[i] = int(dec.precedingMax()) } o := make([]int, d, d) l := make([]int, d, d) for i := range l { off, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read offset of dimension %d: %v", i+1, err) } o[i] = int(off) s, err := dec.readUint32() if err != nil { return fmt.Errorf("could not read actual count of dimension %d: %v", i+1, err) } if m[i] < int(s)+int(off) { m[i] = int(s) + int(off) } l[i] = int(s) } // Initialise size of slices // Initialise the size of the 1st dimension ty := v.Type() v.Set(reflect.MakeSlice(ty, m[0], m[0])) // Initialise the size of the other dimensions recursively makeSubSlices(v, m[1:]) // Get all permutations of the indexes and go through each and fill ps := multiDimensionalIndexPermutations(m) for _, p := range ps { // Get current multi-dimensional index to fill a := v var os bool // should this permutation be skipped due to the offset of any of the dimensions or max is higher than the actual count being passed for i, j := range p { if j < o[i] || j >= l[i] { os = true break } a = a.Index(j) } if os { // This permutation should be skipped as it is less than the offset for one of the dimensions. continue } err := dec.fill(a, tag, def) if err != nil { return fmt.Errorf("could not fill index %v of slice: %v", p, err) } } return nil } rpc-2.0.3/v2/ndr/arrays_test.go000066400000000000000000000150501375324356300163150ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "fmt" "reflect" "testing" "github.com/stretchr/testify/assert" ) const TestHeader = "01100800cccccccca00400000000000000000200" func TestParseDimensions(t *testing.T) { a := [2][2][2][]SimpleTest{} l, ta := parseDimensions(reflect.ValueOf(a)) assert.Equal(t, 4, len(l), "dimension count not as expected") assert.Equal(t, []int{2, 2, 2, 0}, l, "lengths list not as expected") assert.Equal(t, "SimpleTest", ta.Name(), "type within array not as expected") } func TestMakeSubSlices(t *testing.T) { l := []int{2, 5, 3, 1} a := new([][][][]uint32) v := reflect.ValueOf(a) v = v.Elem() ty := v.Type() s := reflect.MakeSlice(ty, l[0], l[0]) v.Set(s) makeSubSlices(v, l[1:]) assert.Equal(t, "[[[[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]]] [[[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]] [[0] [0] [0]]]]", fmt.Sprintf("%v", *a)) } func TestDimensionCountFromTag(t *testing.T) { var a StructWithMultiDimensionalConformantSlice v := reflect.ValueOf(a) d, err := intFromTag(v.Type().Field(0).Tag, "test") if err != nil { t.Errorf("error getting dimensions from tag: %v", err) } assert.Equal(t, 3, d, "number of dimensions not as expected") } type StructWithArray struct { A [4]uint32 } type StructWithMultiDimArray struct { A [2][3][2]uint32 } type StructWithConformantSlice struct { A []uint32 `ndr:"conformant"` } type StructWithVaryingSlice struct { A []uint32 `ndr:"varying"` } type StructWithConformantVaryingSlice struct { A []uint32 `ndr:"conformant,varying"` } type StructWithMultiDimensionalConformantSlice struct { A [][][]uint32 `ndr:"conformant,test:3"` } type StructWithMultiDimensionalVaryingSlice struct { A [][][]uint32 `ndr:"varying"` } type StructWithMultiDimensionalConformantVaryingSlice struct { A [][][]uint32 `ndr:"conformant,varying"` } func TestReadUniDimensionalFixedArray(t *testing.T) { hexStr := TestHeader + "01000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalFixedArray(t *testing.T) { hexStr := TestHeader + "0100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [2][3][2]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional fixed array not as expected") } func TestReadUniDimensionalConformantArray(t *testing.T) { hexStr := TestHeader + "0400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithConformantSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalConformantArray(t *testing.T) { hexStr := TestHeader + "0200000003000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalConformantSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant array not as expected") } func TestReadUniDimensionalVaryingArray(t *testing.T) { hexStr := TestHeader + "000000000400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalVaryingArray(t *testing.T) { hexStr := TestHeader + "0000000002000000000000000300000000000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant varying array not as expected") } func TestReadUniDimensionalConformantVaryingArray(t *testing.T) { hexStr := TestHeader + "04000000000000000400000001000000020000000300000004000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithConformantVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for i := range a.A { assert.Equal(t, uint32(i+1), a.A[i], "Value of index %d not as expected", i) } } func TestReadMultiDimensionalConformantVaryingArray(t *testing.T) { hexStr := TestHeader + "0200000003000000020000000000000002000000000000000300000000000000020000000100000002000000030000000400000005000000060000000700000008000000090000000a0000000b0000000c0000000d0000000e0000000f000000100000001100000012000000130000001400000015000000160000001700000018000000190000001a0000001b0000001c0000001d0000001e0000001f0000002000000021000000220000002300000024000000" b, _ := hex.DecodeString(hexStr) a := new(StructWithMultiDimensionalConformantVaryingSlice) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]uint32{ { {1, 2}, {3, 4}, {5, 6}, }, { {7, 8}, {9, 10}, {11, 12}, }, } assert.Equal(t, ar, a.A, "multi-dimensional conformant varying array not as expected") } rpc-2.0.3/v2/ndr/decoder.go000066400000000000000000000277331375324356300153750ustar00rootroot00000000000000// Package ndr provides the ability to unmarshal NDR encoded byte steams into Go data structures package ndr import ( "bufio" "fmt" "io" "reflect" "strings" ) // Struct tag values const ( TagConformant = "conformant" TagVarying = "varying" TagPointer = "pointer" TagPipe = "pipe" ) // Decoder unmarshals NDR byte stream data into a Go struct representation type Decoder struct { r *bufio.Reader // source of the data size int // initial size of bytes in buffer ch CommonHeader // NDR common header ph PrivateHeader // NDR private header conformantMax []uint32 // conformant max values that were moved to the beginning of the structure s interface{} // pointer to the structure being populated current []string // keeps track of the current field being populated } type deferedPtr struct { v reflect.Value tag reflect.StructTag } // NewDecoder creates a new instance of a NDR Decoder. func NewDecoder(r io.Reader) *Decoder { dec := new(Decoder) dec.r = bufio.NewReader(r) dec.r.Peek(int(commonHeaderBytes)) // For some reason an operation is needed on the buffer to initialise it so Buffered() != 0 dec.size = dec.r.Buffered() return dec } // Decode unmarshals the NDR encoded bytes into the pointer of a struct provided. func (dec *Decoder) Decode(s interface{}) error { dec.s = s err := dec.readCommonHeader() if err != nil { return err } err = dec.readPrivateHeader() if err != nil { return err } _, err = dec.r.Discard(4) //The next 4 bytes are an RPC unique pointer referent. We just skip these. if err != nil { return Errorf("unable to process byte stream: %v", err) } return dec.process(s, reflect.StructTag("")) } func (dec *Decoder) process(s interface{}, tag reflect.StructTag) error { // Scan for conformant fields as their max counts are moved to the beginning // http://pubs.opengroup.org/onlinepubs/9629399/chap14.htm#tagfcjh_37 err := dec.scanConformantArrays(s, tag) if err != nil { return err } // Recursively fill the struct fields var localDef []deferedPtr err = dec.fill(s, tag, &localDef) if err != nil { return Errorf("could not decode: %v", err) } // Read any deferred referents associated with pointers for _, p := range localDef { err = dec.process(p.v, p.tag) if err != nil { return fmt.Errorf("could not decode deferred referent: %v", err) } } return nil } // scanConformantArrays scans the structure for embedded conformant fields and captures the maximum element counts for // dimensions of the array that are moved to the beginning of the structure. func (dec *Decoder) scanConformantArrays(s interface{}, tag reflect.StructTag) error { err := dec.conformantScan(s, tag) if err != nil { return fmt.Errorf("failed to scan for embedded conformant arrays: %v", err) } for i := range dec.conformantMax { dec.conformantMax[i], err = dec.readUint32() if err != nil { return fmt.Errorf("could not read preceding conformant max count index %d: %v", i, err) } } return nil } // conformantScan inspects the structure's fields for whether they are conformant. func (dec *Decoder) conformantScan(s interface{}, tag reflect.StructTag) error { ndrTag := parseTags(tag) if ndrTag.HasValue(TagPointer) { return nil } v := getReflectValue(s) switch v.Kind() { case reflect.Struct: for i := 0; i < v.NumField(); i++ { err := dec.conformantScan(v.Field(i), v.Type().Field(i).Tag) if err != nil { return err } } case reflect.String: if !ndrTag.HasValue(TagConformant) { break } dec.conformantMax = append(dec.conformantMax, uint32(0)) case reflect.Slice: if !ndrTag.HasValue(TagConformant) { break } d, t := sliceDimensions(v.Type()) for i := 0; i < d; i++ { dec.conformantMax = append(dec.conformantMax, uint32(0)) } // For string arrays there is a common max for the strings within the array. if t.Kind() == reflect.String { dec.conformantMax = append(dec.conformantMax, uint32(0)) } } return nil } func (dec *Decoder) isPointer(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) (bool, error) { // Pointer so defer filling the referent ndrTag := parseTags(tag) if ndrTag.HasValue(TagPointer) { p, err := dec.readUint32() if err != nil { return true, fmt.Errorf("could not read pointer: %v", err) } ndrTag.delete(TagPointer) if p != 0 { // if pointer is not zero add to the deferred items at end of stream *def = append(*def, deferedPtr{v, ndrTag.StructTag()}) } return true, nil } return false, nil } func getReflectValue(s interface{}) (v reflect.Value) { if r, ok := s.(reflect.Value); ok { v = r } else { if reflect.ValueOf(s).Kind() == reflect.Ptr { v = reflect.ValueOf(s).Elem() } } return } // fill populates fields with values from the NDR byte stream. func (dec *Decoder) fill(s interface{}, tag reflect.StructTag, localDef *[]deferedPtr) error { v := getReflectValue(s) //// Pointer so defer filling the referent ptr, err := dec.isPointer(v, tag, localDef) if err != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(dec.current, "/"), err) } if ptr { return nil } // Populate the value from the byte stream switch v.Kind() { case reflect.Struct: dec.current = append(dec.current, v.Type().Name()) //Track the current field being filled // in case struct is a union, track this and the selected union field for efficiency var unionTag reflect.Value var unionField string // field to fill if struct is a union // Go through each field in the struct and recursively fill for i := 0; i < v.NumField(); i++ { fieldName := v.Type().Field(i).Name dec.current = append(dec.current, fieldName) //Track the current field being filled //fmt.Fprintf(os.Stderr, "DEBUG Decoding: %s\n", strings.Join(dec.current, "/")) structTag := v.Type().Field(i).Tag ndrTag := parseTags(structTag) // Union handling if !unionTag.IsValid() { // Is this field a union tag? unionTag = dec.isUnion(v.Field(i), structTag) } else { // What is the selected field value of the union if we don't already know if unionField == "" { unionField, err = unionSelectedField(v, unionTag) if err != nil { return fmt.Errorf("could not determine selected union value field for %s with discriminat"+ " tag %s: %v", v.Type().Name(), unionTag, err) } } if ndrTag.HasValue(TagUnionField) && fieldName != unionField { // is a union and this field has not been selected so will skip it. dec.current = dec.current[:len(dec.current)-1] //This field has been skipped so remove it from the current field tracker continue } } // Check if field is a pointer if v.Field(i).Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && v.Field(i).Type().Kind() == reflect.Slice && v.Field(i).Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes structTag, err = addSizeToTag(v, v.Field(i), structTag) if err != nil { return fmt.Errorf("could not get rawbytes field(%s) size: %v", strings.Join(dec.current, "/"), err) } ptr, err := dec.isPointer(v.Field(i), structTag, localDef) if err != nil { return fmt.Errorf("could not process struct field(%s): %v", strings.Join(dec.current, "/"), err) } if !ptr { err := dec.readRawBytes(v.Field(i), structTag) if err != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(dec.current, "/"), err) } } } else { err := dec.fill(v.Field(i), structTag, localDef) if err != nil { return fmt.Errorf("could not fill struct field(%s): %v", strings.Join(dec.current, "/"), err) } } dec.current = dec.current[:len(dec.current)-1] //This field has been filled so remove it from the current field tracker } dec.current = dec.current[:len(dec.current)-1] //This field has been filled so remove it from the current field tracker case reflect.Bool: i, err := dec.readBool() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint8: i, err := dec.readUint8() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint16: i, err := dec.readUint16() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint32: i, err := dec.readUint32() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Uint64: i, err := dec.readUint64() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int8: i, err := dec.readInt8() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int16: i, err := dec.readInt16() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int32: i, err := dec.readInt32() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Int64: i, err := dec.readInt64() if err != nil { return fmt.Errorf("could not fill %s: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.String: ndrTag := parseTags(tag) conformant := ndrTag.HasValue(TagConformant) // strings are always varying so this is assumed without an explicit tag var s string var err error if conformant { s, err = dec.readConformantVaryingString(localDef) if err != nil { return fmt.Errorf("could not fill with conformant varying string: %v", err) } } else { s, err = dec.readVaryingString(localDef) if err != nil { return fmt.Errorf("could not fill with varying string: %v", err) } } v.Set(reflect.ValueOf(s)) case reflect.Float32: i, err := dec.readFloat32() if err != nil { return fmt.Errorf("could not fill %v: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Float64: i, err := dec.readFloat64() if err != nil { return fmt.Errorf("could not fill %v: %v", v.Type().Name(), err) } v.Set(reflect.ValueOf(i)) case reflect.Array: err := dec.fillFixedArray(v, tag, localDef) if err != nil { return err } case reflect.Slice: if v.Type().Implements(reflect.TypeOf(new(RawBytes)).Elem()) && v.Type().Elem().Kind() == reflect.Uint8 { //field is for rawbytes err := dec.readRawBytes(v, tag) if err != nil { return fmt.Errorf("could not fill raw bytes struct field(%s): %v", strings.Join(dec.current, "/"), err) } break } ndrTag := parseTags(tag) conformant := ndrTag.HasValue(TagConformant) varying := ndrTag.HasValue(TagVarying) if ndrTag.HasValue(TagPipe) { err := dec.fillPipe(v, tag) if err != nil { return err } break } _, t := sliceDimensions(v.Type()) if t.Kind() == reflect.String && !ndrTag.HasValue(subStringArrayValue) { // String array err := dec.readStringsArray(v, tag, localDef) if err != nil { return err } break } // varying is assumed as fixed arrays use the Go array type rather than slice if conformant && varying { err := dec.fillConformantVaryingArray(v, tag, localDef) if err != nil { return err } } else if !conformant && varying { err := dec.fillVaryingArray(v, tag, localDef) if err != nil { return err } } else { //default to conformant and not varying err := dec.fillConformantArray(v, tag, localDef) if err != nil { return err } } default: return fmt.Errorf("unsupported type") } return nil } // readBytes returns a number of bytes from the NDR byte stream. func (dec *Decoder) readBytes(n int) ([]byte, error) { //TODO make this take an int64 as input to allow for larger values on all systems? b := make([]byte, n, n) m, err := dec.r.Read(b) if err != nil || m != n { return b, fmt.Errorf("error reading bytes from stream: %v", err) } return b, nil } rpc-2.0.3/v2/ndr/decoder_test.go000066400000000000000000000077751375324356300164400ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func TestReadCommonHeader(t *testing.T) { var tests = []struct { EncodedHex string ExpectFail bool }{ {"01100800cccccccc", false}, // Little Endian {"01000008cccccccc", false}, // Big Endian have to change the bytes for the header size? This test vector was artificially created. Need proper test vector //{"01100800cccccccc1802000000000000", false}, //{"01100800cccccccc0002000000000000", false}, //{"01100800cccccccc0001000000000000", false}, //{"01100800cccccccce000000000000000", false}, //{"01100800ccccccccf000000000000000", false}, //{"01100800cccccccc7801000000000000", false}, //{"01100800cccccccc4801000000000000", false}, //{"01100800ccccccccd001000000000000", false}, {"02100800cccccccc", true}, // Incorrect version {"02100900cccccccc", true}, // Incorrect length } for i, test := range tests { b, _ := hex.DecodeString(test.EncodedHex) dec := NewDecoder(bytes.NewReader(b)) err := dec.readCommonHeader() if err != nil && !test.ExpectFail { t.Errorf("error reading common header of test %d: %v", i, err) } if err == nil && test.ExpectFail { t.Errorf("expected failure on reading common header of test %d: %v", i, err) } } } func TestReadPrivateHeader(t *testing.T) { var tests = []struct { EncodedHex string ExpectFail bool Length int }{ {"01100800cccccccc1802000000000000", false, 536}, {"01100800cccccccc0002000000000000", false, 512}, {"01100800cccccccc0001000000000000", false, 256}, {"01100800ccccccccFF00000000000000", true, 255}, // Length not multiple of 8 {"01100800cccccccc00010000000000", true, 256}, // Too short } for i, test := range tests { b, _ := hex.DecodeString(test.EncodedHex) dec := NewDecoder(bytes.NewReader(b)) err := dec.readCommonHeader() if err != nil { t.Errorf("error reading common header of test %d: %v", i, err) } err = dec.readPrivateHeader() if err != nil && !test.ExpectFail { t.Errorf("error reading private header of test %d: %v", i, err) } if err == nil && test.ExpectFail { t.Errorf("expected failure on reading private header of test %d: %v", i, err) } if dec.ph.ObjectBufferLength != uint32(test.Length) { t.Errorf("Objectbuffer length expected %d actual %d", test.Length, dec.ph.ObjectBufferLength) } } } type SimpleTest struct { A uint32 B uint32 } func TestBasicDecode(t *testing.T) { hexStr := "01100800cccccccca00400000000000000000200d186660f656ac601" b, _ := hex.DecodeString(hexStr) ft := new(SimpleTest) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err != nil { t.Fatalf("error decoding: %v", err) } assert.Equal(t, uint32(258377425), ft.A, "Value of field A not as expected") assert.Equal(t, uint32(29780581), ft.B, "Value of field B not as expected %d") } func TestBasicDecodeOverRun(t *testing.T) { hexStr := "01100800cccccccca00400000000000000000200d186660f" b, _ := hex.DecodeString(hexStr) ft := new(SimpleTest) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err == nil { t.Errorf("Expected error for trying to read more than the bytes we have") } } type testEmbeddingPointer struct { A testEmbeddedPointer `ndr:"pointer"` B uint32 // 1 } type testEmbeddedPointer struct { C testEmbeddedPointer2 `ndr:"pointer"` D uint32 `ndr:"pointer"` // 2 E uint32 // 3 } type testEmbeddedPointer2 struct { F uint32 `ndr:"pointer"` // 4 G uint32 // 5 } func Test_EmbeddedPointers(t *testing.T) { hexStr := TestHeader + "00040002" + "01000000" + "00040002" + "00040002" + "03000000" + "00040002" + "05000000" + "04000000" + "02000000" b, _ := hex.DecodeString(hexStr) ft := new(testEmbeddingPointer) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(ft) if err != nil { t.Fatalf("error decoding: %v", err) } assert.Equal(t, uint32(1), ft.B) assert.Equal(t, uint32(2), ft.A.D) assert.Equal(t, uint32(3), ft.A.E) assert.Equal(t, uint32(4), ft.A.C.F) assert.Equal(t, uint32(5), ft.A.C.G) } rpc-2.0.3/v2/ndr/error.go000066400000000000000000000007421375324356300151100ustar00rootroot00000000000000package ndr import "fmt" // Malformed implements the error interface for malformed NDR encoding errors. type Malformed struct { EText string } // Error implements the error interface on the Malformed struct. func (e Malformed) Error() string { return fmt.Sprintf("malformed NDR stream: %s", e.EText) } // Errorf formats an error message into a malformed NDR error. func Errorf(format string, a ...interface{}) Malformed { return Malformed{EText: fmt.Sprintf(format, a...)} } rpc-2.0.3/v2/ndr/header.go000066400000000000000000000076541375324356300152200ustar00rootroot00000000000000package ndr import ( "encoding/binary" "fmt" ) /* Serialization Version 1 https://msdn.microsoft.com/en-us/library/cc243563.aspx Common Header - https://msdn.microsoft.com/en-us/library/cc243890.aspx 8 bytes in total: - First byte - Version: Must equal 1 - Second byte - 1st 4 bits: Endianess (0=Big; 1=Little); 2nd 4 bits: Character Encoding (0=ASCII; 1=EBCDIC) - 3rd - Floating point representation (This does not seem to be the case in examples for Microsoft test sources) - 4th - Common Header Length: Must equal 8 - 5th - 8th - Filler: MUST be set to 0xcccccccc on marshaling, and SHOULD be ignored during unmarshaling. Private Header - https://msdn.microsoft.com/en-us/library/cc243919.aspx 8 bytes in total: - First 4 bytes - Indicates the length of a serialized top-level type in the octet stream. It MUST include the padding length and exclude the header itself. - Second 4 bytes - Filler: MUST be set to 0 (zero) during marshaling, and SHOULD be ignored during unmarshaling. */ const ( protocolVersion uint8 = 1 commonHeaderBytes uint16 = 8 bigEndian = 0 littleEndian = 1 ascii uint8 = 0 ebcdic uint8 = 1 ieee uint8 = 0 vax uint8 = 1 cray uint8 = 2 ibm uint8 = 3 ) // CommonHeader implements the NDR common header: https://msdn.microsoft.com/en-us/library/cc243889.aspx type CommonHeader struct { Version uint8 Endianness binary.ByteOrder CharacterEncoding uint8 FloatRepresentation uint8 HeaderLength uint16 Filler []byte } // PrivateHeader implements the NDR private header: https://msdn.microsoft.com/en-us/library/cc243919.aspx type PrivateHeader struct { ObjectBufferLength uint32 Filler []byte } func (dec *Decoder) readCommonHeader() error { // Version vb, err := dec.r.ReadByte() if err != nil { return Malformed{EText: "could not read first byte of common header for version"} } dec.ch.Version = uint8(vb) if dec.ch.Version != protocolVersion { return Malformed{EText: fmt.Sprintf("byte stream does not indicate a RPC Type serialization of version %v", protocolVersion)} } // Read Endianness & Character Encoding eb, err := dec.r.ReadByte() if err != nil { return Malformed{EText: "could not read second byte of common header for endianness"} } endian := int(eb >> 4 & 0xF) if endian != 0 && endian != 1 { return Malformed{EText: "common header does not indicate a valid endianness"} } dec.ch.CharacterEncoding = uint8(vb & 0xF) if dec.ch.CharacterEncoding != 0 && dec.ch.CharacterEncoding != 1 { return Malformed{EText: "common header does not indicate a valid character encoding"} } switch endian { case littleEndian: dec.ch.Endianness = binary.LittleEndian case bigEndian: dec.ch.Endianness = binary.BigEndian } // Common header length lb, err := dec.readBytes(2) if err != nil { return Malformed{EText: fmt.Sprintf("could not read common header length: %v", err)} } dec.ch.HeaderLength = dec.ch.Endianness.Uint16(lb) if dec.ch.HeaderLength != commonHeaderBytes { return Malformed{EText: "common header does not indicate a valid length"} } // Filler bytes dec.ch.Filler, err = dec.readBytes(4) if err != nil { return Malformed{EText: fmt.Sprintf("could not read common header filler: %v", err)} } return nil } func (dec *Decoder) readPrivateHeader() error { // The next 8 bytes after the common header comprise the RPC type marshalling private header for constructed types. err := binary.Read(dec.r, dec.ch.Endianness, &dec.ph.ObjectBufferLength) if err != nil { return Malformed{EText: "could not read private header object buffer length"} } if dec.ph.ObjectBufferLength%8 != 0 { return Malformed{EText: "object buffer length not a multiple of 8"} } // Filler bytes dec.ph.Filler, err = dec.readBytes(4) if err != nil { return Malformed{EText: fmt.Sprintf("could not read private header filler: %v", err)} } return nil } rpc-2.0.3/v2/ndr/pipe.go000066400000000000000000000013141375324356300147100ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" ) func (dec *Decoder) fillPipe(v reflect.Value, tag reflect.StructTag) error { s, err := dec.readUint32() // read element count of first chunk if err != nil { return err } a := reflect.MakeSlice(v.Type(), 0, 0) c := reflect.MakeSlice(v.Type(), int(s), int(s)) for s != 0 { for i := 0; i < int(s); i++ { err := dec.fill(c.Index(i), tag, &[]deferedPtr{}) if err != nil { return fmt.Errorf("could not fill element %d of pipe: %v", i, err) } } s, err = dec.readUint32() // read element count of first chunk if err != nil { return err } a = reflect.AppendSlice(a, c) c = reflect.MakeSlice(v.Type(), int(s), int(s)) } v.Set(a) return nil } rpc-2.0.3/v2/ndr/pipe_test.go000066400000000000000000000011121375324356300157430ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const testPipe = "04000000010000000200000003000000040000000300000001000000020000000300000000000000" type structWithPipe struct { A []uint32 `ndr:"pipe"` } func TestFillPipe(t *testing.T) { hexStr := TestHeader + testPipe b, _ := hex.DecodeString(hexStr) a := new(structWithPipe) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } tp := []uint32{1, 2, 3, 4, 1, 2, 3} assert.Equal(t, tp, a.A, "Value of pipe not as expected") } rpc-2.0.3/v2/ndr/primitives.go000066400000000000000000000123711375324356300161530ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/binary" "math" ) // Byte sizes of primitive types const ( SizeBool = 1 SizeChar = 1 SizeUint8 = 1 SizeUint16 = 2 SizeUint32 = 4 SizeUint64 = 8 SizeEnum = 2 SizeSingle = 4 SizeDouble = 8 SizePtr = 4 ) // Bool is an NDR Boolean which is a logical quantity that assumes one of two values: TRUE or FALSE. // NDR represents a Boolean as one octet. // It represents a value of FALSE as a zero octet, an octet in which every bit is reset. // It represents a value of TRUE as a non-zero octet, an octet in which one or more bits are set. // Char is an NDR character. // NDR represents a character as one octet. // Characters have two representation formats: ASCII and EBCDIC. // USmall is an unsigned 8 bit integer // UShort is an unsigned 16 bit integer // ULong is an unsigned 32 bit integer // UHyper is an unsigned 64 bit integer // Small is an signed 8 bit integer // Short is an signed 16 bit integer // Long is an signed 32 bit integer // Hyper is an signed 64 bit integer // Enum is the NDR representation of enumerated types as signed short integers (2 octets) // Single is an NDR defined single-precision floating-point data type // Double is an NDR defined double-precision floating-point data type // readBool reads a byte representing a boolean. // NDR represents a Boolean as one octet. // It represents a value of FALSE as a zero octet, an octet in which every bit is reset. // It represents a value of TRUE as a non-zero octet, an octet in which one or more bits are set. func (dec *Decoder) readBool() (bool, error) { i, err := dec.readUint8() if err != nil { return false, err } if i != 0 { return true, nil } return false, nil } // readChar reads bytes representing a 8bit ASCII integer cast to a rune. func (dec *Decoder) readChar() (rune, error) { var r rune a, err := dec.readUint8() if err != nil { return r, err } return rune(a), nil } // readUint8 reads bytes representing a 8bit unsigned integer. func (dec *Decoder) readUint8() (uint8, error) { b, err := dec.r.ReadByte() if err != nil { return uint8(0), err } return uint8(b), nil } // readUint16 reads bytes representing a 16bit unsigned integer. func (dec *Decoder) readUint16() (uint16, error) { dec.ensureAlignment(SizeUint16) b, err := dec.readBytes(SizeUint16) if err != nil { return uint16(0), err } return dec.ch.Endianness.Uint16(b), nil } // readUint32 reads bytes representing a 32bit unsigned integer. func (dec *Decoder) readUint32() (uint32, error) { dec.ensureAlignment(SizeUint32) b, err := dec.readBytes(SizeUint32) if err != nil { return uint32(0), err } return dec.ch.Endianness.Uint32(b), nil } // readUint32 reads bytes representing a 32bit unsigned integer. func (dec *Decoder) readUint64() (uint64, error) { dec.ensureAlignment(SizeUint64) b, err := dec.readBytes(SizeUint64) if err != nil { return uint64(0), err } return dec.ch.Endianness.Uint64(b), nil } func (dec *Decoder) readInt8() (int8, error) { dec.ensureAlignment(SizeUint8) b, err := dec.readBytes(SizeUint8) if err != nil { return 0, err } var i int8 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt16() (int16, error) { dec.ensureAlignment(SizeUint16) b, err := dec.readBytes(SizeUint16) if err != nil { return 0, err } var i int16 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt32() (int32, error) { dec.ensureAlignment(SizeUint32) b, err := dec.readBytes(SizeUint32) if err != nil { return 0, err } var i int32 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } func (dec *Decoder) readInt64() (int64, error) { dec.ensureAlignment(SizeUint64) b, err := dec.readBytes(SizeUint64) if err != nil { return 0, err } var i int64 buf := bytes.NewReader(b) err = binary.Read(buf, dec.ch.Endianness, &i) if err != nil { return 0, err } return i, nil } // https://en.wikipedia.org/wiki/IEEE_754-1985 func (dec *Decoder) readFloat32() (f float32, err error) { dec.ensureAlignment(SizeSingle) b, err := dec.readBytes(SizeSingle) if err != nil { return } bits := dec.ch.Endianness.Uint32(b) f = math.Float32frombits(bits) return } func (dec *Decoder) readFloat64() (f float64, err error) { dec.ensureAlignment(SizeDouble) b, err := dec.readBytes(SizeDouble) if err != nil { return } bits := dec.ch.Endianness.Uint64(b) f = math.Float64frombits(bits) return } // NDR enforces NDR alignment of primitive data; that is, any primitive of size n octets is aligned at a octet stream // index that is a multiple of n. (In this version of NDR, n is one of {1, 2, 4, 8}.) An octet stream index indicates // the number of an octet in an octet stream when octets are numbered, beginning with 0, from the first octet in the // stream. Where necessary, an alignment gap, consisting of octets of unspecified value, precedes the representation // of a primitive. The gap is of the smallest size sufficient to align the primitive. func (dec *Decoder) ensureAlignment(n int) { p := dec.size - dec.r.Buffered() if s := p % n; s != 0 { dec.r.Discard(n - s) } } rpc-2.0.3/v2/ndr/primitives_test.go000066400000000000000000000021511375324356300172050ustar00rootroot00000000000000package ndr import ( "bufio" "bytes" "encoding/binary" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) func TestReadFloat32(t *testing.T) { tests := []struct { hexStr string value float32 order binary.ByteOrder }{ {"3E200000", 0.15625, binary.BigEndian}, {"00000000", 0.0, binary.BigEndian}, {"3F800000", 1.0, binary.BigEndian}, {"BF800000", -1.0, binary.BigEndian}, {"00000001", 1.4e-45, binary.BigEndian}, {"00400000", 5.877472e-39, binary.BigEndian}, {"007FFFFF", 1.1754942e-38, binary.BigEndian}, {"00800000", 1.1754944e-38, binary.BigEndian}, {"7F7FFFFF", 3.4028235e38, binary.BigEndian}, //TODO need some littleendian test vectors } for i, test := range tests { b, _ := hex.DecodeString(test.hexStr) //t.Logf("%s %08b\n", test.hexStr,b) r := bufio.NewReader(bytes.NewReader(b)) dec := Decoder{ r: r, ch: CommonHeader{Endianness: test.order}, } f, err := dec.readFloat32() if err != nil { t.Errorf("could not read float32 test %d: %v", i, err) } assert.Equal(t, test.value, f, "float32 not as expect for test %d: %s", i, test.hexStr) } } rpc-2.0.3/v2/ndr/rawbytes.go000066400000000000000000000026471375324356300156250ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" "strconv" ) // type MyBytes []byte // implement RawBytes interface const ( sizeMethod = "Size" ) // RawBytes interface should be implemented if reading just a number of bytes from the NDR stream type RawBytes interface { Size(interface{}) int } func rawBytesSize(parent reflect.Value, v reflect.Value) (int, error) { sf := v.MethodByName(sizeMethod) if !sf.IsValid() { return 0, fmt.Errorf("could not find a method called %s on the implementation of RawBytes", sizeMethod) } in := []reflect.Value{parent} f := sf.Call(in) if f[0].Kind() != reflect.Int { return 0, errors.New("the RawBytes size function did not return an integer") } return int(f[0].Int()), nil } func addSizeToTag(parent reflect.Value, v reflect.Value, tag reflect.StructTag) (reflect.StructTag, error) { size, err := rawBytesSize(parent, v) if err != nil { return tag, err } ndrTag := parseTags(tag) ndrTag.Map["size"] = strconv.Itoa(size) return ndrTag.StructTag(), nil } func (dec *Decoder) readRawBytes(v reflect.Value, tag reflect.StructTag) error { ndrTag := parseTags(tag) sizeStr, ok := ndrTag.Map["size"] if !ok { return errors.New("size tag not available") } size, err := strconv.Atoi(sizeStr) if err != nil { return fmt.Errorf("size not valid: %v", err) } b, err := dec.readBytes(size) if err != nil { return err } v.Set(reflect.ValueOf(b).Convert(v.Type())) return nil } rpc-2.0.3/v2/ndr/strings.go000066400000000000000000000030171375324356300154460ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" ) const ( subStringArrayTag = `ndr:"varying,X-subStringArray"` subStringArrayValue = "X-subStringArray" ) func uint16SliceToString(a []uint16) string { s := make([]rune, len(a), len(a)) for i := range s { s[i] = rune(a[i]) } if len(s) > 0 { // Remove any null terminator if s[len(s)-1] == rune(0) { s = s[:len(s)-1] } } return string(s) } func (dec *Decoder) readVaryingString(def *[]deferedPtr) (string, error) { a := new([]uint16) v := reflect.ValueOf(a) var t reflect.StructTag err := dec.fillUniDimensionalVaryingArray(v.Elem(), t, def) if err != nil { return "", err } s := uint16SliceToString(*a) return s, nil } func (dec *Decoder) readConformantVaryingString(def *[]deferedPtr) (string, error) { a := new([]uint16) v := reflect.ValueOf(a) var t reflect.StructTag err := dec.fillUniDimensionalConformantVaryingArray(v.Elem(), t, def) if err != nil { return "", err } s := uint16SliceToString(*a) return s, nil } func (dec *Decoder) readStringsArray(v reflect.Value, tag reflect.StructTag, def *[]deferedPtr) error { d, _ := sliceDimensions(v.Type()) ndrTag := parseTags(tag) var m []int //var ms int if ndrTag.HasValue(TagConformant) { for i := 0; i < d; i++ { m = append(m, int(dec.precedingMax())) } //common max size _ = dec.precedingMax() //ms = int(n) } tag = reflect.StructTag(subStringArrayTag) err := dec.fillVaryingArray(v, tag, def) if err != nil { return fmt.Errorf("could not read string array: %v", err) } return nil } rpc-2.0.3/v2/ndr/strings_test.go000066400000000000000000000203231375324356300165040ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/binary" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const ( TestStr = "hello world!" TestStrUTF16Hex = "680065006c006c006f00200077006f0072006c00640021000000" // little endian format ) type TestStructWithVaryingString struct { A string `ndr:"varying"` } type TestStructWithConformantVaryingString struct { A string `ndr:"conformant,varying"` } type TestStructWithConformantVaryingStringUniArray struct { A []string `ndr:"conformant,varying"` } // Should not have to specify varying tag type TestStructWithNonConformantStringUniArray struct { A []string } type TestStructWithConformantVaryingStringMultiArray struct { A [][][]string `ndr:"conformant,varying"` } // Should not have to specify varying tag type TestStructWithNonConformantStringMultiArray struct { A [][][]string } // Strings are always varying but the array may not be type TestStructWithFixedStringUniArray struct { A [4]string } type TestStructWithFixedStringMultiArray struct { A [2][3][2]string } func Test_uint16SliceToString(t *testing.T) { b, _ := hex.DecodeString(TestStrUTF16Hex) var u []uint16 for i := 0; i < len(b); i += 2 { u = append(u, binary.LittleEndian.Uint16(b[i:i+2])) } s := uint16SliceToString(u) assert.Equal(t, TestStr, s, "uint16SliceToString did not return as expected") } func Test_readVaryingString(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := TestHeader + "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // header:offset(0):actual count:data b, _ := hex.DecodeString(hexStr) a := new(TestStructWithVaryingString) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, TestStr, a.A, "value of decoded varying string not as expected") } func Test_readConformantVaryingString(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := TestHeader + hex.EncodeToString(ac) + "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // header:max:offset(0):actual count:data b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingString) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, TestStr, a.A, "value of decoded varying string not as expected") } func Test_readConformantStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + "04000000" + hex.EncodeToString(ac) + "0000000004000000" + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:1st dimension count(4):max for all strings:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, 4, len(a.A), "length of string array not as expected") for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readConformantStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + "02000000" + "03000000" + "02000000" + hex.EncodeToString(ac) + "0000000002000000" + "0000000003000000" + "0000000002000000" + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithConformantVaryingStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } func Test_readNonConformantStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + "0000000004000000" + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithNonConformantStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } assert.Equal(t, 4, len(a.A), "length of string array not as expected") for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readNonConformantStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + "0000000002000000" + "0000000003000000" + "0000000002000000" + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithNonConformantStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [][][]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } func Test_readFixedStringUniDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes hexStr := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data hexStr = TestHeader + hexStr + "0000" + hexStr + "0000" + hexStr + "0000" + hexStr // header:offset for 1st dim:actual for 1st dim:string array elements(4) with offset and actual counts. Need to include some bytes for alignment. b, _ := hex.DecodeString(hexStr) a := new(TestStructWithFixedStringUniArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } for _, s := range a.A { if s != TestStr { t.Fatalf("string array does not contain the right values") } } } func Test_readFixedStringMultiDimensionalArray(t *testing.T) { ac := make([]byte, 4, 4) binary.LittleEndian.PutUint32(ac, uint32(len(TestStrUTF16Hex)/4)) // actual count of number of uint16 bytes strb := "00000000" + hex.EncodeToString(ac) + TestStrUTF16Hex // offset(0):actual count:data var hexStr string for i := 0; i < 12; i++ { hexStr = hexStr + strb + "0000" } hexStr = TestHeader + hexStr b, _ := hex.DecodeString(hexStr) a := new(TestStructWithFixedStringMultiArray) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("%v", err) } ar := [2][3][2]string{ { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, { {TestStr, TestStr}, {TestStr, TestStr}, {TestStr, TestStr}, }, } assert.Equal(t, ar, a.A, "fixed multi-dimensional string array not as expected") } rpc-2.0.3/v2/ndr/tags.go000066400000000000000000000025111375324356300147110ustar00rootroot00000000000000package ndr import ( "fmt" "reflect" "strings" ) const ndrNameSpace = "ndr" type tags struct { Values []string Map map[string]string } // parse the struct field tags and extract the ndr related ones. // format of tag ndr:"value,key:value1,value2" func parseTags(st reflect.StructTag) tags { s := st.Get(ndrNameSpace) t := tags{ Values: []string{}, Map: make(map[string]string), } if s != "" { ndrTags := strings.Trim(s, `"`) for _, tag := range strings.Split(ndrTags, ",") { if strings.Contains(tag, ":") { m := strings.SplitN(tag, ":", 2) t.Map[m[0]] = m[1] } else { t.Values = append(t.Values, tag) } } } return t } func appendTag(t reflect.StructTag, s string) reflect.StructTag { ts := t.Get(ndrNameSpace) ts = fmt.Sprintf(`%s"%s,%s"`, ndrNameSpace, ts, s) return reflect.StructTag(ts) } func (t *tags) StructTag() reflect.StructTag { mv := t.Values for key, val := range t.Map { mv = append(mv, key+":"+val) } s := ndrNameSpace + ":" + `"` + strings.Join(mv, ",") + `"` return reflect.StructTag(s) } func (t *tags) delete(s string) { for i, x := range t.Values { if x == s { t.Values = append(t.Values[:i], t.Values[i+1:]...) } } delete(t.Map, s) } func (t *tags) HasValue(s string) bool { for _, v := range t.Values { if v == s { return true } } return false } rpc-2.0.3/v2/ndr/tags_test.go000066400000000000000000000023411375324356300157510ustar00rootroot00000000000000package ndr import ( "reflect" "testing" "github.com/stretchr/testify/assert" ) type Test struct { A int `ndr:"value"` B int `ndr:"key:value"` C int `ndr:"value1,key:value2"` D int `dr:"value"` } func TestParseTags(t *testing.T) { var test Test tag0 := reflect.TypeOf(test).Field(0).Tag tag1 := reflect.TypeOf(test).Field(1).Tag tag2 := reflect.TypeOf(test).Field(2).Tag tag3 := reflect.TypeOf(test).Field(3).Tag tg0 := parseTags(tag0) tg1 := parseTags(tag1) tg2 := parseTags(tag2) tg3 := parseTags(tag3) assert.Equal(t, []string{"value"}, tg0.Values, "Values not as expected for test %d", 0) assert.Equal(t, make(map[string]string), tg0.Map, "Map not as expected for test %d", 0) assert.Equal(t, []string{}, tg1.Values, "Values not as expected for test %d", 1) assert.Equal(t, map[string]string{"key": "value"}, tg1.Map, "Map not as expected for test %d", 1) assert.Equal(t, []string{"value1"}, tg2.Values, "Values not as expected for test %d", 2) assert.Equal(t, map[string]string{"key": "value2"}, tg2.Map, "Map not as expected for test %d", 2) assert.Equal(t, []string{}, tg3.Values, "Values not as expected for test %d", 3) assert.Equal(t, make(map[string]string), tg3.Map, "Map not as expected for test %d", 3) } rpc-2.0.3/v2/ndr/union.go000066400000000000000000000042771375324356300151160ustar00rootroot00000000000000package ndr import ( "errors" "fmt" "reflect" ) // Union interface must be implemented by structs that will be unmarshaled into from the NDR byte stream union representation. // The union's discriminating tag will be passed to the SwitchFunc method. // The discriminating tag field must have the struct tag: `ndr:"unionTag"` // If the union is encapsulated the discriminating tag field must have the struct tag: `ndr:"encapsulated"` // The possible value fields that can be selected from must have the struct tag: `ndr:"unionField"` type Union interface { SwitchFunc(t interface{}) string } // Union related constants such as struct tag values const ( unionSelectionFuncName = "SwitchFunc" TagEncapsulated = "encapsulated" TagUnionTag = "unionTag" TagUnionField = "unionField" ) func (dec *Decoder) isUnion(field reflect.Value, tag reflect.StructTag) (r reflect.Value) { ndrTag := parseTags(tag) if !ndrTag.HasValue(TagUnionTag) { return } r = field // For a non-encapsulated union, the discriminant is marshalled into the transmitted data stream twice: once as the // field or parameter, which is referenced by the switch_is construct, in the procedure argument list; and once as // the first part of the union representation. if !ndrTag.HasValue(TagEncapsulated) { dec.r.Discard(int(r.Type().Size())) } return } // unionSelectedField returns the field name of which of the union values to fill func unionSelectedField(union, discriminant reflect.Value) (string, error) { if !union.Type().Implements(reflect.TypeOf(new(Union)).Elem()) { return "", errors.New("struct does not implement union interface") } args := []reflect.Value{discriminant} // Call the SelectFunc of the union struct to find the name of the field to fill with the value selected. sf := union.MethodByName(unionSelectionFuncName) if !sf.IsValid() { return "", fmt.Errorf("could not find a selection function called %s in the unions struct representation", unionSelectionFuncName) } f := sf.Call(args) if f[0].Kind() != reflect.String || f[0].String() == "" { return "", fmt.Errorf("the union select function did not return a string for the name of the field to fill") } return f[0].String(), nil } rpc-2.0.3/v2/ndr/union_test.go000066400000000000000000000046411375324356300161500ustar00rootroot00000000000000package ndr import ( "bytes" "encoding/hex" "testing" "github.com/stretchr/testify/assert" ) const ( testUnionSelected1Enc = "0100000001" testUnionSelected2Enc = "020000000200" testUnionSelected1NonEnc = "010000000100000001" testUnionSelected2NonEnc = "02000000020000000200" ) type testUnionEncapsulated struct { Tag uint32 `ndr:"unionTag,encapsulated"` Value1 uint8 `ndr:"unionField"` Value2 uint16 `ndr:"unionField"` } type testUnionNonEncapsulated struct { Tag uint32 `ndr:"unionTag"` Value1 uint8 `ndr:"unionField"` Value2 uint16 `ndr:"unionField"` } func (u testUnionEncapsulated) SwitchFunc(tag interface{}) string { t := tag.(uint32) switch t { case 1: return "Value1" case 2: return "Value2" } return "" } func (u testUnionNonEncapsulated) SwitchFunc(tag interface{}) string { t := tag.(uint32) switch t { case 1: return "Value1" case 2: return "Value2" } return "" } func Test_readUnionEncapsulated(t *testing.T) { var tests = []struct { Hex string Tag uint32 V1 uint8 V2 uint16 }{ {testUnionSelected1Enc, uint32(1), uint8(1), uint16(0)}, {testUnionSelected2Enc, uint32(2), uint8(0), uint16(2)}, } for i, test := range tests { a := new(testUnionEncapsulated) hexStr := TestHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.Tag, a.Tag, "Tag value not as expected for test: %d", i+1) assert.Equal(t, test.V1, a.Value1, "Value1 not as expected for test: %d", i+1) assert.Equal(t, test.V2, a.Value2, "Value2 value not as expected for test: %d", i+1) } } func Test_readUnionNonEncapsulated(t *testing.T) { var tests = []struct { Hex string Tag uint32 V1 uint8 V2 uint16 }{ {testUnionSelected1NonEnc, uint32(1), uint8(1), uint16(0)}, {testUnionSelected2NonEnc, uint32(2), uint8(0), uint16(2)}, } for i, test := range tests { a := new(testUnionNonEncapsulated) hexStr := TestHeader + test.Hex b, _ := hex.DecodeString(hexStr) dec := NewDecoder(bytes.NewReader(b)) err := dec.Decode(a) if err != nil { t.Fatalf("test %d: %v", i+1, err) } assert.Equal(t, test.Tag, a.Tag, "Tag value not as expected for test: %d", i+1) assert.Equal(t, test.V1, a.Value1, "Value1 not as expected for test: %d", i+1) assert.Equal(t, test.V2, a.Value2, "Value2 value not as expected for test: %d", i+1) } }