sockwrap.c 9.46 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
/* sockwrap.c */

/* Berkley/WinSock socket API wrappers */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html		*
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

38
#include <stdlib.h>		/* alloca/free on FreeBSD */
39
#include <string.h>		/* bzero (for FD_ZERO) on FreeBSD */
40
#include <errno.h>		/* ENOMEM */
41
#include <stdio.h>		/* SEEK_SET */
42
#include <string.h>
43

44
#include "genwrap.h"	/* SLEEP */
deuce's avatar
deuce committed
45
#include "gen_defs.h"	/* BOOL/LOG_WARNING */
46
47
48
#include "sockwrap.h"	/* sendsocket */
#include "filewrap.h"	/* filelength */

49
static socket_option_t socket_options[] = {
50
	{ "TYPE",				0,				SOL_SOCKET,		SO_TYPE				},
51
	{ "ERROR",				0,				SOL_SOCKET,		SO_ERROR			},
52
53
54
55
	{ "DEBUG",				0,				SOL_SOCKET,		SO_DEBUG			},
	{ "LINGER",				SOCK_STREAM,	SOL_SOCKET,		SO_LINGER			},
	{ "SNDBUF",				0,				SOL_SOCKET,		SO_SNDBUF			},
	{ "RCVBUF",				0,				SOL_SOCKET,		SO_RCVBUF			},
56
57

#ifndef _WINSOCKAPI_	/* Defined, but not supported, by WinSock */
58
59
60
61
	{ "SNDLOWAT",			0,				SOL_SOCKET,		SO_SNDLOWAT			},
	{ "RCVLOWAT",			0,				SOL_SOCKET,		SO_RCVLOWAT			},
	{ "SNDTIMEO",			0,				SOL_SOCKET,		SO_SNDTIMEO			},
	{ "RCVTIMEO",			0,				SOL_SOCKET,		SO_RCVTIMEO			},
62
63
64
#ifdef SO_USELOOPBACK	/* SunOS */
	{ "USELOOPBACK",		0,				SOL_SOCKET,		SO_USELOOPBACK		},
#endif
65
#endif
66

67
68
69
70
71
	{ "REUSEADDR",			0,				SOL_SOCKET,		SO_REUSEADDR		},	
	{ "KEEPALIVE",			SOCK_STREAM,	SOL_SOCKET,		SO_KEEPALIVE		},
	{ "DONTROUTE",			0,				SOL_SOCKET,		SO_DONTROUTE		},
	{ "BROADCAST",			SOCK_DGRAM,		SOL_SOCKET,		SO_BROADCAST		},
	{ "OOBINLINE",			SOCK_STREAM,	SOL_SOCKET,		SO_OOBINLINE		},
72

73
#ifdef SO_ACCEPTCONN											
74
	{ "ACCEPTCONN",			SOCK_STREAM,	SOL_SOCKET,		SO_ACCEPTCONN		},
75
#endif
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#ifdef SO_PRIORITY		/* Linux */
	{ "PRIORITY",			0,				SOL_SOCKET,		SO_PRIORITY			},
#endif
#ifdef SO_NO_CHECK		/* Linux */
	{ "NO_CHECK",			0,				SOL_SOCKET,		SO_NO_CHECK			},
#endif
#ifdef SO_PROTOTYPE		/* SunOS */
	{ "PROTOTYPE",			0,				SOL_SOCKET,		SO_PROTOTYPE		},
#endif
#ifdef SO_MAX_MSG_SIZE	/* WinSock2 */
	{ "MAX_MSG_SIZE",		SOCK_DGRAM,		SOL_SOCKET,		SO_MAX_MSG_SIZE		},
#endif
#ifdef SO_CONNECT_TIME	/* WinSock2 */
	{ "CONNECT_TIME",		SOCK_STREAM,	SOL_SOCKET,		SO_CONNECT_TIME		},
#endif
91
92

	/* IPPROTO-level socket options */
93
	{ "TCP_NODELAY",		SOCK_STREAM,	IPPROTO_TCP,	TCP_NODELAY			},
94
95
	/* The following are platform-specific */					
#ifdef TCP_MAXSEG											
96
	{ "TCP_MAXSEG",			SOCK_STREAM,	IPPROTO_TCP,	TCP_MAXSEG			},
97
98
#endif															
#ifdef TCP_CORK													
99
	{ "TCP_CORK",			SOCK_STREAM,	IPPROTO_TCP,	TCP_CORK			},
100
101
#endif															
#ifdef TCP_KEEPIDLE												
102
	{ "TCP_KEEPIDLE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPIDLE		},
103
104
#endif															
#ifdef TCP_KEEPINTVL											
105
	{ "TCP_KEEPINTVL",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPINTVL		},
106
107
#endif															
#ifdef TCP_KEEPCNT												
108
	{ "TCP_KEEPCNT",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPCNT			},
109
#endif															
110
111
112
#ifdef TCP_KEEPALIVE	/* SunOS */
	{ "TCP_KEEPALIVE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPALIVE		},
#endif															
113
#ifdef TCP_SYNCNT												
114
	{ "TCP_SYNCNT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_SYNCNT			},
115
116
#endif															
#ifdef TCP_LINGER2												
117
	{ "TCP_LINGER2",		SOCK_STREAM,	IPPROTO_TCP,	TCP_LINGER2			},
118
119
#endif														
#ifdef TCP_DEFER_ACCEPT										
120
	{ "TCP_DEFER_ACCEPT",	SOCK_STREAM,	IPPROTO_TCP,	TCP_DEFER_ACCEPT	},
121
122
#endif															
#ifdef TCP_WINDOW_CLAMP											
123
	{ "TCP_WINDOW_CLAMP",	SOCK_STREAM,	IPPROTO_TCP,	TCP_WINDOW_CLAMP	},
124
125
#endif														
#ifdef TCP_QUICKACK											
126
	{ "TCP_QUICKACK",		SOCK_STREAM,	IPPROTO_TCP,	TCP_QUICKACK		},
127
128
#endif						
#ifdef TCP_NOPUSH			
129
	{ "TCP_NOPUSH",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOPUSH			},
130
131
#endif						
#ifdef TCP_NOOPT			
132
	{ "TCP_NOOPT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOOPT			},
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#endif
	{ NULL }
};

int getSocketOptionByName(const char* name, int* level)
{
	int i;

	if(level!=NULL)
		*level=SOL_SOCKET;	/* default option level */
	for(i=0;socket_options[i].name;i++) {
		if(stricmp(name,socket_options[i].name)==0) {
			if(level!=NULL)
				*level = socket_options[i].level;
			return(socket_options[i].value);
		}
	}
	if(!isdigit(*name))	/* unknown option name */
		return(-1);
	return(strtol(name,NULL,0));
}

socket_option_t* getSocketOptionList(void)
{
	return(socket_options);
}

160
161
int sendfilesocket(int sock, int file, long *offset, long count)
{
162
	char	buf[1024*16];
163
164
	long	len;
	int		rd;
165
166
	int		wr=0;
	int		total=0;
167
	int		i;
168

169
170
171
/* sendfile() on Linux may or may not work with non-blocking sockets ToDo */
	len=filelength(file);

172
173
174
175
	if(offset!=NULL)
		if(lseek(file,*offset,SEEK_SET)<0)
			return(-1);

176
	if(count<1 || count>len) {
177
		count=len;
178
179
		count-=tell(file);		/* don't try to read beyond EOF */
	}
180
181
182
183
184
185
186
187
#if USE_SENDFILE
	while((i=sendfile(file,sock,(offset==NULL?0:*offset)+total,count-total,NULL,&wr,0))==-1 && errno==EAGAIN)  {
		total+=wr;
		SLEEP(1);
	}
	if(i==0)
		return((int)count);
#endif
188

189
190
191
192
193
194
	if(count<0) {
		errno=EINVAL;
		return(-1);
	}

	while(total<count) {
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
		rd=read(file,buf,sizeof(buf));
		if(rd==-1)
			return(-1);
		if(rd==0)
			break;
		for(i=wr=0;i<rd;i+=wr) {
			wr=sendsocket(sock,buf+i,rd-i);
			if(wr>0)
				continue;
			if(wr==SOCKET_ERROR && ERROR_VALUE==EWOULDBLOCK) {
				wr=0;
				SLEEP(1);
				continue;
			}
			return(wr);
210
		}
211
212
213
		if(i!=rd)
			return(-1);
		total+=rd;
214
215
	}

216
	if(offset!=NULL)
217
218
219
		(*offset)+=total;

	return(total);
220
221
}

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
int recvfilesocket(int sock, int file, long *offset, long count)
{
	/* Writes a file from a socket -
	 *
	 * sock		- Socket to read from
	 * file		- File descriptior to write to
	 *				MUST be open and writeable
	 * offset	- pointer to file offset to start writing at
	 *				is set to offset writing STOPPED
	 *				on return
	 * count	- number of bytes to read/write
	 *
	 * returns -1 if an error occurse, otherwise
	 * returns number ob bytes written and sets offset
	 * to the new offset
	 */
	 
	char*	buf;
	int		rd;
	int		wr;

	if(count<1) {
		errno=ERANGE;
		return(-1);
	}
		
248
	if((buf=(char*)alloca(count))==NULL) {
249
250
251
252
253
254
255
256
257
		errno=ENOMEM;
		return(-1);
	}

	if(offset!=NULL)
		if(lseek(file,*offset,SEEK_SET)<0)
			return(-1);

	rd=read(sock,buf,count);
258
	if(rd!=count)
259
260
261
262
263
264
265
266
267
268
269
		return(-1);

	wr=write(file,buf,rd);

	if(offset!=NULL)
		(*offset)+=wr;

	return(wr);
}


270
/* Return true if connected, optionally sets *rd_p to true if read data available */
271
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
272
273
274
{
	char	ch;
	int		i,rd;
275
	fd_set	rd_set;
276
	fd_set*	rd_set_p=&rd_set;
277
278
	fd_set	wr_set;
	fd_set*	wr_set_p=NULL;
279
280
281
282
283
	struct	timeval tv;

	if(rd_p!=NULL)
		*rd_p=FALSE;

284
285
286
	if(wr_p!=NULL)
		*wr_p=FALSE;

287
288
289
	if(sock==INVALID_SOCKET)
		return(FALSE);

290
291
292
293
294
295
	FD_ZERO(&rd_set);
	FD_SET(sock,&rd_set);
	if(wr_p!=NULL) {
		wr_set_p=&wr_set;
		FD_ZERO(wr_set_p);
		FD_SET(sock,wr_set_p);
296
		if(rd_p==NULL)
297
			rd_set_p=NULL;
298
	}
299

300
301
302
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
303

304
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
305
306
307
308
309
310
	if(i==SOCKET_ERROR)
		return(FALSE);

	if(i==0) 
		return(TRUE);

311
312
313
314
315
316
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

317
	if(rd_p !=NULL || wr_p==NULL)  {
318
319
320
321
322
323
324
		rd=recv(sock,&ch,1,MSG_PEEK);
		if(rd==1 
			|| (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE)) {
			if(rd_p!=NULL)
				*rd_p=TRUE;
			return(TRUE);
		}
325
326
327
328
	}

	return(FALSE);
}
deuce's avatar
deuce committed
329

330
331
332
333
int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
			   ,uint retries, uint wait_secs
			   ,const char* prot
			   ,int (*lprintf)(int level, char *fmt, ...))
deuce's avatar
deuce committed
334
{
335
	char	port_str[128];
deuce's avatar
deuce committed
336
	int		result=-1;
337
	uint	i;
deuce's avatar
deuce committed
338

339
	if(addr->sa_family==AF_INET)
340
		SAFEPRINTF(port_str," to port %u",ntohs(((SOCKADDR_IN *)(addr))->sin_port)); 
341
342
	else
		port_str[0]=0;
deuce's avatar
deuce committed
343
	for(i=0;i<=retries;i++) {
344
		if((result=bind(s,addr,addrlen))==0)
deuce's avatar
deuce committed
345
			break;
346
347
348
349
350
		if(lprintf!=NULL)
			lprintf(i<retries ? LOG_WARNING:LOG_ERR
				,"%04d !ERROR %d binding %s socket%s", s, ERROR_VALUE, prot, port_str);
		if(i<retries) {
			if(lprintf!=NULL)
351
352
				lprintf(LOG_WARNING,"%04d Will retry in %u seconds (%u of %u)"
					,s, wait_secs, i+1, retries);
353
354
			SLEEP(wait_secs*1000);
		}
deuce's avatar
deuce committed
355
356
357
	}
	return(result);
}