sockwrap.c 8.7 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>		/* malloc/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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
	{ "TYPE",				0,				SOL_SOCKET,		SO_TYPE				},
	{ "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			},
#ifndef _WINSOCKAPI_
	{ "SNDLOWAT",			0,				SOL_SOCKET,		SO_SNDLOWAT			},
	{ "RCVLOWAT",			0,				SOL_SOCKET,		SO_RCVLOWAT			},
	{ "SNDTIMEO",			0,				SOL_SOCKET,		SO_SNDTIMEO			},
	{ "RCVTIMEO",			0,				SOL_SOCKET,		SO_RCVTIMEO			},
#endif
	{ "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		},
66
#ifdef SO_ACCEPTCONN											
67
	{ "ACCEPTCONN",			SOCK_STREAM,	SOL_SOCKET,		SO_ACCEPTCONN		},
68
69
70
#endif

	/* IPPROTO-level socket options */
71
	{ "TCP_NODELAY",		SOCK_STREAM,	IPPROTO_TCP,	TCP_NODELAY			},
72
73
	/* The following are platform-specific */					
#ifdef TCP_MAXSEG											
74
	{ "TCP_MAXSEG",			SOCK_STREAM,	IPPROTO_TCP,	TCP_MAXSEG			},
75
76
#endif															
#ifdef TCP_CORK													
77
	{ "TCP_CORK",			SOCK_STREAM,	IPPROTO_TCP,	TCP_CORK			},
78
79
#endif															
#ifdef TCP_KEEPIDLE												
80
	{ "TCP_KEEPIDLE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPIDLE		},
81
82
#endif															
#ifdef TCP_KEEPINTVL											
83
	{ "TCP_KEEPINTVL",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPINTVL		},
84
85
#endif															
#ifdef TCP_KEEPCNT												
86
	{ "TCP_KEEPCNT",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPCNT			},
87
88
#endif															
#ifdef TCP_SYNCNT												
89
	{ "TCP_SYNCNT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_SYNCNT			},
90
91
#endif															
#ifdef TCP_LINGER2												
92
	{ "TCP_LINGER2",		SOCK_STREAM,	IPPROTO_TCP,	TCP_LINGER2			},
93
94
#endif														
#ifdef TCP_DEFER_ACCEPT										
95
	{ "TCP_DEFER_ACCEPT",	SOCK_STREAM,	IPPROTO_TCP,	TCP_DEFER_ACCEPT	},
96
97
#endif															
#ifdef TCP_WINDOW_CLAMP											
98
	{ "TCP_WINDOW_CLAMP",	SOCK_STREAM,	IPPROTO_TCP,	TCP_WINDOW_CLAMP	},
99
100
#endif														
#ifdef TCP_QUICKACK											
101
	{ "TCP_QUICKACK",		SOCK_STREAM,	IPPROTO_TCP,	TCP_QUICKACK		},
102
103
#endif						
#ifdef TCP_NOPUSH			
104
	{ "TCP_NOPUSH",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOPUSH			},
105
106
#endif						
#ifdef TCP_NOOPT			
107
	{ "TCP_NOOPT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOOPT			},
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#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);
}

135
136
int sendfilesocket(int sock, int file, long *offset, long count)
{
137
	char	buf[1024*16];
138
139
	long	len;
	int		rd;
140
141
	int		wr=0;
	int		total=0;
142
	int		i;
143

144
145
146
/* sendfile() on Linux may or may not work with non-blocking sockets ToDo */
	len=filelength(file);

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

151
	if(count<1 || count>len) {
152
		count=len;
153
154
		count-=tell(file);		/* don't try to read beyond EOF */
	}
155
156
157
158
159
160
161
162
#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
163

164
165
166
167
168
169
	if(count<0) {
		errno=EINVAL;
		return(-1);
	}

	while(total<count) {
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
		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);
185
		}
186
187
188
		if(i!=rd)
			return(-1);
		total+=rd;
189
190
	}

191
	if(offset!=NULL)
192
193
194
		(*offset)+=total;

	return(total);
195
196
}

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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);
	}
		
223
	if((buf=(char*)malloc(count))==NULL) {
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
		errno=ENOMEM;
		return(-1);
	}

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

	rd=read(sock,buf,count);
	if(rd!=count) {
		free(buf);
		return(-1);
	}

	wr=write(file,buf,rd);
	free(buf);

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

	return(wr);
}


248
/* Return true if connected, optionally sets *rd_p to true if read data available */
249
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
250
251
252
{
	char	ch;
	int		i,rd;
253
	fd_set	rd_set;
254
	fd_set*	rd_set_p=&rd_set;
255
256
	fd_set	wr_set;
	fd_set*	wr_set_p=NULL;
257
258
259
260
261
	struct	timeval tv;

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

262
263
264
	if(wr_p!=NULL)
		*wr_p=FALSE;

265
266
267
	if(sock==INVALID_SOCKET)
		return(FALSE);

268
269
270
271
272
273
	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);
274
		if(rd_p==NULL)
275
			rd_set_p=NULL;
276
	}
277

278
279
280
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
281

282
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
283
284
285
286
287
288
	if(i==SOCKET_ERROR)
		return(FALSE);

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

289
290
291
292
293
294
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

295
	if(rd_p !=NULL || wr_p==NULL)  {
296
297
298
299
300
301
302
		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);
		}
303
304
305
306
	}

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

308
309
310
311
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
312
{
313
	char	port_str[128];
deuce's avatar
deuce committed
314
	int		result=-1;
315
	uint	i;
deuce's avatar
deuce committed
316

317
	if(addr->sa_family==AF_INET)
318
		SAFEPRINTF(port_str," to port %u",ntohs(((SOCKADDR_IN *)(addr))->sin_port)); 
319
320
	else
		port_str[0]=0;
deuce's avatar
deuce committed
321
	for(i=0;i<=retries;i++) {
322
		if((result=bind(s,addr,addrlen))==0)
deuce's avatar
deuce committed
323
			break;
324
325
326
327
328
		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)
329
330
				lprintf(LOG_WARNING,"%04d Will retry in %u seconds (%u of %u)"
					,s, wait_secs, i+1, retries);
331
332
			SLEEP(wait_secs*1000);
		}
deuce's avatar
deuce committed
333
334
335
	}
	return(result);
}