1/* 2 * Copyright (c) 2000-2004,2011,2014 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// ip++ - C++ layer for IP socket and address management 27// 28// [Also see comments in header file.] 29// 30#include "ip++.h" 31#include "hosts.h" 32#include <security_utilities/debugging.h> 33#include <arpa/inet.h> 34#include <netdb.h> 35 36 37namespace Security { 38namespace IPPlusPlus { 39 40 41typedef unsigned char Byte; // occasionally useful 42 43 44// 45// IPAddress 46// 47static const struct in_addr in_addr_any = { INADDR_ANY }; 48#if BUG_GCC 49const IPAddress &IPAddress::any = *static_cast<const IPAddress *>(&in_addr_any); 50#else 51const IPAddress &IPAddress::any = static_cast<const IPAddress &>(in_addr_any); 52#endif 53 54IPAddress::IPAddress(const char *s) 55{ 56 if (!inet_aton(s, this)) 57 UnixError::throwMe(EINVAL); 58} 59 60IPAddress::operator string() const 61{ 62 // This code is esentially equivalent to inet_ntoa, which we can't use for thread safety. 63 // Note: contents in NBO = always high-endian, thus this cast works everywhere. 64 const Byte *p = reinterpret_cast<const Byte *>(this); 65 char buffer[(3+1)*4]; // nnn.nnn.nnn.nnn\0 66 snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 67 return buffer; 68} 69 70 71// 72// IPSockAddress 73// 74IPSockAddress::IPSockAddress() 75{ 76 sin_family = AF_INET; 77} 78 79IPSockAddress::IPSockAddress(const IPAddress &addr, IPPort port) 80{ 81 sin_family = AF_INET; 82 sin_addr = addr; 83 sin_port = htons(port); 84} 85 86IPSockAddress::operator string () const 87{ 88 char buffer[4*(3+1)+5+1]; // nnn.nnn.nnn.nnn:ppppp 89 snprintf(buffer, sizeof(buffer), "%s:%d", string(address()).c_str(), port()); 90 return buffer; 91} 92 93 94IPSockAddress IPSockAddress::defaults(const IPSockAddress &defaultAddr) const 95{ 96 return defaults(defaultAddr.address(), defaultAddr.port()); 97} 98 99IPSockAddress IPSockAddress::defaults(const IPAddress &defaultAddr, IPPort defaultPort) const 100{ 101 return IPSockAddress( 102 address() ? address() : defaultAddr, 103 port() ? port() : defaultPort 104 ); 105} 106 107IPSockAddress IPSockAddress::defaults(IPPort defaultPort) const 108{ 109 return IPSockAddress(address(), port() ? port() : defaultPort); 110} 111 112 113// 114// UNSockAddress 115// 116UNSockAddress::UNSockAddress() 117{ 118 sun_family = AF_UNIX; 119} 120 121UNSockAddress::UNSockAddress(const char *path) 122{ 123 sun_family = AF_UNIX; 124 size_t length = strlen(path); 125 if (length >= sizeof(sun_path)) // won't fit into struct sockaddr_un 126 UnixError::throwMe(EINVAL); 127 memcpy(sun_path, path, length + 1); 128} 129 130UNSockAddress::UNSockAddress(const string &path) 131{ 132 sun_family = AF_UNIX; 133 if (path.length() >= sizeof(sun_path)) // won't fit into struct sockaddr_un 134 UnixError::throwMe(EINVAL); 135 memcpy(sun_path, path.c_str(), path.length() + 1); 136} 137 138 139string UNSockAddress::path() const 140{ 141 return sun_path; 142} 143 144 145// 146// Sockets 147// 148Socket::Socket(int type) 149{ 150 open(type); 151} 152 153Socket::Socket(int domain, int type, int protocol) 154{ 155 open(domain, type, protocol); 156} 157 158void Socket::open(int domain, int type, int protocol) 159{ 160 checkSetFd(::socket(domain, type, protocol)); 161 mAtEnd = false; 162 secdebug("sockio", "socket(%d,%d) -> %d", type, protocol, fd()); 163} 164 165void Socket::prepare(int fdFlags, int domain, int type, int protocol) 166{ 167 // if file descriptor is closed, open it - otherwise take what's there 168 if (!isOpen()) 169 open(domain, type, protocol); 170 171 // if flags were passed in, set them on the file descriptor now 172 if (fdFlags) 173 setFlag(fdFlags); 174} 175 176 177void Socket::bind(const IPAddress &addr, IPPort port) 178{ 179 bind(IPSockAddress(addr, port)); 180} 181 182void Socket::bind(const IPSockAddress &local) 183{ 184 checkError(::bind(fd(), local, sizeof(local))); 185 secdebug("sockio", "%d bind to %s", fd(), string(local).c_str()); 186} 187 188void Socket::bind(const UNSockAddress &local) 189{ 190 checkError(::bind(fd(), local, sizeof(local))); 191 secdebug("sockio", "%d bind to %s", fd(), string(local).c_str()); 192} 193 194 195void Socket::listen(int backlog) 196{ 197 checkError(::listen(fd(), backlog)); 198} 199 200 201void Socket::accept(Socket &s) 202{ 203 IPSockAddress dummy; // ignored 204 return accept(s, dummy); 205} 206 207void Socket::accept(Socket &s, IPSockAddress &peer) 208{ 209 socklen_t length = sizeof(IPSockAddress); 210 s.checkSetFd(::accept(fd(), peer, &length)); 211 assert(length == sizeof(IPSockAddress)); 212} 213 214void Socket::accept(Socket &s, UNSockAddress &peer) 215{ 216 socklen_t length = sizeof(UNSockAddress); 217 s.checkSetFd(::accept(fd(), peer, &length)); 218 assert(length == sizeof(UNSockAddress)); 219} 220 221 222bool Socket::connect(const IPSockAddress &peer) 223{ 224 if (::connect(fd(), peer, sizeof(peer))) { 225 switch (errno) { 226 case EINPROGRESS: 227 secdebug("sockio", "%d connecting to %s", fd(), string(peer).c_str()); 228 return false; 229 case EALREADY: 230 if (int err = error()) // connect failed 231 UnixError::throwMe(err); 232 // just keep trying 233 secdebug("sockio", "%d still trying to connect", fd()); 234 return false; 235 case EISCONN: 236 if (flags() & O_NONBLOCK) { 237 secdebug("sockio", "%d now connected", fd()); 238 return true; 239 } else { 240 UnixError::throwMe(); 241 } 242 default: 243 UnixError::throwMe(); 244 } 245 } else { 246 secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str()); 247 return true; 248 } 249} 250 251bool Socket::connect(const IPAddress &addr, IPPort port) 252{ 253 return connect(IPSockAddress(addr, port)); 254} 255 256bool Socket::connect(const UNSockAddress &peer) 257{ 258 // no nice async support here: local operation (but keep the niceties) 259 checkError(::connect(fd(), peer, sizeof(peer))); 260 secdebug("sockio", "%d connect to %s", fd(), string(peer).c_str()); 261 return true; 262} 263 264// void Socket::connect(const Host &host, ...): see below. 265 266 267void Socket::shutdown(int how) 268{ 269 assert(how >= 0 && how <= 2); 270 checkError(::shutdown(fd(), how)); 271} 272 273 274IPSockAddress Socket::localAddress() const 275{ 276 IPSockAddress addr; 277 socklen_t length = sizeof(addr); 278 checkError(::getsockname(fd(), addr, &length)); 279 assert(length == sizeof(addr)); 280 return addr; 281} 282 283IPSockAddress Socket::peerAddress() const 284{ 285 IPSockAddress addr; 286 socklen_t length = sizeof(addr); 287 checkError(::getpeername(fd(), addr, &length)); 288 assert(length == sizeof(addr)); 289 return addr; 290} 291 292void Socket::getOption(void *value, socklen_t &length, int name, int level /*= SOL_SOCKET*/) const 293{ 294 UnixError::check(::getsockopt(fd(), level, name, value, &length)); 295} 296 297void Socket::setOption(const void *value, int length, int name, int level /*= SOL_SOCKET*/) const 298{ 299 UnixError::check(::setsockopt(fd(), level, name, value, length)); 300} 301 302 303// 304// Connect to a Host object. 305// This version of connect performs nontrivial work and makes interesting decisions. 306// 307void Socket::connect(const Host &host, IPPort port) 308{ 309 //@@@ use two-step stutter algorithm? 310 //@@@ randomize order? 311 //@@@ keep worked-recently information? 312 //@@@ what about nonblocking operation? 313 set<IPAddress> addrs = host.addresses(); 314 for (set<IPAddress>::const_iterator it = addrs.begin(); it != addrs.end(); it++) { 315 const IPSockAddress address(*it, port); 316 if (::connect(fd(), address, sizeof(IPSockAddress)) == 0) { 317 secdebug("sockio", "%d connect to %s", fd(), string(address).c_str()); 318 return; 319 } 320 } 321 // no joy on any of the candidate addresses. Throw last error 322 //@@@ clean up errno? 323 UnixError::throwMe(); 324} 325 326 327// 328// TCP*Sockets. 329// Note that these will TCP*Socket::open() will *use* its existing file descriptor, 330// on the theory that the caller may have prepared it specially (e.g. to make it nonblocking). 331// 332void TCPClientSocket::open(const IPSockAddress &peer, int fdFlags) 333{ 334 prepare(fdFlags, AF_INET, SOCK_STREAM); 335 connect(peer); 336} 337 338void TCPClientSocket::open(const IPAddress &addr, IPPort port, int fdFlags) 339{ 340 prepare(fdFlags, AF_INET, SOCK_STREAM); 341 connect(addr, port); 342} 343 344void TCPClientSocket::open(const Host &host, IPPort port, int fdFlags) 345{ 346 prepare(fdFlags, AF_INET, SOCK_STREAM); 347 connect(host, port); 348} 349 350TCPClientSocket::~TCPClientSocket() 351{ 352 close(); 353} 354 355 356void TCPServerSocket::open(const IPSockAddress &addr, int depth) 357{ 358 prepare(0, AF_INET, SOCK_STREAM); 359 bind(addr); 360 listen(depth); 361} 362 363void TCPServerSocket::operator () (TCPClientSocket &newClient) 364{ 365 accept(newClient); 366} 367 368void TCPServerSocket::receive(TCPClientSocket &newClient) 369{ 370 accept(newClient); 371 close(); 372} 373 374TCPServerSocket::~TCPServerSocket() 375{ 376 close(); 377} 378 379 380} // end namespace IPPlusPlus 381} // end namespace Security 382