diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c index 4cf22f5..8fe6f57 100644 --- a/client_examples/SDLvncviewer.c +++ b/client_examples/SDLvncviewer.c @@ -524,6 +524,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..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 @@ -50,7 +51,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 +66,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 +92,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 +121,7 @@ listenForIncomingConnections(rfbClient* client) case 0: /* child - return to caller */ close(listenSocket); + close(listen6Socket); return; default: @@ -144,24 +169,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..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 @@ -494,8 +495,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 +529,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/cargs.c b/libvncserver/cargs.c index 2e973e8..b9eb02b 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"); @@ -42,10 +45,17 @@ 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"); 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 +90,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(); @@ -147,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 */ @@ -163,6 +189,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/httpd.c b/libvncserver/httpd.c index 3025aae..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. * @@ -102,17 +103,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 +132,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 +157,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 +172,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 +197,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 +259,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 +386,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 +621,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 +631,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 0edf994..4cb18ac 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); + else 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,12 +838,15 @@ rfbScreenInfoPtr rfbGetScreen(int* argc,char** argv, screen->maxFd=0; screen->listenSock=-1; + screen->listen6Sock=-1; 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/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 9be255f..23c4d77 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,12 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbProtocolVersionMsg pv; rfbClientIteratorPtr iterator; rfbClientPtr cl,cl_; +#ifdef LIBVNCSERVER_IPv6 + struct sockaddr_storage addr; +#else struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); +#endif + socklen_t addrlen = sizeof(addr); rfbProtocolExtension* extension; cl = (rfbClientPtr)calloc(sizeof(rfbClientRec),1); @@ -294,7 +299,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..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. @@ -137,6 +138,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 +153,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 +208,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 +234,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 +311,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 +381,33 @@ 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; + + /* 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 +435,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,12 +856,128 @@ 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); + freeaddrinfo(servinfo); + return -1; + } +#endif + + 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; + } + + if (bind(sock, p->ai_addr, p->ai_addrlen) < 0) { + closesocket(sock); + continue; + } + + break; + } + + if (p == NULL) { + rfbLogPerror("rfbListenOnTCP6Port: error in bind IPv6 socket"); + freeaddrinfo(servinfo); + 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) { - 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)); @@ -803,7 +1001,7 @@ rfbConnectToTcpAddr(char *host, closesocket(sock); return -1; } - +#endif return sock; } diff --git a/rfb/rfb.h b/rfb/rfb.h index e068e76..bf4e011 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -377,6 +377,14 @@ 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; + int http6Port; + SOCKET httpListen6Sock; } rfbScreenInfo, *rfbScreenInfoPtr; @@ -738,6 +746,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); 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 */ 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