From c1a8576c5ca680ab03148074255ba5ec279b22d3 Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Fri, 27 Apr 2012 09:54:07 +0200 Subject: [PATCH] Implemented IPv6 support. --- base/tcpclient.cpp | 53 ++++++++++---- base/tcpserver.cpp | 2 +- base/tcpsocket.cpp | 140 +++++++++++++++++-------------------- base/tcpsocket.h | 11 ++- icinga/endpointmanager.cpp | 5 +- icinga/jsonrpcendpoint.cpp | 1 - 6 files changed, 110 insertions(+), 102 deletions(-) diff --git a/base/tcpclient.cpp b/base/tcpclient.cpp index e609e5116..93e3229d9 100644 --- a/base/tcpclient.cpp +++ b/base/tcpclient.cpp @@ -25,31 +25,54 @@ void TCPClient::Start(void) void TCPClient::Connect(const string& hostname, unsigned short port) { - hostent *hent; - sockaddr_in sin; + m_Role = RoleOutbound; + + stringstream s; + s << port; + string strPort = s.str(); + + addrinfo hints; + addrinfo *result; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(port); + int rc = getaddrinfo(hostname.c_str(), strPort.c_str(), &hints, &result); + + if (rc < 0) { + HandleSocketError(); + + return; + } - hent = gethostbyname(hostname.c_str()); + int fd = INVALID_SOCKET; - if (hent != NULL) - sin.sin_addr.s_addr = ((in_addr *)hent->h_addr_list[0])->s_addr; - else - sin.sin_addr.s_addr = inet_addr(hostname.c_str()); + for (addrinfo *info = result; info != NULL; info = info->ai_next) { + fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); - int rc = connect(GetFD(), (sockaddr *)&sin, sizeof(sin)); + if (fd == INVALID_SOCKET) + continue; + + SetFD(fd); + + rc = connect(fd, info->ai_addr, info->ai_addrlen); #ifdef _WIN32 - if (rc < 0 && WSAGetLastError() != WSAEWOULDBLOCK) { + if (rc < 0 && WSAGetLastError() != WSAEWOULDBLOCK) #else /* _WIN32 */ - if (rc < 0 && errno != EINPROGRESS) { + if (rc < 0 && errno != EINPROGRESS) #endif /* _WIN32 */ - HandleSocketError(); + continue; + + break; } - m_Role = RoleOutbound; + if (fd == INVALID_SOCKET) + HandleSocketError(); + + freeaddrinfo(result); } FIFO::Ptr TCPClient::GetSendQueue(void) diff --git a/base/tcpserver.cpp b/base/tcpserver.cpp index b857c96d0..b35817034 100644 --- a/base/tcpserver.cpp +++ b/base/tcpserver.cpp @@ -37,7 +37,7 @@ void TCPServer::Listen(void) int TCPServer::ReadableEventHandler(const EventArgs& ea) { int fd; - sockaddr_in addr; + sockaddr_storage addr; socklen_t addrlen = sizeof(addr); fd = accept(GetFD(), (sockaddr *)&addr, &addrlen); diff --git a/base/tcpsocket.cpp b/base/tcpsocket.cpp index 82239ef58..86d73afb5 100644 --- a/base/tcpsocket.cpp +++ b/base/tcpsocket.cpp @@ -2,11 +2,11 @@ using namespace icinga; -void TCPSocket::MakeSocket(void) +void TCPSocket::MakeSocket(int family) { assert(GetFD() == INVALID_SOCKET); - int fd = socket(AF_INET, SOCK_STREAM, 0); + int fd = socket(family, SOCK_STREAM, 0); if (fd == INVALID_SOCKET) { HandleSocketError(); @@ -17,118 +17,106 @@ void TCPSocket::MakeSocket(void) SetFD(fd); } -void TCPSocket::Bind(unsigned short port) +void TCPSocket::Bind(unsigned short port, int family) { - Bind(NULL, port); + Bind(NULL, port, family); } -void TCPSocket::Bind(const char *hostname, unsigned short port) +void TCPSocket::Bind(const char *hostname, unsigned short port, int family) { -#ifndef _WIN32 - const int optTrue = 1; - setsockopt(GetFD(), SOL_SOCKET, SO_REUSEADDR, (char *)&optTrue, sizeof(optTrue)); -#endif /* _WIN32 */ + stringstream s; + s << port; + string strPort = s.str(); + + addrinfo hints; + addrinfo *result; - sockaddr_in sin; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = hostname ? inet_addr(hostname) : htonl(INADDR_ANY); - sin.sin_port = htons(port); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; - if (::bind(GetFD(), (sockaddr *)&sin, sizeof(sin)) < 0) + if (getaddrinfo(hostname, strPort.c_str(), &hints, &result) < 0) { HandleSocketError(); -} -string TCPSocket::GetAddressFromSockaddr(sockaddr *address) -{ - static char buffer[256]; + return; + } -#ifdef _WIN32 - DWORD BufferLength = sizeof(buffer); + int fd = INVALID_SOCKET; - socklen_t len; - if (address->sa_family == AF_INET) - len = sizeof(sockaddr_in); - else if (address->sa_family == AF_INET6) - len = sizeof(sockaddr_in6); - else { - assert(0); + for (addrinfo *info = result; info != NULL; info = info->ai_next) { + fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol); - return ""; - } + if (fd == INVALID_SOCKET) + continue; - if (WSAAddressToString(address, len, NULL, buffer, &BufferLength) != 0) - return string(); -#else /* _WIN32 */ - void *IpAddress; + SetFD(fd); - if (address->sa_family == AF_INET) - IpAddress = &(((sockaddr_in *)address)->sin_addr); - else - IpAddress = &(((sockaddr_in6 *)address)->sin6_addr); + const int optFalse = 0; + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&optFalse, sizeof(optFalse)); - if (inet_ntop(address->sa_family, IpAddress, buffer, sizeof(buffer)) == NULL) - return string(); +#ifndef _WIN32 + const int optTrue = 1; + setsockopt(GetFD(), SOL_SOCKET, SO_REUSEADDR, (char *)&optTrue, sizeof(optTrue)); #endif /* _WIN32 */ - return buffer; -} + int rc = ::bind(fd, info->ai_addr, info->ai_addrlen); -unsigned short TCPSocket::GetPortFromSockaddr(sockaddr *address) -{ - if (address->sa_family == AF_INET) - return htons(((sockaddr_in *)address)->sin_port); - else if (address->sa_family == AF_INET6) - return htons(((sockaddr_in6 *)address)->sin6_port); - else { - assert(0); - - return 0; +#ifdef _WIN32 + if (rc < 0 && WSAGetLastError() != WSAEWOULDBLOCK) +#else /* _WIN32 */ + if (rc < 0 && errno != EINPROGRESS) +#endif /* _WIN32 */ + continue; + + break; } -} -bool TCPSocket::GetClientSockaddr(sockaddr_storage *address) -{ - socklen_t len = sizeof(*address); - - if (getsockname(GetFD(), (sockaddr *)address, &len) < 0) { + if (fd == INVALID_SOCKET) HandleSocketError(); - return false; - } - - return true; + freeaddrinfo(result); } -bool TCPSocket::GetPeerSockaddr(sockaddr_storage *address) + +string TCPSocket::GetAddressFromSockaddr(sockaddr *address, socklen_t len) { - socklen_t len = sizeof(*address); - - if (getpeername(GetFD(), (sockaddr *)address, &len) < 0) { - HandleSocketError(); + char host[NI_MAXHOST]; + char service[NI_MAXSERV]; - return false; - } + if (getnameinfo(address, len, host, sizeof(host), service, sizeof(service), NI_NUMERICHOST | NI_NUMERICSERV) < 0) + throw InvalidArgumentException(); /* TODO: throw proper exception */ - return true; + stringstream s; + s << "[" << host << "]:" << service; + return s.str(); } string TCPSocket::GetClientAddress(void) { sockaddr_storage sin; + socklen_t len = sizeof(sin); - if (!GetClientSockaddr(&sin)) - return ""; + if (getsockname(GetFD(), (sockaddr *)&sin, &len) < 0) { + HandleSocketError(); + + return string(); + } - return GetAddressFromSockaddr((sockaddr *)&sin); + return GetAddressFromSockaddr((sockaddr *)&sin, len); } string TCPSocket::GetPeerAddress(void) { sockaddr_storage sin; + socklen_t len = sizeof(sin); - if (!GetPeerSockaddr(&sin)) - return ""; + if (getpeername(GetFD(), (sockaddr *)&sin, &len) < 0) { + HandleSocketError(); + + return string(); + } - return GetAddressFromSockaddr((sockaddr *)&sin); + return GetAddressFromSockaddr((sockaddr *)&sin, len); } diff --git a/base/tcpsocket.h b/base/tcpsocket.h index 2373d2e1f..1545cc4ce 100644 --- a/base/tcpsocket.h +++ b/base/tcpsocket.h @@ -7,17 +7,16 @@ namespace icinga class I2_BASE_API TCPSocket : public Socket { private: - static string GetAddressFromSockaddr(sockaddr *address); - static unsigned short GetPortFromSockaddr(sockaddr *address); + static string GetAddressFromSockaddr(sockaddr *address, socklen_t len); + + void MakeSocket(int family); public: typedef shared_ptr Ptr; typedef weak_ptr WeakPtr; - void MakeSocket(void); - - void Bind(unsigned short port); - void Bind(const char *hostname, unsigned short port); + void Bind(unsigned short port, int family); + void Bind(const char *hostname, unsigned short port, int family); bool GetClientSockaddr(sockaddr_storage *address); bool GetPeerSockaddr(sockaddr_storage *address); diff --git a/icinga/endpointmanager.cpp b/icinga/endpointmanager.cpp index b9adab381..ae869ea8b 100644 --- a/icinga/endpointmanager.cpp +++ b/icinga/endpointmanager.cpp @@ -16,8 +16,7 @@ void EndpointManager::AddListener(unsigned short port) JsonRpcServer::Ptr server = make_shared(m_SSLContext); RegisterServer(server); - server->MakeSocket(); - server->Bind(port); + server->Bind(port, AF_INET6); server->Listen(); server->Start(); } @@ -25,7 +24,7 @@ void EndpointManager::AddListener(unsigned short port) void EndpointManager::AddConnection(string host, unsigned short port) { stringstream s; - s << "Adding new endpoint: " << host << ":" << port; + s << "Adding new endpoint: [" << host << "]:" << port; Application::Log(s.str()); JsonRpcEndpoint::Ptr endpoint = make_shared(); diff --git a/icinga/jsonrpcendpoint.cpp b/icinga/jsonrpcendpoint.cpp index 2ec80924e..0fd58ae0a 100644 --- a/icinga/jsonrpcendpoint.cpp +++ b/icinga/jsonrpcendpoint.cpp @@ -63,7 +63,6 @@ void JsonRpcEndpoint::Connect(string host, unsigned short port, shared_ptr(RoleOutbound, sslContext); - client->MakeSocket(); client->Connect(host, port); client->Start(); -- 2.40.0