106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
package cluster
|
|
|
|
import (
|
|
"bufio"
|
|
"net"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// A node whose whole greeting is a bare "login:" — no newline, no banner — and
|
|
// which then demands a password must still log in.
|
|
//
|
|
// Reported on f5mzn.org:9000: telnet by hand worked, OpsLog sat at "connecting"
|
|
// for ever. Two faults met there. The prompts carry no newline, and the read
|
|
// loop only ever looked at complete LINES, so neither prompt was seen at all;
|
|
// and the blind 1.5 s login never recorded that it had sent the callsign, which
|
|
// left the password branch — gated on that flag — permanently switched off.
|
|
//
|
|
// The test speaks the node's side literally: "login:" with no newline, then
|
|
// "password:" with no newline, then a spot. It asserts both answers arrive and
|
|
// that the session reaches Connected without any welcome banner to lean on.
|
|
func TestBareLoginAndPasswordPromptsWithoutNewlines(t *testing.T) {
|
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer ln.Close()
|
|
|
|
host, portStr, _ := net.SplitHostPort(ln.Addr().String())
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
type answers struct{ login, pwd string }
|
|
got := make(chan answers, 1)
|
|
go func() {
|
|
c, err := ln.Accept()
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
rd := bufio.NewReader(c)
|
|
// No newline, exactly as the node sends it.
|
|
if _, err := c.Write([]byte("login: ")); err != nil {
|
|
return
|
|
}
|
|
call, err := rd.ReadString('\n')
|
|
if err != nil {
|
|
return
|
|
}
|
|
if _, err := c.Write([]byte("password: ")); err != nil {
|
|
return
|
|
}
|
|
pwd, err := rd.ReadString('\n')
|
|
if err != nil {
|
|
return
|
|
}
|
|
got <- answers{strings.TrimSpace(call), strings.TrimSpace(pwd)}
|
|
// Something to prove the link is live and parsing again afterwards.
|
|
_, _ = c.Write([]byte("DX de F4BPO: 14074.0 OY1CT FT8 1234Z\r\n"))
|
|
time.Sleep(time.Second)
|
|
}()
|
|
|
|
spots := make(chan Spot, 4)
|
|
s := &session{
|
|
cfg: ServerConfig{Name: "pwd node", Host: host, Port: port, Password: "s3cret"},
|
|
login: "F4BPO",
|
|
onSpot: func(sp Spot) { spots <- sp },
|
|
onLine: func(Line) {},
|
|
onStatus: func() {},
|
|
stopCh: make(chan struct{}),
|
|
}
|
|
done := make(chan error, 1)
|
|
go func() { _, err := s.runOnce(); done <- err }()
|
|
defer func() { close(s.stopCh); <-done }()
|
|
|
|
select {
|
|
case a := <-got:
|
|
if a.login != "F4BPO" {
|
|
t.Errorf("callsign sent = %q, want F4BPO", a.login)
|
|
}
|
|
if a.pwd != "s3cret" {
|
|
t.Errorf("password sent = %q, want s3cret — the prompt carried no newline", a.pwd)
|
|
}
|
|
case <-time.After(10 * time.Second):
|
|
t.Fatal("the node's newline-less prompts were never answered")
|
|
}
|
|
|
|
select {
|
|
case sp := <-spots:
|
|
if sp.DXCall != "OY1CT" {
|
|
t.Errorf("spot from the wrong station: %+v", sp)
|
|
}
|
|
case <-time.After(5 * time.Second):
|
|
t.Fatal("no spot after the login — the stream is not being parsed")
|
|
}
|
|
|
|
// Connected without a welcome banner: the handshake alone must be enough.
|
|
if st := s.snapshot().State; st != StateConnected {
|
|
t.Errorf("state = %q, want %q — the server would still show as connecting", st, StateConnected)
|
|
}
|
|
}
|