project_files/frontlib/socket.c
branchhedgeroid
changeset 7857 2bc61f8841a1
parent 7314 6171f0bad318
child 8091 e0a76056a633
equal deleted inserted replaced
7855:ddcdedd3330b 7857:2bc61f8841a1
       
     1 /*
       
     2  * Hedgewars, a free turn based strategy game
       
     3  * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com>
       
     4  *
       
     5  * This program is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU General Public License
       
     7  * as published by the Free Software Foundation; either version 2
       
     8  * of the License, or (at your option) any later version.
       
     9  *
       
    10  * This program is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    13  * GNU General Public License for more details.
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License
       
    16  * along with this program; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
       
    18  */
       
    19 
       
    20 #include "socket.h"
       
    21 #include "util/logging.h"
       
    22 #include "util/util.h"
       
    23 #include <stdlib.h>
       
    24 #include <SDL_net.h>
       
    25 #include <time.h>
       
    26 
       
    27 struct _flib_tcpsocket {
       
    28 	TCPsocket sock;
       
    29 	SDLNet_SocketSet sockset;
       
    30 };
       
    31 
       
    32 struct _flib_acceptor {
       
    33 	TCPsocket sock;
       
    34 	uint16_t port;
       
    35 };
       
    36 
       
    37 static uint32_t getPeerIp(TCPsocket sock) {
       
    38 	IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock);
       
    39 	return SDLNet_Read32(&addr->host);
       
    40 }
       
    41 
       
    42 static bool connectionIsLocal(TCPsocket sock) {
       
    43 	return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1
       
    44 }
       
    45 
       
    46 static flib_tcpsocket *createSocket(TCPsocket sdlsock) {
       
    47 	flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket));
       
    48 	if(result) {
       
    49 		result->sock = sdlsock;
       
    50 		result->sockset = SDLNet_AllocSocketSet(1);
       
    51 
       
    52 		if(!result->sockset) {
       
    53 			flib_log_e("Can't allocate socket: Out of memory!");
       
    54 			SDLNet_FreeSocketSet(result->sockset);
       
    55 			free(result);
       
    56 			result = NULL;
       
    57 		} else {
       
    58 			SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock);
       
    59 		}
       
    60 	}
       
    61 	return result;
       
    62 }
       
    63 
       
    64 TCPsocket listen(uint16_t port) {
       
    65 	IPaddress addr;
       
    66 	addr.host = INADDR_ANY;
       
    67 	SDLNet_Write16(port, &addr.port);
       
    68 	TCPsocket sock = SDLNet_TCP_Open(&addr);
       
    69 	if(!sock) {
       
    70 		flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError());
       
    71 	}
       
    72 	return sock;
       
    73 }
       
    74 
       
    75 flib_acceptor *flib_acceptor_create(uint16_t port) {
       
    76 	flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor));
       
    77 	if(result) {
       
    78 		if(port > 0) {
       
    79 			result->port = port;
       
    80 			result->sock = listen(result->port);
       
    81 		} else {
       
    82 			/* SDL_net does not seem to have a way to listen on a random unused port
       
    83 			   and find out which port that is, so let's try to find one ourselves. */
       
    84 			srand(time(NULL));
       
    85 			for(int i=0; !result->sock && i<1000; i++) {
       
    86 				// IANA suggests using ports in the range 49152-65535 for things like this
       
    87 				result->port = 49152+(rand()%(65535-49152));
       
    88 				result->sock = listen(result->port);
       
    89 			}
       
    90 		}
       
    91 		if(!result->sock) {
       
    92 			flib_log_e("Failed to create acceptor.");
       
    93 			free(result);
       
    94 			result = NULL;
       
    95 		}
       
    96 	}
       
    97 	return result;
       
    98 }
       
    99 
       
   100 uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) {
       
   101 	if(!acceptor) {
       
   102 		flib_log_e("Call to flib_acceptor_listenport with acceptor==null");
       
   103 		return 0;
       
   104 	}
       
   105 	return acceptor->port;
       
   106 }
       
   107 
       
   108 void flib_acceptor_close(flib_acceptor *acceptor) {
       
   109 	if(acceptor) {
       
   110 		SDLNet_TCP_Close(acceptor->sock);
       
   111 		free(acceptor);
       
   112 	}
       
   113 }
       
   114 
       
   115 flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) {
       
   116 	flib_tcpsocket *result = NULL;
       
   117 	if(!acceptor) {
       
   118 		flib_log_e("Call to flib_socket_accept with acceptor==null");
       
   119 	} else {
       
   120 		TCPsocket sock = NULL;
       
   121 		while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) {
       
   122 			if(localOnly && !connectionIsLocal(sock)) {
       
   123 				flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock)));
       
   124 			} else {
       
   125 				result = createSocket(sock);
       
   126 			}
       
   127 			if(!result) {
       
   128 				SDLNet_TCP_Close(sock);
       
   129 			}
       
   130 		}
       
   131 	}
       
   132 	return result;
       
   133 }
       
   134 
       
   135 flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) {
       
   136 	flib_tcpsocket *result = NULL;
       
   137 	if(!host || port==0) {
       
   138 		flib_log_e("Invalid parameter in flib_socket_connect");
       
   139 	} else {
       
   140 		IPaddress ip;
       
   141 		if(SDLNet_ResolveHost(&ip,host,port)==-1) {
       
   142 		   flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError());
       
   143 		} else {
       
   144 			TCPsocket sock=SDLNet_TCP_Open(&ip);
       
   145 			if(!sock) {
       
   146 				flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError());
       
   147 			} else {
       
   148 				result = createSocket(sock);
       
   149 				if(result) {
       
   150 					sock = NULL;
       
   151 				}
       
   152 			}
       
   153 			SDLNet_TCP_Close(sock);
       
   154 		}
       
   155 	}
       
   156 	return result;
       
   157 }
       
   158 
       
   159 void flib_socket_close(flib_tcpsocket *sock) {
       
   160 	if(sock) {
       
   161 		SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock);
       
   162 		SDLNet_TCP_Close(sock->sock);
       
   163 		SDLNet_FreeSocketSet(sock->sockset);
       
   164 		free(sock);
       
   165 	}
       
   166 }
       
   167 
       
   168 int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen) {
       
   169 	if(!sock || (maxlen>0 && !data)) {
       
   170 		flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null");
       
   171 		return -1;
       
   172 	}
       
   173 	int readySockets = SDLNet_CheckSockets(sock->sockset, 0);
       
   174 	if(readySockets>0) {
       
   175 		int size = SDLNet_TCP_Recv(sock->sock, data, maxlen);
       
   176 		return size>0 ? size : -1;
       
   177 	} else if(readySockets==0) {
       
   178 		return 0;
       
   179 	} else {
       
   180 		flib_log_e("Error in select system call: %s", SDLNet_GetError());
       
   181 		return -1;
       
   182 	}
       
   183 }
       
   184 
       
   185 int flib_socket_send(flib_tcpsocket *sock, const void *data, int len) {
       
   186 	if(!sock || (len>0 && !data)) {
       
   187 		flib_log_e("Call to flib_socket_send with sock==null or data==null");
       
   188 		return -1;
       
   189 	}
       
   190 	return SDLNet_TCP_Send(sock->sock, data, len);
       
   191 }