sockwrap.c 20.6 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
/*
 * 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
278
279
280
 * the function should return FALSE, unless rd_p is NULL in which case
 * the function should return TRUE.  wr_p will indicate that transmit
 * buffers are available.
281
 *
282
283
284
 * If we have half-closed the socket, wr_p should be TRUE, the function
 * should return TRUE, and rd_p will indicate if there is data available
 * to be received.
285
286
287
288
289
 *
 * 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.
 *
290
 * When the function is open in both directions, wr_p will indicate transmit
291
292
293
294
295
296
297
298
299
300
301
 * 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.
 */
302
BOOL socket_check(SOCKET sock, BOOL* rd_p, BOOL* wr_p, DWORD timeout)
303
{
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#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) {
329
		if (wr_p != NULL && (pfd.revents & POLLOUT)) {
330
			*wr_p = TRUE;
331
332
333
			if (rd_p == NULL)
				return TRUE;
		}
334

335
		if (pfd.revents & (POLLERR | POLLNVAL | POLLHUP))
336
337
			return FALSE;

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

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

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

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

367
368
369
	if(wr_p!=NULL)
		*wr_p=FALSE;

370
371
372
	if(sock==INVALID_SOCKET)
		return(FALSE);

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

383
384
385
	/* Convert timeout from ms to sec/usec */
	tv.tv_sec=timeout/1000;
	tv.tv_usec=(timeout%1000)*1000;
386

387
	i=select(sock+1,rd_set_p,wr_set_p,NULL,&tv);
388
389
390
391
392
393
	if(i==SOCKET_ERROR)
		return(FALSE);

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

394
395
396
397
398
399
	if(wr_p!=NULL && FD_ISSET(sock,wr_set_p)) {
		*wr_p=TRUE;
		if(i==1)
			return(TRUE);
	}

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

	return(FALSE);
411
#endif
412
}
deuce's avatar
deuce committed
413

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

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
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
462
463
464
	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)
{
465
466
467
468
469
470
471
472
473
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLOUT;

	if (poll(&pfd, 1, timeout) == 1)
		return TRUE;
	return FALSE;
#else
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
503
504
505
	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)
{
506
507
508
509
510
511
512
513
514
#ifdef PREFER_POLL
	struct pollfd pfd = {0};
	pfd.fd = sock;
	pfd.events = POLLIN;
	char ch;
	int rd;

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

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

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

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

	result=connect(sock, addr, size);

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

deuce's avatar
deuce committed
610

611
union xp_sockaddr* inet_ptoaddr(const char *addr_str, union xp_sockaddr *addr, size_t size)
deuce's avatar
deuce committed
612
613
614
615
616
617
618
619
{
    struct addrinfo hints = {0};
    struct addrinfo *res, *cur;

    hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
    if(getaddrinfo(addr_str, NULL, &hints, &res))
        return NULL;
    
620
    for(cur = res; cur; cur = cur->ai_next) {
deuce's avatar
deuce committed
621
622
        if(cur->ai_addr->sa_family == AF_INET6)
            break;
623
624
        if(cur->ai_addr->sa_family == AF_INET)
            break;
deuce's avatar
deuce committed
625
626
627
628
629
    }
    if(!cur) {
        freeaddrinfo(res);
        return NULL;
    }
630
631
632
633
634
635
    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
636
637
638
639
    freeaddrinfo(res);
    return addr;
}

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

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

675
void inet_setaddrport(union xp_sockaddr *addr, uint16_t port)
deuce's avatar
deuce committed
676
677
678
679
680
681
682
683
684
685
{
	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;
	}
}
686
687

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

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

725
726
727
728
729
730
731
732
733
DLLEXPORT void set_socket_errno(int err)
{
#if defined(_WINSOCKAPI_)
	WSASetLastError(err);
#else
	errno = err;
#endif
}

734
DLLEXPORT int xp_inet_pton(int af, const char *src, void *dst)
Deucе's avatar
Deucе committed
735
736
737
738
739
{
	struct addrinfo hints = {0};
	struct addrinfo *res, *cur;

	if (af != AF_INET && af != AF_INET6) {
740
		set_socket_errno(EAFNOSUPPORT);
Deucе's avatar
Deucе committed
741
742
743
744
		return -1;
	}

	hints.ai_flags = AI_NUMERICHOST|AI_PASSIVE;
745
	if(getaddrinfo(src, NULL, &hints, &res))
Deucе's avatar
Deucе committed
746
747
748
749
750
751
752
753
754
755
756
757
		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:
758
			memcpy(dst, &(((struct sockaddr_in *)cur)->sin_addr), sizeof(((struct sockaddr_in *)cur)->sin_addr));
Deucе's avatar
Deucе committed
759
760
			break;
		case AF_INET6:
761
			memcpy(dst, &(((struct sockaddr_in6 *)cur)->sin6_addr), sizeof(((struct sockaddr_in6 *)cur)->sin6_addr));
Deucе's avatar
Deucе committed
762
763
764
			break;
	}
	freeaddrinfo(res);
765
	return 1;
Deucе's avatar
Deucе committed
766
}
Deucе's avatar
Deucе committed
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
840
841
842
843
844

#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