sockwrap.c 19.3 KB
Newer Older
1
2
3
4
5
6
/* Berkley/WinSock socket API wrappers */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8
 *																			*
9
10
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
11
12
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
13
14
 * See the GNU General Public License for more details: gpl.txt or			*
 * http://www.fsf.org/copyleft/gpl.html										*
15
16
17
18
19
20
21
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

22
#include <stdlib.h>		/* alloca/free on FreeBSD */
23
#include <string.h>		/* bzero (for FD_ZERO) on FreeBSD */
24
#include <errno.h>		/* ENOMEM */
25
#include <stdio.h>		/* SEEK_SET */
26
#include <string.h>
27
28
29
#if defined(_WIN32)
 #include <malloc.h>	/* alloca() on Win32 */
#endif
30

31
#include "genwrap.h"	/* SLEEP */
deuce's avatar
deuce committed
32
#include "gen_defs.h"	/* BOOL/LOG_WARNING */
33
34
35
#include "sockwrap.h"	/* sendsocket */
#include "filewrap.h"	/* filelength */

36
static socket_option_t socket_options[] = {
37
	{ "TYPE",				0,				SOL_SOCKET,		SO_TYPE				},
38
	{ "ERROR",				0,				SOL_SOCKET,		SO_ERROR			},
39
40
41
42
	{ "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			},
43
44

#ifndef _WINSOCKAPI_	/* Defined, but not supported, by WinSock */
45
46
47
48
	{ "SNDLOWAT",			0,				SOL_SOCKET,		SO_SNDLOWAT			},
	{ "RCVLOWAT",			0,				SOL_SOCKET,		SO_RCVLOWAT			},
	{ "SNDTIMEO",			0,				SOL_SOCKET,		SO_SNDTIMEO			},
	{ "RCVTIMEO",			0,				SOL_SOCKET,		SO_RCVTIMEO			},
49
50
51
#ifdef SO_USELOOPBACK	/* SunOS */
	{ "USELOOPBACK",		0,				SOL_SOCKET,		SO_USELOOPBACK		},
#endif
52
#endif
53

54
	{ "REUSEADDR",			0,				SOL_SOCKET,		SO_REUSEADDR		},	
deuce's avatar
deuce committed
55
56
#ifdef SO_REUSEPORT	/* BSD */
	{ "REUSEPORT",			0,				SOL_SOCKET,		SO_REUSEPORT		},	
57
58
59
#endif
#ifdef SO_EXCLUSIVEADDRUSE /* WinSock */
	{ "EXCLUSIVEADDRUSE",	0,				SOL_SOCKET,		SO_EXCLUSIVEADDRUSE },
deuce's avatar
deuce committed
60
#endif
61
62
63
64
	{ "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		},
65

66
#ifdef SO_ACCEPTCONN											
67
	{ "ACCEPTCONN",			SOCK_STREAM,	SOL_SOCKET,		SO_ACCEPTCONN		},
68
#endif
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#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
84
85

	/* IPPROTO-level socket options */
86
	{ "TCP_NODELAY",		SOCK_STREAM,	IPPROTO_TCP,	TCP_NODELAY			},
87
88
	/* The following are platform-specific */					
#ifdef TCP_MAXSEG											
89
	{ "TCP_MAXSEG",			SOCK_STREAM,	IPPROTO_TCP,	TCP_MAXSEG			},
90
91
#endif															
#ifdef TCP_CORK													
92
	{ "TCP_CORK",			SOCK_STREAM,	IPPROTO_TCP,	TCP_CORK			},
93
94
#endif															
#ifdef TCP_KEEPIDLE												
95
	{ "TCP_KEEPIDLE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPIDLE		},
96
97
#endif															
#ifdef TCP_KEEPINTVL											
98
	{ "TCP_KEEPINTVL",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPINTVL		},
99
100
#endif															
#ifdef TCP_KEEPCNT												
101
	{ "TCP_KEEPCNT",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPCNT			},
102
#endif															
103
104
105
#ifdef TCP_KEEPALIVE	/* SunOS */
	{ "TCP_KEEPALIVE",		SOCK_STREAM,	IPPROTO_TCP,	TCP_KEEPALIVE		},
#endif															
106
#ifdef TCP_SYNCNT												
107
	{ "TCP_SYNCNT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_SYNCNT			},
108
109
#endif															
#ifdef TCP_LINGER2												
110
	{ "TCP_LINGER2",		SOCK_STREAM,	IPPROTO_TCP,	TCP_LINGER2			},
111
112
#endif														
#ifdef TCP_DEFER_ACCEPT										
113
	{ "TCP_DEFER_ACCEPT",	SOCK_STREAM,	IPPROTO_TCP,	TCP_DEFER_ACCEPT	},
114
115
#endif															
#ifdef TCP_WINDOW_CLAMP											
116
	{ "TCP_WINDOW_CLAMP",	SOCK_STREAM,	IPPROTO_TCP,	TCP_WINDOW_CLAMP	},
117
118
#endif														
#ifdef TCP_QUICKACK											
119
	{ "TCP_QUICKACK",		SOCK_STREAM,	IPPROTO_TCP,	TCP_QUICKACK		},
120
121
#endif						
#ifdef TCP_NOPUSH			
122
	{ "TCP_NOPUSH",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOPUSH			},
123
124
#endif						
#ifdef TCP_NOOPT			
125
	{ "TCP_NOOPT",			SOCK_STREAM,	IPPROTO_TCP,	TCP_NOOPT			},
126
127
128
#endif
#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6)
	{ "IPV6_V6ONLY",		0,				IPPROTO_IPV6,	IPV6_V6ONLY			},
129
130
131
132
#endif
	{ NULL }
};

133
int getSocketOptionByName(const char* name, int* level)
134
135
136
137
138
139
140
141
142
143
144
145
{
	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);
		}
	}
146
	if(!IS_DIGIT(*name))	/* unknown option name */
147
148
149
150
		return(-1);
	return(strtol(name,NULL,0));
}

151
socket_option_t* getSocketOptionList(void)
152
153
154
155
{
	return(socket_options);
}

156
off_t sendfilesocket(int sock, int file, off_t *offset, off_t count)
157
{
158
	char		buf[1024*16];
159
	off_t		len;
160
161
162
163
	ssize_t		rd;
	ssize_t		wr=0;
	off_t		total=0;
	ssize_t		i;
164

165
166
167
/* sendfile() on Linux may or may not work with non-blocking sockets ToDo */
	len=filelength(file);

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

172
	if(count<1 || count>len) {
173
		count=len;
174
175
		count-=tell(file);		/* don't try to read beyond EOF */
	}
176
177
178
179
180
181
#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)
182
		return(count);
183
#endif
184

185
186
187
188
189
190
	if(count<0) {
		errno=EINVAL;
		return(-1);
	}

	while(total<count) {
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
		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);
206
		}
207
208
209
		if(i!=rd)
			return(-1);
		total+=rd;
210
211
	}

212
	if(offset!=NULL)
213
214
215
		(*offset)+=total;

	return(total);
216
217
}

218
off_t recvfilesocket(int sock, int file, off_t *offset, off_t count)
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
{
	/* 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;
236
237
	ssize_t	rd;
	ssize_t	wr;
238
239
240
241
242
243

	if(count<1) {
		errno=ERANGE;
		return(-1);
	}
		
244
	if((buf=(char*)malloc((size_t)count))==NULL) {
245
246
247
248
		errno=ENOMEM;
		return(-1);
	}

249
250
251
	if(offset!=NULL) {
		if(lseek(file,*offset,SEEK_SET)<0) {
			free(buf);
252
			return(-1);
253
254
		}
	}
255

256
	rd=read(sock,buf,(size_t)count);
257
258
	if(rd!=count) {
		free(buf);
259
		return(-1);
260
	}
261
262
263
264
265
266

	wr=write(file,buf,rd);

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

267
	free(buf);
268
269
270
271
	return(wr);
}


272
/* Return true if connected, optionally sets *rd_p to true if read data available */
273
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
274
{
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	int j, rd;
	char ch;

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

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

	if(sock==INVALID_SOCKET)
		return(FALSE);

	pfd.fd = sock;
	pfd.events = POLLIN | POLLHUP;
	if (wr_p != NULL)
		pfd.events |= POLLOUT;

	j = poll(&pfd, 1, timeout);

	if (j == 0)
		return TRUE;

	if (j == 1) {
		if (wr_p != NULL && (pfd.revents & POLLOUT))
			*wr_p = TRUE;
		if (rd_p != NULL && (pfd.revents & POLLIN))
			*rd_p = TRUE;

		if (pfd.revents & (POLLERR | POLLNVAL))
			return FALSE;

		if(pfd.revents & (POLLIN | POLLHUP) && (rd_p !=NULL || wr_p==NULL))  {
			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);
			}
			return FALSE;
		}
	}

	if (j == -1) {
		if (errno == EINTR || errno == ENOMEM)
			return TRUE;
		return FALSE;
	}

	return TRUE;
#else
327
328
	char	ch;
	int		i,rd;
329
	fd_set	rd_set;
330
	fd_set*	rd_set_p=&rd_set;
331
332
	fd_set	wr_set;
	fd_set*	wr_set_p=NULL;
333
334
335
336
337
	struct	timeval tv;

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

338
339
340
	if(wr_p!=NULL)
		*wr_p=FALSE;

341
342
343
	if(sock==INVALID_SOCKET)
		return(FALSE);

344
345
346
347
348
349
	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);
350
		if(rd_p==NULL)
351
			rd_set_p=NULL;
352
	}
353

354
355
356
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
357

358
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
359
360
361
362
363
364
	if(i==SOCKET_ERROR)
		return(FALSE);

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

365
366
367
368
369
370
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

371
	if(rd_p !=NULL || wr_p==NULL)  {
372
373
374
375
376
377
378
		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);
		}
379
380
381
	}

	return(FALSE);
382
#endif
383
}
deuce's avatar
deuce committed
384

385
386
387
388
389
390
391
392
393
/*
 * Return TRUE if recv() will not block on socket
 * Will block for timeout ms or forever if timeout is negative
 *
 * This means it will return true if recv() will return an error
 * as well as if the socket is closed (and recv() will return 0)
 */
BOOL socket_readable(SOCKET sock, int timeout)
{
394
395
396
397
398
399
400
401
402
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLIN;

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
	fd_set rd_set;
	struct timeval tv = {0};
	struct timeval *tvp = &tv;

	FD_ZERO(&rd_set);
	FD_SET(sock, &rd_set);
	if (timeout < 0)
		tvp = NULL;
	else {
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
	}

	switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
		case 0:		// Nothing to read
			return FALSE;
		case 1:
			return TRUE;
	}
	// Errors and unexpected cases
	return TRUE;
#endif
}

/*
 * Return TRUE if send() will not block on socket
 * Will block for timeout ms or forever if timeout is negative
 *
 * This means it will return true if send() will return an error
 * as well as if the socket is closed (and send() will return 0)
 */
BOOL socket_writable(SOCKET sock, int timeout)
{
436
437
438
439
440
441
442
443
444
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLOUT;

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
	fd_set wr_set;
	struct timeval tv = {0};
	struct timeval *tvp = &tv;

	FD_ZERO(&wr_set);
	FD_SET(sock, &wr_set);
	if (timeout < 0)
		tvp = NULL;
	else {
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
	}

	switch (select(sock+1, NULL, &wr_set, NULL, tvp)) {
		case 0:		// Nothing to read
			return FALSE;
		case 1:
			return TRUE;
	}
	// Errors and unexpected cases
	return TRUE;
#endif
}

/*
 * Return TRUE if recv() will not block and will return zero
 * or an error. This is *not* a test if a socket is
 * disconnected, but rather that it is disconnected *AND* all
 * data has been recv()ed.
 */
BOOL socket_recvdone(SOCKET sock, int timeout)
{
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLIN;
	char ch;
	int rd;

	switch (poll(&pfd, 1, timeout)) {
		case 1:
			if (pfd.revents & (POLLIN | POLLHUP)) {
				rd = recv(sock,&ch,1,MSG_PEEK);
				if (rd == 1 || (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE))
					return FALSE;
				return TRUE;
			}
			return FALSE;
		case -1:
			if (errno == EINTR || errno == ENOMEM)
				return FALSE;
			return TRUE;
	}
	return FALSE;
#else
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
	fd_set rd_set;
	struct timeval tv = {0};
	struct timeval *tvp = &tv;
	char ch;
	int rd;

	FD_ZERO(&rd_set);
	FD_SET(sock, &rd_set);
	if (timeout < 0)
		tvp = NULL;
	else {
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
	}

	switch (select(sock+1, &rd_set, NULL, NULL, tvp)) {
		case -1:	// Error, call this disconnected
			return TRUE;
		case 0:		// Nothing to read
			return FALSE;
	}
	rd = recv(sock,&ch,1,MSG_PEEK);
	if (rd == 1 || (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE))
		return FALSE;
	return TRUE;
#endif
}

528
int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
529
530
			   ,uint retries, uint wait_secs
			   ,const char* prot
531
			   ,int (*lprintf)(int level, const char *fmt, ...))
deuce's avatar
deuce committed
532
{
533
	char	port_str[128];
534
	char	err[256];
deuce's avatar
deuce committed
535
	int		result=-1;
536
	uint	i;
deuce's avatar
deuce committed
537

538
	if(addr->sa_family==AF_INET)
deuce's avatar
deuce committed
539
		SAFEPRINTF(port_str," to port %u",ntohs(((SOCKADDR_IN *)(addr))->sin_port));
540
541
	else
		port_str[0]=0;
deuce's avatar
deuce committed
542
	for(i=0;i<=retries;i++) {
543
		if((result=bind(s,addr,addrlen))==0)
deuce's avatar
deuce committed
544
			break;
545
		if(lprintf!=NULL)
546
			lprintf(i<retries ? LOG_WARNING:LOG_CRIT
547
				,"%04d !ERROR %d binding %s socket%s: %s", s, ERROR_VALUE, prot, port_str, socket_strerror(socket_errno, err, sizeof(err)));
548
549
		if(i<retries) {
			if(lprintf!=NULL)
550
551
				lprintf(LOG_WARNING,"%04d Will retry in %u seconds (%u of %u)"
					,s, wait_secs, i+1, retries);
552
553
			SLEEP(wait_secs*1000);
		}
deuce's avatar
deuce committed
554
555
556
	}
	return(result);
}
557

558
int nonblocking_connect(SOCKET sock, struct sockaddr* addr, size_t size, unsigned timeout)
559
560
{
	int result;
561
	socklen_t optlen;
562
563
564

	result=connect(sock, addr, size);

565
566
567
	if(result==SOCKET_ERROR) {
		result=ERROR_VALUE;
		if(result==EWOULDBLOCK || result==EINPROGRESS) {
568
569
570
571
			if (socket_writable(sock, timeout * 1000)) {
				result = 0;
			}
			else {
572
				optlen = sizeof(result);
573
				if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&result, &optlen)==SOCKET_ERROR)
574
575
576
					result=ERROR_VALUE;
			}
		}
577
578
579
	}
	return result;
}
580

deuce's avatar
deuce committed
581

582
union xp_sockaddr* inet_ptoaddr(char *addr_str, union xp_sockaddr *addr, size_t size)
deuce's avatar
deuce committed
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
{
    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;
    }
599
600
601
602
603
604
    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
605
606
607
608
    freeaddrinfo(res);
    return addr;
}

609
const char* inet_addrtop(union xp_sockaddr *addr, char *dest, size_t size)
610
{
611
#ifdef _WIN32
deuce's avatar
deuce committed
612
	if(getnameinfo(&addr->addr, xp_sockaddr_len(addr), dest, size, NULL, 0, NI_NUMERICHOST))
613
		safe_snprintf(dest, size, "<Error %u converting address, family=%u>", WSAGetLastError(), addr->addr.sa_family);
614
615
	return dest;
#else
616
	switch(addr->addr.sa_family) {
617
		case AF_INET:
618
			return inet_ntop(addr->in.sin_family, &addr->in.sin_addr, dest, size);
619
		case AF_INET6:
620
			return inet_ntop(addr->in6.sin6_family, &addr->in6.sin6_addr, dest, size);
621
622
623
624
		case AF_UNIX:
			strncpy(dest, addr->un.sun_path, size);
			dest[size-1]=0;
			return dest;
625
		default:
626
			safe_snprintf(dest, size, "<unknown address family: %u>", addr->addr.sa_family);
627
628
			return NULL;
	}
629
#endif
630
631
}

632
uint16_t inet_addrport(union xp_sockaddr *addr)
633
{
634
	switch(addr->addr.sa_family) {
635
		case AF_INET:
636
			return ntohs(addr->in.sin_port);
637
		case AF_INET6:
638
			return ntohs(addr->in6.sin6_port);
639
640
641
642
		default:
			return 0;
	}
}
deuce's avatar
deuce committed
643

644
void inet_setaddrport(union xp_sockaddr *addr, uint16_t port)
deuce's avatar
deuce committed
645
646
647
648
649
650
651
652
653
654
{
	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;
	}
}
655
656

/* Return TRUE if the 2 addresses are the same host (type and address) */
657
BOOL inet_addrmatch(union xp_sockaddr* addr1, union xp_sockaddr* addr2)
658
659
660
661
662
663
664
665
666
667
668
669
{
	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;
}
670
671

/* Return the current socket error description (for Windows), like strerror() does for errno */
672
DLLEXPORT char* socket_strerror(int error_number, char* buf, size_t buflen)
673
{
674
#if defined(_WINSOCKAPI_)
675
676
	strncpy(buf, "Unknown error", buflen);
	buf[buflen - 1] = 0;
677
678
	if(error_number > 0 && error_number < WSABASEERR)
		error_number += WSABASEERR;
679
	if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,	// dwFlags
680
681
682
		NULL,			// lpSource
		error_number,	// dwMessageId
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),    // dwLanguageId
683
684
		buf,
		buflen,
685
686
		NULL))
		safe_snprintf(buf, buflen, "Error %d getting error description", GetLastError());
687
688
689
690
	truncsp(buf);
	return buf;
#else
	return safe_strerror(error_number, buf, buflen);
691
#endif
692
}
Deucе's avatar
Deucе committed
693

694
695
696
697
698
699
700
701
702
DLLEXPORT void set_socket_errno(int err)
{
#if defined(_WINSOCKAPI_)
	WSASetLastError(err);
#else
	errno = err;
#endif
}

703
DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst)
Deucе's avatar
Deucе committed
704
705
706
707
708
{
	struct addrinfo hints = {0};
	struct addrinfo *res, *cur;

	if (af != AF_INET && af != AF_INET6) {
709
		set_socket_errno(EAFNOSUPPORT);
Deucе's avatar
Deucе committed
710
711
712
713
		return -1;
	}

	hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
714
	if(getaddrinfo(src, NULL, &hints, &res))
Deucе's avatar
Deucе committed
715
716
717
718
719
720
721
722
723
724
725
726
		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:
727
			memcpy(dst, &(((struct sockaddr_in *)cur)->sin_addr), sizeof(((struct sockaddr_in *)cur)->sin_addr));
Deucе's avatar
Deucе committed
728
729
			break;
		case AF_INET6:
730
			memcpy(dst, &(((struct sockaddr_in6 *)cur)->sin6_addr), sizeof(((struct sockaddr_in6 *)cur)->sin6_addr));
Deucе's avatar
Deucе committed
731
732
733
			break;
	}
	freeaddrinfo(res);
734
	return 1;
Deucе's avatar
Deucе committed
735
}
Deucе's avatar
Deucе committed
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813

#ifdef _WIN32
DLLEXPORT int
socketpair(int domain, int type, int protocol, SOCKET *sv)
{
	union xp_sockaddr la = {0};
	const int ra = 1;
	SOCKET ls;
	SOCKET *check;
	fd_set rfd;
	struct timeval tv;
	size_t sa_len;

	sv[0] = sv[1] = INVALID_SOCKET;
	ls = socket(domain, type, protocol);
	if (ls == INVALID_SOCKET)
		goto fail;
	switch (domain) {
		case PF_INET:
			if (inet_ptoaddr("127.0.0.1", &la, sizeof(la)) == NULL)
				goto fail;
			sa_len = sizeof(la.in);
			break;
		case PF_INET6:
			if (inet_ptoaddr("::1", &la, sizeof(la)) == NULL)
				goto fail;
			sa_len = sizeof(la.in6);
			break;
		default:
			goto fail;
	}
	inet_setaddrport(&la, 0);
	if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (const char *)&ra, sizeof(ra)) == -1)
		goto fail;
	if (bind(ls, &la.addr, sa_len) == -1)
		goto fail;
	if (getsockname(ls, &la.addr, &sa_len) == -1)
		goto fail;
	if (listen(ls, 1) == -1)
		goto fail;
	sv[0] = socket(la.addr.sa_family, type, protocol);
	if (sv[0] == INVALID_SOCKET)
		goto fail;
	if (connect(sv[0], &la.addr, sa_len) == -1)
		goto fail;
	sv[1] = accept(ls, NULL, NULL);
	if (sv[1] == INVALID_SOCKET)
		goto fail;
	closesocket(ls);
	ls = INVALID_SOCKET;

	if (send(sv[1], (const char *)&sv, sizeof(sv), 0) != sizeof(sv))
		goto fail;
	tv.tv_sec = 0;
	tv.tv_usec = 50000;
	FD_ZERO(&rfd);
	FD_SET(sv[0], &rfd);
	if (select(sv[0] + 1, &rfd, NULL, NULL, &tv) != 1)
		goto fail;
	if (recv(sv[0], (char *)&check, sizeof(check), 0) != sizeof(check))
		goto fail;
	if (check != sv)
		goto fail;
	return 0;

fail:
	if (ls != INVALID_SOCKET)
		closesocket(ls);
	if (sv[0] != INVALID_SOCKET)
		closesocket(sv[0]);
	sv[0] = INVALID_SOCKET;
	if (sv[1] != INVALID_SOCKET)
		closesocket(sv[1]);
	sv[1] = INVALID_SOCKET;
	return -1;
}
#endif