]> git.saurik.com Git - apple/security.git/blame - libsecurity_utilities/lib/ip++.cpp
Security-55471.14.18.tar.gz
[apple/security.git] / libsecurity_utilities / lib / ip++.cpp
CommitLineData
b1ab9ed8
A
1/*
2 * Copyright (c) 2000-2004 Apple Computer, 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