package main import ( "encoding/binary" "errors" "flag" "fmt" "git.cacert.org/cacert-gosigner/datastructures" "git.cacert.org/cacert-gosigner/shared" "io" "log" "time" "github.com/goburrow/serial" ) var ( address string baudrate int databits int stopbits int parity string ) func main() { flag.StringVar(&address, "a", "/dev/ttyUSB0", "address") flag.IntVar(&baudrate, "b", 115200, "baud rate") flag.IntVar(&databits, "d", 8, "data bits") flag.IntVar(&stopbits, "s", 1, "stop bits") flag.StringVar(&parity, "p", "N", "parity (N/E/O)") flag.Parse() config := serial.Config{ Address: address, BaudRate: baudrate, DataBits: databits, StopBits: stopbits, Parity: parity, Timeout: 5 * time.Minute, } log.Printf("connecting %+v", config) port, err := serial.Open(&config) if err != nil { log.Fatal(err) } log.Println("connected") count := 0 defer func() { err := port.Close() if err != nil { log.Fatal(err) } log.Println("closed") }() readLoop: for { // timeout if no command has been received within 15 seconds timeout := time.After(15 * time.Second) commandChan := make(chan datastructures.SignerRequest, 1) errChan := make(chan error, 1) go Receive(&port, &commandChan, &errChan) select { case command := <-commandChan: response, err := Process(command) if err != nil { log.Printf("ERROR %v\n", err) } else { SendResponse(&port, response) } case <-timeout: log.Println("timeout in main loop") break readLoop case err := <-errChan: log.Printf("ERROR %v\n", err) } count++ log.Printf("INFO %d requests processed. Waiting for next request ...\n", count) } } // Send a response to the client func SendResponse(port *serial.Port, response *datastructures.SignerResponse) error { if _, err := (*port).Write([]byte{0x02}); err != nil { return err } if ack, err := receiveBytes(port, 1, 5); err != nil { return err } else if ack[0] != 0x10 { return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0])) } tryAgain := true for ; tryAgain; { data := response.Serialize() if _, err := (*port).Write(data); err != nil { return err } checksum := datastructures.CalculateXorCheckSum([][]byte{data}) if _, err := (*port).Write([]byte{checksum}); err != nil { return err } if _, err := (*port).Write([]byte(shared.MagicTrailer)); err != nil { return err } if ack, err := receiveBytes(port, 1, 5); err != nil { return err } else if ack[0] == 0x10 { tryAgain = false } else if ack[0] != 0x11 { return errors.New(fmt.Sprintf("invalid ack byte 0x%02x", ack[0])) } } return nil } // Process the signer request func Process(command datastructures.SignerRequest) (response *datastructures.SignerResponse, err error) { log.Printf("INFO analyze %+v\n", command) switch command.Action { case datastructures.ActionNul: response, err = handleNulAction(command) return default: return nil, errors.New(fmt.Sprintf("unsupported Action 0x%02x", command.Action)) } } func handleNulAction(command datastructures.SignerRequest) (*datastructures.SignerResponse, error) { // todo: generate script to set system time from command time data in command.content1 return &datastructures.SignerResponse{ Version: command.Version, Action: command.Action, Content1: "", Content2: "", Content3: ""}, nil } // Receive a request and generate a request data structure func Receive(port *serial.Port, commandChan *chan datastructures.SignerRequest, errorChan *chan error) { header, err := receiveBytes(port, 1, 20) if err != nil { *errorChan <- err return } if header[0] != 0x02 { *errorChan <- errors.New(fmt.Sprintf("unexpected byte 0x%x expected 0x02", header)) } if _, err := (*port).Write([]byte{0x10}); err != nil { *errorChan <- errors.New("could not write ACK") return } lengthBytes, err := receiveBytes(port, 3, 2) if err != nil { *errorChan <- err return } blockLength := binary.BigEndian.Uint32([]byte{0x0, lengthBytes[0], lengthBytes[1], lengthBytes[2]}) blockData, err := receiveBytes(port, int(blockLength), 5) if err != nil { *errorChan <- err return } checkSum, err := receiveBytes(port, 1, 2) if err != nil { *errorChan <- err return } command, err := datastructures.SignerRequestFromData(lengthBytes, blockData, checkSum[0]) if err != nil { *errorChan <- err } trailer, err := 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{0x10}); err != nil { *errorChan <- err return } *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 } }