diff --git a/daemon/core/face-uri.cpp b/daemon/core/face-uri.cpp index cc6c8c8f..14751d65 100644 --- a/daemon/core/face-uri.cpp +++ b/daemon/core/face-uri.cpp @@ -6,6 +6,10 @@ #include "face-uri.hpp" #include "core/logger.hpp" +#ifdef HAVE_PCAP +#include "face/ethernet.hpp" +#endif // HAVE_PCAP + #include NFD_LOG_INIT("FaceUri"); @@ -35,30 +39,35 @@ FaceUri::parse(const std::string& uri) m_port.clear(); m_path.clear(); - boost::regex protocolExp("(\\w+\\d?)://([^/]*)(\\/[^?]*)?"); + static const boost::regex protocolExp("(\\w+\\d?)://([^/]*)(\\/[^?]*)?"); boost::smatch protocolMatch; if (!boost::regex_match(uri, protocolMatch, protocolExp)) { return false; } m_scheme = protocolMatch[1]; + const std::string& authority = protocolMatch[2]; m_path = protocolMatch[3]; - const std::string& authority = protocolMatch[2]; + // pattern for IPv6 address enclosed in [ ], with optional port number + static const boost::regex v6Exp("^\\[([a-fA-F0-9:]+)\\](?:\\:(\\d+))?$"); + // pattern for Ethernet address in standard hex-digits-and-colons notation + static const boost::regex etherExp("^((?:[a-fA-F0-9]{1,2}\\:){5}(?:[a-fA-F0-9]{1,2}))$"); + // pattern for IPv4/hostname/fd/ifname, with optional port number + static const boost::regex v4HostExp("^([^:]+)(?:\\:(\\d+))?$"); - boost::regex v6Exp("^\\[(([a-fA-F0-9:]+))\\](:(\\d+))?$"); // [host]:port - boost::regex v4Exp("^((\\d+\\.){3}\\d+)(:(\\d+))?$"); - boost::regex hostExp("^(([^:]*))(:(\\d+))?$"); // host:port - - boost::smatch match; - m_isV6 = boost::regex_match(authority, match, v6Exp); - if (m_isV6 || - boost::regex_match(authority, match, v4Exp) || - boost::regex_match(authority, match, hostExp)) { - m_host = match[1]; - m_port = match[4]; + if (authority.empty()) { + // UNIX, internal } else { - if (m_path.empty()) { + boost::smatch match; + m_isV6 = boost::regex_match(authority, match, v6Exp); + if (m_isV6 || + boost::regex_match(authority, match, etherExp) || + boost::regex_match(authority, match, v4HostExp)) { + m_host = match[1]; + m_port = match[2]; + } + else { return false; } } @@ -84,11 +93,40 @@ FaceUri::FaceUri(const boost::asio::ip::udp::endpoint& endpoint) m_port = boost::lexical_cast(endpoint.port()); } +#ifdef HAVE_UNIX_SOCKETS FaceUri::FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint) : m_isV6(false) { m_scheme = "unix"; m_path = endpoint.path(); } +#endif // HAVE_UNIX_SOCKETS + +FaceUri +FaceUri::fromFd(int fd) +{ + FaceUri uri; + uri.m_scheme = "fd"; + uri.m_host = boost::lexical_cast(fd); + return uri; +} + +#ifdef HAVE_PCAP +FaceUri::FaceUri(const ethernet::Address& address) + : m_isV6(false) +{ + m_scheme = "ether"; + m_host = address.toString(':'); +} +#endif // HAVE_PCAP + +FaceUri +FaceUri::fromDev(const std::string& ifname) +{ + FaceUri uri; + uri.m_scheme = "dev"; + uri.m_host = ifname; + return uri; +} } // namespace nfd diff --git a/daemon/core/face-uri.hpp b/daemon/core/face-uri.hpp index 29647bbb..fa0a62ac 100644 --- a/daemon/core/face-uri.hpp +++ b/daemon/core/face-uri.hpp @@ -11,22 +11,34 @@ namespace nfd { -/// represents a URI in Face Management protocol +#ifdef HAVE_PCAP +namespace ethernet { +class Address; +} // namespace ethernet +#endif // HAVE_PCAP + +/** \brief represents the underlying protocol and address used by a Face + * \sa http://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#FaceUri + */ class FaceUri { public: - class Error : public std::runtime_error + class Error : public std::invalid_argument { public: - Error(const std::string& what) : std::runtime_error(what) {} + explicit + Error(const std::string& what) + : std::invalid_argument(what) + { + } }; FaceUri(); /** \brief construct by parsing * - * \param uri scheme://domain[:port]/path - * \throw FaceUri::Error if URI cannot be parsed + * \param uri scheme://host[:port]/path + * \throw FaceUri::Error if URI cannot be parsed */ explicit FaceUri(const std::string& uri); @@ -36,19 +48,40 @@ public: explicit FaceUri(const char* uri); - explicit - FaceUri(const boost::asio::ip::tcp::endpoint& endpoint); - - explicit - FaceUri(const boost::asio::ip::udp::endpoint& endpoint); - - explicit - FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint); - /// exception-safe parsing bool parse(const std::string& uri); +public: // scheme-specific construction + /// construct tcp4 or tcp6 canonical FaceUri + explicit + FaceUri(const boost::asio::ip::tcp::endpoint& endpoint); + + /// construct udp4 or udp6 canonical FaceUri + explicit + FaceUri(const boost::asio::ip::udp::endpoint& endpoint); + +#ifdef HAVE_UNIX_SOCKETS + /// construct unix canonical FaceUri + explicit + FaceUri(const boost::asio::local::stream_protocol::endpoint& endpoint); +#endif // HAVE_UNIX_SOCKETS + + /// create fd FaceUri from file descriptor + static FaceUri + fromFd(int fd); + +#ifdef HAVE_PCAP + /// construct ether canonical FaceUri + explicit + FaceUri(const ethernet::Address& address); +#endif // HAVE_PCAP + + /// create dev FaceUri from network device name + static FaceUri + fromDev(const std::string& ifname); + +public: // getters /// get scheme (protocol) const std::string& getScheme() const; @@ -76,12 +109,13 @@ private: bool m_isV6; std::string m_port; std::string m_path; - + friend std::ostream& operator<<(std::ostream& os, const FaceUri& uri); }; inline FaceUri::FaceUri() + : m_isV6(false) { } diff --git a/tests/core/face-uri.cpp b/tests/core/face-uri.cpp index 7eb9b62a..80d5a894 100644 --- a/tests/core/face-uri.cpp +++ b/tests/core/face-uri.cpp @@ -5,6 +5,9 @@ */ #include "core/face-uri.hpp" +#ifdef HAVE_PCAP +#include "face/ethernet.hpp" +#endif // HAVE_PCAP #include "tests/test-common.hpp" @@ -13,7 +16,21 @@ namespace tests { BOOST_FIXTURE_TEST_SUITE(CoreFaceUri, BaseFixture) -BOOST_AUTO_TEST_CASE(Basic) +BOOST_AUTO_TEST_CASE(Internal) +{ + FaceUri uri; + + BOOST_CHECK(uri.parse("internal://")); + BOOST_CHECK_EQUAL(uri.getScheme(), "internal"); + BOOST_CHECK_EQUAL(uri.getHost(), ""); + BOOST_CHECK_EQUAL(uri.getPort(), ""); + BOOST_CHECK_EQUAL(uri.getPath(), ""); + + BOOST_CHECK_EQUAL(uri.parse("internal:"), false); + BOOST_CHECK_EQUAL(uri.parse("internal:/"), false); +} + +BOOST_AUTO_TEST_CASE(Udp) { BOOST_CHECK_NO_THROW(FaceUri("udp://hostname:6363")); BOOST_CHECK_THROW(FaceUri("udp//hostname:6363"), FaceUri::Error); @@ -21,7 +38,7 @@ BOOST_AUTO_TEST_CASE(Basic) FaceUri uri; BOOST_CHECK_EQUAL(uri.parse("udp//hostname:6363"), false); - + BOOST_CHECK(uri.parse("udp://hostname:80")); BOOST_CHECK_EQUAL(uri.getScheme(), "udp"); BOOST_CHECK_EQUAL(uri.getHost(), "hostname"); @@ -39,29 +56,116 @@ BOOST_AUTO_TEST_CASE(Basic) BOOST_CHECK_EQUAL(uri.getHost(), "2001:db8:3f9:0::1"); BOOST_CHECK_EQUAL(uri.getPort(), "6363"); BOOST_CHECK_EQUAL(uri.getPath(), ""); - + BOOST_CHECK(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86d3]:6363")); BOOST_CHECK_EQUAL(uri.getScheme(), "udp6"); BOOST_CHECK_EQUAL(uri.getHost(), "2001:db8:3f9:0:3025:ccc5:eeeb:86d3"); BOOST_CHECK_EQUAL(uri.getPort(), "6363"); BOOST_CHECK_EQUAL(uri.getPath(), ""); + BOOST_CHECK_EQUAL(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86dg]:6363"), false); + + using namespace boost::asio; + ip::udp::endpoint endpoint4(ip::address_v4::from_string("192.0.2.1"), 7777); + BOOST_REQUIRE_NO_THROW(FaceUri(endpoint4)); + BOOST_CHECK_EQUAL(FaceUri(endpoint4).toString(), "udp4://192.0.2.1:7777"); + + ip::udp::endpoint endpoint6(ip::address_v6::from_string("2001:DB8::1"), 7777); + BOOST_REQUIRE_NO_THROW(FaceUri(endpoint6)); + BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "udp6://[2001:db8::1]:7777"); +} + +BOOST_AUTO_TEST_CASE(Tcp) +{ + FaceUri uri; + BOOST_CHECK(uri.parse("tcp://random.host.name")); BOOST_CHECK_EQUAL(uri.getScheme(), "tcp"); BOOST_CHECK_EQUAL(uri.getHost(), "random.host.name"); BOOST_CHECK_EQUAL(uri.getPort(), ""); BOOST_CHECK_EQUAL(uri.getPath(), ""); + BOOST_CHECK_EQUAL(uri.parse("tcp://192.0.2.1:"), false); + BOOST_CHECK_EQUAL(uri.parse("tcp://[::zzzz]"), false); + + using namespace boost::asio; + ip::tcp::endpoint endpoint4(ip::address_v4::from_string("192.0.2.1"), 7777); + BOOST_REQUIRE_NO_THROW(FaceUri(endpoint4)); + BOOST_CHECK_EQUAL(FaceUri(endpoint4).toString(), "tcp4://192.0.2.1:7777"); + + ip::tcp::endpoint endpoint6(ip::address_v6::from_string("2001:DB8::1"), 7777); + BOOST_REQUIRE_NO_THROW(FaceUri(endpoint6)); + BOOST_CHECK_EQUAL(FaceUri(endpoint6).toString(), "tcp6://[2001:db8::1]:7777"); +} + +BOOST_AUTO_TEST_CASE(Unix) +{ + FaceUri uri; + BOOST_CHECK(uri.parse("unix:///var/run/example.sock")); BOOST_CHECK_EQUAL(uri.getScheme(), "unix"); BOOST_CHECK_EQUAL(uri.getHost(), ""); BOOST_CHECK_EQUAL(uri.getPort(), ""); BOOST_CHECK_EQUAL(uri.getPath(), "/var/run/example.sock"); - BOOST_CHECK_EQUAL(uri.parse("tcp://192.0.2.1:"), false); - BOOST_CHECK_EQUAL(uri.parse("tcp://[::zzzz]"), false); - BOOST_CHECK_EQUAL(uri.parse("udp6://[2001:db8:3f9:0:3025:ccc5:eeeb:86dg]:6363"), false); + //BOOST_CHECK_EQUAL(uri.parse("unix://var/run/example.sock"), false); + // This is not a valid unix:/ URI, but the parse would treat "var" as host + +#ifdef HAVE_UNIX_SOCKETS + using namespace boost::asio; + local::stream_protocol::endpoint endpoint("/var/run/example.sock"); + BOOST_REQUIRE_NO_THROW(FaceUri(endpoint)); + BOOST_CHECK_EQUAL(FaceUri(endpoint).toString(), "unix:///var/run/example.sock"); +#endif // HAVE_UNIX_SOCKETS +} + +BOOST_AUTO_TEST_CASE(Fd) +{ + FaceUri uri; + + BOOST_CHECK(uri.parse("fd://6")); + BOOST_CHECK_EQUAL(uri.getScheme(), "fd"); + BOOST_CHECK_EQUAL(uri.getHost(), "6"); + BOOST_CHECK_EQUAL(uri.getPort(), ""); BOOST_CHECK_EQUAL(uri.getPath(), ""); + + int fd = 21; + BOOST_REQUIRE_NO_THROW(FaceUri::fromFd(fd)); + BOOST_CHECK_EQUAL(FaceUri::fromFd(fd).toString(), "fd://21"); +} + +BOOST_AUTO_TEST_CASE(Ether) +{ + FaceUri uri; + + BOOST_CHECK(uri.parse("ether://08:00:27:01:dd:01")); + BOOST_CHECK_EQUAL(uri.getScheme(), "ether"); + BOOST_CHECK_EQUAL(uri.getHost(), "08:00:27:01:dd:01"); + BOOST_CHECK_EQUAL(uri.getPort(), ""); + BOOST_CHECK_EQUAL(uri.getPath(), ""); + + BOOST_CHECK_EQUAL(uri.parse("ether://08:00:27:zz:dd:01"), false); + +#ifdef HAVE_PCAP + ethernet::Address address = ethernet::Address::fromString("33:33:01:01:01:01"); + BOOST_REQUIRE_NO_THROW(FaceUri(address)); + BOOST_CHECK_EQUAL(FaceUri(address).toString(), "ether://33:33:01:01:01:01"); +#endif // HAVE_PCAP +} + +BOOST_AUTO_TEST_CASE(Dev) +{ + FaceUri uri; + + BOOST_CHECK(uri.parse("dev://eth0")); + BOOST_CHECK_EQUAL(uri.getScheme(), "dev"); + BOOST_CHECK_EQUAL(uri.getHost(), "eth0"); + BOOST_CHECK_EQUAL(uri.getPort(), ""); + BOOST_CHECK_EQUAL(uri.getPath(), ""); + + std::string ifname = "en1"; + BOOST_REQUIRE_NO_THROW(FaceUri::fromDev(ifname)); + BOOST_CHECK_EQUAL(FaceUri::fromDev(ifname).toString(), "dev://en1"); } BOOST_AUTO_TEST_SUITE_END()