/liquidwar/src/sockex.c
C | 703 lines | 487 code | 50 blank | 166 comment | 85 complexity | ce7e82b23e1f6edb83546675235d237a MD5 | raw file
Possible License(s): LGPL-2.0, GPL-2.0
- /********************************************************************/
- /* */
- /* L I QQ U U I DD W W A RR 555 */
- /* L I Q Q U U I D D W W A A R R 5 */
- /* L I Q Q U U I D D W W W AAA RR 55 */
- /* L I Q Q U U I D D WW WW A A R R 5 */
- /* LLL I Q Q U I DD W W A A R R 55 */
- /* */
- /* b */
- /* bb y y */
- /* b b yyy */
- /* bb y */
- /* yy */
- /* */
- /* U U FFF O O TTT */
- /* U U F O O O O T */
- /* U U TIRET FF O O O O T */
- /* U U F O O O O T */
- /* U F O O T */
- /* */
- /********************************************************************/
- /*****************************************************************************/
- /* Liquid War is a multiplayer wargame */
- /* Copyright (C) 1998-2013 Christian Mauduit */
- /* */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to the Free Software */
- /* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
- /* */
- /* Liquid War homepage : http://www.ufoot.org/liquidwar/v5 */
- /* Contact author : ufoot@ufoot.org */
- /*****************************************************************************/
- /********************************************************************/
- /* name : sockw32.c */
- /* content : simple wrappers on the winsock API */
- /* last update : April 13th 2001 */
- /********************************************************************/
- /*==================================================================*/
- /* includes */
- /*==================================================================*/
- #ifdef WIN32
- #include <winsock.h>
- #else
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <sys/time.h>
- #include <unistd.h>
- #include <fcntl.h>
- #include <signal.h>
- #endif
- #include <stdlib.h>
- #include <time.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include "sockgen.h"
- #include "log.h"
- /*==================================================================*/
- /* defines */
- /*==================================================================*/
- #define LW_SOCK_RECV_SEC 8
- #define LW_SOCK_RECV_USEC 0
- #define LW_SOCK_SEND_SEC 8
- #define LW_SOCK_SEND_USEC 0
- #define LW_SOCK_SEND_BUFFER_SIZE 100
- #define LW_SOCK_RECV_BUFFER_SIZE 200
- /*==================================================================*/
- /* macros */
- /*==================================================================*/
- #ifndef MIN
- #define MIN(a, b) (((a) < (b)) ? (a) : (b))
- #endif
- #ifndef MAX
- #define MAX(a, b) (((a) > (b)) ? (a) : (b))
- #endif
- /*==================================================================*/
- /* globals */
- /*==================================================================*/
- /*==================================================================*/
- /* static fonctions */
- /*==================================================================*/
- static void clean_buffer (char *buffer, int len);
- /*==================================================================*/
- /* fonctions */
- /*==================================================================*/
- /*------------------------------------------------------------------*/
- /*
- * Cleans up a message buffer from all characters which are not
- * plain standard ASCII. Used for logging information.
- */
- static void
- clean_buffer (char *buffer, int len)
- {
- int i;
- for (i = 0; i < len; ++i)
- {
- if (!isalnum (buffer[i]) && buffer[i] != '-' && buffer[i] != '+')
- {
- buffer[i] = '.';
- }
- }
- }
- /*------------------------------------------------------------------*/
- /*
- * Returns true if the given number bytes is available on the socket
- */
- int
- lw_sock_peek_ex (int *sock, int len)
- {
- int result = 0;
- int res;
- fd_set read;
- struct timeval tv;
- char buffer[LW_SOCK_MESSAGE_SIZE];
- int sock_copy;
- if ((sock_copy = *sock) >= 0)
- {
- if (len <= LW_SOCK_MESSAGE_SIZE - 1)
- {
- FD_ZERO (&read);
- FD_SET (sock_copy, &read);
- tv.tv_sec = 0;
- tv.tv_usec = 0;
- res = select (sock_copy + 1, &read, NULL, NULL, &tv);
- if (res > 0)
- {
- if (FD_ISSET (sock_copy, &read))
- {
- /*
- * First we test if there's enough data available
- */
- if (recv (sock_copy, buffer, len, MSG_PEEK) == len)
- {
- result = 1;
- }
- }
- }
- }
- }
- return result;
- }
- /*------------------------------------------------------------------*/
- /*
- * Sends a string on the network.
- * The advantage of this function over a raw "send" is that it does
- * a "strlen" automatically to know the length of the string, and
- * adds a tailing CR+LF so that the message is "telnet/http compliant"
- */
- int
- lw_sock_send_str_ex (int *sock, char *str)
- {
- int result = 0;
- int len;
- char buffer[LW_SOCK_MESSAGE_SIZE];
- int sock_copy;
- fd_set write;
- struct timeval tv;
- int res;
- int total_sent;
- int sent;
- #ifdef WIN32
- int winerr;
- #endif
- if ((sock_copy = *sock) >= 0)
- {
- result = 1;
- /*
- * We put the string in a buffer, since we'll probably have
- * to modify it (cut if it's too long, add a tailing CR+LF).
- */
- len = strlen (str);
- len = MIN (len, LW_SOCK_MESSAGE_SIZE - 3);
- strncpy (buffer, str, len);
- buffer[len] = '\x0d';
- ++len;
- buffer[len] = '\x0a';
- ++len;
- buffer[len] = '\0';
- total_sent = 0;
- while (total_sent < len && result && (sock_copy = *sock) >= 0)
- {
- FD_ZERO (&write);
- FD_SET (sock_copy, &write);
- tv.tv_sec = LW_SOCK_SEND_SEC;
- tv.tv_usec = LW_SOCK_SEND_USEC;
- errno = 0;
- res = select (sock_copy + 1, NULL, &write, NULL, &tv);
- switch (res)
- {
- case -1:
- #ifdef WIN32
- winerr = WSAGetLastError ();
- if (winerr != WSAEINTR && winerr != WSAENOBUFS)
- {
- result = 0;
- }
- #else
- if (errno != EINTR && errno != ENOBUFS)
- {
- result = 0;
- }
- #endif
- break;
- case 1:
- if (FD_ISSET (sock_copy, &write))
- {
- sent =
- send (sock_copy, buffer + total_sent, len - total_sent,
- 0);
- if (sent > 0 && sent <= len - total_sent)
- {
- total_sent += sent;
- }
- else
- {
- result = 0;
- }
- }
- break;
- default:
- result = 0;
- }
- }
- if (LW_SOCK_LOG)
- {
- switch (result)
- {
- case 1:
- log_print_int (sock_copy);
- log_print_str (" > \"");
- log_print_str (str);
- log_print_str ("\"");
- log_println ();
- log_flush ();
- break;
- case 0:
- log_print_int (sock_copy);
- log_print_str (" > timeout!");
- log_println ();
- break;
- default:
- log_print_int (sock_copy);
- log_print_str (" > unexpected error!");
- log_println ();
- break;
- }
- }
- }
- return result;
- }
- /*------------------------------------------------------------------*/
- /*
- * Receives some data from the network.
- * A tailing "\n" is expected. The routine handles both chr(10) and chr(13)
- * correctly so that the program can be used with telnet under UNIX or
- * windows without much trouble.
- * This tailing "\n" is removed, so the return string is just the exact
- * message which has been send with sock_send.
- * Buffer must be able to accept at least LW_SOCK_MESSAGE_SIZE chars.
- * Note that the function will not block forever, if there's no incoming
- * for more than a #define specified amount of time, it will return
- * an error.
- */
- int
- lw_sock_recv_str_ex (int *sock, char *str)
- {
- int result = 0;
- int pos, l;
- int cr_found;
- char *cr;
- fd_set read;
- int sock_copy;
- struct timeval tv;
- int res;
- #ifdef WIN32
- int winerr;
- #endif
- str[0] = '\0';
- if ((sock_copy = *sock) >= 0)
- {
- result = 1;
- /*
- * We keep on trying to get data until
- * - we get a '\n' character
- * - the size of incoming data exceeds LW_SOCK_MESSAGE_SIZE
- * - there's a reception low level error
- */
- cr_found = 0;
- pos = 0;
- while (!cr_found && pos < LW_SOCK_MESSAGE_SIZE - 1 && result > 0
- && (sock_copy = *sock) >= 0)
- {
- FD_ZERO (&read);
- FD_SET (sock_copy, &read);
- tv.tv_sec = LW_SOCK_RECV_SEC;
- tv.tv_usec = LW_SOCK_RECV_USEC;
- errno = 0;
- res = select (sock_copy + 1, &read, NULL, NULL, &tv);
- switch (res)
- {
- case -1:
- #ifdef WIN32
- winerr = WSAGetLastError ();
- if (winerr != WSAEINTR)
- {
- result = 0;
- }
- #else
- if (errno != EINTR)
- {
- result = 0;
- }
- #endif
- break;
- case 1:
- /*
- * We check that the event we just received concerns the socket
- * we are polling. If the event is not for us, let's consider
- * everything is fine...
- */
- if (!FD_ISSET (sock_copy, &read))
- {
- result = 1;
- }
- else
- {
- /*
- * We get the caracters one by one. This is a performance
- * killer but we don't care since this routine is only
- * used at the very beginning of the game, when the players
- * are connecting themselves. And it has the *big* advantage
- * that "netkeys" are not eaten up by this routine. More
- * precisely, there's used to be a bug because when reading
- * the final "OK" message, the clients read some netkeys
- * along with it, which caused some network inconsistency
- * since the messages had "disappeared".
- */
- if ((l = recv (sock_copy, str + pos, 1, 0)) <= 0)
- {
- /*
- * OK, now we have received a message on this socket
- * but there's no data. In most of the cases this means
- * the socket is dead, so we return -1 instead of 0
- * so that the caller can make the difference between
- * a timeout (0) and a dead socket (-1).
- */
- result = -1;
- }
- else
- {
- /*
- * pos is an offset in the buffer. It is used to keep a
- * trace of where new data should be appended in case we
- * have to call recv several times to retrieve the whole
- * message.
- */
- pos += l;
- /*
- * We add a tailing '\0' so that the string is "C compliant"
- */
- str[pos] = 0;
- /*
- * We seek for character 10, which should be '\n'
- */
- if ((cr = strchr (str, '\x0a')) != NULL)
- {
- cr_found = 1;
- /*
- * We handle the special character '13' for very often,
- * especially when using telnet, the strings come
- * with char(13)chr(10) at the end. So if it happens
- * that after removing the 10 there's still a 13, then
- * we remove the 13 as well.
- */
- if ((cr - str) >= 1 && (*(cr - 1)) == '\x0d')
- {
- /*
- * Let's cut this ugly "ascii 13" character
- * along with "ascii 10"
- */
- (*(cr - 1)) = 0;
- }
- else
- {
- /*
- * No "ascii 13" in sight, simply remove "ascii 10"
- */
- (*cr) = 0;
- }
- }
- result = 1;
- }
- }
- break;
- default:
- result = 0;
- }
- }
- if (LW_SOCK_LOG)
- {
- switch (result)
- {
- case 1:
- log_print_int (sock_copy);
- log_print_str (" < \"");
- log_print_str (str);
- log_print_str ("\"");
- log_println ();
- log_flush ();
- break;
- case 0:
- log_print_int (sock_copy);
- log_print_str (" < timeout!");
- log_println ();
- break;
- case -1:
- log_print_int (sock_copy);
- log_print_str (" < closed!");
- log_println ();
- break;
- default:
- log_print_int (sock_copy);
- log_print_str (" < unexpected error!");
- log_println ();
- break;
- }
- }
- }
- return result;
- }
- /*------------------------------------------------------------------*/
- /*
- * Sends a buffer on the network.
- * Only a standard send wrapper
- */
- int
- lw_sock_send_buffer_ex (int *sock, char *buffer, int len)
- {
- int result = 0;
- char trace[LW_SOCK_MESSAGE_SIZE];
- fd_set write;
- struct timeval tv;
- int sock_copy;
- int res;
- int total_sent;
- int sent;
- #ifdef WIN32
- int winerr;
- #endif
- if ((sock_copy = *sock) >= 0)
- {
- result = 1;
- if (len <= LW_SOCK_MESSAGE_SIZE - 1)
- {
- total_sent = 0;
- while (total_sent < len && result && (sock_copy = *sock) >= 0)
- {
- FD_ZERO (&write);
- FD_SET (sock_copy, &write);
- tv.tv_sec = LW_SOCK_SEND_SEC;
- tv.tv_usec = LW_SOCK_SEND_USEC;
- errno = 0;
- res = select (sock_copy + 1, NULL, &write, NULL, &tv);
- switch (res)
- {
- case -1:
- #ifdef WIN32
- winerr = WSAGetLastError ();
- if (winerr != WSAEINTR && winerr != WSAENOBUFS)
- {
- result = 0;
- }
- #else
- if (errno != EINTR && errno != ENOBUFS)
- {
- result = 0;
- }
- #endif
- break;
- case 1:
- if (FD_ISSET (sock_copy, &write))
- {
- sent = send (sock_copy,
- buffer + total_sent,
- MIN (len - total_sent,
- LW_SOCK_SEND_BUFFER_SIZE), 0);
- if (sent > 0 && sent <= len - total_sent)
- {
- total_sent += sent;
- }
- else
- {
- result = 0;
- }
- }
- break;
- default:
- result = 0;
- }
- }
- if (LW_SOCK_LOG)
- {
- if (result)
- {
- strncpy (trace, buffer, len);
- trace[len] = '\0';
- log_print_int (sock_copy);
- log_print_str (" > [");
- clean_buffer (trace, len);
- log_print_str (trace);
- log_print_str ("]");
- log_println ();
- log_flush ();
- }
- else
- {
- log_print_int (sock_copy);
- log_print_str (" > timeout!");
- log_println ();
- }
- }
- }
- else
- {
- result = 0;
- if (LW_SOCK_LOG)
- {
- log_print_int (sock_copy);
- log_print_str (" > message too large!");
- log_println ();
- }
- }
- }
- return result;
- }
- /*------------------------------------------------------------------*/
- /*
- * Receives a buffer on the network.
- * Only a standard recv wrapper
- */
- int
- lw_sock_recv_buffer_ex (int *sock, char *buffer, int len)
- {
- int result = 0;
- char trace[LW_SOCK_MESSAGE_SIZE];
- int res;
- fd_set read;
- struct timeval tv;
- int sock_copy;
- int total_received;
- int received;
- #ifdef WIN32
- int winerr;
- #endif
- memset (buffer, 0, len);
- if ((sock_copy = *sock) >= 0)
- {
- result = 1;
- if (len <= LW_SOCK_MESSAGE_SIZE - 1)
- {
- total_received = 0;
- while (total_received < len && result && (sock_copy = *sock) >= 0)
- {
- FD_ZERO (&read);
- FD_SET (sock_copy, &read);
- tv.tv_sec = LW_SOCK_RECV_SEC;
- tv.tv_usec = LW_SOCK_RECV_USEC;
- errno = 0;
- res = select (sock_copy + 1, &read, NULL, NULL, &tv);
- switch (res)
- {
- case -1:
- #ifdef WIN32
- winerr = WSAGetLastError ();
- if (winerr != WSAEINTR)
- {
- result = 0;
- }
- #else
- if (errno != EINTR)
- {
- result = 0;
- }
- #endif
- break;
- case 1:
- if (FD_ISSET (sock_copy, &read))
- {
- received = recv (sock_copy,
- buffer + total_received,
- MIN (len - total_received,
- LW_SOCK_RECV_BUFFER_SIZE), 0);
- if (received > 0 && received <= len - total_received)
- {
- total_received += received;
- }
- else
- {
- result = 0;
- }
- }
- break;
- default:
- result = 0;
- }
- }
- if (LW_SOCK_LOG)
- {
- if (result)
- {
- strncpy (trace, buffer, len);
- trace[len] = '\0';
- log_print_int (sock_copy);
- log_print_str (" < [");
- clean_buffer (trace, len);
- log_print_str (trace);
- log_print_str ("]");
- log_println ();
- log_flush ();
- }
- else
- {
- log_print_int (sock_copy);
- log_print_str (" < timeout!");
- log_println ();
- }
- }
- }
- else
- {
- result = 0;
- if (LW_SOCK_LOG)
- {
- log_print_int (sock_copy);
- log_print_str (" < message too large!");
- log_println ();
- }
- }
- }
- return result;
- }