]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/ip++.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_utilities / lib / ip++.cpp
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
37 namespace Security {
38 namespace IPPlusPlus {
39
40
41 typedef unsigned char Byte; // occasionally useful
42
43
44 //
45 // IPAddress
46 //
47 static const struct in_addr in_addr_any = { INADDR_ANY };
48 #if BUG_GCC
49 const IPAddress &IPAddress::any = *static_cast<const IPAddress *>(&in_addr_any);
50 #else
51 const IPAddress &IPAddress::any = static_cast<const IPAddress &>(in_addr_any);
52 #endif
53
54 IPAddress::IPAddress(const char *s)
55 {
56 if (!inet_aton(s, this))
57 UnixError::throwMe(EINVAL);
58 }
59
60 IPAddress::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 //
74 IPSockAddress::IPSockAddress()
75 {
76 sin_family = AF_INET;
77 }
78
79 IPSockAddress::IPSockAddress(const IPAddress &addr, IPPort port)
80 {
81 sin_family = AF_INET;
82 sin_addr = addr;
83 sin_port = htons(port);
84 }
85
86 IPSockAddress::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
94 IPSockAddress IPSockAddress::defaults(const IPSockAddress &defaultAddr) const
95 {
96 return defaults(defaultAddr.address(), defaultAddr.port());
97 }
98
99 IPSockAddress IPSockAddress::defaults(const IPAddress &defaultAddr, IPPort defaultPort) const
100 {
101 return IPSockAddress(
102 address() ? address() : defaultAddr,
103 port() ? port() : defaultPort
104 );
105 }
106
107 IPSockAddress IPSockAddress::defaults(IPPort defaultPort) const
108 {
109 return IPSockAddress(address(), port() ? port() : defaultPort);
110 }
111
112
113 //
114 // UNSockAddress
115 //
116 UNSockAddress::UNSockAddress()
117 {
118 sun_family = AF_UNIX;
119 }
120
121 UNSockAddress::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
130 UNSockAddress::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
139 string UNSockAddress::path() const
140 {
141 return sun_path;
142 }
143
144
145 //
146 // Sockets
147 //
148 Socket::Socket(int type)
149 {
150 open(type);
151 }
152
153 Socket::Socket(int domain, int type, int protocol)
154 {
155 open(domain, type, protocol);
156 }
157
158 void 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
165 void 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
177 void Socket::bind(const IPAddress &addr, IPPort port)
178 {
179 bind(IPSockAddress(addr, port));
180 }
181
182 void 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
188 void 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
195 void Socket::listen(int backlog)
196 {
197 checkError(::listen(fd(), backlog));
198 }
199
200
201 void Socket::accept(Socket &s)
202 {
203 IPSockAddress dummy; // ignored
204 return accept(s, dummy);
205 }
206
207 void 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
214 void 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
222 bool 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
251 bool Socket::connect(const IPAddress &addr, IPPort port)
252 {
253 return connect(IPSockAddress(addr, port));
254 }
255
256 bool 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
267 void Socket::shutdown(int how)
268 {
269 assert(how >= 0 && how <= 2);
270 checkError(::shutdown(fd(), how));
271 }
272
273
274 IPSockAddress 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
283 IPSockAddress 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
292 void 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
297 void 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 //
307 void 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 //
332 void TCPClientSocket::open(const IPSockAddress &peer, int fdFlags)
333 {
334 prepare(fdFlags, AF_INET, SOCK_STREAM);
335 connect(peer);
336 }
337
338 void TCPClientSocket::open(const IPAddress &addr, IPPort port, int fdFlags)
339 {
340 prepare(fdFlags, AF_INET, SOCK_STREAM);
341 connect(addr, port);
342 }
343
344 void TCPClientSocket::open(const Host &host, IPPort port, int fdFlags)
345 {
346 prepare(fdFlags, AF_INET, SOCK_STREAM);
347 connect(host, port);
348 }
349
350 TCPClientSocket::~TCPClientSocket()
351 {
352 close();
353 }
354
355
356 void TCPServerSocket::open(const IPSockAddress &addr, int depth)
357 {
358 prepare(0, AF_INET, SOCK_STREAM);
359 bind(addr);
360 listen(depth);
361 }
362
363 void TCPServerSocket::operator () (TCPClientSocket &newClient)
364 {
365 accept(newClient);
366 }
367
368 void TCPServerSocket::receive(TCPClientSocket &newClient)
369 {
370 accept(newClient);
371 close();
372 }
373
374 TCPServerSocket::~TCPServerSocket()
375 {
376 close();
377 }
378
379
380 } // end namespace IPPlusPlus
381 } // end namespace Security