--- /dev/null
+/*
+ * 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