sockwrap.c 8.38 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 2004 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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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
static socket_option_t socket_options[] = {
	{ "TYPE",				SOL_SOCKET,		SO_TYPE				},
	{ "DEBUG",				SOL_SOCKET,		SO_DEBUG			},
	{ "LINGER",				SOL_SOCKET,		SO_LINGER			},
	{ "SNDBUF",				SOL_SOCKET,		SO_SNDBUF			},
	{ "RCVBUF",				SOL_SOCKET,		SO_RCVBUF			},
	{ "SNDLOWAT",			SOL_SOCKET,		SO_SNDLOWAT			},
	{ "RCVLOWAT",			SOL_SOCKET,		SO_RCVLOWAT			},
	{ "SNDTIMEO",			SOL_SOCKET,		SO_SNDTIMEO			},
	{ "RCVTIMEO",			SOL_SOCKET,		SO_RCVTIMEO			},
	{ "REUSEADDR",			SOL_SOCKET,		SO_REUSEADDR		},	
	{ "KEEPALIVE",			SOL_SOCKET,		SO_KEEPALIVE		},
	{ "DONTROUTE",			SOL_SOCKET,		SO_DONTROUTE		},
	{ "BROADCAST",			SOL_SOCKET,		SO_BROADCAST		},
	{ "OOBINLINE",			SOL_SOCKET,		SO_OOBINLINE		},
#ifdef SO_ACCEPTCONN											
	{ "ACCEPTCONN",			SOL_SOCKET,		SO_ACCEPTCONN		},
#endif

	/* IPPROTO-level socket options */
	{ "TCP_NODELAY",		IPPROTO_TCP,	TCP_NODELAY			},
	/* The following are platform-specific */					
#ifdef TCP_MAXSEG											
	{ "TCP_MAXSEG",			IPPROTO_TCP,	TCP_MAXSEG			},
#endif															
#ifdef TCP_CORK													
	{ "TCP_CORK",			IPPROTO_TCP,	TCP_CORK			},
#endif															
#ifdef TCP_KEEPIDLE												
	{ "TCP_KEEPIDLE",		IPPROTO_TCP,	TCP_KEEPIDLE		},
#endif															
#ifdef TCP_KEEPINTVL											
	{ "TCP_KEEPINTVL",		IPPROTO_TCP,	TCP_KEEPINTVL		},
#endif															
#ifdef TCP_KEEPCNT												
	{ "TCP_KEEPCNT",		IPPROTO_TCP,	TCP_KEEPCNT			},
#endif															
#ifdef TCP_SYNCNT												
	{ "TCP_SYNCNT",			IPPROTO_TCP,	TCP_SYNCNT			},
#endif															
#ifdef TCP_LINGER2												
	{ "TCP_LINGER2",		IPPROTO_TCP,	TCP_LINGER2			},
#endif														
#ifdef TCP_DEFER_ACCEPT										
	{ "TCP_DEFER_ACCEPT",	IPPROTO_TCP,	TCP_DEFER_ACCEPT	},
#endif															
#ifdef TCP_WINDOW_CLAMP											
	{ "TCP_WINDOW_CLAMP",	IPPROTO_TCP,	TCP_WINDOW_CLAMP	},
#endif														
#ifdef TCP_QUICKACK											
	{ "TCP_QUICKACK",		IPPROTO_TCP,	TCP_QUICKACK		},
#endif						
#ifdef TCP_NOPUSH			
	{ "TCP_NOPUSH",			IPPROTO_TCP,	TCP_NOPUSH			},
#endif						
#ifdef TCP_NOOPT			
	{ "TCP_NOOPT",			IPPROTO_TCP,	TCP_NOOPT			},
#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);
}

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

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

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

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

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

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

189
	if(offset!=NULL)
190
191
192
		(*offset)+=total;

	return(total);
193
194
}

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
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);
	}
		
221
	if((buf=(char*)malloc(count))==NULL) {
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
		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);
}


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

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

260
261
262
	if(wr_p!=NULL)
		*wr_p=FALSE;

263
264
265
	if(sock==INVALID_SOCKET)
		return(FALSE);

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

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

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

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

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

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

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

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

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