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
+66 -27
View File
@@ -15,34 +15,38 @@ import (
)
type loaded struct {
host string
port int
callsign string
authKey string
talkGroup int
enableLogging bool
logPath string
logNotice string
host string
port int
callsign string
authKey string
talkGroup int
certificateDir string
certFatal string
enableLogging bool
logPath string
logNotice string
}
type fileShape struct {
Host jsontext.Value `json:"host"`
Port jsontext.Value `json:"port"`
Callsign jsontext.Value `json:"callsign"`
AuthKey jsontext.Value `json:"authKey"`
TalkGroup jsontext.Value `json:"talkGroup"`
EnableLogging jsontext.Value `json:"enableLogging"`
LogFile jsontext.Value `json:"logFile"`
Host jsontext.Value `json:"host"`
Port jsontext.Value `json:"port"`
Callsign jsontext.Value `json:"callsign"`
AuthKey jsontext.Value `json:"authKey"`
TalkGroup jsontext.Value `json:"talkGroup"`
CertificateDir jsontext.Value `json:"certificateDir"`
EnableLogging jsontext.Value `json:"enableLogging"`
LogFile jsontext.Value `json:"logFile"`
}
type templateFile struct {
Host string `json:"host"`
Port int `json:"port"`
Callsign string `json:"callsign"`
AuthKey string `json:"authKey"`
TalkGroup int `json:"talkGroup"`
EnableLogging bool `json:"enableLogging"`
LogFile string `json:"logFile"`
Host string `json:"host"`
Port int `json:"port"`
Callsign string `json:"callsign"`
AuthKey string `json:"authKey"`
TalkGroup int `json:"talkGroup"`
CertificateDir string `json:"certificateDir"`
EnableLogging bool `json:"enableLogging"`
LogFile string `json:"logFile"`
}
var callsignPattern = regexp.MustCompile(`^[A-Z0-9][A-Z]{0,2}\d[A-Z0-9]{0,3}[A-Z](?:-[A-Z0-9]{1,3})?$`)
@@ -66,7 +70,7 @@ func writeTemplate(path string) error {
if err != nil {
return err
}
body, err := jsonv2.Marshal(templateFile{LogFile: logFile})
body, err := jsonv2.Marshal(templateFile{CertificateDir: "./funknetz", LogFile: logFile})
if err != nil {
return err
}
@@ -91,15 +95,19 @@ func parseConfig(path string, body []byte) (loaded, error) {
if err := jsonv2.Unmarshal(body, &shape); err != nil {
return loaded{}, fmt.Errorf("the file could not be read: %w", err)
}
if block, notice := blockedLog(shape); block {
certBad := present(shape.CertificateDir) && shape.CertificateDir.Kind() != jsontext.KindString
if block, notice := blockedLog(shape); block || certBad {
cfg, err := parseOperational(shape)
if err != nil {
return loaded{}, err
}
cfg.logNotice = notice
return cfg, nil
if !block {
cfg = applyLog(cfg, shape)
}
return applyCertificate(cfg, shape, path), nil
}
if missingLog(shape) {
if missingCertificate(shape) || missingLog(shape) {
if !operationalKindsOK(shape) {
return parseOperational(shape)
}
@@ -116,7 +124,7 @@ func parseConfig(path string, body []byte) (loaded, error) {
if err != nil {
return loaded{}, err
}
return applyLog(cfg, shape), nil
return applyLog(applyCertificate(cfg, shape, path), shape), nil
}
func parseOperational(shape fileShape) (loaded, error) {
@@ -217,6 +225,10 @@ func missingLog(shape fileShape) bool {
return !present(shape.EnableLogging) || !present(shape.LogFile)
}
func missingCertificate(shape fileShape) bool {
return !present(shape.CertificateDir)
}
func operationalKindsOK(shape fileShape) bool {
if present(shape.Host) && shape.Host.Kind() != jsontext.KindString {
return false
@@ -259,6 +271,7 @@ func withLogDefaults(shape fileShape) (fileShape, error) {
shape.Callsign = rawOr(shape.Callsign, `""`)
shape.AuthKey = rawOr(shape.AuthKey, `""`)
shape.TalkGroup = rawOr(shape.TalkGroup, "0")
shape.CertificateDir = rawOr(shape.CertificateDir, `"./funknetz"`)
shape.EnableLogging = rawOr(shape.EnableLogging, "false")
if !present(shape.LogFile) {
logFile, err := defaultLogPath()
@@ -316,6 +329,32 @@ func rewriteConfig(path string, shape fileShape) error {
return nil
}
func applyCertificate(cfg loaded, shape fileShape, configPath string) loaded {
if !present(shape.CertificateDir) {
cfg.certificateDir = resolveCertDir(configPath, "./funknetz")
return cfg
}
dir, err := stringField("certificateDir", shape.CertificateDir)
if err != nil {
cfg.certFatal = err.Error()
return cfg
}
if strings.TrimSpace(dir) == "" {
cfg.certFatal = "certificateDir is empty"
return cfg
}
cfg.certificateDir = resolveCertDir(configPath, dir)
return cfg
}
func resolveCertDir(configPath, dir string) string {
dir = strings.TrimSpace(dir)
if filepath.IsAbs(dir) {
return filepath.Clean(dir)
}
return filepath.Clean(filepath.Join(filepath.Dir(configPath), dir))
}
func applyLog(cfg loaded, shape fileShape) loaded {
if shape.EnableLogging.Kind() != jsontext.KindTrue {
return cfg