Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

multisock.c 19.7 KB
Newer Older
1 2 3 4 5
// Multi-socket versions ofthe socket API...

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
deuce's avatar
deuce committed
6 7 8 9
#include "gen_defs.h"
#include "sockwrap.h"
#include "dirwrap.h"
#include "multisock.h"
10
#include "haproxy.h"
11
#include <stdarg.h>
12

Rob Swindell's avatar
Rob Swindell committed
13
struct xpms_set* xpms_create(unsigned int retries, unsigned int wait_secs,
14 15 16 17 18 19 20 21 22 23 24 25
	int (*lprintf)(int level, const char *fmt, ...))
{
	struct xpms_set *ret=(struct xpms_set *)calloc(1, sizeof(struct xpms_set));

	if(ret==NULL)
		return ret;
	ret->retries = retries;
	ret->wait_secs = wait_secs;
	ret->lprintf=lprintf;
	return ret;
}

Rob Swindell's avatar
Rob Swindell committed
26
void xpms_destroy(struct xpms_set *xpms_set, void (*sock_destroy)(SOCKET, void *), void *cbdata)
27
{
deuce's avatar
deuce committed
28
	size_t		i;
29

30 31
	if(!xpms_set)
		return;
32 33 34
	for(i=0; i<xpms_set->sock_count; i++) {
		if(xpms_set->socks[i].sock != INVALID_SOCKET) {
			if(xpms_set->lprintf!=NULL)
35
				xpms_set->lprintf(LOG_INFO, "%04d %s closing socket %s port %d"
36
						, xpms_set->socks[i].sock, xpms_set->socks[i].prot?xpms_set->socks[i].prot:"unknown"
37
						, xpms_set->socks[i].address
38 39
						, xpms_set->socks[i].port);
			closesocket(xpms_set->socks[i].sock);
40
			if(sock_destroy)
41
				sock_destroy(xpms_set->socks[i].sock, cbdata);
42 43 44 45 46 47 48 49 50
		}
		xpms_set->socks[i].sock = INVALID_SOCKET;
		FREE_AND_NULL(xpms_set->socks[i].address);
		FREE_AND_NULL(xpms_set->socks[i].prot);
	}
	FREE_AND_NULL(xpms_set->socks);
	free(xpms_set);
}

Rob Swindell's avatar
Rob Swindell committed
51
BOOL xpms_add(struct xpms_set *xpms_set, int domain, int type,
52
	int protocol, const char *addr, uint16_t port, const char *prot, 
53
	void (*sock_init)(SOCKET, void *), int(*bind_init)(BOOL), void *cbdata)
54 55 56
{
	struct xpms_sockdef	*new_socks;
    struct addrinfo		hints;
deuce's avatar
deuce committed
57
    struct addrinfo		*res=NULL;
58 59 60 61
    struct addrinfo		*cur;
    unsigned int		added = 0;
    int					ret;
    char				port_str[6];
62
	char				err[128];
63

deuce's avatar
deuce committed
64 65 66 67 68 69
#ifndef _WIN32
	struct addrinfo		dummy;
	struct sockaddr_un	un_addr;

	if(domain == AF_UNIX) {
		memset(&dummy, 0, sizeof(dummy));
70
		memset(&un_addr, 0, sizeof(un_addr));
deuce's avatar
deuce committed
71 72 73 74 75 76 77
		dummy.ai_family = AF_UNIX;
		dummy.ai_socktype = type;
		dummy.ai_addr = (struct sockaddr *)&un_addr;
		un_addr.sun_family=AF_UNIX;

		if(strlen(addr) >= sizeof(un_addr.sun_path)) {
			if(xpms_set->lprintf)
78
				xpms_set->lprintf(LOG_ERR, "!%s ERROR %s is too long for a portable AF_UNIX socket", prot, addr);
deuce's avatar
deuce committed
79 80 81
			return FALSE;
		}
		strcpy(un_addr.sun_path,addr);
82 83 84 85 86
#ifdef SUN_LEN
		dummy.ai_addrlen = SUN_LEN(&un_addr);
#else
		dummy.ai_addrlen = offsetof(struct sockaddr_un, un_addr.sun_path) + strlen(addr) + 1;
#endif
deuce's avatar
deuce committed
87 88 89 90 91 92 93 94 95 96 97 98
		if(fexist(addr))
			unlink(addr);
		res = &dummy;
	}
#endif
	if(res == NULL) {
		memset(&hints, 0, sizeof(hints));
		hints.ai_flags=AI_PASSIVE;
		hints.ai_family=domain;
		hints.ai_socktype=type;
		hints.ai_protocol=protocol;
		hints.ai_flags|=AI_NUMERICSERV;
99
#ifdef AI_ADDRCONFIG
deuce's avatar
deuce committed
100
		hints.ai_flags|=AI_ADDRCONFIG;
101
#endif
deuce's avatar
deuce committed
102 103 104
		sprintf(port_str, "%hu", port);
		if((ret=getaddrinfo(addr, port_str, &hints, &res))!=0) {
			if(xpms_set->lprintf)
105
				xpms_set->lprintf(LOG_CRIT, "!%s ERROR %d calling getaddrinfo() on %s", prot, ret, addr);
deuce's avatar
deuce committed
106 107
			return FALSE;
		}
108 109 110
	}

	for(cur=res; cur; cur=cur->ai_next) {
111
		new_socks=(struct xpms_sockdef *)realloc(xpms_set->socks, sizeof(struct xpms_sockdef)*(xpms_set->sock_count+1));
112 113 114
		if(new_socks==NULL) {
			/* This may be a partial failure */
			if(xpms_set->lprintf)
115
				xpms_set->lprintf(LOG_CRIT, "!%s ERROR out of memory adding to multisocket", prot);
deuce's avatar
deuce committed
116
			break;
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
		}
		xpms_set->socks=new_socks;
		xpms_set->socks[xpms_set->sock_count].address = strdup(addr);
		xpms_set->socks[xpms_set->sock_count].cb_data = cbdata;
		xpms_set->socks[xpms_set->sock_count].domain = cur->ai_family;	/* Address/Protocol Family */
		xpms_set->socks[xpms_set->sock_count].type = cur->ai_socktype;
		xpms_set->socks[xpms_set->sock_count].protocol = protocol;
		xpms_set->socks[xpms_set->sock_count].port = port;
		xpms_set->socks[xpms_set->sock_count].prot = strdup(prot);
		xpms_set->socks[xpms_set->sock_count].sock = socket(cur->ai_family, cur->ai_socktype, protocol);
		if(xpms_set->socks[xpms_set->sock_count].sock == INVALID_SOCKET) {
			FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address);
			FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot);
			continue;
		}
132
		if(sock_init)
deuce's avatar
deuce committed
133
			sock_init(xpms_set->socks[xpms_set->sock_count].sock, cbdata);
134

135 136 137 138
		if(bind_init) {
			if(port < IPPORT_RESERVED && port > 0)
				bind_init(FALSE);
		}
139 140 141 142
		if(retry_bind(xpms_set->socks[xpms_set->sock_count].sock, cur->ai_addr, cur->ai_addrlen, xpms_set->retries, xpms_set->wait_secs, prot, xpms_set->lprintf)==-1) {
			closesocket(xpms_set->socks[xpms_set->sock_count].sock);
			FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address);
			FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot);
143
			if(bind_init) {
deuce's avatar
deuce committed
144
				if(port < IPPORT_RESERVED)
145 146
					bind_init(TRUE);
			}
147 148
			continue;
		}
149
		if(bind_init) {
deuce's avatar
deuce committed
150
			if(port < IPPORT_RESERVED && port > 0)
151 152
				bind_init(TRUE);
		}
153

154 155 156
		if(type != SOCK_DGRAM) {
			if(listen(xpms_set->socks[xpms_set->sock_count].sock, SOMAXCONN)==-1) {
				if(xpms_set->lprintf)
157
					xpms_set->lprintf(LOG_WARNING, "%04d !%s ERROR %d listening on port %d: %s"
158
						,xpms_set->socks[xpms_set->sock_count].sock, prot, ERROR_VALUE
159
						,port, socket_strerror(socket_errno,err,sizeof(err)));
160 161 162 163 164
				closesocket(xpms_set->socks[xpms_set->sock_count].sock);
				FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].address);
				FREE_AND_NULL(xpms_set->socks[xpms_set->sock_count].prot);
				continue;
			}
165 166 167 168 169 170
		}

		added++;
		xpms_set->sock_count++;
	}

deuce's avatar
deuce committed
171 172 173 174
#ifndef _WIN32
	if(res != &dummy)
#endif
		freeaddrinfo(res);
175 176 177 178 179
	if(added)
		return TRUE;
	return FALSE;
}

Rob Swindell's avatar
Rob Swindell committed
180
BOOL xpms_add_list(struct xpms_set *xpms_set, int domain, int type,
181 182 183 184 185
	int protocol, str_list_t list, uint16_t default_port, const char *prot, 
	void (*sock_init)(SOCKET, void *), int(*bind_init)(BOOL), void *cbdata)
{
	char	**iface;
	char	*host;
186
	char	*host_str;
187 188 189 190 191 192
	char	*p, *p2;
	BOOL	one_good=FALSE;
	
	for(iface=list; iface && *iface; iface++) {
		WORD	port=default_port;

193 194
		host=strdup(*iface);

195
		host_str=host;
196
		p = strrchr(host, ':');
197 198 199 200 201 202
		/*
		 * If there isn't a [, and the first and last colons aren't the same
		 * it's assumed to be an IPv6 address
		 */
		if(strchr(host,'[')==NULL && p != NULL && strchr(host, ':') != p)
			p=NULL;
203
		if(host[0]=='[') {
204
			host_str++;
205 206 207 208 209 210 211 212 213 214
			p2=strrchr(host,']');
			if(p2)
				*p2=0;
			if(p2 > p)
				p=NULL;
		}
		if(p!=NULL) {
			*(p++)=0;
			sscanf(p, "%hu", &port);
		}
215
		if(xpms_set->lprintf)
216
			xpms_set->lprintf(LOG_INFO, "%s listening on socket %s port %hu", prot, host_str, port);
217
		if(xpms_add(xpms_set, domain, type, protocol, host_str, port, prot, sock_init, bind_init, cbdata))
218 219 220 221 222 223
			one_good=TRUE;
		free(host);
	}
	return one_good;
}

Rob Swindell's avatar
Rob Swindell committed
224
BOOL xpms_add_chararray_list(struct xpms_set *xpms_set, int domain, int type,
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
	int protocol, const char *list, uint16_t default_port, const char *prot,
	void (*sock_init)(SOCKET, void *), int(*bind_init)(BOOL), void *cbdata)
{
	str_list_t slist;
	BOOL ret;

	slist = strListSplitCopy(NULL, list, ", \t\r\n");
	if (slist == NULL)
		return FALSE;
	ret = xpms_add_list(xpms_set, domain, type, protocol, slist, default_port, prot,
			sock_init, bind_init, cbdata);
	strListFree(&slist);
	return ret;
}

240
/* Convert a binary variable into a hex string - used for printing in the debug log */
241
static void btox(char *hexstr, const char *srcbuf, size_t srcbuflen, size_t hexstrlen, int (*lprintf)(int level, const char *fmt, ...)) 
242
{
Deucе's avatar
Deucе committed
243
	size_t         i;
Deon George's avatar
Deon George committed
244

245
	if (hexstrlen < srcbuflen*2+1) {
246
		lprintf(LOG_WARNING,"btox hexstr buffer too small [%d] - not all data will be processed",hexstrlen);
247
		srcbuflen = hexstrlen/2-1;
248 249 250
	}

	*hexstr = '\0';
Deon George's avatar
Deon George committed
251 252 253
	for (i=0;i<srcbuflen;i++) {
		sprintf(hexstr+strlen(hexstr),"%02x",(unsigned char)srcbuf[i]);
	}
254 255
}

256
static BOOL read_socket(SOCKET sock, char *buffer, size_t len, int (*lprintf)(int level, const char *fmt, ...))
257
{
258
	size_t            i;
259
	int            rd;
260
	unsigned char           ch;
261 262 263
	char           err[128];

	for (i=0;i<len;i++) {
264
		if (socket_readable(sock, 1000)) {
265
			rd = recv(sock,&ch,1,0);
266 267 268 269 270
			if (rd == 0) {
				lprintf(LOG_WARNING,"%04d multisock read_socket() - remote closed the connection",sock);
				return FALSE;

			} else if (rd == 1) {
271
				buffer[i] = ch;
272

273 274
			} else {
				lprintf(LOG_WARNING,"%04d multisock read_socket() - failed to read from socket. Got [%d] with error [%s]",sock,rd,socket_strerror(socket_errno,err,sizeof(err)));
275
				return FALSE;
276
			}
277

278
		} else {
279
			lprintf(LOG_WARNING,"%04d multisock read_socket() - No data?",sock);
280
			return FALSE;
281

282 283 284 285 286 287
		}
	}

	return TRUE;
}

288
static BOOL read_socket_line(SOCKET sock, char *buffer, size_t buflen, int (*lprintf)(int level, const char *fmt, ...))
289
{
Deucе's avatar
Deucе committed
290
	size_t         i;
291 292 293 294 295

	for (i = 0; i < buflen - 1; i++) {
		if (read_socket(sock, &buffer[i], 1, lprintf)) {
			switch(buffer[i]) {
				case 0:
296
					return FALSE;
297
				case '\n':
298
					buffer[i+1] = 0;
299 300
					return TRUE;
			}
301 302

		} else {
303
			buffer[i] = 0;
304
			return FALSE;
305 306
		}
	}
307

308
	buffer[i] = 0;
309
	return FALSE;
310 311
}

Rob Swindell's avatar
Rob Swindell committed
312
SOCKET xpms_accept(struct xpms_set *xpms_set, union xp_sockaddr * addr, 
313
	socklen_t * addrlen, unsigned int timeout, uint32_t flags, void **cb_data)
314
{
315 316 317 318 319
#ifdef PREFER_POLL
	struct pollfd *fds;
	int poll_timeout;
	nfds_t scnt = 0;
#else
Deucе's avatar
Deucе committed
320 321 322 323
	fd_set         read_fs;
	struct timeval tv;
	struct timeval *tvp;
	SOCKET         max_sock=0;
324 325
#endif
	size_t         i;
326
	SOCKET         ret;
327 328 329 330 331
	char           hapstr[128];
	char           haphex[256];
	char           *p, *tok;
	long           l;
	void           *vp;
332

333 334 335
	if (xpms_set->sock_count < 1)
		return INVALID_SOCKET;

336
#ifdef PREFER_POLL
337
	fds = calloc(xpms_set->sock_count, sizeof(*fds));
338 339
	if (fds == NULL)
		return INVALID_SOCKET;
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
	for (i = 0; i < xpms_set->sock_count; i++) {
		if (xpms_set->socks[i].sock == INVALID_SOCKET)
			continue;
		fds[scnt].fd = xpms_set->socks[i].sock;
		fds[scnt].events = POLLIN;
		scnt++;
	}

	if (timeout == XPMS_FOREVER)
		poll_timeout = -1;
	else if (timeout > INT_MAX)
		poll_timeout = INT_MAX;
	else
		poll_timeout = timeout;

355
	switch (poll(fds, scnt, poll_timeout)) {
356
		case 0:
357
			free(fds);
358 359
			return INVALID_SOCKET;
		case -1:
360
			free(fds);
361 362 363 364 365 366
			return SOCKET_ERROR;
		default:
			scnt = 0;
			for(i=0; i<xpms_set->sock_count; i++) {
				if(xpms_set->socks[i].sock == INVALID_SOCKET)
					continue;
367
				if (fds[scnt].revents & ~(POLLIN)) {
368
					scnt++;
369 370 371 372 373
					closesocket(xpms_set->socks[i].sock);
					xpms_set->lprintf(LOG_ERR, "%04d * Listening socket went bad", xpms_set->socks[i].sock);
					xpms_set->socks[i].sock = INVALID_SOCKET;
					continue;
				}
374
				if (fds[scnt++].revents & POLLIN) {
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
#else
	FD_ZERO(&read_fs);
	for(i=0; i<xpms_set->sock_count; i++) {
		if(xpms_set->socks[i].sock == INVALID_SOCKET)
			continue;
		FD_SET(xpms_set->socks[i].sock, &read_fs);
		if(xpms_set->socks[i].sock >= max_sock)
			max_sock=xpms_set->socks[i].sock+1;
	}

	if(timeout==XPMS_FOREVER)
		tvp=NULL;
	else {
		tv.tv_sec=timeout/1000;
		tv.tv_usec=(timeout%1000)*1000;
		tvp=&tv;
	}
	switch(select(max_sock, &read_fs, NULL, NULL, tvp)) {
		case 0:
			return INVALID_SOCKET;
		case -1:
			return SOCKET_ERROR;
		default:
			for(i=0; i<xpms_set->sock_count; i++) {
				if(xpms_set->socks[i].sock == INVALID_SOCKET)
					continue;
				if(FD_ISSET(xpms_set->socks[i].sock, &read_fs)) {
402
#endif
403 404
					if(cb_data)
						*cb_data=xpms_set->socks[i].cb_data;
405 406 407 408
					ret = accept(xpms_set->socks[i].sock, &addr->addr, addrlen);
					if (ret == INVALID_SOCKET) {
						goto error_return;
					}
409

410 411 412
					// Set host_ip from haproxy protocol, if its used
					// http://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
					if (flags & XPMS_ACCEPT_FLAG_HAPROXY) {
413
						memset(addr, 0, sizeof(*addr));
414
						xpms_set->lprintf(LOG_DEBUG,"%04d Working out client address from HAProxy PROTO",ret);
415 416

						// Read the first line
417 418 419
						// In normal proxy usage, we shouldnt fail here - but if there is a badly implemented HAPROXY PROTO
						// or the user attempts to connect direct to the BBS (not via the proxy) there could be anything 
						// received (IAC sequences, SSH setup, or just badness)
420
						if (! read_socket_line(ret, hapstr, 108, xpms_set->lprintf)) {
421 422
							btox(haphex,hapstr,strlen(hapstr),sizeof(haphex), xpms_set->lprintf);
							xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for version - failed [%s]",ret,haphex);
423
							closesocket(ret);
424
							goto error_return;
425
						}
426 427 428

						btox(haphex,hapstr,strlen(hapstr)>16 ? 16 : strlen(hapstr),sizeof(haphex), xpms_set->lprintf);
						xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY looking for version - 1st %d bytes received [%s] of (%d)",ret,strlen(hapstr)>16 ? 16 : strlen(hapstr),haphex,strlen(hapstr));
429 430 431 432 433 434 435 436 437 438

						// v1 of the protocol uses plain text, starting with "PROXY "
						// eg: "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n"
						if (strncmp((char *)hapstr,"PROXY ",6) == 0) {
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY PROTO v1",ret);

							tok = &hapstr[6];
							// IPV4
							if (strncmp(tok, "TCP4 ", 5) == 0) {
								tok += 5;
439
								xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Proto TCP4",ret,hapstr);
440
								addr->addr.sa_family = AF_INET;
441
								if (addrlen)
442
									*addrlen = sizeof(struct sockaddr_in);
443
								vp = &addr->in.sin_addr;
444 445 446 447 448
							// IPV6
							} else if (strncmp(tok,"TCP6 ",5) == 0) {
								tok += 5;
								xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Proto TCP6",ret,hapstr);
								addr->addr.sa_family = AF_INET6;
449
								if (addrlen)
450 451
									*addrlen = sizeof(struct sockaddr_in6);
								vp = &addr->in6.sin6_addr;
452 453 454 455
							// Unknown?
							} else {
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Unknown Protocol",ret);
								closesocket(ret);
456
								goto error_return;
457 458 459 460 461
							}

							// Look for the space between the next IP
							p = strchr(tok, ' ');
							if (p == NULL) {
462
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Couldnt find IP address",ret);
463
								closesocket(ret);
464
								goto error_return;
465 466
							}
							*p = 0;
467
							if (inet_pton(addr->addr.sa_family, tok, vp) != 1) {
468 469
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Unable to parse %s address [%s]",addr->addr.sa_family == AF_INET ? "IPv4" : "IPv6", tok);
								closesocket(ret);
470
								goto error_return;
471 472 473 474 475
							}
							tok = p + 1;
							// Look for the space before the port number
							p = strchr(tok, ' ');
							if (p == NULL) {
476
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Couldnt find port",ret);
477
								closesocket(ret);
478
								goto error_return;
479 480 481 482
							}
							tok = p + 1;
							l = strtol(tok, NULL, 10);
							if (l <= 0 || l > UINT16_MAX) {
483
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Source port out of range",ret);
484
								closesocket(ret);
485
								goto error_return;
486 487 488
							}
							switch(addr->addr.sa_family) {
								case AF_INET:
Deucе's avatar
Deucе committed
489
									addr->in.sin_port = htons((unsigned short)l);
490 491
									break;
								case AF_INET6:
Deucе's avatar
Deucе committed
492
									addr->in6.sin6_port = htons((unsigned short)l);
493 494 495 496 497 498 499 500 501 502
									break;
							}

						// v2 is in binary with the first 12 bytes "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A"
						// we'll compare the 1st 6 bytes here, since we are determining if we are v1 or v2.
						} else if (strcmp((char *)hapstr,"\r\n") == 0) {
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY PROTO v2",ret);

							// OK, just for sanity, our next 10 chars should be v2...
							memset(hapstr, 0, 10);
503 504 505
							if (read_socket(ret,hapstr,10,xpms_set->lprintf)==FALSE || memcmp(hapstr, "\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A", 10) != 0) {
								btox(haphex,hapstr,10,sizeof(haphex), xpms_set->lprintf);
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Something went wrong - incomplete v2 setup [%s]",ret,haphex);
506
								closesocket(ret);
507
								goto error_return;
508 509 510
							}

							// Command and Version
511
							if (read_socket(ret,hapstr,1,xpms_set->lprintf)==FALSE) {
512
								btox(haphex,hapstr,1,sizeof(haphex), xpms_set->lprintf);
513
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for Verson/Command - failed [%s]",ret,haphex);
514
								closesocket(ret);
515
								goto error_return;
516
							}
517 518 519 520 521 522 523 524 525
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Version [%x]",ret,(hapstr[0]>>4)&0x0f); //Should be 2

							// Check the version
							switch((hapstr[0]>>4)&0x0f) {
								case 0x02:
									break;
								default:
									xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY invalid version [%x]",ret,(hapstr[0]>>4)&0x0f);
									closesocket(ret);
526
									goto error_return;
527 528 529 530 531 532 533 534 535
							}

							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Command [%x]",ret,hapstr[0]&0x0f); //0=Local/1=Proxy

							// Check the command
							switch(hapstr[0]&0x0f) {
								case HAPROXY_LOCAL:
									xpms_set->lprintf(LOG_INFO,"%04d * HAPROXY health check - we are alive!",ret);
									closesocket(ret);
536
									goto error_return;
537 538 539 540 541
								case HAPROXY_PROXY:
									break;
								default:
									xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY invalid command [%x]",ret,hapstr[0]&0x0f);
									closesocket(ret);
542
									goto error_return;
543
							}
544 545

							// Protocol and Family
546
							if (read_socket(ret,hapstr,1,xpms_set->lprintf)==FALSE) {
547
								btox(haphex,hapstr,1,sizeof(haphex), xpms_set->lprintf);
548
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for Protocol/Family - failed [%s]",ret,haphex);
549
								closesocket(ret);
550
								goto error_return;
551 552 553 554 555 556
							}
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Protocol [%x]",ret,hapstr[0]&0x0f); //0=Unspec/1=AF_INET/2=AF_INET6/3=AF_UNIX
							l = (hapstr[0]>>4)&0x0f;
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Family [%x]",ret,l); //0=UNSPEC/1=STREAM/2=DGRAM

							// Address Length - 2 bytes
557
							if (read_socket(ret,hapstr,2,xpms_set->lprintf)==FALSE) {
558
								btox(haphex,hapstr,2,sizeof(haphex), xpms_set->lprintf);
559
								xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for address length - failed [%s]",ret,haphex);
560
								closesocket(ret);
561
								goto error_return;
562 563 564 565 566 567 568 569
							}
							i = ntohs(*(uint16_t*)hapstr);
							xpms_set->lprintf(LOG_DEBUG,"%04d * HAPROXY Address Length [%d]",ret,i);

							switch (l) {
								// IPv4 - AF_INET
								case HAPROXY_AFINET:
									if (i != 12) {
570
										xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Something went wrong - IPv4 address length is incorrect",ret);
571
										closesocket(ret);
572
										goto error_return;
573 574
									}
									addr->in.sin_family = AF_INET;
575
									if (read_socket(ret, hapstr, i, xpms_set->lprintf)==FALSE) {
576 577
										xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for IPv4 address - failed",ret);
										closesocket(ret);
578
										goto error_return;
579 580 581
									}
									memcpy(&addr->in.sin_addr.s_addr, hapstr, 4);
									memcpy(&addr->in.sin_port, &hapstr[8], 2);
582
									if (addrlen)
583
										*addrlen = sizeof(struct sockaddr_in);
584 585 586 587 588 589

									break;

								// IPv6 - AF_INET6
								case HAPROXY_AFINET6:
									if (i != 36) {
590
										xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Something went wrong - IPv6 address length is incorrect.",ret);
591
										closesocket(ret);
592
										goto error_return;
593 594
									}
									addr->in6.sin6_family = AF_INET6;
595
									if (read_socket(ret,hapstr,i,xpms_set->lprintf)==FALSE) {
596 597
										xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY looking for IPv6 address - failed",ret);
										closesocket(ret);
598
										goto error_return;
599
									}
600 601
									memcpy(&addr->in6.sin6_addr.s6_addr, hapstr, 16);
									memcpy(&addr->in6.sin6_port, &hapstr[32], 2);
602
									if (addrlen)
603
										*addrlen = sizeof(struct sockaddr_in6);
604 605 606 607 608 609

									break;

								default:
									xpms_set->lprintf(LOG_ERR,"%04d * HAPROXY Unknown Family [%x]",ret,l);
									closesocket(ret);
610
									goto error_return;
611 612 613 614 615
							}

						} else {
							xpms_set->lprintf(LOG_ERR,"%04d Unknown HAProxy Initialisation - is HAProxy used?",ret);
							closesocket(ret);
616
							goto error_return;
617 618 619 620 621
						}

						hapstr[0] = 0;
						inet_addrtop(addr, hapstr, sizeof(hapstr));
						xpms_set->lprintf(LOG_INFO,"%04d * HAPROXY Source [%s]",ret,hapstr);
622

623
#ifdef PREFER_POLL
624 625
						free(fds);
#endif
626
						return ret;
627
					} else {
628
#ifdef PREFER_POLL
629 630
						free(fds);
#endif
631 632
						return ret;
					}
633 634 635 636
				}
			}
	}

637
error_return:
638
#ifdef PREFER_POLL
639 640
	free(fds);
#endif
641 642
	return INVALID_SOCKET;
}