Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,12 @@ void _fromDefs(Config& self, const std::map<std::string, std::string>& defs, boo
}

if(pickone({"EPICS_PVAS_INTF_ADDR_LIST"})) {
split_addr_into(pickone.name.c_str(), self.interfaces, pickone.val, self.tcp_port, true);
// defaultPort=0, matching the client EPICS_PVA_INTF_ADDR_LIST path below.
// A port-less interface stays at port 0 so the acceptor loop can follow the
// server onto the port actually bound -- including a conflict fallback chosen
// while binding the first interface. Filling in tcp_port here erases that
// "follow me" signal and strands the interface on the unbindable port.
split_addr_into(pickone.name.c_str(), self.interfaces, pickone.val, 0, true);
}

if(pickone({"EPICS_PVAS_IGNORE_ADDR_LIST"})) {
Expand Down
4 changes: 3 additions & 1 deletion test/testconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ void testDefs()
testEq(conf.tcp_port, 5678);
testFalse(conf.auto_beacon);
testEq(conf.beaconDestinations, std::vector<std::string>({"1.2.1.2:1234", "4.3.2.1:1234"}));
testEq(conf.interfaces, std::vector<std::string>({"1.1.1.1:5678", "1.2.3.4:5678"}));
// server INTF list round-trips port-less, matching the client path above
// (a port-less interface follows the server's bound TCP port at bind time).
testEq(conf.interfaces, std::vector<std::string>({"1.1.1.1", "1.2.3.4"}));
}
}

Expand Down
34 changes: 32 additions & 2 deletions test/testwild.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,39 @@ void testconflict(const char* addr)
testNotEq(iconf.tcp_port, fconf.tcp_port)<<"w/ "<<addr;
}

// as testconflict(), but configured as a user would.
// $EPICS_PVAS_INTF_ADDR_LIST fills in $EPICS_PVAS_SERVER_PORT for each interface
// which does not name a port, so the interfaces arrive here carrying the port
// which is about to be found in use. All of them must still follow the fallback.
void testconflictifaces(const char* addr1, const char* addr2)
{
testDiag("%s(\"%s\", \"%s\")", __func__, addr1, addr2);

evsocket otherserver(AF_INET, SOCK_STREAM, 0);
otherserver.bind(SockAddr::any(AF_INET));
otherserver.listen(4);

server::Config::defs_t defs;
defs["EPICS_PVAS_SERVER_PORT"] = SB()<<otherserver.sockname().port();
defs["EPICS_PVAS_BROADCAST_PORT"] = "0"; // choose randomly
defs["EPICS_PVAS_INTF_ADDR_LIST"] = SB()<<addr1<<' '<<addr2;

server::Config iconf;
iconf.applyDefs(defs);

auto serv(iconf.build());
auto fconf(serv.config());
testShow()<<"Server Config\n"<<fconf;

testNotEq(0u, fconf.udp_port);
testNotEq(iconf.tcp_port, fconf.tcp_port);
}

} // namespace

MAIN(testwild)
{
testPlan(18);
testPlan(20);
testSetup();
logger_config_env();
SockAttach attach;
Expand All @@ -98,8 +126,10 @@ MAIN(testwild)
}
if(evsocket::ipstack==evsocket::Linsock) {
testconflict("127.0.0.1");
// 127.0.0.0/8 is entirely local, so a second loopback address is available.
testconflictifaces("127.0.0.1", "127.0.0.2");
} else {
testSkip(2, "Can bind both 0.0.0.0 and 127.0.0.1 to the same port");
testSkip(4, "Can bind both 0.0.0.0 and 127.0.0.1 to the same port, and have one loopback address");
}
testconflict("0.0.0.0");
if(canIPv6) {
Expand Down
Loading