sockwrap.c 14.8 KB
Newer Older
1
2
3
4
/* sockwrap.c */

/* Berkley/WinSock socket API wrappers */

5
/* $Id: sockwrap.c,v 1.74 2020/08/09 02:13:57 rswindell Exp $ */
6
7
8
9
10

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 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.	*
 ****************************************************************************/

deuce's avatar
deuce committed
38
#include <ctype.h>		/* isdigit */
39
#include <stdlib.h>		/* alloca/free on FreeBSD */
40
#include <string.h>		/* bzero (for FD_ZERO) on FreeBSD */
41
#include <errno.h>		/* ENOMEM */
42
#include <stdio.h>		/* SEEK_SET */
43
#include <string.h>
44
45
46
#if defined(_WIN32)
 #include <malloc.h>	/* alloca() on Win32 */
#endif
47

48
#include "genwrap.h"	/* SLEEP */
deuce's avatar
deuce committed
49
#include "gen_defs.h"	/* BOOL/LOG_WARNING */
50
51
52
#include "sockwrap.h"	/* sendsocket */
#include "filewrap.h"	/* filelength */

53
static socket_option_t socket_options[] = {
54
	{ "TYPE",				0,				SOL_SOCKET,		SO_TYPE				},
55
	{ "ERROR",				0,				SOL_SOCKET,		SO_ERROR			},
56
57
58
59
	{ "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			},
60
61

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

71
	{ "REUSEADDR",			0,				SOL_SOCKET,		SO_REUSEADDR		},	
deuce's avatar
deuce committed
72
73
#ifdef SO_REUSEPORT	/* BSD */
	{ "REUSEPORT",			0,				SOL_SOCKET,		SO_REUSEPORT		},	
74
75
76
#endif
#ifdef SO_EXCLUSIVEADDRUSE /* WinSock */
	{ "EXCLUSIVEADDRUSE",	0,				SOL_SOCKET,		SO_EXCLUSIVEADDRUSE },
deuce's avatar
deuce committed
77
#endif
78
79
80
81
	{ "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		},
82

83
#ifdef SO_ACCEPTCONN											
84
	{ "ACCEPTCONN",			SOCK_STREAM,	SOL_SOCKET,		SO_ACCEPTCONN		},
85
#endif
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#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
101
102

	/* IPPROTO-level socket options */
103
	{ "TCP_NODELAY",		SOCK_STREAM,	IPPROTO_TCP,	TCP_NODELAY			},
104
105
	/* The following are platform-specific */					
#ifdef TCP_MAXSEG											
106
	{ "TCP_MAXSEG",			SOCK_STREAM,	IPPROTO_TCP,	TCP_MAXSEG			},
107
108
#endif															
#ifdef TCP_CORK													
109
	{ "TCP_CORK",			SOCK_STREAM,	IPPROTO_TCP,	TCP_CORK			},
110
111
#endif															
#ifdef TCP_KEEPIDLE												
112
	{ "TCP_KEEPIDLE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPIDLE		},
113
114
#endif															
#ifdef TCP_KEEPINTVL											
115
	{ "TCP_KEEPINTVL",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPINTVL		},
116
117
#endif															
#ifdef TCP_KEEPCNT												
118
	{ "TCP_KEEPCNT",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPCNT			},
119
#endif															
120
121
122
#ifdef TCP_KEEPALIVE	/* SunOS */
	{ "TCP_KEEPALIVE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPALIVE		},
#endif															
123
#ifdef TCP_SYNCNT												
124
	{ "TCP_SYNCNT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_SYNCNT			},
125
126
#endif															
#ifdef TCP_LINGER2												
127
	{ "TCP_LINGER2",		SOCK_STREAM,	IPPROTO_TCP,	TCP_LINGER2			},
128
129
#endif														
#ifdef TCP_DEFER_ACCEPT										
130
	{ "TCP_DEFER_ACCEPT",	SOCK_STREAM,	IPPROTO_TCP,	TCP_DEFER_ACCEPT	},
131
132
#endif															
#ifdef TCP_WINDOW_CLAMP											
133
	{ "TCP_WINDOW_CLAMP",	SOCK_STREAM,	IPPROTO_TCP,	TCP_WINDOW_CLAMP	},
134
135
#endif														
#ifdef TCP_QUICKACK											
136
	{ "TCP_QUICKACK",		SOCK_STREAM,	IPPROTO_TCP,	TCP_QUICKACK		},
137
138
#endif						
#ifdef TCP_NOPUSH			
139
	{ "TCP_NOPUSH",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOPUSH			},
140
141
#endif						
#ifdef TCP_NOOPT			
142
	{ "TCP_NOOPT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOOPT			},
143
144
145
#endif
#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6)
	{ "IPV6_V6ONLY",		0,				IPPROTO_IPV6,	IPV6_V6ONLY			},
146
147
148
149
#endif
	{ NULL }
};

150
int getSocketOptionByName(const char* name, int* level)
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
{
	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));
}

168
socket_option_t* getSocketOptionList(void)
169
170
171
172
{
	return(socket_options);
}

173
off_t sendfilesocket(int sock, int file, off_t *offset, off_t count)
174
{
175
	char		buf[1024*16];
176
	off_t		len;
177
178
179
180
	ssize_t		rd;
	ssize_t		wr=0;
	off_t		total=0;
	ssize_t		i;
181

182
183
184
/* sendfile() on Linux may or may not work with non-blocking sockets ToDo */
	len=filelength(file);

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

189
	if(count<1 || count>len) {
190
		count=len;
191
192
		count-=tell(file);		/* don't try to read beyond EOF */
	}
193
194
195
196
197
198
#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)
199
		return(count);
200
#endif
201

202
203
204
205
206
207
	if(count<0) {
		errno=EINVAL;
		return(-1);
	}

	while(total<count) {
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
		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);
223
		}
224
225
226
		if(i!=rd)
			return(-1);
		total+=rd;
227
228
	}

229
	if(offset!=NULL)
230
231
232
		(*offset)+=total;

	return(total);
233
234
}

235
off_t recvfilesocket(int sock, int file, off_t *offset, off_t count)
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
{
	/* 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;
253
254
	ssize_t	rd;
	ssize_t	wr;
255
256
257
258
259
260

	if(count<1) {
		errno=ERANGE;
		return(-1);
	}
		
261
	if((buf=(char*)malloc((size_t)count))==NULL) {
262
263
264
265
		errno=ENOMEM;
		return(-1);
	}

266
267
268
	if(offset!=NULL) {
		if(lseek(file,*offset,SEEK_SET)<0) {
			free(buf);
269
			return(-1);
270
271
		}
	}
272

273
	rd=read(sock,buf,(size_t)count);
274
275
	if(rd!=count) {
		free(buf);
276
		return(-1);
277
	}
278
279
280
281
282
283

	wr=write(file,buf,rd);

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

284
	free(buf);
285
286
287
288
	return(wr);
}


289
/* Return true if connected, optionally sets *rd_p to true if read data available */
290
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
291
292
293
{
	char	ch;
	int		i,rd;
294
	fd_set	rd_set;
295
	fd_set*	rd_set_p=&rd_set;
296
297
	fd_set	wr_set;
	fd_set*	wr_set_p=NULL;
298
299
300
301
302
	struct	timeval tv;

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

303
304
305
	if(wr_p!=NULL)
		*wr_p=FALSE;

306
307
308
	if(sock==INVALID_SOCKET)
		return(FALSE);

309
310
311
312
313
314
	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);
315
		if(rd_p==NULL)
316
			rd_set_p=NULL;
317
	}
318

319
320
321
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
322

323
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
324
325
326
327
328
329
	if(i==SOCKET_ERROR)
		return(FALSE);

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

330
331
332
333
334
335
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

336
	if(rd_p !=NULL || wr_p==NULL)  {
337
338
339
340
341
342
343
		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);
		}
344
345
346
347
	}

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

349
int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
350
351
			   ,uint retries, uint wait_secs
			   ,const char* prot
352
			   ,int (*lprintf)(int level, const char *fmt, ...))
deuce's avatar
deuce committed
353
{
354
	char	port_str[128];
355
	char	err[256];
deuce's avatar
deuce committed
356
	int		result=-1;
357
	uint	i;
deuce's avatar
deuce committed
358

359
	if(addr->sa_family==AF_INET)
deuce's avatar
deuce committed
360
		SAFEPRINTF(port_str," to port %u",ntohs(((SOCKADDR_IN *)(addr))->sin_port));
361
362
	else
		port_str[0]=0;
deuce's avatar
deuce committed
363
	for(i=0;i<=retries;i++) {
364
		if((result=bind(s,addr,addrlen))==0)
deuce's avatar
deuce committed
365
			break;
366
		if(lprintf!=NULL)
367
			lprintf(i<retries ? LOG_WARNING:LOG_CRIT
368
				,"%04d !ERROR %d binding %s socket%s: %s", s, ERROR_VALUE, prot, port_str, socket_strerror(socket_errno, err, sizeof(err)));
369
370
		if(i<retries) {
			if(lprintf!=NULL)
371
372
				lprintf(LOG_WARNING,"%04d Will retry in %u seconds (%u of %u)"
					,s, wait_secs, i+1, retries);
373
374
			SLEEP(wait_secs*1000);
		}
deuce's avatar
deuce committed
375
376
377
	}
	return(result);
}
378

379
int nonblocking_connect(SOCKET sock, struct sockaddr* addr, size_t size, unsigned timeout)
380
381
382
383
384
{
	int result;

	result=connect(sock, addr, size);

385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
	if(result==SOCKET_ERROR) {
		result=ERROR_VALUE;
		if(result==EWOULDBLOCK || result==EINPROGRESS) {
			fd_set		wsocket_set;
			fd_set		esocket_set;
			struct		timeval tv;
			socklen_t	optlen=sizeof(result);
			tv.tv_sec = timeout;
			tv.tv_usec = 0;
			FD_ZERO(&wsocket_set);
			FD_SET(sock,&wsocket_set);
			FD_ZERO(&esocket_set);
			FD_SET(sock,&esocket_set);
			switch(select(sock+1,NULL,&wsocket_set,&esocket_set,&tv)) {
				case 1:
					if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&result, &optlen)==SOCKET_ERROR)
						result=ERROR_VALUE;
					break;
				case 0:
					break;
				case SOCKET_ERROR:
					result=ERROR_VALUE;
					break;
			}
		}
410
411
412
	}
	return result;
}
413

deuce's avatar
deuce committed
414

415
union xp_sockaddr* inet_ptoaddr(char *addr_str, union xp_sockaddr *addr, size_t size)
deuce's avatar
deuce committed
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
{
    struct addrinfo hints = {0};
    struct addrinfo *res, *cur;

    hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
    if(getaddrinfo(addr_str, NULL, &hints, &res))
        return NULL;
    
    for(cur = res; cur; cur++) {
        if(cur->ai_addr->sa_family == AF_INET6)
            break;
    }
    if(!cur) {
        freeaddrinfo(res);
        return NULL;
    }
432
433
434
435
436
437
    if (size < sizeof(struct sockaddr_in6)) {
        freeaddrinfo(res);
        return NULL;
	}
	size = sizeof(struct sockaddr_in6);
    memcpy(addr, ((struct sockaddr_in6 *)(cur->ai_addr)), size);
deuce's avatar
deuce committed
438
439
440
441
    freeaddrinfo(res);
    return addr;
}

442
const char* inet_addrtop(union xp_sockaddr *addr, char *dest, size_t size)
443
{
444
#ifdef _WIN32
deuce's avatar
deuce committed
445
	if(getnameinfo(&addr->addr, xp_sockaddr_len(addr), dest, size, NULL, 0, NI_NUMERICHOST))
446
		safe_snprintf(dest, size, "<Error %u converting address, family=%u>", WSAGetLastError(), addr->addr.sa_family);
447
448
	return dest;
#else
449
	switch(addr->addr.sa_family) {
450
		case AF_INET:
451
			return inet_ntop(addr->in.sin_family, &addr->in.sin_addr, dest, size);
452
		case AF_INET6:
453
			return inet_ntop(addr->in6.sin6_family, &addr->in6.sin6_addr, dest, size);
454
455
456
457
		case AF_UNIX:
			strncpy(dest, addr->un.sun_path, size);
			dest[size-1]=0;
			return dest;
458
		default:
459
			safe_snprintf(dest, size, "<unknown address family: %u>", addr->addr.sa_family);
460
461
			return NULL;
	}
462
#endif
463
464
}

465
uint16_t inet_addrport(union xp_sockaddr *addr)
466
{
467
	switch(addr->addr.sa_family) {
468
		case AF_INET:
469
			return ntohs(addr->in.sin_port);
470
		case AF_INET6:
471
			return ntohs(addr->in6.sin6_port);
472
473
474
475
		default:
			return 0;
	}
}
deuce's avatar
deuce committed
476

477
void inet_setaddrport(union xp_sockaddr *addr, uint16_t port)
deuce's avatar
deuce committed
478
479
480
481
482
483
484
485
486
487
{
	switch(addr->addr.sa_family) {
		case AF_INET:
			addr->in.sin_port = htons(port);
			break;
		case AF_INET6:
			addr->in6.sin6_port = htons(port);
			break;
	}
}
488
489

/* Return TRUE if the 2 addresses are the same host (type and address) */
490
BOOL inet_addrmatch(union xp_sockaddr* addr1, union xp_sockaddr* addr2)
491
492
493
494
495
496
497
498
499
500
501
502
{
	if(addr1->addr.sa_family != addr2->addr.sa_family)
		return FALSE;

	switch(addr1->addr.sa_family) {
		case AF_INET:
			return memcmp(&addr1->in.sin_addr, &addr2->in.sin_addr, sizeof(addr1->in.sin_addr)) == 0;
		case AF_INET6:
			return memcmp(&addr1->in6.sin6_addr, &addr2->in6.sin6_addr, sizeof(addr1->in6.sin6_addr)) == 0;
	}
	return FALSE;
}
503
504

/* Return the current socket error description (for Windows), like strerror() does for errno */
505
DLLEXPORT char* socket_strerror(int error_number, char* buf, size_t buflen)
506
{
507
#if defined(_WINSOCKAPI_)
508
509
	strncpy(buf, "Unknown error", buflen);
	buf[buflen - 1] = 0;
510
511
	if(error_number > 0 && error_number < WSABASEERR)
		error_number += WSABASEERR;
512
	if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,	// dwFlags
513
514
515
		NULL,			// lpSource
		error_number,	// dwMessageId
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),    // dwLanguageId
516
517
		buf,
		buflen,
518
519
		NULL))
		safe_snprintf(buf, buflen, "Error %d getting error description", GetLastError());
520
521
522
523
	truncsp(buf);
	return buf;
#else
	return safe_strerror(error_number, buf, buflen);
524
#endif
525
}
Deucе's avatar
Deucе committed
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561

#ifdef _WIN32
DLLEXPORT int inet_pton(int af, const char *src, void *dst)
{
	struct addrinfo hints = {0};
	struct addrinfo *res, *cur;

	if (af != AF_INET && af != AF_INET6) {
		// TODO: Should set socket_errno to EAFNOSUPPORT
		return -1;
	}

	hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
	if(getaddrinfo(addr_str, NULL, &hints, &res))
		return -1;

	for(cur = res; cur; cur++) {
		if(cur->ai_addr->sa_family == af)
			break;
	}
	if(!cur) {
		freeaddrinfo(res);
		return 0;
	}
	switch(af) {
		case AF_INET:
			memcpy(dst, ((struct sockaddr_in *)(cur->sin_addr)), sizeof((struct sockaddr_in *)cur->sin_addr));
			break;
		case AF_INET6:
			memcpy(dst, ((struct sockaddr_in6 *)(cur->sin6_addr)), sizeof((struct sockaddr_in6 *)cur->sin6_addr));
			break;
	}
	freeaddrinfo(res);
	return addr;
}
#endif