Speak protocol 3.0 and fall back to 2.0 only when that is the reflector's maximum.

A reflector that has moved to 3.0 will not accept a station that always announces 2.0, and a 2.x reflector still needs one explicit plaintext session.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 14:24:00 +02:00
co-authored by Cursor
parent 0930d4b727
commit 3d1a2a1669
24 changed files with 3180 additions and 244 deletions
+63 -7
View File
@@ -1,6 +1,7 @@
package station_test
import (
"crypto/tls"
"encoding/binary"
"encoding/json"
"io"
@@ -133,10 +134,18 @@ type reflector struct {
ln net.Listener
udp *net.UDPConn
tcp net.Conn
tls *tls.Conn
peer *net.UDPAddr
port int
}
func (r *reflector) conn() net.Conn {
if r.tls != nil {
return r.tls
}
return r.tcp
}
func newReflector(t *testing.T) *reflector {
t.Helper()
ln, err := net.Listen("tcp", "127.0.0.1:0")
@@ -166,16 +175,18 @@ func (r *reflector) accept(t *testing.T) {
t.Fatal(err)
}
r.tcp = conn
r.tls = nil
t.Cleanup(func() { conn.Close() })
}
func (r *reflector) readFrame(t *testing.T) (uint16, []byte) {
t.Helper()
if err := r.tcp.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
conn := r.conn()
if err := conn.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
t.Fatal(err)
}
var hdr [4]byte
if _, err := io.ReadFull(r.tcp, hdr[:]); err != nil {
if _, err := io.ReadFull(conn, hdr[:]); err != nil {
t.Fatal(err)
}
n := binary.BigEndian.Uint32(hdr[:])
@@ -183,7 +194,7 @@ func (r *reflector) readFrame(t *testing.T) (uint16, []byte) {
t.Fatalf("frame length %d", n)
}
buf := make([]byte, n)
if _, err := io.ReadFull(r.tcp, buf); err != nil {
if _, err := io.ReadFull(conn, buf); err != nil {
t.Fatal(err)
}
if len(buf) < 2 {
@@ -199,14 +210,29 @@ func (r *reflector) writeFrame(t *testing.T, typ uint16, body []byte) {
copy(payload[2:], body)
var hdr [4]byte
binary.BigEndian.PutUint32(hdr[:], uint32(len(payload)))
if err := r.tcp.SetWriteDeadline(time.Now().Add(2 * time.Second)); err != nil {
conn := r.conn()
if err := conn.SetWriteDeadline(time.Now().Add(2 * time.Second)); err != nil {
t.Fatal(err)
}
if _, err := r.tcp.Write(append(hdr[:], payload...)); err != nil {
if _, err := conn.Write(append(hdr[:], payload...)); err != nil {
t.Fatal(err)
}
}
func (r *reflector) handshake(t *testing.T, cfg *tls.Config) error {
t.Helper()
r.tls = tls.Server(r.tcp, cfg)
if err := r.tls.SetDeadline(time.Now().Add(2 * time.Second)); err != nil {
return err
}
err := r.tls.Handshake()
_ = r.tls.SetDeadline(time.Time{})
if err != nil {
r.tls = nil
}
return err
}
func (r *reflector) readUDP(t *testing.T) (typ, id, seq uint16, body []byte) {
t.Helper()
if err := r.udp.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
@@ -228,6 +254,33 @@ func (r *reflector) readUDP(t *testing.T) (typ, id, seq uint16, body []byte) {
return typ, id, seq, body
}
func (r *reflector) readRawUDP(t *testing.T) []byte {
t.Helper()
if err := r.udp.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil {
t.Fatal(err)
}
buf := make([]byte, 2048)
n, addr, err := r.udp.ReadFromUDP(buf)
if err != nil {
t.Fatal(err)
}
r.peer = addr
return append([]byte(nil), buf[:n]...)
}
func (r *reflector) writeRawUDP(t *testing.T, pkt []byte) {
t.Helper()
if r.peer == nil {
t.Fatal("no udp peer")
}
if err := r.udp.SetWriteDeadline(time.Now().Add(2 * time.Second)); err != nil {
t.Fatal(err)
}
if _, err := r.udp.WriteToUDP(pkt, r.peer); err != nil {
t.Fatal(err)
}
}
func (r *reflector) writeUDP(t *testing.T, typ, id, seq uint16, body []byte) {
t.Helper()
if r.peer == nil {
@@ -294,8 +347,11 @@ func requireSoftwareIdentity(t *testing.T, body []byte) {
if err := json.Unmarshal([]byte(raw), &got); err != nil {
t.Fatalf("software identity %q: %v", raw, err)
}
if len(got) != 2 || got["sw"] != station.SoftwareName || got["swVer"] != station.SoftwareVersion {
t.Fatalf("software identity = %#v, want sw %q and swVer %q only", got, station.SoftwareName, station.SoftwareVersion)
if len(got) != 3 || got["sw"] != station.SoftwareName || got["swVer"] != station.SoftwareVersion || got["projVer"] != station.SoftwareVersion {
t.Fatalf("software identity = %#v, want sw, swVer, and projVer %q", got, station.SoftwareName)
}
if _, ok := got["machineArch"]; ok {
t.Fatal("protocol 2.0 node information includes machineArch")
}
}