1 // Emacs style mode select	 -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: i_net.c,v 1.2 1997/12/29 19:50:54 pekangas Exp $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
11 //
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
16 //
17 // DESCRIPTION:
18 //		Low-level networking code. Uses BSD sockets for UDP networking.
19 //
20 //-----------------------------------------------------------------------------
21 
22 
23 /* [Petteri] Check if compiling for Win32:	*/
24 #if defined(__WINDOWS__) || defined(__NT__) || defined(_MSC_VER) || defined(_WIN32)
25 #ifndef __WIN32__
26 #	define __WIN32__
27 #endif
28 #endif
29 /* Follow #ifdef __WIN32__ marks */
30 
31 #include <stdlib.h>
32 #include <string.h>
33 #include <stdio.h>
34 
35 /* [Petteri] Use Winsock for Win32: */
36 #ifdef __WIN32__
37 #	define WIN32_LEAN_AND_MEAN
38 #	include <windows.h>
39 #	include <winsock.h>
40 #define USE_WINDOWS_DWORD
41 #else
42 #	include <sys/socket.h>
43 #	include <netinet/in.h>
44 #	include <arpa/inet.h>
45 #	include <errno.h>
46 #	include <unistd.h>
47 #	include <netdb.h>
48 #	include <sys/ioctl.h>
49 #	ifdef __sun
50 #		include <fcntl.h>
51 #	endif
52 #endif
53 
54 #include "doomtype.h"
55 #include "i_system.h"
56 #include "d_event.h"
57 #include "d_net.h"
58 #include "m_argv.h"
59 #include "m_swap.h"
60 #include "m_crc32.h"
61 #include "d_player.h"
62 #include "templates.h"
63 #include "c_console.h"
64 #include "st_start.h"
65 #include "m_misc.h"
66 #include "doomstat.h"
67 
68 #include "i_net.h"
69 
70 // As per http://support.microsoft.com/kb/q192599/ the standard
71 // size for network buffers is 8k.
72 #define TRANSMIT_SIZE		8000
73 
74 /* [Petteri] Get more portable: */
75 #ifndef __WIN32__
76 typedef int SOCKET;
77 #define SOCKET_ERROR		-1
78 #define INVALID_SOCKET		-1
79 #define closesocket			close
80 #define ioctlsocket			ioctl
81 #define Sleep(x)			usleep (x * 1000)
82 #define WSAEWOULDBLOCK		EWOULDBLOCK
83 #define WSAECONNRESET		ECONNRESET
84 #define WSAGetLastError()	errno
85 #endif
86 
87 #ifndef IPPORT_USERRESERVED
88 #define IPPORT_USERRESERVED 5000
89 #endif
90 
91 #ifdef __WIN32__
92 typedef int socklen_t;
93 #endif
94 
95 //
96 // NETWORKING
97 //
98 
99 static u_short DOOMPORT = (IPPORT_USERRESERVED + 29);
100 static SOCKET mysocket = INVALID_SOCKET;
101 static sockaddr_in sendaddress[MAXNETNODES];
102 static BYTE sendplayer[MAXNETNODES];
103 
104 #ifdef __WIN32__
105 const char *neterror (void);
106 #else
107 #define neterror() strerror(errno)
108 #endif
109 
110 enum
111 {
112 	PRE_CONNECT,			// Sent from guest to host for initial connection
113 	PRE_KEEPALIVE,
114 	PRE_DISCONNECT,			// Sent from guest that aborts the game
115 	PRE_ALLHERE,			// Sent from host to guest when everybody has connected
116 	PRE_CONACK,				// Sent from host to guest to acknowledge PRE_CONNECT receipt
117 	PRE_ALLFULL,			// Sent from host to an unwanted guest
118 	PRE_ALLHEREACK,			// Sent from guest to host to acknowledge PRE_ALLHEREACK receipt
119 	PRE_GO					// Sent from host to guest to continue game startup
120 };
121 
122 // Set PreGamePacket.fake to this so that the game rejects any pregame packets
123 // after it starts. This translates to NCMD_SETUP|NCMD_MULTI.
124 #define PRE_FAKE 0x30
125 
126 struct PreGamePacket
127 {
128 	BYTE Fake;
129 	BYTE Message;
130 	BYTE NumNodes;
131 	union
132 	{
133 		BYTE ConsoleNum;
134 		BYTE NumPresent;
135 	};
136 	struct
137 	{
138 		DWORD	address;
139 		WORD	port;
140 		BYTE	player;
141 		BYTE	pad;
142 	} machines[MAXNETNODES];
143 };
144 
145 BYTE TransmitBuffer[TRANSMIT_SIZE];
146 
147 //
148 // UDPsocket
149 //
UDPsocket(void)150 SOCKET UDPsocket (void)
151 {
152 	SOCKET s;
153 
154 	// allocate a socket
155 	s = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP);
156 	if (s == INVALID_SOCKET)
157 		I_FatalError ("can't create socket: %s", neterror ());
158 
159 	return s;
160 }
161 
162 //
163 // BindToLocalPort
164 //
BindToLocalPort(SOCKET s,u_short port)165 void BindToLocalPort (SOCKET s, u_short port)
166 {
167 	int v;
168 	sockaddr_in address;
169 
170 	memset (&address, 0, sizeof(address));
171 	address.sin_family = AF_INET;
172 	address.sin_addr.s_addr = INADDR_ANY;
173 	address.sin_port = htons(port);
174 
175 	v = bind (s, (sockaddr *)&address, sizeof(address));
176 	if (v == SOCKET_ERROR)
177 		I_FatalError ("BindToPort: %s", neterror ());
178 }
179 
FindNode(const sockaddr_in * address)180 int FindNode (const sockaddr_in *address)
181 {
182 	int i;
183 
184 	// find remote node number
185 	for (i = 0; i<doomcom.numnodes; i++)
186 		if (address->sin_addr.s_addr == sendaddress[i].sin_addr.s_addr
187 			&& address->sin_port == sendaddress[i].sin_port)
188 			break;
189 
190 	if (i == doomcom.numnodes)
191 	{
192 		// packet is not from one of the players (new game broadcast?)
193 		i = -1;
194 	}
195 	return i;
196 }
197 
198 //
199 // PacketSend
200 //
PacketSend(void)201 void PacketSend (void)
202 {
203 	int c;
204 
205 	// FIXME: Catch this before we've overflown the buffer. With long chat
206 	// text and lots of backup tics, it could conceivably happen. (Though
207 	// apparently it hasn't yet, which is good.)
208 	if (doomcom.datalength > MAX_MSGLEN)
209 	{
210 		I_FatalError("Netbuffer overflow!");
211 	}
212 	assert(!(doomcom.data[0] & NCMD_COMPRESSED));
213 
214 	uLong size = TRANSMIT_SIZE - 1;
215 	if (doomcom.datalength >= 10)
216 	{
217 		TransmitBuffer[0] = doomcom.data[0] | NCMD_COMPRESSED;
218 		c = compress2(TransmitBuffer + 1, &size, doomcom.data + 1, doomcom.datalength - 1, 9);
219 		size += 1;
220 	}
221 	else
222 	{
223 		c = -1;	// Just some random error code to avoid sending the compressed buffer.
224 	}
225 	if (c == Z_OK && size < (uLong)doomcom.datalength)
226 	{
227 //		Printf("send %lu/%d\n", size, doomcom.datalength);
228 		c = sendto(mysocket, (char *)TransmitBuffer, size,
229 			0, (sockaddr *)&sendaddress[doomcom.remotenode],
230 			sizeof(sendaddress[doomcom.remotenode]));
231 	}
232 	else
233 	{
234 		if (doomcom.datalength > TRANSMIT_SIZE)
235 		{
236 			I_Error("Net compression failed (zlib error %d)", c);
237 		}
238 		else
239 		{
240 //			Printf("send %d\n", doomcom.datalength);
241 			c = sendto(mysocket, (char *)doomcom.data, doomcom.datalength,
242 				0, (sockaddr *)&sendaddress[doomcom.remotenode],
243 				sizeof(sendaddress[doomcom.remotenode]));
244 		}
245 	}
246 	//	if (c == -1)
247 	//			I_Error ("SendPacket error: %s",strerror(errno));
248 }
249 
250 
251 //
252 // PacketGet
253 //
PacketGet(void)254 void PacketGet (void)
255 {
256 	int c;
257 	socklen_t fromlen;
258 	sockaddr_in fromaddress;
259 	int node;
260 
261 	fromlen = sizeof(fromaddress);
262 	c = recvfrom (mysocket, (char*)TransmitBuffer, TRANSMIT_SIZE, 0,
263 				  (sockaddr *)&fromaddress, &fromlen);
264 	node = FindNode (&fromaddress);
265 
266 	if (node >= 0 && c == SOCKET_ERROR)
267 	{
268 		int err = WSAGetLastError();
269 
270 		if (err == WSAECONNRESET)
271 		{ // The remote node aborted unexpectedly, so pretend it sent an exit packet
272 
273 			if (StartScreen != NULL)
274 			{
275 				StartScreen->NetMessage ("The connection from %s was dropped.\n",
276 					players[sendplayer[node]].userinfo.GetName());
277 			}
278 			else
279 			{
280 				Printf("The connection from %s was dropped.\n",
281 					players[sendplayer[node]].userinfo.GetName());
282 			}
283 
284 			doomcom.data[0] = 0x80;	// NCMD_EXIT
285 			c = 1;
286 		}
287 		else if (err != WSAEWOULDBLOCK)
288 		{
289 			I_Error ("GetPacket: %s", neterror ());
290 		}
291 		else
292 		{
293 			doomcom.remotenode = -1;		// no packet
294 			return;
295 		}
296 	}
297 	else if (node >= 0 && c > 0)
298 	{
299 		doomcom.data[0] = TransmitBuffer[0] & ~NCMD_COMPRESSED;
300 		if (TransmitBuffer[0] & NCMD_COMPRESSED)
301 		{
302 			uLongf msgsize = MAX_MSGLEN - 1;
303 			int err = uncompress(doomcom.data + 1, &msgsize, TransmitBuffer + 1, c - 1);
304 //			Printf("recv %d/%lu\n", c, msgsize + 1);
305 			if (err != Z_OK)
306 			{
307 				Printf("Net decompression failed (zlib error %s)\n", M_ZLibError(err).GetChars());
308 				// Pretend no packet
309 				doomcom.remotenode = -1;
310 				return;
311 			}
312 			c = msgsize + 1;
313 		}
314 		else
315 		{
316 //			Printf("recv %d\n", c);
317 			memcpy(doomcom.data + 1, TransmitBuffer + 1, c - 1);
318 		}
319 	}
320 	else if (c > 0)
321 	{	//The packet is not from any in-game node, so we might as well discard it.
322 		// Don't show the message for disconnect notifications.
323 		if (c != 2 || TransmitBuffer[0] != PRE_FAKE || TransmitBuffer[1] != PRE_DISCONNECT)
324 		{
325 			DPrintf("Dropped packet: Unknown host (%s:%d)\n", inet_ntoa(fromaddress.sin_addr), fromaddress.sin_port);
326 		}
327 		doomcom.remotenode = -1;
328 		return;
329 	}
330 
331 	doomcom.remotenode = node;
332 	doomcom.datalength = (short)c;
333 }
334 
PreGet(void * buffer,int bufferlen,bool noabort)335 sockaddr_in *PreGet (void *buffer, int bufferlen, bool noabort)
336 {
337 	static sockaddr_in fromaddress;
338 	socklen_t fromlen;
339 	int c;
340 
341 	fromlen = sizeof(fromaddress);
342 	c = recvfrom (mysocket, (char *)buffer, bufferlen, 0,
343 		(sockaddr *)&fromaddress, &fromlen);
344 
345 	if (c == SOCKET_ERROR)
346 	{
347 		int err = WSAGetLastError();
348 		if (err == WSAEWOULDBLOCK || (noabort && err == WSAECONNRESET))
349 			return NULL;	// no packet
350 		I_Error ("PreGet: %s", neterror ());
351 	}
352 	return &fromaddress;
353 }
354 
PreSend(const void * buffer,int bufferlen,const sockaddr_in * to)355 void PreSend (const void *buffer, int bufferlen, const sockaddr_in *to)
356 {
357 	sendto (mysocket, (const char *)buffer, bufferlen, 0, (const sockaddr *)to, sizeof(*to));
358 }
359 
BuildAddress(sockaddr_in * address,const char * name)360 void BuildAddress (sockaddr_in *address, const char *name)
361 {
362 	hostent *hostentry;		// host information entry
363 	u_short port;
364 	const char *portpart;
365 	bool isnamed = false;
366 	int curchar;
367 	char c;
368 	FString target;
369 
370 	address->sin_family = AF_INET;
371 
372 	if ( (portpart = strchr (name, ':')) )
373 	{
374 		target = FString(name, portpart - name);
375 		port = atoi (portpart + 1);
376 		if (!port)
377 		{
378 			Printf ("Weird port: %s (using %d)\n", portpart + 1, DOOMPORT);
379 			port = DOOMPORT;
380 		}
381 	}
382 	else
383 	{
384 		target = name;
385 		port = DOOMPORT;
386 	}
387 	address->sin_port = htons(port);
388 
389 	for (curchar = 0; (c = target[curchar]) ; curchar++)
390 	{
391 		if ((c < '0' || c > '9') && c != '.')
392 		{
393 			isnamed = true;
394 			break;
395 		}
396 	}
397 
398 	if (!isnamed)
399 	{
400 		address->sin_addr.s_addr = inet_addr (target);
401 		Printf ("Node number %d, address %s\n", doomcom.numnodes, target.GetChars());
402 	}
403 	else
404 	{
405 		hostentry = gethostbyname (target);
406 		if (!hostentry)
407 			I_FatalError ("gethostbyname: couldn't find %s\n%s", target.GetChars(), neterror());
408 		address->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0];
409 		Printf ("Node number %d, hostname %s\n",
410 			doomcom.numnodes, hostentry->h_name);
411 	}
412 }
413 
CloseNetwork(void)414 void CloseNetwork (void)
415 {
416 	if (mysocket != INVALID_SOCKET)
417 	{
418 		closesocket (mysocket);
419 		mysocket = INVALID_SOCKET;
420 	}
421 #ifdef __WIN32__
422 	WSACleanup ();
423 #endif
424 }
425 
StartNetwork(bool autoPort)426 void StartNetwork (bool autoPort)
427 {
428 	u_long trueval = 1;
429 #ifdef __WIN32__
430 	WSADATA wsad;
431 
432 	if (WSAStartup (0x0101, &wsad))
433 	{
434 		I_FatalError ("Could not initialize Windows Sockets");
435 	}
436 #endif
437 
438 	atterm (CloseNetwork);
439 
440 	netgame = true;
441 	multiplayer = true;
442 
443 	// create communication socket
444 	mysocket = UDPsocket ();
445 	BindToLocalPort (mysocket, autoPort ? 0 : DOOMPORT);
446 #ifndef __sun
447 	ioctlsocket (mysocket, FIONBIO, &trueval);
448 #else
449 	fcntl(mysocket, F_SETFL, trueval | O_NONBLOCK);
450 #endif
451 }
452 
SendAbort(void)453 void SendAbort (void)
454 {
455 	BYTE dis[2] = { PRE_FAKE, PRE_DISCONNECT };
456 	int i, j;
457 
458 	if (doomcom.numnodes > 1)
459 	{
460 		if (consoleplayer == 0)
461 		{
462 			// The host needs to let everyone know
463 			for (i = 1; i < doomcom.numnodes; ++i)
464 			{
465 				for (j = 4; j > 0; --j)
466 				{
467 					PreSend (dis, 2, &sendaddress[i]);
468 				}
469 			}
470 		}
471 		else
472 		{
473 			// Guests only need to let the host know.
474 			for (i = 4; i > 0; --i)
475 			{
476 				PreSend (dis, 2, &sendaddress[1]);
477 			}
478 		}
479 	}
480 }
481 
SendConAck(int num_connected,int num_needed)482 static void SendConAck (int num_connected, int num_needed)
483 {
484 	PreGamePacket packet;
485 
486 	packet.Fake = PRE_FAKE;
487 	packet.Message = PRE_CONACK;
488 	packet.NumNodes = num_needed;
489 	packet.NumPresent = num_connected;
490 	for (int node = 1; node < doomcom.numnodes; ++node)
491 	{
492 		PreSend (&packet, 4, &sendaddress[node]);
493 	}
494 	StartScreen->NetProgress (doomcom.numnodes);
495 }
496 
Host_CheckForConnects(void * userdata)497 bool Host_CheckForConnects (void *userdata)
498 {
499 	PreGamePacket packet;
500 	int numplayers = (int)(intptr_t)userdata;
501 	sockaddr_in *from;
502 	int node;
503 
504 	while ( (from = PreGet (&packet, sizeof(packet), false)) )
505 	{
506 		if (packet.Fake != PRE_FAKE)
507 		{
508 			continue;
509 		}
510 		switch (packet.Message)
511 		{
512 		case PRE_CONNECT:
513 			node = FindNode (from);
514 			if (doomcom.numnodes == numplayers)
515 			{
516 				if (node == -1)
517 				{
518 					const BYTE *s_addr_bytes = (const BYTE *)&from->sin_addr;
519 					StartScreen->NetMessage ("Got extra connect from %d.%d.%d.%d:%d",
520 						s_addr_bytes[0], s_addr_bytes[1], s_addr_bytes[2], s_addr_bytes[3],
521 						from->sin_port);
522 					packet.Message = PRE_ALLFULL;
523 					PreSend (&packet, 2, from);
524 				}
525 			}
526 			else
527 			{
528 				if (node == -1)
529 				{
530 					node = doomcom.numnodes++;
531 					sendaddress[node] = *from;
532 					StartScreen->NetMessage ("Got connect from node %d.", node);
533 				}
534 
535 				// Let the new guest (and everyone else) know we got their message.
536 				SendConAck (doomcom.numnodes, numplayers);
537 			}
538 			break;
539 
540 		case PRE_DISCONNECT:
541 			node = FindNode (from);
542 			if (node >= 0)
543 			{
544 				StartScreen->NetMessage ("Got disconnect from node %d.", node);
545 				doomcom.numnodes--;
546 				while (node < doomcom.numnodes)
547 				{
548 					sendaddress[node] = sendaddress[node+1];
549 					node++;
550 				}
551 
552 				// Let remaining guests know that somebody left.
553 				SendConAck (doomcom.numnodes, numplayers);
554 			}
555 			break;
556 
557 		case PRE_KEEPALIVE:
558 			break;
559 		}
560 	}
561 	if (doomcom.numnodes < numplayers)
562 	{
563 		// Send message to everyone as a keepalive
564 		SendConAck(doomcom.numnodes, numplayers);
565 		return false;
566 	}
567 
568 	// It's possible somebody bailed out after all players were found.
569 	// Unfortunately, this isn't guaranteed to catch all of them.
570 	// Oh well. Better than nothing.
571 	while ( (from = PreGet (&packet, sizeof(packet), false)) )
572 	{
573 		if (packet.Fake == PRE_FAKE && packet.Message == PRE_DISCONNECT)
574 		{
575 			node = FindNode (from);
576 			if (node >= 0)
577 			{
578 				doomcom.numnodes--;
579 				while (node < doomcom.numnodes)
580 				{
581 					sendaddress[node] = sendaddress[node+1];
582 					node++;
583 				}
584 				// Let remaining guests know that somebody left.
585 				SendConAck (doomcom.numnodes, numplayers);
586 			}
587 			break;
588 		}
589 	}
590 	return doomcom.numnodes >= numplayers;
591 }
592 
Host_SendAllHere(void * userdata)593 bool Host_SendAllHere (void *userdata)
594 {
595 	int *gotack = (int *)userdata;	// ackcount is at gotack[MAXNETNODES]
596 	PreGamePacket packet;
597 	int node;
598 	sockaddr_in *from;
599 
600 	// Send out address information to all guests. Guests that have already
601 	// acknowledged receipt effectively get just a heartbeat packet.
602 	packet.Fake = PRE_FAKE;
603 	packet.Message = PRE_ALLHERE;
604 	for (node = 1; node < doomcom.numnodes; node++)
605 	{
606 		int machine, spot = 0;
607 
608 		packet.ConsoleNum = node;
609 		if (!gotack[node])
610 		{
611 			for (spot = 0, machine = 1; machine < doomcom.numnodes; machine++)
612 			{
613 				if (node != machine)
614 				{
615 					packet.machines[spot].address = sendaddress[machine].sin_addr.s_addr;
616 					packet.machines[spot].port = sendaddress[machine].sin_port;
617 					packet.machines[spot].player = node;
618 
619 					spot++;	// fixes problem of new address replacing existing address in
620 							// array; it's supposed to increment the index before getting
621 							// and storing in the packet the next address.
622 				}
623 			}
624 			packet.NumNodes = doomcom.numnodes - 2;
625 		}
626 		else
627 		{
628 			packet.NumNodes = 0;
629 		}
630 		PreSend (&packet, 4 + spot*8, &sendaddress[node]);
631 	}
632 
633 	// Check for replies.
634 	while ( (from = PreGet (&packet, sizeof(packet), false)) )
635 	{
636 		if (packet.Fake == PRE_FAKE && packet.Message == PRE_ALLHEREACK)
637 		{
638 			node = FindNode (from);
639 			if (node >= 0)
640 			{
641 				if (!gotack[node])
642 				{
643 					gotack[node] = true;
644 					gotack[MAXNETNODES]++;
645 				}
646 			}
647 			PreSend (&packet, 2, from);
648 		}
649 	}
650 
651 	// If everybody has replied, then this loop can end.
652 	return gotack[MAXNETNODES] == doomcom.numnodes - 1;
653 }
654 
HostGame(int i)655 void HostGame (int i)
656 {
657 	PreGamePacket packet;
658 	int numplayers;
659 	int node;
660 	int gotack[MAXNETNODES+1];
661 
662 	if ((i == Args->NumArgs() - 1) || !(numplayers = atoi (Args->GetArg(i+1))))
663 	{	// No player count specified, assume 2
664 		numplayers = 2;
665 	}
666 
667 	if (numplayers > MAXNETNODES)
668 	{
669 		I_FatalError("You cannot host a game with %d players. The limit is currently %d.", numplayers, MAXNETNODES);
670 		return;
671 	}
672 
673 	if (numplayers == 1)
674 	{ // Special case: Only 1 player, so don't bother starting the network
675 		netgame = false;
676 		multiplayer = true;
677 		doomcom.id = DOOMCOM_ID;
678 		doomcom.numplayers = doomcom.numnodes = 1;
679 		doomcom.consoleplayer = 0;
680 		return;
681 	}
682 
683 	StartNetwork (false);
684 
685 	// [JC] - this computer is starting the game, therefore it should
686 	// be the Net Arbitrator.
687 	doomcom.consoleplayer = 0;
688 	Printf ("Console player number: %d\n", doomcom.consoleplayer);
689 
690 	doomcom.numnodes = 1;
691 
692 	atterm (SendAbort);
693 
694 	StartScreen->NetInit ("Waiting for players", numplayers);
695 
696 	// Wait for numplayers-1 different connections
697 	if (!StartScreen->NetLoop (Host_CheckForConnects, (void *)(intptr_t)numplayers))
698 	{
699 		exit (0);
700 	}
701 
702 	// Now inform everyone of all machines involved in the game
703 	memset (gotack, 0, sizeof(gotack));
704 	StartScreen->NetMessage ("Sending all here.");
705 	StartScreen->NetInit ("Done waiting", 1);
706 
707 	if (!StartScreen->NetLoop (Host_SendAllHere, (void *)gotack))
708 	{
709 		exit (0);
710 	}
711 
712 	popterm ();
713 
714 	// Now go
715 	StartScreen->NetMessage ("Go");
716 	packet.Fake = PRE_FAKE;
717 	packet.Message = PRE_GO;
718 	for (node = 1; node < doomcom.numnodes; node++)
719 	{
720 		// If we send the packets eight times to each guest,
721 		// hopefully at least one of them will get through.
722 		for (int i = 8; i != 0; --i)
723 		{
724 			PreSend (&packet, 2, &sendaddress[node]);
725 		}
726 	}
727 
728 	StartScreen->NetMessage ("Total players: %d", doomcom.numnodes);
729 
730 	doomcom.id = DOOMCOM_ID;
731 	doomcom.numplayers = doomcom.numnodes;
732 
733 	// On the host, each player's number is the same as its node number
734 	for (i = 0; i < doomcom.numnodes; ++i)
735 	{
736 		sendplayer[i] = i;
737 	}
738 }
739 
740 // This routine is used by a guest to notify the host of its presence.
741 // Once that host acknowledges receipt of the notification, this routine
742 // is never called again.
743 
Guest_ContactHost(void * userdata)744 bool Guest_ContactHost (void *userdata)
745 {
746 	sockaddr_in *from;
747 	PreGamePacket packet;
748 
749 	// Let the host know we are here.
750 	packet.Fake = PRE_FAKE;
751 	packet.Message = PRE_CONNECT;
752 	PreSend (&packet, 2, &sendaddress[1]);
753 
754 	// Listen for a reply.
755 	while ( (from = PreGet (&packet, sizeof(packet), true)) )
756 	{
757 		if (packet.Fake == PRE_FAKE && FindNode(from) == 1)
758 		{
759 			if (packet.Message == PRE_CONACK)
760 			{
761 				StartScreen->NetMessage ("Total players: %d", packet.NumNodes);
762 				StartScreen->NetInit ("Waiting for other players", packet.NumNodes);
763 				StartScreen->NetProgress (packet.NumPresent);
764 				return true;
765 			}
766 			else if (packet.Message == PRE_DISCONNECT)
767 			{
768 				doomcom.numnodes = 0;
769 				I_FatalError ("The host cancelled the game.");
770 			}
771 			else if (packet.Message == PRE_ALLFULL)
772 			{
773 				doomcom.numnodes = 0;
774 				I_FatalError ("The game is full.");
775 			}
776 		}
777 	}
778 
779 	// In case the progress bar could not be marqueed, bump it.
780 	StartScreen->NetProgress (0);
781 
782 	return false;
783 }
784 
Guest_WaitForOthers(void * userdata)785 bool Guest_WaitForOthers (void *userdata)
786 {
787 	sockaddr_in *from;
788 	PreGamePacket packet;
789 
790 	while ( (from = PreGet (&packet, sizeof(packet), false)) )
791 	{
792 		if (packet.Fake != PRE_FAKE || FindNode(from) != 1)
793 		{
794 			continue;
795 		}
796 		switch (packet.Message)
797 		{
798 		case PRE_CONACK:
799 			StartScreen->NetProgress (packet.NumPresent);
800 			break;
801 
802 		case PRE_ALLHERE:
803 			if (doomcom.numnodes == 2)
804 			{
805 				int node;
806 
807 				doomcom.numnodes = packet.NumNodes + 2;
808 				sendplayer[0] = packet.ConsoleNum;	// My player number
809 				doomcom.consoleplayer = packet.ConsoleNum;
810 				StartScreen->NetMessage ("Console player number: %d", doomcom.consoleplayer);
811 				for (node = 0; node < packet.NumNodes; node++)
812 				{
813 					sendaddress[node+2].sin_addr.s_addr = packet.machines[node].address;
814 					sendaddress[node+2].sin_port = packet.machines[node].port;
815 					sendplayer[node+2] = packet.machines[node].player;
816 
817 					// [JC] - fixes problem of games not starting due to
818 					// no address family being assigned to nodes stored in
819 					// sendaddress[] from the All Here packet.
820 					sendaddress[node+2].sin_family = AF_INET;
821 				}
822 			}
823 
824 			StartScreen->NetMessage ("Received All Here, sending ACK.");
825 			packet.Fake = PRE_FAKE;
826 			packet.Message = PRE_ALLHEREACK;
827 			PreSend (&packet, 2, &sendaddress[1]);
828 			break;
829 
830 		case PRE_GO:
831 			StartScreen->NetMessage ("Received \"Go.\"");
832 			return true;
833 
834 		case PRE_DISCONNECT:
835 			I_FatalError ("The host cancelled the game.");
836 			break;
837 		}
838 	}
839 
840 	packet.Fake = PRE_FAKE;
841 	packet.Message = PRE_KEEPALIVE;
842 	PreSend(&packet, 2, &sendaddress[1]);
843 
844 	return false;
845 }
846 
JoinGame(int i)847 void JoinGame (int i)
848 {
849 	if ((i == Args->NumArgs() - 1) ||
850 		(Args->GetArg(i+1)[0] == '-') ||
851 		(Args->GetArg(i+1)[0] == '+'))
852 		I_FatalError ("You need to specify the host machine's address");
853 
854 	StartNetwork (true);
855 
856 	// Host is always node 1
857 	BuildAddress (&sendaddress[1], Args->GetArg(i+1));
858 	sendplayer[1] = 0;
859 	doomcom.numnodes = 2;
860 
861 	atterm (SendAbort);
862 
863 	// Let host know we are here
864 	StartScreen->NetInit ("Contacting host", 0);
865 
866 	if (!StartScreen->NetLoop (Guest_ContactHost, NULL))
867 	{
868 		exit (0);
869 	}
870 
871 	// Wait for everyone else to connect
872 	if (!StartScreen->NetLoop (Guest_WaitForOthers, 0))
873 	{
874 		exit (0);
875 	}
876 
877 	popterm ();
878 
879 	StartScreen->NetMessage ("Total players: %d", doomcom.numnodes);
880 
881 	doomcom.id = DOOMCOM_ID;
882 	doomcom.numplayers = doomcom.numnodes;
883 }
884 
PrivateNetOf(in_addr in)885 static int PrivateNetOf(in_addr in)
886 {
887 	int addr = ntohl(in.s_addr);
888 		 if ((addr & 0xFFFF0000) == 0xC0A80000)		// 192.168.0.0
889 	{
890 		return 0xC0A80000;
891 	}
892 	else if ((addr & 0xFFF00000) == 0xAC100000)		// 172.16.0.0
893 	{
894 		return 0xAC100000;
895 	}
896 	else if ((addr & 0xFF000000) == 0x0A000000)		// 10.0.0.0
897 	{
898 		return 0x0A000000;
899 	}
900 	else if ((addr & 0xFF000000) == 0x7F000000)		// 127.0.0.0 (localhost)
901 	{
902 		return 0x7F000000;
903 	}
904 	// Not a private IP
905 	return 0;
906 }
907 
908 //
909 // NodesOnSameNetwork
910 //
911 // The best I can really do here is check if the others are on the same
912 // private network, since that means we (probably) are too.
913 //
914 
NodesOnSameNetwork()915 static bool NodesOnSameNetwork()
916 {
917 	int net1;
918 
919 	net1 = PrivateNetOf(sendaddress[1].sin_addr);
920 //	Printf("net1 = %08x\n", net1);
921 	if (net1 == 0)
922 	{
923 		return false;
924 	}
925 	for (int i = 2; i < doomcom.numnodes; ++i)
926 	{
927 		int net = PrivateNetOf(sendaddress[i].sin_addr);
928 //		Printf("Net[%d] = %08x\n", i, net);
929 		if (net != net1)
930 		{
931 			return false;
932 		}
933 	}
934 	return true;
935 }
936 
937 //
938 // I_InitNetwork
939 //
940 // Returns true if packet server mode might be a good idea.
941 //
I_InitNetwork(void)942 bool I_InitNetwork (void)
943 {
944 	int i;
945 	const char *v;
946 
947 	memset (&doomcom, 0, sizeof(doomcom));
948 
949 	// set up for network
950 	v = Args->CheckValue ("-dup");
951 	if (v)
952 	{
953 		doomcom.ticdup = clamp (atoi (v), 1, MAXTICDUP);
954 	}
955 	else
956 	{
957 		doomcom.ticdup = 1;
958 	}
959 
960 	v = Args->CheckValue ("-port");
961 	if (v)
962 	{
963 		DOOMPORT = atoi (v);
964 		Printf ("using alternate port %i\n", DOOMPORT);
965 	}
966 
967 	// parse network game options,
968 	//		player 1: -host <numplayers>
969 	//		player x: -join <player 1's address>
970 	if ( (i = Args->CheckParm ("-host")) )
971 	{
972 		HostGame (i);
973 	}
974 	else if ( (i = Args->CheckParm ("-join")) )
975 	{
976 		JoinGame (i);
977 	}
978 	else
979 	{
980 		// single player game
981 		netgame = false;
982 		multiplayer = false;
983 		doomcom.id = DOOMCOM_ID;
984 		doomcom.numplayers = doomcom.numnodes = 1;
985 		doomcom.consoleplayer = 0;
986 		return false;
987 	}
988 	if (doomcom.numnodes < 3)
989 	{ // Packet server mode with only two players is effectively the same as
990 	  // peer-to-peer but with some slightly larger packets.
991 		return false;
992 	}
993 	return doomcom.numnodes > 3 || !NodesOnSameNetwork();
994 }
995 
996 
I_NetCmd(void)997 void I_NetCmd (void)
998 {
999 	if (doomcom.command == CMD_SEND)
1000 	{
1001 		PacketSend ();
1002 	}
1003 	else if (doomcom.command == CMD_GET)
1004 	{
1005 		PacketGet ();
1006 	}
1007 	else
1008 		I_Error ("Bad net cmd: %i\n",doomcom.command);
1009 }
1010 
1011 #ifdef __WIN32__
neterror(void)1012 const char *neterror (void)
1013 {
1014 	static char neterr[16];
1015 	int			code;
1016 
1017 	switch (code = WSAGetLastError ()) {
1018 		case WSAEACCES:				return "EACCES";
1019 		case WSAEADDRINUSE:			return "EADDRINUSE";
1020 		case WSAEADDRNOTAVAIL:		return "EADDRNOTAVAIL";
1021 		case WSAEAFNOSUPPORT:		return "EAFNOSUPPORT";
1022 		case WSAEALREADY:			return "EALREADY";
1023 		case WSAECONNABORTED:		return "ECONNABORTED";
1024 		case WSAECONNREFUSED:		return "ECONNREFUSED";
1025 		case WSAECONNRESET:			return "ECONNRESET";
1026 		case WSAEDESTADDRREQ:		return "EDESTADDRREQ";
1027 		case WSAEFAULT:				return "EFAULT";
1028 		case WSAEHOSTDOWN:			return "EHOSTDOWN";
1029 		case WSAEHOSTUNREACH:		return "EHOSTUNREACH";
1030 		case WSAEINPROGRESS:		return "EINPROGRESS";
1031 		case WSAEINTR:				return "EINTR";
1032 		case WSAEINVAL:				return "EINVAL";
1033 		case WSAEISCONN:			return "EISCONN";
1034 		case WSAEMFILE:				return "EMFILE";
1035 		case WSAEMSGSIZE:			return "EMSGSIZE";
1036 		case WSAENETDOWN:			return "ENETDOWN";
1037 		case WSAENETRESET:			return "ENETRESET";
1038 		case WSAENETUNREACH:		return "ENETUNREACH";
1039 		case WSAENOBUFS:			return "ENOBUFS";
1040 		case WSAENOPROTOOPT:		return "ENOPROTOOPT";
1041 		case WSAENOTCONN:			return "ENOTCONN";
1042 		case WSAENOTSOCK:			return "ENOTSOCK";
1043 		case WSAEOPNOTSUPP:			return "EOPNOTSUPP";
1044 		case WSAEPFNOSUPPORT:		return "EPFNOSUPPORT";
1045 		case WSAEPROCLIM:			return "EPROCLIM";
1046 		case WSAEPROTONOSUPPORT:	return "EPROTONOSUPPORT";
1047 		case WSAEPROTOTYPE:			return "EPROTOTYPE";
1048 		case WSAESHUTDOWN:			return "ESHUTDOWN";
1049 		case WSAESOCKTNOSUPPORT:	return "ESOCKTNOSUPPORT";
1050 		case WSAETIMEDOUT:			return "ETIMEDOUT";
1051 		case WSAEWOULDBLOCK:		return "EWOULDBLOCK";
1052 		case WSAHOST_NOT_FOUND:		return "HOST_NOT_FOUND";
1053 		case WSANOTINITIALISED:		return "NOTINITIALISED";
1054 		case WSANO_DATA:			return "NO_DATA";
1055 		case WSANO_RECOVERY:		return "NO_RECOVERY";
1056 		case WSASYSNOTREADY:		return "SYSNOTREADY";
1057 		case WSATRY_AGAIN:			return "TRY_AGAIN";
1058 		case WSAVERNOTSUPPORTED:	return "VERNOTSUPPORTED";
1059 		case WSAEDISCON:			return "EDISCON";
1060 
1061 		default:
1062 			mysnprintf (neterr, countof(neterr), "%d", code);
1063 			return neterr;
1064 	}
1065 }
1066 #endif
1067