10017
+ − 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()%(65536-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
}