summaryrefslogtreecommitdiffstats
path: root/libvncserver/sockets.c
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 /libvncserver/sockets.c
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>
Diffstat (limited to 'libvncserver/sockets.c')
-rw-r--r--libvncserver/sockets.c166
1 files changed, 159 insertions, 7 deletions
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)