Protocol improvements
- add a client generated command ID for tracing commands and responses - define protocol delimiter in protocol.CobsDelimiter - apply code simplifications suggested by golangci-lint - add Makefile - add compile time build information for signer binary - make sure that dependencies for msgpackgen survive go mod tidy - extract MsgPackHandler into its own file - add CRL number to fetch CRL response - remove port.Flush() to avoid removing written data before it reaches the client
This commit is contained in:
parent
8e443bd8b4
commit
f0d456dd13
11 changed files with 482 additions and 643 deletions
26
Makefile
Normal file
26
Makefile
Normal file
|
@ -0,0 +1,26 @@
|
|||
GOFILES := $(shell find -type f -name '*.go')
|
||||
BUILD_TIME := $(shell date --rfc-3339=seconds)
|
||||
COMMIT := $(shell git show-ref --head --abbrev=8 HEAD|cut -d ' ' -f 1)
|
||||
VERSION := $(shell git describe --always --dirty)
|
||||
|
||||
all: pkg/messages/resolver.msgpackgen.go lint test clientsim signer
|
||||
|
||||
pkg/messages/resolver.msgpackgen.go: pkg/messages/messages.go
|
||||
go generate $<
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
test:
|
||||
go test -race ./...
|
||||
|
||||
clientsim: $(GOFILES)
|
||||
go build -race ./cmd/clientsim
|
||||
|
||||
signer: $(GOFILES)
|
||||
go build -race -ldflags="-X 'main.date=$(BUILD_TIME)' -X 'main.commit=$(COMMIT)' -X 'main.version=$(VERSION)'" ./cmd/signer
|
||||
|
||||
clean:
|
||||
rm -f signer clientsim pkg/messages/resolver.msgpackgen.go
|
||||
|
||||
.PHONY: test lint all clean
|
|
@ -37,7 +37,7 @@ import (
|
|||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
)
|
||||
|
||||
var cobsConfig = cobs.Config{SpecialByte: 0x00, Delimiter: true, EndingSave: true}
|
||||
var cobsConfig = cobs.Config{SpecialByte: protocol.CobsDelimiter, Delimiter: true, EndingSave: true}
|
||||
|
||||
type protocolState int8
|
||||
|
||||
|
@ -82,17 +82,16 @@ func (g *TestCommandGenerator) CmdAnnouncement() ([]byte, error) {
|
|||
g.lock.Lock()
|
||||
defer g.lock.Unlock()
|
||||
|
||||
select {
|
||||
case g.currentCommand = <-g.commands:
|
||||
announceData, err := msgpack.Marshal(g.currentCommand.Announce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal command annoucement: %w", err)
|
||||
}
|
||||
g.currentCommand = <-g.commands
|
||||
|
||||
g.logger.WithField("announcement", &g.currentCommand.Announce).Info("write command announcement")
|
||||
|
||||
return announceData, nil
|
||||
announceData, err := msgpack.Marshal(g.currentCommand.Announce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal command annoucement: %w", err)
|
||||
}
|
||||
|
||||
g.logger.WithField("announcement", &g.currentCommand.Announce).Info("write command announcement")
|
||||
|
||||
return announceData, nil
|
||||
}
|
||||
|
||||
func (g *TestCommandGenerator) CmdData() ([]byte, error) {
|
||||
|
@ -138,13 +137,15 @@ func (g *TestCommandGenerator) HandleResponse(frame []byte) error {
|
|||
return fmt.Errorf("unmarshal failed: %w", err)
|
||||
}
|
||||
|
||||
g.currentResponse.Response = response
|
||||
g.currentResponse.Response = &response
|
||||
case messages.RespFetchCRL:
|
||||
var response messages.FetchCRLResponse
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &response); err != nil {
|
||||
return fmt.Errorf("unmarshal failed: %w", err)
|
||||
}
|
||||
|
||||
g.currentResponse.Response = &response
|
||||
}
|
||||
|
||||
g.logger.WithField(
|
||||
|
@ -159,14 +160,27 @@ func (g *TestCommandGenerator) HandleResponse(frame []byte) error {
|
|||
}
|
||||
|
||||
func (g *TestCommandGenerator) GenerateCommands(ctx context.Context) error {
|
||||
const healthInterval = 10 * time.Second
|
||||
const (
|
||||
healthInterval = 5 * time.Second
|
||||
startPause = 3 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
announce *messages.CommandAnnounce
|
||||
err error
|
||||
)
|
||||
|
||||
g.logger.Info("start generating commands")
|
||||
|
||||
time.Sleep(healthInterval)
|
||||
time.Sleep(startPause)
|
||||
|
||||
announce, err = messages.BuildCommandAnnounce(messages.CmdFetchCRL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build command announce failed: %w", err)
|
||||
}
|
||||
|
||||
g.commands <- &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdFetchCRL),
|
||||
Announce: announce,
|
||||
Command: &messages.FetchCRLCommand{IssuerID: "sub-ecc_person_2022"},
|
||||
}
|
||||
|
||||
|
@ -181,8 +195,13 @@ func (g *TestCommandGenerator) GenerateCommands(ctx context.Context) error {
|
|||
|
||||
return nil
|
||||
case <-timer.C:
|
||||
announce, err = messages.BuildCommandAnnounce(messages.CmdHealth)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build command announce failed: %w", err)
|
||||
}
|
||||
|
||||
g.commands <- &protocol.Command{
|
||||
Announce: messages.BuildCommandAnnounce(messages.CmdHealth),
|
||||
Announce: announce,
|
||||
Command: &messages.HealthCommand{},
|
||||
}
|
||||
}
|
||||
|
@ -203,7 +222,9 @@ func (c *clientSimulator) readFrames() error {
|
|||
const readInterval = 50 * time.Millisecond
|
||||
|
||||
var frame []byte
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
delimiter := []byte{cobsConfig.SpecialByte}
|
||||
|
||||
for {
|
||||
|
@ -231,6 +252,8 @@ func (c *clientSimulator) readFrames() error {
|
|||
rest := buffer.Bytes()
|
||||
|
||||
if !bytes.Contains(rest, delimiter) {
|
||||
c.logger.Tracef("read data does not contain the delimiter %x", delimiter)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -254,6 +277,8 @@ func (c *clientSimulator) readFrames() error {
|
|||
c.logger.Tracef("frame decoded to length %d", len(decoded))
|
||||
|
||||
c.framesIn <- decoded
|
||||
|
||||
c.logger.Tracef("%d bytes remaining", len(rest))
|
||||
}
|
||||
|
||||
buffer.Truncate(0)
|
||||
|
@ -266,9 +291,6 @@ func (c *clientSimulator) readFrames() error {
|
|||
func (c *clientSimulator) writeFrame(frame []byte) error {
|
||||
encoded := cobs.Encode(frame, cobsConfig)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if _, err := io.Copy(os.Stdout, bytes.NewBuffer(encoded)); err != nil {
|
||||
return fmt.Errorf("could not write data: %w", err)
|
||||
}
|
||||
|
@ -281,11 +303,15 @@ func (c *clientSimulator) readFromStdin() ([]byte, error) {
|
|||
|
||||
buf := make([]byte, bufferSize)
|
||||
|
||||
c.logger.Trace("waiting for input")
|
||||
|
||||
count, err := os.Stdin.Read(buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading input failed: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Tracef("read %d bytes from stdin", count)
|
||||
|
||||
return buf[:count], nil
|
||||
}
|
||||
|
||||
|
@ -308,25 +334,6 @@ func (c *clientSimulator) writeCmdAnnouncement() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *clientSimulator) writeCommandAnnouncement() error {
|
||||
frame, err := c.commandGenerator.CmdAnnouncement()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get command announcement: %w", err)
|
||||
}
|
||||
|
||||
c.logger.Trace("writing command announcement")
|
||||
|
||||
if err := c.writeFrame(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *clientSimulator) writeCommand() error {
|
||||
frame, err := c.commandGenerator.CmdData()
|
||||
if err != nil {
|
||||
|
@ -349,19 +356,18 @@ func (c *clientSimulator) writeCommand() error {
|
|||
func (c *clientSimulator) handleResponseAnnounce() error {
|
||||
c.logger.Trace("waiting for response announce")
|
||||
|
||||
select {
|
||||
case frame := <-c.framesIn:
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
frame := <-c.framesIn
|
||||
|
||||
if err := c.commandGenerator.HandleResponseAnnounce(frame); err != nil {
|
||||
return fmt.Errorf("response announce handling failed: %w", err)
|
||||
}
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.commandGenerator.HandleResponseAnnounce(frame); err != nil {
|
||||
return fmt.Errorf("response announce handling failed: %w", err)
|
||||
}
|
||||
|
||||
if err := c.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -370,19 +376,18 @@ func (c *clientSimulator) handleResponseAnnounce() error {
|
|||
func (c *clientSimulator) handleResponseData() error {
|
||||
c.logger.Trace("waiting for response data")
|
||||
|
||||
select {
|
||||
case frame := <-c.framesIn:
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
frame := <-c.framesIn
|
||||
|
||||
if err := c.commandGenerator.HandleResponse(frame); err != nil {
|
||||
return fmt.Errorf("response handler failed: %w", err)
|
||||
}
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.commandGenerator.HandleResponse(frame); err != nil {
|
||||
return fmt.Errorf("response handler failed: %w", err)
|
||||
}
|
||||
|
||||
if err := c.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -412,6 +417,7 @@ func (c *clientSimulator) Run(ctx context.Context) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("error from handler loop: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
default:
|
||||
if err := c.handleProtocolState(); err != nil {
|
||||
|
@ -424,6 +430,9 @@ func (c *clientSimulator) Run(ctx context.Context) error {
|
|||
func (c *clientSimulator) handleProtocolState() error {
|
||||
c.logger.Tracef("handling protocol state %s", c.protocolState)
|
||||
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
switch c.protocolState {
|
||||
case cmdAnnounce:
|
||||
if err := c.writeCmdAnnouncement(); err != nil {
|
||||
|
@ -462,17 +471,17 @@ func (c *clientSimulator) nextState() error {
|
|||
func main() {
|
||||
logger := logrus.New()
|
||||
logger.SetOutput(os.Stderr)
|
||||
logger.SetLevel(logrus.TraceLevel)
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
|
||||
messages.RegisterGeneratedResolver()
|
||||
|
||||
sim := &clientSimulator{
|
||||
commandGenerator: &TestCommandGenerator{
|
||||
logger: logger,
|
||||
commands: make(chan *protocol.Command, 0),
|
||||
commands: make(chan *protocol.Command),
|
||||
},
|
||||
logger: logger,
|
||||
framesIn: make(chan []byte, 0),
|
||||
framesIn: make(chan []byte),
|
||||
}
|
||||
|
||||
err := sim.Run(context.Background())
|
||||
|
|
|
@ -35,9 +35,9 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
commit string
|
||||
date string
|
||||
version string
|
||||
commit = "dev"
|
||||
date = "unknown"
|
||||
version = "unknown"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
3
go.mod
3
go.mod
|
@ -4,6 +4,8 @@ go 1.17
|
|||
|
||||
require (
|
||||
github.com/ThalesIgnite/crypto11 v1.2.5
|
||||
github.com/dave/jennifer v1.4.1
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/justincpresley/go-cobs v1.2.0
|
||||
github.com/shamaton/msgpackgen v0.3.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
|
@ -14,7 +16,6 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/dave/jennifer v1.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -5,6 +5,8 @@ github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpT
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/justincpresley/go-cobs v1.2.0 h1:dyWszWzXObEv8sxVMJTAIo9XT7HEM10vkAOZq2eVEsQ=
|
||||
github.com/justincpresley/go-cobs v1.2.0/go.mod h1:L0d+EbGirv6IzsXNzwULduI2/z3ijkkAmsAuPMpLfqA=
|
||||
github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
|
|
|
@ -24,8 +24,14 @@ import (
|
|||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
// required for msgpackgen
|
||||
_ "github.com/dave/jennifer"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type CommandCode int8
|
||||
|
@ -72,33 +78,48 @@ func (c ResponseCode) String() string {
|
|||
|
||||
type CommandAnnounce struct {
|
||||
Code CommandCode `msgpack:"code"`
|
||||
ID string `msgpack:"id"`
|
||||
Created time.Time `msgpack:"created"`
|
||||
}
|
||||
|
||||
func (r *CommandAnnounce) String() string {
|
||||
return fmt.Sprintf("CommandAnnounce[code=%s, created=%s]", r.Code, r.Created)
|
||||
return fmt.Sprintf("code=%s, id=%s, created=%s", r.Code, r.ID, r.Created.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
type ResponseAnnounce struct {
|
||||
Code ResponseCode `msgpack:"code"`
|
||||
Created time.Time `msgpack:"created"`
|
||||
ID string `msgpack:"id"`
|
||||
}
|
||||
|
||||
func (r *ResponseAnnounce) String() string {
|
||||
return fmt.Sprintf("ResponseAnnounce[code=%s, created=%s]", r.Code, r.Created)
|
||||
return fmt.Sprintf("code=%s, id=%s, created=%s", r.Code, r.ID, r.Created.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
type FetchCRLCommand struct {
|
||||
IssuerID string `msgpack:"issuer_id"`
|
||||
LastKnownHash struct {
|
||||
Algorithm string `msgpack:"algorithm"`
|
||||
Value string `msgpack:"value"`
|
||||
} `msgpack:"last_known_hash"` // optional last known hash in format
|
||||
IssuerID string `msgpack:"issuer_id"`
|
||||
LastKnownID *big.Int `msgpack:"last_known_id"`
|
||||
}
|
||||
|
||||
func (f *FetchCRLCommand) String() string {
|
||||
builder := &strings.Builder{}
|
||||
|
||||
_, _ = fmt.Fprintf(builder, "issuerId='%s'", f.IssuerID)
|
||||
|
||||
if f.LastKnownID != nil {
|
||||
_, _ = fmt.Fprintf(builder, ", lastKnownId=%s", f.LastKnownID.Text(16))
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
type HealthCommand struct {
|
||||
}
|
||||
|
||||
func (h *HealthCommand) String() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type HealthInfo struct {
|
||||
Source string
|
||||
Healthy bool
|
||||
|
@ -138,9 +159,10 @@ func (h *HealthResponse) String() string {
|
|||
}
|
||||
|
||||
type FetchCRLResponse struct {
|
||||
IssuerID string `msgpack:"issuer_id"`
|
||||
IsDelta bool `msgpack:"is_delta"`
|
||||
CRLData []byte `msgpack:"crl_data"`
|
||||
IssuerID string `msgpack:"issuer_id"`
|
||||
IsDelta bool `msgpack:"is_delta"`
|
||||
CRLData []byte `msgpack:"crl_data"`
|
||||
CRLNumber *big.Int
|
||||
}
|
||||
|
||||
func (r *FetchCRLResponse) String() string {
|
||||
|
@ -151,12 +173,18 @@ func (r *FetchCRLResponse) String() string {
|
|||
if r.IsDelta {
|
||||
_, _ = fmt.Fprint(builder, ", delta CRL data not shown")
|
||||
} else {
|
||||
revlist, err := x509.ParseRevocationList(r.CRLData)
|
||||
revocationList, err := x509.ParseRevocationList(r.CRLData)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(builder, ", could not parse CRL: %s", err.Error())
|
||||
} else {
|
||||
_, _ = fmt.Fprintf(builder, ", CRL info: issuer=%s, number=%s, next update=%s, revoked certificates=%d",
|
||||
revlist.Issuer, revlist.Number, revlist.NextUpdate, len(revlist.RevokedCertificates))
|
||||
_, _ = fmt.Fprintf(
|
||||
builder,
|
||||
", CRL info: issuer=%s, number=%s, next update=%s, revoked certificates=%d",
|
||||
revocationList.Issuer,
|
||||
revocationList.Number,
|
||||
revocationList.NextUpdate,
|
||||
len(revocationList.RevokedCertificates),
|
||||
)
|
||||
_, _ = builder.WriteString(", CRL data:\n")
|
||||
_ = pem.Encode(builder, &pem.Block{
|
||||
Type: "CERTIFICATE REVOCATION LIST",
|
||||
|
@ -172,10 +200,19 @@ type ErrorResponse struct {
|
|||
Message string `msgpack:"message"`
|
||||
}
|
||||
|
||||
func BuildCommandAnnounce(code CommandCode) *CommandAnnounce {
|
||||
return &CommandAnnounce{Code: code, Created: time.Now().UTC()}
|
||||
func (e *ErrorResponse) String() string {
|
||||
return fmt.Sprintf("message=%s", e.Message)
|
||||
}
|
||||
|
||||
func BuildResponseAnnounce(code ResponseCode) *ResponseAnnounce {
|
||||
return &ResponseAnnounce{Code: code, Created: time.Now().UTC()}
|
||||
func BuildCommandAnnounce(code CommandCode) (*CommandAnnounce, error) {
|
||||
commandID, err := uuid.NewUUID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not build command id: %w", err)
|
||||
}
|
||||
|
||||
return &CommandAnnounce{Code: code, ID: commandID.String(), Created: time.Now().UTC()}, nil
|
||||
}
|
||||
|
||||
func BuildResponseAnnounce(code ResponseCode, commandID string) *ResponseAnnounce {
|
||||
return &ResponseAnnounce{Code: code, ID: commandID, Created: time.Now().UTC()}
|
||||
}
|
||||
|
|
|
@ -116,36 +116,6 @@ func ___encodeAsArray(i interface{}) ([]byte, error) {
|
|||
return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case FetchCRLResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcArraySizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder.MakeBytes(size)
|
||||
b, offset, err := ___encodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if size != offset {
|
||||
return nil, fmt.Errorf("%s size / offset different %d : %d", "FetchCRLResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case *FetchCRLResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcArraySizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder.MakeBytes(size)
|
||||
b, offset, err := ___encodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if size != offset {
|
||||
return nil, fmt.Errorf("%s size / offset different %d : %d", "FetchCRLResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case ErrorResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcArraySizeErrorResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
|
||||
|
@ -273,36 +243,6 @@ func ___encodeAsMap(i interface{}) ([]byte, error) {
|
|||
return nil, fmt.Errorf("%s size / offset different %d : %d", "HealthResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case FetchCRLResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcMapSizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder.MakeBytes(size)
|
||||
b, offset, err := ___encodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if size != offset {
|
||||
return nil, fmt.Errorf("%s size / offset different %d : %d", "FetchCRLResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case *FetchCRLResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcMapSizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
encoder.MakeBytes(size)
|
||||
b, offset, err := ___encodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, encoder, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if size != offset {
|
||||
return nil, fmt.Errorf("%s size / offset different %d : %d", "FetchCRLResponse", size, offset)
|
||||
}
|
||||
return b, err
|
||||
case ErrorResponse:
|
||||
encoder := enc.NewEncoder()
|
||||
size, err := ___calcMapSizeErrorResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, encoder)
|
||||
|
@ -391,20 +331,6 @@ func ___decodeAsArray(data []byte, i interface{}) (bool, error) {
|
|||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case *FetchCRLResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
|
||||
if err == nil && offset != decoder.Len() {
|
||||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case **FetchCRLResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, decoder, 0)
|
||||
if err == nil && offset != decoder.Len() {
|
||||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case *ErrorResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeArrayErrorResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
|
||||
|
@ -468,20 +394,6 @@ func ___decodeAsMap(data []byte, i interface{}) (bool, error) {
|
|||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case *FetchCRLResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
|
||||
if err == nil && offset != decoder.Len() {
|
||||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case **FetchCRLResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(*v, decoder, 0)
|
||||
if err == nil && offset != decoder.Len() {
|
||||
return true, fmt.Errorf("read length is different [%d] [%d] ", offset, decoder.Len())
|
||||
}
|
||||
return true, err
|
||||
case *ErrorResponse:
|
||||
decoder := dec.NewDecoder(data)
|
||||
offset, err := ___decodeMapErrorResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v, decoder, 0)
|
||||
|
@ -1057,217 +969,6 @@ func ___decodeMapHealthResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f
|
|||
return offset, err
|
||||
}
|
||||
|
||||
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___calcArraySizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLResponse, encoder *enc.Encoder) (int, error) {
|
||||
size := 0
|
||||
size += encoder.CalcStructHeaderFix(3)
|
||||
size += encoder.CalcString(v.IssuerID)
|
||||
size += encoder.CalcBool(v.IsDelta)
|
||||
if v.CRLData != nil {
|
||||
s, err := encoder.CalcSliceLength(len(v.CRLData), true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size += s
|
||||
for _, vv := range v.CRLData {
|
||||
size += encoder.CalcByte(vv)
|
||||
}
|
||||
} else {
|
||||
size += encoder.CalcNil()
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___calcMapSizeFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLResponse, encoder *enc.Encoder) (int, error) {
|
||||
size := 0
|
||||
size += encoder.CalcStructHeaderFix(3)
|
||||
size += encoder.CalcStringFix(9)
|
||||
size += encoder.CalcString(v.IssuerID)
|
||||
size += encoder.CalcStringFix(8)
|
||||
size += encoder.CalcBool(v.IsDelta)
|
||||
size += encoder.CalcStringFix(8)
|
||||
if v.CRLData != nil {
|
||||
s, err := encoder.CalcSliceLength(len(v.CRLData), true)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
size += s
|
||||
for _, vv := range v.CRLData {
|
||||
size += encoder.CalcByte(vv)
|
||||
}
|
||||
} else {
|
||||
size += encoder.CalcNil()
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// encode from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___encodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLResponse, encoder *enc.Encoder, offset int) ([]byte, int, error) {
|
||||
var err error
|
||||
offset = encoder.WriteStructHeaderFixAsArray(3, offset)
|
||||
offset = encoder.WriteString(v.IssuerID, offset)
|
||||
offset = encoder.WriteBool(v.IsDelta, offset)
|
||||
if v.CRLData != nil {
|
||||
offset = encoder.WriteSliceLength(len(v.CRLData), offset, true)
|
||||
for _, vv := range v.CRLData {
|
||||
offset = encoder.WriteByte(vv, offset)
|
||||
}
|
||||
} else {
|
||||
offset = encoder.WriteNil(offset)
|
||||
}
|
||||
return encoder.EncodedBytes(), offset, err
|
||||
}
|
||||
|
||||
// encode from git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___encodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v FetchCRLResponse, encoder *enc.Encoder, offset int) ([]byte, int, error) {
|
||||
var err error
|
||||
offset = encoder.WriteStructHeaderFixAsMap(3, offset)
|
||||
offset = encoder.WriteStringFix("issuer_id", 9, offset)
|
||||
offset = encoder.WriteString(v.IssuerID, offset)
|
||||
offset = encoder.WriteStringFix("is_delta", 8, offset)
|
||||
offset = encoder.WriteBool(v.IsDelta, offset)
|
||||
offset = encoder.WriteStringFix("crl_data", 8, offset)
|
||||
if v.CRLData != nil {
|
||||
offset = encoder.WriteSliceLength(len(v.CRLData), offset, true)
|
||||
for _, vv := range v.CRLData {
|
||||
offset = encoder.WriteByte(vv, offset)
|
||||
}
|
||||
} else {
|
||||
offset = encoder.WriteNil(offset)
|
||||
}
|
||||
return encoder.EncodedBytes(), offset, err
|
||||
}
|
||||
|
||||
// decode to git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___decodeArrayFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v *FetchCRLResponse, decoder *dec.Decoder, offset int) (int, error) {
|
||||
offset, err := decoder.CheckStructHeader(3, offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
{
|
||||
var vv string
|
||||
vv, offset, err = decoder.AsString(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v.IssuerID = vv
|
||||
}
|
||||
{
|
||||
var vv bool
|
||||
vv, offset, err = decoder.AsBool(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v.IsDelta = vv
|
||||
}
|
||||
if !decoder.IsCodeNil(offset) {
|
||||
var vv []byte
|
||||
var vvl int
|
||||
vvl, offset, err = decoder.SliceLength(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vv = make([]byte, vvl)
|
||||
for vvi := range vv {
|
||||
var vvv byte
|
||||
vvv, offset, err = decoder.AsByte(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vv[vvi] = vvv
|
||||
}
|
||||
v.CRLData = vv
|
||||
} else {
|
||||
offset++
|
||||
}
|
||||
return offset, err
|
||||
}
|
||||
|
||||
// decode to git.cacert.org/cacert-gosigner/pkg/messages.FetchCRLResponse
|
||||
func ___decodeMapFetchCRLResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v *FetchCRLResponse, decoder *dec.Decoder, offset int) (int, error) {
|
||||
keys := [][]byte{
|
||||
{uint8(0x69), uint8(0x73), uint8(0x73), uint8(0x75), uint8(0x65), uint8(0x72), uint8(0x5f), uint8(0x69), uint8(0x64)}, // issuer_id
|
||||
{uint8(0x69), uint8(0x73), uint8(0x5f), uint8(0x64), uint8(0x65), uint8(0x6c), uint8(0x74), uint8(0x61)}, // is_delta
|
||||
{uint8(0x63), uint8(0x72), uint8(0x6c), uint8(0x5f), uint8(0x64), uint8(0x61), uint8(0x74), uint8(0x61)}, // crl_data
|
||||
}
|
||||
offset, err := decoder.CheckStructHeader(3, offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
count := 0
|
||||
for count < 3 {
|
||||
var dataKey []byte
|
||||
dataKey, offset, err = decoder.AsStringBytes(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fieldIndex := -1
|
||||
for i, key := range keys {
|
||||
if len(dataKey) != len(key) {
|
||||
continue
|
||||
}
|
||||
fieldIndex = i
|
||||
for dataKeyIndex := range dataKey {
|
||||
if dataKey[dataKeyIndex] != key[dataKeyIndex] {
|
||||
fieldIndex = -1
|
||||
break
|
||||
}
|
||||
}
|
||||
if fieldIndex >= 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
switch fieldIndex {
|
||||
case 0:
|
||||
{
|
||||
var vv string
|
||||
vv, offset, err = decoder.AsString(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v.IssuerID = vv
|
||||
}
|
||||
count++
|
||||
case 1:
|
||||
{
|
||||
var vv bool
|
||||
vv, offset, err = decoder.AsBool(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v.IsDelta = vv
|
||||
}
|
||||
count++
|
||||
case 2:
|
||||
if !decoder.IsCodeNil(offset) {
|
||||
var vv []byte
|
||||
var vvl int
|
||||
vvl, offset, err = decoder.SliceLength(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vv = make([]byte, vvl)
|
||||
for vvi := range vv {
|
||||
var vvv byte
|
||||
vvv, offset, err = decoder.AsByte(offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
vv[vvi] = vvv
|
||||
}
|
||||
v.CRLData = vv
|
||||
} else {
|
||||
offset++
|
||||
}
|
||||
count++
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown key[%s] found", string(dataKey))
|
||||
}
|
||||
}
|
||||
return offset, err
|
||||
}
|
||||
|
||||
// calculate size from git.cacert.org/cacert-gosigner/pkg/messages.ErrorResponse
|
||||
func ___calcArraySizeErrorResponse_e587a81c7cb163b35488bdef0f58c292f99f4bd65a81377f81e5b18c3d86855d(v ErrorResponse, encoder *enc.Encoder) (int, error) {
|
||||
size := 0
|
||||
|
|
282
pkg/protocol/msgpack.go
Normal file
282
pkg/protocol/msgpack.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
Copyright 2022 CAcert Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/shamaton/msgpackgen/msgpack"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/health"
|
||||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/revoking"
|
||||
)
|
||||
|
||||
// MsgPackHandler is a Handler implementation for the msgpack serialization format.
|
||||
type MsgPackHandler struct {
|
||||
logger *logrus.Logger
|
||||
healthHandler *health.Handler
|
||||
fetchCRLHandler *revoking.FetchCRLHandler
|
||||
currentCommand *Command
|
||||
currentResponse *Response
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) HandleCommandAnnounce(frame []byte) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
var ann messages.CommandAnnounce
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &ann); err != nil {
|
||||
return fmt.Errorf("could not unmarshal command announcement: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("announcement", &ann).Info("received command announcement")
|
||||
|
||||
m.currentCommand = &Command{Announce: &ann}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) HandleCommand(frame []byte) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
err := m.parseCommand(frame)
|
||||
if err != nil {
|
||||
m.currentResponse = m.buildErrorResponse(err.Error())
|
||||
|
||||
m.logCommandResponse()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err = m.handleCommand()
|
||||
if err != nil {
|
||||
m.logger.WithError(err).Error("command handling failed")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
m.logCommandResponse()
|
||||
|
||||
m.currentCommand = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) logCommandResponse() {
|
||||
m.logger.WithField("command", m.currentCommand.Announce).Info("handled command")
|
||||
m.logger.WithField(
|
||||
"command",
|
||||
m.currentCommand,
|
||||
).WithField(
|
||||
"response",
|
||||
m.currentResponse,
|
||||
).Debug("command and response")
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) ResponseAnnounce() ([]byte, error) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
announceData, err := msgpack.Marshal(m.currentResponse.Announce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal response announcement: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("announcement", m.currentResponse.Announce).Debug("write response announcement")
|
||||
|
||||
return announceData, nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) ResponseData() ([]byte, error) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
responseData, err := msgpack.Marshal(m.currentResponse.Response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal response: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("response", m.currentResponse.Response).Debug("write response")
|
||||
|
||||
return responseData, nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) parseHealthCommand(frame []byte) error {
|
||||
var command messages.HealthCommand
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &command); err != nil {
|
||||
m.logger.WithError(err).Error("unmarshal failed")
|
||||
|
||||
return errors.New("could not unmarshal health command")
|
||||
}
|
||||
|
||||
m.currentCommand.Command = &command
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) parseFetchCRLCommand(frame []byte) error {
|
||||
var command messages.FetchCRLCommand
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &command); err != nil {
|
||||
m.logger.WithError(err).Error("unmarshal failed")
|
||||
|
||||
return errors.New("could not unmarshal fetch crl command")
|
||||
}
|
||||
|
||||
m.currentCommand.Command = &command
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) currentID() string {
|
||||
return m.currentCommand.Announce.ID
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) handleCommand() error {
|
||||
var (
|
||||
err error
|
||||
responseData interface{}
|
||||
responseCode messages.ResponseCode
|
||||
)
|
||||
|
||||
switch m.currentCommand.Command.(type) {
|
||||
case *messages.HealthCommand:
|
||||
response, err := m.handleHealthCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseCode, responseData = messages.RespHealth, response
|
||||
case *messages.FetchCRLCommand:
|
||||
response, err := m.handleFetchCRLCommand()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
responseCode, responseData = messages.RespFetchCRL, response
|
||||
default:
|
||||
return fmt.Errorf("unhandled command %s", m.currentCommand.Announce)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error from command handler: %w", err)
|
||||
}
|
||||
|
||||
m.currentResponse = &Response{
|
||||
Announce: messages.BuildResponseAnnounce(responseCode, m.currentID()),
|
||||
Response: responseData,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) buildErrorResponse(errMsg string) *Response {
|
||||
return &Response{
|
||||
Announce: messages.BuildResponseAnnounce(messages.RespError, m.currentID()),
|
||||
Response: &messages.ErrorResponse{Message: errMsg},
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) parseCommand(frame []byte) error {
|
||||
switch m.currentCommand.Announce.Code {
|
||||
case messages.CmdHealth:
|
||||
return m.parseHealthCommand(frame)
|
||||
case messages.CmdFetchCRL:
|
||||
return m.parseFetchCRLCommand(frame)
|
||||
default:
|
||||
return fmt.Errorf("unhandled command code %s", m.currentCommand.Announce.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) handleHealthCommand() (*messages.HealthResponse, error) {
|
||||
res, err := m.healthHandler.CheckHealth()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not check health: %w", err)
|
||||
}
|
||||
|
||||
response := &messages.HealthResponse{
|
||||
Version: res.Version,
|
||||
Healthy: res.Healthy,
|
||||
}
|
||||
|
||||
for _, info := range res.Info {
|
||||
response.Info = append(response.Info, &messages.HealthInfo{
|
||||
Source: info.Source,
|
||||
Healthy: info.Healthy,
|
||||
MoreInfo: info.MoreInfo,
|
||||
})
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) handleFetchCRLCommand() (*messages.FetchCRLResponse, error) {
|
||||
fetchCRLPayload, ok := m.currentCommand.Command.(*messages.FetchCRLCommand)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not use payload as FetchCRLPayload")
|
||||
}
|
||||
|
||||
res, err := m.fetchCRLHandler.FetchCRL(fetchCRLPayload.IssuerID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch CRL: %w", err)
|
||||
}
|
||||
|
||||
response := &messages.FetchCRLResponse{
|
||||
IsDelta: false,
|
||||
CRLNumber: res.Number,
|
||||
CRLData: res.CRLData,
|
||||
}
|
||||
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func New(logger *logrus.Logger, handlers ...RegisterHandler) (Handler, error) {
|
||||
messages.RegisterGeneratedResolver()
|
||||
|
||||
h := &MsgPackHandler{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
for _, reg := range handlers {
|
||||
reg(h)
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type RegisterHandler func(handler *MsgPackHandler)
|
||||
|
||||
func RegisterHealthHandler(healthHandler *health.Handler) func(*MsgPackHandler) {
|
||||
return func(h *MsgPackHandler) {
|
||||
h.healthHandler = healthHandler
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterFetchCRLHandler(fetchCRLHandler *revoking.FetchCRLHandler) func(handler *MsgPackHandler) {
|
||||
return func(h *MsgPackHandler) {
|
||||
h.fetchCRLHandler = fetchCRLHandler
|
||||
}
|
||||
}
|
|
@ -19,250 +19,39 @@ limitations under the License.
|
|||
package protocol
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/shamaton/msgpackgen/msgpack"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/x509/revoking"
|
||||
|
||||
"git.cacert.org/cacert-gosigner/pkg/health"
|
||||
"git.cacert.org/cacert-gosigner/pkg/messages"
|
||||
)
|
||||
|
||||
const CobsDelimiter = 0x00
|
||||
|
||||
type Command struct {
|
||||
Announce *messages.CommandAnnounce
|
||||
Command interface{}
|
||||
}
|
||||
|
||||
func (c *Command) String() string {
|
||||
return fmt.Sprintf("Cmd[announce={%s}, data={%s}]", c.Announce, c.Command)
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Announce *messages.ResponseAnnounce
|
||||
Response interface{}
|
||||
}
|
||||
|
||||
func (r *Response) String() string {
|
||||
return fmt.Sprintf("Response[Code=%s] created=%s data=%s", r.Announce.Code, r.Announce.Created, r.Response)
|
||||
return fmt.Sprintf("Rsp[announce={%s}, data={%s}]", r.Announce, r.Response)
|
||||
}
|
||||
|
||||
// Handler is responsible for parsing incoming frames and calling commands
|
||||
type Handler interface {
|
||||
// HandleCommandAnnounce handles the initial announcement of a command.
|
||||
HandleCommandAnnounce([]byte) error
|
||||
// HandleCommand handles the command data.
|
||||
HandleCommand([]byte) error
|
||||
// ResponseAnnounce generates the announcement for a response.
|
||||
ResponseAnnounce() ([]byte, error)
|
||||
// ResponseData generates the response data.
|
||||
ResponseData() ([]byte, error)
|
||||
}
|
||||
|
||||
type MsgPackHandler struct {
|
||||
logger *logrus.Logger
|
||||
healthHandler *health.Handler
|
||||
fetchCRLHandler *revoking.FetchCRLHandler
|
||||
currentCommand *Command
|
||||
currentResponse *Response
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) HandleCommandAnnounce(frame []byte) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
var ann messages.CommandAnnounce
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &ann); err != nil {
|
||||
return fmt.Errorf("could not unmarshal command announcement: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("announcement", &ann).Info("received command announcement")
|
||||
|
||||
m.currentCommand = &Command{Announce: &ann}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) HandleCommand(frame []byte) error {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
var clientError error
|
||||
|
||||
switch m.currentCommand.Announce.Code {
|
||||
case messages.CmdHealth:
|
||||
// health has no payload, ignore the frame
|
||||
response, err := m.handleCommand()
|
||||
if err != nil {
|
||||
m.logger.WithError(err).Error("health handling failed")
|
||||
|
||||
clientError = errors.New("could not handle request")
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
m.currentResponse = response
|
||||
case messages.CmdFetchCRL:
|
||||
var command messages.FetchCRLCommand
|
||||
|
||||
if err := msgpack.Unmarshal(frame, &command); err != nil {
|
||||
m.logger.WithError(err).Error("unmarshal failed")
|
||||
|
||||
clientError = errors.New("could not unmarshal fetch crl command")
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
m.currentCommand.Command = command
|
||||
|
||||
response, err := m.handleCommand()
|
||||
if err != nil {
|
||||
m.logger.WithError(err).Error("fetch CRL handling failed")
|
||||
|
||||
clientError = errors.New("could not handle request")
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
m.currentResponse = response
|
||||
}
|
||||
|
||||
if clientError != nil {
|
||||
m.currentResponse = buildErrorResponse(clientError.Error())
|
||||
}
|
||||
|
||||
m.logger.WithField(
|
||||
"command",
|
||||
m.currentCommand,
|
||||
).WithField(
|
||||
"response",
|
||||
m.currentResponse,
|
||||
).Info("handled command")
|
||||
|
||||
m.currentCommand = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) ResponseAnnounce() ([]byte, error) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
announceData, err := msgpack.Marshal(m.currentResponse.Announce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal response announcement: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("announcement", &m.currentResponse.Announce).Info("write response announcement")
|
||||
|
||||
return announceData, nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) ResponseData() ([]byte, error) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
responseData, err := msgpack.Marshal(m.currentResponse.Response)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not marshal response: %w", err)
|
||||
}
|
||||
|
||||
m.logger.WithField("response", &m.currentResponse.Response).Info("write response")
|
||||
|
||||
return responseData, nil
|
||||
}
|
||||
|
||||
func (m *MsgPackHandler) handleCommand() (*Response, error) {
|
||||
var (
|
||||
err error
|
||||
responseData interface{}
|
||||
responseCode messages.ResponseCode
|
||||
)
|
||||
|
||||
switch m.currentCommand.Announce.Code {
|
||||
case messages.CmdHealth:
|
||||
var res *health.Result
|
||||
|
||||
res, err = m.healthHandler.CheckHealth()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &messages.HealthResponse{
|
||||
Version: res.Version,
|
||||
Healthy: res.Healthy,
|
||||
}
|
||||
|
||||
for _, info := range res.Info {
|
||||
response.Info = append(response.Info, &messages.HealthInfo{
|
||||
Source: info.Source,
|
||||
Healthy: info.Healthy,
|
||||
MoreInfo: info.MoreInfo,
|
||||
})
|
||||
}
|
||||
|
||||
responseCode, responseData = messages.RespHealth, response
|
||||
case messages.CmdFetchCRL:
|
||||
var res *revoking.Result
|
||||
|
||||
fetchCRLPayload, ok := m.currentCommand.Command.(messages.FetchCRLCommand)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not use payload as FetchCRLPayload")
|
||||
}
|
||||
|
||||
res, err = m.fetchCRLHandler.FetchCRL(fetchCRLPayload.IssuerID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
response := &messages.FetchCRLResponse{
|
||||
IsDelta: false,
|
||||
CRLData: res.Data,
|
||||
}
|
||||
|
||||
responseCode, responseData = messages.RespFetchCRL, response
|
||||
default:
|
||||
return nil, fmt.Errorf("unhandled command %s", m.currentCommand.Announce)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error from command handler: %w", err)
|
||||
}
|
||||
|
||||
return &Response{
|
||||
Announce: messages.BuildResponseAnnounce(responseCode),
|
||||
Response: responseData,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildErrorResponse(errMsg string) *Response {
|
||||
return &Response{
|
||||
Announce: messages.BuildResponseAnnounce(messages.RespError),
|
||||
Response: &messages.ErrorResponse{Message: errMsg},
|
||||
}
|
||||
}
|
||||
|
||||
func New(logger *logrus.Logger, handlers ...RegisterHandler) (Handler, error) {
|
||||
messages.RegisterGeneratedResolver()
|
||||
|
||||
h := &MsgPackHandler{
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
for _, reg := range handlers {
|
||||
reg(h)
|
||||
}
|
||||
|
||||
return h, nil
|
||||
}
|
||||
|
||||
type RegisterHandler func(handler *MsgPackHandler)
|
||||
|
||||
func RegisterHealthHandler(healthHandler *health.Handler) func(*MsgPackHandler) {
|
||||
return func(h *MsgPackHandler) {
|
||||
h.healthHandler = healthHandler
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterFetchCRLHandler(fetchCRLHandler *revoking.FetchCRLHandler) func(handler *MsgPackHandler) {
|
||||
return func(h *MsgPackHandler) {
|
||||
h.fetchCRLHandler = fetchCRLHandler
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ func (h *Handler) Close() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
var cobsConfig = cobs.Config{SpecialByte: 0x00, Delimiter: true, EndingSave: true}
|
||||
var cobsConfig = cobs.Config{SpecialByte: protocol.CobsDelimiter, Delimiter: true, EndingSave: true}
|
||||
|
||||
func (h *Handler) Run(ctx context.Context) error {
|
||||
h.protocolState = cmdAnnounce
|
||||
|
@ -133,7 +133,9 @@ func (h *Handler) readFrames() error {
|
|||
)
|
||||
|
||||
var frame []byte
|
||||
|
||||
buffer := &bytes.Buffer{}
|
||||
|
||||
delimiter := []byte{cobsConfig.SpecialByte}
|
||||
|
||||
for {
|
||||
|
@ -212,6 +214,9 @@ func (h *Handler) nextState() error {
|
|||
func (h *Handler) handleProtocolState() error {
|
||||
h.logger.Tracef("handling protocol state %s", h.protocolState)
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
switch h.protocolState {
|
||||
case cmdAnnounce:
|
||||
if err := h.handleCmdAnnounce(); err != nil {
|
||||
|
@ -237,9 +242,6 @@ func (h *Handler) handleProtocolState() error {
|
|||
}
|
||||
|
||||
func (h *Handler) writeToPort(data []byte) error {
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
reader := bytes.NewReader(data)
|
||||
|
||||
n, err := io.Copy(h.port, reader)
|
||||
|
@ -249,10 +251,6 @@ func (h *Handler) writeToPort(data []byte) error {
|
|||
|
||||
h.logger.Tracef("wrote %d bytes", n)
|
||||
|
||||
if err := h.port.Flush(); err != nil {
|
||||
return fmt.Errorf("could not flush data: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -272,19 +270,17 @@ func (h *Handler) readFromPort() ([]byte, error) {
|
|||
func (h *Handler) handleCmdAnnounce() error {
|
||||
h.logger.Trace("waiting for command announce")
|
||||
|
||||
select {
|
||||
case frame := <-h.framesIn:
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
frame := <-h.framesIn
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := h.protocolHandler.HandleCommandAnnounce(frame); err != nil {
|
||||
return fmt.Errorf("command announce handling failed: %w", err)
|
||||
}
|
||||
if err := h.protocolHandler.HandleCommandAnnounce(frame); err != nil {
|
||||
return fmt.Errorf("command announce handling failed: %w", err)
|
||||
}
|
||||
|
||||
if err := h.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -293,20 +289,18 @@ func (h *Handler) handleCmdAnnounce() error {
|
|||
func (h *Handler) handleCmdData() error {
|
||||
h.logger.Trace("waiting for command data")
|
||||
|
||||
select {
|
||||
case frame := <-h.framesIn:
|
||||
frame := <-h.framesIn
|
||||
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
if frame == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := h.protocolHandler.HandleCommand(frame); err != nil {
|
||||
return fmt.Errorf("command handler failed: %w", err)
|
||||
}
|
||||
if err := h.protocolHandler.HandleCommand(frame); err != nil {
|
||||
return fmt.Errorf("command handler failed: %w", err)
|
||||
}
|
||||
|
||||
if err := h.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := h.nextState(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -318,8 +312,6 @@ func (h *Handler) handleRespAnnounce() error {
|
|||
return fmt.Errorf("could not get response announcement: %w", err)
|
||||
}
|
||||
|
||||
h.logger.Trace("writing response announce")
|
||||
|
||||
if err := h.writeFrame(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -337,8 +329,6 @@ func (h *Handler) handleRespData() error {
|
|||
return fmt.Errorf("could not get response data: %w", err)
|
||||
}
|
||||
|
||||
h.logger.Trace("writing response data")
|
||||
|
||||
if err := h.writeFrame(frame); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -354,7 +344,7 @@ func New(cfg *config.Serial, logger *logrus.Logger, protocolHandler protocol.Han
|
|||
h := &Handler{
|
||||
protocolHandler: protocolHandler,
|
||||
logger: logger,
|
||||
framesIn: make(chan []byte, 0),
|
||||
framesIn: make(chan []byte),
|
||||
}
|
||||
h.config = &serial.Config{Name: cfg.Device, Baud: cfg.Baud, ReadTimeout: cfg.Timeout}
|
||||
|
||||
|
|
|
@ -108,7 +108,8 @@ func NewRevokeCertificate(serialNumber *big.Int, reason CRLReason) *RevokeCertif
|
|||
}
|
||||
|
||||
type CRLInformation struct {
|
||||
CRL []byte // DER encoded CRL
|
||||
CRL []byte // DER encoded CRL
|
||||
Number *big.Int
|
||||
}
|
||||
|
||||
func (r *X509Revoking) Revoke(revokeCertificate *RevokeCertificate) (*pkix.RevokedCertificate, error) {
|
||||
|
@ -146,7 +147,7 @@ func (r *X509Revoking) CreateCRL() (*CRLInformation, error) {
|
|||
return nil, fmt.Errorf("could not sign revocation list: %w", err)
|
||||
}
|
||||
|
||||
return &CRLInformation{CRL: list}, nil
|
||||
return &CRLInformation{CRL: list, Number: nextNumber}, nil
|
||||
}
|
||||
|
||||
func NewX509Revoking(
|
||||
|
@ -159,7 +160,8 @@ func NewX509Revoking(
|
|||
}
|
||||
|
||||
type Result struct {
|
||||
Data []byte
|
||||
CRLData []byte
|
||||
Number *big.Int
|
||||
}
|
||||
|
||||
type FetchCRLHandler struct {
|
||||
|
@ -181,5 +183,5 @@ func (h *FetchCRLHandler) FetchCRL(issuerID string) (*Result, error) {
|
|||
return nil, fmt.Errorf("could not create CRL for issuer ID %s: %w", issuerID, err)
|
||||
}
|
||||
|
||||
return &Result{Data: currentCRL.CRL}, nil
|
||||
return &Result{CRLData: currentCRL.CRL, Number: currentCRL.Number}, nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue