/** * Copyright - See the COPYRIGHT that is included with this distribution. * pvxs is distributed subject to a Software License Agreement found * in file LICENSE that is included with this distribution. */ #include #ifndef _WIN32 # include #endif #include #include #include #include #include #include #include #include #include #include #include namespace { using namespace pvxs; #ifdef _WIN32 # define poll WSAPoll # ifndef POLLIN # define POLLIN POLLRDNORM # endif #endif void testStackID() { testDiag("Enter %s", __func__); const char unknown[] = "Unknown?"; const char *name = unknown; switch(evsocket::ipstack) { #define CASE(NAME) case evsocket:: NAME : name = #NAME ; break CASE(Linsock); CASE(Winsock); CASE(GenericBSD); #undef CASE } testOk(name!=unknown, "Detected IP stack: %s", name); } void testEndPoint() { testDiag("Enter %s", __func__); { SockEndpoint ep("127.0.0.1"); testEq(ep.addr.tostring(), "127.0.0.1"); testEq(ep.iface, ""); testEq(ep.ttl, -1); } { SockEndpoint ep("127.0.0.1:12"); testEq(ep.addr.tostring(), "127.0.0.1:12"); testEq(ep.iface, ""); testEq(ep.ttl, -1); } { SockEndpoint ep("127.0.0.1,1"); testEq(ep.addr.tostring(), "127.0.0.1"); testEq(ep.iface, ""); testEq(ep.ttl, 1); } { SockEndpoint ep("127.0.0.1@ifname"); testEq(ep.addr.tostring(), "127.0.0.1"); testEq(ep.iface, "ifname"); testEq(ep.ttl, -1); } { SockEndpoint ep("127.0.0.1,1@ifname"); testEq(ep.addr.tostring(), "127.0.0.1"); testEq(ep.iface, "ifname"); testEq(ep.ttl, 1); } { SockEndpoint ep("127.0.0.1:12,1@ifname"); testEq(ep.addr.tostring(), "127.0.0.1:12"); testEq(ep.iface, "ifname"); testEq(ep.ttl, 1); } { SockEndpoint ep("[::ffff:192.168.1.1]"); testEq(ep.addr.tostring(), "[::ffff:192.168.1.1]"); testEq(ep.addr.map6to4().tostring(), "192.168.1.1"); testEq(ep.iface, ""); testEq(ep.ttl, -1); } { SockEndpoint ep("[1234::1]"); testEq(ep.addr.tostring(), "[1234::1]"); testEq(ep.addr.map6to4().tostring(), "[1234::1]"); testEq(ep.iface, ""); testEq(ep.ttl, -1); } std::vector bad({ "127.0.0.", "127.0.0.1:foo", "127.0.0.1:", "127.0.0.1:12,foo", //"127.0.0.1:12@", // should fail... "127.0.0.1:12,@", "127.0,0.1,1@ifname", }); for(const auto& inp : bad) { testThrows([&inp](){ SockEndpoint x(inp); (void)x; })<<" "<byIndex.empty())<<" found "<byIndex.size()<<" interfaces"; bool foundlo = false; const auto lo(SockAddr::loopback(AF_INET)); for(const auto& pair : ifs.current->byIndex) { auto& iface = pair.second; testDiag("Interface %u \"%s\"", unsigned(iface.index), iface.name.c_str()); for(const auto& pair : iface.addrs) { testDiag(" Address %s/%s", pair.first.tostring().c_str(), pair.second.tostring().c_str()); if(pair.first!=lo) continue; testTrue(!foundlo)<<" Found loopback with index "<0 && txbuf>0, "non-zero OS socket buffer sizes %zu, %zu\n", rxbuf, txbuf); } A.enable_IP_PKTINFO(); B.enable_IP_PKTINFO(); try{ A.bind(bind_addr); }catch(std::system_error& e){ if(af==AF_INET6 && e.code().value()==SOCK_EADDRNOTAVAIL) { testSkip(7, "No runtime IPv6 support"); return; } testAbort("Unable to bind %s : (%d) %s", bind_addr.tostring().c_str(), e.code().value(), e.what()); } testNotEq(bind_addr.port(), 0)<<"bound port"; SockAddr send_addr(bind_addr); send_addr.setPort(0); B.bind(send_addr); testNotEq(send_addr.port(), 0); testNotEq(send_addr.port(), bind_addr.port()); { uint8_t msg[] = {0x12, 0x34, 0x56, 0x78}; int ret = sendtox{B.sock, (char*)msg, sizeof(msg), &bind_addr}.call(); testOk(ret==(int)sizeof(msg), "Send test ret==%d(%d)", ret, EVUTIL_SOCKET_ERROR()); } SockAddr reply_to, reply_from; uint64_t reply_from_iface = 0; { testDiag("Call recvfrom()"); uint8_t rxbuf[8] = {}; SockAddr src, dest; auto rx(recvfromx{A.sock, (char*)rxbuf, sizeof(rxbuf), &src, &dest}); int ret = rx.call(); // only the destination address is captured, not the port if(dest.family()!=AF_UNSPEC) dest.setPort(bind_addr.port()); testOk(ret==4 && rxbuf[0]==0x12 && rxbuf[1]==0x34 && rxbuf[2]==0x56 && rxbuf[3]==0x78, "Recv'd %d(%d) [%u, %u, %u, %u]", ret, EVUTIL_SOCKET_ERROR(), rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); testEq(src, send_addr); testEq(dest, bind_addr); reply_to = src; reply_from = dest; reply_from_iface = rx.dstif; } { testDiag("Call sendto() with source addr override"); uint8_t msg[] = {0x9a, 0xbc, 0xde, 0xf0}; // reply to "src" from "dest" int ret = sendtox{A.sock, (char*)msg, sizeof(msg), &reply_to, &reply_from, reply_from_iface}.call(); testOk(ret==(int)sizeof(msg), "Send test ret==%d(%d)", ret, EVUTIL_SOCKET_ERROR()); } { testDiag("Call recvfrom()"); uint8_t rxbuf[8] = {}; SockAddr src, dest; auto rx(recvfromx{B.sock, (char*)rxbuf, sizeof(rxbuf), &src, &dest}); int ret = rx.call(); // only the destination address is captured, not the port if(dest.family()!=AF_UNSPEC) dest.setPort(send_addr.port()); testOk(ret==4 && rxbuf[0]==0x9a && rxbuf[1]==0xbc && rxbuf[2]==0xde && rxbuf[3]==0xf0, "Recv'd %d(%d) [%u, %u, %u, %u]", ret, EVUTIL_SOCKET_ERROR(), rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); testEq(src, reply_from); testEq(dest, reply_to); } } void test_local_mcast() { testDiag("Enter %s", __func__); auto ifinfo(IfaceMap::instance()); evsocket A(AF_INET, SOCK_DGRAM, 0, true), B(AF_INET, SOCK_DGRAM, 0, true); SockEndpoint mcast_addr("224.0.0.128,1@127.0.0.1"); // We could bind to mcast_addr on all targets except WIN32 SockAddr bind_addr(SockAddr::any(AF_INET)); A.enable_IP_PKTINFO(); A.bind(bind_addr); mcast_addr.addr.setPort(bind_addr.port()); SockAddr sender_addr(SockAddr::loopback(AF_INET)); B.bind(sender_addr); // receiving socket joins on the loopback interface A.mcast_join(mcast_addr.resolve()); // ignores port(s) // sending socket targets the loopback interface B.mcast_prep_sendto(mcast_addr); // ignores port(s) B.mcast_loop(true); uint8_t msg[] = {0x12, 0x34, 0x56, 0x78}; int ret = sendto(B.sock, (char*)msg, sizeof(msg), 0, &mcast_addr.addr->sa, mcast_addr.addr.size()); testEq(ret, (int)sizeof(msg))<<"Send test"; uint8_t rxbuf[8] = {}; SockAddr src; SockAddr dest; testDiag("Call recvfrom()"); recvfromx rx{A.sock, (char*)rxbuf, sizeof(rxbuf), &src, &dest}; ret = rx.call(); if(dest.family()==AF_INET) dest.setPort(mcast_addr.addr.port()); testTrue(ret>=0 && rx.dstif>0 && ifinfo.has_address(rx.dstif, sender_addr)) <<" received on index "<sa, mcast_addr.addr.size()); testEq(ret, int(msglen))<<" sendto("< "<in.sin_addr.s_addr==htonl(INADDR_LOOPBACK), "%08x == 0x7f000001", (unsigned)ntohl(val->in.sin_addr.s_addr)); } } void test_to_wire() { testDiag("Enter %s", __func__); { const uint32_t val = 0xdeadbeef; uint8_t buf[8+1]; FixedBuf pkt(true, buf); to_wire(pkt, val); testEq(pkt.size(), 4u); testOk1(pkt.good()); testOk(buf[0]==0xde && buf[1]==0xad && buf[2]==0xbe && buf[3]==0xef, "0x%02x%02x%02x%02x == 0xdeadbeef", buf[0], buf[1], buf[2], buf[3]); } { const uint32_t val = 0xdeadbeef; uint8_t buf[8+1]; FixedBuf pkt(false, buf); to_wire(pkt, val); testEq(pkt.size(), 4u); testOk1(pkt.good()); testOk(buf[0]==0xef && buf[1]==0xbe && buf[2]==0xad && buf[3]==0xde, "0x%02x%02x%02x%02x == 0xefbeadde", buf[0], buf[1], buf[2], buf[3]); } { const SockAddr val(SockAddr::loopback(AF_INET)); uint8_t buf[16+4+1]; FixedBuf pkt(true, buf); to_wire(pkt, val); testEq(pkt.size(), 4u); testOk1(pkt.good()); const uint8_t expect[16] = {0,0,0,0, 0,0,0,0, 0,0,0xff,0xff, 0x7f,0,0,1}; testOk1(std::memcmp(buf, expect, 16)==0); } { const uint32_t val = 0xdeadbeef; uint8_t buf[] = "\0\0\0\0\0\0\0\0"; FixedBuf pkt(true, buf, 2); to_wire(pkt, val); testEq(pkt.size(), 2u); testOk1(!pkt.good()); testOk(buf[0]==0 && buf[1]==0 && buf[2]==0 && buf[3]==0, "0x%02x%02x%02x%02x == 0", buf[0], buf[1], buf[2], buf[3]); } } } // namespace MAIN(testsock) { SockAttach attach; logger_config_env(); testPlan(109); testSetup(); testStackID(); testEndPoint(); // check for behavior when binding ipv4 and ipv6 to the same socket // as a function of socket type and order. if(evsocket::canIPv6) { // IPv4 and v6 loopback addresses are entirely distinct, // so no problem binding to both w/ or w/o IPV6_V6ONLY test_bind46("127.0.0.1" , "::1" , SOCK_DGRAM , 0); test_bind46("::1" , "127.0.0.1" , SOCK_DGRAM , 0); test_bind46("127.0.0.1" , "::1" , SOCK_STREAM , 0); test_bind46("::1" , "127.0.0.1" , SOCK_STREAM , 0); #if defined(_WIN32) || defined(__rtems__) test_bind46("0.0.0.0" , "::" , SOCK_DGRAM , 0); test_bind46("::" , "0.0.0.0" , SOCK_DGRAM , 0); test_bind46("0.0.0.0" , "::" , SOCK_STREAM , 0); test_bind46("::" , "0.0.0.0" , SOCK_STREAM , 0); #elif defined(__linux__) test_bind46("0.0.0.0" , "::" , SOCK_DGRAM , EADDRINUSE); test_bind46("::" , "0.0.0.0" , SOCK_DGRAM , EADDRINUSE); test_bind46("0.0.0.0" , "::" , SOCK_STREAM , EADDRINUSE); test_bind46("::" , "0.0.0.0" , SOCK_STREAM , EADDRINUSE); #else test_bind46("0.0.0.0" , "::" , SOCK_DGRAM , 0); test_bind46("::" , "0.0.0.0" , SOCK_DGRAM , EADDRINUSE); test_bind46("0.0.0.0" , "::" , SOCK_STREAM , 0); test_bind46("::" , "0.0.0.0" , SOCK_STREAM , EADDRINUSE); #endif } else { testSkip(8, "No IPv6 runtime support"); } test_ifacemap(); test_udp(AF_INET); try{ test_udp(AF_INET6); }catch(std::exception&e){ testAbort("test_udp6: %s", e.what()); } test_local_mcast(); test_mcast_scope(); test_from_wire(); test_to_wire(); testDiag("Done"); cleanup_for_valgrind(); return testDone(); }