]> git.mxchange.org Git - simgear.git/blobdiff - simgear/io/sg_socket.cxx
Check for valid hostname lookup in sg_socket.cxx.
[simgear.git] / simgear / io / sg_socket.cxx
index d501b29f9410bed468f395ade3b1d3ce9fd90e72..905639b91d2c01f437dbe9a01d2b9827246f5658 100644 (file)
@@ -23,7 +23,7 @@
 
 #include <simgear/compiler.h>
 
-#if ! defined( _MSC_VER )
+#if !defined(_MSC_VER)
 #  include <sys/time.h>                // select()
 #  include <sys/types.h>       // socket(), bind(), select(), accept()
 #  include <sys/socket.h>      // socket(), bind(), listen(), accept()
 #  include <netdb.h>           // gethostbyname()
 #  include <unistd.h>          // select(), fsync()/fdatasync(), fcntl()
 #  include <fcntl.h>           // fcntl()
-#else
-#  include <sys/timeb.h>       // select()
-#  include <winsock2.h>                // socket(), bind(), listen(), accept(),
-                               // struct sockaddr_in, gethostbyname()
-#  include <windows.h>
-#  include <io.h>
 #endif
 
 #if defined( sgi )
 #include <strings.h>
 #endif
 
-#include STL_STRING
-
 #include <simgear/debug/logstream.hxx>
 
 #include "sg_socket.hxx"
 
-FG_USING_STD(string);
-
 
 SGSocket::SGSocket( const string& host, const string& port, 
                    const string& style ) :
+    hostname(host),
+    port_str(port),
     save_len(0)
 {
-    hostname = host;
-    port_str = port;
+#if defined(_MSC_VER)
+    if (!wsock_init && !wsastartup()) {
+       SG_LOG( SG_IO, SG_ALERT, "Winsock not available");
+    }
+#endif
 
     if ( style == "udp" ) {
        sock_style = SOCK_DGRAM;
@@ -65,7 +60,7 @@ SGSocket::SGSocket( const string& host, const string& port,
        sock_style = SOCK_STREAM;
     } else {
        sock_style = SOCK_DGRAM;
-       FG_LOG( FG_IO, FG_ALERT,
+       SG_LOG( SG_IO, SG_ALERT,
                "Error: SGSocket() unknown style = " << style );
     }
 
@@ -77,7 +72,7 @@ SGSocket::~SGSocket() {
 }
 
 
-int SGSocket::make_server_socket () {
+SGSocket::SocketType SGSocket::make_server_socket () {
     struct sockaddr_in name;
 
 #if defined( __CYGWIN__ ) || defined( __CYGWIN32__ ) || defined( sgi ) || defined( _MSC_VER )
@@ -88,10 +83,10 @@ int SGSocket::make_server_socket () {
      
     // Create the socket.
     sock = socket (PF_INET, sock_style, 0);
-    if (sock < 0) {
-       FG_LOG( FG_IO, FG_ALERT, 
-               "Error: socket() failed in make_server_socket()" );
-       return -1;
+    if (sock == INVALID_SOCKET) {
+        SG_LOG( SG_IO, SG_ALERT, 
+                "Error: socket() failed in make_server_socket()" );
+        return INVALID_SOCKET;
     }
      
     // Give the socket a name.
@@ -99,18 +94,18 @@ int SGSocket::make_server_socket () {
     name.sin_addr.s_addr = INADDR_ANY;
     name.sin_port = htons(port); // set port to zero to let system pick
     name.sin_addr.s_addr = htonl (INADDR_ANY);
-    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0) {
-       FG_LOG( FG_IO, FG_ALERT,
-               "Error: bind() failed in make_server_socket()" );
-       return -1;
+    if (bind (sock, (struct sockaddr *) &name, sizeof (name)) != 0) {
+           SG_LOG( SG_IO, SG_ALERT,
+                   "Error: bind() failed in make_server_socket()" );
+        return INVALID_SOCKET;
     }
-     
+
     // Find the assigned port number
     length = sizeof(struct sockaddr_in);
     if ( getsockname(sock, (struct sockaddr *) &name, &length) ) {
-       FG_LOG( FG_IO, FG_ALERT,
+       SG_LOG( SG_IO, SG_ALERT,
                "Error: getsockname() failed in make_server_socket()" );
-       return -1;
+        return INVALID_SOCKET;
     }
     port = ntohs(name.sin_port);
 
@@ -118,18 +113,18 @@ int SGSocket::make_server_socket () {
 }
 
 
-int SGSocket::make_client_socket () {
+SGSocket::SocketType SGSocket::make_client_socket () {
     struct sockaddr_in name;
     struct hostent *hp;
      
-    FG_LOG( FG_IO, FG_INFO, "Make client socket()" );
+    SG_LOG( SG_IO, SG_INFO, "Make client socket()" );
 
     // Create the socket.
     sock = socket (PF_INET, sock_style, 0);
-    if (sock < 0) {
-       FG_LOG( FG_IO, FG_ALERT, 
-               "Error: socket() failed in make_client_socket()" );
-       return -1;
+    if (sock == INVALID_SOCKET) {
+        SG_LOG( SG_IO, SG_ALERT, 
+                "Error: socket() failed in make_server_socket()" );
+        return INVALID_SOCKET;
     }
      
     // specify address family
@@ -137,6 +132,10 @@ int SGSocket::make_client_socket () {
 
     // get the hosts official name/info
     hp = gethostbyname( hostname.c_str() );
+    if (hp == NULL) {
+       SG_LOG( SG_IO, SG_ALERT, "Error: hostname lookup failed" );
+       return INVALID_SOCKET;
+    }
 
     // Connect this socket to the host and the port specified on the
     // command line
@@ -148,26 +147,48 @@ int SGSocket::make_client_socket () {
     name.sin_port = htons(port);
 
     if ( connect(sock, (struct sockaddr *) &name, 
-                sizeof(struct sockaddr_in)) < 0 )
+                sizeof(struct sockaddr_in)) != 0 )
     {
-#ifdef _MSC_VER
-       _close(sock);
-#else
-       std::close(sock);
-#endif
-       FG_LOG( FG_IO, FG_ALERT, 
+       closesocket(sock);
+       SG_LOG( SG_IO, SG_ALERT, 
                "Error: connect() failed in make_client_socket()" );
-       return -1;
+       return INVALID_SOCKET;
     }
 
     return sock;
 }
 
 
+// Wrapper functions
+size_t SGSocket::readsocket( int fd, void *buf, size_t count ) {
+#if defined(_MSC_VER)
+    return ::recv( fd, (char *)buf, count, 0 );
+#else
+    return ::read( fd, buf, count );
+#endif
+}
+
+size_t SGSocket::writesocket( int fd, const void *buf, size_t count ) {
+#if defined(_MSC_VER)
+    return ::send( fd, (const char*)buf, count, 0 );
+#else
+    return ::write( fd, buf, count );
+#endif
+}
+
+#if !defined(_MSC_VER)
+int SGSocket::closesocket( int fd ) {
+    return ::close( fd );
+}
+#endif
+
+
 // If specified as a server (in direction for now) open the master
 // listening socket.  If specified as a client (out direction), open a
 // connection to a server.
-bool SGSocket::open( SGProtocolDir dir ) {
+bool SGSocket::open( const SGProtocolDir d ) {
+    set_dir( d );
+
     if ( port_str == "" || port_str == "any" ) {
        port = 0; 
     } else {
@@ -176,45 +197,69 @@ bool SGSocket::open( SGProtocolDir dir ) {
     
     // client_connections.clear();
 
-    if ( dir == SG_IO_IN ) {
+    if ( get_dir() == SG_IO_IN ) {
        // this means server for now
 
        // Setup socket to listen on.  Set "port" before making this
        // call.  A port of "0" indicates that we want to let the os
        // pick any available port.
        sock = make_server_socket();
-       FG_LOG( FG_IO, FG_INFO, "socket is connected to port = " << port );
+       if ( sock == INVALID_SOCKET ) {
+           SG_LOG( SG_IO, SG_ALERT, "socket creation failed" );
+           return false;
+       }
+
+       SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
 
        if ( sock_style == SOCK_DGRAM ) {
            // Non-blocking UDP
-           fcntl( sock, F_SETFL, O_NONBLOCK );
+           nonblock();
        } else {
            // Blocking TCP
            // Specify the maximum length of the connection queue
            listen( sock, SG_MAX_SOCKET_QUEUE );
        }
 
-    } else if ( dir == SG_IO_OUT ) {
+    } else if ( get_dir() == SG_IO_OUT ) {
        // this means client for now
 
        sock = make_client_socket();
+       // TODO: check for error.
 
        if ( sock_style == SOCK_DGRAM ) {
            // Non-blocking UDP
-           fcntl( sock, F_SETFL, O_NONBLOCK );
+           nonblock();
        }
+    } else if ( get_dir() == SG_IO_BI && sock_style == SOCK_STREAM ) {
+       // this means server for TCP sockets
+
+       // Setup socket to listen on.  Set "port" before making this
+       // call.  A port of "0" indicates that we want to let the os
+       // pick any available port.
+       sock = make_server_socket();
+       // TODO: check for error.
+
+       SG_LOG( SG_IO, SG_INFO, "socket is connected to port = " << port );
+
+       // Blocking TCP
+       // Specify the maximum length of the connection queue
+       listen( sock, SG_MAX_SOCKET_QUEUE );
     } else {
-       FG_LOG( FG_IO, FG_ALERT, 
-               "Error:  bidirection mode not available yet for sockets." );
+       SG_LOG( SG_IO, SG_ALERT, 
+               "Error:  bidirection mode not available for UDP sockets." );
        return false;
     }
 
     if ( sock < 0 ) {
-       FG_LOG( FG_IO, FG_ALERT, "Error opening socket: " << hostname
+       SG_LOG( SG_IO, SG_ALERT, "Error opening socket: " << hostname
                << ":" << port );
        return false;
     }
 
+    // extra SOCK_STREAM stuff
+    msgsock = INVALID_SOCKET;
+    first_read = false;
+
     return true;
 }
 
@@ -222,8 +267,11 @@ bool SGSocket::open( SGProtocolDir dir ) {
 // read data from socket (server)
 // read a block of data of specified size
 int SGSocket::read( char *buf, int length ) {
-    int result = 0;
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
 
+    int result = 0;
     // check for potential input
     fd_set ready;
     FD_ZERO(&ready);
@@ -237,13 +285,22 @@ int SGSocket::read( char *buf, int length ) {
     select(32, &ready, 0, 0, &tv);
 
     if ( FD_ISSET(sock, &ready) ) {
-#ifdef _MSC_VER
-       result = _read( sock, buf, length );
-#else
-       result = std::read( sock, buf, length );
-#endif
+       // cout << "data ready" << endl;
+
+       if ( sock_style == SOCK_STREAM ) {
+           if ( msgsock == INVALID_SOCKET ) {
+               msgsock = accept(sock, 0, 0);
+               closesocket(sock);
+               sock = msgsock;
+           } else {
+               result = readsocket( sock, buf, length );
+           }
+       } else {
+           result = readsocket( sock, buf, length );
+       }
+
        if ( result != length ) {
-           FG_LOG( FG_IO, FG_INFO, 
+           SG_LOG( SG_IO, SG_INFO, 
                    "Warning: read() not enough bytes." );
        }
     }
@@ -254,7 +311,11 @@ int SGSocket::read( char *buf, int length ) {
 
 // read a line of data, length is max size of input buffer
 int SGSocket::readline( char *buf, int length ) {
-    int result = 0;
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
+
+    // cout << "sock = " << sock << endl;
 
     // check for potential input
     fd_set ready;
@@ -266,22 +327,55 @@ int SGSocket::readline( char *buf, int length ) {
 
     // test for any input read on sock (returning immediately, even if
     // nothing)
-    select(32, &ready, 0, 0, &tv);
+    int result = select(32, &ready, 0, 0, &tv);
+    // cout << "result = " << result << endl;
 
     if ( FD_ISSET(sock, &ready) ) {
+       // cout << "fd change state\n";
        // read a chunk, keep in the save buffer until we have the
        // requested amount read
 
-       char *buf_ptr = save_buf + save_len;
-#ifdef _MSC_VER
-       result = _read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
-#else
-       result = std::read( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
-#endif
-       save_len += result;
+       if ( sock_style == SOCK_STREAM ) {
+           // cout << "sock_stream\n";
+           if ( msgsock == INVALID_SOCKET ) {
+               // cout << "msgsock == invalid\n";
+               msgsock = accept(sock, 0, 0);
+               closesocket(sock);
+               sock = msgsock;
+           } else {
+               // cout << "ready to read\n";
+               char *buf_ptr = save_buf + save_len;
+               result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE
+                                    - save_len );
+               // cout << "read result = " << result << endl;
+
+               if ( result > 0 ) {
+                   first_read = true;
+               }
+
+               save_len += result;
+
+               // Try and detect that the remote end died.  This
+               // could cause problems so if you see connections
+               // dropping for unexplained reasons, LOOK HERE!
+               if ( result == 0 && save_len == 0 && first_read == true ) {
+                   SG_LOG( SG_IO, SG_ALERT, 
+                           "Connection closed by foreign host." );
+                   closesocket(sock);
+                   open( get_dir() );
+               }
+           }
+       } else {
+           char *buf_ptr = save_buf + save_len;
+           result = readsocket( sock, buf_ptr, SG_IO_MAX_MSG_SIZE - save_len );
+           save_len += result;
+       }
+
        // cout << "current read = " << buf_ptr << endl;
        // cout << "current save_buf = " << save_buf << endl;
        // cout << "save_len = " << save_len << endl;
+    } else {
+       // cout << "no data ready\n";
     }
 
     // look for the end of line in save_buf
@@ -314,15 +408,15 @@ int SGSocket::readline( char *buf, int length ) {
 
 
 // write data to socket (client)
-int SGSocket::write( char *buf, int length ) {
+int SGSocket::write( const char *buf, const int length ) {
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
+
     bool error_condition = false;
 
-#ifdef _MSC_VER
-    if ( _write(sock, buf, length) < 0 ) {
-#else
-    if ( std::write(sock, buf, length) < 0 ) {
-#endif
-       FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
+    if ( writesocket(sock, buf, length) < 0 ) {
+       SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
        error_condition = true;
     }
 
@@ -343,7 +437,7 @@ int SGSocket::write( char *buf, int length ) {
     if ( FD_ISSET(sock, &ready) ) {
        int msgsock = accept(sock, 0, 0);
        if ( msgsock < 0 ) {
-           FG_LOG( FG_IO, FG_ALERT, 
+           SG_LOG( SG_IO, SG_ALERT, 
                    "Error: accept() failed in write()" );
            return 0;
        } else {
@@ -351,7 +445,7 @@ int SGSocket::write( char *buf, int length ) {
        }
     }
 
-    FG_LOG( FG_IO, FG_INFO, "Client connections = " << 
+    SG_LOG( SG_IO, SG_INFO, "Client connections = " << 
            client_connections.size() );
     for ( int i = 0; i < (int)client_connections.size(); ++i ) {
        int msgsock = client_connections[i];
@@ -361,12 +455,8 @@ int SGSocket::write( char *buf, int length ) {
        // std::read( msgsock, junk, SG_IO_MAX_MSG_SIZE );
 
        // write the interesting data to the socket
-#ifdef _MSC_VER
-       if ( _write(msgsock, buf, length) < 0 ) {
-#else
-       if ( std::write(msgsock, buf, length) < 0 ) {
-#endif
-           FG_LOG( FG_IO, FG_ALERT, "Error writing to socket: " << port );
+       if ( writesocket(msgsock, buf, length) == SOCKET_ERROR ) {
+           SG_LOG( SG_IO, SG_ALERT, "Error writing to socket: " << port );
            error_condition = true;
        } else {
 #ifdef _POSIX_SYNCHRONIZED_IO
@@ -387,7 +477,11 @@ int SGSocket::write( char *buf, int length ) {
 
 
 // write null terminated string to socket (server)
-int SGSocket::writestring( char *str ) {
+int SGSocket::writestring( const char *str ) {
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
+
     int length = strlen( str );
     return write( str, length );
 }
@@ -395,21 +489,63 @@ int SGSocket::writestring( char *str ) {
 
 // close the port
 bool SGSocket::close() {
-#if 0
-    for ( int i = 0; i < (int)client_connections.size(); ++i ) {
-       int msgsock = client_connections[i];
-#ifdef _MSC_VER
-       _close( msgsock );
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
+
+    closesocket( sock );
+    return true;
+}
+
+
+// configure the socket as non-blocking
+bool SGSocket::nonblock() {
+    if ( sock == INVALID_SOCKET ) {
+       return 0;
+    }
+
+#if defined(_MSC_VER)
+    u_long arg = 1;
+    if (ioctlsocket( sock, FIONBIO, &arg ) != 0) {
+        int error_code = WSAGetLastError();
+        SG_LOG( SG_IO, SG_ALERT, 
+                "Error " << error_code << ": unable to set non-blocking mode"
+);
+            return false;
+    }
 #else
-       std::close( msgsock );
+    fcntl( sock, F_SETFL, O_NONBLOCK );
 #endif
+    return true;
+}
+
+#if defined(_MSC_VER)
+
+bool SGSocket::wsock_init = false;
+
+bool
+SGSocket::wsastartup() {
+    WORD wVersionRequested;
+    WSADATA wsaData;
+
+    //wVersionRequested = MAKEWORD( 2, 2 );
+    wVersionRequested = MAKEWORD( 1, 1 );
+    int err = WSAStartup( wVersionRequested, &wsaData );
+    if (err != 0)
+    {
+        SG_LOG( SG_IO, SG_ALERT, "Error: Couldn't load winsock" );
+        return false;
     }
-#endif
 
-#ifdef _MSC_VER
-    _close( sock );
-#else
-    std::close( sock );
+#if 0
+    if ( LOBYTE( wsaData.wVersion ) != 2 ||
+        HIBYTE( wsaData.wVersion ) != 2 ) {
+        SG_LOG( SG_IO, SG_ALERT, "Couldn't load a suitable winsock");
+        WSACleanup( );
+        return false;
+    }
 #endif
+    wsock_init = true;
     return true;
 }
+#endif