Files
petrbalvin a9b8039ef7
Test / test (push) Successful in 2m4s
Release / gates (push) Successful in 2m5s
Release / build (amd64, freebsd) (push) Successful in 1m27s
Release / build (amd64, linux) (push) Successful in 1m22s
Release / build (amd64, netbsd) (push) Successful in 1m19s
Release / build (amd64, openbsd) (push) Successful in 1m20s
Release / build (arm64, darwin) (push) Successful in 1m21s
Release / build (arm64, freebsd) (push) Successful in 1m26s
Release / build (arm64, linux) (push) Successful in 1m25s
Release / build (arm64, netbsd) (push) Successful in 1m31s
Release / build (arm64, openbsd) (push) Successful in 1m27s
Release / build (loong64, linux) (push) Successful in 1m37s
Release / build (riscv64, linux) (push) Successful in 1m21s
Release / release (push) Successful in 40s
feat: full NFSv4.2 server and client in pure Go
Assisted-by: GLM 5.3 Flash
2026-09-21 18:51:17 +02:00

79 lines
2.5 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
package rpc
import (
"testing"
"sourcedock.dev/petrbalvin/nfs/internal/xdr"
)
// The RPCSEC_GSSv3 credential round trips with the version field in
// front, RFC 7861 section 5.1.
func TestGSSv3CredShape(t *testing.T) {
body := AppendGSSv3Cred(nil, GSSProcCreate, 7, SvcPrivacy, []byte("parent"))
cred, err := DecodeGSSv3Cred(body)
if err != nil {
t.Fatal(err)
}
if cred.Proc != GSSProcCreate || cred.Seq != 7 || cred.Service != SvcPrivacy ||
string(cred.Handle) != "parent" {
t.Fatalf("cred %+v", cred)
}
if _, err := DecodeGSSv3Cred(append([]byte{0, 0, 0, 1}, body[4:]...)); err != ErrGSSv3 {
t.Fatalf("version one body accepted: %v", err)
}
}
// The create arguments and reply round trip through their codecs,
// including the optional fields and the assertion union.
func TestGSSv3CreateShapes(t *testing.T) {
args := AppendCreateArgs(nil,
&MpAuth{InnerHandle: []byte("inner"), HeaderMic: []byte("mic")},
[]byte("binding"),
[]Assertion{
{Type: AssertionLabel, Label: Label{LfsId: 1, PiId: 2, Bytes: []byte("secret")}},
{Type: AssertionPrivs, Privs: Privs{Who: "petr", Grant: "admin", Bytes: []byte("x")}},
{Type: 9, Ext: []byte("ext")},
})
mp, bind, assertions, err := DecodeCreateArgs(args)
if err != nil {
t.Fatal(err)
}
if mp == nil || string(mp.InnerHandle) != "inner" || string(mp.HeaderMic) != "mic" {
t.Fatalf("mp auth %+v", mp)
}
if string(bind) != "binding" {
t.Fatalf("binding %q", bind)
}
if len(assertions) != 3 || assertions[0].Label.LfsId != 1 ||
assertions[1].Privs.Who != "petr" || assertions[2].Ext == nil {
t.Fatalf("assertions %+v", assertions)
}
// The reply mirrors the shape with the child handle.
res := AppendCreateRes(nil, []byte("child"), nil, nil,
[]Assertion{{Type: AssertionLabel, Label: Label{LfsId: 1, Bytes: []byte("secret")}}})
handle, _, _, granted, err := DecodeCreateRes(res)
if err != nil {
t.Fatal(err)
}
if string(handle) != "child" || len(granted) != 1 {
t.Fatalf("res handle %q granted %d", handle, len(granted))
}
// The list reply carries the type array.
list := AppendListRes(nil, []uint32{AssertionLabel, AssertionPrivs})
d := xdr.NewDecoder(list)
n, err := d.Uint32()
if err != nil || n != 2 {
t.Fatalf("list count %d: %v", n, err)
}
t1, _ := d.Uint32()
t2, _ := d.Uint32()
if t1 != AssertionLabel || t2 != AssertionPrivs {
t.Fatalf("list types %d %d", t1, t2)
}
}