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