Skip to content
This repository has been archived by the owner on Apr 6, 2019. It is now read-only.

Commit

Permalink
Support IPv6 (#24)
Browse files Browse the repository at this point in the history
* ipv6 support

* keep SOCKET_ERROR on windows
  • Loading branch information
yuangu authored and Cylix committed Nov 14, 2017
1 parent c6e5399 commit e2aa5bb
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 63 deletions.
6 changes: 6 additions & 0 deletions includes/tacopie/network/tcp_socket.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,12 @@ class tcp_socket {
//!
fd_t get_fd(void) const;

public:
//!
//! \return whether the host is IPV6
//!
bool is_ipv6(void) const;

private:
//!
//! create a new socket if no socket has been initialized yet
Expand Down
53 changes: 45 additions & 8 deletions sources/network/common/tcp_socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@
#endif /* SOCKET_ERROR */

#if _WIN32
#define __TACOPIE_LENGTH(size) static_cast<int>(size) // for Windows, convert buffer size to `int`
#pragma warning ( disable: 4996 ) // for Windows, `inet_ntoa` is deprecated as it does not support IPv6
#define __TACOPIE_LENGTH(size) static_cast<int>(size) // for Windows, convert buffer size to `int`
#pragma warning(disable : 4996) // for Windows, `inet_ntoa` is deprecated as it does not support IPv6
#else
#define __TACOPIE_LENGTH(size) size // for Unix, keep buffer size as `size_t`
#endif /* _WIN32 */
#define __TACOPIE_LENGTH(size) size // for Unix, keep buffer size as `size_t`
#endif /* _WIN32 */

namespace tacopie {

Expand Down Expand Up @@ -139,14 +139,42 @@ tcp_socket::accept(void) {
create_socket_if_necessary();
check_or_set_type(type::SERVER);

struct sockaddr_in client_info;
socklen_t client_info_struct_size = sizeof(client_info);
struct sockaddr_storage ss;
socklen_t addrlen = sizeof(ss);

fd_t client_fd = ::accept(m_fd, (struct sockaddr*) &client_info, &client_info_struct_size);
fd_t client_fd = ::accept(m_fd, reinterpret_cast<struct sockaddr*>(&ss), &addrlen);

if (client_fd == __TACOPIE_INVALID_FD) { __TACOPIE_THROW(error, "accept() failure"); }

return {client_fd, inet_ntoa(client_info.sin_addr), client_info.sin_port, type::CLIENT};
//! now determine host and port based on socket type
std::string saddr;
std::uint32_t port;

//! ipv6
if (ss.ss_family == AF_INET6) {
struct sockaddr_in6* addr6 = reinterpret_cast<struct sockaddr_in6*>(&ss);
char buf[INET6_ADDRSTRLEN] = {};
const char* addr = ::inet_ntop(ss.ss_family, &addr6->sin6_addr, buf, INET6_ADDRSTRLEN);

if (addr) {
saddr = std::string("[") + addr + "]";
}

port = ntohs(addr6->sin6_port);
}
//! ipv4
else {
struct sockaddr_in* addr4 = reinterpret_cast<struct sockaddr_in*>(&ss);
char buf[INET_ADDRSTRLEN] = {};
const char* addr = ::inet_ntop(ss.ss_family, &addr4->sin_addr, buf, INET_ADDRSTRLEN);

if (addr) {
saddr = addr;
}

port = ntohs(addr4->sin_port);
}
return {client_fd, saddr, port, type::CLIENT};
}

//!
Expand Down Expand Up @@ -203,6 +231,15 @@ tcp_socket::get_fd(void) const {
return m_fd;
}

//!
//! ipv6 checking
//!

bool
tcp_socket::is_ipv6(void) const {
return m_host.find(':') != std::string::npos;
}

//!
//! comparison operator
//!
Expand Down
108 changes: 81 additions & 27 deletions sources/network/unix/unix_tcp_socket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,34 @@ tcp_socket::connect(const std::string& host, std::uint32_t port, std::uint32_t t
create_socket_if_necessary();
check_or_set_type(type::CLIENT);

struct sockaddr_un server_addr_un;
struct sockaddr_in server_addr_in;
struct sockaddr_storage ss;
socklen_t addr_len;

//! 0-init addr info struct
std::memset(&ss, 0, sizeof(ss));

//! Handle case of unix sockets if port is 0
bool is_unix_socket = m_port == 0;
if (is_unix_socket) {
std::memset(&server_addr_un, 0, sizeof(server_addr_un));
server_addr_un.sun_family = AF_UNIX;
strncpy(server_addr_un.sun_path, host.c_str(), sizeof(server_addr_un.sun_path) - 1);
//! init sockaddr_un struct
struct sockaddr_un* addr = reinterpret_cast<struct sockaddr_un*>(&ss);
//! host
strncpy(addr->sun_path, host.c_str(), sizeof(addr->sun_path) - 1);
//! Remaining fields
ss.ss_family = AF_UNIX;
addr_len = sizeof(*addr);
}
else if (is_ipv6()) {
//! init sockaddr_in6 struct
struct sockaddr_in6* addr = reinterpret_cast<struct sockaddr_in6*>(&ss);
//! convert addr
if (::inet_pton(AF_INET6, host.data(), &addr->sin6_addr) < 0) {
__TACOPIE_THROW(error, "inet_pton() failure");
}
//! remaining fields
ss.ss_family = AF_INET6;
addr->sin6_port = htons(port);
addr_len = sizeof(*addr);
}
else {
struct addrinfo* result = nullptr;
Expand All @@ -65,19 +84,21 @@ tcp_socket::connect(const std::string& host, std::uint32_t port, std::uint32_t t
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;

//! resolve DNS
if (getaddrinfo(host.c_str(), nullptr, &hints, &result) != 0) { __TACOPIE_THROW(error, "getaddrinfo() failure"); }

std::memset(&server_addr_in, 0, sizeof(server_addr_in));
server_addr_in.sin_addr = ((struct sockaddr_in*) (result->ai_addr))->sin_addr;
server_addr_in.sin_port = htons(port);
server_addr_in.sin_family = AF_INET;
//! init sockaddr_in struct
struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(&ss);
//! host
addr->sin_addr = ((struct sockaddr_in*) (result->ai_addr))->sin_addr;
//! Remaining fields
addr->sin_port = htons(port);
ss.ss_family = AF_INET;
addr_len = sizeof(*addr);

freeaddrinfo(result);
}

socklen_t addr_len = is_unix_socket ? sizeof(server_addr_un) : sizeof(server_addr_in);
const struct sockaddr* server_addr = is_unix_socket ? (const struct sockaddr*) &server_addr_un : (const struct sockaddr*) &server_addr_in;

if (timeout_msecs > 0) {
//! for timeout connection handling:
//! 1. set socket to non blocking
Expand All @@ -98,7 +119,7 @@ tcp_socket::connect(const std::string& host, std::uint32_t port, std::uint32_t t
}
}

int ret = ::connect(m_fd, server_addr, addr_len);
int ret = ::connect(m_fd, reinterpret_cast<const struct sockaddr*>(&ss), addr_len);
if (ret < 0 && errno != EINPROGRESS) {
close();
__TACOPIE_THROW(error, "connect() failure");
Expand Down Expand Up @@ -150,35 +171,56 @@ tcp_socket::bind(const std::string& host, std::uint32_t port) {
create_socket_if_necessary();
check_or_set_type(type::SERVER);

struct sockaddr_un server_addr_un;
struct sockaddr_in server_addr_in;
struct sockaddr_storage ss;
socklen_t addr_len;

//! 0-init addr info struct
std::memset(&ss, 0, sizeof(ss));

//! Handle case of unix sockets if port is 0
bool is_unix_socket = m_port == 0;
if (is_unix_socket) {
std::memset(&server_addr_un, 0, sizeof(server_addr_un));
server_addr_un.sun_family = AF_UNIX;
strncpy(server_addr_un.sun_path, host.c_str(), sizeof(server_addr_un.sun_path) - 1);
//! init sockaddr_un struct
struct sockaddr_un* addr = reinterpret_cast<struct sockaddr_un*>(&ss);
//! host
strncpy(addr->sun_path, host.c_str(), sizeof(addr->sun_path) - 1);
//! remaining fields
ss.ss_family = AF_UNIX;
addr_len = sizeof(*addr);
}
else if (is_ipv6()) {
//! init sockaddr_in6 struct
struct sockaddr_in6* addr = reinterpret_cast<struct sockaddr_in6*>(&ss);
//! convert addr
if (::inet_pton(AF_INET6, host.data(), &addr->sin6_addr) < 0) {
__TACOPIE_THROW(error, "inet_pton() failure");
}
//! remaining fields
addr->sin6_port = htons(port);
ss.ss_family = AF_INET6;
addr_len = sizeof(*addr);
}
else {
struct addrinfo* result = nullptr;

//! dns resolution
if (getaddrinfo(host.c_str(), nullptr, nullptr, &result) != 0) {
__TACOPIE_THROW(error, "getaddrinfo() failure");
}

std::memset(&server_addr_in, 0, sizeof(server_addr_in));
server_addr_in.sin_addr = ((struct sockaddr_in*) (result->ai_addr))->sin_addr;
server_addr_in.sin_port = htons(port);
server_addr_in.sin_family = AF_INET;
//! init sockaddr_in struct
struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(&ss);
//! addr
addr->sin_addr = ((struct sockaddr_in*) (result->ai_addr))->sin_addr;
//! remaining fields
addr->sin_port = htons(port);
ss.ss_family = AF_INET;
addr_len = sizeof(*addr);

freeaddrinfo(result);
}

socklen_t addr_len = is_unix_socket ? sizeof(server_addr_un) : sizeof(server_addr_in);
const struct sockaddr* server_addr = is_unix_socket ? (const struct sockaddr*) &server_addr_un : (const struct sockaddr*) &server_addr_in;

if (::bind(m_fd, server_addr, addr_len) == -1) { __TACOPIE_THROW(error, "bind() failure"); }
if (::bind(m_fd, reinterpret_cast<const struct sockaddr*>(&ss), addr_len) == -1) { __TACOPIE_THROW(error, "bind() failure"); }
}

//!
Expand All @@ -205,7 +247,19 @@ tcp_socket::create_socket_if_necessary(void) {

//! new TCP socket
//! handle case of unix sockets by checking whether the port is 0 or not
m_fd = socket(m_port == 0 ? AF_UNIX : AF_INET, SOCK_STREAM, 0);
//! also handle ipv6 addr
short family;
if (m_port == 0) {
family = AF_UNIX;
}
else if (is_ipv6()) {
family = AF_INET6;
}
else {
family = AF_INET;
}

m_fd = socket(family, SOCK_STREAM, 0);
m_type = type::UNKNOWN;

if (m_fd == __TACOPIE_INVALID_FD) { __TACOPIE_THROW(error, "tcp_socket::create_socket_if_necessary: socket() failure"); }
Expand Down
Loading

0 comments on commit e2aa5bb

Please sign in to comment.