mirror of https://github.com/macssh/macssh.git
457 lines
26 KiB
C++
Executable File
457 lines
26 KiB
C++
Executable File
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
// % Project : GUSI - Grand Unified Socket Interface
|
|
// % File : GUSIOpenTransport.nw- OpenTransport sockets
|
|
// % Author : Matthias Neeracher
|
|
// % Language : C++
|
|
// %
|
|
// % $Log$
|
|
// % Revision 1.2 2001/03/28 14:04:32 chombier
|
|
// % GUSI 2.1.6b2 update
|
|
// %
|
|
// % Revision 1.21 2001/03/20 08:12:55 neeri
|
|
// % Further select repairs
|
|
// %
|
|
// % Revision 1.20 2001/03/20 02:35:21 neeri
|
|
// % Refined new select() implementation
|
|
// %
|
|
// % Revision 1.19 2001/03/09 09:25:21 neeri
|
|
// % Fixed select logic bugs, listener queue
|
|
// %
|
|
// % Revision 1.18 2001/01/17 08:58:06 neeri
|
|
// % Releasing 2.1.4
|
|
// %
|
|
// % Revision 1.17 2000/10/16 04:07:23 neeri
|
|
// % Fix accept code
|
|
// %
|
|
// % Revision 1.16 2000/06/01 06:31:10 neeri
|
|
// % Reset shutdown flags on connect, refine test for data available, fix memory leak in UDP sendto
|
|
// %
|
|
// % Revision 1.15 2000/05/23 07:13:19 neeri
|
|
// % Improve formatting, implement immediate close and sorrect linger handling
|
|
// %
|
|
// % Revision 1.14 2000/03/15 07:19:53 neeri
|
|
// % Fix numerous race conditions
|
|
// %
|
|
// % Revision 1.13 2000/03/06 06:10:01 neeri
|
|
// % Reorganize Yield()
|
|
// %
|
|
// % Revision 1.12 1999/12/14 06:28:29 neeri
|
|
// % Read pending data while closing
|
|
// %
|
|
// % Revision 1.11 1999/12/13 02:44:19 neeri
|
|
// % Fix SO_LINGER, read results for disconnected sockets
|
|
// %
|
|
// % Revision 1.10 1999/10/15 02:48:50 neeri
|
|
// % Make disconnects orderly
|
|
// %
|
|
// % Revision 1.9 1999/09/09 07:20:29 neeri
|
|
// % Fix numerous bugs, add support for interface ioctls
|
|
// %
|
|
// % Revision 1.8 1999/09/03 06:31:36 neeri
|
|
// % Needed more mopups
|
|
// %
|
|
// % Revision 1.7 1999/08/26 05:43:09 neeri
|
|
// % Supply missing Unbind
|
|
// %
|
|
// % Revision 1.6 1999/08/02 07:02:45 neeri
|
|
// % Support for asynchronous errors and other socket options
|
|
// %
|
|
// % Revision 1.5 1999/07/19 06:17:44 neeri
|
|
// % Fix nonblocking connect
|
|
// %
|
|
// % Revision 1.4 1999/06/28 06:04:59 neeri
|
|
// % Support interrupted calls
|
|
// %
|
|
// % Revision 1.3 1999/05/30 03:06:41 neeri
|
|
// % MPW compiler compatibility, fix select for datagram sockets
|
|
// %
|
|
// % Revision 1.2 1999/04/29 05:33:19 neeri
|
|
// % Fix fcntl prototype
|
|
// %
|
|
// % Revision 1.1 1999/03/17 09:05:11 neeri
|
|
// % Added GUSITimer, expanded docs
|
|
// %
|
|
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
//
|
|
// \chapter{Open Transport socket infrastructure}
|
|
//
|
|
// A [[GUSIOTSocket]] defines a class of Open Transport sockets. Since most
|
|
// families differ only in a few details, like address representation, we
|
|
// abstract the typical differences in a strategy class [[GUSIOTStrategy]].
|
|
//
|
|
// <GUSIOpenTransport.h>=
|
|
#ifndef _GUSIOpenTransport_
|
|
#define _GUSIOpenTransport_
|
|
|
|
#ifdef GUSI_INTERNAL
|
|
|
|
#include "GUSISocket.h"
|
|
#include "GUSISocketMixins.h"
|
|
#include "GUSIFactory.h"
|
|
|
|
#include <netinet/in.h>
|
|
#include <netinet/tcp.h>
|
|
|
|
#undef O_ASYNC
|
|
#undef O_NDELAY
|
|
#undef O_NONBLOCK
|
|
#undef SIGHUP
|
|
#undef SIGURG
|
|
#undef AF_INET
|
|
#undef TCP_KEEPALIVE
|
|
#undef TCP_NODELAY
|
|
#undef TCP_MAXSEG
|
|
|
|
#if UNIVERSAL_INTERFACES_VERSION >= 0x0340
|
|
#undef IP_OPTIONS
|
|
#undef IP_TOS
|
|
#undef IP_TTL
|
|
#undef IP_REUSEADDR
|
|
#undef IP_DONTROUTE
|
|
#undef IP_BROADCAST
|
|
#undef IP_HDRINCL
|
|
#undef IP_RCVOPTS
|
|
#undef IP_RCVDSTADDR
|
|
#undef IP_MULTICAST_IF
|
|
#undef IP_MULTICAST_TTL
|
|
#undef IP_MULTICAST_LOOP
|
|
#undef IP_ADD_MEMBERSHIP
|
|
#undef IP_DROP_MEMBERSHIP
|
|
#undef IP_RCVIFADDR
|
|
|
|
#undef SIGURG
|
|
#endif
|
|
|
|
#include <OpenTransport.h>
|
|
#include <OpenTptInternet.h>
|
|
|
|
// \section{Definition of [[GUSIOTStrategy]]}
|
|
//
|
|
// A [[GUSIOTStrategy]] contains all the tricky parts of each Open Transport
|
|
// family.
|
|
//
|
|
// <Definition of class [[GUSIOTStrategy]]>=
|
|
class GUSIOTStrategy {
|
|
public:
|
|
// [[CreateConfiguration]] creates an appropriate [[OTConfiguration]]. This
|
|
// method is not virtual, as it relies on the strategy method
|
|
// [[ConfigPath]].
|
|
//
|
|
// <Strategic interfaces for [[GUSIOTStrategy]]>=
|
|
OTConfiguration * CreateConfiguration();
|
|
// [[PackAddress]] converts a socket address into an OT address, and
|
|
// [[UnpackAddress]] performs the reverse step. [[CopyAddress]] copies an address.
|
|
//
|
|
// <Strategic interfaces for [[GUSIOTStrategy]]>=
|
|
virtual int PackAddress(
|
|
const void * address, socklen_t len, TNetbuf & addr, bool non_null = false) = 0;
|
|
virtual int UnpackAddress(const TNetbuf & addr, void * address, socklen_t * len) = 0;
|
|
virtual int CopyAddress(const TNetbuf & from, TNetbuf & to);
|
|
// [[EndpointInfo]] returns a data structure storing endpoint parameters. We only
|
|
// need one copy per socket type.
|
|
//
|
|
// <Strategic interfaces for [[GUSIOTStrategy]]>=
|
|
TEndpointInfo * EndpointInfo() { return &fEndpointInfo; }
|
|
protected:
|
|
// <Privatissima of [[GUSIOTStrategy]]>=
|
|
virtual const char * ConfigPath() = 0;
|
|
// <Privatissima of [[GUSIOTStrategy]]>=
|
|
TEndpointInfo fEndpointInfo;
|
|
// \section{Implementation of [[GUSIOTStrategy]]}
|
|
//
|
|
// [[GUSIOTStrategy]] is mostly abstract, except for the [[CreateConfiguration]]
|
|
// and [[CopyAddress]] methods.
|
|
//
|
|
// <Privatissima of [[GUSIOTStrategy]]>=
|
|
OTConfiguration * fConfig;
|
|
GUSIOTStrategy() : fConfig(nil) {}
|
|
virtual ~GUSIOTStrategy();
|
|
};
|
|
// \section{Definition of [[GUSIOTFactory]] and descendants}
|
|
//
|
|
// A [[GUSIOTFactory]] is an abstract class combining a socket creation
|
|
// mechanism with a strategy instance. To clarify our intent, we isolate
|
|
// the latter in [[GUSIOTStrategy]].
|
|
//
|
|
// <Definition of class [[GUSIOTFactory]]>=
|
|
class GUSIOTFactory : public GUSISocketFactory {
|
|
public:
|
|
static bool Initialize();
|
|
protected:
|
|
virtual GUSIOTStrategy *Strategy(int domain, int type, int protocol) = 0;
|
|
private:
|
|
// \section{Implementation of [[GUSIOTFactory]] and descendants}
|
|
//
|
|
//
|
|
// <Privatissima of [[GUSIOTFactory]]>=
|
|
static bool sOK;
|
|
};
|
|
// <Definition of class [[GUSIOTStreamFactory]]>=
|
|
class GUSIOTStreamFactory : public GUSIOTFactory {
|
|
public:
|
|
GUSISocket * socket(int domain, int type, int protocol);
|
|
};
|
|
// <Definition of class [[GUSIOTDatagramFactory]]>=
|
|
class GUSIOTDatagramFactory : public GUSIOTFactory {
|
|
public:
|
|
GUSISocket * socket(int domain, int type, int protocol);
|
|
};
|
|
// \section{Definition of [[GUSIOT]]}
|
|
//
|
|
// Open Transport allocates and deallocates many data structures, which we
|
|
// simplify with a smart template. Allocation works via class allocation
|
|
// operators, which is a bit weird admittedly.
|
|
//
|
|
// <Definition of template [[GUSIOT]]>=
|
|
template <class T, int tag> class GUSIOT : public T {
|
|
public:
|
|
void * operator new(size_t, EndpointRef ref)
|
|
{ OSStatus err; return OTAlloc(ref, tag, T_ALL, &err); }
|
|
void * operator new(size_t, EndpointRef ref, int fields)
|
|
{ OSStatus err; return OTAlloc(ref, tag, fields, &err); }
|
|
void operator delete(void * o)
|
|
{ if (o) OTFree(o, tag); }
|
|
};
|
|
template <class T, int tag> class GUSIOTAddr : public GUSIOT<T, tag> {
|
|
public:
|
|
int Pack(GUSIOTStrategy * strategy, const void * address, socklen_t len, bool non_null=false)
|
|
{ return strategy->PackAddress(address, len, addr, non_null); }
|
|
int Unpack(GUSIOTStrategy * strategy, void * address, socklen_t * len)
|
|
{ return strategy->UnpackAddress(addr, address, len); }
|
|
int Copy(GUSIOTStrategy * strategy, GUSIOTAddr<T, tag> * to)
|
|
{ return strategy->CopyAddress(addr, to->addr); }
|
|
};
|
|
|
|
typedef GUSIOTAddr<TBind, T_BIND> GUSIOTTBind;
|
|
typedef GUSIOTAddr<TCall, T_CALL> GUSIOTTCall;
|
|
typedef GUSIOTAddr<TUnitData, T_UNITDATA> GUSIOTTUnitData;
|
|
typedef GUSIOTAddr<TUDErr, T_UDERROR> GUSIOTTUDErr;
|
|
typedef GUSIOT<TDiscon, T_DIS> GUSIOTTDiscon;
|
|
typedef GUSIOT<TOptMgmt, T_OPTMGMT> GUSIOTTOptMgmt;
|
|
// \section{Definition of [[GUSIOTSocket]] and descendants}
|
|
//
|
|
// Open Transport sockets are rather lightweight, since OT is rather similar
|
|
// to sockets already.
|
|
//
|
|
// <Definition of class [[GUSIOTSocket]]>=
|
|
class GUSIOTSocket :
|
|
public GUSISocket,
|
|
protected GUSISMBlocking,
|
|
protected GUSISMState,
|
|
protected GUSISMAsyncError
|
|
{
|
|
public:
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int bind(void * name, socklen_t namelen);
|
|
// [[getsockname]] and [[getpeername]] unpack the stored addresses.
|
|
// Note that the reaction to [[nil]] addresses is a bit different.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int getsockname(void * name, socklen_t * namelen);
|
|
// [[shutdown]] just delegates to [[GUSISMState]].
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int shutdown(int how);
|
|
// [[fcntl]] handles the blocking support.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int fcntl(int cmd, va_list arg);
|
|
// [[ioctl]] deals with blocking support and with [[FIONREAD]].
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int ioctl(unsigned int request, va_list arg);
|
|
// Before we call [[select]], we check whether there is data currently available and turn
|
|
// off the data flags otherwise.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual bool pre_select(bool wantRead, bool wantWrite, bool wantExcept);
|
|
// [[getsockopt]] and [[setsockopt]] are available for a variety of options.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int getsockopt(int level, int optname, void *optval, socklen_t * optlen);
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual int setsockopt(int level, int optname, void *optval, socklen_t optlen);
|
|
// Open Transport sockets implement socket style calls.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTSocket]]>=
|
|
virtual bool Supports(ConfigOption config);
|
|
protected:
|
|
GUSIOTSocket(GUSIOTStrategy * strategy);
|
|
|
|
// \section{Implementation of [[GUSIOTSocket]]}
|
|
//
|
|
// Open Transport may call this routine for dozens and dozens of different
|
|
// reasons. Pretty much every call results in a wakeup of the threads attached
|
|
// to the socket. We save some of the more interesting events in bitsets.
|
|
// in [[MopupEvents]].
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
uint16_t fNewEvent;
|
|
uint16_t fCurEvent;
|
|
uint16_t fEvent;
|
|
uint32_t fNewCompletion;
|
|
uint32_t fCurCompletion;
|
|
uint32_t fCompletion;
|
|
friend pascal void GUSIOTNotify(GUSIOTSocket *, OTEventCode, OTResult, void *);
|
|
// To avoid race conditions with the notifier, we sometimes need a lock.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
class Lock {
|
|
public:
|
|
Lock(EndpointRef end) : fEndpoint(end) { OTEnterNotifier(fEndpoint); }
|
|
~Lock() { OTLeaveNotifier(fEndpoint); }
|
|
private:
|
|
EndpointRef fEndpoint;
|
|
};
|
|
// For some events, we have to take a followup action at a more convenient time.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
virtual void MopupEvents();
|
|
// [[GUSIOTSocket]] creates an asynchronous endpoint for the appropriate
|
|
// provider.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
GUSIOTStrategy * fStrategy;
|
|
EndpointRef fEndpoint;
|
|
linger fLinger;
|
|
UInt32 fDeadline;
|
|
// The destructor tears down the connection as gracefully as possible. It also respects
|
|
// the linger settings.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
virtual void close();
|
|
virtual ~GUSIOTSocket();
|
|
// [[Clone]] creates another socket of the same class.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
virtual GUSIOTSocket * Clone() = 0;
|
|
// At the time the socket function [[bind]] is called, we are not really ready
|
|
// yet to call [[OTBind]], but if we don't call it, we can't report whether the
|
|
// address was free.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
GUSIOTTBind * fSockName;
|
|
int BindToAddress(GUSIOTTBind * addr);
|
|
// Open Transport takes unbinding a lot more serious than MacTCP.
|
|
//
|
|
// <Privatissima of [[GUSIOTSocket]]>=
|
|
void Unbind();
|
|
|
|
friend class GUSIOTStreamSocket;
|
|
friend class GUSIOTDatagramSocket;
|
|
};
|
|
// <Definition of class [[GUSIOTStreamSocket]]>=
|
|
class GUSIOTStreamSocket : public GUSIOTSocket {
|
|
public:
|
|
// [[Clone]] creates another socket of the same class.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual GUSIOTSocket * Clone();
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual void close();
|
|
virtual bool Close(UInt32 now);
|
|
~GUSIOTStreamSocket();
|
|
// Stream sockets include a mopup action for connect and disconnect.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual void MopupEvents();
|
|
// [[listen]] is a bit embarassing, because we already committed ourselves
|
|
// to a queue length of [[0]], so we have to unbind and rebind ourselves.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual int listen(int qlen);
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual int getpeername(void * name, socklen_t * namelen);
|
|
// [[accept]] may become quite complex, because connections could nest. The
|
|
// listening socket calls [[OTListen]], queues candidates by their
|
|
// [[fNextListener]] field, and then trys calling [[OTAccept]] on the first
|
|
// candidate.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual GUSISocket * accept(void * address, socklen_t * addrlen);
|
|
// [[connect]] is comparatively simple.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual int connect(void * address, socklen_t addrlen);
|
|
// Data transfer is simple as well. Here is the version for stream protocols.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual ssize_t recvfrom(const GUSIScatterer & buffer, int flags, void * from, socklen_t * fromlen);
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual ssize_t sendto(const GUSIGatherer & buffer, int flags, const void * to, socklen_t tolen);
|
|
// [[select]] for stream sockets intermingles data information and connection
|
|
// information as usual.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual bool select(bool * canRead, bool * canWrite, bool * except);
|
|
// [[shutdown]] for stream sockets has to send an orderly disconnect.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTStreamSocket]]>=
|
|
virtual int shutdown(int how);
|
|
protected:
|
|
GUSIOTStreamSocket(GUSIOTStrategy * strategy);
|
|
|
|
// Since all we need to know is in the [[GUSIOTStrategy]], it often suffices
|
|
// simply to create a [[GUSIOTSocket]]. Stream and datagram sockets differ
|
|
// merely in the descendant they create.
|
|
//
|
|
// <Privatissima of [[GUSIOTStreamSocket]]>=
|
|
friend class GUSIOTStreamFactory;
|
|
// <Privatissima of [[GUSIOTStreamSocket]]>=
|
|
friend pascal void GUSIOTNotify(GUSIOTSocket *, OTEventCode, OTResult, void *);
|
|
// The peer address for a [[GUSIOTStreamSocket]] is stored in a [[GUSIOTTCall]]
|
|
// structure.
|
|
//
|
|
// <Privatissima of [[GUSIOTStreamSocket]]>=
|
|
GUSIOTTCall * fPeerName;
|
|
// <Privatissima of [[GUSIOTStreamSocket]]>=
|
|
GUSIOTStreamSocket * fNextListener;
|
|
};
|
|
// <Definition of class [[GUSIOTDatagramSocket]]>=
|
|
class GUSIOTDatagramSocket : public GUSIOTSocket {
|
|
public:
|
|
// [[Clone]] creates another socket of the same class.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual GUSIOTSocket * Clone();
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
~GUSIOTDatagramSocket();
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual int getpeername(void * name, socklen_t * namelen);
|
|
// A datagram socket can [[connect]] as many times as it wants.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual int connect(void * address, socklen_t addrlen);
|
|
// Datagram protocols use slightly different calls for data transfers.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual ssize_t recvfrom(const GUSIScatterer & buffer, int flags, void * from, socklen_t * fromlen);
|
|
// [[sendto]] needs either a valid [[to]] address or a stored peer address set by
|
|
// [[connect]].
|
|
//
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual ssize_t sendto(const GUSIGatherer & buffer, int flags, const void * to, socklen_t tolen);
|
|
// [[select]] for datagram sockets returns data information only.
|
|
//
|
|
// <Overridden member functions for [[GUSIOTDatagramSocket]]>=
|
|
virtual bool select(bool * canRead, bool * canWrite, bool * except);
|
|
protected:
|
|
GUSIOTDatagramSocket(GUSIOTStrategy * strategy);
|
|
|
|
// <Privatissima of [[GUSIOTDatagramSocket]]>=
|
|
friend class GUSIOTDatagramFactory;
|
|
// Datagram sockets might be bound at rather arbitrary times.
|
|
//
|
|
// <Privatissima of [[GUSIOTDatagramSocket]]>=
|
|
int BindIfUnbound();
|
|
// The peer address for a [[GUSIOTDatagramSocket]] is stored in a [[GUSIOTTBind]]
|
|
// structure.
|
|
//
|
|
// <Privatissima of [[GUSIOTDatagramSocket]]>=
|
|
GUSIOTTBind * fPeerName;
|
|
};
|
|
|
|
#endif /* GUSI_INTERNAL */
|
|
|
|
#endif /* _GUSIOpenTransport_ */
|