From 2a8ba97ec5b0f7fbfcfc8adab6732a95e95c7204 Mon Sep 17 00:00:00 2001
From: runge <runge@karlrunge.com>
Date: Fri, 9 Apr 2010 20:09:15 -0400
Subject: x11vnc: exit(1) for -connect_or_exit failure, quiet query mode for
 grab_state, pointer_pos, etc. ipv6 support. STUNNEL_LISTEN for particular
 interface. -input_eagerly in addition to -allinput.  quiet Xinerama message.

---
 x11vnc/inet.c | 327 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 324 insertions(+), 3 deletions(-)

(limited to 'x11vnc/inet.c')

diff --git a/x11vnc/inet.c b/x11vnc/inet.c
index d63aef4..60e26f5 100644
--- a/x11vnc/inet.c
+++ b/x11vnc/inet.c
@@ -44,6 +44,7 @@ char *host2ip(char *host);
 char *raw2host(char *raw, int len);
 char *raw2ip(char *raw);
 char *ip2host(char *ip);
+int ipv6_ip(char *host);
 int dotted_ip(char *host);
 int get_remote_port(int sock);
 int get_local_port(int sock);
@@ -51,7 +52,13 @@ char *get_remote_host(int sock);
 char *get_local_host(int sock);
 char *ident_username(rfbClientPtr client);
 int find_free_port(int start, int end);
+int find_free_port6(int start, int end);
 int have_ssh_env(void);
+char *ipv6_getnameinfo(struct sockaddr *paddr, int addrlen);
+char *ipv6_getipaddr(struct sockaddr *paddr, int addrlen);
+int listen6(int port);
+int connect_tcp(char *host, int port);
+int listen_tcp(int port, in_addr_t iface, int try6);
 
 static int get_port(int sock, int remote);
 static char *get_host(int sock, int remote);
@@ -126,6 +133,43 @@ char *ip2host(char *ip) {
 	return str;
 }
 
+int ipv6_ip(char *host_in) {
+	char *p, *host, a[2];
+	int ncol = 0, nhex = 0;
+
+	if (host_in[0] == '[')  {
+		host = host_in + 1;
+	} else {
+		host = host_in;
+	}
+
+	if (strstr(host, "::ffff:") == host) {
+		return dotted_ip(host + strlen("::ffff:"));
+	}
+
+	a[1] = '\0';
+
+	p = host;
+	while (*p != '\0' && *p != '%' && *p != ']') {
+		if (*p == ':') {
+			ncol++;
+		} else {
+			nhex++;
+		}
+		a[0] = *p;
+		if (strpbrk(a, ":abcdef0123456789") == a) {
+			p++;
+			continue;
+		}
+		return 0;
+	}
+	if (ncol < 2 || ncol > 8 || nhex == 0) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+
 int dotted_ip(char *host) {
 	char *p = host;
 	while (*p != '\0') {
@@ -251,7 +295,7 @@ char *ident_username(rfbClientPtr client) {
 			signal(SIGQUIT, SIG_DFL);
 			signal(SIGTERM, SIG_DFL);
 
-			if ((sock = rfbConnectToTcpAddr(client->host, 113)) < 0) {
+			if ((sock = connect_tcp(client->host, 113)) < 0) {
 				exit(1);
 			} else {
 				close(sock);
@@ -262,7 +306,7 @@ char *ident_username(rfbClientPtr client) {
 #endif
 		if (block || refused) {
 			;
-		} else if ((sock = rfbConnectToTcpAddr(client->host, 113)) < 0) {
+		} else if ((sock = connect_tcp(client->host, 113)) < 0) {
 			rfbLog("ident_username: could not connect to ident: %s:%d\n",
 			    client->host, 113);
 		} else {
@@ -356,7 +400,25 @@ int find_free_port(int start, int end) {
 		end = 65530;
 	}
 	for (port = start; port <= end; port++)  {
-		int sock = rfbListenOnTCPPort(port, htonl(INADDR_ANY));
+		int sock = listen_tcp(port, htonl(INADDR_ANY), 0);
+		if (sock >= 0) {
+			close(sock);
+			return port;
+		}
+	}
+	return 0;
+}
+
+int find_free_port6(int start, int end) {
+	int port;
+	if (start <= 0) {
+		start = 1024;
+	}
+	if (end <= 0) {
+		end = 65530;
+	}
+	for (port = start; port <= end; port++)  {
+		int sock = listen6(port);
 		if (sock >= 0) {
 			close(sock);
 			return port;
@@ -428,3 +490,262 @@ if (0) fprintf(stderr, "%d/%d - '%s' '%s'\n", atoi(rport), atoi(lport), rhost, l
 	return 0;
 }
 
+char *ipv6_getnameinfo(struct sockaddr *paddr, int addrlen) {
+#if X11VNC_IPV6
+	char name[200];
+	if (noipv6) {
+		return strdup("unknown");
+	}
+	if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, 0) == 0) {
+		return strdup(name);
+	}
+#endif
+	return strdup("unknown");
+}
+
+char *ipv6_getipaddr(struct sockaddr *paddr, int addrlen) {
+#if X11VNC_IPV6
+	char name[200];
+	if (noipv6) {
+		return strdup("unknown");
+	}
+	if (getnameinfo(paddr, addrlen, name, sizeof(name), NULL, 0, NI_NUMERICHOST) == 0) {
+		return strdup(name);
+	}
+#endif
+	return strdup("unknown");
+}
+
+int listen6(int port) {
+#if X11VNC_IPV6
+	struct sockaddr_in6 sin;
+	int fd = -1, one = 1;
+
+	if (noipv6) {
+		return -1;
+	}
+
+	fd = socket(AF_INET6, SOCK_STREAM, 0);
+	if (fd < 0) {
+		rfbLogPerror("listen6: socket");
+		return -1;
+	}
+
+	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) {
+		rfbLogPerror("listen6: setsockopt1"); 
+		close(fd);
+		return -1;
+	}
+
+#if defined(SOL_IPV6) && defined(IPV6_V6ONLY)
+	if (setsockopt(fd, SOL_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) {
+		rfbLogPerror("listen6: setsockopt2"); 
+		close(fd);
+		return -1;
+	}
+#endif
+
+	memset((char *)&sin, 0, sizeof(sin));
+	sin.sin6_family = AF_INET6;
+	sin.sin6_port   = htons(port);
+	sin.sin6_addr   = in6addr_any;
+
+	if (listen_str6) {
+		if (!strcmp(listen_str6, "localhost")) {
+			sin.sin6_addr = in6addr_loopback;
+		} else if (ipv6_ip(listen_str6)) {
+			int err = inet_pton(AF_INET6, listen_str6, &(sin.sin6_addr));
+			if (err <= 0) {
+				rfbLog("inet_pton[%d] failed -listen6 %s\n", err, listen_str6);
+				close(fd);
+				return -1;
+			}
+		} else {
+			rfbLog("Unsupported -listen6 string: %s\n", listen_str6);
+			close(fd);
+			return -1;
+		}
+	} else if (allow_list && !strcmp(allow_list, "127.0.0.1")) {
+		sin.sin6_addr = in6addr_loopback;
+	} else if (listen_str) {
+		if (!strcmp(listen_str, "localhost")) {
+			sin.sin6_addr = in6addr_loopback;
+		}
+	}
+
+	if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+		rfbLogPerror("listen6: bind"); 
+		close(fd);
+		return -1;
+	}
+	if (listen(fd, 32) < 0) {
+		rfbLogPerror("listen6: listen");
+		close(fd);
+		return -1;
+	}
+	return fd;
+#else
+	if (port) {}
+	return -1;
+#endif
+}
+
+int connect_tcp(char *host, int port) {
+	double t0 = dnow();
+	int fd = -1;
+	int fail4 = noipv4;
+	if (getenv("IPV4_FAILS")) {
+		fail4 = 2;
+	}
+
+	rfbLog("connect_tcp: trying:   %s %d\n", host, port);
+
+	if (fail4) {
+		if (fail4 > 1) {
+			rfbLog("TESTING: IPV4_FAILS for connect_tcp.\n");
+		}
+	} else {
+		fd = rfbConnectToTcpAddr(host, port);
+	}
+
+	if (fd >= 0) {
+		return fd;
+	}
+	rfbLogPerror("connect_tcp: connection failed");
+
+	if (dnow() - t0 < 4.0) {
+		rfbLog("connect_tcp: re-trying %s %d\n", host, port);
+		usleep (100 * 1000);
+		if (!fail4) {
+			fd = rfbConnectToTcpAddr(host, port);
+		}
+		if (fd < 0) {
+			rfbLogPerror("connect_tcp: connection failed");
+		}
+	}
+
+	if (fd < 0 && !noipv6) {
+#if X11VNC_IPV6 && defined(AI_ADDRCONFIG)
+		int err;
+		struct addrinfo *ai;
+		struct addrinfo hints;
+		char service[32], *host2, *q;
+
+		rfbLog("connect_tcp: trying IPv6 %s %d\n", host, port);
+
+		memset(&hints, 0, sizeof(hints));
+		sprintf(service, "%d", port);
+
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_socktype = SOCK_STREAM;
+		hints.ai_flags = AI_ADDRCONFIG;
+		if(ipv6_ip(host)) {
+#ifdef AI_NUMERICHOST
+			rfbLog("connect_tcp[ipv6]: setting AI_NUMERICHOST for %s\n", host);
+			hints.ai_flags |= AI_NUMERICHOST;
+#else
+			rfbLog("connect_tcp[ipv6]: no AI_NUMERICHOST for %s\n", host);
+#endif
+		}
+#ifdef AI_NUMERICSERV
+		hints.ai_flags |= AI_NUMERICSERV;
+#endif
+
+		if (!strcmp(host, "127.0.0.1")) {
+			host2 = strdup("::1");
+		} else if (host[0] == '[') {
+			host2 = strdup(host+1);
+		} else {
+			host2 = strdup(host);
+		}
+		q = strrchr(host2, ']');
+		if (q) {
+			*q = '\0';
+		}
+		
+
+		err = getaddrinfo(host2, service, &hints, &ai);
+		if (err != 0) {
+			rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err));
+			usleep(100 * 1000);
+			err = getaddrinfo(host2, service, &hints, &ai);
+		}
+		free(host2);
+
+		if (err != 0) {
+			rfbLog("connect_tcp[ipv6]: getaddrinfo[%d]: %s\n", err, gai_strerror(err));
+		} else {
+			struct addrinfo *ap = ai;
+			while (ap != NULL) {
+				int sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
+				if (sock == -1) {
+					rfbLogPerror("connect_tcp[ipv6]: socket");
+				} else {
+					char *s = ipv6_getipaddr(ap->ai_addr, ap->ai_addrlen);
+					if (!s) s = strdup("unknown");
+					rfbLog("connect_tcp[ipv6]: trying sock=%d using %s\n", sock, s);
+					if (connect(sock, ap->ai_addr, ap->ai_addrlen) == 0) {
+						rfbLog("connect_tcp[ipv6]: connect OK\n");
+						fd = sock;
+						if (!ipv6_client_ip_str) {
+							ipv6_client_ip_str = strdup(s);
+						}
+						free(s);
+						break;
+					} else {
+						close(sock);
+						rfbLogPerror("connect_tcp[ipv6]: connect");
+					}
+					free(s);
+				}
+				ap = ap->ai_next;
+			}
+			freeaddrinfo(ai);
+		}
+#endif
+	}
+	return fd;
+}
+
+int listen_tcp(int port, in_addr_t iface, int try6) {
+	int fd = -1;
+	int fail4 = noipv4;
+	if (getenv("IPV4_FAILS")) {
+		fail4 = 2;
+	}
+
+	if (fail4) {
+		if (fail4 > 1) {
+			rfbLog("TESTING: IPV4_FAILS for listen_tcp: port=%d try6=%d\n", port, try6);
+		}
+	} else {
+		fd = rfbListenOnTCPPort(port, iface);
+	}
+
+	if (fd >= 0) {
+		return fd;
+	}
+	if (fail4 > 1) {
+		rfbLogPerror("listen_tcp: listen failed");
+	}
+
+	if (fd < 0 && try6 && ipv6_listen && !noipv6) {
+#if X11VNC_IPV6
+		char *save = listen_str6;
+		if (iface == htonl(INADDR_LOOPBACK)) {
+			listen_str6 = "localhost";
+			rfbLog("listen_tcp: retrying on IPv6 in6addr_loopback ...\n");
+			fd = listen6(port);
+		} else if (iface == htonl(INADDR_ANY)) {
+			listen_str6 = NULL;
+			rfbLog("listen_tcp: retrying on IPv6 in6addr_any ...\n");
+			fd = listen6(port);
+		}
+		listen_str6 = save;
+#else
+		if (try6) {}
+#endif
+	}
+	return fd;
+}
+
-- 
cgit v1.2.1