1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Mozilla Communicator client code, released
15  * March 31, 1998.
16  *
17  * The Initial Developer of the Original Code is
18  * Netscape Communications Corporation.
19  * Portions created by the Initial Developer are Copyright (C) 1998-1999
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *
24  * Alternatively, the contents of this file may be used under the terms of
25  * either of the GNU General Public License Version 2 or later (the "GPL"),
26  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27  * in which case the provisions of the GPL or the LGPL are applicable instead
28  * of those above. If you wish to allow use of your version of this file only
29  * under the terms of either the GPL or the LGPL, and not to allow others to
30  * use your version of this file under the terms of the MPL, indicate your
31  * decision by deleting the provisions above and replace them with the notice
32  * and other provisions required by the GPL or the LGPL. If you do not delete
33  * the provisions above, a recipient may use your version of this file under
34  * the terms of any one of the MPL, the GPL or the LGPL.
35  *
36  * ***** END LICENSE BLOCK ***** */
37 /*
38  *	Copyright (c) 1990-92 Regents of the University of Michigan.
39  *	All rights reserved.
40  */
41 /*
42  *	tcp.c -- TCP communication related routines
43  */
44 #include "lber.h"
45 #include "ldap.h"
46 #include "tcp.h"
47 
48 #include <Devices.h>
49 #include <Events.h>		/* to get SystemTask() */
50 #include <Memory.h>
51 #include <Errors.h>
52 #include <Gestalt.h>
53 
54 /*
55  * local prototypes
56  */
57 #ifdef NEEDPROTOS
58 void		bzero( char *buf, unsigned long count );
59 pascal void setdoneflag( struct hostInfo *hip, char *donep );
60 pascal void EventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */);
61 pascal void DNREventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */);
62 #else /* NEEDPROTOS */
63 void		bzero();
64 pascal void setdoneflag();
65 pascal void EventHandler();
66 pascal void DNREventHandler();
67 #endif /* NEEDPROTOS */
68 
69 static Boolean gHaveOT = false;
70 static OTNotifyUPP		sEventHandlerUPP;
71 static EventRecord		dummyEvent;
72 
73 
74 /*
75  * Initialize the tcp module.  This mainly consists of seeing if we have
76  * Open Transport and initializing it and setting our global saying so.
77  */
78 OSStatus
tcp_init(void)79 tcp_init( void )
80 {
81 	static Boolean	sInitialized = false;
82 	Boolean 		hasOT;
83 	long 			gestaltResult;
84 	OSStatus		otErr = kOTNoError;
85 	OSErr			err;
86 
87 	if ( sInitialized ) {
88 		return otErr;
89 	}
90 
91 	err = Gestalt(gestaltOpenTpt, &gestaltResult);
92 
93 	hasOT = (err == noErr) && ((gestaltResult & gestaltOpenTptPresentMask) != 0);
94 	hasOT = hasOT && ((gestaltResult & gestaltOpenTptTCPPresentMask) != 0);
95 
96 	sInitialized = true;
97 
98 	if ( hasOT ) {
99 #if TARGET_CARBON
100 		gHaveOT = ( InitOpenTransportInContext(kInitOTForApplicationMask, NULL) == noErr );
101 #else /* TARGET_CARBON */
102 		gHaveOT = ( InitOpenTransport() == noErr );
103 #endif /* TARGET_CARBON */
104 
105 		sEventHandlerUPP = NewOTNotifyUPP(EventHandler);
106 		/* Somewhere we should probably do a DisposeOTNotifyUPP(sEventHandlerUPP) but since we only
107 		   create one once I'm not going to worry about a leak */
108 
109 	}
110 
111 	return otErr;
112 }
113 
114 
115 Boolean
tcp_have_opentransport(void)116 tcp_have_opentransport( void )
117 {
118 	return( gHaveOT );
119 }
120 
121 
122 /*
123  * open and return an pointer to a TCP stream (return NULL if error)
124  * "buf" is a buffer for receives of length "buflen."
125  */
126 tcpstream *
tcpopen(unsigned char * buf,long buflen)127 tcpopen( unsigned char * buf, long buflen ) {
128 	OSStatus		err;
129 	tcpstream *		tsp;
130 	short			drefnum;
131 	TEndpointInfo	info;
132 
133 	if (nil == (tsp = (tcpstream *)NewPtrClear(sizeof(tcpstream)))) {
134 		return( nil );
135 	}
136 
137 	if ( gHaveOT ) {
138 
139 		//
140 		// Now create a TCP
141 		//
142 #if TARGET_CARBON
143 		tsp->tcps_ep = OTOpenEndpointInContext( OTCreateConfiguration( kTCPName ), 0, &info, &err, NULL );
144 #else /* TARGET_CARBON */
145 		tsp->tcps_ep = OTOpenEndpoint( OTCreateConfiguration( kTCPName ), 0, &info, &err );
146 #endif /* TARGET_CARBON */
147 
148 		if ( !tsp->tcps_ep ) {
149 			if ( err == kOTNoError ) {
150 				err = -1;
151 			}
152 		}
153 
154 		if ( !err ) {
155 			err = OTSetSynchronous( tsp->tcps_ep );
156 		}
157 
158 		tsp->tcps_data = 0;
159 		tsp->tcps_terminated = tsp->tcps_connected = false;
160 
161 		//
162 		// Install notifier we're going to use
163 		//	need to pass tsp to make it work		%%%
164 		//
165 		if ( !err ) {
166 			err = OTInstallNotifier( tsp->tcps_ep, sEventHandlerUPP, tsp );
167 		}
168 
169 		if ( err != kOTNoError ) {
170 			if ( tsp->tcps_ep ) {
171 				OTCloseProvider( tsp->tcps_ep );
172 			}
173 			DisposePtr( (Ptr)tsp );
174 			tsp = nil;
175 		}
176 	}
177 	return( tsp );
178 }
179 
180 /*
181  * connect to remote host at IP address "addr", TCP port "port"
182  * return local port assigned, 0 if error
183  */
184 InetPort
tcpconnect(tcpstream * tsp,InetHost addr,InetPort port)185 tcpconnect( tcpstream * tsp, InetHost addr, InetPort port ) {
186 	OSStatus	err;
187 	struct InetAddress sndsin, rcvsin, retsin;
188 	TCall		sndcall, rcvcall;
189 	TBind		ret;
190 	TDiscon		localDiscon;
191 	unsigned long	time, end_time;
192 	int		timeout = 45;				/*	%%%	*/
193 
194 	if ( gHaveOT ) {
195 
196 		if ( tsp->tcps_ep == NULL ) {
197 			return( 0 );
198 		}
199 
200 		bzero( (char *)&sndsin, sizeof( struct InetAddress ));
201 		bzero( (char *)&rcvsin, sizeof( struct InetAddress ));
202 		bzero( (char *)&retsin, sizeof( struct InetAddress ));
203 		bzero( (char *)&sndcall, sizeof( TCall ));
204 		bzero( (char *)&rcvcall, sizeof( TCall));
205 		bzero( (char *)&ret, sizeof( TBind ));
206 
207 		// Bind TCP to an address and port.  We don't care which one, so we pass null for
208 		// the requested address.
209 		ret.addr.maxlen = sizeof( struct InetAddress );
210 		ret.addr.buf = (unsigned char *)&retsin;
211 		err = OTBind( tsp->tcps_ep, NULL, &ret );
212 		if ( err != kOTNoError ) {
213 			return( 0 );
214 		}
215 
216 
217 		// set to async mode, then OTConnect		%%%
218 		err = OTSetAsynchronous( tsp->tcps_ep );
219 
220 		OTInitInetAddress( &sndsin, port, addr );
221 		sndcall.addr.len = sizeof( struct InetAddress );
222 		sndcall.addr.buf = (UInt8 *)&sndsin;
223 		rcvcall.addr.maxlen = sizeof( struct InetAddress );
224 		rcvcall.addr.buf = (unsigned char *)&rcvsin;
225 
226 		err = OTConnect( tsp->tcps_ep, &sndcall, nil );
227 		if ( (err != kOTNoDataErr) && (err != kOTNoError) )
228 			return 0;
229 
230 		// wait for the OTConnect done		%%%
231 		GetDateTime( &time );
232 		end_time = time + timeout;
233 		while(( timeout <= 0 || end_time > time ) && !tsp->tcps_connected &&
234 				!tsp->tcps_terminated ) {
235 			GetDateTime( &time );
236 			(void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
237 		}
238 
239 		if ( tsp->tcps_connected )
240 		{
241 			err = OTRcvConnect( tsp->tcps_ep, &rcvcall );
242 			if ( err == kOTNoError)
243 			{
244 				tsp->tcps_remoteport = rcvsin.fPort;
245 				tsp->tcps_remoteaddr = rcvsin.fHost;
246 			}
247 			OTSetSynchronous( tsp->tcps_ep );	// set back to sync	%%%
248 			return( retsin.fPort );
249 		}
250 		else
251 		{
252 			if ( tsp->tcps_terminated )
253 				OTRcvDisconnect(tsp->tcps_ep, &localDiscon );
254 			return 0;
255 		}
256 	}
257 }
258 
259 
260 /*
261  * close and release a TCP stream
262  * returns 0 if no error, -1 if any error occurs
263  */
264 short
tcpclose(tcpstream * tsp)265 tcpclose( tcpstream * tsp ) {
266 	OSStatus		rc;
267 
268 	if ( gHaveOT ) {
269 
270 
271 		if ( tsp->tcps_ep == NULL ) {
272 			return( -1 );
273 		}
274 
275 #ifdef notdef
276 		/*
277 		 * if connected execute a close
278 		 */
279 		if ( tcp->tcps_connected ) {
280 			Call OTSndOrderlyDisconnect and wait for the other end to respond.  This requires
281 			waiting for and reading any data that comes in in the meantime.  This code was ifdefed
282 			out in the MacTCP so it is here too.
283 		}
284 #endif /* notdef */
285 
286 		rc = OTSndDisconnect( tsp->tcps_ep, NULL );
287 
288 		OTCloseProvider( tsp->tcps_ep );
289 		DisposePtr( (Ptr)tsp );
290 
291 		if ( rc != 0 ) {
292 			rc = -1;
293 		}
294 
295 	}
296 	return( rc );
297 }
298 
299 
300 /*
301  * wait for new data to arrive
302  *
303  * if "timeout" is NULL, wait until data arrives or connection goes away
304  * if "timeout" is a pointer to a zero'ed struct, poll
305  * else wait for "timeout->tv_sec + timeout->tv_usec" seconds
306  */
307 short
308 tcpselect( tcpstream * tsp, struct timeval * timeout )
309 {
310 	unsigned long ticks, endticks;
311 	short rc;
312 
313 	if ( timeout != NULL ) {
314 		endticks = 60 * timeout->tv_sec + ( 60 * timeout->tv_usec ) / 1000000 + TickCount();
315 	}
316 	ticks = 0;
317 
318 	while (( rc = tcpreadready( tsp )) == 0 && ( timeout == NULL || ticks < endticks )) {
319 		(void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
320 		ticks = TickCount();
321 	}
322 
323 	return ( rc );
324 }
325 
326 
327 short
328 tcpreadready( tcpstream *tsp )
329 {
330 	size_t 	dataAvail;
331 
332 	if (gHaveOT) {
333 		if ( tsp->tcps_ep == NULL ) {
334 			return( -1 );
335 		}
336 
337 		OTCountDataBytes( tsp->tcps_ep, &dataAvail );
338 		tsp->tcps_data = ( dataAvail != 0 );
339 	}
340 
341 	return ( tsp->tcps_terminated ? -1 : ( tsp->tcps_data < 1 ) ? 0 : 1 );
342 }
343 
344 
345 short
346 tcpwriteready( tcpstream *tsp )
347 {
348 	if (gHaveOT) {
349 		if ( tsp->tcps_ep == NULL ) {
350 			return( -1 );
351 		}
352 	}
353 
354 	return ( tsp->tcps_terminated ? -1 : 1 );
355 }
356 
357 
358 /*
359  * read up to "rbuflen" bytes into "rbuf" from a connected TCP stream
360  * wait up to "timeout" seconds for data (if timeout == 0, wait "forever")
361  */
362 long
363 tcpread( tcpstream * tsp, UInt8 timeout, unsigned char * rbuf,
364 						  unsigned short rbuflen, DialogPtr dlp ) {
365 #pragma unused (dlp)
366 	unsigned long	time, end_time;
367 	OTFlags			flags;
368 	OTResult		result;
369 	size_t 			dataAvail;
370 
371 	if ( gHaveOT ) {
372 
373 		if ( tsp->tcps_ep == NULL ) {
374 			return( -1 );
375 		}
376 
377 		// Try to read data.  Since we're in non-blocking mode, this will fail with kOTNoDataErr
378 		// if no data is available.
379 		result = OTRcv( tsp->tcps_ep, rbuf, rbuflen, &flags );
380 		if ( result == kOTNoDataErr ) {
381 			// Nothing available, wait for some.  The ugly spin loop below is the way the old
382 			// MacTCP code worked.  I should fix it, but I don't have time right now.
383 			tsp->tcps_data = 0;
384 			GetDateTime( &time );
385 			end_time = time + timeout;
386 
387 			while (( timeout <= 0 || end_time > time ) && tsp->tcps_data < 1 && !tsp->tcps_terminated ) {
388 				OTCountDataBytes( tsp->tcps_ep, &dataAvail );
389 				if ( dataAvail > 0 ) {
390 					tsp->tcps_data = 1;
391 				}
392 				GetDateTime( &time );
393 				(void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
394 			}
395 
396 			if ( tsp->tcps_data < 1 ) {
397 				return( tsp->tcps_terminated ? -3 : -1 );
398 			}
399 
400 			// Should have data available now, try again.
401 			result = OTRcv( tsp->tcps_ep, rbuf, rbuflen, &flags );
402 		}
403 
404 		if ( result < 0 ) {
405 			return( -1 );
406 		}
407 
408 		OTCountDataBytes( tsp->tcps_ep, &dataAvail );
409 		if ( dataAvail == 0 ) {
410 			tsp->tcps_data = 0;
411 		}
412 
413 		return( result );
414 
415 	}
416 }
417 
418 /*
419  * send TCP data
420  * "sp" is the stream to write to
421  * "wbuf" is "wbuflen" bytes of data to send
422  * returns < 0 if error, number of bytes written if no error
423  */
424 long
425 tcpwrite( tcpstream * tsp, unsigned char * wbuf, unsigned short wbuflen ) {
426 	OSErr 		err;
427 	OTResult 	result;
428 
429 	if ( gHaveOT ) {
430 
431 		if ( tsp->tcps_ep == NULL ) {
432 			return( -1 );
433 		}
434 
435 		OTSetBlocking( tsp->tcps_ep );
436 		result = OTSnd( tsp->tcps_ep, wbuf, wbuflen, 0 );
437 		OTSetNonBlocking( tsp->tcps_ep );
438 		return( result );
439 
440 	}
441 }
442 
443 
444 
445 short
446 tcpgetpeername( tcpstream *tsp, InetHost *addrp, InetPort *portp ) {
447 	OSErr		err;
448 
449 	if ( gHaveOT ) {
450 
451 		if ( tsp->tcps_ep == NULL ) {
452 			return( -1 );
453 		}
454 
455 		if ( addrp != NULL ) {
456 			*addrp = tsp->tcps_remoteaddr;
457 		}
458 
459 		if ( portp != NULL ) {
460 			*portp = tsp->tcps_remoteport;
461 		}
462 
463 	}
464 
465 	return( kOTNoError );
466 }
467 
468 
469 /*
470  * bzero -- set "len" bytes starting at "p" to zero
471  */
472 static void
473 bzero( char *p, unsigned long len )
474 {
475 	unsigned long	i;
476 
477 	for ( i = 0; i < len; ++i ) {
478 		*p++ = '\0';
479 	}
480 }
481 
482 
483 /*
484  * return a hostInfo structure for "host" (return != noErr if error)
485  */
486 short
487 gethostinfobyname( char *host, InetHostInfo *hip ) {
488 	char			done = 0;
489 	OSStatus		err;
490 	unsigned long	time;
491 	InetSvcRef		inetsvc;		/* Internet services reference */
492 	unsigned long	cur_time, end_time;
493 	int				timeout = 20;
494 	static int 		sNotifiedWithResult;
495 	static int* 	sNpointer;
496 	OTNotifyUPP		DNREventHandlerUPP;
497 
498 	if ( gHaveOT ) {
499 
500 		// Get an Internet Services reference
501 		inetsvc = OTOpenInternetServices( kDefaultInternetServicesPath, 0, &err );
502 		if ( !inetsvc || err != kOTNoError ) {
503 			if ( err == kOTNoError ) {
504 				err = -1;
505 			}
506 			inetsvc = nil;
507 		}
508 
509 		if ( !err ) {
510 			// set to async mode				%%%
511 			sNotifiedWithResult = 0;
512 			sNpointer = &sNotifiedWithResult;
513 			DNREventHandlerUPP = NewOTNotifyUPP(DNREventHandler);
514 			err = OTInstallNotifier(inetsvc, DNREventHandlerUPP, (void*)sNpointer);
515 			err = OTSetAsynchronous( inetsvc );
516 
517 			err = OTInetStringToAddress( inetsvc, host, hip );
518 			if ( err == kOTNoError ) {
519 				GetDateTime( &cur_time );
520 				end_time = cur_time + timeout;
521 				while(( timeout <= 0 || end_time > cur_time ) && (sNotifiedWithResult == 0)) {
522 					GetDateTime( &cur_time );
523 					(void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
524 				}
525 				if ( sNotifiedWithResult != 1 )
526 					err = -1;
527 			}
528 
529 			DisposeOTNotifyUPP(DNREventHandlerUPP);
530 		}
531 
532 		if ( inetsvc ) {
533 			OTCloseProvider(inetsvc);
534 		}
535 	}
536 
537 	return( err );
538 }
539 
540 /*
541  * return a hostInfo structure for "addr" (return != noErr if error)
542  */
543 short
544 gethostinfobyaddr( InetHost addr, InetHostInfo *hip )
545 {
546 
547 	char			done = 0;
548 	OSStatus		err;
549 	unsigned long	time;
550 	InetSvcRef		inetsvc;		/* Internet services reference */
551 	unsigned long	cur_time, end_time;
552 	int				timeout = 20;
553 	static int 		sNotifiedWithResult2;
554 	static int* 	sNpointer2;
555 	OTNotifyUPP		DNREventHandlerUPP;
556 
557 	if ( gHaveOT ) {
558 		// Get an Internet Services reference
559 		inetsvc = OTOpenInternetServices( kDefaultInternetServicesPath, 0, &err );
560 		if ( !inetsvc || err != kOTNoError ) {
561 			if ( err == kOTNoError ) {
562 				err = -1;
563 			}
564 			inetsvc = nil;
565 		}
566 
567 		if ( !err ) {
568 			// set to async mode				%%%
569 			sNotifiedWithResult2 = 0;
570 			sNpointer2 = &sNotifiedWithResult2;
571 			DNREventHandlerUPP = NewOTNotifyUPP(DNREventHandler);
572 			err = OTInstallNotifier(inetsvc, DNREventHandlerUPP, (void*)sNpointer2);
573 			err = OTSetAsynchronous( inetsvc );
574 
575 			err = OTInetAddressToName( inetsvc, addr, hip->name );
576 			if ( err == kOTNoError ) {
577 				GetDateTime( &cur_time );
578 				end_time = cur_time + timeout;
579 				while(( timeout <= 0 || end_time > cur_time ) && (sNotifiedWithResult2 == 0)) {
580 					GetDateTime( &cur_time );
581 					(void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
582 				}
583 				if ( sNotifiedWithResult2 != 1 )
584 					err = -1;
585 
586 				DisposeOTNotifyUPP(DNREventHandlerUPP);
587 			}
588 		}
589 
590 		if ( inetsvc ) {
591 			OTCloseProvider( inetsvc );
592 		}
593 	}
594 	return( err );
595 }
596 
597 /*
598  * return a ASCII equivalent of ipaddr.  addrstr must be at large enough to hold the
599  * result which is of the form "AAA.BBB.CCC.DDD" and can be a maximum of 16 bytes in size
600  */
601 short
602 ipaddr2str( InetHost ipaddr, char *addrstr )
603 {
604 	OSStatus				err;
605 
606 	if ( gHaveOT ) {
607 
608 		OTInetHostToString( ipaddr, addrstr );
609 		err = kOTNoError;
610 
611 	}
612 	return( err );
613 }
614 
615 
616 /*******************************************************************************
617 ** EventHandler
618 ********************************************************************************/
619 static pascal void EventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */)
620 {
621 	tcpstream *tsp	= (tcpstream *)contextPtr;
622 	Boolean enteredNotifier = OTEnterNotifier(tsp->tcps_ep);
623 
624 	switch(event)
625 	{
626    		// OTConnect callback		 		%%%
627 		case T_CONNECT:
628 			if ( result == noErr )	{
629 				tsp->tcps_connected = true;
630 			}
631 			break;
632 
633 		case T_ORDREL:
634 		case T_DISCONNECT:
635 			tsp->tcps_terminated = true;
636 			break;
637 		case T_DATA:
638 		case T_EXDATA:
639 			tsp->tcps_data += 1;
640 			break;
641 		default:
642 			break;
643 	}
644 
645 	if (enteredNotifier) {
646 		OTLeaveNotifier(tsp->tcps_ep);
647 	}
648 
649 	return;
650 }
651 
652 /*******************************************************************************
653 ** Simplified EventHandler for DNR events
654 ********************************************************************************/
655 static pascal void DNREventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */)
656 {
657 	switch(event)
658 	{
659 		// OTInetStringToAddress Complete	%%%
660         case T_DNRSTRINGTOADDRCOMPLETE:
661         // OTInetAddressToName Complete		%%%
662         case T_DNRADDRTONAMECOMPLETE:
663         	{
664         	int *done = (void *)contextPtr;
665         	if ( result == noErr )
666 				*done = 1;
667 			else
668 				*done = 2;
669 			}
670 			break;
671 
672 		default:
673 			break;
674 	}
675 }
676