summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Beier <dontmind@freeshell.org>2012-02-20 15:52:19 +0100
committerChristian Beier <dontmind@freeshell.org>2012-02-20 15:52:19 +0100
commit83a7c713a99a65f910fabab1bb95428762f569fb (patch)
tree46e2fad855fd03a2e9a8bf6e03c2d2242b26de43
parent1078e8a8b050b5b4ebbcb011750f5dd2d8eacc37 (diff)
downloadlibtdevnc-83a7c713a99a65f910fabab1bb95428762f569fb.tar.gz
libtdevnc-83a7c713a99a65f910fabab1bb95428762f569fb.zip
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 <dontmind@freeshell.org>
-rw-r--r--libvncserver/cargs.c23
-rw-r--r--libvncserver/main.c38
-rw-r--r--libvncserver/rfbserver.c15
-rw-r--r--libvncserver/sockets.c166
-rw-r--r--rfb/rfb.h7
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 <netinet/in.h>
#include <netinet/tcp.h>
+#include <netdb.h>
#include <arpa/inet.h>
#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);