Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

net_chan.c (17808B)


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