/* * Hedgewars, a free turn based strategy game * Copyright (C) 2012 Simeon Maxein <smaxein@googlemail.com> * * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. */#include "socket.h"#include "util/logging.h"#include "util/util.h"#include <stdlib.h>#include <SDL_net.h>#include <time.h>struct _flib_tcpsocket { TCPsocket sock; SDLNet_SocketSet sockset;};struct _flib_acceptor { TCPsocket sock; uint16_t port;};static uint32_t getPeerIp(TCPsocket sock) { IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); return SDLNet_Read32(&addr->host);}static bool connectionIsLocal(TCPsocket sock) { return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1}static flib_tcpsocket *createSocket(TCPsocket sdlsock) { flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket)); if(result) { result->sock = sdlsock; result->sockset = SDLNet_AllocSocketSet(1); if(!result->sockset) { flib_log_e("Can't allocate socket: Out of memory!"); SDLNet_FreeSocketSet(result->sockset); free(result); result = NULL; } else { SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); } } return result;}TCPsocket listen(uint16_t port) { IPaddress addr; addr.host = INADDR_ANY; SDLNet_Write16(port, &addr.port); TCPsocket sock = SDLNet_TCP_Open(&addr); if(!sock) { flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError()); } return sock;}flib_acceptor *flib_acceptor_create(uint16_t port) { flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor)); if(result) { if(port > 0) { result->port = port; result->sock = listen(result->port); } else { /* SDL_net does not seem to have a way to listen on a random unused port and find out which port that is, so let's try to find one ourselves. */ srand(time(NULL)); for(int i=0; !result->sock && i<1000; i++) { // IANA suggests using ports in the range 49152-65535 for things like this result->port = 49152+(rand()%(65536-49152)); result->sock = listen(result->port); } } if(!result->sock) { flib_log_e("Failed to create acceptor."); free(result); result = NULL; } } return result;}uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) { if(!acceptor) { flib_log_e("Call to flib_acceptor_listenport with acceptor==null"); return 0; } return acceptor->port;}void flib_acceptor_close(flib_acceptor *acceptor) { if(acceptor) { SDLNet_TCP_Close(acceptor->sock); free(acceptor); }}flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) { flib_tcpsocket *result = NULL; if(!acceptor) { flib_log_e("Call to flib_socket_accept with acceptor==null"); } else { TCPsocket sock = NULL; while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) { if(localOnly && !connectionIsLocal(sock)) { flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock))); } else { result = createSocket(sock); } if(!result) { SDLNet_TCP_Close(sock); } } } return result;}flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) { flib_tcpsocket *result = NULL; if(!host || port==0) { flib_log_e("Invalid parameter in flib_socket_connect"); } else { IPaddress ip; if(SDLNet_ResolveHost(&ip,host,port)==-1) { flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); } else { TCPsocket sock=SDLNet_TCP_Open(&ip); if(!sock) { flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); } else { result = createSocket(sock); if(result) { sock = NULL; } } SDLNet_TCP_Close(sock); } } return result;}void flib_socket_close(flib_tcpsocket *sock) { if(sock) { SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock); SDLNet_TCP_Close(sock->sock); SDLNet_FreeSocketSet(sock->sockset); free(sock); }}int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen) { if(!sock || (maxlen>0 && !data)) { flib_log_e("Call to flib_socket_nbrecv with sock==null or data==null"); return -1; } int readySockets = SDLNet_CheckSockets(sock->sockset, 0); if(readySockets>0) { int size = SDLNet_TCP_Recv(sock->sock, data, maxlen); return size>0 ? size : -1; } else if(readySockets==0) { return 0; } else { flib_log_e("Error in select system call: %s", SDLNet_GetError()); return -1; }}int flib_socket_send(flib_tcpsocket *sock, const void *data, int len) { if(!sock || (len>0 && !data)) { flib_log_e("Call to flib_socket_send with sock==null or data==null"); return -1; } return SDLNet_TCP_Send(sock->sock, data, len);}