]> git.saurik.com Git - apple/security.git/blame - sslViewer/ioSock.c
Security-57337.60.2.tar.gz
[apple/security.git] / sslViewer / ioSock.c
CommitLineData
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
65static 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
111static time_t lastTime = (time_t)0;
112#define TIME_INTERVAL 3
113
114static 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 */
131void initSslOt(void)
132{
133
134}
135
136/*
137 * Connect to server.
138 */
139#define GETHOST_RETRIES 3
140
141OSStatus 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)
182 { printf("connect returned error\n");
427c49bc 183 return errSecIO;
b1ab9ed8
A
184 }
185
186 if(nonBlocking) {
187 /* OK to do this after connect? */
188 int rtn = fcntl(sock, F_SETFL, O_NONBLOCK);
189 if(rtn == -1) {
190 perror("fctnl(O_NONBLOCK)");
427c49bc 191 return errSecIO;
b1ab9ed8
A
192 }
193 }
194
195 peer->ipAddr = addr.sin_addr.s_addr;
196 peer->port = htons((u_short)port);
197 *socketNo = (otSocket)sock;
427c49bc 198 return errSecSuccess;
b1ab9ed8
A
199}
200
201/*
202 * Set up an otSocket to listen for client connections. Call once, then
203 * use multiple AcceptClientConnection calls.
204 */
205OSStatus ListenForClients(
206 int port,
207 int nonBlocking, // 0 or 1
208 otSocket *socketNo) // RETURNED
209{
210 struct sockaddr_in addr;
211 struct hostent *ent;
212 int len;
213 int sock;
214
215 sock = socket(AF_INET, SOCK_STREAM, 0);
216 if(sock < 1) {
217 perror("socket");
427c49bc 218 return errSecIO;
b1ab9ed8
A
219 }
220
221 int reuse = 1;
222 int err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
223 if (err != 0) {
224 perror("setsockopt");
225 return err;
226 }
227
228 ent = gethostbyname("localhost");
229 if (!ent) {
230 perror("gethostbyname");
427c49bc 231 return errSecIO;
b1ab9ed8
A
232 }
233 memcpy(&addr.sin_addr, ent->h_addr, sizeof(struct in_addr));
234
235 addr.sin_port = htons((u_short)port);
236 addr.sin_addr.s_addr = INADDR_ANY;
237 addr.sin_family = AF_INET;
238 len = sizeof(struct sockaddr_in);
239 if (bind(sock, (struct sockaddr *) &addr, len)) {
240 int theErr = errno;
241 perror("bind");
242 if(theErr == EADDRINUSE) {
427c49bc 243 return errSecOpWr;
b1ab9ed8
A
244 }
245 else {
427c49bc 246 return errSecIO;
b1ab9ed8
A
247 }
248 }
249 if(nonBlocking) {
250 int rtn = fcntl(sock, F_SETFL, O_NONBLOCK);
251 if(rtn == -1) {
252 perror("fctnl(O_NONBLOCK)");
427c49bc 253 return errSecIO;
b1ab9ed8
A
254 }
255 }
256
257 for(;;) {
258 int rtn = listen(sock, 1);
259 switch(rtn) {
260 case 0:
261 *socketNo = (otSocket)sock;
427c49bc 262 rtn = errSecSuccess;
b1ab9ed8
A
263 break;
264 case EWOULDBLOCK:
265 continue;
266 default:
267 perror("listen");
427c49bc 268 rtn = errSecIO;
b1ab9ed8
A
269 break;
270 }
271 return rtn;
272 }
273 /* NOT REACHED */
274 return 0;
275}
276
277/*
278 * Accept a client connection.
279 */
280
281/*
282 * Currently we always get back a different peer port number on successive
283 * connections, no matter what the client is doing. To test for resumable
284 * session support, force peer port = 0.
285 */
286#define FORCE_ACCEPT_PEER_PORT_ZERO 1
287
288OSStatus AcceptClientConnection(
289 otSocket listenSock, // obtained from ListenForClients
290 otSocket *acceptSock, // RETURNED
291 PeerSpec *peer) // RETURNED
292{
293 struct sockaddr_in addr;
294 int sock;
295 socklen_t len;
296
297 len = sizeof(struct sockaddr_in);
298 do {
299 sock = accept((int)listenSock, (struct sockaddr *) &addr, &len);
300 if (sock < 0) {
301 if(errno == EAGAIN) {
302 /* nonblocking, no connection yet */
303 continue;
304 }
305 else {
306 perror("accept");
427c49bc 307 return errSecIO;
b1ab9ed8
A
308 }
309 }
310 else {
311 break;
312 }
313 } while(1);
314 *acceptSock = (otSocket)sock;
315 peer->ipAddr = addr.sin_addr.s_addr;
316 #if FORCE_ACCEPT_PEER_PORT_ZERO
317 peer->port = 0;
318 #else
319 peer->port = ntohs(addr.sin_port);
320 #endif
427c49bc 321 return errSecSuccess;
b1ab9ed8
A
322}
323
324/*
325 * Shut down a connection.
326 */
327void endpointShutdown(
328 otSocket sock)
329{
330 close((int)sock);
331}
332
333/*
334 * R/W. Called out from SSL.
335 */
336OSStatus SocketRead(
337 SSLConnectionRef connection,
338 void *data, /* owned by
339 * caller, data
340 * RETURNED */
341 size_t *dataLength) /* IN/OUT */
342{
d8f41ccd
A
343 size_t bytesToGo = *dataLength;
344 size_t initLen = bytesToGo;
b1ab9ed8
A
345 UInt8 *currData = (UInt8 *)data;
346 int sock = (int)((long)connection);
427c49bc 347 OSStatus rtn = errSecSuccess;
d8f41ccd 348 size_t bytesRead = 0;
b1ab9ed8
A
349 ssize_t rrtn;
350
351 *dataLength = 0;
352
353 for(;;) {
b1ab9ed8
A
354 /* paranoid check, ensure errno is getting written */
355 errno = -555;
356 rrtn = recv(sock, currData, bytesToGo, 0);
357 if (rrtn <= 0) {
358 if(rrtn == 0) {
359 /* closed, EOF */
360 rtn = errSSLClosedGraceful;
361 break;
362 }
363 int theErr = errno;
364 switch(theErr) {
365 case ENOENT:
366 /*
367 * Undocumented but I definitely see this.
368 * Non-blocking sockets only. Definitely retriable
369 * just like an EAGAIN.
370 */
371 dprintf(("SocketRead RETRYING on ENOENT, rrtn %d\n",
372 (int)rrtn));
373 /* normal... */
374 //rtn = errSSLWouldBlock;
375 /* ...for temp testing.... */
d8f41ccd 376 rtn = errSecIO;
b1ab9ed8
A
377 break;
378 case ECONNRESET:
379 /* explicit peer abort */
380 rtn = errSSLClosedAbort;
381 break;
382 case EAGAIN:
383 /* nonblocking, no data */
384 rtn = errSSLWouldBlock;
385 break;
386 default:
387 dprintf(("SocketRead: read(%u) error %d, rrtn %d\n",
388 (unsigned)bytesToGo, theErr, (int)rrtn));
427c49bc 389 rtn = errSecIO;
b1ab9ed8
A
390 break;
391 }
392 /* in any case, we're done with this call if rrtn <= 0 */
393 break;
394 }
395 bytesRead = rrtn;
396 bytesToGo -= bytesRead;
397 currData += bytesRead;
398
399 if(bytesToGo == 0) {
400 /* filled buffer with incoming data, done */
401 break;
402 }
403 }
404 *dataLength = initLen - bytesToGo;
405 tprintf("SocketRead", initLen, *dataLength, (UInt8 *)data);
406
407 #if SSL_OT_DOT || (SSL_OT_DEBUG && !SSL_OT_IO_TRACE)
408 if((rtn == 0) && (*dataLength == 0)) {
409 /* keep UI alive */
410 outputDot();
411 }
412 #endif
413 #if SSL_DISPL_WOULD_BLOCK
414 if(rtn == errSSLWouldBlock) {
415 printf("."); fflush(stdout);
416 }
417 #endif
418 return rtn;
419}
420
421int oneAtATime = 0;
422
423OSStatus SocketWrite(
424 SSLConnectionRef connection,
425 const void *data,
426 size_t *dataLength) /* IN/OUT */
427{
428 size_t bytesSent = 0;
429 int sock = (int)((long)connection);
d8f41ccd 430 size_t length;
b1ab9ed8
A
431 size_t dataLen = *dataLength;
432 const UInt8 *dataPtr = (UInt8 *)data;
433 OSStatus ortn;
434
435 if(oneAtATime && (*dataLength > 1)) {
436 size_t i;
437 size_t outLen;
438 size_t thisMove;
439
440 outLen = 0;
441 for(i=0; i<dataLen; i++) {
442 thisMove = 1;
443 ortn = SocketWrite(connection, dataPtr, &thisMove);
444 outLen += thisMove;
445 dataPtr++;
446 if(ortn) {
447 return ortn;
448 }
449 }
427c49bc 450 return errSecSuccess;
b1ab9ed8
A
451 }
452 *dataLength = 0;
453
454 do {
455 length = write(sock,
456 (char*)dataPtr + bytesSent,
457 dataLen - bytesSent);
458 } while ((length > 0) &&
459 ( (bytesSent += length) < dataLen) );
460
461 if(length <= 0) {
462 int theErr = errno;
463 switch(theErr) {
464 case EAGAIN:
465 ortn = errSSLWouldBlock; break;
466 case EPIPE:
467 ortn = errSSLClosedAbort; break;
468 default:
469 dprintf(("SocketWrite: write(%u) error %d\n",
470 (unsigned)(dataLen - bytesSent), theErr));
427c49bc 471 ortn = errSecIO;
b1ab9ed8
A
472 break;
473 }
474 }
475 else {
427c49bc 476 ortn = errSecSuccess;
b1ab9ed8
A
477 }
478 tprintf("SocketWrite", dataLen, bytesSent, dataPtr);
479 *dataLength = bytesSent;
480 return ortn;
481}