From fe591a169a7db6b39128bc5a7b0aac694569e1a6 Mon Sep 17 00:00:00 2001 From: Kane Scipioni Date: Sun, 5 Jul 2026 13:46:09 -0500 Subject: [PATCH] Added support for unix sockets --- dlib/sockets/sockets_extensions.cpp | 18 + dlib/sockets/sockets_extensions.h | 6 + dlib/sockets/sockets_extensions_abstract.h | 16 + dlib/sockets/sockets_kernel_1.cpp | 415 ++++++++++++++++---- dlib/sockets/sockets_kernel_1.h | 149 +++++++- dlib/sockets/sockets_kernel_2.cpp | 425 +++++++++++++++++---- dlib/sockets/sockets_kernel_2.h | 126 +++++- dlib/sockets/sockets_kernel_abstract.h | 121 ++++++ dlib/test/sockets2.cpp | 33 +- 9 files changed, 1145 insertions(+), 164 deletions(-) diff --git a/dlib/sockets/sockets_extensions.cpp b/dlib/sockets/sockets_extensions.cpp index be08c1998b..2b2537e565 100644 --- a/dlib/sockets/sockets_extensions.cpp +++ b/dlib/sockets/sockets_extensions.cpp @@ -256,6 +256,24 @@ namespace dlib return data->con; } +// ---------------------------------------------------------------------------------------- + + connection* connect ( + const std::string& path + ) + { + connection* con; + + if(create_connection(con,path)) + { + std::ostringstream sout; + sout << "unable to connect to '" << path << "'"; + throw socket_error(sout.str()); + } + + return con; + } + // ---------------------------------------------------------------------------------------- bool is_ip_address ( diff --git a/dlib/sockets/sockets_extensions.h b/dlib/sockets/sockets_extensions.h index 9faa34e011..5203c9bebf 100644 --- a/dlib/sockets/sockets_extensions.h +++ b/dlib/sockets/sockets_extensions.h @@ -119,6 +119,12 @@ namespace dlib unsigned long timeout ); +// ---------------------------------------------------------------------------------------- + + connection* connect ( + const std::string& path + ); + // ---------------------------------------------------------------------------------------- bool is_ip_address ( diff --git a/dlib/sockets/sockets_extensions_abstract.h b/dlib/sockets/sockets_extensions_abstract.h index 194c22ab24..d2f95783e4 100644 --- a/dlib/sockets/sockets_extensions_abstract.h +++ b/dlib/sockets/sockets_extensions_abstract.h @@ -222,6 +222,22 @@ namespace dlib - std::bad_alloc !*/ +// ---------------------------------------------------------------------------------------- + + connection* connect ( + const std::string& path + ); + /*! + ensures + - returns a connection object that is connected to a unix socket created + at the given path + throws + - dlib::socket_error + This exception is thrown if there is some problem that prevents us from + creating the connection + - std::bad_alloc + !*/ + // ---------------------------------------------------------------------------------------- diff --git a/dlib/sockets/sockets_kernel_1.cpp b/dlib/sockets/sockets_kernel_1.cpp index 55e39569f6..5ec14e90cd 100644 --- a/dlib/sockets/sockets_kernel_1.cpp +++ b/dlib/sockets/sockets_kernel_1.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2003 Davis E. King (davis@dlib.net), Miguel Grinberg + // Copyright (C) 2003 Davis E. King (davis@dlib.net), Miguel Grinberg // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_SOCKETS_KERNEL_1_CPp_ #define DLIB_SOCKETS_KERNEL_1_CPp_ @@ -7,6 +7,8 @@ #ifdef WIN32 #include +#include +#include #ifndef _WINSOCKAPI_ #define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ @@ -245,10 +247,12 @@ namespace dlib ) : user_data(0), connection_socket(*(new SOCKET_container())), - connection_foreign_port(foreign_port), - connection_foreign_ip(foreign_ip), - connection_local_port(local_port), - connection_local_ip(local_ip), + info(in_place_tag{}, + foreign_port, + foreign_ip, + local_port, + local_ip + ), sd(false), sdo(false), sdr(0) @@ -256,6 +260,23 @@ namespace dlib connection_socket = sock; } +// ---------------------------------------------------------------------------------------- + + connection:: + connection( + SOCKET_container sock, + const std::string& path + ) : + user_data(0), + connection_socket(*(new SOCKET_container())), + info(in_place_tag{}, path), + sd(false), + sdo(false), + sdr(0) + { + connection_socket = sock; + } + // ---------------------------------------------------------------------------------------- connection:: @@ -477,9 +498,20 @@ namespace dlib const std::string& ip ) : listening_socket(*(new SOCKET_container)), - listening_port(port), - listening_ip(ip), - inaddr_any(listening_ip.empty()) + info(in_place_tag{}, ip, port) + { + listening_socket = sock; + } + +// ---------------------------------------------------------------------------------------- + + listener:: + listener( + SOCKET_container sock, + const std::string& path + ) : + listening_socket(*(new SOCKET_container)), + info(in_place_tag{}, path) { listening_socket = sock; } @@ -521,8 +553,8 @@ namespace dlib ) { SOCKET incoming; - sockaddr_in incomingAddr; - int length = sizeof(sockaddr_in); + sockaddr_storage incomingAddr; + int length = sizeof(sockaddr_storage); // implement timeout with select if timeout is > 0 if (timeout > 0) @@ -561,80 +593,113 @@ namespace dlib if ( incoming == INVALID_SOCKET ) return OTHER_ERROR; - - // get the port of the foreign host into foreign_port - int foreign_port = ntohs(incomingAddr.sin_port); - - // get the IP of the foreign host into foreign_ip - std::string foreign_ip; + if (incomingAddr.ss_family == AF_INET) { - char* foreign_ip_temp = inet_ntoa(incomingAddr.sin_addr); + sockaddr_in& inet_addr = *reinterpret_cast(&incomingAddr); + + // get the port of the foreign host into foreign_port + int foreign_port = ntohs(inet_addr.sin_port); - // check if inet_ntoa() returned an error - if (foreign_ip_temp == NULL) + // get the IP of the foreign host into foreign_ip + std::string foreign_ip; { - closesocket(incoming); - return OTHER_ERROR; - } + char* foreign_ip_temp = inet_ntoa(inet_addr.sin_addr); - foreign_ip.assign(foreign_ip_temp); - } + // check if inet_ntoa() returned an error + if (foreign_ip_temp == NULL) + { + closesocket(incoming); + return OTHER_ERROR; + } + foreign_ip.assign(foreign_ip_temp); + } - // get the local ip - std::string local_ip; - if (inaddr_any == true) - { - sockaddr_in local_info; - length = sizeof(sockaddr_in); - // get the local sockaddr_in structure associated with this new connection - if ( getsockname ( + + // get the local ip + std::string local_ip; + if (info.cast_to().inaddr_any == true) + { + sockaddr_in local_info; + length = sizeof(sockaddr_in); + // get the local sockaddr_in structure associated with this new connection + if (getsockname( incoming, reinterpret_cast(&local_info), &length - ) == SOCKET_ERROR - ) - { // an error occurred + ) == SOCKET_ERROR + ) + { // an error occurred + closesocket(incoming); + return OTHER_ERROR; + } + char* temp = inet_ntoa(local_info.sin_addr); + + // check if inet_ntoa() returned an error + if (temp == NULL) + { + closesocket(incoming); + return OTHER_ERROR; + } + local_ip.assign(temp); + } + else + { + local_ip = info.cast_to().listening_ip; + } + + + // set the SO_OOBINLINE option + int flag_value = 1; + if (setsockopt(incoming, SOL_SOCKET, SO_OOBINLINE, reinterpret_cast(&flag_value), sizeof(int)) == SOCKET_ERROR) + { closesocket(incoming); return OTHER_ERROR; } - char* temp = inet_ntoa(local_info.sin_addr); - - // check if inet_ntoa() returned an error - if (temp == NULL) + + + // make a new connection object for this new connection + try { - closesocket(incoming); - return OTHER_ERROR; + new_connection = new connection( + incoming, + foreign_port, + foreign_ip, + info.cast_to().listening_port, + local_ip + ); } - local_ip.assign(temp); + catch (...) { closesocket(incoming); return OTHER_ERROR; } } - else + else if (incomingAddr.ss_family == AF_UNIX) { - local_ip = listening_ip; - } + sockaddr_un& un_addr = *reinterpret_cast(&incomingAddr); + length = sizeof(sockaddr_un); + if (getsockname( + incoming, + reinterpret_cast(&un_addr), + &length) == -1) + { // an error occurred + closesocket(incoming); + return OTHER_ERROR; + } - // set the SO_OOBINLINE option - int flag_value = 1; - if (setsockopt(incoming,SOL_SOCKET,SO_OOBINLINE,reinterpret_cast(&flag_value),sizeof(int)) == SOCKET_ERROR ) - { - closesocket(incoming); - return OTHER_ERROR; + // make a new connection object for this new connection + try + { + new_connection = new connection(incoming, un_addr.sun_path); + } + catch (...) + { + closesocket(incoming); + return OTHER_ERROR; + } } - - - // make a new connection object for this new connection - try - { - new_connection = new connection ( - incoming, - foreign_port, - foreign_ip, - listening_port, - local_ip - ); + else + { + return OTHER_ERROR; } - catch (...) { closesocket(incoming); return OTHER_ERROR; } return 0; } @@ -761,6 +826,161 @@ namespace dlib return 0; } + int create_listener ( + std::unique_ptr& new_listener, + const std::string& path + ) + { + new_listener.reset(); + listener* temp; + int status = create_listener(temp, path); + + if (status == 0) + new_listener.reset(temp); + + return status; + } + + int create_listener ( + listener*& new_listener, + const std::string& path + ) + { + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + sockaddr_un sa; // local socket structure + ZeroMemory(&sa, sizeof(sockaddr_un)); // initialize sa + + const char* sock_path = path.c_str(); + + // remove any previous socket with the same path + if (access(sock_path, 0) == 0) { + if (unlink(sock_path) == -1) { + return OTHER_ERROR; + } + } + + SOCKET sock = socket(AF_UNIX, SOCK_STREAM, 0); // get a new socket + + // if socket() returned an error then return OTHER_ERROR + if (sock == INVALID_SOCKET) + { + return OTHER_ERROR; + } + + // set the local socket structure + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, sock_path, sizeof(sa.sun_path) - 1); + + // bind the new socket to the requested port and ip + if (bind(sock, reinterpret_cast(&sa), sizeof(sockaddr_un)) == SOCKET_ERROR) + { + const int err = WSAGetLastError(); + // if there was an error + closesocket(sock); + + // if the port is already bound then return PORTINUSE + if (err == WSAEADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + // tell the new socket to listen + if (listen(sock, SOMAXCONN) == SOCKET_ERROR) + { + const int err = WSAGetLastError(); + // if there was an error return OTHER_ERROR + closesocket(sock); + + // if the port is already bound then return PORTINUSE + if (err == WSAEADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + // initialize a listener object on the heap with the new socket + try { new_listener = new listener(sock, path); } + catch (...) { closesocket(sock); return OTHER_ERROR; } + + return 0; + } + + int create_listener_from_socket ( + std::unique_ptr& new_listener, + connection::socket_descriptor_type sock + ) + { + new_listener.reset(); + listener* temp; + int status = create_listener(temp, sock); + + if (status == 0) + new_listener.reset(temp); + + return status; + } + + int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ) + { + if (sock == INVALID_SOCKET) return OTHER_ERROR; + + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + sockaddr_storage sa; // local socket structure + int length = sizeof(sa); + + if (getsockname( + sock, + reinterpret_cast(&sa), + &length) == -1) + { // an error occurred + closesocket(sock); + return OTHER_ERROR; + } + + if (sa.ss_family == AF_INET) + { + sockaddr_in& inet_addr = *reinterpret_cast(&sa); + + int port = ntohs(inet_addr.sin_port); + + std::string ip; + char* temp = inet_ntoa(inet_addr.sin_addr); + if (temp == NULL) + { + closesocket(sock); + return OTHER_ERROR; + } + ip.assign(temp); + + try { new_listener = new listener(sock, port, ip); } + catch (...) { closesocket(sock); return OTHER_ERROR; } + } + else if (sa.ss_family == AF_UNIX) + { + sockaddr_un& un_addr = *reinterpret_cast(&sa); + + std::string path(un_addr.sun_path); + try { new_listener = new listener(sock, path); } + catch (...) { closesocket(sock); return OTHER_ERROR; } + } + else + { + return OTHER_ERROR; + } + + return 0; + } + // ---------------------------------------------------------------------------------------- int create_connection ( @@ -969,6 +1189,73 @@ namespace dlib return 0; } + int create_connection ( + std::unique_ptr& new_connection, + const std::string& path + ) + { + new_connection.reset(); + connection* temp; + int status = create_connection(temp, path); + + if (status == 0) + new_connection.reset(temp); + + return status; + } + + int create_connection ( + connection*& new_connection, + const std::string& path + ) + { + // ensure that WSAStartup has been called and WSACleanup + // will eventually be called when program ends + sockets_startup(); + + + sockaddr_un sa; // local socket structure + ZeroMemory(&sa, sizeof(sockaddr_un)); // initialize local_sa + + SOCKET sock = socket(AF_UNIX, SOCK_STREAM, 0); // get a new socket + + // if socket() returned an error then return OTHER_ERROR + if (sock == INVALID_SOCKET) + { + return OTHER_ERROR; + } + + // set the foreign socket structure + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, path.c_str(), sizeof(sa.sun_path) - 1); + + // connect the socket + if (connect( + sock, + reinterpret_cast(&sa), + sizeof(sockaddr_un) + ) == SOCKET_ERROR + ) + { + const int err = WSAGetLastError(); + closesocket(sock); + // if the port is already bound then return PORTINUSE + if (err == WSAEADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + // initialize a connection object on the heap with the new socket + try + { + new_connection = new connection(sock, path); + } + catch (...) { closesocket(sock); return OTHER_ERROR; } + + return 0; + } + // ---------------------------------------------------------------------------------------- } diff --git a/dlib/sockets/sockets_kernel_1.h b/dlib/sockets/sockets_kernel_1.h index 5fb73ecd66..0a9561e3d5 100644 --- a/dlib/sockets/sockets_kernel_1.h +++ b/dlib/sockets/sockets_kernel_1.h @@ -15,6 +15,7 @@ #include "../algs.h" #include "../threads.h" #include "../uintn.h" +#include "../type_safe_union.h" namespace dlib @@ -95,6 +96,11 @@ namespace dlib const std::string& local_ip ); + friend int create_connection ( + connection*& new_connection, + const std::string& path + ); + public: ~connection ( @@ -119,17 +125,26 @@ namespace dlib ); unsigned short get_local_port ( - ) const { return connection_local_port; } + ) const { return info.cast_to().connection_local_port; } unsigned short get_foreign_port ( - ) const { return connection_foreign_port; } + ) const { return info.cast_to().connection_foreign_port; } const std::string& get_local_ip ( - ) const { return connection_local_ip; } + ) const { return info.cast_to().connection_local_ip; } const std::string& get_foreign_ip ( - ) const { return connection_foreign_ip; } + ) const { return info.cast_to().connection_foreign_ip; } + + const std::string& get_connection_path( + ) const { + return info.cast_to().connection_path; + } + bool is_inet( + ) const { + return info.contains(); + } int shutdown_outgoing ( ); @@ -192,13 +207,31 @@ namespace dlib return temp; } + struct inet_info { + int connection_foreign_port; + std::string connection_foreign_ip; + int connection_local_port; + std::string connection_local_ip; + + inet_info(int foreign_port, + const std::string& foreign_ip, + int local_port, + const std::string& local_ip) : + connection_foreign_port(foreign_port), + connection_foreign_ip(foreign_ip), + connection_local_port(local_port), + connection_local_ip(local_ip) {} + }; + + struct unix_info { + std::string connection_path; + + unix_info(const std::string& path) : connection_path(path) {} + }; // data members SOCKET_container& connection_socket; - const unsigned short connection_foreign_port; - const std::string connection_foreign_ip; - const unsigned short connection_local_port; - const std::string connection_local_ip; + const type_safe_union info; bool sd; // called shutdown bool sdo; // called shutdown_outgoing @@ -222,6 +255,17 @@ namespace dlib *this is initialized correctly with the above parameters !*/ + connection( + SOCKET_container sock, + const std::string& path + ); + /*! + requires + sock is a socket handle and + sock is the handle for the connection through path + ensures + *this is initialized correctly with the above parameters + !*/ // restricted functions connection(connection&); // copy constructor @@ -259,6 +303,16 @@ namespace dlib const std::string& ip ); + friend int create_listener ( + listener*& new_listener, + const std::string& path + ); + + friend int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ); + public: ~listener ( @@ -274,19 +328,48 @@ namespace dlib unsigned long timeout = 0 ); - unsigned short get_listening_port ( - ) { return listening_port; } + int get_listening_port( + ) const { + return info.cast_to().listening_port; + } + + const std::string& get_listening_ip( + ) const { + return info.cast_to().listening_ip; + } - const std::string& get_listening_ip ( - ) { return listening_ip; } + const std::string& get_listening_path( + ) const { + return info.cast_to().listening_path; + } + + bool is_inet( + ) const { + return info.contains(); + } private: + struct inet_info { + std::string listening_ip; + int listening_port; + bool inaddr_any; + + inet_info(const std::string& ip, int port) : + listening_ip(ip), + listening_port(port), + inaddr_any(ip.empty()) {} + }; + + struct unix_info { + std::string listening_path; + + unix_info(const std::string& path) : listening_path(path) {} + }; + // data members SOCKET_container& listening_socket; - const unsigned short listening_port; - const std::string listening_ip; - const bool inaddr_any; + const type_safe_union info; listener( SOCKET_container sock, @@ -302,6 +385,17 @@ namespace dlib *this is initialized correctly with the above parameters !*/ + listener( + SOCKET_container sock, + const std::string& path + ); + /*! + requires + sock is a socket handle and + sock is listening on path + ensures + *this is initialized correctly with the above parameters + !*/ // restricted functions listener(listener&); // copy constructor @@ -316,6 +410,16 @@ namespace dlib const std::string& ip = "" ); + int create_listener ( + listener*& new_listener, + const std::string& path + ); + + int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ); + int create_connection ( connection*& new_connection, unsigned short foreign_port, @@ -330,6 +434,16 @@ namespace dlib const std::string& ip = "" ); + int create_listener ( + std::unique_ptr& new_listener, + const std::string& path + ); + + int create_listener_from_socket ( + std::unique_ptr& new_listener, + connection::socket_descriptor_type sock + ); + int create_connection ( std::unique_ptr& new_connection, unsigned short foreign_port, @@ -338,6 +452,11 @@ namespace dlib const std::string& local_ip = "" ); + int create_connection ( + std::unique_ptr& new_connection, + const std::string& path + ); + // ---------------------------------------------------------------------------------------- diff --git a/dlib/sockets/sockets_kernel_2.cpp b/dlib/sockets/sockets_kernel_2.cpp index 455f3dc688..78d3a1e061 100644 --- a/dlib/sockets/sockets_kernel_2.cpp +++ b/dlib/sockets/sockets_kernel_2.cpp @@ -12,6 +12,7 @@ #include #include "../set.h" #include +#include #include @@ -302,10 +303,26 @@ namespace dlib const std::string& local_ip ) : connection_socket(sock), - connection_foreign_port(foreign_port), - connection_foreign_ip(foreign_ip), - connection_local_port(local_port), - connection_local_ip(local_ip), + info(in_place_tag{}, + foreign_port, + foreign_ip, + local_port, + local_ip + ), + sd(false), + sdo(false), + sdr(0) + {} + +// ---------------------------------------------------------------------------------------- + + connection:: + connection( + int sock, + const std::string& path + ) : + connection_socket(sock), + info(in_place_tag{}, path), sd(false), sdo(false), sdr(0) @@ -504,9 +521,18 @@ namespace dlib const std::string& ip ) : listening_socket(sock), - listening_port(port), - listening_ip(ip), - inaddr_any(listening_ip.empty()) + info(in_place_tag{}, ip, port) + {} + +// ---------------------------------------------------------------------------------------- + + listener:: + listener( + int sock, + const std::string& path + ) : + listening_socket(sock), + info(in_place_tag{}, path) {} // ---------------------------------------------------------------------------------------- @@ -551,8 +577,8 @@ namespace dlib ) { int incoming; - sockaddr_in incomingAddr; - dsocklen_t length = sizeof(sockaddr_in); + sockaddr_storage incomingAddr; + dsocklen_t length = sizeof(sockaddr_storage); // implement timeout with select if timeout is > 0 if (timeout > 0) @@ -673,31 +699,59 @@ namespace dlib } - - // get the port of the foreign host into foreign_port - int foreign_port = ntohs(incomingAddr.sin_port); - // get the IP of the foreign host into foreign_ip - char foreign_ip[16]; - inet_ntop(AF_INET,&incomingAddr.sin_addr,foreign_ip,16); + if (incomingAddr.ss_family == AF_INET) + { + sockaddr_in& inet_addr = *reinterpret_cast(&incomingAddr); + // get the port of the foreign host into foreign_port + int foreign_port = ntohs(inet_addr.sin_port); + // get the IP of the foreign host into foreign_ip + char foreign_ip[16]; + inet_ntop(AF_INET,&inet_addr.sin_addr,foreign_ip,16); - // get the local ip for this connection into local_ip - char temp_local_ip[16]; - std::string local_ip; - if (inaddr_any == true) - { - sockaddr_in local_info; - length = sizeof(sockaddr_in); - // get the local sockaddr_in structure associated with this new connection - if ( getsockname ( - incoming, - reinterpret_cast(&local_info), - &length - ) == -1 - ) - { // an error occurred + + + // get the local ip for this connection into local_ip + char temp_local_ip[16]; + std::string local_ip; + if (info.cast_to().inaddr_any == true) + { + sockaddr_in local_info; + length = sizeof(sockaddr_in); + // get the local sockaddr_in structure associated with this new connection + if ( getsockname ( + incoming, + reinterpret_cast(&local_info), + &length + ) == -1 + ) + { // an error occurred + while (true) + { + int status = ::close(incoming); + if (status == -1 && errno == EINTR) + continue; + break; + } + return OTHER_ERROR; + } + local_ip = const_cast ( + inet_ntop(AF_INET,&local_info.sin_addr,temp_local_ip,16) + ); + } + else + { + local_ip = info.cast_to().listening_ip; + } + + + + // set the SO_OOBINLINE option + int flag_value = 1; + if (setsockopt(incoming,SOL_SOCKET,SO_OOBINLINE,reinterpret_cast(&flag_value),sizeof(int))) + { while (true) { int status = ::close(incoming); @@ -707,56 +761,73 @@ namespace dlib } return OTHER_ERROR; } - local_ip = const_cast ( - inet_ntop(AF_INET,&local_info.sin_addr,temp_local_ip,16) - ); - } - else - { - local_ip = listening_ip; - } - // set the SO_OOBINLINE option - int flag_value = 1; - if (setsockopt(incoming,SOL_SOCKET,SO_OOBINLINE,reinterpret_cast(&flag_value),sizeof(int))) - { - while (true) + // make a new connection object for this new connection + try { - int status = ::close(incoming); - if (status == -1 && errno == EINTR) - continue; - break; + new_connection = new connection ( + incoming, + foreign_port, + foreign_ip, + info.cast_to().listening_port, + local_ip + ); + } + catch (...) + { + while (true) + { + int status = ::close(incoming); + if (status == -1 && errno == EINTR) + continue; + break; + } + return OTHER_ERROR; } - return OTHER_ERROR; - } + } + else if (incomingAddr.ss_family == AF_UNIX) + { + sockaddr_un& un_addr = *reinterpret_cast(&incomingAddr); + length = sizeof(sockaddr_un); + if (getsockname( + incoming, + reinterpret_cast(&un_addr), + &length) == -1) + { // an error occurred + while (true) + { + int status = ::close(incoming); + if (status == -1 && errno == EINTR) + continue; + break; + } + return OTHER_ERROR; + } - // make a new connection object for this new connection - try - { - new_connection = new connection ( - incoming, - foreign_port, - foreign_ip, - listening_port, - local_ip - ); - } - catch (...) - { - while (true) + // make a new connection object for this new connection + try { - int status = ::close(incoming); - if (status == -1 && errno == EINTR) - continue; - break; + new_connection = new connection (incoming, un_addr.sun_path); + } + catch (...) + { + while (true) + { + int status = ::close(incoming); + if (status == -1 && errno == EINTR) + continue; + break; + } + return OTHER_ERROR; } - return OTHER_ERROR; - } + } else { + return OTHER_ERROR; + } return 0; } @@ -905,6 +976,156 @@ namespace dlib return 0; } + int create_listener ( + std::unique_ptr& new_listener, + const std::string& path + ) + { + new_listener.reset(); + listener* temp; + int status = create_listener(temp,path); + + if (status == 0) + new_listener.reset(temp); + + return status; + } + + int create_listener ( + listener*& new_listener, + const std::string& path + ) + { + sockets_startup(); + + + sockaddr_un sa; // local socket structure + memset(&sa,'\0',sizeof(sockaddr_un)); // initialize sa + + const char *sock_path = path.c_str(); + + // remove any previous socket with the same path + if (access(sock_path, F_OK) == 0) { + if (unlink(sock_path) == -1) { + return OTHER_ERROR; + } + } + + int sock = socket (AF_UNIX, SOCK_STREAM, 0); // get a new socket + + // if socket() returned an error then return OTHER_ERROR + if (sock == -1) + { + return OTHER_ERROR; + } + + // set the local socket structure + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, sock_path, sizeof(sa.sun_path)-1); + + // bind the new socket to the requested port and ip + if (bind(sock,reinterpret_cast(&sa),sizeof(sockaddr_un)) == -1) + { // if there was an error + close_socket(sock); + + // if the port is already bound then return PORTINUSE + if (errno == EADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + + // tell the new socket to listen + if ( listen(sock,SOMAXCONN) == -1) + { + // if there was an error return OTHER_ERROR + close_socket(sock); + + // if the port is already bound then return PORTINUSE + if (errno == EADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + // initialize a listener object on the heap with the new socket + try { new_listener = new listener(sock,path); } + catch(...) { close_socket(sock); return OTHER_ERROR; } + + return 0; + } + + int create_listener_from_socket ( + std::unique_ptr& new_listener, + connection::socket_descriptor_type sock + ) + { + new_listener.reset(); + listener* temp; + int status = create_listener(temp,sock); + + if (status == 0) + new_listener.reset(temp); + + return status; + } + + int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ) + { + if (sock == -1) return OTHER_ERROR; + + sockets_startup(); + + sockaddr_storage sa; + dsocklen_t length = sizeof(sa); + + if (getsockname( + sock, + reinterpret_cast(&sa), + &length) == -1) + { // an error occurred + while (true) + { + int status = ::close(sock); + if (status == -1 && errno == EINTR) + continue; + break; + } + return OTHER_ERROR; + } + + if (sa.ss_family == AF_INET) + { + sockaddr_in& inet_addr = *reinterpret_cast(&sa); + + int port = ntohs(inet_addr.sin_port); + + char ip[16]; + inet_ntop(AF_INET, &inet_addr.sin_addr, ip, sizeof(ip)); + + try { new_listener = new listener(sock, port, ip); } + catch(...) { close_socket(sock); return OTHER_ERROR; } + } + else if (sa.ss_family == AF_UNIX) + { + sockaddr_un& un_addr = *reinterpret_cast(&sa); + + std::string path(un_addr.sun_path); + try { new_listener = new listener(sock, path); } + catch(...) { close_socket(sock); return OTHER_ERROR; } + } + else + { + return OTHER_ERROR; + } + + return 0; + } + // ---------------------------------------------------------------------------------------- int create_connection ( @@ -1100,6 +1321,72 @@ namespace dlib return 0; } + int create_connection ( + std::unique_ptr& new_connection, + const std::string& path + ) + { + new_connection.reset(); + connection* temp; + int status = create_connection(temp, path); + + if (status == 0) + new_connection.reset(temp); + + return status; + } + + int + create_connection ( + connection*& new_connection, + const std::string& path + ) + { + sockets_startup(); + + sockaddr_un sa; // local socket structure + memset(&sa,'\0',sizeof(sockaddr_un)); // initialize sa + + dsocklen_t length; + + int sock = socket (AF_UNIX, SOCK_STREAM, 0); // get a new socket + + // if socket() returned an error then return OTHER_ERROR + if (sock == -1 ) + { + return OTHER_ERROR; + } + + // set up the local socket structure + sa.sun_family = AF_UNIX; + strncpy(sa.sun_path, path.c_str(), sizeof(sa.sun_path) - 1); + + // connect the socket + if ( connect ( + sock, + reinterpret_cast(&sa), + sizeof(sockaddr_un) + ) == -1 + ) + { + close_socket(sock); + // if the port is already bound then return PORTINUSE + if (errno == EADDRINUSE) + return PORTINUSE; + else + return OTHER_ERROR; + } + + // initialize a connection object on the heap with the new socket + try + { + new_connection = new connection (sock, path); + } + catch(...) {close_socket(sock); return OTHER_ERROR; } + + return 0; + } + // ---------------------------------------------------------------------------------------- } diff --git a/dlib/sockets/sockets_kernel_2.h b/dlib/sockets/sockets_kernel_2.h index f3bc94ec01..ba67e26135 100644 --- a/dlib/sockets/sockets_kernel_2.h +++ b/dlib/sockets/sockets_kernel_2.h @@ -35,6 +35,7 @@ #include "../threads.h" #include "../algs.h" +#include "../type_safe_union.h" @@ -120,6 +121,11 @@ namespace dlib const std::string& local_ip ); + friend int create_connection ( + connection*& new_connection, + const std::string& path + ); + public: ~connection(); @@ -143,16 +149,22 @@ namespace dlib ); int get_local_port ( - ) const { return connection_local_port; } + ) const { return info.cast_to().connection_local_port; } int get_foreign_port ( - ) const { return connection_foreign_port; } + ) const { return info.cast_to().connection_foreign_port; } const std::string& get_local_ip ( - ) const { return connection_local_ip; } + ) const { return info.cast_to().connection_local_ip; } const std::string& get_foreign_ip ( - ) const { return connection_foreign_ip; } + ) const { return info.cast_to().connection_foreign_ip; } + + const std::string& get_connection_path ( + ) const { return info.cast_to().connection_path; } + + bool is_inet ( + ) const { return info.contains(); } int shutdown_outgoing ( ) @@ -239,12 +251,31 @@ namespace dlib } + struct inet_info { + int connection_foreign_port; + std::string connection_foreign_ip; + int connection_local_port; + std::string connection_local_ip; + + inet_info(int foreign_port, + const std::string& foreign_ip, + int local_port, + const std::string& local_ip) : + connection_foreign_port(foreign_port), + connection_foreign_ip(foreign_ip), + connection_local_port(local_port), + connection_local_ip(local_ip) {} + }; + + struct unix_info { + std::string connection_path; + + unix_info(const std::string& path) : connection_path(path) {} + }; + // data members int connection_socket; - const int connection_foreign_port; - const std::string connection_foreign_ip; - const int connection_local_port; - const std::string connection_local_ip; + const type_safe_union info; bool sd; // called shutdown bool sdo; // called shutdown_outgoing @@ -267,6 +298,10 @@ namespace dlib - *this is initialized correctly with the above parameters !*/ + connection( + int sock, + const std::string& path + ); // restricted functions connection(); @@ -305,6 +340,16 @@ namespace dlib const std::string& ip ); + friend int create_listener ( + listener*& new_listener, + const std::string& path + ); + + friend int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ); + public: ~listener(); @@ -320,18 +365,39 @@ namespace dlib ); int get_listening_port ( - ) const { return listening_port; } + ) const { return info.cast_to().listening_port; } const std::string& get_listening_ip ( - ) const { return listening_ip; } + ) const { return info.cast_to().listening_ip; } + + const std::string& get_listening_path ( + ) const { return info.cast_to().listening_path; } + + bool is_inet ( + ) const { return info.contains(); } private: + struct inet_info { + std::string listening_ip; + int listening_port; + bool inaddr_any; + + inet_info(const std::string& ip, int port) : + listening_ip(ip), + listening_port(port), + inaddr_any(ip.empty()) {} + }; + + struct unix_info { + std::string listening_path; + + unix_info(const std::string& path) : listening_path(path) {} + }; + // data members int listening_socket; - const int listening_port; - const std::string listening_ip; - const bool inaddr_any; + const type_safe_union info; listener( int sock, @@ -347,6 +413,10 @@ namespace dlib - *this is initialized correctly with the above parameters !*/ + listener( + int sock, + const std::string& path + ); // restricted functions listener(); @@ -362,6 +432,16 @@ namespace dlib const std::string& ip = "" ); + int create_listener ( + listener*& new_listener, + const std::string& path + ); + + int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ); + int create_connection ( connection*& new_connection, unsigned short foreign_port, @@ -370,12 +450,27 @@ namespace dlib const std::string& local_ip = "" ); + int create_connection ( + connection*& new_connection, + const std::string& path + ); + int create_listener ( std::unique_ptr& new_listener, unsigned short port, const std::string& ip = "" ); + int create_listener ( + std::unique_ptr& new_listener, + const std::string& path + ); + + int create_listener_from_socket ( + std::unique_ptr& new_listener, + connection::socket_descriptor_type sock + ); + int create_connection ( std::unique_ptr& new_connection, unsigned short foreign_port, @@ -384,6 +479,11 @@ namespace dlib const std::string& local_ip = "" ); + int create_connection ( + std::unique_ptr& new_connection, + const std::string& path + ); + // ---------------------------------------------------------------------------------------- } diff --git a/dlib/sockets/sockets_kernel_abstract.h b/dlib/sockets/sockets_kernel_abstract.h index d4571acad9..7435eb490c 100644 --- a/dlib/sockets/sockets_kernel_abstract.h +++ b/dlib/sockets/sockets_kernel_abstract.h @@ -126,6 +126,57 @@ namespace dlib std::unique_ptr smart pointer instead of a C pointer. !*/ + int create_listener ( + listener*& new_listener, + const std::string& path + ); + /*! + requires + - path.empty() == false + ensures + - if (#create_listener() == 0) then + - #new_listener == a pointer to a listener object that is listening on + the specified unix socket for an incoming connection + + - returns 0 if create_listener was successful + - returns OTHER_ERROR if some other error occurred + !*/ + + int create_listener ( + std::unique_ptr& new_listener, + const std::string& path + ); + /*! + This function is just an overload of the above function but it gives you a + std::unique_ptr smart pointer instead of a C pointer. + !*/ + + int create_listener_from_socket ( + listener*& new_listener, + connection::socket_descriptor_type sock + ); + /*! + requires + - sock is either a ICP/IP socket or a Unix socket + - sock is bound and configured to listening state + ensures + - if (#create_listener() == 0) then + - #new_listener == a pointer to a listener object that is listening on + the specified socket for an incoming connection + + - returns 0 if create_listener was successful + - returns OTHER_ERROR if some other error occurred + !*/ + + int create_listener_from_socket ( + std::unique_ptr& new_listener, + connection::socket_descriptor_type sock + ); + /*! + This function is just an overload of the above function but it gives you a + std::unique_ptr smart pointer instead of a C pointer. + !*/ + int create_connection ( connection*& new_connection, unsigned short foreign_port, @@ -165,6 +216,32 @@ namespace dlib std::unique_ptr smart pointer instead of a C pointer. !*/ + int create_connection ( + connection*& new_connection, + const std::string& path + ); + /*! + requires + - path.empty() == false + ensures + - if (#create_connection() == 0) then + - #new_connection == a pointer to a connection object that is connected + to a local unix socket at path + - #new_connection->user_data == 0 + + - returns 0 if create_connection was successful + - returns OTHER_ERROR if some other error occurred + !*/ + + int create_connection ( + std::unique_ptr& new_connection, + const std::string& path + ); + /*! + This function is just an overload of the above function but it gives you a + std::unique_ptr smart pointer instead of a C pointer. + !*/ + // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // connection object @@ -307,6 +384,8 @@ namespace dlib unsigned short get_local_port ( ) const; /*! + requires + - is_inet() == true ensures - returns the local port number for this connection !*/ @@ -314,6 +393,8 @@ namespace dlib unsigned short get_foreign_port ( ) const; /*! + requires + - is_inet() == true ensures - returns the foreign port number for this connection !*/ @@ -321,6 +402,8 @@ namespace dlib const std::string& get_local_ip ( ) const; /*! + requires + - is_inet() == true ensures - returns the IP of the local interface this connection is using !*/ @@ -328,10 +411,28 @@ namespace dlib const std::string& get_foreign_ip ( ) const; /*! + requires + - is_inet() == true ensures - returns the IP of the foreign host for this connection !*/ + const std::string& get_connection_path ( + ) const; + /*! + requires + - is_inet() == false + ensures + - returns the filesystem path of the local socket this connection is using + !*/ + + bool is_inet ( + ) const; + /*! + ensures + - returns true if the connection is using a TCP/IP socket + !*/ + int shutdown ( ); /*! @@ -470,6 +571,8 @@ namespace dlib unsigned short get_listening_port ( ) const; /*! + requires + - is_inet() == true ensures - returns the port number that this object is listening on !*/ @@ -477,12 +580,30 @@ namespace dlib const std::string& get_listening_ip ( ) const; /*! + requires + - is_inet() == true ensures - returns a string containing the IP (e.g. "127.0.0.1") of the interface this object is listening on - returns "" if it is accepting connections on all interfaces !*/ + const std::string& get_listening_path ( + ) const; + /*! + requires + - is_inet() == false + ensures + - returns the filesystem path of the listening socket + !*/ + + bool is_inet ( + ) const; + /*! + ensures + - returns true if listening on a TCP/IP socket + !*/ + private: // restricted functions listener(); diff --git a/dlib/test/sockets2.cpp b/dlib/test/sockets2.cpp index 3521e751d7..54abc9188c 100644 --- a/dlib/test/sockets2.cpp +++ b/dlib/test/sockets2.cpp @@ -22,6 +22,7 @@ namespace // should start with "test." dlib::logger dlog("test.sockets2"); + const std::string socket_name = "dlib-test-socket"; class sockets2_tester : public tester, private multithreaded_object { @@ -33,15 +34,26 @@ namespace short port_num; string data_to_send; + bool test_unix; bool test_failed; + connection *get_connection( + ) + { + if (test_unix) { + return connect(socket_name); + } else { + return connect("127.0.0.1", port_num); + } + } + void write_thread ( ) { try { - std::unique_ptr con(connect("127.0.0.1", port_num)); + std::unique_ptr con(get_connection()); // Send a copy of the data down the connection so we can test our the read() function // that uses timeouts in the main thread. @@ -65,7 +77,7 @@ namespace { try { - std::unique_ptr con(connect("127.0.0.1", port_num)); + std::unique_ptr con(get_connection()); // just do nothing until the connection closes char ch; @@ -96,9 +108,20 @@ namespace register_thread(*this, &sockets2_tester::no_write_thread); } + ~sockets2_tester ( + ) + { + unlink(socket_name.c_str()); + } + void perform_test ( ) { + test_unix = false; + run_tests(0); + run_tests(40); + + test_unix = true; run_tests(0); run_tests(40); } @@ -123,7 +146,11 @@ namespace std::unique_ptr list; - DLIB_TEST(create_listener(list, port_num, "127.0.0.1") == 0); + if (test_unix) { + DLIB_TEST(create_listener(list, socket_name) == 0); + } else { + DLIB_TEST(create_listener(list, port_num, "127.0.0.1") == 0); + } DLIB_TEST(bool(list)); // kick off the sending threads