]>
Commit | Line | Data |
---|---|---|
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 | ||
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 |