1 //
2 // Copyright(C) 2005-2014 Simon Howard
3 //
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // DESCRIPTION:
15 //     Networking module which uses SDL_net
16 //
17 
18 #include <stdlib.h>
19 #include <string.h>
20 #include <stdio.h>
21 
22 #include "doomtype.h"
23 #include "i_system.h"
24 #include "m_argv.h"
25 #include "m_misc.h"
26 #include "net_defs.h"
27 #include "net_io.h"
28 #include "net_packet.h"
29 #include "net_sdl.h"
30 #include "z_zone.h"
31 
32 //
33 // NETWORKING
34 //
35 
36 #include <SDL_net.h>
37 
38 #define DEFAULT_PORT 2342
39 
40 static boolean initted = false;
41 static int port = DEFAULT_PORT;
42 static UDPsocket udpsocket;
43 static UDPpacket *recvpacket;
44 
45 typedef struct
46 {
47     net_addr_t net_addr;
48     IPaddress sdl_addr;
49 } addrpair_t;
50 
51 static addrpair_t **addr_table;
52 static int addr_table_size = -1;
53 
54 // Initializes the address table
55 
NET_SDL_InitAddrTable(void)56 static void NET_SDL_InitAddrTable(void)
57 {
58     addr_table_size = 16;
59 
60     addr_table = Z_Malloc(sizeof(addrpair_t *) * addr_table_size,
61                           PU_STATIC, 0);
62     memset(addr_table, 0, sizeof(addrpair_t *) * addr_table_size);
63 }
64 
AddressesEqual(IPaddress * a,IPaddress * b)65 static boolean AddressesEqual(IPaddress *a, IPaddress *b)
66 {
67     return a->host == b->host
68         && a->port == b->port;
69 }
70 
71 // Finds an address by searching the table.  If the address is not found,
72 // it is added to the table.
73 
NET_SDL_FindAddress(IPaddress * addr)74 static net_addr_t *NET_SDL_FindAddress(IPaddress *addr)
75 {
76     addrpair_t *new_entry;
77     int empty_entry = -1;
78     int i;
79 
80     if (addr_table_size < 0)
81     {
82         NET_SDL_InitAddrTable();
83     }
84 
85     for (i=0; i<addr_table_size; ++i)
86     {
87         if (addr_table[i] != NULL
88          && AddressesEqual(addr, &addr_table[i]->sdl_addr))
89         {
90             return &addr_table[i]->net_addr;
91         }
92 
93         if (empty_entry < 0 && addr_table[i] == NULL)
94             empty_entry = i;
95     }
96 
97     // Was not found in list.  We need to add it.
98 
99     // Is there any space in the table? If not, increase the table size
100 
101     if (empty_entry < 0)
102     {
103         addrpair_t **new_addr_table;
104         int new_addr_table_size;
105 
106         // after reallocing, we will add this in as the first entry
107         // in the new block of memory
108 
109         empty_entry = addr_table_size;
110 
111         // allocate a new array twice the size, init to 0 and copy
112         // the existing table in.  replace the old table.
113 
114         new_addr_table_size = addr_table_size * 2;
115         new_addr_table = Z_Malloc(sizeof(addrpair_t *) * new_addr_table_size,
116                                   PU_STATIC, 0);
117         memset(new_addr_table, 0, sizeof(addrpair_t *) * new_addr_table_size);
118         memcpy(new_addr_table, addr_table,
119                sizeof(addrpair_t *) * addr_table_size);
120         Z_Free(addr_table);
121         addr_table = new_addr_table;
122         addr_table_size = new_addr_table_size;
123     }
124 
125     // Add a new entry
126 
127     new_entry = Z_Malloc(sizeof(addrpair_t), PU_STATIC, 0);
128 
129     new_entry->sdl_addr = *addr;
130     new_entry->net_addr.refcount = 0;
131     new_entry->net_addr.handle = &new_entry->sdl_addr;
132     new_entry->net_addr.module = &net_sdl_module;
133 
134     addr_table[empty_entry] = new_entry;
135 
136     return &new_entry->net_addr;
137 }
138 
NET_SDL_FreeAddress(net_addr_t * addr)139 static void NET_SDL_FreeAddress(net_addr_t *addr)
140 {
141     int i;
142 
143     for (i=0; i<addr_table_size; ++i)
144     {
145         if (addr == &addr_table[i]->net_addr)
146         {
147             Z_Free(addr_table[i]);
148             addr_table[i] = NULL;
149             return;
150         }
151     }
152 
153     I_Error("NET_SDL_FreeAddress: Attempted to remove an unused address!");
154 }
155 
NET_SDL_InitClient(void)156 static boolean NET_SDL_InitClient(void)
157 {
158     int p;
159 
160     if (initted)
161         return true;
162 
163     //!
164     // @category net
165     // @arg <n>
166     //
167     // Use the specified UDP port for communications, instead of
168     // the default (2342).
169     //
170 
171     p = M_CheckParmWithArgs("-port", 1);
172     if (p > 0)
173         port = atoi(myargv[p+1]);
174 
175     SDLNet_Init();
176 
177     udpsocket = SDLNet_UDP_Open(0);
178 
179     if (udpsocket == NULL)
180     {
181         I_Error("NET_SDL_InitClient: Unable to open a socket!");
182     }
183 
184     recvpacket = SDLNet_AllocPacket(1500);
185 
186 #ifdef DROP_PACKETS
187     srand(time(NULL));
188 #endif
189 
190     initted = true;
191 
192     return true;
193 }
194 
NET_SDL_InitServer(void)195 static boolean NET_SDL_InitServer(void)
196 {
197     int p;
198 
199     if (initted)
200         return true;
201 
202     p = M_CheckParmWithArgs("-port", 1);
203     if (p > 0)
204         port = atoi(myargv[p+1]);
205 
206     SDLNet_Init();
207 
208     udpsocket = SDLNet_UDP_Open(port);
209 
210     if (udpsocket == NULL)
211     {
212         I_Error("NET_SDL_InitServer: Unable to bind to port %i", port);
213     }
214 
215     recvpacket = SDLNet_AllocPacket(1500);
216 #ifdef DROP_PACKETS
217     srand(time(NULL));
218 #endif
219 
220     initted = true;
221 
222     return true;
223 }
224 
NET_SDL_SendPacket(net_addr_t * addr,net_packet_t * packet)225 static void NET_SDL_SendPacket(net_addr_t *addr, net_packet_t *packet)
226 {
227     UDPpacket sdl_packet;
228     IPaddress ip;
229 
230     if (addr == &net_broadcast_addr)
231     {
232         SDLNet_ResolveHost(&ip, NULL, port);
233         ip.host = INADDR_BROADCAST;
234     }
235     else
236     {
237         ip = *((IPaddress *) addr->handle);
238     }
239 
240 #if 0
241     {
242         static int this_second_sent = 0;
243         static int lasttime;
244 
245         this_second_sent += packet->len + 64;
246 
247         if (I_GetTime() - lasttime > TICRATE)
248         {
249             printf("%i bytes sent in the last second\n", this_second_sent);
250             lasttime = I_GetTime();
251             this_second_sent = 0;
252         }
253     }
254 #endif
255 
256 #ifdef DROP_PACKETS
257     if ((rand() % 4) == 0)
258         return;
259 #endif
260 
261     sdl_packet.channel = 0;
262     sdl_packet.data = packet->data;
263     sdl_packet.len = packet->len;
264     sdl_packet.address = ip;
265 
266     if (!SDLNet_UDP_Send(udpsocket, -1, &sdl_packet))
267     {
268         I_Error("NET_SDL_SendPacket: Error transmitting packet: %s",
269                 SDLNet_GetError());
270     }
271 }
272 
NET_SDL_RecvPacket(net_addr_t ** addr,net_packet_t ** packet)273 static boolean NET_SDL_RecvPacket(net_addr_t **addr, net_packet_t **packet)
274 {
275     int result;
276 
277     result = SDLNet_UDP_Recv(udpsocket, recvpacket);
278 
279     if (result < 0)
280     {
281         I_Error("NET_SDL_RecvPacket: Error receiving packet: %s",
282                 SDLNet_GetError());
283     }
284 
285     // no packets received
286 
287     if (result == 0)
288         return false;
289 
290     // Put the data into a new packet structure
291 
292     *packet = NET_NewPacket(recvpacket->len);
293     memcpy((*packet)->data, recvpacket->data, recvpacket->len);
294     (*packet)->len = recvpacket->len;
295 
296     // Address
297 
298     *addr = NET_SDL_FindAddress(&recvpacket->address);
299 
300     return true;
301 }
302 
NET_SDL_AddrToString(net_addr_t * addr,char * buffer,int buffer_len)303 void NET_SDL_AddrToString(net_addr_t *addr, char *buffer, int buffer_len)
304 {
305     IPaddress *ip;
306     uint32_t host;
307     uint16_t port;
308 
309     ip = (IPaddress *) addr->handle;
310     host = SDLNet_Read32(&ip->host);
311     port = SDLNet_Read16(&ip->port);
312 
313     M_snprintf(buffer, buffer_len, "%i.%i.%i.%i",
314                (host >> 24) & 0xff, (host >> 16) & 0xff,
315                (host >> 8) & 0xff, host & 0xff);
316 
317     // If we are using the default port we just need to show the IP address,
318     // but otherwise we need to include the port. This is important because
319     // we use the string representation in the setup tool to provided an
320     // address to connect to.
321     if (port != DEFAULT_PORT)
322     {
323         char portbuf[10];
324         M_snprintf(portbuf, sizeof(portbuf), ":%i", port);
325         M_StringConcat(buffer, portbuf, buffer_len);
326     }
327 }
328 
NET_SDL_ResolveAddress(const char * address)329 net_addr_t *NET_SDL_ResolveAddress(const char *address)
330 {
331     IPaddress ip;
332     char *addr_hostname;
333     int addr_port;
334     int result;
335     char *colon;
336 
337     colon = strchr(address, ':');
338 
339     addr_hostname = M_StringDuplicate(address);
340     if (colon != NULL)
341     {
342 	addr_hostname[colon - address] = '\0';
343 	addr_port = atoi(colon + 1);
344     }
345     else
346     {
347 	addr_port = port;
348     }
349 
350     result = SDLNet_ResolveHost(&ip, addr_hostname, addr_port);
351 
352     free(addr_hostname);
353 
354     if (result)
355     {
356         // unable to resolve
357 
358         return NULL;
359     }
360     else
361     {
362         return NET_SDL_FindAddress(&ip);
363     }
364 }
365 
366 // Complete module
367 
368 net_module_t net_sdl_module =
369 {
370     NET_SDL_InitClient,
371     NET_SDL_InitServer,
372     NET_SDL_SendPacket,
373     NET_SDL_RecvPacket,
374     NET_SDL_AddrToString,
375     NET_SDL_FreeAddress,
376     NET_SDL_ResolveAddress,
377 };
378 
379