138 lines
3.8 KiB
Go
138 lines
3.8 KiB
Go
package ca
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/uopi/goca/proto"
|
|
)
|
|
|
|
// buildOldSearchReply builds a CA_PROTO_SEARCH UDP reply using the old
|
|
// payload format (CA < 4.9): [minor_version(2)][pad(2)][IP(4)].
|
|
// When ip is nil, the IP field is 0xFFFFFFFF (use-sender convention).
|
|
func buildOldSearchReply(searchID uint32, serverIP net.IP, port int) []byte {
|
|
payload := make([]byte, 8)
|
|
payload[0] = 0x00
|
|
payload[1] = proto.MinorVersion // e.g. 0x0D = 13
|
|
// payload[2:4] = 0x00 0x00 (pad)
|
|
if serverIP == nil {
|
|
payload[4] = 0xFF
|
|
payload[5] = 0xFF
|
|
payload[6] = 0xFF
|
|
payload[7] = 0xFF
|
|
} else {
|
|
copy(payload[4:], serverIP.To4())
|
|
}
|
|
h := proto.Header{
|
|
Command: proto.CmdSearch,
|
|
DataType: uint16(port),
|
|
Parameter1: proto.MinorVersion,
|
|
Parameter2: searchID,
|
|
}
|
|
return proto.BuildMessage(h, payload)
|
|
}
|
|
|
|
// TestParseReplyOldFormatAllFF verifies the old format with 0xFFFFFFFF IP.
|
|
func TestParseReplyOldFormatAllFF(t *testing.T) {
|
|
se := &searchEngine{waiters: make(map[uint32]*searchWaiter)}
|
|
const searchID = uint32(42)
|
|
result := make(chan searchResult, 1)
|
|
se.mu.Lock()
|
|
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV", ch: result}
|
|
se.mu.Unlock()
|
|
|
|
pkt := buildOldSearchReply(searchID, nil, 5064) // nil → 0xFFFFFFFF
|
|
src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064}
|
|
se.parseReply(pkt, src)
|
|
|
|
select {
|
|
case r := <-result:
|
|
if r.addr != "127.0.0.1:5064" {
|
|
t.Errorf("addr = %q, want 127.0.0.1:5064", r.addr)
|
|
}
|
|
default:
|
|
t.Fatal("parseReply did not deliver a result")
|
|
}
|
|
}
|
|
|
|
// TestParseReplyOldFormatAllZero verifies the old format with 0.0.0.0 IP
|
|
// (used by some EPICS Base versions to also mean "use sender IP").
|
|
func TestParseReplyOldFormatAllZero(t *testing.T) {
|
|
se := &searchEngine{waiters: make(map[uint32]*searchWaiter)}
|
|
const searchID = uint32(43)
|
|
result := make(chan searchResult, 1)
|
|
se.mu.Lock()
|
|
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV", ch: result}
|
|
se.mu.Unlock()
|
|
|
|
pkt := buildOldSearchReply(searchID, net.IPv4(0, 0, 0, 0).To4(), 5064)
|
|
src := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 5064}
|
|
se.parseReply(pkt, src)
|
|
|
|
select {
|
|
case r := <-result:
|
|
if r.addr != "127.0.0.1:5064" {
|
|
t.Errorf("addr = %q, want 127.0.0.1:5064", r.addr)
|
|
}
|
|
default:
|
|
t.Fatal("parseReply did not deliver a result")
|
|
}
|
|
}
|
|
|
|
// TestParseReplyOldFormatExplicitIP verifies that an explicit server IP in
|
|
// the old payload format is read from the correct offset (bytes [4:8]).
|
|
func TestParseReplyOldFormatExplicitIP(t *testing.T) {
|
|
se := &searchEngine{
|
|
waiters: make(map[uint32]*searchWaiter),
|
|
}
|
|
|
|
const searchID = uint32(7)
|
|
result := make(chan searchResult, 1)
|
|
se.mu.Lock()
|
|
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV2", ch: result}
|
|
se.mu.Unlock()
|
|
|
|
explicitIP := net.ParseIP("10.0.0.5").To4()
|
|
pkt := buildOldSearchReply(searchID, explicitIP, 5064)
|
|
src := &net.UDPAddr{IP: net.ParseIP("10.0.0.5"), Port: 5064}
|
|
|
|
se.parseReply(pkt, src)
|
|
|
|
select {
|
|
case r := <-result:
|
|
if r.addr != "10.0.0.5:5064" {
|
|
t.Errorf("addr = %q, want 10.0.0.5:5064", r.addr)
|
|
}
|
|
default:
|
|
t.Fatal("parseReply did not deliver a result")
|
|
}
|
|
}
|
|
|
|
// TestParseReplyNewFormat verifies the new payload format (IP at [0:4]).
|
|
func TestParseReplyNewFormat(t *testing.T) {
|
|
se := &searchEngine{
|
|
waiters: make(map[uint32]*searchWaiter),
|
|
}
|
|
|
|
const searchID = uint32(99)
|
|
result := make(chan searchResult, 1)
|
|
se.mu.Lock()
|
|
se.waiters[searchID] = &searchWaiter{pvName: "TEST:PV3", ch: result}
|
|
se.mu.Unlock()
|
|
|
|
// New format: IP = 0xFFFFFFFF at [0:4].
|
|
pkt := EncodeSearchReply(searchID, nil, 5064)
|
|
src := &net.UDPAddr{IP: net.ParseIP("192.168.1.10"), Port: 5064}
|
|
|
|
se.parseReply(pkt, src)
|
|
|
|
select {
|
|
case r := <-result:
|
|
if r.addr != "192.168.1.10:5064" {
|
|
t.Errorf("addr = %q, want 192.168.1.10:5064", r.addr)
|
|
}
|
|
default:
|
|
t.Fatal("parseReply did not deliver a result")
|
|
}
|
|
}
|