/* * 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 #include #include 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(&in_addr_any); #else const IPAddress &IPAddress::any = static_cast(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(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 addrs = host.addresses(); for (set::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