Home | History | Annotate | Download | only in x11vnc
      1 /*
      2    Copyright (C) 2002-2010 Karl J. Runge <runge (at) karlrunge.com>
      3    All rights reserved.
      4 
      5 This file is part of x11vnc.
      6 
      7 x11vnc is free software; you can redistribute it and/or modify
      8 it under the terms of the GNU General Public License as published by
      9 the Free Software Foundation; either version 2 of the License, or (at
     10 your option) any later version.
     11 
     12 x11vnc is distributed in the hope that it will be useful,
     13 but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with x11vnc; if not, write to the Free Software
     19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA
     20 or see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, as a special exception, Karl J. Runge
     23 gives permission to link the code of its release of x11vnc with the
     24 OpenSSL project's "OpenSSL" library (or with modified versions of it
     25 that use the same license as the "OpenSSL" library), and distribute
     26 the linked executables.  You must obey the GNU General Public License
     27 in all respects for all of the code used other than "OpenSSL".  If you
     28 modify this file, you may extend this exception to your version of the
     29 file, but you are not obligated to do so.  If you do not wish to do
     30 so, delete this exception statement from your version.
     31 */
     32 
     33 /* -- inet.c -- */
     34 
     35 #include "x11vnc.h"
     36 #include "unixpw.h"
     37 #include "sslhelper.h"
     38 
     39 /*
     40  * Simple utility to map host name to dotted IP address.  Ignores aliases.
     41  * Up to caller to free returned string.
     42  */
     43 char *host2ip(char *host);
     44 char *raw2host(char *raw, int len);
     45 char *raw2ip(char *raw);
     46 char *ip2host(char *ip);
     47 int ipv6_ip(char *host);
     48 int dotted_ip(char *host, int partial);
     49 int get_remote_port(int sock);
     50 int get_local_port(int sock);
     51 char *get_remote_host(int sock);
     52 char *get_local_host(int sock);
     53 char *ident_username(rfbClientPtr client);
     54 int find_free_port(int start, int end);
     55 int find_free_port6(int start, int end);
     56 int have_ssh_env(void);
     57 char *ipv6_getnameinfo(struct sockaddr *paddr, int addrlen);
     58 char *ipv6_getipaddr(struct sockaddr *paddr, int addrlen);
     59 int listen6(int port);
     60 int listen_unix(char *file);
     61 int accept_unix(int s);
     62 int connect_tcp(char *host, int port);
     63 int listen_tcp(int port, in_addr_t iface, int try6);
     64 
     65 static int get_port(int sock, int remote);
     66 static char *get_host(int sock, int remote);
     67 
     68 char *host2ip(char *host) {
     69 	struct hostent *hp;
     70 	struct sockaddr_in addr;
     71 	char *str;
     72 
     73 	if (! host_lookup) {
     74 		return NULL;
     75 	}
     76 
     77 	hp = gethostbyname(host);
     78 	if (!hp) {
     79 		return NULL;
     80 	}
     81 	memset(&addr, 0, sizeof(addr));
     82 	addr.sin_family = AF_INET;
     83 	addr.sin_addr.s_addr =  *(unsigned long *)hp->h_addr;
     84 	str = strdup(inet_ntoa(addr.sin_addr));
     85 	return str;
     86 }
     87 
     88 char *raw2host(char *raw, int len) {
     89 	char *str;
     90 #if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H
     91 	struct hostent *hp;
     92 
     93 	if (! host_lookup) {
     94 		return strdup("unknown");
     95 	}
     96 
     97 	hp = gethostbyaddr(raw, len, AF_INET);
     98 	if (!hp) {
     99 		return strdup(inet_ntoa(*((struct in_addr *)raw)));
    100 	}
    101 	str = strdup(hp->h_name);
    102 #else
    103 	str = strdup("unknown");
    104 #endif
    105 	return str;
    106 }
    107 
    108 char *raw2ip(char *raw) {
    109 	return strdup(inet_ntoa(*((struct in_addr *)raw)));
    110 }
    111 
    112 char *ip2host(char *ip) {
    113 	char *str;
    114 #if LIBVNCSERVER_HAVE_NETDB_H && LIBVNCSERVER_HAVE_NETINET_IN_H
    115 	struct hostent *hp;
    116 	in_addr_t iaddr;
    117 
    118 	if (! host_lookup) {
    119 		return strdup("unknown");
    120 	}
    121 
    122 	iaddr = inet_addr(ip);
    123 	if (iaddr == htonl(INADDR_NONE)) {
    124 		return strdup("unknown");
    125 	}
    126 
    127 	hp = gethostbyaddr((char *)&iaddr, sizeof(in_addr_t), AF_INET);
    128 	if (!hp) {
    129 		return strdup("unknown");
    130 	}
    131 	str = strdup(hp->h_name);
    132 #else
    133 	str = strdup("unknown");
    134 #endif
    135 	return str;
    136 }
    137 
    138 int ipv6_ip(char *host_in) {
    139 	char *p, *host, a[2];
    140 	int ncol = 0, nhex = 0;
    141 
    142 	if (host_in[0] == '[')  {
    143 		host = host_in + 1;
    144 	} else {
    145 		host = host_in;
    146 	}
    147 
    148 	if (strstr(host, "::ffff:") == host || strstr(host, "::FFFF:") == host) {
    149 		return dotted_ip(host + strlen("::ffff:"), 0);
    150 	}
    151 
    152 	a[1] = '\0';
    153 
    154 	p = host;
    155 	while (*p != '\0' && *p != '%' && *p != ']') {
    156 		if (*p == ':') {
    157 			ncol++;
    158 		} else {
    159 			nhex++;
    160 		}
    161 		a[0] = *p;
    162 		if (strpbrk(a, ":abcdef0123456789") == a) {
    163 			p++;
    164 			continue;
    165 		}
    166 		return 0;
    167 	}
    168 	if (ncol < 2 || ncol > 8 || nhex == 0) {
    169 		return 0;
    170 	} else {
    171 		return 1;
    172 	}
    173 }
    174 
    175 int dotted_ip(char *host, int partial) {
    176 	int len, dots = 0;
    177 	char *p = host;
    178 
    179 	if (!host) {
    180 		return 0;
    181 	}
    182 
    183 	if (!isdigit((unsigned char) host[0])) {
    184 		return 0;
    185 	}
    186 
    187 	len = strlen(host);
    188 	if (!partial && !isdigit((unsigned char) host[len-1])) {
    189 		return 0;
    190 	}
    191 
    192 	while (*p != '\0') {
    193 		if (*p == '.') dots++;
    194 		if (*p == '.' || isdigit((unsigned char) (*p))) {
    195 			p++;
    196 			continue;
    197 		}
    198 		return 0;
    199 	}
    200 	if (!partial && dots != 3) {
    201 		return 0;
    202 	}
    203 	return 1;
    204 }
    205 
    206 static int get_port(int sock, int remote) {
    207 	struct sockaddr_in saddr;
    208 	unsigned int saddr_len;
    209 	int saddr_port;
    210 
    211 	saddr_len = sizeof(saddr);
    212 	memset(&saddr, 0, sizeof(saddr));
    213 	saddr_port = -1;
    214 	if (remote) {
    215 		if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) {
    216 			saddr_port = ntohs(saddr.sin_port);
    217 		}
    218 	} else {
    219 		if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) {
    220 			saddr_port = ntohs(saddr.sin_port);
    221 		}
    222 	}
    223 	return saddr_port;
    224 }
    225 
    226 int get_remote_port(int sock) {
    227 	return get_port(sock, 1);
    228 }
    229 
    230 int get_local_port(int sock) {
    231 	return get_port(sock, 0);
    232 }
    233 
    234 static char *get_host(int sock, int remote) {
    235 	struct sockaddr_in saddr;
    236 	unsigned int saddr_len;
    237 	int saddr_port;
    238 	char *saddr_ip_str = NULL;
    239 
    240 	saddr_len = sizeof(saddr);
    241 	memset(&saddr, 0, sizeof(saddr));
    242 	saddr_port = -1;
    243 #if LIBVNCSERVER_HAVE_NETINET_IN_H
    244 	if (remote) {
    245 		if (!getpeername(sock, (struct sockaddr *)&saddr, &saddr_len)) {
    246 			saddr_ip_str = inet_ntoa(saddr.sin_addr);
    247 		}
    248 	} else {
    249 		if (!getsockname(sock, (struct sockaddr *)&saddr, &saddr_len)) {
    250 			saddr_ip_str = inet_ntoa(saddr.sin_addr);
    251 		}
    252 	}
    253 #endif
    254 	if (! saddr_ip_str) {
    255 		saddr_ip_str = "unknown";
    256 	}
    257 	return strdup(saddr_ip_str);
    258 }
    259 
    260 char *get_remote_host(int sock) {
    261 	return get_host(sock, 1);
    262 }
    263 
    264 char *get_local_host(int sock) {
    265 	return get_host(sock, 0);
    266 }
    267 
    268 char *ident_username(rfbClientPtr client) {
    269 	ClientData *cd = (ClientData *) client->clientData;
    270 	char *str, *newhost, *user = NULL, *newuser = NULL;
    271 	int len;
    272 
    273 	if (cd) {
    274 		user = cd->username;
    275 	}
    276 	if (!user || *user == '\0') {
    277 		int n, sock, ok = 0;
    278 		int block = 0;
    279 		int refused = 0;
    280 
    281 		/*
    282 		 * need to check to see if the operation will block for
    283 		 * a long time: a firewall may just ignore our packets.
    284 		 */
    285 #if LIBVNCSERVER_HAVE_FORK
    286 	    {	pid_t pid, pidw;
    287 		int rc;
    288 		if ((pid = fork()) > 0) {
    289 			usleep(100 * 1000);	/* 0.1 sec for quick success or refusal */
    290 			pidw = waitpid(pid, &rc, WNOHANG);
    291 			if (pidw <= 0) {
    292 				usleep(1500 * 1000);	/* 1.5 sec */
    293 				pidw = waitpid(pid, &rc, WNOHANG);
    294 				if (pidw <= 0) {
    295 					int rc2;
    296 					rfbLog("ident_username: set block=1 (hung)\n");
    297 					block = 1;
    298 					kill(pid, SIGTERM);
    299 					usleep(100 * 1000);
    300 					waitpid(pid, &rc2, WNOHANG);
    301 				}
    302 			}
    303 			if (pidw > 0 && !block) {
    304 				if (WIFEXITED(rc) && WEXITSTATUS(rc) == 1) {
    305 					rfbLog("ident_username: set refused=1 (exit)\n");
    306 					refused = 1;
    307 				}
    308 			}
    309 		} else if (pid == -1) {
    310 			;
    311 		} else {
    312 			/* child */
    313 			signal(SIGHUP,  SIG_DFL);
    314 			signal(SIGINT,  SIG_DFL);
    315 			signal(SIGQUIT, SIG_DFL);
    316 			signal(SIGTERM, SIG_DFL);
    317 
    318 			if ((sock = connect_tcp(client->host, 113)) < 0) {
    319 				exit(1);
    320 			} else {
    321 				close(sock);
    322 				exit(0);
    323 			}
    324 		}
    325 	    }
    326 #endif
    327 		if (block || refused) {
    328 			;
    329 		} else if ((sock = connect_tcp(client->host, 113)) < 0) {
    330 			rfbLog("ident_username: could not connect to ident: %s:%d\n",
    331 			    client->host, 113);
    332 		} else {
    333 			char msg[128];
    334 			int ret;
    335 			fd_set rfds;
    336 			struct timeval tv;
    337 			int rport = get_remote_port(client->sock);
    338 			int lport = get_local_port(client->sock);
    339 
    340 			sprintf(msg, "%d, %d\r\n", rport, lport);
    341 			n = write(sock, msg, strlen(msg));
    342 
    343 			FD_ZERO(&rfds);
    344 			FD_SET(sock, &rfds);
    345 			tv.tv_sec  = 3;
    346 			tv.tv_usec = 0;
    347 			ret = select(sock+1, &rfds, NULL, NULL, &tv);
    348 
    349 			if (ret > 0) {
    350 				int i;
    351 				char *q, *p;
    352 				for (i=0; i < (int) sizeof(msg); i++) {
    353 					msg[i] = '\0';
    354 				}
    355 				usleep(250*1000);
    356 				n = read(sock, msg, 127);
    357 				close(sock);
    358 				if (n <= 0) goto badreply;
    359 
    360 				/* 32782 , 6000 : USERID : UNIX :runge */
    361 				q = strstr(msg, "USERID");
    362 				if (!q) goto badreply;
    363 				q = strstr(q, ":");
    364 				if (!q) goto badreply;
    365 				q++;
    366 				q = strstr(q, ":");
    367 				if (!q) goto badreply;
    368 				q++;
    369 				q = lblanks(q);
    370 				p = q;
    371 				while (*p) {
    372 					if (*p == '\r' || *p == '\n') {
    373 						*p = '\0';
    374 					}
    375 					p++;
    376 				}
    377 				ok = 1;
    378 				if (strlen(q) > 24) {
    379 					*(q+24) = '\0';
    380 				}
    381 				newuser = strdup(q);
    382 
    383 				badreply:
    384 				n = 0;	/* avoid syntax error */
    385 			} else {
    386 				close(sock);
    387 			}
    388 		}
    389 		if (! ok || !newuser) {
    390 			newuser = strdup("unknown-user");
    391 		}
    392 		if (cd) {
    393 			if (cd->username) {
    394 				free(cd->username);
    395 			}
    396 			cd->username = newuser;
    397 		}
    398 		user = newuser;
    399 	}
    400 	if (!strcmp(user, "unknown-user") && cd && cd->unixname[0] != '\0') {
    401 		user = cd->unixname;
    402 	}
    403 	if (unixpw && openssl_last_ip && strstr("UNIX:", user) != user) {
    404 		newhost = ip2host(openssl_last_ip);
    405 	} else {
    406 		newhost = ip2host(client->host);
    407 	}
    408 	len = strlen(user) + 1 + strlen(newhost) + 1;
    409 	str = (char *) malloc(len);
    410 	sprintf(str, "%s@%s", user, newhost);
    411 	free(newhost);
    412 	return str;
    413 }
    414 
    415 int find_free_port(int start, int end) {
    416 	int port;
    417 	if (start <= 0) {
    418 		start = 1024;
    419 	}
    420 	if (end <= 0) {
    421 		end = 65530;
    422 	}
    423 	for (port = start; port <= end; port++)  {
    424 		int sock = listen_tcp(port, htonl(INADDR_ANY), 0);
    425 		if (sock >= 0) {
    426 			close(sock);
    427 			return port;
    428 		}
    429 	}
    430 	return 0;
    431 }
    432 
    433 int find_free_port6(int start, int end) {
    434 	int port;
    435 	if (start <= 0) {
    436 		start = 1024;
    437 	}
    438 	if (end <= 0) {
    439 		end = 65530;
    440 	}
    441 	for (port = start; port <= end; port++)  {
    442 		int sock = listen6(port);
    443 		if (sock >= 0) {
    444 			close(sock);
    445 			return port;
    446 		}
    447 	}
    448 	return 0;
    449 }
    450 
    451 int have_ssh_env(void) {
    452 	char *str, *p = getenv("SSH_CONNECTION");
    453 	char *rhost, *rport, *lhost, *lport;
    454 
    455 	if (! p) {
    456 		char *q = getenv("SSH_CLIENT");
    457 		if (! q) {
    458 			return 0;
    459 		}
    460 		if (strstr(q, "127.0.0.1") != NULL) {
    461 			return 0;
    462 		}
    463 		return 1;
    464 	}
    465 
    466 	if (strstr(p, "127.0.0.1") != NULL) {
    467 		return 0;
    468 	}
    469 
    470 	str = strdup(p);
    471 
    472 	p = strtok(str, " ");
    473 	rhost = p;
    474 
    475 	p = strtok(NULL, " ");
    476 	if (! p) goto fail;
    477 
    478 	rport = p;
    479 
    480 	p = strtok(NULL, " ");
    481 	if (! p) goto fail;
    482 
    483 	lhost = p;
    484 
    485 	p = strtok(NULL, " ");
    486 	if (! p) goto fail;
    487 
    488 	lport = p;
    489 
    490 if (0) fprintf(stderr, "%d/%d - '%s' '%s'\n", atoi(rport), atoi(lport), rhost, lhost);
    491 
    492 	if (atoi(rport) <= 16 || atoi(rport) > 65535) {
    493 		goto fail;
    494 	}
    495 	if (atoi(lport) <= 16 || atoi(lport) > 65535) {
    496 		goto fail;
    497 	}
    498 
    499 	if (!strcmp(rhost, lhost)) {
    500 		goto fail;
    501 	}
    502 
    503 	free(str);
    504 
    505 	return 1;
    506 
    507 	fail:
    508 
    509 	free(str);
    510 
    511 	return 0;
    512 }
    513 
    514 char *ipv6_getnameinfo(struct sockaddr *paddr, int addrlen) {
    515 #if X11VNC_IPV6
    516 	char name[200];
    517 	if (noipv6) {
    518 		return strdup("unknown");
    519 	}
    520 	if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, 0) == 0) {
    521 		return strdup(name);
    522 	}
    523 #endif
    524 	return strdup("unknown");
    525 }
    526 
    527 char *ipv6_getipaddr(struct sockaddr *paddr, int addrlen) {
    528 #if X11VNC_IPV6 && defined(NI_NUMERICHOST)
    529 	char name[200];
    530 	if (noipv6) {
    531 		return strdup("unknown");
    532 	}
    533 	if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, NI_NUMERICHOST) == 0) {
    534 		return strdup(name);
    535 	}
    536 #endif
    537 	return strdup("unknown");
    538 }
    539 
    540 int listen6(int port) {
    541 #if X11VNC_IPV6
    542 	struct sockaddr_in6 sin;
    543 	int fd = -1, one = 1;
    544 
    545 	if (noipv6) {
    546 		return -1;
    547 	}
    548 	if (port <= 0 || 65535 < port) {
    549 		/* for us, invalid port means do not listen. */
    550 		return -1;
    551 	}
    552 
    553 	fd = socket(AF_INET6, SOCK_STREAM, 0);
    554 	if (fd < 0) {
    555 		rfbLogPerror("listen6: socket");
    556 		rfbLog("(Ignore the above error if this system is IPv4-only.)\n");
    557 		return -1;
    558 	}
    559 
    560 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) {
    561 		rfbLogPerror("listen6: setsockopt SO_REUSEADDR");
    562 		close(fd);
    563 		return -1;
    564 	}
    565 
    566 #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
    567 	if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) {
    568 		rfbLogPerror("listen6: setsockopt IPV6_V6ONLY");
    569 		close(fd);
    570 		return -1;
    571 	}
    572 #endif
    573 
    574 	memset((char *)&sin, 0, sizeof(sin));
    575 	sin.sin6_family = AF_INET6;
    576 	sin.sin6_port   = htons(port);
    577 	sin.sin6_addr   = in6addr_any;
    578 
    579 	if (listen_str6) {
    580 		if (!strcmp(listen_str6, "localhost") || !strcmp(listen_str6, "::1")) {
    581 			sin.sin6_addr = in6addr_loopback;
    582 		} else {
    583 			int err;
    584 			struct addrinfo *ai;
    585 			struct addrinfo hints;
    586 			char service[32];
    587 
    588 			memset(&hints, 0, sizeof(hints));
    589 			sprintf(service, "%d", port);
    590 
    591 			hints.ai_family = AF_INET6;
    592 			hints.ai_socktype = SOCK_STREAM;
    593 #ifdef AI_ADDRCONFIG
    594 			hints.ai_flags |= AI_ADDRCONFIG;
    595 #endif
    596 #ifdef AI_NUMERICHOST
    597 			if(ipv6_ip(listen_str6)) {
    598 				hints.ai_flags |= AI_NUMERICHOST;
    599 			}
    600 #endif
    601 #ifdef AI_NUMERICSERV
    602 			hints.ai_flags |= AI_NUMERICSERV;
    603 #endif
    604 			err = getaddrinfo(listen_str6, service, &hints, &ai);
    605 			if (err == 0) {
    606 				struct addrinfo *ap = ai;
    607 				err = 1;
    608 				while (ap != NULL) {
    609 					char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen);
    610 					if (!s) s = strdup("unknown");
    611 
    612 					rfbLog("listen6: checking: %s family: %d\n", s, ap->ai_family);
    613 					if (ap->ai_family == AF_INET6) {
    614 						memcpy((char *)&sin, ap->ai_addr, sizeof(sin));
    615 						rfbLog("listen6: using:    %s scope_id: %d\n", s, sin.sin6_scope_id);
    616 						err = 0;
    617 						free(s);
    618 						break;
    619 					}
    620 					free(s);
    621 					ap = ap->ai_next;
    622 				}
    623 				freeaddrinfo(ai);
    624 			}
    625 
    626 			if (err != 0) {
    627 				rfbLog("Invalid or Unsupported -listen6 string: %s\n", listen_str6);
    628 				close(fd);
    629 				return -1;
    630 			}
    631 		}
    632 	} else if (allow_list && !strcmp(allow_list, "127.0.0.1")) {
    633 		sin.sin6_addr = in6addr_loopback;
    634 	} else if (listen_str) {
    635 		if (!strcmp(listen_str, "localhost")) {
    636 			sin.sin6_addr = in6addr_loopback;
    637 		}
    638 	}
    639 
    640 	if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
    641 		rfbLogPerror("listen6: bind");
    642 		close(fd);
    643 		return -1;
    644 	}
    645 	if (listen(fd, 32) < 0) {
    646 		rfbLogPerror("listen6: listen");
    647 		close(fd);
    648 		return -1;
    649 	}
    650 	return fd;
    651 #else
    652 	if (port) {}
    653 	return -1;
    654 #endif
    655 }
    656 
    657 #ifdef LIBVNCSERVER_HAVE_SYS_SOCKET_H
    658 #include <sys/un.h>
    659 #endif
    660 
    661 int listen_unix(char *file) {
    662 #if !defined(AF_UNIX) || !defined(LIBVNCSERVER_HAVE_SYS_SOCKET_H)
    663 	if (sock) {}
    664 	return -1;
    665 #else
    666 	int s, len;
    667 	struct sockaddr_un saun;
    668 
    669 	s = socket(AF_UNIX, SOCK_STREAM, 0);
    670 	if (s < 0) {
    671 		rfbLogPerror("listen_unix: socket");
    672 		return -1;
    673 	}
    674 	saun.sun_family = AF_UNIX;
    675 	strcpy(saun.sun_path, file);
    676 	unlink(file);
    677 
    678 	len = sizeof(saun.sun_family) + strlen(saun.sun_path);
    679 
    680 	if (bind(s, (struct sockaddr *)&saun, len) < 0) {
    681 		rfbLogPerror("listen_unix: bind");
    682 		close(s);
    683 		return -1;
    684 	}
    685 
    686 	if (listen(s, 32) < 0) {
    687 		rfbLogPerror("listen_unix: listen");
    688 		close(s);
    689 		return -1;
    690 	}
    691 	rfbLog("listening on unix socket: %s fd=%d\n", file, s);
    692 	return s;
    693 #endif
    694 }
    695 
    696 int accept_unix(int s) {
    697 #if !defined(AF_UNIX) || !defined(LIBVNCSERVER_HAVE_SYS_SOCKET_H)
    698 	if (s) {}
    699 	return -1;
    700 #else
    701 	int fd, fromlen;
    702 	struct sockaddr_un fsaun;
    703 
    704 	fd = accept(s, (struct sockaddr *)&fsaun, &fromlen);
    705 	if (fd < 0) {
    706 		rfbLogPerror("accept_unix: accept");
    707 		return -1;
    708 	}
    709 	return fd;
    710 #endif
    711 }
    712 
    713 int connect_tcp(char *host, int port) {
    714 	double t0 = dnow();
    715 	int fd = -1;
    716 	int fail4 = noipv4;
    717 	if (getenv("IPV4_FAILS")) {
    718 		fail4 = 2;
    719 	}
    720 
    721 	rfbLog("connect_tcp: trying:   %s %d\n", host, port);
    722 
    723 	if (fail4) {
    724 		if (fail4 > 1) {
    725 			rfbLog("TESTING: IPV4_FAILS for connect_tcp.\n");
    726 		}
    727 	} else {
    728 		fd = rfbConnectToTcpAddr(host, port);
    729 	}
    730 
    731 	if (fd >= 0) {
    732 		return fd;
    733 	}
    734 	rfbLogPerror("connect_tcp: connection failed");
    735 
    736 	if (dnow() - t0 < 4.0) {
    737 		rfbLog("connect_tcp: re-trying %s %d\n", host, port);
    738 		usleep (100 * 1000);
    739 		if (!fail4) {
    740 			fd = rfbConnectToTcpAddr(host, port);
    741 		}
    742 		if (fd < 0) {
    743 			rfbLogPerror("connect_tcp: connection failed");
    744 		}
    745 	}
    746 
    747 	if (fd < 0 && !noipv6) {
    748 #if X11VNC_IPV6
    749 		int err;
    750 		struct addrinfo *ai;
    751 		struct addrinfo hints;
    752 		char service[32], *host2, *q;
    753 
    754 		rfbLog("connect_tcp: trying IPv6 %s %d\n", host, port);
    755 
    756 		memset(&hints, 0, sizeof(hints));
    757 		sprintf(service, "%d", port);
    758 
    759 		hints.ai_family = AF_UNSPEC;
    760 		hints.ai_socktype = SOCK_STREAM;
    761 #ifdef AI_ADDRCONFIG
    762 		hints.ai_flags |= AI_ADDRCONFIG;
    763 #endif
    764 		if(ipv6_ip(host)) {
    765 #ifdef AI_NUMERICHOST
    766 			rfbLog("connect_tcp[ipv6]: setting AI_NUMERICHOST for %s\n", host);
    767 			hints.ai_flags |= AI_NUMERICHOST;
    768 #endif
    769 		}
    770 #ifdef AI_NUMERICSERV
    771 		hints.ai_flags |= AI_NUMERICSERV;
    772 #endif
    773 
    774 		if (!strcmp(host, "127.0.0.1")) {
    775 			host2 = strdup("::1");
    776 		} else if (host[0] == '[') {
    777 			host2 = strdup(host+1);
    778 		} else {
    779 			host2 = strdup(host);
    780 		}
    781 		q = strrchr(host2, ']');
    782 		if (q) {
    783 			*q = '\0';
    784 		}
    785 
    786 		err = getaddrinfo(host2, service, &hints, &ai);
    787 		if (err != 0) {
    788 			rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err));
    789 			usleep(100 * 1000);
    790 			err = getaddrinfo(host2, service, &hints, &ai);
    791 		}
    792 		free(host2);
    793 
    794 		if (err != 0) {
    795 			rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err));
    796 		} else {
    797 			struct addrinfo *ap = ai;
    798 			while (ap != NULL) {
    799 				int sock;
    800 
    801 				if (fail4) {
    802 					struct sockaddr_in6 *s6ptr;
    803 					if (ap->ai_family != AF_INET6) {
    804 						rfbLog("connect_tcp[ipv6]: skipping AF_INET address under -noipv4\n");
    805 						ap = ap->ai_next;
    806 						continue;
    807 					}
    808 #ifdef IN6_IS_ADDR_V4MAPPED
    809 					s6ptr = (struct sockaddr_in6 *) ap->ai_addr;
    810 					if (IN6_IS_ADDR_V4MAPPED(&(s6ptr->sin6_addr))) {
    811 						rfbLog("connect_tcp[ipv6]: skipping V4MAPPED address under -noipv4\n");
    812 						ap = ap->ai_next;
    813 						continue;
    814 					}
    815 #endif
    816 				}
    817 
    818 				sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
    819 
    820 				if (sock == -1) {
    821 					rfbLogPerror("connect_tcp[ipv6]: socket");
    822 					if (0) rfbLog("(Ignore the above error if this system is IPv4-only.)\n");
    823 				} else {
    824 					int res = -1, dmsg = 0;
    825 					char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen);
    826 					if (!s) s = strdup("unknown");
    827 
    828 					rfbLog("connect_tcp[ipv6]: trying sock=%d fam=%d proto=%d using %s\n",
    829 					    sock, ap->ai_family, ap->ai_protocol, s);
    830 					res = connect(sock, ap->ai_addr, ap->ai_addrlen);
    831 #if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
    832 					if (res != 0) {
    833 						int zero = 0;
    834 						rfbLogPerror("connect_tcp[ipv6]: connect");
    835 						dmsg = 1;
    836 						if (setsockopt(sock, SOL_IPV6, IPV6_V6ONLY, (char *)&zero, sizeof(zero)) == 0) {
    837 							rfbLog("connect_tcp[ipv6]: trying again with IPV6_V6ONLY=0\n");
    838 							res = connect(sock, ap->ai_addr, ap->ai_addrlen);
    839 							dmsg = 0;
    840 						} else {
    841 							rfbLogPerror("connect_tcp[ipv6]: setsockopt IPV6_V6ONLY");
    842 						}
    843 					}
    844 #endif
    845 					if (res == 0) {
    846 						rfbLog("connect_tcp[ipv6]: connect OK\n");
    847 						fd = sock;
    848 						if (!ipv6_client_ip_str) {
    849 							ipv6_client_ip_str = strdup(s);
    850 						}
    851 						free(s);
    852 						break;
    853 					} else {
    854 						if (!dmsg) rfbLogPerror("connect_tcp[ipv6]: connect");
    855 						close(sock);
    856 					}
    857 					free(s);
    858 				}
    859 				ap = ap->ai_next;
    860 			}
    861 			freeaddrinfo(ai);
    862 		}
    863 #endif
    864 	}
    865 	if (fd < 0 && !fail4) {
    866 		/* this is a kludge for IPv4-only machines getting v4mapped string. */
    867 		char *q, *host2;
    868 		if (host[0] == '[') {
    869 			host2 = strdup(host+1);
    870 		} else {
    871 			host2 = strdup(host);
    872 		}
    873 		q = strrchr(host2, ']');
    874 		if (q) {
    875 			*q = '\0';
    876 		}
    877 		if (strstr(host2, "::ffff:") == host2 || strstr(host2, "::FFFF:") == host2) {
    878 			char *host3 = host2 + strlen("::ffff:");
    879 			if (dotted_ip(host3, 0)) {
    880 				rfbLog("connect_tcp[ipv4]: trying fallback to IPv4 for %s\n", host2);
    881 				fd = rfbConnectToTcpAddr(host3, port);
    882 				if (fd < 0) {
    883 					rfbLogPerror("connect_tcp[ipv4]: connection failed");
    884 				}
    885 			}
    886 		}
    887 		free(host2);
    888 	}
    889 	return fd;
    890 }
    891 
    892 int listen_tcp(int port, in_addr_t iface, int try6) {
    893 	int fd = -1;
    894 	int fail4 = noipv4;
    895 	if (getenv("IPV4_FAILS")) {
    896 		fail4 = 2;
    897 	}
    898 
    899 	if (port <= 0 || 65535 < port) {
    900 		/* for us, invalid port means do not listen. */
    901 		return -1;
    902 	}
    903 
    904 	if (fail4) {
    905 		if (fail4 > 1) {
    906 			rfbLog("TESTING: IPV4_FAILS for listen_tcp: port=%d try6=%d\n", port, try6);
    907 		}
    908 	} else {
    909 		fd = rfbListenOnTCPPort(port, iface);
    910 	}
    911 
    912 	if (fd >= 0) {
    913 		return fd;
    914 	}
    915 	if (fail4 > 1) {
    916 		rfbLogPerror("listen_tcp: listen failed");
    917 	}
    918 
    919 	if (fd < 0 && try6 && ipv6_listen && !noipv6) {
    920 #if X11VNC_IPV6
    921 		char *save = listen_str6;
    922 		if (iface == htonl(INADDR_LOOPBACK)) {
    923 			listen_str6 = "localhost";
    924 			rfbLog("listen_tcp: retrying on IPv6 in6addr_loopback ...\n");
    925 			fd = listen6(port);
    926 		} else if (iface == htonl(INADDR_ANY)) {
    927 			listen_str6 = NULL;
    928 			rfbLog("listen_tcp: retrying on IPv6 in6addr_any ...\n");
    929 			fd = listen6(port);
    930 		}
    931 		listen_str6 = save;
    932 #endif
    933 	}
    934 	return fd;
    935 }
    936 
    937