]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2006-2008,2010,2012-2013 Apple Inc. All Rights Reserved. |
b1ab9ed8 | 3 | * |
d8f41ccd | 4 | * io_sock.c - SecureTransport sample I/O module, X sockets version |
b1ab9ed8 A |
5 | */ |
6 | ||
7 | #include "ioSock.h" | |
8 | #include <errno.h> | |
9 | #include <stdio.h> | |
d8f41ccd | 10 | #include <Security/SecBase.h> |
b1ab9ed8 A |
11 | #include <unistd.h> |
12 | #include <sys/types.h> | |
13 | #include <netinet/in.h> | |
14 | #include <sys/socket.h> | |
15 | #include <netdb.h> | |
16 | #include <arpa/inet.h> | |
17 | #include <fcntl.h> | |
18 | ||
427c49bc | 19 | #include <Security/SecBase.h> |
d8f41ccd | 20 | |
b1ab9ed8 A |
21 | #include <time.h> |
22 | #include <strings.h> | |
23 | ||
24 | /* debugging for this module */ | |
25 | #define SSL_OT_DEBUG 1 | |
26 | ||
27 | /* log errors to stdout */ | |
28 | #define SSL_OT_ERRLOG 1 | |
29 | ||
30 | /* trace all low-level network I/O */ | |
31 | #define SSL_OT_IO_TRACE 0 | |
32 | ||
33 | /* if SSL_OT_IO_TRACE, only log non-zero length transfers */ | |
34 | #define SSL_OT_IO_TRACE_NZ 1 | |
35 | ||
36 | /* pause after each I/O (only meaningful if SSL_OT_IO_TRACE == 1) */ | |
37 | #define SSL_OT_IO_PAUSE 0 | |
38 | ||
39 | /* print a stream of dots while I/O pending */ | |
40 | #define SSL_OT_DOT 1 | |
41 | ||
42 | /* dump some bytes of each I/O (only meaningful if SSL_OT_IO_TRACE == 1) */ | |
43 | #define SSL_OT_IO_DUMP 0 | |
44 | #define SSL_OT_IO_DUMP_SIZE 256 | |
45 | ||
46 | /* indicate errSSLWouldBlock with a '.' */ | |
47 | #define SSL_DISPL_WOULD_BLOCK 0 | |
48 | ||
49 | /* general, not-too-verbose debugging */ | |
50 | #if SSL_OT_DEBUG | |
51 | #define dprintf(s) printf s | |
52 | #else | |
53 | #define dprintf(s) | |
54 | #endif | |
55 | ||
56 | /* errors --> stdout */ | |
57 | #if SSL_OT_ERRLOG | |
58 | #define eprintf(s) printf s | |
59 | #else | |
60 | #define eprintf(s) | |
61 | #endif | |
62 | ||
63 | /* trace completion of every r/w */ | |
64 | #if SSL_OT_IO_TRACE | |
65 | static void tprintf( | |
66 | const char *str, | |
67 | UInt32 req, | |
68 | UInt32 act, | |
69 | const UInt8 *buf) | |
70 | { | |
71 | #if SSL_OT_IO_TRACE_NZ | |
72 | if(act == 0) { | |
73 | return; | |
74 | } | |
75 | #endif | |
76 | printf("%s(%u): moved (%u) bytes\n", str, (unsigned)req, (unsigned)act); | |
77 | #if SSL_OT_IO_DUMP | |
78 | { | |
79 | unsigned i; | |
80 | ||
81 | for(i=0; i<act; i++) { | |
82 | printf("%02X ", buf[i]); | |
83 | if(i >= (SSL_OT_IO_DUMP_SIZE - 1)) { | |
84 | break; | |
85 | } | |
86 | } | |
87 | printf("\n"); | |
88 | } | |
89 | #endif | |
90 | #if SSL_OT_IO_PAUSE | |
91 | { | |
92 | char instr[20]; | |
93 | printf("CR to continue: "); | |
94 | gets(instr); | |
95 | } | |
96 | #endif | |
97 | } | |
98 | ||
99 | #else | |
100 | #define tprintf(str, req, act, buf) | |
101 | #endif /* SSL_OT_IO_TRACE */ | |
102 | ||
103 | /* | |
104 | * If SSL_OT_DOT, output a '.' every so often while waiting for | |
105 | * connection. This gives user a chance to do something else with the | |
106 | * UI. | |
107 | */ | |
108 | ||
109 | #if SSL_OT_DOT | |
110 | ||
111 | static time_t lastTime = (time_t)0; | |
112 | #define TIME_INTERVAL 3 | |
113 | ||
114 | static void outputDot() | |
115 | { | |
116 | time_t thisTime = time(0); | |
117 | ||
118 | if((thisTime - lastTime) >= TIME_INTERVAL) { | |
119 | printf("."); fflush(stdout); | |
120 | lastTime = thisTime; | |
121 | } | |
122 | } | |
123 | #else | |
124 | #define outputDot() | |
125 | #endif | |
126 | ||
127 | ||
128 | /* | |
129 | * One-time only init. | |
130 | */ | |
131 | void initSslOt(void) | |
132 | { | |
133 | ||
134 | } | |
135 | ||
136 | /* | |
137 | * Connect to server. | |
138 | */ | |
139 | #define GETHOST_RETRIES 3 | |
140 | ||
141 | OSStatus MakeServerConnection( | |
142 | const char *hostName, | |
143 | int port, | |
144 | int nonBlocking, // 0 or 1 | |
145 | otSocket *socketNo, // RETURNED | |
146 | PeerSpec *peer) // RETURNED | |
147 | { | |
148 | struct sockaddr_in addr; | |
149 | struct hostent *ent; | |
150 | struct in_addr host; | |
151 | int sock = 0; | |
152 | ||
153 | *socketNo = 0; | |
154 | if (hostName[0] >= '0' && hostName[0] <= '9') | |
155 | { | |
156 | host.s_addr = inet_addr(hostName); | |
157 | } | |
158 | else { | |
159 | unsigned dex; | |
160 | /* seeing a lot of soft failures here that I really don't want to track down */ | |
161 | for(dex=0; dex<GETHOST_RETRIES; dex++) { | |
162 | if(dex != 0) { | |
163 | printf("\n...retrying gethostbyname(%s)", hostName); | |
164 | } | |
165 | ent = gethostbyname(hostName); | |
166 | if(ent != NULL) { | |
167 | break; | |
168 | } | |
169 | } | |
170 | if(ent == NULL) { | |
171 | printf("\n***gethostbyname(%s) returned: %s\n", hostName, hstrerror(h_errno)); | |
427c49bc | 172 | return errSecIO; |
b1ab9ed8 A |
173 | } |
174 | memcpy(&host, ent->h_addr, sizeof(struct in_addr)); | |
175 | } | |
176 | sock = socket(AF_INET, SOCK_STREAM, 0); | |
177 | addr.sin_addr = host; | |
178 | addr.sin_port = htons((u_short)port); | |
179 | ||
180 | addr.sin_family = AF_INET; | |
181 | if (connect(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) != 0) | |
fa7225c8 A |
182 | { |
183 | perror("connect returned error"); | |
427c49bc | 184 | return errSecIO; |
b1ab9ed8 A |
185 | } |
186 | ||
187 | if(nonBlocking) { | |
188 | /* OK to do this after connect? */ | |
189 | int rtn = fcntl(sock, F_SETFL, O_NONBLOCK); | |
190 | if(rtn == -1) { | |
191 | perror("fctnl(O_NONBLOCK)"); | |
427c49bc | 192 | return errSecIO; |
b1ab9ed8 A |
193 | } |
194 | } | |
195 | ||
196 | peer->ipAddr = addr.sin_addr.s_addr; | |
197 | peer->port = htons((u_short)port); | |
198 | *socketNo = (otSocket)sock; | |
427c49bc | 199 | return errSecSuccess; |
b1ab9ed8 A |
200 | } |
201 | ||
202 | /* | |
203 | * Set up an otSocket to listen for client connections. Call once, then | |
204 | * use multiple AcceptClientConnection calls. | |
205 | */ | |
206 | OSStatus ListenForClients( | |
207 | int port, | |
208 | int nonBlocking, // 0 or 1 | |
209 | otSocket *socketNo) // RETURNED | |
210 | { | |
211 | struct sockaddr_in addr; | |
212 | struct hostent *ent; | |
213 | int len; | |
214 | int sock; | |
215 | ||
216 | sock = socket(AF_INET, SOCK_STREAM, 0); | |
217 | if(sock < 1) { | |
218 | perror("socket"); | |
427c49bc | 219 | return errSecIO; |
b1ab9ed8 A |
220 | } |
221 | ||
222 | int reuse = 1; | |
223 | int err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); | |
224 | if (err != 0) { | |
225 | perror("setsockopt"); | |
226 | return err; | |
227 | } | |
228 | ||
229 | ent = gethostbyname("localhost"); | |
230 | if (!ent) { | |
231 | perror("gethostbyname"); | |
427c49bc | 232 | return errSecIO; |
b1ab9ed8 A |
233 | } |
234 | memcpy(&addr.sin_addr, ent->h_addr, sizeof(struct in_addr)); | |
235 | ||
236 | addr.sin_port = htons((u_short)port); | |
237 | addr.sin_addr.s_addr = INADDR_ANY; | |
238 | addr.sin_family = AF_INET; | |
239 | len = sizeof(struct sockaddr_in); | |
240 | if (bind(sock, (struct sockaddr *) &addr, len)) { | |
241 | int theErr = errno; | |
242 | perror("bind"); | |
243 | if(theErr == EADDRINUSE) { | |
427c49bc | 244 | return errSecOpWr; |
b1ab9ed8 A |
245 | } |
246 | else { | |
427c49bc | 247 | return errSecIO; |
b1ab9ed8 A |
248 | } |
249 | } | |
250 | if(nonBlocking) { | |
251 | int rtn = fcntl(sock, F_SETFL, O_NONBLOCK); | |
252 | if(rtn == -1) { | |
253 | perror("fctnl(O_NONBLOCK)"); | |
427c49bc | 254 | return errSecIO; |
b1ab9ed8 A |
255 | } |
256 | } | |
257 | ||
258 | for(;;) { | |
259 | int rtn = listen(sock, 1); | |
260 | switch(rtn) { | |
261 | case 0: | |
262 | *socketNo = (otSocket)sock; | |
427c49bc | 263 | rtn = errSecSuccess; |
b1ab9ed8 A |
264 | break; |
265 | case EWOULDBLOCK: | |
266 | continue; | |
267 | default: | |
268 | perror("listen"); | |
427c49bc | 269 | rtn = errSecIO; |
b1ab9ed8 A |
270 | break; |
271 | } | |
272 | return rtn; | |
273 | } | |
274 | /* NOT REACHED */ | |
275 | return 0; | |
276 | } | |
277 | ||
278 | /* | |
279 | * Accept a client connection. | |
280 | */ | |
281 | ||
282 | /* | |
283 | * Currently we always get back a different peer port number on successive | |
284 | * connections, no matter what the client is doing. To test for resumable | |
285 | * session support, force peer port = 0. | |
286 | */ | |
287 | #define FORCE_ACCEPT_PEER_PORT_ZERO 1 | |
288 | ||
289 | OSStatus AcceptClientConnection( | |
290 | otSocket listenSock, // obtained from ListenForClients | |
291 | otSocket *acceptSock, // RETURNED | |
292 | PeerSpec *peer) // RETURNED | |
293 | { | |
294 | struct sockaddr_in addr; | |
295 | int sock; | |
296 | socklen_t len; | |
297 | ||
298 | len = sizeof(struct sockaddr_in); | |
299 | do { | |
300 | sock = accept((int)listenSock, (struct sockaddr *) &addr, &len); | |
301 | if (sock < 0) { | |
302 | if(errno == EAGAIN) { | |
303 | /* nonblocking, no connection yet */ | |
304 | continue; | |
305 | } | |
306 | else { | |
307 | perror("accept"); | |
427c49bc | 308 | return errSecIO; |
b1ab9ed8 A |
309 | } |
310 | } | |
311 | else { | |
312 | break; | |
313 | } | |
314 | } while(1); | |
315 | *acceptSock = (otSocket)sock; | |
316 | peer->ipAddr = addr.sin_addr.s_addr; | |
317 | #if FORCE_ACCEPT_PEER_PORT_ZERO | |
318 | peer->port = 0; | |
319 | #else | |
320 | peer->port = ntohs(addr.sin_port); | |
321 | #endif | |
427c49bc | 322 | return errSecSuccess; |
b1ab9ed8 A |
323 | } |
324 | ||
325 | /* | |
326 | * Shut down a connection. | |
327 | */ | |
328 | void endpointShutdown( | |
329 | otSocket sock) | |
330 | { | |
331 | close((int)sock); | |
332 | } | |
333 | ||
334 | /* | |
335 | * R/W. Called out from SSL. | |
336 | */ | |
337 | OSStatus SocketRead( | |
338 | SSLConnectionRef connection, | |
339 | void *data, /* owned by | |
340 | * caller, data | |
341 | * RETURNED */ | |
342 | size_t *dataLength) /* IN/OUT */ | |
343 | { | |
d8f41ccd A |
344 | size_t bytesToGo = *dataLength; |
345 | size_t initLen = bytesToGo; | |
b1ab9ed8 A |
346 | UInt8 *currData = (UInt8 *)data; |
347 | int sock = (int)((long)connection); | |
427c49bc | 348 | OSStatus rtn = errSecSuccess; |
d8f41ccd | 349 | size_t bytesRead = 0; |
b1ab9ed8 A |
350 | ssize_t rrtn; |
351 | ||
352 | *dataLength = 0; | |
353 | ||
354 | for(;;) { | |
b1ab9ed8 A |
355 | /* paranoid check, ensure errno is getting written */ |
356 | errno = -555; | |
357 | rrtn = recv(sock, currData, bytesToGo, 0); | |
358 | if (rrtn <= 0) { | |
359 | if(rrtn == 0) { | |
360 | /* closed, EOF */ | |
361 | rtn = errSSLClosedGraceful; | |
362 | break; | |
363 | } | |
364 | int theErr = errno; | |
365 | switch(theErr) { | |
366 | case ENOENT: | |
367 | /* | |
368 | * Undocumented but I definitely see this. | |
369 | * Non-blocking sockets only. Definitely retriable | |
370 | * just like an EAGAIN. | |
371 | */ | |
372 | dprintf(("SocketRead RETRYING on ENOENT, rrtn %d\n", | |
373 | (int)rrtn)); | |
374 | /* normal... */ | |
375 | //rtn = errSSLWouldBlock; | |
376 | /* ...for temp testing.... */ | |
d8f41ccd | 377 | rtn = errSecIO; |
b1ab9ed8 A |
378 | break; |
379 | case ECONNRESET: | |
380 | /* explicit peer abort */ | |
381 | rtn = errSSLClosedAbort; | |
382 | break; | |
383 | case EAGAIN: | |
384 | /* nonblocking, no data */ | |
385 | rtn = errSSLWouldBlock; | |
386 | break; | |
387 | default: | |
388 | dprintf(("SocketRead: read(%u) error %d, rrtn %d\n", | |
389 | (unsigned)bytesToGo, theErr, (int)rrtn)); | |
427c49bc | 390 | rtn = errSecIO; |
b1ab9ed8 A |
391 | break; |
392 | } | |
393 | /* in any case, we're done with this call if rrtn <= 0 */ | |
394 | break; | |
395 | } | |
396 | bytesRead = rrtn; | |
397 | bytesToGo -= bytesRead; | |
398 | currData += bytesRead; | |
399 | ||
400 | if(bytesToGo == 0) { | |
401 | /* filled buffer with incoming data, done */ | |
402 | break; | |
403 | } | |
404 | } | |
405 | *dataLength = initLen - bytesToGo; | |
406 | tprintf("SocketRead", initLen, *dataLength, (UInt8 *)data); | |
407 | ||
408 | #if SSL_OT_DOT || (SSL_OT_DEBUG && !SSL_OT_IO_TRACE) | |
409 | if((rtn == 0) && (*dataLength == 0)) { | |
410 | /* keep UI alive */ | |
411 | outputDot(); | |
412 | } | |
413 | #endif | |
414 | #if SSL_DISPL_WOULD_BLOCK | |
415 | if(rtn == errSSLWouldBlock) { | |
416 | printf("."); fflush(stdout); | |
417 | } | |
418 | #endif | |
419 | return rtn; | |
420 | } | |
421 | ||
422 | int oneAtATime = 0; | |
423 | ||
424 | OSStatus SocketWrite( | |
425 | SSLConnectionRef connection, | |
426 | const void *data, | |
427 | size_t *dataLength) /* IN/OUT */ | |
428 | { | |
429 | size_t bytesSent = 0; | |
430 | int sock = (int)((long)connection); | |
d8f41ccd | 431 | size_t length; |
b1ab9ed8 A |
432 | size_t dataLen = *dataLength; |
433 | const UInt8 *dataPtr = (UInt8 *)data; | |
434 | OSStatus ortn; | |
435 | ||
436 | if(oneAtATime && (*dataLength > 1)) { | |
437 | size_t i; | |
438 | size_t outLen; | |
439 | size_t thisMove; | |
440 | ||
441 | outLen = 0; | |
442 | for(i=0; i<dataLen; i++) { | |
443 | thisMove = 1; | |
444 | ortn = SocketWrite(connection, dataPtr, &thisMove); | |
445 | outLen += thisMove; | |
446 | dataPtr++; | |
447 | if(ortn) { | |
448 | return ortn; | |
449 | } | |
450 | } | |
427c49bc | 451 | return errSecSuccess; |
b1ab9ed8 A |
452 | } |
453 | *dataLength = 0; | |
454 | ||
455 | do { | |
456 | length = write(sock, | |
457 | (char*)dataPtr + bytesSent, | |
458 | dataLen - bytesSent); | |
459 | } while ((length > 0) && | |
460 | ( (bytesSent += length) < dataLen) ); | |
461 | ||
462 | if(length <= 0) { | |
463 | int theErr = errno; | |
464 | switch(theErr) { | |
465 | case EAGAIN: | |
466 | ortn = errSSLWouldBlock; break; | |
467 | case EPIPE: | |
468 | ortn = errSSLClosedAbort; break; | |
469 | default: | |
470 | dprintf(("SocketWrite: write(%u) error %d\n", | |
471 | (unsigned)(dataLen - bytesSent), theErr)); | |
427c49bc | 472 | ortn = errSecIO; |
b1ab9ed8 A |
473 | break; |
474 | } | |
475 | } | |
476 | else { | |
427c49bc | 477 | ortn = errSecSuccess; |
b1ab9ed8 A |
478 | } |
479 | tprintf("SocketWrite", dataLen, bytesSent, dataPtr); | |
480 | *dataLength = bytesSent; | |
481 | return ortn; | |
482 | } |