sockwrap.c 20.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
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
/*
 * The exact conditions where rd_p is set to TRUE and the return value
 * is true or false are complex, but the intent appears to be as follows:
 *
 * If the remote has half-closed the socket, rd_p should be FALSE and
 * the function should return FALSE.
 *
 * If we have half-closed the socket, wr_p should be TRUE and the function
 * should return TRUE.
 *
 * If the socket is completely closed, wr_p should be TRUE, rd_p should be
 * FALSE, and the function should return FALSE, unless rd_p is NULL in which
 * case, the function should return TRUE.
 *
 * When the function is open in both directions, wr_p will indicate write
 * buffers are available, rd_p will indicate data is available to be recv()ed
 * and the return value should be TRUE.
 *
 * If the socket is invalid, rd_p should be FALSE, wr_p should be FALSE, and
 * the function should return FALSE.
 *
 * These rules have various exceptions when errors are returned by select(),
 * poll(), or recv(), which will generally cause a FALSE return with rd_p
 * being FALSE and wr_p being FALSE if select/poll failed, or indicating
 * available write buffers otherwise.
 */
299
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
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
#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) {
326
		if (wr_p != NULL && (pfd.revents & POLLOUT)) {
327
			*wr_p = TRUE;
328
329
330
			if (rd_p == NULL)
				return TRUE;
		}
331

332
		if (pfd.revents & (POLLERR | POLLNVAL | POLLHUP))
333
334
			return FALSE;

335
		if(pfd.revents & ~(POLLOUT) && (rd_p !=NULL || wr_p==NULL))  {
336
337
338
339
			rd=recv(sock,&ch,1,MSG_PEEK);
			if(rd==1 || (rd==SOCKET_ERROR && ERROR_VALUE==EMSGSIZE)) {
				if(rd_p!=NULL)
					*rd_p=TRUE;
340
				return TRUE;
341
342
			}
		}
343
		return FALSE;
344
345
346
347
348
349
350
	}

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

351
	return FALSE;
352
#else
353
354
	char	ch;
	int		i,rd;
355
	fd_set	rd_set;
356
	fd_set*	rd_set_p=&rd_set;
357
358
	fd_set	wr_set;
	fd_set*	wr_set_p=NULL;
359
360
361
362
363
	struct	timeval tv;

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

364
365
366
	if(wr_p!=NULL)
		*wr_p=FALSE;

367
368
369
	if(sock==INVALID_SOCKET)
		return(FALSE);

370
371
372
373
374
375
	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);
376
		if(rd_p==NULL)
377
			rd_set_p=NULL;
378
	}
379

380
381
382
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
383

384
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
385
386
387
388
389
390
	if(i==SOCKET_ERROR)
		return(FALSE);

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

391
392
393
394
395
396
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

397
	if(rd_p !=NULL || wr_p==NULL)  {
398
399
400
401
402
403
404
		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);
		}
405
406
407
	}

	return(FALSE);
408
#endif
409
}
deuce's avatar
deuce committed
410

411
412
413
414
415
416
417
418
419
/*
 * 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)
{
420
421
422
423
424
425
426
427
428
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLIN;

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
	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)
{
462
463
464
465
466
467
468
469
470
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLOUT;

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
	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)
{
503
504
505
506
507
508
509
510
511
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLIN;
	char ch;
	int rd;

	switch (poll(&pfd, 1, timeout)) {
		case 1:
512
			if (pfd.revents) {
513
514
515
516
517
518
519
520
521
522
523
524
525
				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
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
	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
}

554
int retry_bind(SOCKET s, const struct sockaddr *addr, socklen_t addrlen
555
556
			   ,uint retries, uint wait_secs
			   ,const char* prot
557
			   ,int (*lprintf)(int level, const char *fmt, ...))
deuce's avatar
deuce committed
558
{
559
	char	port_str[128];
560
	char	err[256];
deuce's avatar
deuce committed
561
	int		result=-1;
562
	uint	i;
deuce's avatar
deuce committed
563

564
	if(addr->sa_family==AF_INET)
deuce's avatar
deuce committed
565
		SAFEPRINTF(port_str," to port %u",ntohs(((SOCKADDR_IN *)(addr))->sin_port));
566
567
	else
		port_str[0]=0;
deuce's avatar
deuce committed
568
	for(i=0;i<=retries;i++) {
569
		if((result=bind(s,addr,addrlen))==0)
deuce's avatar
deuce committed
570
			break;
571
		if(lprintf!=NULL)
572
			lprintf(i<retries ? LOG_WARNING:LOG_CRIT
573
				,"%04d !ERROR %d binding %s socket%s: %s", s, ERROR_VALUE, prot, port_str, socket_strerror(socket_errno, err, sizeof(err)));
574
575
		if(i<retries) {
			if(lprintf!=NULL)
576
577
				lprintf(LOG_WARNING,"%04d Will retry in %u seconds (%u of %u)"
					,s, wait_secs, i+1, retries);
578
579
			SLEEP(wait_secs*1000);
		}
deuce's avatar
deuce committed
580
581
582
	}
	return(result);
}
583

584
int nonblocking_connect(SOCKET sock, struct sockaddr* addr, size_t size, unsigned timeout)
585
586
{
	int result;
587
	socklen_t optlen;
588
589
590

	result=connect(sock, addr, size);

591
592
593
	if(result==SOCKET_ERROR) {
		result=ERROR_VALUE;
		if(result==EWOULDBLOCK || result==EINPROGRESS) {
594
595
596
597
			if (socket_writable(sock, timeout * 1000)) {
				result = 0;
			}
			else {
598
				optlen = sizeof(result);
599
				if(getsockopt(sock, SOL_SOCKET, SO_ERROR, (void*)&result, &optlen)==SOCKET_ERROR)
600
601
602
					result=ERROR_VALUE;
			}
		}
603
604
605
	}
	return result;
}
606

deuce's avatar
deuce committed
607

608
union xp_sockaddr* inet_ptoaddr(char *addr_str, union xp_sockaddr *addr, size_t size)
deuce's avatar
deuce committed
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
{
    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;
    }
625
626
627
628
629
630
    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
631
632
633
634
    freeaddrinfo(res);
    return addr;
}

635
const char* inet_addrtop(union xp_sockaddr *addr, char *dest, size_t size)
636
{
637
#ifdef _WIN32
deuce's avatar
deuce committed
638
	if(getnameinfo(&addr->addr, xp_sockaddr_len(addr), dest, size, NULL, 0, NI_NUMERICHOST))
639
		safe_snprintf(dest, size, "<Error %u converting address, family=%u>", WSAGetLastError(), addr->addr.sa_family);
640
641
	return dest;
#else
642
	switch(addr->addr.sa_family) {
643
		case AF_INET:
644
			return inet_ntop(addr->in.sin_family, &addr->in.sin_addr, dest, size);
645
		case AF_INET6:
646
			return inet_ntop(addr->in6.sin6_family, &addr->in6.sin6_addr, dest, size);
647
648
649
650
		case AF_UNIX:
			strncpy(dest, addr->un.sun_path, size);
			dest[size-1]=0;
			return dest;
651
		default:
652
			safe_snprintf(dest, size, "<unknown address family: %u>", addr->addr.sa_family);
653
654
			return NULL;
	}
655
#endif
656
657
}

658
uint16_t inet_addrport(union xp_sockaddr *addr)
659
{
660
	switch(addr->addr.sa_family) {
661
		case AF_INET:
662
			return ntohs(addr->in.sin_port);
663
		case AF_INET6:
664
			return ntohs(addr->in6.sin6_port);
665
666
667
668
		default:
			return 0;
	}
}
deuce's avatar
deuce committed
669

670
void inet_setaddrport(union xp_sockaddr *addr, uint16_t port)
deuce's avatar
deuce committed
671
672
673
674
675
676
677
678
679
680
{
	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;
	}
}
681
682

/* Return TRUE if the 2 addresses are the same host (type and address) */
683
BOOL inet_addrmatch(union xp_sockaddr* addr1, union xp_sockaddr* addr2)
684
685
686
687
688
689
690
691
692
693
694
695
{
	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;
}
696
697

/* Return the current socket error description (for Windows), like strerror() does for errno */
698
DLLEXPORT char* socket_strerror(int error_number, char* buf, size_t buflen)
699
{
700
#if defined(_WINSOCKAPI_)
701
702
	strncpy(buf, "Unknown error", buflen);
	buf[buflen - 1] = 0;
703
704
	if(error_number > 0 && error_number < WSABASEERR)
		error_number += WSABASEERR;
705
	if(!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,	// dwFlags
706
707
708
		NULL,			// lpSource
		error_number,	// dwMessageId
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),    // dwLanguageId
709
710
		buf,
		buflen,
711
712
		NULL))
		safe_snprintf(buf, buflen, "Error %d getting error description", GetLastError());
713
714
715
716
	truncsp(buf);
	return buf;
#else
	return safe_strerror(error_number, buf, buflen);
717
#endif
718
}
Deucе's avatar
Deucе committed
719

720
721
722
723
724
725
726
727
728
DLLEXPORT void set_socket_errno(int err)
{
#if defined(_WINSOCKAPI_)
	WSASetLastError(err);
#else
	errno = err;
#endif
}

729
DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst)
Deucе's avatar
Deucе committed
730
731
732
733
734
{
	struct addrinfo hints = {0};
	struct addrinfo *res, *cur;

	if (af != AF_INET && af != AF_INET6) {
735
		set_socket_errno(EAFNOSUPPORT);
Deucе's avatar
Deucе committed
736
737
738
739
		return -1;
	}

	hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
740
	if(getaddrinfo(src, NULL, &hints, &res))
Deucе's avatar
Deucе committed
741
742
743
744
745
746
747
748
749
750
751
752
		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:
753
			memcpy(dst, &(((struct sockaddr_in *)cur)->sin_addr), sizeof(((struct sockaddr_in *)cur)->sin_addr));
Deucе's avatar
Deucе committed
754
755
			break;
		case AF_INET6:
756
			memcpy(dst, &(((struct sockaddr_in6 *)cur)->sin6_addr), sizeof(((struct sockaddr_in6 *)cur)->sin6_addr));
Deucе's avatar
Deucе committed
757
758
759
			break;
	}
	freeaddrinfo(res);
760
	return 1;
Deucе's avatar
Deucе committed
761
}
Deucе's avatar
Deucе committed
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
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839

#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