diff options
Diffstat (limited to 'ml/dlib/dlib/sockets/sockets_kernel_1.cpp')
-rw-r--r-- | ml/dlib/dlib/sockets/sockets_kernel_1.cpp | 979 |
1 files changed, 979 insertions, 0 deletions
diff --git a/ml/dlib/dlib/sockets/sockets_kernel_1.cpp b/ml/dlib/dlib/sockets/sockets_kernel_1.cpp new file mode 100644 index 00000000..55e39569 --- /dev/null +++ b/ml/dlib/dlib/sockets/sockets_kernel_1.cpp @@ -0,0 +1,979 @@ +// 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_ +#include "../platform.h" + +#ifdef WIN32 + +#include <winsock2.h> + +#ifndef _WINSOCKAPI_ +#define _WINSOCKAPI_ /* Prevent inclusion of winsock.h in windows.h */ +#endif + +#include "../windows_magic.h" + +#include "sockets_kernel_1.h" + +#include <windows.h> + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif + + +// tell visual studio to link to the libraries we need if we are +// in fact using visual studio +#ifdef _MSC_VER +#pragma comment (lib, "ws2_32.lib") +#endif + +#include "../assert.h" + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + class SOCKET_container + { + /*! + This object is just a wrapper around the SOCKET type. It exists + so that we can #include the windows.h and Winsock2.h header files + in this cpp file and not at all in the header file. + !*/ + public: + SOCKET_container ( + SOCKET s = INVALID_SOCKET + ) : val(s) {} + + SOCKET val; + operator SOCKET&() { return val; } + + SOCKET_container& operator= ( + const SOCKET& s + ) { val = s; return *this; } + + bool operator== ( + const SOCKET& s + ) const { return s == val; } + }; + +// ---------------------------------------------------------------------------------------- +// stuff to ensure that WSAStartup() is always called before any sockets stuff is needed + + namespace sockets_kernel_1_mutex + { + mutex startup_lock; + } + + class sockets_startupdown + { + public: + sockets_startupdown(); + ~sockets_startupdown() { WSACleanup( ); } + + }; + sockets_startupdown::sockets_startupdown ( + ) + { + WSADATA wsaData; + WSAStartup (MAKEWORD(2,0), &wsaData); + } + + void sockets_startup() + { + // mutex crap to make this function thread-safe + sockets_kernel_1_mutex::startup_lock.lock(); + static sockets_startupdown a; + sockets_kernel_1_mutex::startup_lock.unlock(); + } + +// ---------------------------------------------------------------------------------------- + + // lookup functions + + int + get_local_hostname ( + std::string& hostname + ) + { + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + try + { + + char temp[NI_MAXHOST]; + if (gethostname(temp,NI_MAXHOST) == SOCKET_ERROR ) + { + return OTHER_ERROR; + } + + hostname = temp; + } + catch (...) + { + return OTHER_ERROR; + } + + return 0; + } + +// ----------------- + + int + hostname_to_ip ( + const std::string& hostname, + std::string& ip, + int n + ) + { + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + try + { + // lock this mutex since gethostbyname isn't really thread safe + auto_mutex M(sockets_kernel_1_mutex::startup_lock); + + // if no hostname was given then return error + if ( hostname.empty()) + return OTHER_ERROR; + + hostent* address; + address = gethostbyname(hostname.c_str()); + + if (address == 0) + { + return OTHER_ERROR; + } + + // find the nth address + in_addr* addr = reinterpret_cast<in_addr*>(address->h_addr_list[0]); + for (int i = 1; i <= n; ++i) + { + addr = reinterpret_cast<in_addr*>(address->h_addr_list[i]); + + // if there is no nth address then return error + if (addr == 0) + return OTHER_ERROR; + } + + char* resolved_ip = inet_ntoa(*addr); + + // check if inet_ntoa returned an error + if (resolved_ip == NULL) + { + return OTHER_ERROR; + } + + ip.assign(resolved_ip); + + } + catch(...) + { + return OTHER_ERROR; + } + + return 0; + } + +// ----------------- + + int + ip_to_hostname ( + const std::string& ip, + std::string& hostname + ) + { + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + try + { + // lock this mutex since gethostbyaddr isn't really thread safe + auto_mutex M(sockets_kernel_1_mutex::startup_lock); + + // if no ip was given then return error + if (ip.empty()) + return OTHER_ERROR; + + hostent* address; + unsigned long ipnum = inet_addr(ip.c_str()); + + // if inet_addr couldn't convert ip then return an error + if (ipnum == INADDR_NONE) + { + return OTHER_ERROR; + } + address = gethostbyaddr(reinterpret_cast<char*>(&ipnum),4,AF_INET); + + // check if gethostbyaddr returned an error + if (address == 0) + { + return OTHER_ERROR; + } + hostname.assign(address->h_name); + + } + catch (...) + { + return OTHER_ERROR; + } + return 0; + + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // connection object +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + connection:: + connection( + SOCKET_container sock, + unsigned short foreign_port, + const std::string& foreign_ip, + unsigned short local_port, + const std::string& local_ip + ) : + 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), + sd(false), + sdo(false), + sdr(0) + { + connection_socket = sock; + } + +// ---------------------------------------------------------------------------------------- + + connection:: + ~connection ( + ) + { + if (connection_socket != INVALID_SOCKET) + closesocket(connection_socket); + delete &connection_socket; + } + +// ---------------------------------------------------------------------------------------- + + int connection:: + disable_nagle() + { + int flag = 1; + int status = setsockopt( connection_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(flag) ); + + if (status == SOCKET_ERROR) + return OTHER_ERROR; + else + return 0; + } + +// ---------------------------------------------------------------------------------------- + + long connection:: + write ( + const char* buf, + long num + ) + { + const long old_num = num; + long status; + const long max_send_length = 1024*1024*100; + while (num > 0) + { + // Make sure to cap the max value num can take on so that if it is + // really large (it might be big on 64bit platforms) so that the OS + // can't possibly get upset about it being large. + const long length = std::min(max_send_length, num); + if ( (status = send(connection_socket,buf,length,0)) == SOCKET_ERROR) + { + if (sdo_called()) + return SHUTDOWN; + else + return OTHER_ERROR; + } + num -= status; + buf += status; + } + return old_num; + } + +// ---------------------------------------------------------------------------------------- + + long connection:: + read ( + char* buf, + long num + ) + { + const long max_recv_length = 1024*1024*100; + // Make sure to cap the max value num can take on so that if it is + // really large (it might be big on 64bit platforms) so that the OS + // can't possibly get upset about it being large. + const long length = std::min(max_recv_length, num); + long status = recv(connection_socket,buf,length,0); + if (status == SOCKET_ERROR) + { + // if this error is the result of a shutdown call then return SHUTDOWN + if (sd_called()) + return SHUTDOWN; + else + return OTHER_ERROR; + } + else if (status == 0 && sd_called()) + { + return SHUTDOWN; + } + return status; + } + +// ---------------------------------------------------------------------------------------- + + long connection:: + read ( + char* buf, + long num, + unsigned long timeout + ) + { + if (readable(timeout) == false) + return TIMEOUT; + + const long max_recv_length = 1024*1024*100; + // Make sure to cap the max value num can take on so that if it is + // really large (it might be big on 64bit platforms) so that the OS + // can't possibly get upset about it being large. + const long length = std::min(max_recv_length, num); + long status = recv(connection_socket,buf,length,0); + if (status == SOCKET_ERROR) + { + // if this error is the result of a shutdown call then return SHUTDOWN + if (sd_called()) + return SHUTDOWN; + else + return OTHER_ERROR; + } + else if (status == 0 && sd_called()) + { + return SHUTDOWN; + } + return status; + } + +// ---------------------------------------------------------------------------------------- + + bool connection:: + readable ( + unsigned long timeout + ) const + { + fd_set read_set; + // initialize read_set + FD_ZERO(&read_set); + + // add the listening socket to read_set + FD_SET(connection_socket, &read_set); + + // setup a timeval structure + timeval time_to_wait; + time_to_wait.tv_sec = static_cast<long>(timeout/1000); + time_to_wait.tv_usec = static_cast<long>((timeout%1000)*1000); + + // wait on select + int status = select(0,&read_set,0,0,&time_to_wait); + + // if select timed out or there was an error + if (status <= 0) + return false; + + // data is ready to be read + return true; + } + +// ---------------------------------------------------------------------------------------- + + int connection:: + shutdown_outgoing ( + ) + { + sd_mutex.lock(); + if (sdo || sd) + { + sd_mutex.unlock(); + return sdr; + } + sdo = true; + sdr = ::shutdown(connection_socket,SD_SEND); + + // convert -1 error code into the OTHER_ERROR error code + if (sdr == -1) + sdr = OTHER_ERROR; + + int temp = sdr; + + sd_mutex.unlock(); + return temp; + } + +// ---------------------------------------------------------------------------------------- + + int connection:: + shutdown ( + ) + { + sd_mutex.lock(); + if (sd) + { + sd_mutex.unlock(); + return sdr; + } + sd = true; + SOCKET stemp = connection_socket; + connection_socket = INVALID_SOCKET; + sdr = closesocket(stemp); + + // convert SOCKET_ERROR error code into the OTHER_ERROR error code + if (sdr == SOCKET_ERROR) + sdr = OTHER_ERROR; + + int temp = sdr; + + sd_mutex.unlock(); + return temp; + } + +// ---------------------------------------------------------------------------------------- + + connection::socket_descriptor_type connection:: + get_socket_descriptor ( + ) const + { + return connection_socket.val; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // listener object +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + listener:: + listener( + SOCKET_container sock, + unsigned short port, + const std::string& ip + ) : + listening_socket(*(new SOCKET_container)), + listening_port(port), + listening_ip(ip), + inaddr_any(listening_ip.empty()) + { + listening_socket = sock; + } + +// ---------------------------------------------------------------------------------------- + + listener:: + ~listener ( + ) + { + closesocket(listening_socket); + delete &listening_socket; + } + +// ---------------------------------------------------------------------------------------- + + int listener:: + accept ( + std::unique_ptr<connection>& new_connection, + unsigned long timeout + ) + { + new_connection.reset(0); + connection* con; + int status = this->accept(con, timeout); + + if (status == 0) + new_connection.reset(con); + + return status; + } + +// ---------------------------------------------------------------------------------------- + + int listener:: + accept ( + connection*& new_connection, + unsigned long timeout + ) + { + SOCKET incoming; + sockaddr_in incomingAddr; + int length = sizeof(sockaddr_in); + + // implement timeout with select if timeout is > 0 + if (timeout > 0) + { + fd_set read_set; + // initialize read_set + FD_ZERO(&read_set); + + // add the listening socket to read_set + FD_SET(listening_socket, &read_set); + + // setup a timeval structure + timeval time_to_wait; + time_to_wait.tv_sec = static_cast<long>(timeout/1000); + time_to_wait.tv_usec = static_cast<long>((timeout%1000)*1000); + + + // wait on select + int status = select(0,&read_set,0,0,&time_to_wait); + + // if select timed out + if (status == 0) + return TIMEOUT; + + // if select returned an error + if (status == SOCKET_ERROR) + return OTHER_ERROR; + + } + + + // call accept to get a new connection + incoming=::accept(listening_socket,reinterpret_cast<sockaddr*>(&incomingAddr),&length); + + // if there was an error return OTHER_ERROR + 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; + { + char* foreign_ip_temp = inet_ntoa(incomingAddr.sin_addr); + + // 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 ( + incoming, + reinterpret_cast<sockaddr*>(&local_info), + &length + ) == 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 = listening_ip; + } + + + // set the SO_OOBINLINE option + int flag_value = 1; + if (setsockopt(incoming,SOL_SOCKET,SO_OOBINLINE,reinterpret_cast<const char*>(&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, + foreign_port, + foreign_ip, + listening_port, + local_ip + ); + } + catch (...) { closesocket(incoming); return OTHER_ERROR; } + + return 0; + } + +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + // socket creation functions +// ---------------------------------------------------------------------------------------- +// ---------------------------------------------------------------------------------------- + + int create_listener ( + std::unique_ptr<listener>& new_listener, + unsigned short port, + const std::string& ip + ) + { + new_listener.reset(); + listener* temp; + int status = create_listener(temp,port,ip); + + if (status == 0) + new_listener.reset(temp); + + return status; + } + + int create_listener ( + listener*& new_listener, + unsigned short port, + const std::string& ip + ) + { + // ensure that WSAStartup has been called and WSACleanup will eventually + // be called when program ends + sockets_startup(); + + sockaddr_in sa; // local socket structure + ZeroMemory(&sa,sizeof(sockaddr_in)); // initialize sa + + SOCKET sock = socket (AF_INET, 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.sin_family = AF_INET; + sa.sin_port = htons(port); + if (ip.empty()) + { + // if the listener should listen on any IP + sa.sin_addr.S_un.S_addr = htons(INADDR_ANY); + } + else + { + // if there is a specific ip to listen on + sa.sin_addr.S_un.S_addr = inet_addr(ip.c_str()); + // if inet_addr couldn't convert the ip then return an error + if ( sa.sin_addr.S_un.S_addr == INADDR_NONE ) + { + closesocket(sock); + return OTHER_ERROR; + } + } + + // set the SO_REUSEADDR option + int flag_value = 1; + setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,reinterpret_cast<const char*>(&flag_value),sizeof(int)); + + // bind the new socket to the requested port and ip + if (bind(sock,reinterpret_cast<sockaddr*>(&sa),sizeof(sockaddr_in))==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; + } + + // determine the port used if necessary + if (port == 0) + { + sockaddr_in local_info; + int length = sizeof(sockaddr_in); + if ( getsockname ( + sock, + reinterpret_cast<sockaddr*>(&local_info), + &length + ) == SOCKET_ERROR + ) + { + closesocket(sock); + return OTHER_ERROR; + } + port = ntohs(local_info.sin_port); + } + + + // initialize a listener object on the heap with the new socket + try { new_listener = new listener(sock,port,ip); } + catch(...) { closesocket(sock); return OTHER_ERROR; } + + return 0; + } + +// ---------------------------------------------------------------------------------------- + + int create_connection ( + std::unique_ptr<connection>& new_connection, + unsigned short foreign_port, + const std::string& foreign_ip, + unsigned short local_port, + const std::string& local_ip + ) + { + new_connection.reset(); + connection* temp; + int status = create_connection(temp,foreign_port, foreign_ip, local_port, local_ip); + + if (status == 0) + new_connection.reset(temp); + + return status; + } + + int create_connection ( + connection*& new_connection, + unsigned short foreign_port, + const std::string& foreign_ip, + unsigned short local_port, + const std::string& local_ip + ) + { + // ensure that WSAStartup has been called and WSACleanup + // will eventually be called when program ends + sockets_startup(); + + + sockaddr_in local_sa; // local socket structure + sockaddr_in foreign_sa; // foreign socket structure + ZeroMemory(&local_sa,sizeof(sockaddr_in)); // initialize local_sa + ZeroMemory(&foreign_sa,sizeof(sockaddr_in)); // initialize foreign_sa + + int length; + + SOCKET sock = socket (AF_INET, 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 + foreign_sa.sin_family = AF_INET; + foreign_sa.sin_port = htons(foreign_port); + foreign_sa.sin_addr.S_un.S_addr = inet_addr(foreign_ip.c_str()); + + // if inet_addr couldn't convert the ip then return an error + if ( foreign_sa.sin_addr.S_un.S_addr == INADDR_NONE ) + { + closesocket(sock); + return OTHER_ERROR; + } + + + // set up the local socket structure + local_sa.sin_family = AF_INET; + + // set the local ip + if (local_ip.empty()) + { + // if the listener should listen on any IP + local_sa.sin_addr.S_un.S_addr = htons(INADDR_ANY); + } + else + { + // if there is a specific ip to listen on + local_sa.sin_addr.S_un.S_addr = inet_addr(local_ip.c_str()); + + // if inet_addr couldn't convert the ip then return an error + if (local_sa.sin_addr.S_un.S_addr == INADDR_NONE) + { + closesocket(sock); + return OTHER_ERROR; + } + } + + // set the local port + local_sa.sin_port = htons(local_port); + + + + // bind the new socket to the requested local port and local ip + if ( bind ( + sock, + reinterpret_cast<sockaddr*>(&local_sa), + sizeof(sockaddr_in) + ) == 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; + } + + // connect the socket + if (connect ( + sock, + reinterpret_cast<sockaddr*>(&foreign_sa), + sizeof(sockaddr_in) + ) == 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; + } + + + + // determine the local port and IP and store them in used_local_ip + // and used_local_port + int used_local_port; + std::string used_local_ip; + sockaddr_in local_info; + if (local_port == 0) + { + length = sizeof(sockaddr_in); + if (getsockname ( + sock, + reinterpret_cast<sockaddr*>(&local_info), + &length + ) == SOCKET_ERROR + ) + { + closesocket(sock); + return OTHER_ERROR; + } + used_local_port = ntohs(local_info.sin_port); + } + else + { + used_local_port = local_port; + } + + // determine real local ip + if (local_ip.empty()) + { + // if local_port is not 0 then we must fill the local_info structure + if (local_port != 0) + { + length = sizeof(sockaddr_in); + if ( getsockname ( + sock, + reinterpret_cast<sockaddr*>(&local_info), + &length + ) == SOCKET_ERROR + ) + { + closesocket(sock); + return OTHER_ERROR; + } + } + char* temp = inet_ntoa(local_info.sin_addr); + + // check if inet_ntoa returned an error + if (temp == NULL) + { + closesocket(sock); + return OTHER_ERROR; + } + used_local_ip.assign(temp); + } + else + { + used_local_ip = local_ip; + } + + // set the SO_OOBINLINE option + int flag_value = 1; + if (setsockopt(sock,SOL_SOCKET,SO_OOBINLINE,reinterpret_cast<const char*>(&flag_value),sizeof(int)) == SOCKET_ERROR ) + { + closesocket(sock); + return OTHER_ERROR; + } + + // initialize a connection object on the heap with the new socket + try + { + new_connection = new connection ( + sock, + foreign_port, + foreign_ip, + used_local_port, + used_local_ip + ); + } + catch(...) {closesocket(sock); return OTHER_ERROR; } + + return 0; + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // WIN32 + +#endif // DLIB_SOCKETS_KERNEL_1_CPp_ + |