ip++.h   [plain text]


/*
 * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved.
 * 
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */


//
// ip++ - C++ layer for IP socket and address management
//
// Key to comments:
//	HBO = host byte order, NBO = network byte order
//
// Rules for byte ordering: C++ objects store addresses and ports in NBO.
// Struct in_addr arguments are in NBO. Integer type arguments are in HBO.
// Stick with the conversion methods and you win. Cast around and you lose.
//
// @@@ Which namespace should we be in?
//
#ifndef _H_IPPLUSPLUS
#define _H_IPPLUSPLUS

#include "unix++.h"
#include "timeflow.h"
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <cstdio>
#include <cstdarg>
#include <map>

using namespace UnixPlusPlus;


namespace Security {
namespace IPPlusPlus {

class Host;


//
// For now, ports are simply a short unsigned integer type, in HBO.
//
typedef UInt16 IPPort;


//
// An IP host address.
//
class IPAddress : public in_addr {
public:
    IPAddress()						{ s_addr = htonl(INADDR_ANY); }
    IPAddress(const struct in_addr &addr) { s_addr = addr.s_addr; }
    explicit IPAddress(UInt32 addr)	{ s_addr = htonl(addr); }
    IPAddress(const char *s);		// ONLY dotted-quad form - use hosts.h for name resolution
    
    operator UInt32 () const		{ return ntohl(s_addr); }
    operator string () const;		// "n.n.n.n" (no name resolution)
    
public:
    bool operator == (const IPAddress &other) const	{ return s_addr == other.s_addr; }
    bool operator != (const IPAddress &other) const	{ return s_addr != other.s_addr; }
    bool operator < (const IPAddress &other) const	{ return s_addr < other.s_addr; }
    
    operator bool () const			{ return s_addr != htonl(INADDR_ANY); }
    bool operator ! () const		{ return s_addr == htonl(INADDR_ANY); }
    
public:
    static const IPAddress &any;
};


//
// An IP "socket address", i.e. a combined host address and port.
//
class IPSockAddress : public sockaddr_in {
public:
    IPSockAddress();
    IPSockAddress(const struct sockaddr_in &sockaddr)	{ *(sockaddr_in *)this = sockaddr; }
    IPSockAddress(const IPAddress &addr, IPPort port);
    
    IPAddress address() const		{ return sin_addr; }
    void address(IPAddress addr)	{ sin_addr = addr; }
    IPPort port() const				{ return ntohs(sin_port); }
    void port(IPPort p)				{ sin_port = htons(p); }
    
    operator string () const;		// "n.n.n.n:p" (no name resolution)

    // automatically convert to struct sockaddr * for use in system calls
    operator struct sockaddr * ()
    { return reinterpret_cast<struct sockaddr *>(this); }
    operator const struct sockaddr * () const
    { return reinterpret_cast<const struct sockaddr *>(this); }
    
    // conveniences
    IPSockAddress defaults(const IPSockAddress &defaultAddr) const;
    IPSockAddress defaults(const IPAddress &defaultAddr, IPPort defaultPort = 0) const;
    IPSockAddress defaults(IPPort defaultPort) const;
};


//
// UNIX Domain Socket addresses, for those who care.
// An "UNAddress", such as it were, is simply a string.
//
class UNSockAddress : public sockaddr_un {
public:
	UNSockAddress();
	UNSockAddress(const char *path);
	UNSockAddress(const std::string &path);
	
	string path() const;
	operator string () const		{ return path(); }

    // automatically convert to struct sockaddr * for use in system calls
    operator struct sockaddr * ()
    { return reinterpret_cast<struct sockaddr *>(this); }
    operator const struct sockaddr * () const
    { return reinterpret_cast<const struct sockaddr *>(this); }
};


//
// An IP socket.
// This inherits all functionality of a FileDesc, so I/O is fun and easy.
// Socket is "passive"; it doesn't own any resources and does nothing on destruction.
// On the upside, you can assign Sockets freely.
// If you want self-managing sockets that clean up after themselves,
// use the subclasses below.
//
class Socket : public FileDesc {
public:
    Socket() { }
	explicit Socket(int domain, int type, int protocol = 0);
    explicit Socket(int type);
    
    Socket &operator = (int fd)				{ setFd(fd); return *this; }
    
    // basic open (socket system call)
	void open(int domain, int type, int protocol = 0);
	void open(int type)						{ open(AF_INET, type, 0); }
    
    // standard socket operations
    void bind(const IPSockAddress &addr);	// to this socket address
    void bind(const IPAddress &addr = IPAddress::any, IPPort port = 0);
	void bind(const UNSockAddress &addr);	// to this UNIX domain socket
    void listen(int backlog = 1);
    void accept(Socket &s);
    void accept(Socket &s, IPSockAddress &peer);
    void accept(Socket &s, UNSockAddress &peer);
	bool connect(const struct sockaddr *peer);
    bool connect(const IPSockAddress &peer);
    bool connect(const IPAddress &addr, IPPort port);
	bool connect(const UNSockAddress &peer);
    void connect(const Host &host, IPPort port);	// any address of this host
    void shutdown(int type);
    enum { shutdownRead = 0, shutdownWrite = 1, shutdownBoth = 2 };
    
    // get endpoint addresses
    IPSockAddress localAddress() const;
    IPSockAddress peerAddress() const;
    
    // socket options
    void setOption(const void *value, int length, int name, int level = SOL_SOCKET) const;
    void getOption(void *value, socklen_t &length, int name, int level = SOL_SOCKET) const;
    
    template <class T> void setOption(const T &value, int name, int level = SOL_SOCKET) const
    { setOption(&value, sizeof(value), name, level); }
    
    template <class T> T getOption(int name, int level = SOL_SOCKET) const
    {
        T value; socklen_t length = sizeof(value);
        getOption(&value, length, name, level);
        assert(length == sizeof(value));
        return value;
    }
    
    // some specific useful options
    int type() const		{ return getOption<int>(SO_TYPE); }
    int error() const		{ return getOption<int>(SO_ERROR); }
    
public:
#if defined(SOMAXCONN)
    static const int listenMaxQueue = SOMAXCONN;
#else
    static const int listenMaxQueue = 5;	// the traditional BSD UNIX value
#endif

protected:
    void prepare(int fdFlags, int domain, int type, int protocol = 0);
};


//
// A TCPClientSocket is a self-connecting TCP socket that connects (actively) to a server.
// Since TCP, once established, is symmetric, it can also be used for the server side
// of a TCP pipe. You can think of it as the least complex embodiment of a TCP connection.
//
class TCPClientSocket : public Socket {
    NOCOPY(TCPClientSocket)
public:
    TCPClientSocket() { }
    ~TCPClientSocket();	// closes connection
    
#if BUG_GCC
    void open(int type, int protocol = 0)	{ Socket::open(type, protocol); }
#else
    using Socket::open;
#endif
    
    void open(const IPSockAddress &peer, int fdFlags = 0);
    void open(const IPAddress &addr, IPPort port, int fdFlags = 0);
    void open(const Host &host, IPPort port, int fdFlags = 0);

    TCPClientSocket(const IPSockAddress &peer, int fdFlags = 0)
    { open(peer, fdFlags); }
    TCPClientSocket(const IPAddress &addr, IPPort port, int fdFlags = 0)
    { open(addr, port, fdFlags); }
    TCPClientSocket(const Host &host, IPPort port, int fdFlags = 0)
    { open(host, port, fdFlags); }
    
protected:	// for serverSocket/clientSocket footsy play
    void setFd(int fd)			{ Socket::setFd(fd); }
    
private:
    TCPClientSocket(int sockfd);
};


//
// A TCPServerSocket is a self-initializing listener socket for incoming TCP requests
// (usually to a server). Its function operator yields the next incoming connection request
// as a TCPClientSocket (see above). For one-shot receivers, the receive() method will
// create the client and close the listener atomically (which is sometimes faster).
//
class TCPServerSocket : public Socket {
    NOCOPY(TCPServerSocket)
public:
    TCPServerSocket() { }
    ~TCPServerSocket();	// closes listener; existing connections unaffected
    
    void open(const IPSockAddress &local, int depth = 1);
    void open(IPPort port = 0, int depth = 1)
    { open(IPSockAddress(IPAddress::any, port), depth); }

    TCPServerSocket(const IPSockAddress &local, int depth = 1)	{ open(local, depth); }
    TCPServerSocket(IPPort port, int depth = 1)					{ open(port, depth); }
    
    void operator () (TCPClientSocket &newClient);	// retrieve next connection
    void receive(TCPClientSocket &client);			// accept once, then close listener
};


}	// end namespace IPPlusPlus
}	// end namespace Security


#endif //_H_IPPLUSPLUS