1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2000-2006 Tim Angus
5 
6 This file is part of Tremulous.
7 
8 Tremulous is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12 
13 Tremulous is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Tremulous; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21 ===========================================================================
22 */
23 
24 #include "q_shared.h"
25 #include "qcommon.h"
26 
27 /*
28 
29 packet header
30 -------------
31 4	outgoing sequence.  high bit will be set if this is a fragmented message
32 [2	qport (only for client to server)]
33 [2	fragment start byte]
34 [2	fragment length. if < FRAGMENT_SIZE, this is the last fragment]
35 
36 if the sequence number is -1, the packet should be handled as an out-of-band
37 message instead of as part of a netcon.
38 
39 All fragments will have the same sequence numbers.
40 
41 The qport field is a workaround for bad address translating routers that
42 sometimes remap the client's source port on a packet during gameplay.
43 
44 If the base part of the net address matches and the qport matches, then the
45 channel matches even if the IP port differs.  The IP port should be updated
46 to the new value before sending out any replies.
47 
48 */
49 
50 
51 #define	MAX_PACKETLEN			1400		// max size of a network packet
52 
53 #define	FRAGMENT_SIZE			(MAX_PACKETLEN - 100)
54 #define	PACKET_HEADER			10			// two ints and a short
55 
56 #define	FRAGMENT_BIT	(1<<31)
57 
58 cvar_t		*showpackets;
59 cvar_t		*showdrop;
60 cvar_t		*qport;
61 
62 static char *netsrcString[2] = {
63 	"client",
64 	"server"
65 };
66 
67 /*
68 ===============
69 Netchan_Init
70 
71 ===============
72 */
Netchan_Init(int port)73 void Netchan_Init( int port ) {
74 	port &= 0xffff;
75 	showpackets = Cvar_Get ("showpackets", "0", CVAR_TEMP );
76 	showdrop = Cvar_Get ("showdrop", "0", CVAR_TEMP );
77 	qport = Cvar_Get ("net_qport", va("%i", port), CVAR_INIT );
78 }
79 
80 /*
81 ==============
82 Netchan_Setup
83 
84 called to open a channel to a remote system
85 ==============
86 */
Netchan_Setup(netsrc_t sock,netchan_t * chan,netadr_t adr,int qport)87 void Netchan_Setup( netsrc_t sock, netchan_t *chan, netadr_t adr, int qport ) {
88 	Com_Memset (chan, 0, sizeof(*chan));
89 
90 	chan->sock = sock;
91 	chan->remoteAddress = adr;
92 	chan->qport = qport;
93 	chan->incomingSequence = 0;
94 	chan->outgoingSequence = 1;
95 }
96 
97 // TTimo: unused, commenting out to make gcc happy
98 #if 0
99 /*
100 ==============
101 Netchan_ScramblePacket
102 
103 A probably futile attempt to make proxy hacking somewhat
104 more difficult.
105 ==============
106 */
107 #define	SCRAMBLE_START	6
108 static void Netchan_ScramblePacket( msg_t *buf ) {
109 	unsigned	seed;
110 	int			i, j, c, mask, temp;
111 	int			seq[MAX_PACKETLEN];
112 
113 	seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 );
114 	c = buf->cursize;
115 	if ( c <= SCRAMBLE_START ) {
116 		return;
117 	}
118 	if ( c > MAX_PACKETLEN ) {
119 		Com_Error( ERR_DROP, "MAX_PACKETLEN" );
120 	}
121 
122 	// generate a sequence of "random" numbers
123 	for (i = 0 ; i < c ; i++) {
124 		seed = (119 * seed + 1);
125 		seq[i] = seed;
126 	}
127 
128 	// transpose each character
129 	for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) {
130 	}
131 	mask >>= 1;
132 	for (i = SCRAMBLE_START ; i < c ; i++) {
133 		j = SCRAMBLE_START + ( seq[i] & mask );
134 		temp = buf->data[j];
135 		buf->data[j] = buf->data[i];
136 		buf->data[i] = temp;
137 	}
138 
139 	// byte xor the data after the header
140 	for (i = SCRAMBLE_START ; i < c ; i++) {
141 		buf->data[i] ^= seq[i];
142 	}
143 }
144 
145 static void Netchan_UnScramblePacket( msg_t *buf ) {
146 	unsigned	seed;
147 	int			i, j, c, mask, temp;
148 	int			seq[MAX_PACKETLEN];
149 
150 	seed = ( LittleLong( *(unsigned *)buf->data ) * 3 ) ^ ( buf->cursize * 123 );
151 	c = buf->cursize;
152 	if ( c <= SCRAMBLE_START ) {
153 		return;
154 	}
155 	if ( c > MAX_PACKETLEN ) {
156 		Com_Error( ERR_DROP, "MAX_PACKETLEN" );
157 	}
158 
159 	// generate a sequence of "random" numbers
160 	for (i = 0 ; i < c ; i++) {
161 		seed = (119 * seed + 1);
162 		seq[i] = seed;
163 	}
164 
165 	// byte xor the data after the header
166 	for (i = SCRAMBLE_START ; i < c ; i++) {
167 		buf->data[i] ^= seq[i];
168 	}
169 
170 	// transpose each character in reverse order
171 	for ( mask = 1 ; mask < c-SCRAMBLE_START ; mask = ( mask << 1 ) + 1 ) {
172 	}
173 	mask >>= 1;
174 	for (i = c-1 ; i >= SCRAMBLE_START ; i--) {
175 		j = SCRAMBLE_START + ( seq[i] & mask );
176 		temp = buf->data[j];
177 		buf->data[j] = buf->data[i];
178 		buf->data[i] = temp;
179 	}
180 }
181 #endif
182 
183 /*
184 =================
185 Netchan_TransmitNextFragment
186 
187 Send one fragment of the current message
188 =================
189 */
Netchan_TransmitNextFragment(netchan_t * chan)190 void Netchan_TransmitNextFragment( netchan_t *chan ) {
191 	msg_t		send;
192 	byte		send_buf[MAX_PACKETLEN];
193 	int			fragmentLength;
194 
195 	// write the packet header
196 	MSG_InitOOB (&send, send_buf, sizeof(send_buf));				// <-- only do the oob here
197 
198 	MSG_WriteLong( &send, chan->outgoingSequence | FRAGMENT_BIT );
199 
200 	// send the qport if we are a client
201 	if ( chan->sock == NS_CLIENT ) {
202 		MSG_WriteShort( &send, qport->integer );
203 	}
204 
205 	// copy the reliable message to the packet first
206 	fragmentLength = FRAGMENT_SIZE;
207 	if ( chan->unsentFragmentStart  + fragmentLength > chan->unsentLength ) {
208 		fragmentLength = chan->unsentLength - chan->unsentFragmentStart;
209 	}
210 
211 	MSG_WriteShort( &send, chan->unsentFragmentStart );
212 	MSG_WriteShort( &send, fragmentLength );
213 	MSG_WriteData( &send, chan->unsentBuffer + chan->unsentFragmentStart, fragmentLength );
214 
215 	// send the datagram
216 	NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress );
217 
218 	if ( showpackets->integer ) {
219 		Com_Printf ("%s send %4i : s=%i fragment=%i,%i\n"
220 			, netsrcString[ chan->sock ]
221 			, send.cursize
222 			, chan->outgoingSequence
223 			, chan->unsentFragmentStart, fragmentLength);
224 	}
225 
226 	chan->unsentFragmentStart += fragmentLength;
227 
228 	// this exit condition is a little tricky, because a packet
229 	// that is exactly the fragment length still needs to send
230 	// a second packet of zero length so that the other side
231 	// can tell there aren't more to follow
232 	if ( chan->unsentFragmentStart == chan->unsentLength && fragmentLength != FRAGMENT_SIZE ) {
233 		chan->outgoingSequence++;
234 		chan->unsentFragments = qfalse;
235 	}
236 }
237 
238 
239 /*
240 ===============
241 Netchan_Transmit
242 
243 Sends a message to a connection, fragmenting if necessary
244 A 0 length will still generate a packet.
245 ================
246 */
Netchan_Transmit(netchan_t * chan,int length,const byte * data)247 void Netchan_Transmit( netchan_t *chan, int length, const byte *data ) {
248 	msg_t		send;
249 	byte		send_buf[MAX_PACKETLEN];
250 
251 	if ( length > MAX_MSGLEN ) {
252 		Com_Error( ERR_DROP, "Netchan_Transmit: length = %i", length );
253 	}
254 	chan->unsentFragmentStart = 0;
255 
256 	// fragment large reliable messages
257 	if ( length >= FRAGMENT_SIZE ) {
258 		chan->unsentFragments = qtrue;
259 		chan->unsentLength = length;
260 		Com_Memcpy( chan->unsentBuffer, data, length );
261 
262 		// only send the first fragment now
263 		Netchan_TransmitNextFragment( chan );
264 
265 		return;
266 	}
267 
268 	// write the packet header
269 	MSG_InitOOB (&send, send_buf, sizeof(send_buf));
270 
271 	MSG_WriteLong( &send, chan->outgoingSequence );
272 	chan->outgoingSequence++;
273 
274 	// send the qport if we are a client
275 	if ( chan->sock == NS_CLIENT ) {
276 		MSG_WriteShort( &send, qport->integer );
277 	}
278 
279 	MSG_WriteData( &send, data, length );
280 
281 	// send the datagram
282 	NET_SendPacket( chan->sock, send.cursize, send.data, chan->remoteAddress );
283 
284 	if ( showpackets->integer ) {
285 		Com_Printf( "%s send %4i : s=%i ack=%i\n"
286 			, netsrcString[ chan->sock ]
287 			, send.cursize
288 			, chan->outgoingSequence - 1
289 			, chan->incomingSequence );
290 	}
291 }
292 
293 /*
294 =================
295 Netchan_Process
296 
297 Returns qfalse if the message should not be processed due to being
298 out of order or a fragment.
299 
300 Msg must be large enough to hold MAX_MSGLEN, because if this is the
301 final fragment of a multi-part message, the entire thing will be
302 copied out.
303 =================
304 */
Netchan_Process(netchan_t * chan,msg_t * msg)305 qboolean Netchan_Process( netchan_t *chan, msg_t *msg ) {
306 	int			sequence;
307 	int			qport;
308 	int			fragmentStart, fragmentLength;
309 	qboolean	fragmented;
310 
311 	// XOR unscramble all data in the packet after the header
312 //	Netchan_UnScramblePacket( msg );
313 
314 	// get sequence numbers
315 	MSG_BeginReadingOOB( msg );
316 	sequence = MSG_ReadLong( msg );
317 
318 	// check for fragment information
319 	if ( sequence & FRAGMENT_BIT ) {
320 		sequence &= ~FRAGMENT_BIT;
321 		fragmented = qtrue;
322 	} else {
323 		fragmented = qfalse;
324 	}
325 
326 	// read the qport if we are a server
327 	if ( chan->sock == NS_SERVER ) {
328 		qport = MSG_ReadShort( msg );
329 	}
330 
331 	// read the fragment information
332 	if ( fragmented ) {
333 		fragmentStart = MSG_ReadShort( msg );
334 		fragmentLength = MSG_ReadShort( msg );
335 	} else {
336 		fragmentStart = 0;		// stop warning message
337 		fragmentLength = 0;
338 	}
339 
340 	if ( showpackets->integer ) {
341 		if ( fragmented ) {
342 			Com_Printf( "%s recv %4i : s=%i fragment=%i,%i\n"
343 				, netsrcString[ chan->sock ]
344 				, msg->cursize
345 				, sequence
346 				, fragmentStart, fragmentLength );
347 		} else {
348 			Com_Printf( "%s recv %4i : s=%i\n"
349 				, netsrcString[ chan->sock ]
350 				, msg->cursize
351 				, sequence );
352 		}
353 	}
354 
355 	//
356 	// discard out of order or duplicated packets
357 	//
358 	if ( sequence <= chan->incomingSequence ) {
359 		if ( showdrop->integer || showpackets->integer ) {
360 			Com_Printf( "%s:Out of order packet %i at %i\n"
361 				, NET_AdrToString( chan->remoteAddress )
362 				,  sequence
363 				, chan->incomingSequence );
364 		}
365 		return qfalse;
366 	}
367 
368 	//
369 	// dropped packets don't keep the message from being used
370 	//
371 	chan->dropped = sequence - (chan->incomingSequence+1);
372 	if ( chan->dropped > 0 ) {
373 		if ( showdrop->integer || showpackets->integer ) {
374 			Com_Printf( "%s:Dropped %i packets at %i\n"
375 			, NET_AdrToString( chan->remoteAddress )
376 			, chan->dropped
377 			, sequence );
378 		}
379 	}
380 
381 
382 	//
383 	// if this is the final framgent of a reliable message,
384 	// bump incoming_reliable_sequence
385 	//
386 	if ( fragmented ) {
387 		// TTimo
388 		// make sure we add the fragments in correct order
389 		// either a packet was dropped, or we received this one too soon
390 		// we don't reconstruct the fragments. we will wait till this fragment gets to us again
391 		// (NOTE: we could probably try to rebuild by out of order chunks if needed)
392 		if ( sequence != chan->fragmentSequence ) {
393 			chan->fragmentSequence = sequence;
394 			chan->fragmentLength = 0;
395 		}
396 
397 		// if we missed a fragment, dump the message
398 		if ( fragmentStart != chan->fragmentLength ) {
399 			if ( showdrop->integer || showpackets->integer ) {
400 				Com_Printf( "%s:Dropped a message fragment\n"
401 				, NET_AdrToString( chan->remoteAddress )
402 				, sequence);
403 			}
404 			// we can still keep the part that we have so far,
405 			// so we don't need to clear chan->fragmentLength
406 			return qfalse;
407 		}
408 
409 		// copy the fragment to the fragment buffer
410 		if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize ||
411 			chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) {
412 			if ( showdrop->integer || showpackets->integer ) {
413 				Com_Printf ("%s:illegal fragment length\n"
414 				, NET_AdrToString (chan->remoteAddress ) );
415 			}
416 			return qfalse;
417 		}
418 
419 		Com_Memcpy( chan->fragmentBuffer + chan->fragmentLength,
420 			msg->data + msg->readcount, fragmentLength );
421 
422 		chan->fragmentLength += fragmentLength;
423 
424 		// if this wasn't the last fragment, don't process anything
425 		if ( fragmentLength == FRAGMENT_SIZE ) {
426 			return qfalse;
427 		}
428 
429 		if ( chan->fragmentLength > msg->maxsize ) {
430 			Com_Printf( "%s:fragmentLength %i > msg->maxsize\n"
431 				, NET_AdrToString (chan->remoteAddress ),
432 				chan->fragmentLength );
433 			return qfalse;
434 		}
435 
436 		// copy the full message over the partial fragment
437 
438 		// make sure the sequence number is still there
439 		*(int *)msg->data = LittleLong( sequence );
440 
441 		Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength );
442 		msg->cursize = chan->fragmentLength + 4;
443 		chan->fragmentLength = 0;
444 		msg->readcount = 4;	// past the sequence number
445 		msg->bit = 32;	// past the sequence number
446 
447 		// TTimo
448 		// clients were not acking fragmented messages
449 		chan->incomingSequence = sequence;
450 
451 		return qtrue;
452 	}
453 
454 	//
455 	// the message can now be read from the current message pointer
456 	//
457 	chan->incomingSequence = sequence;
458 
459 	return qtrue;
460 }
461 
462 
463 //==============================================================================
464 
465 /*
466 ===================
467 NET_CompareBaseAdr
468 
469 Compares without the port
470 ===================
471 */
NET_CompareBaseAdr(netadr_t a,netadr_t b)472 qboolean	NET_CompareBaseAdr (netadr_t a, netadr_t b)
473 {
474 	if (a.type != b.type)
475 		return qfalse;
476 
477 	if (a.type == NA_LOOPBACK)
478 		return qtrue;
479 
480 	if (a.type == NA_IP)
481 	{
482 		if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3])
483 			return qtrue;
484 		return qfalse;
485 	}
486 
487 	if (a.type == NA_IPX)
488 	{
489 		if ((memcmp(a.ipx, b.ipx, 10) == 0))
490 			return qtrue;
491 		return qfalse;
492 	}
493 
494 
495 	Com_Printf ("NET_CompareBaseAdr: bad address type\n");
496 	return qfalse;
497 }
498 
NET_AdrToString(netadr_t a)499 const char	*NET_AdrToString (netadr_t a)
500 {
501 	static	char	s[64];
502 
503 	if (a.type == NA_LOOPBACK) {
504 		Com_sprintf (s, sizeof(s), "loopback");
505 	} else if (a.type == NA_BOT) {
506 		Com_sprintf (s, sizeof(s), "bot");
507 	} else if (a.type == NA_IP) {
508 		Com_sprintf (s, sizeof(s), "%i.%i.%i.%i:%hu",
509 			a.ip[0], a.ip[1], a.ip[2], a.ip[3], BigShort(a.port));
510 	} else {
511 		Com_sprintf (s, sizeof(s), "%02x%02x%02x%02x.%02x%02x%02x%02x%02x%02x:%hu",
512 		a.ipx[0], a.ipx[1], a.ipx[2], a.ipx[3], a.ipx[4], a.ipx[5], a.ipx[6], a.ipx[7], a.ipx[8], a.ipx[9],
513 		BigShort(a.port));
514 	}
515 
516 	return s;
517 }
518 
519 
NET_CompareAdr(netadr_t a,netadr_t b)520 qboolean	NET_CompareAdr (netadr_t a, netadr_t b)
521 {
522 	if (a.type != b.type)
523 		return qfalse;
524 
525 	if (a.type == NA_LOOPBACK)
526 		return qtrue;
527 
528 	if (a.type == NA_IP)
529 	{
530 		if (a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] && a.ip[2] == b.ip[2] && a.ip[3] == b.ip[3] && a.port == b.port)
531 			return qtrue;
532 		return qfalse;
533 	}
534 
535 	if (a.type == NA_IPX)
536 	{
537 		if ((memcmp(a.ipx, b.ipx, 10) == 0) && a.port == b.port)
538 			return qtrue;
539 		return qfalse;
540 	}
541 
542 	Com_Printf ("NET_CompareAdr: bad address type\n");
543 	return qfalse;
544 }
545 
546 
NET_IsLocalAddress(netadr_t adr)547 qboolean	NET_IsLocalAddress( netadr_t adr ) {
548 	return adr.type == NA_LOOPBACK;
549 }
550 
551 
552 
553 /*
554 =============================================================================
555 
556 LOOPBACK BUFFERS FOR LOCAL PLAYER
557 
558 =============================================================================
559 */
560 
561 // there needs to be enough loopback messages to hold a complete
562 // gamestate of maximum size
563 #define	MAX_LOOPBACK	16
564 
565 typedef struct {
566 	byte	data[MAX_PACKETLEN];
567 	int		datalen;
568 } loopmsg_t;
569 
570 typedef struct {
571 	loopmsg_t	msgs[MAX_LOOPBACK];
572 	int			get, send;
573 } loopback_t;
574 
575 loopback_t	loopbacks[2];
576 
577 
NET_GetLoopPacket(netsrc_t sock,netadr_t * net_from,msg_t * net_message)578 qboolean	NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message)
579 {
580 	int		i;
581 	loopback_t	*loop;
582 
583 	loop = &loopbacks[sock];
584 
585 	if (loop->send - loop->get > MAX_LOOPBACK)
586 		loop->get = loop->send - MAX_LOOPBACK;
587 
588 	if (loop->get >= loop->send)
589 		return qfalse;
590 
591 	i = loop->get & (MAX_LOOPBACK-1);
592 	loop->get++;
593 
594 	Com_Memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen);
595 	net_message->cursize = loop->msgs[i].datalen;
596 	Com_Memset (net_from, 0, sizeof(*net_from));
597 	net_from->type = NA_LOOPBACK;
598 	return qtrue;
599 
600 }
601 
602 
NET_SendLoopPacket(netsrc_t sock,int length,const void * data,netadr_t to)603 void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to)
604 {
605 	int		i;
606 	loopback_t	*loop;
607 
608 	loop = &loopbacks[sock^1];
609 
610 	i = loop->send & (MAX_LOOPBACK-1);
611 	loop->send++;
612 
613 	Com_Memcpy (loop->msgs[i].data, data, length);
614 	loop->msgs[i].datalen = length;
615 }
616 
617 //=============================================================================
618 
619 
NET_SendPacket(netsrc_t sock,int length,const void * data,netadr_t to)620 void NET_SendPacket( netsrc_t sock, int length, const void *data, netadr_t to ) {
621 
622 	// sequenced packets are shown in netchan, so just show oob
623 	if ( showpackets->integer && *(int *)data == -1 )	{
624 		Com_Printf ("send packet %4i\n", length);
625 	}
626 
627 	if ( to.type == NA_LOOPBACK ) {
628 		NET_SendLoopPacket (sock, length, data, to);
629 		return;
630 	}
631 	if ( to.type == NA_BOT ) {
632 		return;
633 	}
634 	if ( to.type == NA_BAD ) {
635 		return;
636 	}
637 
638 	Sys_SendPacket( length, data, to );
639 }
640 
641 /*
642 ===============
643 NET_OutOfBandPrint
644 
645 Sends a text message in an out-of-band datagram
646 ================
647 */
NET_OutOfBandPrint(netsrc_t sock,netadr_t adr,const char * format,...)648 void QDECL NET_OutOfBandPrint( netsrc_t sock, netadr_t adr, const char *format, ... ) {
649 	va_list		argptr;
650 	char		string[MAX_MSGLEN];
651 
652 
653 	// set the header
654 	string[0] = -1;
655 	string[1] = -1;
656 	string[2] = -1;
657 	string[3] = -1;
658 
659 	va_start( argptr, format );
660 	vsprintf( string+4, format, argptr );
661 	va_end( argptr );
662 
663 	// send the datagram
664 	NET_SendPacket( sock, strlen( string ), string, adr );
665 }
666 
667 /*
668 ===============
669 NET_OutOfBandPrint
670 
671 Sends a data message in an out-of-band datagram (only used for "connect")
672 ================
673 */
NET_OutOfBandData(netsrc_t sock,netadr_t adr,byte * format,int len)674 void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ) {
675 	byte		string[MAX_MSGLEN*2];
676 	int			i;
677 	msg_t		mbuf;
678 
679 	// set the header
680 	string[0] = 0xff;
681 	string[1] = 0xff;
682 	string[2] = 0xff;
683 	string[3] = 0xff;
684 
685 	for(i=0;i<len;i++) {
686 		string[i+4] = format[i];
687 	}
688 
689 	mbuf.data = string;
690 	mbuf.cursize = len+4;
691 	Huff_Compress( &mbuf, 12);
692 	// send the datagram
693 	NET_SendPacket( sock, mbuf.cursize, mbuf.data, adr );
694 }
695 
696 /*
697 =============
698 NET_StringToAdr
699 
700 Traps "localhost" for loopback, passes everything else to system
701 =============
702 */
NET_StringToAdr(const char * s,netadr_t * a)703 qboolean	NET_StringToAdr( const char *s, netadr_t *a ) {
704 	qboolean	r;
705 	char	base[MAX_STRING_CHARS];
706 	char	*port;
707 
708 	if (!strcmp (s, "localhost")) {
709 		Com_Memset (a, 0, sizeof(*a));
710 		a->type = NA_LOOPBACK;
711 		return qtrue;
712 	}
713 
714 	// look for a port number
715 	Q_strncpyz( base, s, sizeof( base ) );
716 	port = strstr( base, ":" );
717 	if ( port ) {
718 		*port = 0;
719 		port++;
720 	}
721 
722 	r = Sys_StringToAdr( base, a );
723 
724 	if ( !r ) {
725 		a->type = NA_BAD;
726 		return qfalse;
727 	}
728 
729 	// inet_addr returns this if out of range
730 	if ( a->ip[0] == 255 && a->ip[1] == 255 && a->ip[2] == 255 && a->ip[3] == 255 ) {
731 		a->type = NA_BAD;
732 		return qfalse;
733 	}
734 
735 	if ( port ) {
736 		a->port = BigShort( (short)atoi( port ) );
737 	} else {
738 		a->port = BigShort( PORT_SERVER );
739 	}
740 
741 	return qtrue;
742 }
743 
744