Separate the window sentence from the wrapped error so a bad certificate file is not reported as a key mismatch.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 15:01:38 +02:00
co-authored by Cursor
parent a405048f23
commit e642b24be0
17 changed files with 842 additions and 742 deletions
+105 -559
View File
@@ -1,19 +1,17 @@
package station
import (
"bytes"
"context"
"crypto/hmac"
"crypto/rand"
"crypto/sha1"
"crypto/tls"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@@ -126,27 +124,40 @@ func (s *Station) wait(ctx context.Context, d time.Duration) bool {
}
type framed struct {
conn net.Conn
mu sync.Mutex
w io.Writer
frames chan frame
errs chan error
upgrade chan *tls.Config
up chan error
max atomic.Uint32
conn net.Conn
mu sync.Mutex
w io.Writer
frames chan frame
errs chan error
upgrade chan *tls.Config
handshake chan error
max atomic.Uint32
}
func newFramed(conn net.Conn, max uint32, done <-chan struct{}) *framed {
// exchange is the state one TCP session's frame handlers share.
type exchange struct {
ph phase
authed bool
client tls.Certificate
haveCert bool
done chan struct{}
udpCh chan []byte
udpErr chan error
udpReady bool
lastTCP time.Time
limit time.Time
}
func newFramed(conn net.Conn, max uint32) *framed {
f := &framed{
conn: conn,
w: conn,
frames: make(chan frame, 8),
errs: make(chan error, 1),
upgrade: make(chan *tls.Config, 1),
up: make(chan error, 1),
conn: conn,
w: conn,
frames: make(chan frame, 8),
errs: make(chan error, 1),
upgrade: make(chan *tls.Config, 1),
handshake: make(chan error, 1),
}
f.max.Store(max)
go f.read(done)
return f
}
@@ -167,7 +178,9 @@ func (s *Station) startTLS(ctx context.Context, f *framed, cfg *tls.Config, unti
case <-ctx.Done():
return ctx.Err()
}
_ = f.conn.SetReadDeadline(time.Now())
if err := f.conn.SetReadDeadline(time.Now()); err != nil {
return err
}
wait := until.Sub(s.clock.Now())
if wait < 0 {
wait = 0
@@ -175,15 +188,19 @@ func (s *Station) startTLS(ctx context.Context, f *framed, cfg *tls.Config, unti
timer := s.clock.NewTimer(wait)
defer timer.Stop()
select {
case err := <-f.up:
case err := <-f.handshake:
return err
case <-ctx.Done():
_ = f.conn.SetDeadline(time.Now())
if err := f.conn.SetDeadline(time.Now()); err != nil {
return err
}
return ctx.Err()
case <-timer.C():
_ = f.conn.SetDeadline(time.Now())
if err := f.conn.SetDeadline(time.Now()); err != nil {
return err
}
select {
case <-f.up:
case <-f.handshake:
case <-ctx.Done():
}
return errTLSTimeout
@@ -195,22 +212,24 @@ func (f *framed) read(done <-chan struct{}) {
for {
fr, err := readFrameMax(src, f.max.Load())
if err != nil {
var ne net.Error
if errors.As(err, &ne) && ne.Timeout() {
if ne, ok := errors.AsType[net.Error](err); ok && ne.Timeout() {
select {
case cfg := <-f.upgrade:
_ = f.conn.SetReadDeadline(time.Time{})
if derr := f.conn.SetReadDeadline(time.Time{}); derr != nil {
f.handshake <- derr
return
}
tc := tls.Client(f.conn, cfg)
herr := tc.Handshake()
if herr != nil {
f.up <- herr
f.handshake <- herr
return
}
f.mu.Lock()
f.w = tc
f.mu.Unlock()
src = tc
f.up <- nil
f.handshake <- nil
continue
case <-done:
return
@@ -257,15 +276,16 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
}
}()
limit := uint32(maxFrame)
ph := phCA
frameMax := uint32(maxFrame)
ex := &exchange{ph: phCA, done: done, lastTCP: s.clock.Now()}
label := "3.0"
if major < 3 {
limit = maxPreauth20
ph = phChallenge
frameMax = maxPreauth20
ex.ph = phChallenge
label = "2.0"
}
wire := newFramed(conn, limit, done)
wire := newFramed(conn, frameMax)
go wire.read(done)
s.mu.Lock()
s.protocol = label
s.rxSeq = 0
@@ -281,17 +301,9 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
if err := writeFrame(wire, tcpProtoVer, versionWire(major, 0)); err != nil {
return err
}
now := s.clock.Now()
lastTCP := now
now := ex.lastTCP
tcpRx := now.Add(15 * time.Second)
udpRx := now.Add(60 * time.Second)
authed := false
var udpCh chan []byte
var udpErr chan error
udpReady := false
var client tls.Certificate
var haveCert bool
for {
s.mu.Lock()
@@ -299,14 +311,14 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
silence, silenceOK := s.nextSilence()
s.mu.Unlock()
next := lastTCP.Add(10 * time.Second)
next := ex.lastTCP.Add(10 * time.Second)
if t := tcpRx; t.Before(next) {
next = t
}
if t := udpRx; t.Before(next) {
next = t
}
if udpReady {
if ex.udpReady {
if t := lastUDP.Add(15 * time.Second); t.Before(next) {
next = t
}
@@ -322,9 +334,9 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
var udpRecv <-chan []byte
var udpFail <-chan error
if udpCh != nil {
udpRecv = udpCh
udpFail = udpErr
if ex.udpCh != nil {
udpRecv = ex.udpCh
udpFail = ex.udpErr
}
var reason string
@@ -364,16 +376,16 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
switch reason {
case "tcp":
tcpRx = now.Add(15 * time.Second)
limit := tcpRx
if udpRx.Before(limit) {
limit = udpRx
ex.limit = tcpRx
if udpRx.Before(ex.limit) {
ex.limit = udpRx
}
err := s.onFrame(ctx, wire, major, tcpf, &ph, &authed, &client, &haveCert, done, &udpCh, &udpErr, &udpReady, &lastTCP, limit)
err := s.onFrame(ctx, wire, major, tcpf, ex)
if err != nil {
return err
}
case "udp":
if err := s.onUDP(wire, udpp, &ph, &udpRx, now, &lastTCP); err != nil {
if err := s.onUDP(wire, udpp, ex, &udpRx, now); err != nil {
return err
}
default:
@@ -381,19 +393,19 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
s.onClock(now)
s.mu.Unlock()
if !now.Before(tcpRx) {
return s.retry("tcp receive timeout")
return s.retry("tcp receive timeout", nil)
}
if !now.Before(udpRx) {
return s.retry("udp receive timeout")
return s.retry("udp receive timeout", nil)
}
if !now.Before(lastTCP.Add(10 * time.Second)) {
if !now.Before(ex.lastTCP.Add(10 * time.Second)) {
if err := writeFrame(wire, tcpHeartbeat, nil); err != nil {
return err
}
lastTCP = s.clock.Now()
ex.lastTCP = s.clock.Now()
}
s.mu.Lock()
if udpReady && s.udp != nil && !s.clock.Now().Before(s.lastUDPSend.Add(15*time.Second)) {
if ex.udpReady && s.udp != nil && !s.clock.Now().Before(s.lastUDPSend.Add(15*time.Second)) {
var err error
if s.udpWait {
err = s.sendRegistration()
@@ -410,11 +422,15 @@ func (s *Station) session(ctx context.Context, conn net.Conn, major uint16) erro
}
}
func (s *Station) retry(reason string) error {
func (s *Station) retry(detail string, cause error) error {
s.mu.Lock()
s.retryReason = reason
s.retryReason = detail
s.mu.Unlock()
return errors.New(reason)
text := strings.ToLower(detail)
if cause == nil {
return errors.New(text)
}
return fmt.Errorf("%s: %w", text, cause)
}
func (s *Station) nextSilence() (time.Time, bool) {
@@ -448,7 +464,7 @@ func (s *Station) onClock(now time.Time) {
}
}
func (s *Station) onFrame(ctx context.Context, wire *framed, major uint16, f frame, ph *phase, authed *bool, client *tls.Certificate, haveCert *bool, done chan struct{}, udpCh *chan []byte, udpErr *chan error, udpReady *bool, lastTCP *time.Time, limit time.Time) error {
func (s *Station) onFrame(ctx context.Context, wire *framed, major uint16, f frame, ex *exchange) error {
switch f.typ {
case tcpHeartbeat:
return nil
@@ -457,19 +473,20 @@ func (s *Station) onFrame(ctx context.Context, wire *framed, major uint16, f fra
case tcpProtoDown:
return s.onDowngrade(f.body, major)
}
if !*authed && f.typ >= 100 {
return s.retry("Protocol error")
if !ex.authed && f.typ >= 100 {
return s.retry("Protocol error", nil)
}
if major < 3 {
return s.onV2(wire, f, ph, authed, done, udpCh, udpErr, udpReady, lastTCP)
return s.onV2(wire, f, ex)
}
return s.onV3(ctx, wire, f, ph, authed, client, haveCert, done, udpCh, udpErr, udpReady, lastTCP, limit)
return s.onV3(ctx, wire, f, ex)
}
func (s *Station) onReflectorError(body []byte, major uint16) error {
msg, _, err := parseString(body)
if err != nil {
msg = "could not be read"
s.endFatalCause("could not be read", err, true)
return fmt.Errorf("could not be read: %w", err)
}
if major >= 3 && fallback20(msg) {
return errFallback
@@ -494,20 +511,20 @@ func strconvU(v uint16) string {
return strconv.FormatUint(uint64(v), 10)
}
func (s *Station) onV2(wire *framed, f frame, ph *phase, authed *bool, done chan struct{}, udpCh *chan []byte, udpErr *chan error, udpReady *bool, lastTCP *time.Time) error {
func (s *Station) onV2(wire *framed, f frame, ex *exchange) error {
switch f.typ {
case tcpAuthOK:
*authed = true
*ph = phServer
ex.authed = true
ex.ph = phServer
wire.setMax(maxFrame)
return nil
case tcpAuthChallenge:
return s.answerChallenge(wire, f.body, lastTCP)
return s.answerChallenge(wire, f.body, ex)
case tcpServerInfo:
if *ph != phServer {
return s.retry("Protocol error")
if ex.ph != phServer {
return s.retry("Protocol error", nil)
}
return s.finishV2(wire, f.body, done, udpCh, udpErr, udpReady, lastTCP)
return s.finishV2(wire, f.body, ex)
case tcpTalkerStart:
s.onTalker(f.body, true)
return nil
@@ -516,7 +533,7 @@ func (s *Station) onV2(wire *framed, f frame, ph *phase, authed *bool, done chan
return nil
case tcpNodeList, tcpNodeJoined, tcpNodeLeft, tcpRequestQSY, tcpStateEvent, tcpSignal, tcpTxStatus:
if err := unpackIgnored(f); err != nil {
return s.retry("Protocol error")
return s.retry("Protocol error", err)
}
return nil
default:
@@ -524,9 +541,12 @@ func (s *Station) onV2(wire *framed, f frame, ph *phase, authed *bool, done chan
}
}
func (s *Station) answerChallenge(w io.Writer, body []byte, lastTCP *time.Time) error {
func (s *Station) answerChallenge(w io.Writer, body []byte, ex *exchange) error {
chal, _, err := parseBlob(body)
if err != nil || len(chal) != 20 {
if err != nil {
return fmt.Errorf("bad challenge: %w", err)
}
if len(chal) != 20 {
return errors.New("bad challenge")
}
mac := hmac.New(sha1.New, []byte(s.cfg.authKey))
@@ -536,204 +556,11 @@ func (s *Station) answerChallenge(w io.Writer, body []byte, lastTCP *time.Time)
if err := writeFrame(w, tcpAuthResponse, out); err != nil {
return err
}
*lastTCP = s.clock.Now()
ex.lastTCP = s.clock.Now()
return nil
}
func (s *Station) onV3(ctx context.Context, wire *framed, f frame, ph *phase, authed *bool, client *tls.Certificate, haveCert *bool, done chan struct{}, udpCh *chan []byte, udpErr *chan error, udpReady *bool, lastTCP *time.Time, limit time.Time) error {
switch f.typ {
case tcpCAInfo:
if *ph != phCA {
return nil
}
return s.onCAInfo(wire, f.body, ph, lastTCP)
case tcpCABundle:
if *ph != phBundle {
return nil
}
return s.onCABundle(wire, f.body, ph, lastTCP)
case tcpStartEnc:
if *ph != phStart {
return nil
}
return s.onStartEncryption(ctx, wire, ph, client, haveCert, limit)
case tcpCsrReq:
return s.sendCSR(wire, lastTCP)
case tcpAuthChallenge:
return s.answerChallenge(wire, f.body, lastTCP)
case tcpAuthOK:
*authed = true
*ph = phServer
if csrNewerThanCert(s.cfg.certificateDir) {
if err := s.sendCSR(wire, lastTCP); err != nil {
return err
}
}
return nil
case tcpClientCert:
return s.saveCert(f.body)
case tcpServerInfo:
if !*authed {
return s.retry("Protocol error")
}
return s.finishV3Server(wire, f.body, ph, lastTCP)
case tcpStartUDPEnc:
if *ph != phUDP {
return nil
}
return s.finishV3UDP(wire.conn, done, udpCh, udpErr, udpReady, ph)
case tcpTalkerStart:
s.onTalker(f.body, true)
return nil
case tcpTalkerStop:
s.onTalker(f.body, false)
return nil
case tcpNodeList, tcpNodeJoined, tcpNodeLeft, tcpRequestQSY, tcpStateEvent, tcpSignal, tcpTxStatus:
if err := unpackIgnored(f); err != nil {
return s.retry("Protocol error")
}
return nil
default:
return nil
}
}
func (s *Station) onCAInfo(w io.Writer, body []byte, ph *phase, lastTCP *time.Time) error {
_, digest, err := parseCAInfo(body)
if err != nil {
return s.retry("Protocol error")
}
if err := ensureCertDir(s.cfg.certificateDir); err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
sum, ok, err := caDigest(s.cfg.certificateDir)
if err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
if ok {
if !bytes.Equal(sum[:], digest) {
s.mu.Lock()
s.writeLog(s.clock.Now(), "CA digest differs")
s.mu.Unlock()
}
if err := writeFrame(w, tcpStartEncReq, nil); err != nil {
return err
}
*lastTCP = s.clock.Now()
*ph = phStart
return nil
}
if err := writeFrame(w, tcpCABundleReq, nil); err != nil {
return err
}
*lastTCP = s.clock.Now()
*ph = phBundle
return nil
}
func parseCAInfo(b []byte) (uint16, []byte, error) {
size, rest, err := parseU16(b)
if err != nil {
return 0, nil, err
}
digest, rest, err := parseBlob(rest)
if err != nil {
return 0, nil, err
}
if len(digest) != sha256Len || len(rest) != 0 {
return 0, nil, io.ErrUnexpectedEOF
}
return size, digest, nil
}
const sha256Len = 32
func (s *Station) onCABundle(w io.Writer, body []byte, ph *phase, lastTCP *time.Time) error {
caPEM, rest, err := parseString(body)
if err != nil {
return s.retry("CA signature rejected")
}
sig, rest, err := parseBlob(rest)
if err != nil {
return s.retry("CA signature rejected")
}
certPEM, rest, err := parseString(rest)
if err != nil || len(rest) != 0 {
return s.retry("CA signature rejected")
}
if err := verifyCABundle(caPEM, sig, certPEM); err != nil {
return s.retry("CA signature rejected")
}
if err := writeCABundle(s.cfg.certificateDir, caPEM); err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
if err := writeFrame(w, tcpStartEncReq, nil); err != nil {
return err
}
*lastTCP = s.clock.Now()
*ph = phStart
return nil
}
func (s *Station) onStartEncryption(ctx context.Context, wire *framed, ph *phase, client *tls.Certificate, haveCert *bool, limit time.Time) error {
pair, present, fatal := prepareIdentity(s.cfg.certificateDir, s.cfg.callsign)
if fatal != "" {
s.endFatal(fatal, true)
return errStopped
}
*client = pair
*haveCert = present
caPEM, err := os.ReadFile(filepath.Join(s.cfg.certificateDir, caFileName))
if err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
cfg, err := tlsConfig(s.cfg.host, caPEM, pair, present)
if err != nil {
return s.retry("TLS handshake failed")
}
if err := s.startTLS(ctx, wire, cfg, limit); err != nil {
if errors.Is(err, errHostMismatch) {
return s.retry("Reflector certificate does not match the host")
}
if ctx.Err() != nil {
return ctx.Err()
}
return s.retry("TLS handshake failed")
}
*ph = phIdent
return nil
}
func (s *Station) sendCSR(w io.Writer, lastTCP *time.Time) error {
pem, err := readCSR(s.cfg.certificateDir)
if err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
if err := writeFrame(w, tcpCsr, appendString(nil, pem)); err != nil {
return err
}
*lastTCP = s.clock.Now()
return nil
}
func (s *Station) saveCert(body []byte) error {
pem, _, err := parseString(body)
if err != nil || pem == "" {
return s.retry("Protocol error")
}
if err := writeClientCert(s.cfg.certificateDir, pem); err != nil {
s.endFatal("Cannot create "+s.cfg.certificateDir, true)
return errStopped
}
return errReconnect
}
func (s *Station) finishV2(wire *framed, body []byte, done chan struct{}, udpCh *chan []byte, udpErr *chan error, udpReady *bool, lastTCP *time.Time) error {
func (s *Station) finishV2(wire *framed, body []byte, ex *exchange) error {
id, enc, dec, err := s.acceptServer(body)
if err != nil {
return err
@@ -759,7 +586,7 @@ func (s *Station) finishV2(wire *framed, body []byte, done chan struct{}, udpCh
dec.Close()
return err
}
*lastTCP = s.clock.Now()
ex.lastTCP = s.clock.Now()
udpConn, peer, err := dialAudio(wire.conn)
if err != nil {
enc.Close()
@@ -768,7 +595,7 @@ func (s *Station) finishV2(wire *framed, body []byte, done chan struct{}, udpCh
}
ch := make(chan []byte, 32)
errc := make(chan error, 1)
go readUDP(udpConn, peer, ch, errc, done)
go readUDP(udpConn, peer, ch, errc, ex.done)
s.mu.Lock()
s.clientID = id
s.udpPeer = peer
@@ -793,9 +620,9 @@ func (s *Station) finishV2(wire *framed, body []byte, done chan struct{}, udpCh
s.writeLog(s.clock.Now(), "connected")
s.publish()
s.mu.Unlock()
*udpCh = ch
*udpErr = errc
*udpReady = true
ex.udpCh = ch
ex.udpErr = errc
ex.udpReady = true
return nil
}
@@ -820,287 +647,6 @@ func (s *Station) acceptServer(body []byte) (uint16, *opus.Encoder, *opus.Decode
return id, enc, dec, nil
}
func (s *Station) finishV3Server(wire *framed, body []byte, ph *phase, lastTCP *time.Time) error {
id, enc, dec, err := s.acceptServer(body)
if err != nil {
return err
}
if enc == nil {
return errStopped
}
iv := make([]byte, 6)
key := make([]byte, 16)
if _, err := rand.Read(iv); err != nil || randFill(key) != nil {
enc.Close()
dec.Close()
return errors.New("udp key")
}
info, err := nodeInfoV3(iv, key)
if err != nil {
enc.Close()
dec.Close()
return err
}
if err := writeFrame(wire, tcpNodeInfo, info); err != nil {
enc.Close()
dec.Close()
return err
}
*lastTCP = s.clock.Now()
s.mu.Lock()
s.clientID = id
s.enc = enc
s.dec = dec
s.ivPrefix = iv
s.udpKey = key
s.secure = true
s.mu.Unlock()
*ph = phUDP
return nil
}
func randFill(b []byte) error {
_, err := rand.Read(b)
return err
}
func (s *Station) finishV3UDP(conn net.Conn, done chan struct{}, udpCh *chan []byte, udpErr *chan error, udpReady *bool, ph *phase) error {
udpConn, peer, err := dialAudio(conn)
if err != nil {
s.dropAudio()
return err
}
ch := make(chan []byte, 32)
errc := make(chan error, 1)
go readUDP(udpConn, peer, ch, errc, done)
s.mu.Lock()
s.udp = udpConn
s.udpPeer = peer
s.udpWait = true
s.txCounter = 1
s.rxCounter = 0
err = s.sendRegistration()
s.mu.Unlock()
if err != nil {
s.dropAudio()
udpConn.Close()
return err
}
*udpCh = ch
*udpErr = errc
*udpReady = true
*ph = phBeat
return nil
}
func dialAudio(conn net.Conn) (*net.UDPConn, *net.UDPAddr, error) {
remote, ok := conn.RemoteAddr().(*net.TCPAddr)
if !ok {
return nil, nil, errors.New("tcp peer has no address")
}
local := &net.UDPAddr{Port: 0}
if tcpLocal, ok := conn.LocalAddr().(*net.TCPAddr); ok && tcpLocal.IP != nil {
local.IP = tcpLocal.IP
}
udpConn, err := net.ListenUDP("udp", local)
if err != nil {
return nil, nil, err
}
return udpConn, &net.UDPAddr{IP: append(net.IP(nil), remote.IP...), Port: remote.Port}, nil
}
func (s *Station) dropAudio() {
s.mu.Lock()
defer s.mu.Unlock()
if s.enc != nil {
s.enc.Close()
s.enc = nil
}
if s.dec != nil {
s.dec.Close()
s.dec = nil
}
}
func readUDP(conn *net.UDPConn, peer *net.UDPAddr, out chan<- []byte, errc chan<- error, done <-chan struct{}) {
buf := make([]byte, 2048)
for {
n, addr, err := conn.ReadFromUDP(buf)
if err != nil {
select {
case errc <- err:
case <-done:
}
return
}
if addr == nil || peer == nil || !addr.IP.Equal(peer.IP) || addr.Port != peer.Port {
continue
}
pkt := append([]byte(nil), buf[:n]...)
select {
case out <- pkt:
case <-done:
return
}
}
}
func (s *Station) onUDP(w io.Writer, pkt []byte, ph *phase, udpRx *time.Time, now time.Time, lastTCP *time.Time) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.secure {
plain, ok := s.openSecure(pkt)
if !ok {
return nil
}
*udpRx = now.Add(60 * time.Second)
if len(plain) < 2 {
return nil
}
typ := binary.BigEndian.Uint16(plain[:2])
body := plain[2:]
if s.udpWait {
if typ != udpHeartbeat {
return nil
}
s.udpWait = false
s.mu.Unlock()
var tg [4]byte
binary.BigEndian.PutUint32(tg[:], uint32(s.cfg.talkGroup))
err := writeFrame(w, tcpSelectTG, tg[:])
s.mu.Lock()
if err != nil {
return err
}
*lastTCP = s.clock.Now()
s.state.Connection = "Connected"
s.up = true
s.outage = false
s.noteTransmitLocked()
s.writeLog(s.clock.Now(), "connected")
s.publish()
*ph = phRun
return nil
}
s.onPlain(typ, body, now)
return nil
}
if !s.acceptV2(pkt) {
return nil
}
*udpRx = now.Add(60 * time.Second)
typ := binary.BigEndian.Uint16(pkt[:2])
s.onPlain(typ, pkt[6:], now)
return nil
}
func (s *Station) openSecure(pkt []byte) ([]byte, bool) {
if len(pkt) < 12 || s.udpKey == nil {
return nil, false
}
counter := binary.BigEndian.Uint32(pkt[:4])
if counter < s.rxCounter {
return nil, false
}
tag := pkt[4:12]
ct := pkt[12:]
plain, err := openDatagram(s.udpKey, s.ivPrefix, 0, counter, pkt[:4], tag, ct)
if err != nil {
return nil, false
}
s.rxCounter = counter + 1
return plain, true
}
func (s *Station) acceptV2(pkt []byte) bool {
if len(pkt) < 6 {
return false
}
if binary.BigEndian.Uint16(pkt[2:4]) != s.clientID {
return false
}
seq := binary.BigEndian.Uint16(pkt[4:6])
diff := seq - s.rxSeq
if diff > 32767 {
return false
}
s.rxSeq = seq + 1
return true
}
func (s *Station) onPlain(typ uint16, body []byte, now time.Time) {
switch typ {
case udpAllFlushed:
s.flushWaiting = false
case udpAudio:
if s.dec == nil || s.transmitting() {
return
}
packet, _, err := parseBlob(body)
if err != nil {
return
}
pcm, err := s.dec.Decode(packet)
if err != nil {
return
}
s.play = append(s.play, pcm...)
s.roomBusy = true
s.audioHeard = true
s.lastAudio = now
s.publish()
}
}
func (s *Station) sendRegistration() error {
return s.sendSecure(udpHeartbeat, nil, 0, true)
}
func (s *Station) sendUDP(typ uint16, body []byte) error {
if s.udp == nil {
return errors.New("udp is down")
}
if s.secure {
return s.sendSecure(typ, body, s.txCounter, false)
}
buf := make([]byte, 6+len(body))
binary.BigEndian.PutUint16(buf[0:2], typ)
binary.BigEndian.PutUint16(buf[2:4], s.clientID)
binary.BigEndian.PutUint16(buf[4:6], s.seq)
s.seq++
copy(buf[6:], body)
if _, err := s.udp.WriteToUDP(buf, s.udpPeer); err != nil {
return err
}
s.lastUDPSend = s.clock.Now()
return nil
}
func (s *Station) sendSecure(typ uint16, body []byte, counter uint32, reg bool) error {
plain := appendU16(nil, typ)
plain = append(plain, body...)
var aad []byte
id := s.clientID
if reg {
aad = registrationAAD(s.clientID)
counter = 0
} else {
aad = counterAAD(counter)
}
wire, err := sealDatagram(s.udpKey, s.ivPrefix, id, counter, aad, plain)
if err != nil {
return err
}
if _, err := s.udp.WriteToUDP(wire, s.udpPeer); err != nil {
return err
}
s.lastUDPSend = s.clock.Now()
if !reg {
s.txCounter = counter + 1
}
return nil
}
func (s *Station) stop(msg string) { s.endFatal(msg, false) }
// Fail stops retries and shows message until the operator quits.