]> git.saurik.com Git - apple/security.git/blob - sslViewer/ioSock.c
Security-59306.11.20.tar.gz
[apple/security.git] / sslViewer / ioSock.c
1 /*
2 * Copyright (c) 2006-2008,2010,2012-2013 Apple Inc. All Rights Reserved.
3 *
4 * io_sock.c - SecureTransport sample I/O module, X sockets version
5 */
6
7 #include "ioSock.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #include <Security/SecBase.h>
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
19 #include <Security/SecBase.h>
20
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 = NULL;
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));
172 return errSecIO;
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)
182 {
183 perror("connect returned error");
184 return errSecIO;
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)");
192 return errSecIO;
193 }
194 }
195
196 peer->ipAddr = addr.sin_addr.s_addr;
197 peer->port = htons((u_short)port);
198 *socketNo = (otSocket)sock;
199 return errSecSuccess;
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");
219 return errSecIO;
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");
232 return errSecIO;
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) {
244 return errSecOpWr;
245 }
246 else {
247 return errSecIO;
248 }
249 }
250 if(nonBlocking) {
251 int rtn = fcntl(sock, F_SETFL, O_NONBLOCK);
252 if(rtn == -1) {
253 perror("fctnl(O_NONBLOCK)");
254 return errSecIO;
255 }
256 }
257
258 for(;;) {
259 int rtn = listen(sock, 1);
260 switch(rtn) {
261 case 0:
262 *socketNo = (otSocket)sock;
263 rtn = errSecSuccess;
264 break;
265 case EWOULDBLOCK:
266 continue;
267 default:
268 perror("listen");
269 rtn = errSecIO;
270 break;
271 }
272 return rtn;
273 }
274 }
275
276 /*
277 * Accept a client connection.
278 */
279
280 /*
281 * Currently we always get back a different peer port number on successive
282 * connections, no matter what the client is doing. To test for resumable
283 * session support, force peer port = 0.
284 */
285 #define FORCE_ACCEPT_PEER_PORT_ZERO 1
286
287 OSStatus AcceptClientConnection(
288 otSocket listenSock, // obtained from ListenForClients
289 otSocket *acceptSock, // RETURNED
290 PeerSpec *peer) // RETURNED
291 {
292 struct sockaddr_in addr;
293 int sock;
294 socklen_t len;
295
296 len = sizeof(struct sockaddr_in);
297 do {
298 sock = accept((int)listenSock, (struct sockaddr *) &addr, &len);
299 if (sock < 0) {
300 if(errno == EAGAIN) {
301 /* nonblocking, no connection yet */
302 continue;
303 }
304 else {
305 perror("accept");
306 return errSecIO;
307 }
308 }
309 else {
310 break;
311 }
312 } while(1);
313 *acceptSock = (otSocket)sock;
314 peer->ipAddr = addr.sin_addr.s_addr;
315 #if FORCE_ACCEPT_PEER_PORT_ZERO
316 peer->port = 0;
317 #else
318 peer->port = ntohs(addr.sin_port);
319 #endif
320 return errSecSuccess;
321 }
322
323 /*
324 * Shut down a connection.
325 */
326 void endpointShutdown(
327 otSocket sock)
328 {
329 close((int)sock);
330 }
331
332 /*
333 * R/W. Called out from SSL.
334 */
335 OSStatus SocketRead(
336 SSLConnectionRef connection,
337 void *data, /* owned by
338 * caller, data
339 * RETURNED */
340 size_t *dataLength) /* IN/OUT */
341 {
342 size_t bytesToGo = *dataLength;
343 size_t initLen = bytesToGo;
344 UInt8 *currData = (UInt8 *)data;
345 int sock = (int)((long)connection);
346 OSStatus rtn = errSecSuccess;
347 ssize_t rrtn;
348
349 *dataLength = 0;
350
351 for(;;) {
352 /* paranoid check, ensure errno is getting written */
353 errno = -555;
354 rrtn = recv(sock, currData, bytesToGo, 0);
355 if (rrtn <= 0) {
356 if(rrtn == 0) {
357 /* closed, EOF */
358 rtn = errSSLClosedGraceful;
359 break;
360 }
361 int theErr = errno;
362 switch(theErr) {
363 case ENOENT:
364 /*
365 * Undocumented but I definitely see this.
366 * Non-blocking sockets only. Definitely retriable
367 * just like an EAGAIN.
368 */
369 dprintf(("SocketRead RETRYING on ENOENT, rrtn %d\n",
370 (int)rrtn));
371 /* normal... */
372 //rtn = errSSLWouldBlock;
373 /* ...for temp testing.... */
374 rtn = errSecIO;
375 break;
376 case ECONNRESET:
377 /* explicit peer abort */
378 rtn = errSSLClosedAbort;
379 break;
380 case EAGAIN:
381 /* nonblocking, no data */
382 rtn = errSSLWouldBlock;
383 break;
384 default:
385 dprintf(("SocketRead: read(%u) error %d, rrtn %d\n",
386 (unsigned)bytesToGo, theErr, (int)rrtn));
387 rtn = errSecIO;
388 break;
389 }
390 /* in any case, we're done with this call if rrtn <= 0 */
391 break;
392 }
393 bytesToGo -= rrtn;
394 currData += rrtn;
395
396 if(bytesToGo == 0) {
397 /* filled buffer with incoming data, done */
398 break;
399 }
400 }
401 *dataLength = initLen - bytesToGo;
402 tprintf("SocketRead", initLen, *dataLength, (UInt8 *)data);
403
404 #if SSL_OT_DOT || (SSL_OT_DEBUG && !SSL_OT_IO_TRACE)
405 if((rtn == 0) && (*dataLength == 0)) {
406 /* keep UI alive */
407 outputDot();
408 }
409 #endif
410 #if SSL_DISPL_WOULD_BLOCK
411 if(rtn == errSSLWouldBlock) {
412 printf("."); fflush(stdout);
413 }
414 #endif
415 return rtn;
416 }
417
418 int oneAtATime = 0;
419
420 OSStatus SocketWrite(
421 SSLConnectionRef connection,
422 const void *data,
423 size_t *dataLength) /* IN/OUT */
424 {
425 size_t bytesSent = 0;
426 int sock = (int)((long)connection);
427 size_t length;
428 size_t dataLen = *dataLength;
429 const UInt8 *dataPtr = (UInt8 *)data;
430 OSStatus ortn;
431
432 if(oneAtATime && (*dataLength > 1)) {
433 size_t i;
434 size_t outLen;
435 size_t thisMove;
436
437 outLen = 0;
438 for(i=0; i<dataLen; i++) {
439 thisMove = 1;
440 ortn = SocketWrite(connection, dataPtr, &thisMove);
441 outLen += thisMove;
442 dataPtr++;
443 if(ortn) {
444 return ortn;
445 }
446 }
447 return errSecSuccess;
448 }
449 *dataLength = 0;
450
451 do {
452 length = write(sock,
453 (char*)dataPtr + bytesSent,
454 dataLen - bytesSent);
455 } while ((length > 0) &&
456 ( (bytesSent += length) < dataLen) );
457
458 if(length <= 0) {
459 int theErr = errno;
460 switch(theErr) {
461 case EAGAIN:
462 ortn = errSSLWouldBlock; break;
463 case EPIPE:
464 ortn = errSSLClosedAbort; break;
465 default:
466 dprintf(("SocketWrite: write(%u) error %d\n",
467 (unsigned)(dataLen - bytesSent), theErr));
468 ortn = errSecIO;
469 break;
470 }
471 }
472 else {
473 ortn = errSecSuccess;
474 }
475 tprintf("SocketWrite", dataLen, bytesSent, dataPtr);
476 *dataLength = bytesSent;
477 return ortn;
478 }