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>
pull/1/head
Christian Beier 11 years ago
parent 1078e8a8b0
commit 83a7c713a9

@ -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) {

@ -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;

@ -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);

@ -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)

@ -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);

Loading…
Cancel
Save