Implement first client command
The client can now talk to the old Perl signer implementation. Running socat has been documented in README.md. Commonly used I/O code has been moved to the shared/io.go file.
This commit is contained in:
parent
5aa557c9aa
commit
65855152ce
6 changed files with 287 additions and 84 deletions
26
README.md
Normal file
26
README.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# CAcert signer in Go
|
||||||
|
|
||||||
|
This is a reimplementation of the CAcert signer code from the
|
||||||
|
[CommModule directory](https://git.cacert.org/gitweb/?p=cacert-devel.git;a=tree;f=CommModule) of the CAcert software
|
||||||
|
in [Go](https://golang.org/). The goal of this effort is to provide a more maintainable version of the software.
|
||||||
|
|
||||||
|
## Running the signer and client locally
|
||||||
|
|
||||||
|
The signer is usually attached to a USB serial port. You can run the signer and client locally using the
|
||||||
|
[socat](https://manpages.debian.org/buster/socat/socat.1.en.html) utility.
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
socat -d -d PTY,link=$(pwd)/ttyS0 PTY,link=$(pwd)/ttyS1
|
||||||
|
```
|
||||||
|
|
||||||
|
You may then run the server
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
go run signer/main.go -a $(pwd)/dev/ttyS0
|
||||||
|
```
|
||||||
|
|
||||||
|
and the client:
|
||||||
|
|
||||||
|
```shell script
|
||||||
|
go run client/main.go -a $(pwd)/dev/ttyS1
|
||||||
|
```
|
264
client/main.go
264
client/main.go
|
@ -1,71 +1,132 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
"log"
|
"io/ioutil"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/goburrow/serial"
|
"github.com/goburrow/serial"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type SerialConfig struct {
|
||||||
address string
|
Address string `yaml:"address"`
|
||||||
baudrate int
|
BaudRate int `yaml:"baudrate"`
|
||||||
databits int
|
DataBits int `yaml:"databits"`
|
||||||
stopbits int
|
StopBits int `yaml:"stopbits"`
|
||||||
parity string
|
Parity string `yaml:"parity"`
|
||||||
message string
|
}
|
||||||
)
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
Serial SerialConfig `yaml:"serial_config"`
|
||||||
|
Paranoid bool `yaml:"paranoid"`
|
||||||
|
Debug bool `yaml:"debug"`
|
||||||
|
GNUPGBinary string `yaml:"gnupg_bin"`
|
||||||
|
OpenSSLBinary string `yaml:"openssl_bin"`
|
||||||
|
MySQLDSN string `yaml:"mysql_dsn"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultConfig = ClientConfig{
|
||||||
|
Serial: SerialConfig{
|
||||||
|
Address: "/dev/ttyUSB0",
|
||||||
|
BaudRate: 115200,
|
||||||
|
DataBits: 8,
|
||||||
|
StopBits: 1,
|
||||||
|
Parity: "N",
|
||||||
|
},
|
||||||
|
Paranoid: false,
|
||||||
|
Debug: false,
|
||||||
|
OpenSSLBinary: "/usr/bin/openssl",
|
||||||
|
GNUPGBinary: "/usr/bin/gpg",
|
||||||
|
MySQLDSN: "<username>:<password>@/database?parseTime=true",
|
||||||
|
}
|
||||||
|
|
||||||
|
const HandshakeByte = 0x02
|
||||||
|
const AckByte = 0x10
|
||||||
|
const NackByte = 0x11
|
||||||
|
|
||||||
|
func readConfig(configFile string) (config *ClientConfig, err error) {
|
||||||
|
source, err := ioutil.ReadFile(configFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("opening configuration file failed: %v", err)
|
||||||
|
if exampleConfig, err := generateExampleConfig(configFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
log.Info("starting with default config")
|
||||||
|
return exampleConfig, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal(source, &config); err != nil {
|
||||||
|
return nil, fmt.Errorf("loading configuration file failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateExampleConfig(configFile string) (config *ClientConfig, err error) {
|
||||||
|
config = &defaultConfig
|
||||||
|
configBytes, err := yaml.Marshal(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("could not generate configuration data")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("example data for %s:\n\n---\n%s\n", configFile, configBytes)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.StringVar(&address, "a", "/dev/ttyUSB0", "address")
|
var configFile string
|
||||||
flag.IntVar(&baudrate, "b", 115200, "baud rate")
|
|
||||||
flag.IntVar(&databits, "d", 8, "data bits")
|
flag.StringVar(&configFile, "c", "client.yaml", "client configuration file in YAML format")
|
||||||
flag.IntVar(&stopbits, "s", 1, "stop bits")
|
|
||||||
flag.StringVar(&parity, "p", "N", "parity (N/E/O)")
|
|
||||||
flag.StringVar(&message, "m", "serial", "message")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
config := serial.Config{
|
var clientConfig *ClientConfig
|
||||||
Address: address,
|
var serialConfig *serial.Config
|
||||||
BaudRate: baudrate,
|
var err error
|
||||||
DataBits: databits,
|
|
||||||
StopBits: stopbits,
|
if clientConfig, err = readConfig(configFile); err != nil {
|
||||||
Parity: parity,
|
log.Panic(err)
|
||||||
|
}
|
||||||
|
serialConfig = &serial.Config{
|
||||||
|
Address: clientConfig.Serial.Address,
|
||||||
|
BaudRate: clientConfig.Serial.BaudRate,
|
||||||
|
DataBits: clientConfig.Serial.DataBits,
|
||||||
|
StopBits: clientConfig.Serial.StopBits,
|
||||||
|
Parity: clientConfig.Serial.Parity,
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 30 * time.Second,
|
||||||
}
|
}
|
||||||
log.Printf("connecting %+v", config)
|
if clientConfig.Debug {
|
||||||
port, err := serial.Open(&config)
|
log.SetLevel(log.DebugLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("connecting %+v", serialConfig)
|
||||||
|
port, err := serial.Open(serialConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Println("connected")
|
log.Debug("connected")
|
||||||
defer func() {
|
defer func() {
|
||||||
err := port.Close()
|
err := port.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
log.Println("closed")
|
log.Debug("closed")
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if _, err = port.Write([]byte{0x02}); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
resp := make([]byte, 1)
|
|
||||||
if _, err = port.Read(resp); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
request := datastructures.NewNulRequest()
|
request := datastructures.NewNulRequest()
|
||||||
timeout := time.After(2700 * time.Millisecond)
|
timeout := time.After(2700 * time.Millisecond)
|
||||||
errorChannel := make(chan error, 1)
|
errorChannel := make(chan error, 1)
|
||||||
responseChannel := make(chan *datastructures.SignerResponse, 1)
|
responseChannel := make(chan *datastructures.SignerResponse, 1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if response, err := SendRequest(&port, request); err != nil {
|
if response, err := SendRequest(port, request); err != nil {
|
||||||
errorChannel <- err
|
errorChannel <- err
|
||||||
} else {
|
} else {
|
||||||
responseChannel <- response
|
responseChannel <- response
|
||||||
|
@ -77,30 +138,133 @@ func main() {
|
||||||
log.Fatal("timeout")
|
log.Fatal("timeout")
|
||||||
case err := <-errorChannel:
|
case err := <-errorChannel:
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
case response := <- responseChannel:
|
case response := <-responseChannel:
|
||||||
if err := Process(response); err != nil {
|
if err := Process(response); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
requestBytes := request.Serialize()
|
|
||||||
|
|
||||||
if _, err = port.Write(requestBytes); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = port.Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err = port.Write([]byte(shared.MagicTrailer)); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Process(response *datastructures.SignerResponse) error {
|
func Process(response *datastructures.SignerResponse) error {
|
||||||
|
log.Debugf("process %v", response)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendRequest(port *serial.Port, request *datastructures.SignerRequest) (*datastructures.SignerResponse, error) {
|
func sendHandShake(port serial.Port) error {
|
||||||
|
log.Debug("Shaking hands ...")
|
||||||
|
if length, err := port.Write([]byte{HandshakeByte}); err != nil {
|
||||||
|
return fmt.Errorf("could not write handshake byte: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("wrote %d handshake bytes", length)
|
||||||
|
}
|
||||||
|
handShakeResponse := make([]byte, 1)
|
||||||
|
if length, err := port.Read(handShakeResponse); err != nil {
|
||||||
|
return fmt.Errorf("failed to read handshake response: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("read %d bytes", length)
|
||||||
|
}
|
||||||
|
if handShakeResponse[0] != AckByte {
|
||||||
|
return fmt.Errorf("invalid handshake response expected 0x10 received %x", handShakeResponse[0])
|
||||||
|
}
|
||||||
|
log.Debug("Handshake successful")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func receiveResponse(port *serial.Port, responseChan *chan []byte, errorChan *chan error) {
|
||||||
|
header, err := shared.ReceiveBytes(port, 1, 20)
|
||||||
|
if err != nil {
|
||||||
|
*errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if header[0] != HandshakeByte {
|
||||||
|
*errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, HandshakeByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := (*port).Write([]byte{AckByte}); err != nil {
|
||||||
|
*errorChan <- errors.New("could not write ACK")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lengthBytes, err := shared.ReceiveBytes(port, 3, 2)
|
||||||
|
if err != nil {
|
||||||
|
*errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]})
|
||||||
|
blockData, err := shared.ReceiveBytes(port, int(blockLength), 5)
|
||||||
|
if err != nil {
|
||||||
|
*errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSum, err := shared.ReceiveBytes(port, 1, 2)
|
||||||
|
if err != nil {
|
||||||
|
*errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Debugf("block checksum is %d", checkSum)
|
||||||
|
trailer, err := shared.ReceiveBytes(port, len(shared.MagicTrailer), 2)
|
||||||
|
if err != nil {
|
||||||
|
*errorChan <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if string(trailer) != shared.MagicTrailer {
|
||||||
|
*errorChan <- errors.New("expected trailer bytes not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := (*port).Write([]byte{AckByte}); err != nil {
|
||||||
|
*errorChan <- fmt.Errorf("could not write ACK byte: %v", err)
|
||||||
|
}
|
||||||
|
*responseChan <- blockData
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendRequest(port serial.Port, request *datastructures.SignerRequest) (response *datastructures.SignerResponse, err error) {
|
||||||
|
log.Debugf("send request %v to serial port %v", request, port)
|
||||||
|
|
||||||
|
if err = sendHandShake(port); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBytes := request.Serialize()
|
||||||
|
if length, err := port.Write(requestBytes); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("wrote %d request bytes", length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if length, err := port.Write([]byte{datastructures.CalculateXorCheckSum([][]byte{requestBytes})}); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("wrote %d checksum bytes", length)
|
||||||
|
}
|
||||||
|
|
||||||
|
if length, err := port.Write([]byte(shared.MagicTrailer)); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
} else {
|
||||||
|
log.Debugf("wrote %d trailer bytes", length)
|
||||||
|
}
|
||||||
|
|
||||||
|
header, err := shared.ReceiveBytes(&port, 1, 20)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if header[0] != AckByte {
|
||||||
|
return nil, fmt.Errorf("unexpected byte 0x%x expected 0x%x", header, AckByte)
|
||||||
|
}
|
||||||
|
|
||||||
|
responseChan := make(chan []byte, 1)
|
||||||
|
errChan := make(chan error, 1)
|
||||||
|
go receiveResponse(&port, &responseChan, &errChan)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case responseData := <-responseChan:
|
||||||
|
log.Debugf("response data: %v", responseData)
|
||||||
|
case err := <-errChan:
|
||||||
|
log.Errorf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ type SignerRequest struct {
|
||||||
Content3 string
|
Content3 string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const protocolVersion = 1
|
||||||
|
|
||||||
func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerRequest, error) {
|
func SignerRequestFromData(lengthBytes []byte, blockData []byte, checkSum byte) (*SignerRequest, error) {
|
||||||
headerLength := decode24BitLength(blockData[0:3])
|
headerLength := decode24BitLength(blockData[0:3])
|
||||||
headerBytes := blockData[3 : 3+headerLength]
|
headerBytes := blockData[3 : 3+headerLength]
|
||||||
|
@ -76,5 +78,9 @@ func (r SignerRequest) Serialize() []byte {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNulRequest() *SignerRequest {
|
func NewNulRequest() *SignerRequest {
|
||||||
return &SignerRequest{Version: 1, Action: ActionNul, Content1: time.Now().UTC().Format("010203042006.05")}
|
return &SignerRequest{
|
||||||
|
Version: protocolVersion,
|
||||||
|
Action: ActionNul,
|
||||||
|
Content1: time.Now().UTC().Format("010203042006.05"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
shared/io.go
Normal file
31
shared/io.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/goburrow/serial"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// receive the requested number of bytes from serial port and stop after the given timeout in seconds
|
||||||
|
func ReceiveBytes(port *serial.Port, count int, timeout time.Duration) ([]byte, error) {
|
||||||
|
timeoutCh := time.After(timeout * time.Second)
|
||||||
|
readCh := make(chan []byte, 1)
|
||||||
|
errCh := make(chan error, 1)
|
||||||
|
go func() {
|
||||||
|
data := make([]byte, count)
|
||||||
|
if _, err := io.ReadAtLeast(*port, data, count); err != nil {
|
||||||
|
errCh <- err
|
||||||
|
} else {
|
||||||
|
readCh <- data
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-timeoutCh:
|
||||||
|
return nil, errors.New("timeout")
|
||||||
|
case err := <-errCh:
|
||||||
|
return nil, err
|
||||||
|
case data := <-readCh:
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
package shared
|
package shared
|
||||||
|
|
||||||
const MagicTrailer = "rie4Ech7"
|
const MagicTrailer = "rie4Ech7"
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.cacert.org/cacert-gosigner/datastructures"
|
"git.cacert.org/cacert-gosigner/datastructures"
|
||||||
"git.cacert.org/cacert-gosigner/shared"
|
"git.cacert.org/cacert-gosigner/shared"
|
||||||
"io"
|
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -70,7 +69,7 @@ readLoop:
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR %v\n", err)
|
log.Printf("ERROR %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
SendResponse(&port, response)
|
_ = SendResponse(&port, response)
|
||||||
}
|
}
|
||||||
case <-timeout:
|
case <-timeout:
|
||||||
log.Println("timeout in main loop")
|
log.Println("timeout in main loop")
|
||||||
|
@ -90,7 +89,7 @@ func SendResponse(port *serial.Port, response *datastructures.SignerResponse) er
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ack, err := receiveBytes(port, 1, 5); err != nil {
|
if ack, err := shared.ReceiveBytes(port, 1, 5); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ack[0] != 0x10 {
|
} else if ack[0] != 0x10 {
|
||||||
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0]))
|
||||||
|
@ -112,7 +111,7 @@ func SendResponse(port *serial.Port, response *datastructures.SignerResponse) er
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ack, err := receiveBytes(port, 1, 5); err != nil {
|
if ack, err := shared.ReceiveBytes(port, 1, 5); err != nil {
|
||||||
return err
|
return err
|
||||||
} else if ack[0] == 0x10 {
|
} else if ack[0] == 0x10 {
|
||||||
tryAgain = false
|
tryAgain = false
|
||||||
|
@ -146,13 +145,13 @@ func handleNulAction(command datastructures.SignerRequest) (*datastructures.Sign
|
||||||
|
|
||||||
// Receive a request and generate a request data structure
|
// Receive a request and generate a request data structure
|
||||||
func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, errorChan *chan error) {
|
func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, errorChan *chan error) {
|
||||||
header, err := receiveBytes(port, 1, 20)
|
header, err := shared.ReceiveBytes(port, 1, 20)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if header[0] != 0x02 {
|
if header[0] != 0x02 {
|
||||||
*errorChan <- errors.New(fmt.Sprintf("unexpected byte 0x%x expected 0x02", header))
|
*errorChan <- fmt.Errorf("unexpected byte 0x%x expected 0x02", header)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := (*port).Write([]byte{0x10}); err != nil {
|
if _, err := (*port).Write([]byte{0x10}); err != nil {
|
||||||
|
@ -160,19 +159,19 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
lengthBytes, err := receiveBytes(port, 3, 2)
|
lengthBytes, err := shared.ReceiveBytes(port, 3, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]})
|
blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]})
|
||||||
blockData, err := receiveBytes(port, int(blockLength), 5)
|
blockData, err := shared.ReceiveBytes(port, int(blockLength), 5)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
checkSum, err := receiveBytes(port, 1, 2)
|
checkSum, err := shared.ReceiveBytes(port, 1, 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
return
|
return
|
||||||
|
@ -183,7 +182,7 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest,
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
}
|
}
|
||||||
|
|
||||||
trailer, err := receiveBytes(port, len(shared.MagicTrailer), 2)
|
trailer, err := shared.ReceiveBytes(port, len(shared.MagicTrailer), 2)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errorChan <- err
|
*errorChan <- err
|
||||||
return
|
return
|
||||||
|
@ -201,25 +200,3 @@ func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest,
|
||||||
*commandChan <- *command
|
*commandChan <- *command
|
||||||
}
|
}
|
||||||
|
|
||||||
// receive the requested number of bytes from serial port and stop after the given timeout in seconds
|
|
||||||
func receiveBytes(port *serial.Port, count int, timeout time.Duration) ([]byte, error) {
|
|
||||||
timeoutCh := time.After(timeout * time.Second)
|
|
||||||
readCh := make(chan []byte, 1)
|
|
||||||
errCh := make(chan error, 1)
|
|
||||||
go func() {
|
|
||||||
data := make([]byte, count)
|
|
||||||
if _, err := io.ReadAtLeast(*port, data, count); err != nil {
|
|
||||||
errCh <- err
|
|
||||||
} else {
|
|
||||||
readCh <- data
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
select {
|
|
||||||
case <-timeoutCh:
|
|
||||||
return nil, errors.New("timeout")
|
|
||||||
case err := <-errCh:
|
|
||||||
return nil, err
|
|
||||||
case data := <-readCh:
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue