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