diff --git a/client_examples/SDLvncviewer.c b/client_examples/SDLvncviewer.c index 5fa8f2c..c817941 100644 --- a/client_examples/SDLvncviewer.c +++ b/client_examples/SDLvncviewer.c @@ -518,6 +518,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..9e8fe99 100644 --- a/libvncclient/listen.c +++ b/libvncclient/listen.c @@ -50,7 +50,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 +65,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 +91,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 +120,7 @@ listenForIncomingConnections(rfbClient* client) case 0: /* child - return to caller */ close(listenSocket); + close(listen6Socket); return; default: @@ -144,24 +168,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..01ec3a2 100644 --- a/libvncclient/sockets.c +++ b/libvncclient/sockets.c @@ -494,8 +494,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 +528,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/sockets.c b/libvncserver/sockets.c index 723e769..1727eb0 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -937,8 +937,46 @@ 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)); @@ -962,7 +1000,7 @@ rfbConnectToTcpAddr(char *host, closesocket(sock); return -1; } - +#endif return 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 */