project_files/frontlib/socket.c
changeset 7171 906e72caea7b
child 7175 038e3415100a
equal deleted inserted replaced
7169:b66eef8c8092 7171:906e72caea7b
       
     1 #include "socket.h"
       
     2 #include "logging.h"
       
     3 #include <stdlib.h>
       
     4 #include <SDL_net.h>
       
     5 #include <time.h>
       
     6 
       
     7 typedef struct _flib_tcpsocket {
       
     8 	TCPsocket sock;
       
     9 	SDLNet_SocketSet sockset;
       
    10 } _flib_tcpsocket;
       
    11 
       
    12 typedef struct _flib_acceptor {
       
    13 	TCPsocket sock;
       
    14 	uint16_t port;
       
    15 } _flib_acceptor;
       
    16 
       
    17 static uint32_t get_peer_ip(TCPsocket sock) {
       
    18 	IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
       
    19 	return SDLNet_Read32(&addr->host);
       
    20 }
       
    21 
       
    22 static bool connection_is_local(TCPsocket sock) {
       
    23 	return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
       
    24 }
       
    25 
       
    26 flib_acceptor flib_acceptor_create(uint16_t port) {
       
    27 	flib_acceptor result = malloc(sizeof(_flib_acceptor));
       
    28 	if(!result) {
       
    29 		flib_log_e("Out of memory!");
       
    30 		return NULL;
       
    31 	}
       
    32 
       
    33 	IPaddress addr;
       
    34 	addr.host = INADDR_ANY;
       
    35 
       
    36 	if(port > 0) {
       
    37 		result->port = port;
       
    38 		SDLNet_Write16(port, &addr.port);
       
    39 		result->sock = SDLNet_TCP_Open(&addr);
       
    40 		if(result->sock) {
       
    41 			return result;
       
    42 		} else {
       
    43 			flib_log_e("Unable to listen on port %u: %s", port, SDLNet_GetError());
       
    44 			free(result);
       
    45 			return NULL;
       
    46 		}
       
    47 	} else {
       
    48 		/* SDL_net does not seem to have a way to listen on a random unused port
       
    49 		   and find out which port that is, so let's try to find one ourselves. */
       
    50 		// TODO: Is socket binding fail-fast on all platforms?
       
    51 		srand(time(NULL));
       
    52 		rand();
       
    53 		for(int i=0; i<1000; i++) {
       
    54 			// IANA suggests using ports in the range 49152-65535 for things like this
       
    55 			result->port = 49152+(rand()%(65535-49152));
       
    56 			SDLNet_Write16(result->port, &addr.port);
       
    57 			result->sock = SDLNet_TCP_Open(&addr);
       
    58 			if(result->sock) {
       
    59 				return result;
       
    60 			} else {
       
    61 				flib_log_i("Unable to listen on port %u: %s", result->port, SDLNet_GetError());
       
    62 			}
       
    63 		}
       
    64 		flib_log_e("Unable to listen on a random unused port.");
       
    65 		free(result);
       
    66 		return NULL;
       
    67 	}
       
    68 }
       
    69 
       
    70 uint16_t flib_acceptor_listenport(flib_acceptor acceptor) {
       
    71 	return acceptor->port;
       
    72 }
       
    73 
       
    74 void flib_acceptor_close(flib_acceptor *acceptorptr) {
       
    75 	if(acceptorptr == NULL || *acceptorptr == NULL) {
       
    76 		return;
       
    77 	}
       
    78 	SDLNet_TCP_Close((*acceptorptr)->sock);
       
    79 	free(*acceptorptr);
       
    80 	*acceptorptr = NULL;
       
    81 }
       
    82 
       
    83 flib_tcpsocket flib_socket_accept(flib_acceptor acceptor, bool localOnly) {
       
    84 	flib_tcpsocket result = NULL;
       
    85 	if(!acceptor) {
       
    86 		return NULL;
       
    87 	}
       
    88 	while(result==NULL) {
       
    89 		TCPsocket sock = SDLNet_TCP_Accept(acceptor->sock);
       
    90 		if(!sock) {
       
    91 			// No incoming connections
       
    92 			return NULL;
       
    93 		}
       
    94 		if(localOnly && !connection_is_local(sock)) {
       
    95 			flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock)));
       
    96 			SDLNet_TCP_Close(sock);
       
    97 		} else {
       
    98 			result = malloc(sizeof(_flib_tcpsocket));
       
    99 			if(result==NULL) {
       
   100 				flib_log_e("Out of memory!");
       
   101 				SDLNet_TCP_Close(sock);
       
   102 				return NULL;
       
   103 			}
       
   104 			result->sock = sock;
       
   105 			result->sockset = SDLNet_AllocSocketSet(1);
       
   106 			if(result->sockset==NULL) {
       
   107 				flib_log_e("Out of memory!");
       
   108 				SDLNet_TCP_Close(sock);
       
   109 				free(result);
       
   110 				return NULL;
       
   111 			}
       
   112 			SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
       
   113 		}
       
   114 	}
       
   115 	return result;
       
   116 }
       
   117 
       
   118 void flib_socket_close(flib_tcpsocket *sockptr) {
       
   119 	if(sockptr==NULL || *sockptr == NULL) {
       
   120 		return;
       
   121 	}
       
   122 	flib_tcpsocket sock = *sockptr;
       
   123 	SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
       
   124 	SDLNet_TCP_Close(sock->sock);
       
   125 	SDLNet_FreeSocketSet(sock->sockset);
       
   126 	free(sock);
       
   127 	*sockptr = NULL;
       
   128 }
       
   129 
       
   130 int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen) {
       
   131 	if(!sock) {
       
   132 		flib_log_e("Attempt to receive on a NULL socket.");
       
   133 		return -1;
       
   134 	}
       
   135 	int readySockets = SDLNet_CheckSockets(sock->sockset, 0);
       
   136 	if(readySockets>0) {
       
   137 		int size = SDLNet_TCP_Recv(sock->sock, data, maxlen);
       
   138 		return size>0 ? size : -1;
       
   139 	} else if(readySockets==0) {
       
   140 		return 0;
       
   141 	} else {
       
   142 		flib_log_e("Error in select system call: %s", SDLNet_GetError());
       
   143 		return -1;
       
   144 	}
       
   145 }
       
   146 
       
   147 int flib_socket_send(flib_tcpsocket sock, void *data, int len) {
       
   148 	return SDLNet_TCP_Send(sock->sock, data, len);
       
   149 }