1/*
2 * io_sock.c - SecureTransport sample I/O module, X sockets version
3 */
4
5#include "ioSockThr.h"
6#include <errno.h>
7#include <stdio.h>
8
9#include <unistd.h>
10#include <sys/types.h>
11#include <netinet/in.h>
12#include <sys/socket.h>
13#include <netdb.h>
14#include <arpa/inet.h>
15#include <fcntl.h>
16
17#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
18#include <time.h>
19#include <strings.h>
20
21/* debugging for this module */
22#define SSL_OT_DEBUG		1
23
24/* log errors to stdout */
25#define SSL_OT_ERRLOG		1
26
27/* trace all low-level network I/O */
28#define SSL_OT_IO_TRACE		0
29
30/* if SSL_OT_IO_TRACE, only log non-zero length transfers */
31#define SSL_OT_IO_TRACE_NZ	1
32
33/* pause after each I/O (only meaningful if SSL_OT_IO_TRACE == 1) */
34#define SSL_OT_IO_PAUSE		0
35
36/* print a stream of dots while I/O pending */
37#define SSL_OT_DOT			1
38
39/* dump some bytes of each I/O (only meaningful if SSL_OT_IO_TRACE == 1) */
40#define SSL_OT_IO_DUMP		0
41#define SSL_OT_IO_DUMP_SIZE	64
42
43/* general, not-too-verbose debugging */
44#if		SSL_OT_DEBUG
45#define dprintf(s)	printf s
46#else
47#define dprintf(s)
48#endif
49
50/* errors --> stdout */
51#if		SSL_OT_ERRLOG
52#define eprintf(s)	printf s
53#else
54#define eprintf(s)
55#endif
56
57/* enable nonblocking I/O - maybe should be an arg to MakeServerConnection() */
58#define NON_BLOCKING	0
59
60/* trace completion of every r/w */
61#if		SSL_OT_IO_TRACE
62static void tprintf(
63	const char *str,
64	UInt32 req,
65	UInt32 act,
66	const UInt8 *buf)
67{
68	#if	SSL_OT_IO_TRACE_NZ
69	if(act == 0) {
70		return;
71	}
72	#endif
73	printf("%s(%d): moved %d bytes\n", str, req, act);
74	#if	SSL_OT_IO_DUMP
75	{
76		int i;
77
78		for(i=0; i<act; i++) {
79			printf("%02X ", buf[i]);
80			if(i >= (SSL_OT_IO_DUMP_SIZE - 1)) {
81				break;
82			}
83		}
84		printf("\n");
85	}
86	#endif
87	#if SSL_OT_IO_PAUSE
88	{
89		char instr[20];
90		printf("CR to continue: ");
91		gets(instr);
92	}
93	#endif
94}
95
96#else
97#define tprintf(str, req, act, buf)
98#endif	/* SSL_OT_IO_TRACE */
99
100/*
101 * If SSL_OT_DOT, output a '.' every so often while waiting for
102 * connection. This gives user a chance to do something else with the
103 * UI.
104 */
105
106#if	SSL_OT_DOT
107
108static time_t lastTime = (time_t)0;
109#define TIME_INTERVAL		3
110
111static void outputDot()
112{
113	time_t thisTime = time(0);
114
115	if((thisTime - lastTime) >= TIME_INTERVAL) {
116		printf("."); fflush(stdout);
117		lastTime = thisTime;
118	}
119}
120#else
121#define outputDot()
122#endif
123
124
125/*
126 * One-time only init.
127 */
128void initSslOt()
129{
130
131}
132
133/*
134 * Connect to server.
135 *
136 * Seeing a lot of soft errors...for threadTest (only) let's retry.
137 */
138#define CONNECT_RETRIES	10
139
140OSStatus MakeServerConnection(
141	const char *hostName,
142	int port,
143	otSocket *socketNo, 	// RETURNED
144	PeerSpec *peer)			// RETURNED
145{
146    struct sockaddr_in  addr;
147    struct hostent      *ent;
148    struct in_addr      host;
149	int					sock = 0;
150	int					i;
151
152	*socketNo = NULL;
153    if (hostName[0] >= '0' && hostName[0] <= '9') {
154        host.s_addr = inet_addr(hostName);
155    }
156    else {
157		for(i=0; i<CONNECT_RETRIES; i++) {
158			/* seeing a lot of spurious "No address associated with name"
159			 * failures on known good names (www.amazon.com) */
160			ent = gethostbyname(hostName);
161			if (ent == NULL) {
162				printf("gethostbyname failed\n");
163				herror("hostName");
164
165			}
166			else {
167				memcpy(&host, ent->h_addr, sizeof(struct in_addr));
168				break;
169			}
170		}
171		if(ent == NULL) {
172			return ioErr;
173		}
174    }
175    addr.sin_family = AF_INET;
176	for(i=0; i<CONNECT_RETRIES; i++) {
177		sock = socket(AF_INET, SOCK_STREAM, 0);
178		addr.sin_addr = host;
179		addr.sin_port = htons((u_short)port);
180
181		if (connect(sock, (struct sockaddr *) &addr,
182				sizeof(struct sockaddr_in)) == 0) {
183			break;
184		}
185		/* retry */
186		close(sock);
187		fprintf(stderr, "%s ", hostName);
188		perror("connect");
189	}
190	if(i == CONNECT_RETRIES) {
191		return ioErr;
192	}
193
194	#if		NON_BLOCKING
195	/* OK to do this after connect? */
196	{
197		int rtn = fcntl(sock, F_SETFL, O_NONBLOCK);
198		if(rtn == -1) {
199			perror("fctnl(O_NONBLOCK)");
200			return ioErr;
201		}
202	}
203	#endif	/* NON_BLOCKING*/
204
205    peer->ipAddr = addr.sin_addr.s_addr;
206    peer->port = htons((u_short)port);
207	*socketNo = (otSocket)sock;
208    return noErr;
209}
210
211/*
212 * Accept a client connection.
213 */
214OSStatus AcceptClientConnection(
215	int port,
216	otSocket *socketNo, 	// RETURNED
217	PeerSpec *peer)			// RETURNED
218{
219	/* maybe some day */
220	return unimpErr;
221}
222
223/*
224 * Shut down a connection.
225 */
226void endpointShutdown(
227	otSocket socket)
228{
229	close((int)socket);
230}
231
232/*
233 * R/W. Called out from SSL.
234 */
235OSStatus SocketRead(
236	SSLConnectionRef 	connection,
237	void 				*data, 			/* owned by
238	 									 * caller, data
239	 									 * RETURNED */
240	size_t 				*dataLength)	/* IN/OUT */
241{
242	size_t			bytesToGo = *dataLength;
243	size_t 			initLen = bytesToGo;
244	UInt8			*currData = (UInt8 *)data;
245	int		        sock = (int)((long)connection);
246	OSStatus		rtn = noErr;
247	size_t			bytesRead;
248	int				rrtn;
249
250	*dataLength = 0;
251
252	for(;;) {
253		bytesRead = 0;
254		rrtn = read(sock, currData, bytesToGo);
255		if (rrtn <= 0) {
256			/* this is guesswork... */
257			switch(errno) {
258				case ENOENT:
259					/* connection closed */
260					rtn = errSSLClosedGraceful;
261					break;
262				#if	NON_BLOCKING
263				case EAGAIN:
264				#else
265				case 0:		/* ??? */
266				#endif
267					rtn = errSSLWouldBlock;
268					break;
269				default:
270					dprintf(("SocketRead: read(%lu) error %d\n",
271						bytesToGo, errno));
272					rtn = ioErr;
273					break;
274			}
275			break;
276		}
277		else {
278			bytesRead = rrtn;
279		}
280		bytesToGo -= bytesRead;
281		currData  += bytesRead;
282
283		if(bytesToGo == 0) {
284			/* filled buffer with incoming data, done */
285			break;
286		}
287	}
288	*dataLength = initLen - bytesToGo;
289	tprintf("SocketRead", initLen, *dataLength, (UInt8 *)data);
290
291	#if SSL_OT_DOT || (SSL_OT_DEBUG && !SSL_OT_IO_TRACE)
292	if((rtn == 0) && (*dataLength == 0)) {
293		/* keep UI alive */
294		outputDot();
295	}
296	#endif
297	return rtn;
298}
299
300OSStatus SocketWrite(
301	SSLConnectionRef 	connection,
302	const void	 		*data,
303	size_t 				*dataLength)	/* IN/OUT */
304{
305	size_t		bytesSent = 0;
306	int			sock = (int)((long)connection);
307	int 		length;
308	UInt32		dataLen = *dataLength;
309	const UInt8 *dataPtr = (UInt8 *)data;
310	OSStatus	ortn;
311
312	*dataLength = 0;
313
314    do {
315        length = write(sock,
316				(char*)dataPtr + bytesSent,
317				dataLen - bytesSent);
318    } while ((length > 0) &&
319			 ( (bytesSent += length) < dataLen) );
320
321	if(length == 0) {
322		if(errno == EAGAIN) {
323			ortn = errSSLWouldBlock;
324		}
325		else {
326			ortn = ioErr;
327		}
328	}
329	else {
330		ortn = noErr;
331	}
332	tprintf("SocketWrite", dataLen, bytesSent, dataPtr);
333	*dataLength = bytesSent;
334	return ortn;
335}
336