macssh/GUSI/include/GUSISocket.h

363 lines
20 KiB
C++
Executable File

// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// % Project : GUSI - Grand Unified Socket Interface
// % File : GUSISocket.nw - The socket class
// % Author : Matthias Neeracher
// % Language : C++
// %
// % $Log$
// % Revision 1.1.1.1 2001/03/03 21:50:14 chombier
// % Initial import
// %
// % Revision 1.18 2000/10/16 04:34:23 neeri
// % Releasing 2.1.2
// %
// % Revision 1.17 2000/05/23 07:19:34 neeri
// % Improve formatting, add close queue
// %
// % Revision 1.16 2000/03/15 07:20:53 neeri
// % Add GUSISocket::AddContextInScope
// %
// % Revision 1.15 1999/10/15 02:48:51 neeri
// % Make disconnects orderly
// %
// % Revision 1.14 1999/09/26 03:59:26 neeri
// % Releasing 2.0fc1
// %
// % Revision 1.13 1999/08/26 05:45:09 neeri
// % Fixes for literate edition of source code
// %
// % Revision 1.12 1999/06/08 04:31:31 neeri
// % Getting ready for 2.0b2
// %
// % Revision 1.11 1999/05/29 06:26:45 neeri
// % Fixed header guards
// %
// % Revision 1.10 1999/04/29 05:33:18 neeri
// % Fix fcntl prototype
// %
// % Revision 1.9 1999/03/17 09:05:13 neeri
// % Added GUSITimer, expanded docs
// %
// % Revision 1.8 1998/11/22 23:07:01 neeri
// % Releasing 2.0a4 in a hurry
// %
// % Revision 1.7 1998/10/11 16:45:23 neeri
// % Ready to release 2.0a2
// %
// % Revision 1.6 1998/08/01 21:29:53 neeri
// % Use context queues
// %
// % Revision 1.5 1998/01/25 20:53:58 neeri
// % Engine implemented, except for signals & scheduling
// %
// % Revision 1.4 1997/11/13 21:12:12 neeri
// % Fall 1997
// %
// % Revision 1.3 1996/11/24 13:00:28 neeri
// % Fix comment leaders
// %
// % Revision 1.2 1996/11/24 12:52:09 neeri
// % Added GUSIPipeSockets
// %
// % Revision 1.1.1.1 1996/11/03 02:43:32 neeri
// % Imported into CVS
// %
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//
// \chapter{The GUSI Socket Class}
//
// GUSI is constructed around the [[GUSISocket]] class. This class is
// mostly an abstract superclass, but all virtual procedures are implemented
// to return sensible error codes.
//
// <GUSISocket.h>=
#ifndef _GUSISocket_
#define _GUSISocket_
#ifdef GUSI_SOURCE
#include "GUSIBasics.h"
#include "GUSIContext.h"
#include "GUSIContextQueue.h"
#include "GUSIBuffer.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <stdarg.h>
#include <ConditionalMacros.h>
#include <LowMem.h>
#if PRAGMA_STRUCT_ALIGN
#pragma options align=native
#endif
// \section{Definition of [[GUSISocket]]}
//
// [[GUSISocket]] consists of a few maintenance functions and the socket operations.
// Each operation consists to a POSIX/BSD function with the file descriptor operand
// left out.
//
// <Definition of class [[GUSISocket]]>=
class GUSISocket {
// Since a single [[GUSISocket]] may (through [[dup]]) be installed multiply
// in a descriptor table or even in multiple descriptor tables, [[GUSISocket]]s
// are not destroyed directly, but by manipulating a reference count. As soon
// as the reference count hits zero, the destructor (which, of course, should
// probably be overridden) is called.
//
// Since destructors cannot call virtual functions, we call [[close]] which
// eventually calls the destructor. Some socket types can take quite long to close
// under unfavorable circumstances. To speed up the process, we have the option of
// queueing the socket up and regularly having [[Close]] called on it.
//
// <Reference counting for [[GUSISocket]]>=
public:
void AddReference();
void RemoveReference();
virtual void close();
void CheckClose(UInt32 now = LMGetTicks());
protected:
GUSISocket();
virtual ~GUSISocket();
virtual bool Close(UInt32 now = LMGetTicks());
private:
u_long fRefCount;
// [[GUSIContext]]s are defined in {\tt GUSIBasics}. A context references all
// information you need in a completion procedure: The contents of [[A5]],
// the process ID, and thread information. [[Wakeup]] wakes up the threads
// and/or processes associated with the socket and is guaranteed to work even
// at interrupt level. [[AddContext]] adds another context. [[RemoveContext]]
// indicates that this context no longer should be woken up when something happens.
// To keep a context added inside a scope, declare an automatic object of class
// [[AddContextInScope]].
//
// <Context links for [[GUSISocket]]>=
public:
void Wakeup();
void AddContext(GUSIContext * context = nil);
void RemoveContext(GUSIContext * context = nil);
class AddContextInScope {
public:
AddContextInScope(GUSISocket * sock, GUSIContext * context = nil)
: fSocket(sock), fContext(context)
{ fSocket->AddContext(fContext); }
~AddContextInScope() { fSocket->RemoveContext(fContext); }
private:
GUSISocket * fSocket;
GUSIContext * fContext;
};
private:
GUSIContextQueue fContexts;
// There may be various reasons to keep sockets in queue. Currently the
// only reason is to queue up dying sockets.
//
// <Queue management for [[GUSISocket]]>=
public:
void Enqueue(GUSISocket ** queue);
void Dequeue();
private:
GUSISocket ** fQueue;
GUSISocket * fNextSocket;
GUSISocket * fPrevSocket;
// Both read and write calls on sockets come in five different variants:
//
// \begin{enumerate}
// \item [[read]] and [[write]]
// \item [[recv]] and [[send]]
// \item [[readv]] and [[writev]]
// \item [[recvfrom]] and [[sendto]]
// \item [[recvmsg]] and [[sendmsg]]
// \end{enumerate}
//
// GUSI initially maps variants 3 and 5 of these calls to the [[recvmsg]] and
// [[sendmsg]] member functions, variants 2 and 4 to the [[recvfrom]] and
// [[sendto]] member functions, and variant 1 to the [[read]] and
// [[write]] member functions.
//
// The simpler member functions can always be translated into the complex member
// functions, and under some circumstances, the opposite is also possible.
// To avoid translation loops, the translation routines (i.e., the default
// implementation of [[GUSISocket::read]] and [[GUSISocket::recvmsg]]
// check for the availablility of the other function by calling [[Supports]].
// This member function must be overridden for any reasonable socket class.
//
// <Configuration options for [[GUSISocket]]>=
protected:
enum ConfigOption {
kSimpleCalls, // [[read]], [[write]]
kSocketCalls, // [[recvfrom]], [[sendto]]
kMsgCalls // [[recvmsg]], [[sendmsg]]
};
virtual bool Supports(ConfigOption config);
public:
// Most sockets have names, which to [[GUSISocket]] are just opaque blocks of
// memory. A name for a socket is established (before the socket is actually
// used, of course) through [[bind]]. The name may be queried with
// [[getsockname]] and once the socket is connected, the name of the peer
// endpoint may be queried with [[getpeername]].
//
// <Socket name management for [[GUSISocket]]>=
virtual int bind(void * name, socklen_t namelen);
virtual int getsockname(void * name, socklen_t * namelen);
virtual int getpeername(void * name, socklen_t * namelen);
// Sockets follow either a virtual circuit model where all data is exchanged
// with the same peer throughout the lifetime of the connection, or a datagram
// model where potentially every message is exchanged with a different peer.
//
// The vast majority of protocols follow the virtual circuit model. The server
// end, typically after calling [[bind]] to attach the socket to a well known
// address, calls [[listen]] to establish its willingness to accept connections.
// [[listen]] takes a queue length parameter, which however is ignored for many
// types of sockets.
//
// Incoming connections are then accepted by calling [[accept]]. When [[accept]]
// is successful, it always returns a new [[GUSISocket]], while the original socket
// remains available for further connections. To avoid blocking on [[accept]], you may poll for connections with an
// [[accept()] call in nonblocking mode or query the result of [[select]] whether
// the socket is ready for reading.
//
// The client end in the virtual circuit model connects itself to the well known
// address by calling [[connect]]. To avoid blocking on [[connect]], you may
// call it in nonblocking mode and then query the result of [[select]] whether
// the socket is ready for writing.
//
// In the datagram model, you don't need to establish connections. You may call
// [[connect]] anyway to temporarily establish a virtual circuit.
//
// <Connection establishment for [[GUSISocket]]>=
virtual int listen(int qlen);
virtual GUSISocket * accept(void * address, socklen_t * addrlen);
virtual int connect(void * address, socklen_t addrlen);
// As mentioned before, there are three variants each for reading and writing.
// The socket variants provide a means to pass a peer address for the datagram
// model, while the msg variants also provides fields for passing access rights,
// which is, however not currently supported in GUSI. As syntactic sugar, the more
// traditional flavors with [[buffer]]/[[length]] buffers are also supported.
//
// <Sending and receiving data for [[GUSISocket]]>=
virtual ssize_t read(const GUSIScatterer & buffer);
virtual ssize_t write(const GUSIGatherer & buffer);
virtual ssize_t recvfrom(
const GUSIScatterer & buffer, int flags, void * from, socklen_t * fromlen);
virtual ssize_t sendto(
const GUSIGatherer & buffer, int flags, const void * to, socklen_t tolen);
virtual ssize_t recvmsg(msghdr * msg, int flags);
virtual ssize_t sendmsg(const msghdr * msg, int flags);
ssize_t read(void * buffer, size_t length);
ssize_t write(const void * buffer, size_t length);
ssize_t recvfrom(
void * buffer, size_t length, int flags, void * from, socklen_t * fromlen);
ssize_t sendto(
const void * buffer, size_t length, int flags, const void * to, socklen_t tolen);
// A multitude of parameters can be manipulated for a [[GUSISocket]] through
// the socket oriented calls [[getsockopt]], [[setsockopt]], the file oriented
// call [[fcntl]], and the device oriented call [[ioctl]].
//
// [[isatty]] returns whether the socket should be considered an interactive
// console.
//
// <Maintaining properties for [[GUSISocket]]>=
virtual int getsockopt(int level, int optname, void *optval, socklen_t * optlen);
virtual int setsockopt(int level, int optname, void *optval, socklen_t optlen);
virtual int fcntl(int cmd, va_list arg);
virtual int ioctl(unsigned int request, va_list arg);
virtual int isatty();
// Three of the operations make sense primarily for files, and most other socket
// types accept the default implementations. [[fstat]] returns information about
// an open file, [[lseek]] repositions the read/write pointer, and [[ftruncate]]
// cuts off an open file at a certain point.
//
// <File oriented operations for [[GUSISocket]]>=
virtual int fstat(struct stat * buf);
virtual off_t lseek(off_t offset, int whence);
virtual int ftruncate(off_t offset);
// [[select]] polls or waits for one of a group of [[GUSISocket]] to become
// ready for reading, writing, or for an exceptional condition to occur.
// First, [[pre_select]] is called once for all [[GUSISocket]]s in the group.
// It returns [[true]] is the socket will wake up as soon as one of the events
// occurs and [[false]] if GUSI needs to poll.
// Next, [[select]] is called for all [[GUSISocket]]s once or multiple times,
// until a condition becomes true or the call times out. Finally, [[post_select]]
// is called for all members of the group.
//
// <Multiplexing for [[GUSISocket]]>=
virtual bool pre_select(bool wantRead, bool wantWrite, bool wantExcept);
virtual bool select(bool * canRead, bool * canWrite, bool * exception);
virtual void post_select(bool wantRead, bool wantWrite, bool wantExcept);
// A socket connection is usually full duplex. By calling [[shutdown(1)]], you
// indicate that you won't write any more data on this socket. The values 0 (no
// more reads) and 2 (no more read/write) are used less frequently.
//
// <Miscellaneous operations for [[GUSISocket]]>=
virtual int shutdown(int how);
// Some socket types do not write out data immediately. Calling [[fsync]] guarantees
// that all data is written.
//
// <Miscellaneous operations for [[GUSISocket]]>=
virtual int fsync();
};
#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif
// \section{Implementation of [[GUSISocket]]}
//
// \subsection{General socket management}
//
//
// <Inline member functions for class [[GUSISocket]]>=
inline void GUSISocket::AddReference()
{
++fRefCount;
}
inline void GUSISocket::RemoveReference()
{
if (!--fRefCount)
close();
}
// \subsection{Context management}
//
//
// <Inline member functions for class [[GUSISocket]]>=
inline void GUSISocket::Wakeup()
{
fContexts.Wakeup();
}
// The traditional flavors of the I/O calls are translated to the scatterer/gatherer
// variants.
//
// <Inline member functions for class [[GUSISocket]]>=
inline ssize_t GUSISocket::read(void * buffer, size_t length)
{
return read(GUSIScatterer(buffer, length));
}
inline ssize_t GUSISocket::write(const void * buffer, size_t length)
{
return write(GUSIGatherer(buffer, length));
}
inline ssize_t GUSISocket::recvfrom(
void * buffer, size_t length, int flags, void * from, socklen_t * fromlen)
{
return recvfrom(GUSIScatterer(buffer, length), flags, from, fromlen);
}
inline ssize_t GUSISocket::sendto(
const void * buffer, size_t length, int flags, const void * to, socklen_t tolen)
{
return sendto(GUSIGatherer(buffer, length), flags, to, tolen);
}
#endif /* GUSI_SOURCE */
#endif /* _GUSISocket_ */