diff --git a/prototype2/common/Socket.cpp b/prototype2/common/Socket.cpp index 52592d49fe3903c269258f3e396337337ce1c8ee..2d89c61f76be3623a54d2c30e91493b16145092b 100644 --- a/prototype2/common/Socket.cpp +++ b/prototype2/common/Socket.cpp @@ -17,6 +17,23 @@ #define SEND_FLAGS 0 #endif + +bool Socket::isValidIp(std::string ipAddress) { + struct sockaddr_in SockAddr; + return inet_pton(AF_INET, ipAddress.c_str(), &(SockAddr.sin_addr)) != 0; +} + +std::string Socket::getHostByName(std::string &name) { + hostent * HostEntry = gethostbyname(name.c_str()); + if (HostEntry == nullptr) { + throw std::runtime_error(fmt::format("Unable to resolve hostname {}", name)); + } else { // Just return the first entry + auto IpAddress = inet_ntoa(*reinterpret_cast<in_addr*>(HostEntry->h_addr_list[0])); + LOG(IPC, Sev::Info, "Hostname resolved to {}", IpAddress); + return IpAddress; + } +} + Socket::Socket(Socket::type stype) { auto type = (stype == Socket::type::UDP) ? SOCK_DGRAM : SOCK_STREAM; auto proto = (stype == Socket::type::UDP) ? IPPROTO_UDP : IPPROTO_TCP; @@ -70,28 +87,30 @@ int Socket::setNOSIGPIPE() { #endif } -void Socket::setLocalSocket(const char *ipaddr, int port) { +void Socket::setLocalSocket(const std::string ipaddr, int port) { // zero out the structures struct sockaddr_in localSockAddr; std::memset((char *)&localSockAddr, 0, sizeof(localSockAddr)); localSockAddr.sin_family = AF_INET; localSockAddr.sin_port = htons(port); - int ret = inet_aton(ipaddr, &localSockAddr.sin_addr); + int ret = inet_aton(ipaddr.c_str(), &localSockAddr.sin_addr); if (ret == 0) { - LOG(IPC, Sev::Error, "invalid ip address {}", ipaddr); - throw std::runtime_error("setLocalSocket() - invalid ip"); + auto Msg = fmt::format("setLocalSocket() - invalid ip address {}", ipaddr); + LOG(IPC, Sev::Error, Msg); + throw std::runtime_error(Msg); } // bind socket to port ret = bind(SocketFileDescriptor, (struct sockaddr *)&localSockAddr, sizeof(localSockAddr)); if (ret != 0) { - LOG(IPC, Sev::Error, "bind failed - is port {} already in use?", port); - throw std::runtime_error("setLocalSocket() - bind() failed"); + auto Msg = fmt::format("setLocalSocket(): bind failed, is port {} already in use?", port); + LOG(IPC, Sev::Error, Msg); + throw std::runtime_error(Msg); } } -void Socket::setRemoteSocket(const char *ipaddr, int port) { +void Socket::setRemoteSocket(const std::string ipaddr, int port) { RemoteIp = ipaddr; RemotePort = port; // zero out the structures @@ -99,10 +118,11 @@ void Socket::setRemoteSocket(const char *ipaddr, int port) { remoteSockAddr.sin_family = AF_INET; remoteSockAddr.sin_port = htons(port); - int ret = inet_aton(ipaddr, &remoteSockAddr.sin_addr); + int ret = inet_aton(ipaddr.c_str(), &remoteSockAddr.sin_addr); if (ret == 0) { - LOG(IPC, Sev::Error, "invalid ip address {}", ipaddr); - throw std::runtime_error("setRemoteSocket() - invalid ip"); + auto Msg = fmt::format("etRemoteSocket(): invalid ip address {}", ipaddr); + LOG(IPC, Sev::Error, Msg); + throw std::runtime_error(Msg); } } @@ -112,7 +132,7 @@ int Socket::connectToRemote() { std::memset((char *)&remoteSockAddr, 0, sizeof(remoteSockAddr)); remoteSockAddr.sin_family = AF_INET; remoteSockAddr.sin_port = htons(RemotePort); - int ret = inet_aton(RemoteIp, &remoteSockAddr.sin_addr); + int ret = inet_aton(RemoteIp.c_str(), &remoteSockAddr.sin_addr); if (ret == 0) { LOG(IPC, Sev::Error, "invalid ip address {}", RemoteIp); throw std::runtime_error("connectToRemote() - invalid ip"); @@ -176,7 +196,7 @@ bool Socket::isValidSocket() { /// /// /// -TCPTransmitter::TCPTransmitter(const char *ipaddr, int port) : Socket(Socket::type::TCP) { +TCPTransmitter::TCPTransmitter(const std::string ipaddr, int port) : Socket(Socket::type::TCP) { setRemoteSocket(ipaddr, port); setNOSIGPIPE(); connectToRemote(); diff --git a/prototype2/common/Socket.h b/prototype2/common/Socket.h index 6e1bd287a00b795a41bf682a04d6ba500ddd6af3..2616f35d81f3938466a5ff6e8fdfd9d6e0c6c276 100644 --- a/prototype2/common/Socket.h +++ b/prototype2/common/Socket.h @@ -13,6 +13,7 @@ #include <cassert> #include <cinttypes> #include <sys/socket.h> +#include <netinet/ip.h> /// BSD Socket abstractions for TCP and UDP transmitters and receivers class Socket { @@ -22,12 +23,22 @@ public: class Endpoint { public: - const char *ipaddr; + const std::string ipaddr; uint16_t port; - Endpoint(const char *ip_address, uint16_t port_number) + Endpoint(const std::string ip_address, uint16_t port_number) : ipaddr(ip_address), port(port_number) {} }; + /// \brief Is this a dotted quad ip address? + /// Valid addresses must be of the form 'a.b.d.c' where + /// a-d can range from 0 to 255 + static bool isValidIp(std::string ipAddress); + + /// \brief Return dotted quad by resolving hostname + /// Essentially a wrapper for gethostbyname() returning + /// the first entry in the ip address table + static std::string getHostByName(std::string &name); + /// Create a socker abstraction of type UDP or TCP Socket(Socket::type type); @@ -47,10 +58,10 @@ public: int setNOSIGPIPE(); /// Specify ip address of interface to receive data on and port number to listen on - void setLocalSocket(const char *ipaddr, int port); + void setLocalSocket(const std::string ipaddr, int port); /// Specify ip address and port number of remote end - void setRemoteSocket(const char *ipaddr, int port); + void setRemoteSocket(const std::string ipaddr, int port); /// Connect (TCP only) to remote endpoint int connectToRemote(); @@ -61,12 +72,12 @@ public: /// Send data in buffer with specified length int send(void *dataBuffer, int dataLength); - /// \brief To check is data can be transmitted or received + /// \brief To check if data can be transmitted or received bool isValidSocket(); private: int SocketFileDescriptor{-1}; - const char * RemoteIp; + std::string RemoteIp; int RemotePort; struct sockaddr_in remoteSockAddr; @@ -97,7 +108,7 @@ public: class TCPTransmitter : public Socket { public: /// - TCPTransmitter(const char *ip, int port); + TCPTransmitter(const std::string ip, int port); /// int senddata(char *buffer, int len); diff --git a/prototype2/common/StatPublisher.cpp b/prototype2/common/StatPublisher.cpp index 05777679482aca49bf2ac1577099c853a88145c1..c2d9b5d35b0120321e8b14c5e5647d524ef685dc 100644 --- a/prototype2/common/StatPublisher.cpp +++ b/prototype2/common/StatPublisher.cpp @@ -11,6 +11,9 @@ /// StatPublisher::StatPublisher(std::string ip, int port) :IpAddress(ip), TCPPort(port) { + if (not Socket::isValidIp(IpAddress)) { + IpAddress = Socket::getHostByName(IpAddress); + } StatDb.reset(new TCPTransmitter(IpAddress.c_str(), TCPPort)); } diff --git a/prototype2/common/StatPublisher.h b/prototype2/common/StatPublisher.h index c4d194f3046e2688bff919f90687dc4a6cda038d..624ca09e06ccc6ae86ea43e173210347e599577a 100644 --- a/prototype2/common/StatPublisher.h +++ b/prototype2/common/StatPublisher.h @@ -17,7 +17,7 @@ class StatPublisher { public: - /// \brief Connect to a Carbon/Graphite server bu ip address and tcp port + /// \brief Connect to a Carbon/Graphite server by ip address/hostname and tcp port StatPublisher(std::string ip, int port); /// \brief Send detector metrics to Carbon/Graphite server given additional stats diff --git a/prototype2/common/test/SocketTest.cpp b/prototype2/common/test/SocketTest.cpp index fb009c6e6115618de158ba4782b5a5393f01733a..7aa5d80d411a676d6b842d406c1f797b075d9b2b 100644 --- a/prototype2/common/test/SocketTest.cpp +++ b/prototype2/common/test/SocketTest.cpp @@ -3,6 +3,8 @@ #include <prototype2/test/TestBase.h> #include <common/Socket.h> +std::vector<std::string> ipOk = {"0.0.0.0", "10.10.10.10", "127.0.0.1", "224.1.2.3", "255.255.255.255"}; +std::vector<std::string> ipNotOk = {"a.0.0.0", "1.2.3", "1.2", "", "127.0.0.256", "metrics"}; class SocketTest : public ::testing::Test { protected: @@ -31,6 +33,32 @@ TEST_F(SocketTest, SendUninitialized) { ASSERT_FALSE(tcpsocket.isValidSocket()); } +TEST_F(SocketTest, ValidInvalidIp) { + for (auto ipaddr : ipOk) { + ASSERT_TRUE(Socket::isValidIp(ipaddr)); + auto res = Socket::getHostByName(ipaddr); + ASSERT_TRUE(res == ipaddr); + } + for (auto ipaddr : ipNotOk) { + ASSERT_FALSE(Socket::isValidIp(ipaddr)); + } +} + +TEST_F(SocketTest, GetHostByName) { + std::string name {"localhost"}; + auto res = Socket::getHostByName(name); + ASSERT_TRUE(res == "127.0.0.1"); + for (auto ipaddr : ipOk) { + auto res = Socket::getHostByName(ipaddr); + ASSERT_TRUE(res == ipaddr); + } + // Checking weird case - not sure if this is right + // this step can be deleted if it causes problems later + std::string weirdIp {"8.8.8"}; + res = Socket::getHostByName(weirdIp); + ASSERT_TRUE(res == "8.8.0.8"); +} + int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS();