]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_utilities/lib/ip++.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_utilities / lib / ip++.cpp
diff --git a/Security/libsecurity_utilities/lib/ip++.cpp b/Security/libsecurity_utilities/lib/ip++.cpp
new file mode 100644 (file)
index 0000000..b589c11
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * Copyright (c) 2000-2004,2011,2014 Apple 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
+//
+// [Also see comments in header file.]
+//
+#include "ip++.h"
+#include "hosts.h"
+#include <security_utilities/debugging.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+
+namespace Security {
+namespace IPPlusPlus {
+
+
+typedef unsigned char Byte;            // occasionally useful
+
+
+//
+// IPAddress
+//
+static const struct in_addr in_addr_any = { INADDR_ANY };
+#if BUG_GCC
+const IPAddress &IPAddress::any = *static_cast<const IPAddress *>(&in_addr_any);
+#else
+const IPAddress &IPAddress::any = static_cast<const IPAddress &>(in_addr_any);
+#endif
+
+IPAddress::IPAddress(const char *s)
+{
+    if (!inet_aton(s, this))
+        UnixError::throwMe(EINVAL);
+}
+
+IPAddress::operator string() const
+{
+    // This code is esentially equivalent to inet_ntoa, which we can't use for thread safety.
+    // Note: contents in NBO = always high-endian, thus this cast works everywhere.
+    const Byte *p = reinterpret_cast<const Byte *>(this);
+    char buffer[(3+1)*4];      // nnn.nnn.nnn.nnn\0
+    snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+    return buffer;
+}
+
+
+//
+// IPSockAddress
+//
+IPSockAddress::IPSockAddress()
+{
+    sin_family = AF_INET;
+}
+
+IPSockAddress::IPSockAddress(const IPAddress &addr, IPPort port)
+{
+    sin_family = AF_INET;
+    sin_addr = addr;
+    sin_port = htons(port);
+}
+
+IPSockAddress::operator string () const
+{
+    char buffer[4*(3+1)+5+1];  // nnn.nnn.nnn.nnn:ppppp
+    snprintf(buffer, sizeof(buffer), "%s:%d", string(address()).c_str(), port());
+    return buffer;
+}
+
+
+IPSockAddress IPSockAddress::defaults(const IPSockAddress &defaultAddr) const
+{
+    return defaults(defaultAddr.address(), defaultAddr.port());
+}
+
+IPSockAddress IPSockAddress::defaults(const IPAddress &defaultAddr, IPPort defaultPort) const
+{
+    return IPSockAddress(
+        address() ? address() : defaultAddr,
+        port() ? port() : defaultPort
+    );
+}
+
+IPSockAddress IPSockAddress::defaults(IPPort defaultPort) const
+{
+    return IPSockAddress(address(), port() ? port() : defaultPort);
+}
+
+
+//
+// UNSockAddress
+//
+UNSockAddress::UNSockAddress()
+{
+       sun_family = AF_UNIX;
+}
+
+UNSockAddress::UNSockAddress(const char *path)
+{
+       sun_family = AF_UNIX;
+       size_t length = strlen(path);
+       if (length >= sizeof(sun_path)) // won't fit into struct sockaddr_un
+               UnixError::throwMe(EINVAL);
+       memcpy(sun_path, path, length + 1);
+}
+
+UNSockAddress::UNSockAddress(const string &path)
+{
+       sun_family = AF_UNIX;
+       if (path.length() >= sizeof(sun_path))  // won't fit into struct sockaddr_un
+               UnixError::throwMe(EINVAL);
+       memcpy(sun_path, path.c_str(), path.length() + 1);
+}
+
+
+string UNSockAddress::path() const
+{
+       return sun_path;
+}
+
+
+//
+// Sockets
+//
+Socket::Socket(int type)
+{
+    open(type);
+}
+
+Socket::Socket(int domain, int type, int protocol)
+{
+       open(domain, type, protocol);
+}
+
+void Socket::open(int domain, int type, int protocol)
+{
+    checkSetFd(::socket(domain, type, protocol));
+    mAtEnd = false;
+    secdebug("sockio", "socket(%d,%d) -> %d", type, protocol, fd());
+}
+
+void Socket::prepare(int fdFlags, int domain, int type, int protocol)
+{
+    // if file descriptor is closed, open it - otherwise take what's there
+    if (!isOpen())
+        open(domain, type, protocol);
+        
+    // if flags were passed in, set them on the file descriptor now
+    if (fdFlags)
+        setFlag(fdFlags);
+}
+
+
+void Socket::bind(const IPAddress &addr, IPPort port)
+{
+    bind(IPSockAddress(addr, port));
+}
+
+void Socket::bind(const IPSockAddress &local)
+{
+    checkError(::bind(fd(), local, sizeof(local)));
+    secdebug("sockio", "%d bind to %s", fd(), string(local).c_str());
+}
+
+void Socket::bind(const UNSockAddress &local)
+{
+    checkError(::bind(fd(), local, sizeof(local)));
+    secdebug("sockio", "%d bind to %s", fd(), string(local).c_str());
+}
+
+
+void Socket::listen(int backlog)
+{
+    checkError(::listen(fd(), backlog));
+}
+
+
+void Socket::accept(Socket &s)
+{
+    IPSockAddress dummy;       // ignored
+    return accept(s, dummy);
+}
+
+void Socket::accept(Socket &s, IPSockAddress &peer)
+{
+    socklen_t length = sizeof(IPSockAddress);
+    s.checkSetFd(::accept(fd(), peer, &length));
+    assert(length == sizeof(IPSockAddress));
+}
+
+void Socket::accept(Socket &s, UNSockAddress &peer)
+{
+    socklen_t length = sizeof(UNSockAddress);
+    s.checkSetFd(::accept(fd(), peer, &length));
+    assert(length == sizeof(UNSockAddress));
+}
+
+
+bool Socket::connect(const IPSockAddress &peer)
+{
+    if (::connect(fd(), peer, sizeof(peer))) {
+        switch (errno) {
+        case EINPROGRESS:
+            secdebug("sockio", "%d connecting to %s", fd(), string(peer).c_str());
+            return false;
+        case EALREADY:
+            if (int err = error())             // connect failed
+                UnixError::throwMe(err);
+            // just keep trying
+            secdebug("sockio", "%d still trying to connect", fd());
+            return false;
+        case EISCONN:
+            if (flags() & O_NONBLOCK) {
+                secdebug("sockio", "%d now connected", fd());
+                return true;
+            } else {
+                UnixError::throwMe();
+            }
+        default:
+            UnixError::throwMe();
+        }
+    } else {
+        secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str());
+        return true;
+    }
+}
+
+bool Socket::connect(const IPAddress &addr, IPPort port)
+{
+    return connect(IPSockAddress(addr, port));
+}
+
+bool Socket::connect(const UNSockAddress &peer)
+{
+       // no nice async support here: local operation (but keep the niceties)
+       checkError(::connect(fd(), peer, sizeof(peer)));
+       secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str());
+       return true;
+}
+
+// void Socket::connect(const Host &host, ...): see below.
+
+
+void Socket::shutdown(int how)
+{
+    assert(how >= 0 && how <= 2);
+    checkError(::shutdown(fd(), how));
+}
+
+
+IPSockAddress Socket::localAddress() const
+{
+    IPSockAddress addr;
+    socklen_t length = sizeof(addr);
+    checkError(::getsockname(fd(), addr, &length));
+    assert(length == sizeof(addr));
+    return addr;
+}
+
+IPSockAddress Socket::peerAddress() const
+{
+    IPSockAddress addr;
+    socklen_t length = sizeof(addr);
+    checkError(::getpeername(fd(), addr, &length));
+    assert(length == sizeof(addr));
+    return addr;
+}
+
+void Socket::getOption(void *value, socklen_t &length, int name, int level /*= SOL_SOCKET*/) const
+{
+    UnixError::check(::getsockopt(fd(), level, name, value, &length));
+}
+
+void Socket::setOption(const void *value, int length, int name, int level /*= SOL_SOCKET*/) const
+{
+    UnixError::check(::setsockopt(fd(), level, name, value, length));
+}
+
+    
+//
+// Connect to a Host object.
+// This version of connect performs nontrivial work and makes interesting decisions.
+//
+void Socket::connect(const Host &host, IPPort port)
+{
+    //@@@ use two-step stutter algorithm?
+    //@@@ randomize order?
+    //@@@ keep worked-recently information?
+    //@@@ what about nonblocking operation?
+    set<IPAddress> addrs = host.addresses();
+    for (set<IPAddress>::const_iterator it = addrs.begin(); it != addrs.end(); it++) {
+        const IPSockAddress address(*it, port);
+        if (::connect(fd(), address, sizeof(IPSockAddress)) == 0) {
+            secdebug("sockio", "%d connect to %s", fd(), string(address).c_str());
+            return;
+        }
+    }
+    // no joy on any of the candidate addresses. Throw last error
+    //@@@ clean up errno?
+    UnixError::throwMe();
+}
+
+
+//
+// TCP*Sockets.
+// Note that these will TCP*Socket::open() will *use* its existing file descriptor,
+// on the theory that the caller may have prepared it specially (e.g. to make it nonblocking).
+//
+void TCPClientSocket::open(const IPSockAddress &peer, int fdFlags)
+{
+    prepare(fdFlags, AF_INET, SOCK_STREAM);
+    connect(peer);
+}
+
+void TCPClientSocket::open(const IPAddress &addr, IPPort port, int fdFlags)
+{
+    prepare(fdFlags, AF_INET, SOCK_STREAM);
+    connect(addr, port);
+}
+
+void TCPClientSocket::open(const Host &host, IPPort port, int fdFlags)
+{
+    prepare(fdFlags, AF_INET, SOCK_STREAM);
+    connect(host, port);
+}
+
+TCPClientSocket::~TCPClientSocket()
+{
+    close();
+}
+
+
+void TCPServerSocket::open(const IPSockAddress &addr, int depth)
+{
+    prepare(0, AF_INET, SOCK_STREAM);
+    bind(addr);
+    listen(depth);
+}
+
+void TCPServerSocket::operator () (TCPClientSocket &newClient)
+{
+    accept(newClient);
+}
+
+void TCPServerSocket::receive(TCPClientSocket &newClient)
+{
+    accept(newClient);
+    close();
+}
+
+TCPServerSocket::~TCPServerSocket()
+{
+    close();
+}
+
+
+}      // end namespace IPPlusPlus
+}      // end namespace Security