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 Quake III Arena source code; 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 "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 */
Netchan_Init(int port)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 */
Netchan_Setup(netsrc_t sock,netchan_t * chan,netadr_t adr,int qport)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 */
Netchan_TransmitNextFragment(netchan_t * chan)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 */
Netchan_Transmit(netchan_t * chan,int length,const byte * data)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 */
Netchan_Process(netchan_t * chan,msg_t * msg)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 			}
402 			// we can still keep the part that we have so far,
403 			// so we don't need to clear chan->fragmentLength
404 			return qfalse;
405 		}
406 
407 		// copy the fragment to the fragment buffer
408 		if ( fragmentLength < 0 || msg->readcount + fragmentLength > msg->cursize ||
409 			chan->fragmentLength + fragmentLength > sizeof( chan->fragmentBuffer ) ) {
410 			if ( showdrop->integer || showpackets->integer ) {
411 				Com_Printf ("%s:illegal fragment length\n"
412 				, NET_AdrToString (chan->remoteAddress ) );
413 			}
414 			return qfalse;
415 		}
416 
417 		Com_Memcpy( chan->fragmentBuffer + chan->fragmentLength,
418 			msg->data + msg->readcount, fragmentLength );
419 
420 		chan->fragmentLength += fragmentLength;
421 
422 		// if this wasn't the last fragment, don't process anything
423 		if ( fragmentLength == FRAGMENT_SIZE ) {
424 			return qfalse;
425 		}
426 
427 		if ( chan->fragmentLength > msg->maxsize ) {
428 			Com_Printf( "%s:fragmentLength %i > msg->maxsize\n"
429 				, NET_AdrToString (chan->remoteAddress ),
430 				chan->fragmentLength );
431 			return qfalse;
432 		}
433 
434 		// copy the full message over the partial fragment
435 
436 		// make sure the sequence number is still there
437 		*(int *)msg->data = LittleLong( sequence );
438 
439 		Com_Memcpy( msg->data + 4, chan->fragmentBuffer, chan->fragmentLength );
440 		msg->cursize = chan->fragmentLength + 4;
441 		chan->fragmentLength = 0;
442 		msg->readcount = 4;	// past the sequence number
443 		msg->bit = 32;	// past the sequence number
444 
445 		// TTimo
446 		// clients were not acking fragmented messages
447 		chan->incomingSequence = sequence;
448 
449 		return qtrue;
450 	}
451 
452 	//
453 	// the message can now be read from the current message pointer
454 	//
455 	chan->incomingSequence = sequence;
456 
457 	return qtrue;
458 }
459 
460 
461 //==============================================================================
462 
463 
464 /*
465 =============================================================================
466 
467 LOOPBACK BUFFERS FOR LOCAL PLAYER
468 
469 =============================================================================
470 */
471 
472 // there needs to be enough loopback messages to hold a complete
473 // gamestate of maximum size
474 #define	MAX_LOOPBACK	16
475 
476 typedef struct {
477 	byte	data[MAX_PACKETLEN];
478 	int		datalen;
479 } loopmsg_t;
480 
481 typedef struct {
482 	loopmsg_t	msgs[MAX_LOOPBACK];
483 	int			get, send;
484 } loopback_t;
485 
486 loopback_t	loopbacks[2];
487 
488 
NET_GetLoopPacket(netsrc_t sock,netadr_t * net_from,msg_t * net_message)489 qboolean	NET_GetLoopPacket (netsrc_t sock, netadr_t *net_from, msg_t *net_message)
490 {
491 	int		i;
492 	loopback_t	*loop;
493 
494 	loop = &loopbacks[sock];
495 
496 	if (loop->send - loop->get > MAX_LOOPBACK)
497 		loop->get = loop->send - MAX_LOOPBACK;
498 
499 	if (loop->get >= loop->send)
500 		return qfalse;
501 
502 	i = loop->get & (MAX_LOOPBACK-1);
503 	loop->get++;
504 
505 	Com_Memcpy (net_message->data, loop->msgs[i].data, loop->msgs[i].datalen);
506 	net_message->cursize = loop->msgs[i].datalen;
507 	Com_Memset (net_from, 0, sizeof(*net_from));
508 	net_from->type = NA_LOOPBACK;
509 	return qtrue;
510 
511 }
512 
513 
NET_SendLoopPacket(netsrc_t sock,int length,const void * data,netadr_t to)514 void NET_SendLoopPacket (netsrc_t sock, int length, const void *data, netadr_t to)
515 {
516 	int		i;
517 	loopback_t	*loop;
518 
519 	loop = &loopbacks[sock^1];
520 
521 	i = loop->send & (MAX_LOOPBACK-1);
522 	loop->send++;
523 
524 	Com_Memcpy (loop->msgs[i].data, data, length);
525 	loop->msgs[i].datalen = length;
526 }
527 
528 //=============================================================================
529 
530 typedef struct packetQueue_s {
531         struct packetQueue_s *next;
532         int length;
533         byte *data;
534         netadr_t to;
535         int release;
536 } packetQueue_t;
537 
538 packetQueue_t *packetQueue = NULL;
539 
NET_QueuePacket(int length,const void * data,netadr_t to,int offset)540 static void NET_QueuePacket( int length, const void *data, netadr_t to,
541 	int offset )
542 {
543 	packetQueue_t *new, *next = packetQueue;
544 
545 	if(offset > 999)
546 		offset = 999;
547 
548 	new = S_Malloc(sizeof(packetQueue_t));
549 	new->data = S_Malloc(length);
550 	Com_Memcpy(new->data, data, length);
551 	new->length = length;
552 	new->to = to;
553 	new->release = Sys_Milliseconds() + offset;
554 	new->next = NULL;
555 
556 	if(!packetQueue) {
557 		packetQueue = new;
558 		return;
559 	}
560 	while(next) {
561 		if(!next->next) {
562 			next->next = new;
563 			return;
564 		}
565 		next = next->next;
566 	}
567 }
568 
NET_FlushPacketQueue(void)569 void NET_FlushPacketQueue(void)
570 {
571 	packetQueue_t *last;
572 	int now;
573 
574 	while(packetQueue) {
575 		now = Sys_Milliseconds();
576 		if(packetQueue->release >= now)
577 			break;
578 		Sys_SendPacket(packetQueue->length, packetQueue->data,
579 			packetQueue->to);
580 		last = packetQueue;
581 		packetQueue = packetQueue->next;
582 		Z_Free(last->data);
583 		Z_Free(last);
584 	}
585 }
586 
NET_SendPacket(netsrc_t sock,int length,const void * data,netadr_t to)587 void NET_SendPacket( netsrc_t sock, int length, const void *data, netadr_t to ) {
588 
589 	// sequenced packets are shown in netchan, so just show oob
590 	if ( showpackets->integer && *(int *)data == -1 )	{
591 		Com_Printf ("send packet %4i\n", length);
592 	}
593 
594 	if ( to.type == NA_LOOPBACK ) {
595 		NET_SendLoopPacket (sock, length, data, to);
596 		return;
597 	}
598 	if ( to.type == NA_BOT ) {
599 		return;
600 	}
601 	if ( to.type == NA_BAD ) {
602 		return;
603 	}
604 
605 	if ( sock == NS_CLIENT && cl_packetdelay->integer > 0 ) {
606 		NET_QueuePacket( length, data, to, cl_packetdelay->integer );
607 	}
608 	else if ( sock == NS_SERVER && sv_packetdelay->integer > 0 ) {
609 		NET_QueuePacket( length, data, to, sv_packetdelay->integer );
610 	}
611 	else {
612 		Sys_SendPacket( length, data, to );
613 	}
614 }
615 
616 /*
617 ===============
618 NET_OutOfBandPrint
619 
620 Sends a text message in an out-of-band datagram
621 ================
622 */
NET_OutOfBandPrint(netsrc_t sock,netadr_t adr,const char * format,...)623 void QDECL NET_OutOfBandPrint( netsrc_t sock, netadr_t adr, const char *format, ... ) {
624 	va_list		argptr;
625 	char		string[MAX_MSGLEN];
626 
627 
628 	// set the header
629 	string[0] = -1;
630 	string[1] = -1;
631 	string[2] = -1;
632 	string[3] = -1;
633 
634 	va_start( argptr, format );
635 	Q_vsnprintf( string+4, sizeof(string)-4, format, argptr );
636 	va_end( argptr );
637 
638 	// send the datagram
639 	NET_SendPacket( sock, strlen( string ), string, adr );
640 }
641 
642 /*
643 ===============
644 NET_OutOfBandPrint
645 
646 Sends a data message in an out-of-band datagram (only used for "connect")
647 ================
648 */
NET_OutOfBandData(netsrc_t sock,netadr_t adr,byte * format,int len)649 void QDECL NET_OutOfBandData( netsrc_t sock, netadr_t adr, byte *format, int len ) {
650 	byte		string[MAX_MSGLEN*2];
651 	int			i;
652 	msg_t		mbuf;
653 
654 	// set the header
655 	string[0] = 0xff;
656 	string[1] = 0xff;
657 	string[2] = 0xff;
658 	string[3] = 0xff;
659 
660 	for(i=0;i<len;i++) {
661 		string[i+4] = format[i];
662 	}
663 
664 	mbuf.data = string;
665 	mbuf.cursize = len+4;
666 	Huff_Compress( &mbuf, 12);
667 	// send the datagram
668 	NET_SendPacket( sock, mbuf.cursize, mbuf.data, adr );
669 }
670 
671 /*
672 =============
673 NET_StringToAdr
674 
675 Traps "localhost" for loopback, passes everything else to system
676 return 0 on address not found, 1 on address found with port, 2 on address found without port.
677 =============
678 */
NET_StringToAdr(const char * s,netadr_t * a,netadrtype_t family)679 int NET_StringToAdr( const char *s, netadr_t *a, netadrtype_t family )
680 {
681 	char	base[MAX_STRING_CHARS], *search;
682 	char	*port = NULL;
683 
684 	if (!strcmp (s, "localhost")) {
685 		Com_Memset (a, 0, sizeof(*a));
686 		a->type = NA_LOOPBACK;
687 // as NA_LOOPBACK doesn't require ports report port was given.
688 		return 1;
689 	}
690 
691 	Q_strncpyz( base, s, sizeof( base ) );
692 
693 	if(*base == '[' || Q_CountChar(base, ':') > 1)
694 	{
695 		// This is an ipv6 address, handle it specially.
696 		search = strchr(base, ']');
697 		if(search)
698 		{
699 			*search = '\0';
700 			search++;
701 
702 			if(*search == ':')
703 				port = search + 1;
704 		}
705 
706 		if(*base == '[')
707 			search = base + 1;
708 		else
709 			search = base;
710 	}
711 	else
712 	{
713 		// look for a port number
714 		port = strchr( base, ':' );
715 
716 		if ( port ) {
717 			*port = '\0';
718 			port++;
719 		}
720 
721 		search = base;
722 	}
723 
724 	if(!Sys_StringToAdr(search, a, family))
725 	{
726 		a->type = NA_BAD;
727 		return 0;
728 	}
729 
730 	if(port)
731 	{
732 		a->port = BigShort((short) atoi(port));
733 		return 1;
734 	}
735 	else
736 	{
737 		a->port = BigShort(PORT_SERVER);
738 		return 2;
739 	}
740 }
741