From 83a7c713a99a65f910fabab1bb95428762f569fb Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 20 Feb 2012 15:52:19 +0100 Subject: [PATCH 1/8] IPv6 support for LibVNCServer, part one: accept IPv4 and IPv6 connections. This uses a separate-socket approach since there are systems that do not support dual binding sockets under *any* circumstances, for instance OpenBSD. Using separate sockets for IPv4 and IPv6 is thus more portable than having a v6 socket handle v4 connections as well. Signed-off-by: Christian Beier --- libvncserver/cargs.c | 23 ++++++ libvncserver/main.c | 38 ++++++--- libvncserver/rfbserver.c | 15 +++- libvncserver/sockets.c | 166 +++++++++++++++++++++++++++++++++++++-- rfb/rfb.h | 7 ++ 5 files changed, 230 insertions(+), 19 deletions(-) diff --git a/libvncserver/cargs.c b/libvncserver/cargs.c index 2e973e8..27ceb97 100644 --- a/libvncserver/cargs.c +++ b/libvncserver/cargs.c @@ -22,6 +22,9 @@ rfbUsage(void) rfbProtocolExtension* extension; fprintf(stderr, "-rfbport port TCP port for RFB protocol\n"); +#ifdef LIBVNCSERVER_IPv6 + fprintf(stderr, "-rfbportv6 port TCP6 port for RFB protocol\n"); +#endif fprintf(stderr, "-rfbwait time max time in ms to wait for RFB client\n"); fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n" " (use 'storepasswd' to create a password file)\n"); @@ -46,6 +49,10 @@ rfbUsage(void) fprintf(stderr, "-progressive height enable progressive updating for slow links\n"); fprintf(stderr, "-listen ipaddr listen for connections only on network interface with\n"); fprintf(stderr, " addr ipaddr. '-listen localhost' and hostname work too.\n"); +#ifdef LIBVNCSERVER_IPv6 + fprintf(stderr, "-listenv6 ipv6addr listen for IPv6 connections only on network interface with\n"); + fprintf(stderr, " addr ipv6addr. '-listen localhost' and hostname work too.\n"); +#endif for(extension=rfbGetExtensionIterator();extension;extension=extension->next) if(extension->usage) @@ -80,6 +87,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) return FALSE; } rfbScreen->port = atoi(argv[++i]); +#ifdef LIBVNCSERVER_IPv6 + } else if (strcmp(argv[i], "-rfbportv6") == 0) { /* -rfbportv6 port */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->ipv6port = atoi(argv[++i]); +#endif } else if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */ if (i + 1 >= *argc) { rfbUsage(); @@ -163,6 +178,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) if (! rfbStringToAddr(argv[++i], &(rfbScreen->listenInterface))) { return FALSE; } +#ifdef LIBVNCSERVER_IPv6 + } else if (strcmp(argv[i], "-listenv6") == 0) { /* -listenv6 ipv6addr */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->listen6Interface = argv[++i]; +#endif #ifdef LIBVNCSERVER_WITH_WEBSOCKETS } else if (strcmp(argv[i], "-sslkeyfile") == 0) { /* -sslkeyfile sslkeyfile */ if (i + 1 >= *argc) { diff --git a/libvncserver/main.c b/libvncserver/main.c index 0edf994..7ecc842 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -569,21 +569,37 @@ listenerRun(void *data) { rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data; int client_fd; - struct sockaddr_in peer; - rfbClientPtr cl; + struct sockaddr_storage peer; + rfbClientPtr cl = NULL; socklen_t len; - - len = sizeof(peer); + fd_set listen_fds; /* temp file descriptor list for select() */ /* TODO: this thread wont die by restarting the server */ /* TODO: HTTP is not handled */ - while ((client_fd = accept(screen->listenSock, - (struct sockaddr*)&peer, &len)) >= 0) { - cl = rfbNewClient(screen,client_fd); - len = sizeof(peer); - + while (1) { + client_fd = -1; + FD_ZERO(&listen_fds); + if(screen->listenSock >= 0) + FD_SET(screen->listenSock, &listen_fds); + if(screen->listen6Sock >= 0) + FD_SET(screen->listen6Sock, &listen_fds); + + if (select(screen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) { + rfbLogPerror("listenerRun: error in select"); + return NULL; + } + + /* there is something on the listening sockets, handle new connections */ + len = sizeof (peer); + if (FD_ISSET(screen->listenSock, &listen_fds)) + client_fd = accept(screen->listenSock, (struct sockaddr*)&peer, &len); + if (FD_ISSET(screen->listen6Sock, &listen_fds)) + client_fd = accept(screen->listen6Sock, (struct sockaddr*)&peer, &len); + + if(client_fd >= 0) + cl = rfbNewClient(screen,client_fd); if (cl && !cl->onHold ) - rfbStartOnHoldClient(cl); + rfbStartOnHoldClient(cl); } return(NULL); } @@ -809,6 +825,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->clientHead=NULL; screen->pointerClient=NULL; screen->port=5900; + screen->ipv6port=5900; screen->socketState=RFB_SOCKET_INIT; screen->inetdInitDone = FALSE; @@ -821,6 +838,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->maxFd=0; screen->listenSock=-1; + screen->listen6Sock=-1; screen->httpInitDone=FALSE; screen->httpEnableProxyConnect=FALSE; diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 9be255f..4db3f21 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -50,6 +50,7 @@ #ifdef LIBVNCSERVER_HAVE_NETINET_IN_H #include #include +#include #include #endif #endif @@ -270,8 +271,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbProtocolVersionMsg pv; rfbClientIteratorPtr iterator; rfbClientPtr cl,cl_; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); + struct sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); rfbProtocolExtension* extension; cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); @@ -294,7 +295,17 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, int one=1; getpeername(sock, (struct sockaddr *)&addr, &addrlen); +#ifdef LIBVNCSERVER_IPv6 + char host[1024]; + if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { + rfbLogPerror("rfbNewClient: error in getnameinfo"); + cl->host = strdup(""); + } + else + cl->host = strdup(host); +#else cl->host = strdup(inet_ntoa(addr.sin_addr)); +#endif rfbLog(" other clients:\n"); iterator = rfbGetClientIterator(rfbScreen); diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 415f712..132a721 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -137,6 +137,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) if(rfbScreen->autoPort) { int i; + FD_ZERO(&(rfbScreen->allFds)); + rfbLog("Autoprobing TCP port \n"); for (i = 5900; i < 6000; i++) { if ((rfbScreen->listenSock = rfbListenOnTCPPort(i, iface)) >= 0) { @@ -150,22 +152,52 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) return; } - rfbLog("Autoprobing selected port %d\n", rfbScreen->port); - FD_ZERO(&(rfbScreen->allFds)); + rfbLog("Autoprobing selected TCP port %d\n", rfbScreen->port); FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->listenSock; + +#ifdef LIBVNCSERVER_IPv6 + rfbLog("Autoprobing TCP6 port \n"); + for (i = 5900; i < 6000; i++) { + if ((rfbScreen->listen6Sock = rfbListenOnTCP6Port(i, rfbScreen->listen6Interface)) >= 0) { + rfbScreen->ipv6port = i; + break; + } + } + + if (i >= 6000) { + rfbLogPerror("Failure autoprobing"); + return; + } + + rfbLog("Autoprobing selected TCP6 port %d\n", rfbScreen->ipv6port); + FD_SET(rfbScreen->listen6Sock, &(rfbScreen->allFds)); + rfbScreen->maxFd = max((int)rfbScreen->listen6Sock,rfbScreen->maxFd); +#endif } else if(rfbScreen->port>0) { - rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port); + FD_ZERO(&(rfbScreen->allFds)); if ((rfbScreen->listenSock = rfbListenOnTCPPort(rfbScreen->port, iface)) < 0) { rfbLogPerror("ListenOnTCPPort"); return; } - - FD_ZERO(&(rfbScreen->allFds)); + rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port); + FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); rfbScreen->maxFd = rfbScreen->listenSock; + +#ifdef LIBVNCSERVER_IPv6 + if ((rfbScreen->listen6Sock = rfbListenOnTCP6Port(rfbScreen->ipv6port, rfbScreen->listen6Interface)) < 0) { + /* ListenOnTCP6Port has its own detailed error printout */ + return; + } + rfbLog("Listening for VNC connections on TCP6 port %d\n", rfbScreen->ipv6port); + + FD_SET(rfbScreen->listen6Sock, &(rfbScreen->allFds)); + rfbScreen->maxFd = max((int)rfbScreen->listen6Sock,rfbScreen->maxFd); +#endif + } if (rfbScreen->udpPort != 0) { @@ -175,6 +207,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) rfbLogPerror("ListenOnUDPPort"); return; } + rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port); + FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds)); rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd); } @@ -199,6 +233,12 @@ void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen) rfbScreen->listenSock=-1; } + if(rfbScreen->listen6Sock>-1) { + closesocket(rfbScreen->listen6Sock); + FD_CLR(rfbScreen->listen6Sock,&rfbScreen->allFds); + rfbScreen->listen6Sock=-1; + } + if(rfbScreen->udpSock>-1) { closesocket(rfbScreen->udpSock); FD_CLR(rfbScreen->udpSock,&rfbScreen->allFds); @@ -270,6 +310,16 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) return result; } + if (rfbScreen->listen6Sock != -1 && FD_ISSET(rfbScreen->listen6Sock, &fds)) { + + if (!rfbProcessNewConnection(rfbScreen)) + return -1; + + FD_CLR(rfbScreen->listen6Sock, &fds); + if (--nfds == 0) + return result; + } + if ((rfbScreen->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) { if(!rfbScreen->udpClient) rfbNewUDPClient(rfbScreen); @@ -330,10 +380,29 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) { const int one = 1; int sock = -1; - struct sockaddr_in addr; + struct sockaddr_storage addr; socklen_t addrlen = sizeof(addr); + fd_set listen_fds; + int chosen_listen_sock = -1; + + /* Do another select() call to find out which listen socket + has an incoming connection pending. We know that at least + one of them has, so this should not block for too long! */ + FD_ZERO(&listen_fds); + if(rfbScreen->listenSock >= 0) + FD_SET(rfbScreen->listenSock, &listen_fds); + if(rfbScreen->listen6Sock >= 0) + FD_SET(rfbScreen->listen6Sock, &listen_fds); + if (select(rfbScreen->maxFd+1, &listen_fds, NULL, NULL, NULL) == -1) { + rfbLogPerror("rfbProcessNewConnection: error in select"); + return FALSE; + } + if (FD_ISSET(rfbScreen->listenSock, &listen_fds)) + chosen_listen_sock = rfbScreen->listenSock; + if (FD_ISSET(rfbScreen->listen6Sock, &listen_fds)) + chosen_listen_sock = rfbScreen->listen6Sock; - if ((sock = accept(rfbScreen->listenSock, + if ((sock = accept(chosen_listen_sock, (struct sockaddr *)&addr, &addrlen)) < 0) { rfbLogPerror("rfbCheckFds: accept"); return FALSE; @@ -361,7 +430,15 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) } #endif +#ifdef LIBVNCSERVER_IPv6 + char host[1024]; + if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { + rfbLogPerror("rfbProcessNewConnection: error in getnameinfo"); + } + rfbLog("Got connection from client %s\n", host); +#else rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr)); +#endif rfbNewClient(rfbScreen,sock); @@ -774,6 +851,81 @@ rfbListenOnTCPPort(int port, return sock; } + +int +rfbListenOnTCP6Port(int port, + const char* iface) +{ +#ifndef LIBVNCSERVER_IPv6 + rfbLogPerror("This LibVNCServer does not have IPv6 support"); + return -1; +#else + int sock; + int one = 1; + int rv; + struct addrinfo hints, *servinfo, *p; + char port_str[8]; + + snprintf(port_str, 8, "%d", port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET6; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; /* fill in wildcard address if iface == NULL */ + + if ((rv = getaddrinfo(iface, port_str, &hints, &servinfo)) != 0) { + rfbErr("rfbListenOnTCP6Port error in getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + /* loop through all the results and bind to the first we can */ + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) { + continue; + } + +#ifdef IPV6_V6ONLY + /* we have seperate IPv4 and IPv6 sockets since some OS's do not support dual binding */ + if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rfbListenOnTCP6Port error in setsockopt IPV6_V6ONLY"); + closesocket(sock); + return -1; + } +#endif + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { + rfbLogPerror("rfbListenOnTCP6Port: error in setsockopt SO_REUSEADDR"); + closesocket(sock); + return -1; + } + + if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { + closesocket(sock); + continue; + } + + break; + } + + if (p == NULL) { + rfbLogPerror("rfbListenOnTCP6Port: error in bind IPv6 socket"); + return -1; + } + + /* all done with this structure now */ + freeaddrinfo(servinfo); + + if (listen(sock, 32) < 0) { + rfbLogPerror("rfbListenOnTCP6Port: error in listen on IPv6 socket"); + closesocket(sock); + return -1; + } + + return sock; +#endif +} + + int rfbConnectToTcpAddr(char *host, int port) diff --git a/rfb/rfb.h b/rfb/rfb.h index e068e76..9e9fbfe 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -377,6 +377,12 @@ typedef struct _rfbScreenInfo char *sslkeyfile; char *sslcertfile; #endif + int ipv6port; /**< The port to listen on when using IPv6. */ + char* listen6Interface; + /* We have an additional IPv6 listen socket since there are systems that + don't support dual binding sockets under *any* circumstances, for + instance OpenBSD */ + SOCKET listen6Sock; } rfbScreenInfo, *rfbScreenInfoPtr; @@ -738,6 +744,7 @@ extern int rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec); extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port); extern int rfbConnectToTcpAddr(char* host, int port); extern int rfbListenOnTCPPort(int port, in_addr_t iface); +extern int rfbListenOnTCP6Port(int port, const char* iface); extern int rfbListenOnUDPPort(int port, in_addr_t iface); extern int rfbStringToAddr(char* string,in_addr_t* addr); extern rfbBool rfbSetNonBlocking(int sock); From 23413bf1200ce2fb3f5325a0292cbca7a4609e26 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 27 Feb 2012 15:05:59 +0100 Subject: [PATCH 2/8] IPv6 support for LibVNCServer, part onepointfive: Fix compilation with IPv6 missing. There was an oversight that crept in... --- libvncserver/rfbserver.c | 4 ++++ libvncserver/sockets.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 4db3f21..23c4d77 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -271,7 +271,11 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbProtocolVersionMsg pv; rfbClientIteratorPtr iterator; rfbClientPtr cl,cl_; +#ifdef LIBVNCSERVER_IPv6 struct sockaddr_storage addr; +#else + struct sockaddr_in addr; +#endif socklen_t addrlen = sizeof(addr); rfbProtocolExtension* extension; diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 132a721..3b5ef60 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -380,7 +380,11 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) { const int one = 1; int sock = -1; +#ifdef LIBVNCSERVER_IPv6 struct sockaddr_storage addr; +#else + struct sockaddr_in addr; +#endif socklen_t addrlen = sizeof(addr); fd_set listen_fds; int chosen_listen_sock = -1; From 0e74b5db9af7a19ce4fee4bc166040cb3a50108e Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 27 Feb 2012 15:10:57 +0100 Subject: [PATCH 3/8] IPv6 support for LibVNCServer, part onepointsix: fix a small logic error. Without this, we would have gotten a stale IPv4 socket in a race condition. --- libvncserver/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/main.c b/libvncserver/main.c index 7ecc842..a045d4c 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -593,7 +593,7 @@ listenerRun(void *data) len = sizeof (peer); if (FD_ISSET(screen->listenSock, &listen_fds)) client_fd = accept(screen->listenSock, (struct sockaddr*)&peer, &len); - if (FD_ISSET(screen->listen6Sock, &listen_fds)) + else if (FD_ISSET(screen->listen6Sock, &listen_fds)) client_fd = accept(screen->listen6Sock, (struct sockaddr*)&peer, &len); if(client_fd >= 0) From e7dfd0a9d66637ff9ee33132a6f0bd07083aec86 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 27 Feb 2012 15:19:52 +0100 Subject: [PATCH 4/8] IPv6 support for LibVNCServer, part two: Let the http server listen on IPv6, too. As done with the RFB sockets, this uses a separate-socket approach as well. --- libvncserver/cargs.c | 11 ++++++ libvncserver/httpd.c | 90 ++++++++++++++++++++++++++++++++++++-------- libvncserver/main.c | 2 + rfb/rfb.h | 2 + 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/libvncserver/cargs.c b/libvncserver/cargs.c index 27ceb97..b9eb02b 100644 --- a/libvncserver/cargs.c +++ b/libvncserver/cargs.c @@ -45,6 +45,9 @@ rfbUsage(void) "instead)\n"); fprintf(stderr, "-httpdir dir-path enable http server using dir-path home\n"); fprintf(stderr, "-httpport portnum use portnum for http connection\n"); +#ifdef LIBVNCSERVER_IPv6 + fprintf(stderr, "-httpportv6 portnum use portnum for IPv6 http connection\n"); +#endif fprintf(stderr, "-enablehttpproxy enable http proxy support\n"); fprintf(stderr, "-progressive height enable progressive updating for slow links\n"); fprintf(stderr, "-listen ipaddr listen for connections only on network interface with\n"); @@ -162,6 +165,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) return FALSE; } rfbScreen->httpPort = atoi(argv[++i]); +#ifdef LIBVNCSERVER_IPv6 + } else if (strcmp(argv[i], "-httpportv6") == 0) { /* -httpportv6 portnum */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->http6Port = atoi(argv[++i]); +#endif } else if (strcmp(argv[i], "-enablehttpproxy") == 0) { rfbScreen->httpEnableProxyConnect = TRUE; } else if (strcmp(argv[i], "-progressive") == 0) { /* -httpport portnum */ diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index 3025aae..6e46b99 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -102,17 +102,27 @@ rfbHttpInitSockets(rfbScreenInfoPtr rfbScreen) rfbScreen->httpPort = rfbScreen->port-100; } - rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); - - rfbLog(" URL http://%s:%d\n",rfbScreen->thisHost,rfbScreen->httpPort); - if ((rfbScreen->httpListenSock = rfbListenOnTCPPort(rfbScreen->httpPort, rfbScreen->listenInterface)) < 0) { rfbLogPerror("ListenOnTCPPort"); return; } + rfbLog("Listening for HTTP connections on TCP port %d\n", rfbScreen->httpPort); + rfbLog(" URL http://%s:%d\n",rfbScreen->thisHost,rfbScreen->httpPort); + +#ifdef LIBVNCSERVER_IPv6 + if (rfbScreen->http6Port == 0) { + rfbScreen->http6Port = rfbScreen->ipv6port-100; + } - /*AddEnabledDevice(httpListenSock);*/ + if ((rfbScreen->httpListen6Sock + = rfbListenOnTCP6Port(rfbScreen->http6Port, rfbScreen->listen6Interface)) < 0) { + /* ListenOnTCP6Port has its own detailed error printout */ + return; + } + rfbLog("Listening for HTTP connections on TCP6 port %d\n", rfbScreen->http6Port); + rfbLog(" URL http://%s:%d\n",rfbScreen->thisHost,rfbScreen->http6Port); +#endif } void rfbHttpShutdownSockets(rfbScreenInfoPtr rfbScreen) { @@ -121,6 +131,18 @@ void rfbHttpShutdownSockets(rfbScreenInfoPtr rfbScreen) { FD_CLR(rfbScreen->httpSock,&rfbScreen->allFds); rfbScreen->httpSock=-1; } + + if(rfbScreen->httpListenSock>-1) { + close(rfbScreen->httpListenSock); + FD_CLR(rfbScreen->httpListenSock,&rfbScreen->allFds); + rfbScreen->httpListenSock=-1; + } + + if(rfbScreen->httpListen6Sock>-1) { + close(rfbScreen->httpListen6Sock); + FD_CLR(rfbScreen->httpListen6Sock,&rfbScreen->allFds); + rfbScreen->httpListen6Sock=-1; + } } /* @@ -134,7 +156,11 @@ rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen) int nfds; fd_set fds; struct timeval tv; +#ifdef LIBVNCSERVER_IPv6 + struct sockaddr_storage addr; +#else struct sockaddr_in addr; +#endif socklen_t addrlen = sizeof(addr); if (!rfbScreen->httpDir) @@ -145,12 +171,15 @@ rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen) FD_ZERO(&fds); FD_SET(rfbScreen->httpListenSock, &fds); + if (rfbScreen->httpListen6Sock >= 0) { + FD_SET(rfbScreen->httpListen6Sock, &fds); + } if (rfbScreen->httpSock >= 0) { FD_SET(rfbScreen->httpSock, &fds); } tv.tv_sec = 0; tv.tv_usec = 0; - nfds = select(max(rfbScreen->httpSock,rfbScreen->httpListenSock) + 1, &fds, NULL, NULL, &tv); + nfds = select(max(rfbScreen->httpListen6Sock, max(rfbScreen->httpSock,rfbScreen->httpListenSock)) + 1, &fds, NULL, NULL, &tv); if (nfds == 0) { return; } @@ -167,19 +196,36 @@ rfbHttpCheckFds(rfbScreenInfoPtr rfbScreen) httpProcessInput(rfbScreen); } - if (FD_ISSET(rfbScreen->httpListenSock, &fds)) { + if (FD_ISSET(rfbScreen->httpListenSock, &fds) || FD_ISSET(rfbScreen->httpListen6Sock, &fds)) { if (rfbScreen->httpSock >= 0) close(rfbScreen->httpSock); - if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, - (struct sockaddr *)&addr, &addrlen)) < 0) { - rfbLogPerror("httpCheckFds: accept"); - return; + if(FD_ISSET(rfbScreen->httpListenSock, &fds)) { + if ((rfbScreen->httpSock = accept(rfbScreen->httpListenSock, (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("httpCheckFds: accept"); + return; + } } + else if(FD_ISSET(rfbScreen->httpListen6Sock, &fds)) { + if ((rfbScreen->httpSock = accept(rfbScreen->httpListen6Sock, (struct sockaddr *)&addr, &addrlen)) < 0) { + rfbLogPerror("httpCheckFds: accept"); + return; + } + } + #ifdef USE_LIBWRAP - if(!hosts_ctl("vnc",STRING_UNKNOWN,inet_ntoa(addr.sin_addr), + char host[1024]; +#ifdef LIBVNCSERVER_IPv6 + if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { + rfbLogPerror("httpCheckFds: error in getnameinfo"); + host[0] = '\0'; + } +#else + memcpy(host, inet_ntoa(addr.sin_addr), sizeof(host)); +#endif + if(!hosts_ctl("vnc",STRING_UNKNOWN, host, STRING_UNKNOWN)) { rfbLog("Rejected HTTP connection from client %s\n", - inet_ntoa(addr.sin_addr)); + host); close(rfbScreen->httpSock); rfbScreen->httpSock=-1; return; @@ -212,7 +258,11 @@ static rfbClientRec cl; static void httpProcessInput(rfbScreenInfoPtr rfbScreen) { +#ifdef LIBVNCSERVER_IPv6 + struct sockaddr_storage addr; +#else struct sockaddr_in addr; +#endif socklen_t addrlen = sizeof(addr); char fullFname[512]; char params[1024]; @@ -335,8 +385,16 @@ httpProcessInput(rfbScreenInfoPtr rfbScreen) getpeername(rfbScreen->httpSock, (struct sockaddr *)&addr, &addrlen); +#ifdef LIBVNCSERVER_IPv6 + char host[1024]; + if(getnameinfo((struct sockaddr*)&addr, addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST) != 0) { + rfbLogPerror("httpProcessInput: error in getnameinfo"); + } + rfbLog("httpd: get '%s' for %s\n", fname+1, host); +#else rfbLog("httpd: get '%s' for %s\n", fname+1, inet_ntoa(addr.sin_addr)); +#endif /* Extract parameters from the URL string if necessary */ @@ -562,7 +620,8 @@ parseParams(const char *request, char *result, int max_bytes) /* * Check if the string consists only of alphanumeric characters, '+' - * signs, underscores, and dots. Replace all '+' signs with spaces. + * signs, underscores, dots, colons and square brackets. + * Replace all '+' signs with spaces. */ static rfbBool @@ -571,7 +630,8 @@ validateString(char *str) char *ptr; for (ptr = str; *ptr != '\0'; ptr++) { - if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.') { + if (!isalnum(*ptr) && *ptr != '_' && *ptr != '.' + && *ptr != ':' && *ptr != '[' && *ptr != ']' ) { if (*ptr == '+') { *ptr = ' '; } else { diff --git a/libvncserver/main.c b/libvncserver/main.c index a045d4c..4cb18ac 100644 --- a/libvncserver/main.c +++ b/libvncserver/main.c @@ -843,8 +843,10 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->httpInitDone=FALSE; screen->httpEnableProxyConnect=FALSE; screen->httpPort=0; + screen->http6Port=0; screen->httpDir=NULL; screen->httpListenSock=-1; + screen->httpListen6Sock=-1; screen->httpSock=-1; screen->desktopName = "LibVNCServer"; diff --git a/rfb/rfb.h b/rfb/rfb.h index 9e9fbfe..bf4e011 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -383,6 +383,8 @@ typedef struct _rfbScreenInfo don't support dual binding sockets under *any* circumstances, for instance OpenBSD */ SOCKET listen6Sock; + int http6Port; + SOCKET httpListen6Sock; } rfbScreenInfo, *rfbScreenInfoPtr; From b7e043abad2d3ab7b6e60e6a6c16f6ff25caa0e0 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Fri, 9 Mar 2012 15:35:53 +0100 Subject: [PATCH 5/8] IPv6 support for LibVNCServer, part twopointone: properly surround IPv6 addresses with [] for noVNC URL. Some browsers omit the square brackets in document.location.hostname, so add them if missing. --- webclients/index.vnc | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/webclients/index.vnc b/webclients/index.vnc index 8254a70..0a92bd4 100644 --- a/webclients/index.vnc +++ b/webclients/index.vnc @@ -22,7 +22,15 @@ If the above Java applet does not work, you can also try the new JavaScript-only From edc75fa4f4f0dbadf7cb21a7511626dd35a3c330 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 10 Mar 2012 17:53:20 +0100 Subject: [PATCH 6/8] IPv6 support for LibVNCServer, part onepointseven: Plug a memleak. We have to properly free the addrinfo struct when jumping out of the function. --- libvncserver/sockets.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 3b5ef60..723e769 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -893,6 +893,7 @@ rfbListenOnTCP6Port(int port, if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbListenOnTCP6Port error in setsockopt IPV6_V6ONLY"); closesocket(sock); + freeaddrinfo(servinfo); return -1; } #endif @@ -900,6 +901,7 @@ rfbListenOnTCP6Port(int port, if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { rfbLogPerror("rfbListenOnTCP6Port: error in setsockopt SO_REUSEADDR"); closesocket(sock); + freeaddrinfo(servinfo); return -1; } @@ -913,6 +915,7 @@ rfbListenOnTCP6Port(int port, if (p == NULL) { rfbLogPerror("rfbListenOnTCP6Port: error in bind IPv6 socket"); + freeaddrinfo(servinfo); return -1; } From 75bfb1f5d396b2908a9615cd02bebfc952baa045 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sat, 10 Mar 2012 21:31:44 +0100 Subject: [PATCH 7/8] IPv6 support for LibVNCServer, part three: make reverse connections IPv6-capable. Besided making libvncserver reverseVNC IPv6-aware, this introduces some changes on the client side as well to make clients listen on IPv6 sockets, too. Like the server side, this also uses a separate-socket approach. --- client_examples/SDLvncviewer.c | 1 + libvncclient/listen.c | 76 +++++++++++++++++++++++++++++----- libvncclient/sockets.c | 63 +++++++++++++++++++++++++++- libvncclient/vncviewer.c | 2 + libvncserver/sockets.c | 42 ++++++++++++++++++- rfb/rfbclient.h | 5 ++- 6 files changed, 174 insertions(+), 15 deletions(-) diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c index 5fa8f2c..c817941 100644 --- a/client_examples/SDLvncviewer.c +++ b/client_examples/SDLvncviewer.c @@ -518,6 +518,7 @@ int main(int argc,char** argv) { cl->HandleTextChat=text_chat; cl->GotXCutText = got_selection; cl->listenPort = LISTEN_PORT_OFFSET; + cl->listen6Port = LISTEN_PORT_OFFSET; if(!rfbInitClient(cl,&argc,argv)) { cl = NULL; /* rfbInitClient has already freed the client struct */ diff --git a/libvncclient/listen.c b/libvncclient/listen.c index 2e9fafb..9e8fe99 100644 --- a/libvncclient/listen.c +++ b/libvncclient/listen.c @@ -50,7 +50,7 @@ listenForIncomingConnections(rfbClient* client) rfbClientErr("listenForIncomingConnections on MinGW32 NOT IMPLEMENTED\n"); return; #else - int listenSocket; + int listenSocket, listen6Socket = -1; fd_set fds; client->listenSpecified = TRUE; @@ -65,8 +65,24 @@ listenForIncomingConnections(rfbClient* client) rfbClientLog("%s -listen: Command line errors are not reported until " "a connection comes in.\n", client->programName); - while (TRUE) { +#ifdef LIBVNCSERVER_IPv6 /* only try that if we're IPv6-capable, otherwise we may try to bind to the same port which would make all that listening fail */ + /* only do IPv6 listen of listen6Port is set */ + if (client->listen6Port > 0) + { + listen6Socket = ListenAtTcpPortAndAddress(client->listen6Port, client->listen6Address); + + if (listen6Socket < 0) + return; + + rfbClientLog("%s -listen: Listening on IPV6 port %d\n", + client->programName,client->listenPort); + rfbClientLog("%s -listen: Command line errors are not reported until " + "a connection comes in.\n", client->programName); + } +#endif + while (TRUE) { + int r; /* reap any zombies */ int status, pid; while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0); @@ -75,12 +91,19 @@ listenForIncomingConnections(rfbClient* client) FD_ZERO(&fds); - FD_SET(listenSocket, &fds); + if(listenSocket >= 0) + FD_SET(listenSocket, &fds); + if(listen6Socket >= 0) + FD_SET(listen6Socket, &fds); + + r = select(max(listenSocket, listen6Socket)+1, &fds, NULL, NULL, NULL); - select(listenSocket+1, &fds, NULL, NULL, NULL); + if (r > 0) { + if (FD_ISSET(listenSocket, &fds)) + client->sock = AcceptTcpConnection(client->listenSock); + else if (FD_ISSET(listen6Socket, &fds)) + client->sock = AcceptTcpConnection(client->listen6Sock); - if (FD_ISSET(listenSocket, &fds)) { - client->sock = AcceptTcpConnection(listenSocket); if (client->sock < 0) return; if (!SetNonBlocking(client->sock)) @@ -97,6 +120,7 @@ listenForIncomingConnections(rfbClient* client) case 0: /* child - return to caller */ close(listenSocket); + close(listen6Socket); return; default: @@ -144,24 +168,54 @@ listenForIncomingConnectionsNoFork(rfbClient* client, int timeout) "a connection comes in.\n", client->programName); } +#ifdef LIBVNCSERVER_IPv6 /* only try that if we're IPv6-capable, otherwise we may try to bind to the same port which would make all that listening fail */ + /* only do IPv6 listen of listen6Port is set */ + if (client->listen6Port > 0 && client->listen6Sock < 0) + { + client->listen6Sock = ListenAtTcpPortAndAddress(client->listen6Port, client->listen6Address); + + if (client->listen6Sock < 0) + return -1; + + rfbClientLog("%s -listennofork: Listening on IPV6 port %d\n", + client->programName,client->listenPort); + rfbClientLog("%s -listennofork: Command line errors are not reported until " + "a connection comes in.\n", client->programName); + } +#endif + FD_ZERO(&fds); - FD_SET(client->listenSock, &fds); + if(client->listenSock >= 0) + FD_SET(client->listenSock, &fds); + if(client->listen6Sock >= 0) + FD_SET(client->listen6Sock, &fds); if (timeout < 0) - r = select(client->listenSock+1, &fds, NULL, NULL, NULL); + r = select(max(client->listenSock, client->listen6Sock) +1, &fds, NULL, NULL, NULL); else - r = select(client->listenSock+1, &fds, NULL, NULL, &to); + r = select(max(client->listenSock, client->listen6Sock) +1, &fds, NULL, NULL, &to); if (r > 0) { - client->sock = AcceptTcpConnection(client->listenSock); + if (FD_ISSET(client->listenSock, &fds)) + client->sock = AcceptTcpConnection(client->listenSock); + else if (FD_ISSET(client->listen6Sock, &fds)) + client->sock = AcceptTcpConnection(client->listen6Sock); + if (client->sock < 0) return -1; if (!SetNonBlocking(client->sock)) return -1; - close(client->listenSock); + if(client->listenSock >= 0) { + close(client->listenSock); + client->listenSock = -1; + } + if(client->listen6Sock >= 0) { + close(client->listen6Sock); + client->listen6Sock = -1; + } return r; } diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index be9924a..01ec3a2 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -494,8 +494,9 @@ int ListenAtTcpPortAndAddress(int port, const char *address) { int sock; - struct sockaddr_in addr; int one = 1; +#ifndef LIBVNCSERVER_IPv6 + struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); @@ -527,6 +528,66 @@ ListenAtTcpPortAndAddress(int port, const char *address) return -1; } +#else + int rv; + struct addrinfo hints, *servinfo, *p; + char port_str[8]; + + snprintf(port_str, 8, "%d", port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; /* fill in wildcard address if address == NULL */ + + if (!initSockets()) + return -1; + + if ((rv = getaddrinfo(address, port_str, &hints, &servinfo)) != 0) { + rfbClientErr("ListenAtTcpPortAndAddress: error in getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + /* loop through all the results and bind to the first we can */ + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) { + continue; + } + +#ifdef IPV6_V6ONLY + /* we have seperate IPv4 and IPv6 sockets since some OS's do not support dual binding */ + if (p->ai_family == AF_INET6 && setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof(one)) < 0) { + rfbClientErr("ListenAtTcpPortAndAddress: error in setsockopt IPV6_V6ONLY: %s\n", strerror(errno)); + close(sock); + freeaddrinfo(servinfo); + return -1; + } +#endif + + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { + rfbClientErr("ListenAtTcpPortAndAddress: error in setsockopt SO_REUSEADDR: %s\n", strerror(errno)); + close(sock); + freeaddrinfo(servinfo); + return -1; + } + + if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { + close(sock); + continue; + } + + break; + } + + if (p == NULL) { + rfbClientErr("ListenAtTcpPortAndAddress: error in bind: %s\n", strerror(errno)); + return -1; + } + + /* all done with this structure now */ + freeaddrinfo(servinfo); +#endif + if (listen(sock, 5) < 0) { rfbClientErr("ListenAtTcpPort: listen\n"); close(sock); diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c index 10b430f..6a4f006 100644 --- a/libvncclient/vncviewer.c +++ b/libvncclient/vncviewer.c @@ -197,6 +197,8 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel, client->sock = -1; client->listenSock = -1; client->listenAddress = NULL; + client->listen6Sock = -1; + client->listen6Address = NULL; client->clientAuthSchemes = NULL; return client; } diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 723e769..1727eb0 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -937,8 +937,46 @@ int rfbConnectToTcpAddr(char *host, int port) { - struct hostent *hp; int sock; +#ifdef LIBVNCSERVER_IPv6 + struct addrinfo hints, *servinfo, *p; + int rv; + char port_str[8]; + + snprintf(port_str, 8, "%d", port); + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((rv = getaddrinfo(host, port_str, &hints, &servinfo)) != 0) { + rfbErr("rfbConnectToTcpAddr: error in getaddrinfo: %s\n", gai_strerror(rv)); + return -1; + } + + /* loop through all the results and connect to the first we can */ + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) + continue; + + if (connect(sock, p->ai_addr, p->ai_addrlen) < 0) { + closesocket(sock); + continue; + } + + break; + } + + /* all failed */ + if (p == NULL) { + rfbLogPerror("rfbConnectToTcoAddr: failed to connect\n"); + sock = -1; /* set return value */ + } + + /* all done with this structure now */ + freeaddrinfo(servinfo); +#else + struct hostent *hp; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); @@ -962,7 +1000,7 @@ rfbConnectToTcpAddr(char *host, closesocket(sock); return -1; } - +#endif return sock; } diff --git a/rfb/rfbclient.h b/rfb/rfbclient.h index 36ffe13..36c109e 100644 --- a/rfb/rfbclient.h +++ b/rfb/rfbclient.h @@ -347,7 +347,10 @@ typedef struct _rfbClient { FinishedFrameBufferUpdateProc FinishedFrameBufferUpdate; char *listenAddress; - + /* IPv6 listen socket, address and port*/ + int listen6Sock; + char* listen6Address; + int listen6Port; } rfbClient; /* cursor.c */ From 2d50fc84f7ba869767ce052aa2aa9b11a104e0de Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Mon, 2 Apr 2012 16:29:06 +0200 Subject: [PATCH 8/8] IPv6 support for LibVNCServer, part four: add copyright notices to files with non-trivial changes. --- libvncclient/listen.c | 1 + libvncclient/sockets.c | 1 + libvncserver/httpd.c | 1 + libvncserver/sockets.c | 1 + 4 files changed, 4 insertions(+) diff --git a/libvncclient/listen.c b/libvncclient/listen.c index 9e8fe99..c91ad6e 100644 --- a/libvncclient/listen.c +++ b/libvncclient/listen.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011-2012 Christian Beier * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * * This is free software; you can redistribute it and/or modify diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c index 01ec3a2..1a8df56 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -1,4 +1,5 @@ /* + * Copyright (C) 2011-2012 Christian Beier * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * * This is free software; you can redistribute it and/or modify diff --git a/libvncserver/httpd.c b/libvncserver/httpd.c index 6e46b99..ed91e46 100644 --- a/libvncserver/httpd.c +++ b/libvncserver/httpd.c @@ -3,6 +3,7 @@ */ /* + * Copyright (C) 2011-2012 Christian Beier * Copyright (C) 2002 RealVNC Ltd. * Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved. * diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 1727eb0..84c9c98 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -19,6 +19,7 @@ */ /* + * Copyright (C) 2011-2012 Christian Beier * Copyright (C) 2005 Rohit Kumar, Johannes E. Schindelin * OSXvnc Copyright (C) 2001 Dan McGuirk . * Original Xvnc code Copyright (C) 1999 AT&T Laboratories Cambridge.