]> git.saurik.com Git - apple/security.git/blame - sslViewer/ioSock.c
Security-57740.31.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)
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 */
206OSStatus 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
289OSStatus 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 */
328void endpointShutdown(
329 otSocket sock)
330{
331 close((int)sock);
332}
333
334/*
335 * R/W. Called out from SSL.
336 */
337OSStatus 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
422int oneAtATime = 0;
423
424OSStatus 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}