Back to index

lightning-sunbird  0.9+nobinonly
tcp.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 /*
00038  *     Copyright (c) 1990-92 Regents of the University of Michigan.
00039  *     All rights reserved.
00040  */
00041 /*
00042  *     tcp.c -- TCP communication related routines
00043  */
00044 #include "lber.h"
00045 #include "ldap.h"
00046 #include "tcp.h"
00047 
00048 #include <Devices.h>
00049 #include <Events.h>         /* to get SystemTask() */
00050 #include <Memory.h>
00051 #include <Errors.h>
00052 #include <Gestalt.h>
00053 
00054 /*
00055  * local prototypes
00056  */
00057 #ifdef NEEDPROTOS
00058 void          bzero( char *buf, unsigned long count );
00059 pascal void setdoneflag( struct hostInfo *hip, char *donep );
00060 pascal void EventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */);
00061 pascal void DNREventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */);
00062 #else /* NEEDPROTOS */
00063 void          bzero();
00064 pascal void setdoneflag();
00065 pascal void EventHandler();
00066 pascal void DNREventHandler();
00067 #endif /* NEEDPROTOS */
00068 
00069 static Boolean gHaveOT = false;
00070 static OTNotifyUPP          sEventHandlerUPP;
00071 static EventRecord          dummyEvent;
00072 
00073 
00074 /*
00075  * Initialize the tcp module.  This mainly consists of seeing if we have
00076  * Open Transport and initializing it and setting our global saying so.
00077  */ 
00078 OSStatus
00079 tcp_init( void )
00080 {
00081        static Boolean       sInitialized = false;
00082        Boolean              hasOT;
00083        long                 gestaltResult;
00084        OSStatus             otErr = kOTNoError;
00085        OSErr                err;
00086        
00087        if ( sInitialized ) {
00088               return otErr;
00089        }
00090        
00091        err = Gestalt(gestaltOpenTpt, &gestaltResult);
00092        
00093        hasOT = (err == noErr) && ((gestaltResult & gestaltOpenTptPresentMask) != 0);
00094        hasOT = hasOT && ((gestaltResult & gestaltOpenTptTCPPresentMask) != 0);
00095 
00096        sInitialized = true;
00097        
00098        if ( hasOT ) {
00099 #if TARGET_CARBON
00100               gHaveOT = ( InitOpenTransportInContext(kInitOTForApplicationMask, NULL) == noErr );
00101 #else /* TARGET_CARBON */
00102               gHaveOT = ( InitOpenTransport() == noErr );
00103 #endif /* TARGET_CARBON */
00104 
00105               sEventHandlerUPP = NewOTNotifyUPP(EventHandler);
00106               /* Somewhere we should probably do a DisposeOTNotifyUPP(sEventHandlerUPP) but since we only
00107                  create one once I'm not going to worry about a leak */
00108 
00109        }
00110 
00111        return otErr;
00112 }
00113 
00114 
00115 Boolean
00116 tcp_have_opentransport( void )
00117 {
00118        return( gHaveOT );
00119 }
00120 
00121 
00122 /*
00123  * open and return an pointer to a TCP stream (return NULL if error)
00124  * "buf" is a buffer for receives of length "buflen."
00125  */
00126 tcpstream *
00127 tcpopen( unsigned char * buf, long buflen ) {
00128        OSStatus             err;
00129        tcpstream *          tsp;
00130        short                drefnum;
00131        TEndpointInfo info;
00132 
00133        if (nil == (tsp = (tcpstream *)NewPtrClear(sizeof(tcpstream)))) {
00134               return( nil );
00135        }
00136        
00137        if ( gHaveOT ) {
00138        
00139               //
00140               // Now create a TCP
00141               //
00142 #if TARGET_CARBON
00143               tsp->tcps_ep = OTOpenEndpointInContext( OTCreateConfiguration( kTCPName ), 0, &info, &err, NULL );
00144 #else /* TARGET_CARBON */
00145               tsp->tcps_ep = OTOpenEndpoint( OTCreateConfiguration( kTCPName ), 0, &info, &err );
00146 #endif /* TARGET_CARBON */
00147 
00148               if ( !tsp->tcps_ep ) {
00149                      if ( err == kOTNoError ) {
00150                             err = -1;
00151                      }
00152               }
00153        
00154               if ( !err ) {
00155                      err = OTSetSynchronous( tsp->tcps_ep );
00156               }
00157        
00158               tsp->tcps_data = 0;
00159               tsp->tcps_terminated = tsp->tcps_connected = false;
00160               
00161               //
00162               // Install notifier we're going to use
00163               //     need to pass tsp to make it work          %%%
00164               //
00165               if ( !err ) {
00166                      err = OTInstallNotifier( tsp->tcps_ep, sEventHandlerUPP, tsp );
00167               }
00168        
00169               if ( err != kOTNoError ) {
00170                      if ( tsp->tcps_ep ) {
00171                             OTCloseProvider( tsp->tcps_ep );
00172                      }
00173                      DisposePtr( (Ptr)tsp );
00174                      tsp = nil;
00175               }
00176        }      
00177        return( tsp );
00178 }
00179 
00180 /*
00181  * connect to remote host at IP address "addr", TCP port "port"
00182  * return local port assigned, 0 if error
00183  */
00184 InetPort
00185 tcpconnect( tcpstream * tsp, InetHost addr, InetPort port ) {
00186        OSStatus      err;
00187        struct InetAddress sndsin, rcvsin, retsin;
00188        TCall         sndcall, rcvcall;
00189        TBind         ret;
00190        TDiscon              localDiscon;
00191        unsigned long time, end_time;
00192        int           timeout = 45;                      /*     %%%    */
00193 
00194        if ( gHaveOT ) {
00195        
00196               if ( tsp->tcps_ep == NULL ) {
00197                      return( 0 );
00198               }
00199        
00200               bzero( (char *)&sndsin, sizeof( struct InetAddress ));
00201               bzero( (char *)&rcvsin, sizeof( struct InetAddress ));
00202               bzero( (char *)&retsin, sizeof( struct InetAddress ));
00203               bzero( (char *)&sndcall, sizeof( TCall ));
00204               bzero( (char *)&rcvcall, sizeof( TCall));
00205               bzero( (char *)&ret, sizeof( TBind ));
00206               
00207               // Bind TCP to an address and port.  We don't care which one, so we pass null for
00208               // the requested address.
00209               ret.addr.maxlen = sizeof( struct InetAddress );
00210               ret.addr.buf = (unsigned char *)&retsin;
00211               err = OTBind( tsp->tcps_ep, NULL, &ret );
00212               if ( err != kOTNoError ) {
00213                      return( 0 );
00214               }
00215        
00216        
00217               // set to async mode, then OTConnect             %%%
00218               err = OTSetAsynchronous( tsp->tcps_ep );
00219 
00220               OTInitInetAddress( &sndsin, port, addr );
00221               sndcall.addr.len = sizeof( struct InetAddress );
00222               sndcall.addr.buf = (UInt8 *)&sndsin;
00223               rcvcall.addr.maxlen = sizeof( struct InetAddress );
00224               rcvcall.addr.buf = (unsigned char *)&rcvsin;
00225 
00226               err = OTConnect( tsp->tcps_ep, &sndcall, nil );
00227               if ( (err != kOTNoDataErr) && (err != kOTNoError) )     
00228                      return 0;
00229        
00230               // wait for the OTConnect done            %%%    
00231               GetDateTime( &time );
00232               end_time = time + timeout;
00233               while(( timeout <= 0 || end_time > time ) && !tsp->tcps_connected &&
00234                             !tsp->tcps_terminated ) {
00235                      GetDateTime( &time );
00236                      (void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
00237               }
00238               
00239               if ( tsp->tcps_connected )
00240               {
00241                      err = OTRcvConnect( tsp->tcps_ep, &rcvcall );
00242                      if ( err == kOTNoError)
00243                      {
00244                             tsp->tcps_remoteport = rcvsin.fPort;
00245                             tsp->tcps_remoteaddr = rcvsin.fHost;
00246                      }
00247                      OTSetSynchronous( tsp->tcps_ep );  // set back to sync  %%%    
00248                      return( retsin.fPort );
00249               }
00250               else
00251               {
00252                      if ( tsp->tcps_terminated )
00253                             OTRcvDisconnect(tsp->tcps_ep, &localDiscon );
00254                      return 0;
00255               }      
00256        }
00257 }
00258 
00259 
00260 /*
00261  * close and release a TCP stream
00262  * returns 0 if no error, -1 if any error occurs
00263  */
00264 short
00265 tcpclose( tcpstream * tsp ) {
00266        OSStatus             rc;
00267        
00268        if ( gHaveOT ) {
00269        
00270        
00271               if ( tsp->tcps_ep == NULL ) {
00272                      return( -1 );
00273               }
00274        
00275 #ifdef notdef
00276               /*
00277                * if connected execute a close
00278                */
00279               if ( tcp->tcps_connected ) {
00280                      Call OTSndOrderlyDisconnect and wait for the other end to respond.  This requires
00281                      waiting for and reading any data that comes in in the meantime.  This code was ifdefed
00282                      out in the MacTCP so it is here too.
00283               }
00284 #endif /* notdef */
00285               
00286               rc = OTSndDisconnect( tsp->tcps_ep, NULL );
00287        
00288               OTCloseProvider( tsp->tcps_ep );
00289               DisposePtr( (Ptr)tsp );
00290               
00291               if ( rc != 0 ) {
00292                      rc = -1;
00293               }
00294 
00295        }             
00296        return( rc );
00297 }
00298 
00299 
00300 /*
00301  * wait for new data to arrive
00302  *
00303  * if "timeout" is NULL, wait until data arrives or connection goes away
00304  * if "timeout" is a pointer to a zero'ed struct, poll
00305  * else wait for "timeout->tv_sec + timeout->tv_usec" seconds
00306  */
00307 short
00308 tcpselect( tcpstream * tsp, struct timeval * timeout )
00309 {
00310        unsigned long ticks, endticks;
00311        short rc;
00312 
00313        if ( timeout != NULL ) {
00314               endticks = 60 * timeout->tv_sec + ( 60 * timeout->tv_usec ) / 1000000 + TickCount();
00315        }
00316        ticks = 0;
00317 
00318        while (( rc = tcpreadready( tsp )) == 0 && ( timeout == NULL || ticks < endticks )) {
00319               (void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
00320               ticks = TickCount();
00321        }
00322 
00323        return ( rc );
00324 }
00325 
00326 
00327 short
00328 tcpreadready( tcpstream *tsp )
00329 {
00330        size_t        dataAvail;
00331 
00332        if (gHaveOT) {
00333               if ( tsp->tcps_ep == NULL ) {
00334                      return( -1 );
00335               }
00336        
00337               OTCountDataBytes( tsp->tcps_ep, &dataAvail );
00338               tsp->tcps_data = ( dataAvail != 0 );
00339        }
00340 
00341        return ( tsp->tcps_terminated ? -1 : ( tsp->tcps_data < 1 ) ? 0 : 1 );
00342 }
00343 
00344 
00345 short
00346 tcpwriteready( tcpstream *tsp )
00347 {
00348        if (gHaveOT) {
00349               if ( tsp->tcps_ep == NULL ) {
00350                      return( -1 );
00351               }
00352        }
00353 
00354        return ( tsp->tcps_terminated ? -1 : 1 );
00355 }
00356 
00357 
00358 /*
00359  * read up to "rbuflen" bytes into "rbuf" from a connected TCP stream
00360  * wait up to "timeout" seconds for data (if timeout == 0, wait "forever")
00361  */
00362 long
00363 tcpread( tcpstream * tsp, UInt8 timeout, unsigned char * rbuf,
00364                                             unsigned short rbuflen, DialogPtr dlp ) {
00365 #pragma unused (dlp)
00366        unsigned long time, end_time;
00367        OTFlags                     flags;
00368        OTResult             result;
00369        size_t                      dataAvail;
00370        
00371        if ( gHaveOT ) {
00372        
00373               if ( tsp->tcps_ep == NULL ) {
00374                      return( -1 );
00375               }
00376               
00377               // Try to read data.  Since we're in non-blocking mode, this will fail with kOTNoDataErr
00378               // if no data is available.
00379               result = OTRcv( tsp->tcps_ep, rbuf, rbuflen, &flags );
00380               if ( result == kOTNoDataErr ) {
00381                      // Nothing available, wait for some.  The ugly spin loop below is the way the old
00382                      // MacTCP code worked.  I should fix it, but I don't have time right now.
00383                      tsp->tcps_data = 0;
00384                      GetDateTime( &time );
00385                      end_time = time + timeout;
00386               
00387                      while (( timeout <= 0 || end_time > time ) && tsp->tcps_data < 1 && !tsp->tcps_terminated ) {
00388                             OTCountDataBytes( tsp->tcps_ep, &dataAvail );
00389                             if ( dataAvail > 0 ) {
00390                                    tsp->tcps_data = 1;
00391                             }
00392                             GetDateTime( &time );
00393                             (void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
00394                      }
00395                      
00396                      if ( tsp->tcps_data < 1 ) {
00397                             return( tsp->tcps_terminated ? -3 : -1 );
00398                      }
00399                      
00400                      // Should have data available now, try again.
00401                      result = OTRcv( tsp->tcps_ep, rbuf, rbuflen, &flags );
00402               }
00403               
00404               if ( result < 0 ) {
00405                      return( -1 );
00406               }
00407               
00408               OTCountDataBytes( tsp->tcps_ep, &dataAvail );
00409               if ( dataAvail == 0 ) {
00410                      tsp->tcps_data = 0;
00411               }
00412 
00413               return( result );
00414        
00415        }      
00416 }
00417 
00418 /*
00419  * send TCP data
00420  * "sp" is the stream to write to
00421  * "wbuf" is "wbuflen" bytes of data to send
00422  * returns < 0 if error, number of bytes written if no error
00423  */
00424 long
00425 tcpwrite( tcpstream * tsp, unsigned char * wbuf, unsigned short wbuflen ) {
00426        OSErr         err;
00427        OTResult      result;
00428        
00429        if ( gHaveOT ) {
00430        
00431               if ( tsp->tcps_ep == NULL ) {
00432                      return( -1 );
00433               }
00434               
00435               OTSetBlocking( tsp->tcps_ep );
00436               result = OTSnd( tsp->tcps_ep, wbuf, wbuflen, 0 );
00437               OTSetNonBlocking( tsp->tcps_ep );
00438               return( result );
00439               
00440        }
00441 }
00442 
00443 
00444 
00445 short
00446 tcpgetpeername( tcpstream *tsp, InetHost *addrp, InetPort *portp ) {
00447        OSErr         err;
00448 
00449        if ( gHaveOT ) {
00450        
00451               if ( tsp->tcps_ep == NULL ) {
00452                      return( -1 );
00453               }
00454                      
00455               if ( addrp != NULL ) {
00456                      *addrp = tsp->tcps_remoteaddr;
00457               }
00458        
00459               if ( portp != NULL ) {
00460                      *portp = tsp->tcps_remoteport;
00461               }
00462               
00463        }
00464 
00465        return( kOTNoError );
00466 }
00467 
00468 
00469 /*
00470  * bzero -- set "len" bytes starting at "p" to zero
00471  */
00472 static void
00473 bzero( char *p, unsigned long len )
00474 {
00475        unsigned long i;
00476        
00477        for ( i = 0; i < len; ++i ) {
00478               *p++ = '\0';
00479        }
00480 }
00481 
00482 
00483 /*
00484  * return a hostInfo structure for "host" (return != noErr if error)
00485  */
00486 short
00487 gethostinfobyname( char *host, InetHostInfo *hip ) {
00488        char                 done = 0;
00489        OSStatus             err;
00490        unsigned long time;
00491        InetSvcRef           inetsvc;             /* Internet services reference */
00492        unsigned long cur_time, end_time;
00493        int                         timeout = 20;
00494        static int           sNotifiedWithResult;
00495        static int*   sNpointer;
00496        OTNotifyUPP          DNREventHandlerUPP;
00497        
00498        if ( gHaveOT ) {
00499        
00500               // Get an Internet Services reference
00501               inetsvc = OTOpenInternetServices( kDefaultInternetServicesPath, 0, &err );
00502               if ( !inetsvc || err != kOTNoError ) {
00503                      if ( err == kOTNoError ) {
00504                             err = -1;
00505                      }
00506                      inetsvc = nil;
00507               }
00508               
00509               if ( !err ) {
00510                      // set to async mode                      %%%
00511                      sNotifiedWithResult = 0;
00512                      sNpointer = &sNotifiedWithResult;
00513                      DNREventHandlerUPP = NewOTNotifyUPP(DNREventHandler);
00514                      err = OTInstallNotifier(inetsvc, DNREventHandlerUPP, (void*)sNpointer);
00515                      err = OTSetAsynchronous( inetsvc );
00516 
00517                      err = OTInetStringToAddress( inetsvc, host, hip );
00518                      if ( err == kOTNoError ) {
00519                             GetDateTime( &cur_time );
00520                             end_time = cur_time + timeout;
00521                             while(( timeout <= 0 || end_time > cur_time ) && (sNotifiedWithResult == 0)) {
00522                                    GetDateTime( &cur_time );
00523                                    (void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
00524                             }
00525                             if ( sNotifiedWithResult != 1 )
00526                                    err = -1;
00527                      }                           
00528                      
00529                      DisposeOTNotifyUPP(DNREventHandlerUPP);
00530               }
00531               
00532               if ( inetsvc ) {
00533                      OTCloseProvider(inetsvc);
00534               }
00535        }
00536        
00537        return( err );
00538 }
00539 
00540 /*
00541  * return a hostInfo structure for "addr" (return != noErr if error)
00542  */
00543 short
00544 gethostinfobyaddr( InetHost addr, InetHostInfo *hip )
00545 {
00546        
00547        char                 done = 0;
00548        OSStatus             err;
00549        unsigned long time;
00550        InetSvcRef           inetsvc;             /* Internet services reference */
00551        unsigned long cur_time, end_time;
00552        int                         timeout = 20;
00553        static int           sNotifiedWithResult2;
00554        static int*   sNpointer2;
00555        OTNotifyUPP          DNREventHandlerUPP;
00556 
00557        if ( gHaveOT ) {
00558               // Get an Internet Services reference
00559               inetsvc = OTOpenInternetServices( kDefaultInternetServicesPath, 0, &err );
00560               if ( !inetsvc || err != kOTNoError ) {
00561                      if ( err == kOTNoError ) {
00562                             err = -1;
00563                      }
00564                      inetsvc = nil;
00565               }
00566               
00567               if ( !err ) {
00568                      // set to async mode                      %%%
00569                      sNotifiedWithResult2 = 0;
00570                      sNpointer2 = &sNotifiedWithResult2;
00571                      DNREventHandlerUPP = NewOTNotifyUPP(DNREventHandler);
00572                      err = OTInstallNotifier(inetsvc, DNREventHandlerUPP, (void*)sNpointer2);
00573                      err = OTSetAsynchronous( inetsvc );
00574 
00575                      err = OTInetAddressToName( inetsvc, addr, hip->name );
00576                      if ( err == kOTNoError ) {
00577                             GetDateTime( &cur_time );
00578                             end_time = cur_time + timeout;
00579                             while(( timeout <= 0 || end_time > cur_time ) && (sNotifiedWithResult2 == 0)) {
00580                                    GetDateTime( &cur_time );
00581                                    (void)WaitNextEvent(nullEvent, &dummyEvent, 1, NULL);
00582                             }
00583                             if ( sNotifiedWithResult2 != 1 )
00584                                    err = -1;
00585 
00586                             DisposeOTNotifyUPP(DNREventHandlerUPP);
00587                      }                           
00588               }
00589               
00590               if ( inetsvc ) {
00591                      OTCloseProvider( inetsvc );
00592               }
00593        }
00594        return( err );
00595 }
00596 
00597 /*
00598  * return a ASCII equivalent of ipaddr.  addrstr must be at large enough to hold the
00599  * result which is of the form "AAA.BBB.CCC.DDD" and can be a maximum of 16 bytes in size
00600  */
00601 short
00602 ipaddr2str( InetHost ipaddr, char *addrstr )
00603 {
00604        OSStatus                           err;
00605 
00606        if ( gHaveOT ) {
00607        
00608               OTInetHostToString( ipaddr, addrstr );
00609               err = kOTNoError;
00610        
00611        }      
00612        return( err );
00613 }
00614 
00615 
00616 /*******************************************************************************
00617 ** EventHandler
00618 ********************************************************************************/
00619 static pascal void EventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */)
00620 {
00621        tcpstream *tsp       = (tcpstream *)contextPtr;
00622        Boolean enteredNotifier = OTEnterNotifier(tsp->tcps_ep);
00623        
00624        switch(event)
00625        {
00626               // OTConnect callback                            %%%
00627               case T_CONNECT:      
00628                      if ( result == noErr )      {                           
00629                             tsp->tcps_connected = true;
00630                      }
00631                      break;
00632 
00633               case T_ORDREL:
00634               case T_DISCONNECT:
00635                      tsp->tcps_terminated = true;
00636                      break;
00637               case T_DATA:
00638               case T_EXDATA:
00639                      tsp->tcps_data += 1;
00640                      break;
00641               default:
00642                      break;
00643        }
00644        
00645        if (enteredNotifier) {
00646               OTLeaveNotifier(tsp->tcps_ep);
00647        }
00648        
00649        return;
00650 }
00651 
00652 /*******************************************************************************
00653 ** Simplified EventHandler for DNR events
00654 ********************************************************************************/
00655 static pascal void DNREventHandler(void * contextPtr, OTEventCode event, OTResult result, void * /* cookie */)
00656 {
00657        switch(event)
00658        {
00659               // OTInetStringToAddress Complete  %%%
00660         case T_DNRSTRINGTOADDRCOMPLETE:
00661         // OTInetAddressToName Complete          %%%    
00662         case T_DNRADDRTONAMECOMPLETE:
00663               {
00664               int *done = (void *)contextPtr;
00665               if ( result == noErr )
00666                             *done = 1;
00667                      else
00668                             *done = 2;
00669                      }
00670                      break;
00671                      
00672               default:
00673                      break;
00674        }
00675 }