Commit 83a7c713 authored by Christian Beier's avatar Christian Beier

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: 's avatarChristian Beier <dontmind@freeshell.org>
parent 1078e8a8
...@@ -22,6 +22,9 @@ rfbUsage(void) ...@@ -22,6 +22,9 @@ rfbUsage(void)
rfbProtocolExtension* extension; rfbProtocolExtension* extension;
fprintf(stderr, "-rfbport port TCP port for RFB protocol\n"); 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, "-rfbwait time max time in ms to wait for RFB client\n");
fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n" fprintf(stderr, "-rfbauth passwd-file use authentication on RFB protocol\n"
" (use 'storepasswd' to create a password file)\n"); " (use 'storepasswd' to create a password file)\n");
...@@ -46,6 +49,10 @@ rfbUsage(void) ...@@ -46,6 +49,10 @@ rfbUsage(void)
fprintf(stderr, "-progressive height enable progressive updating for slow links\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"); fprintf(stderr, "-listen ipaddr listen for connections only on network interface with\n");
fprintf(stderr, " addr ipaddr. '-listen localhost' and hostname work too.\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) for(extension=rfbGetExtensionIterator();extension;extension=extension->next)
if(extension->usage) if(extension->usage)
...@@ -80,6 +87,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) ...@@ -80,6 +87,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[])
return FALSE; return FALSE;
} }
rfbScreen->port = atoi(argv[++i]); 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 */ } else if (strcmp(argv[i], "-rfbwait") == 0) { /* -rfbwait ms */
if (i + 1 >= *argc) { if (i + 1 >= *argc) {
rfbUsage(); rfbUsage();
...@@ -163,6 +178,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) ...@@ -163,6 +178,14 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[])
if (! rfbStringToAddr(argv[++i], &(rfbScreen->listenInterface))) { if (! rfbStringToAddr(argv[++i], &(rfbScreen->listenInterface))) {
return FALSE; 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 #ifdef LIBVNCSERVER_WITH_WEBSOCKETS
} else if (strcmp(argv[i], "-sslkeyfile") == 0) { /* -sslkeyfile sslkeyfile */ } else if (strcmp(argv[i], "-sslkeyfile") == 0) { /* -sslkeyfile sslkeyfile */
if (i + 1 >= *argc) { if (i + 1 >= *argc) {
......
...@@ -569,21 +569,37 @@ listenerRun(void *data) ...@@ -569,21 +569,37 @@ listenerRun(void *data)
{ {
rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data; rfbScreenInfoPtr screen=(rfbScreenInfoPtr)data;
int client_fd; int client_fd;
struct sockaddr_in peer; struct sockaddr_storage peer;
rfbClientPtr cl; rfbClientPtr cl = NULL;
socklen_t len; socklen_t len;
fd_set listen_fds; /* temp file descriptor list for select() */
len = sizeof(peer);
/* TODO: this thread wont die by restarting the server */ /* TODO: this thread wont die by restarting the server */
/* TODO: HTTP is not handled */ /* TODO: HTTP is not handled */
while ((client_fd = accept(screen->listenSock, while (1) {
(struct sockaddr*)&peer, &len)) >= 0) { client_fd = -1;
cl = rfbNewClient(screen,client_fd); FD_ZERO(&listen_fds);
len = sizeof(peer); 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 ) if (cl && !cl->onHold )
rfbStartOnHoldClient(cl); rfbStartOnHoldClient(cl);
} }
return(NULL); return(NULL);
} }
...@@ -809,6 +825,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, ...@@ -809,6 +825,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
screen->clientHead=NULL; screen->clientHead=NULL;
screen->pointerClient=NULL; screen->pointerClient=NULL;
screen->port=5900; screen->port=5900;
screen->ipv6port=5900;
screen->socketState=RFB_SOCKET_INIT; screen->socketState=RFB_SOCKET_INIT;
screen->inetdInitDone = FALSE; screen->inetdInitDone = FALSE;
...@@ -821,6 +838,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, ...@@ -821,6 +838,7 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv,
screen->maxFd=0; screen->maxFd=0;
screen->listenSock=-1; screen->listenSock=-1;
screen->listen6Sock=-1;
screen->httpInitDone=FALSE; screen->httpInitDone=FALSE;
screen->httpEnableProxyConnect=FALSE; screen->httpEnableProxyConnect=FALSE;
......
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#ifdef LIBVNCSERVER_HAVE_NETINET_IN_H #ifdef LIBVNCSERVER_HAVE_NETINET_IN_H
#include <netinet/in.h> #include <netinet/in.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netdb.h>
#include <arpa/inet.h> #include <arpa/inet.h>
#endif #endif
#endif #endif
...@@ -270,8 +271,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, ...@@ -270,8 +271,8 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
rfbProtocolVersionMsg pv; rfbProtocolVersionMsg pv;
rfbClientIteratorPtr iterator; rfbClientIteratorPtr iterator;
rfbClientPtr cl,cl_; rfbClientPtr cl,cl_;
struct sockaddr_in addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof(struct sockaddr_in); socklen_t addrlen = sizeof(addr);
rfbProtocolExtension* extension; rfbProtocolExtension* extension;
cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1);
...@@ -294,7 +295,17 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, ...@@ -294,7 +295,17 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen,
int one=1; int one=1;
getpeername(sock, (struct sockaddr *)&addr, &addrlen); 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)); cl->host = strdup(inet_ntoa(addr.sin_addr));
#endif
rfbLog(" other clients:\n"); rfbLog(" other clients:\n");
iterator = rfbGetClientIterator(rfbScreen); iterator = rfbGetClientIterator(rfbScreen);
......
...@@ -137,6 +137,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) ...@@ -137,6 +137,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen)
if(rfbScreen->autoPort) { if(rfbScreen->autoPort) {
int i; int i;
FD_ZERO(&(rfbScreen->allFds));
rfbLog("Autoprobing TCP port \n"); rfbLog("Autoprobing TCP port \n");
for (i = 5900; i < 6000; i++) { for (i = 5900; i < 6000; i++) {
if ((rfbScreen->listenSock = rfbListenOnTCPPort(i, iface)) >= 0) { if ((rfbScreen->listenSock = rfbListenOnTCPPort(i, iface)) >= 0) {
...@@ -150,22 +152,52 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) ...@@ -150,22 +152,52 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen)
return; return;
} }
rfbLog("Autoprobing selected port %d\n", rfbScreen->port); rfbLog("Autoprobing selected TCP port %d\n", rfbScreen->port);
FD_ZERO(&(rfbScreen->allFds));
FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbScreen->listenSock; 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) { 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) { if ((rfbScreen->listenSock = rfbListenOnTCPPort(rfbScreen->port, iface)) < 0) {
rfbLogPerror("ListenOnTCPPort"); rfbLogPerror("ListenOnTCPPort");
return; return;
} }
rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port);
FD_ZERO(&(rfbScreen->allFds));
FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds)); FD_SET(rfbScreen->listenSock, &(rfbScreen->allFds));
rfbScreen->maxFd = rfbScreen->listenSock; 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) { if (rfbScreen->udpPort != 0) {
...@@ -175,6 +207,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen) ...@@ -175,6 +207,8 @@ rfbInitSockets(rfbScreenInfoPtr rfbScreen)
rfbLogPerror("ListenOnUDPPort"); rfbLogPerror("ListenOnUDPPort");
return; return;
} }
rfbLog("Listening for VNC connections on TCP port %d\n", rfbScreen->port);
FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds)); FD_SET(rfbScreen->udpSock, &(rfbScreen->allFds));
rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd); rfbScreen->maxFd = max((int)rfbScreen->udpSock,rfbScreen->maxFd);
} }
...@@ -199,6 +233,12 @@ void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen) ...@@ -199,6 +233,12 @@ void rfbShutdownSockets(rfbScreenInfoPtr rfbScreen)
rfbScreen->listenSock=-1; rfbScreen->listenSock=-1;
} }
if(rfbScreen->listen6Sock>-1) {
closesocket(rfbScreen->listen6Sock);
FD_CLR(rfbScreen->listen6Sock,&rfbScreen->allFds);
rfbScreen->listen6Sock=-1;
}
if(rfbScreen->udpSock>-1) { if(rfbScreen->udpSock>-1) {
closesocket(rfbScreen->udpSock); closesocket(rfbScreen->udpSock);
FD_CLR(rfbScreen->udpSock,&rfbScreen->allFds); FD_CLR(rfbScreen->udpSock,&rfbScreen->allFds);
...@@ -270,6 +310,16 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec) ...@@ -270,6 +310,16 @@ rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec)
return result; 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->udpSock != -1) && FD_ISSET(rfbScreen->udpSock, &fds)) {
if(!rfbScreen->udpClient) if(!rfbScreen->udpClient)
rfbNewUDPClient(rfbScreen); rfbNewUDPClient(rfbScreen);
...@@ -330,10 +380,29 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) ...@@ -330,10 +380,29 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen)
{ {
const int one = 1; const int one = 1;
int sock = -1; int sock = -1;
struct sockaddr_in addr; struct sockaddr_storage addr;
socklen_t addrlen = sizeof(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) { (struct sockaddr *)&addr, &addrlen)) < 0) {
rfbLogPerror("rfbCheckFds: accept"); rfbLogPerror("rfbCheckFds: accept");
return FALSE; return FALSE;
...@@ -361,7 +430,15 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen) ...@@ -361,7 +430,15 @@ rfbProcessNewConnection(rfbScreenInfoPtr rfbScreen)
} }
#endif #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)); rfbLog("Got connection from client %s\n", inet_ntoa(addr.sin_addr));
#endif
rfbNewClient(rfbScreen,sock); rfbNewClient(rfbScreen,sock);
...@@ -774,6 +851,81 @@ rfbListenOnTCPPort(int port, ...@@ -774,6 +851,81 @@ rfbListenOnTCPPort(int port,
return sock; 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 int
rfbConnectToTcpAddr(char *host, rfbConnectToTcpAddr(char *host,
int port) int port)
......
...@@ -377,6 +377,12 @@ typedef struct _rfbScreenInfo ...@@ -377,6 +377,12 @@ typedef struct _rfbScreenInfo
char *sslkeyfile; char *sslkeyfile;
char *sslcertfile; char *sslcertfile;
#endif #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; } rfbScreenInfo, *rfbScreenInfoPtr;
...@@ -738,6 +744,7 @@ extern int rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec); ...@@ -738,6 +744,7 @@ extern int rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec);
extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port); extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port);
extern int rfbConnectToTcpAddr(char* host, int port); extern int rfbConnectToTcpAddr(char* host, int port);
extern int rfbListenOnTCPPort(int port, in_addr_t iface); 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 rfbListenOnUDPPort(int port, in_addr_t iface);
extern int rfbStringToAddr(char* string,in_addr_t* addr); extern int rfbStringToAddr(char* string,in_addr_t* addr);
extern rfbBool rfbSetNonBlocking(int sock); extern rfbBool rfbSetNonBlocking(int sock);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment