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