/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifdef HAVE_CONFIG_H #include #endif #ifdef WIN32 //#include #include #include #define errorNumber WSAGetLastError() #else #define errorNumber errno #define closesocket close #include #include #include #include #include #endif #include #include #include #include #include #include #include #ifdef WIN32 #include #endif #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long)-1) #endif #ifndef INADDR_LOOPBACK #define INADDR_LOOPBACK ((unsigned long)0x7F000001) #endif #ifndef IN6_ARE_ADDR_EQUAL #define IN6_ARE_ADDR_EQUAL(a,b) \ (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0) #endif // Missing on older Windows and OS X #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif using namespace network; using namespace rdr; static rfb::LogWriter vlog("TcpSocket"); static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true); static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true); /* Tunnelling support. */ int network::findFreeTcpPort (void) { int sock; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = INADDR_ANY; if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) throw socket_error("Unable to create socket", errorNumber); addr.sin_port = 0; if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0) throw socket_error("Unable to find free port", errorNumber); socklen_t n = sizeof(addr); if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0) throw socket_error("Unable to get port number", errorNumber); closesocket (sock); return ntohs(addr.sin_port); } int network::getSockPort(int sock) { vnc_sockaddr_t sa; socklen_t sa_size = sizeof(sa); if (getsockname(sock, &sa.u.sa, &sa_size) < 0) return 0; switch (sa.u.sa.sa_family) { case AF_INET6: return ntohs(sa.u.sin6.sin6_port); default: return ntohs(sa.u.sin.sin_port); } } // -=- TcpSocket TcpSocket::TcpSocket(int sock) : Socket(sock) { // Disable Nagle's algorithm, to reduce latency enableNagles(false); } TcpSocket::TcpSocket(const char *host, int port) { int sock, err, result; struct addrinfo *ai, *current, hints; // - Create a socket memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_canonname = nullptr; hints.ai_addr = nullptr; hints.ai_next = nullptr; if ((result = getaddrinfo(host, nullptr, &hints, &ai)) != 0) { throw getaddrinfo_error("Unable to resolve host by name", result); } sock = -1; err = 0; for (current = ai; current != nullptr; current = current->ai_next) { int family; vnc_sockaddr_t sa; socklen_t salen; char ntop[NI_MAXHOST]; family = current->ai_family; switch (family) { case AF_INET: if (!UseIPv4) continue; break; case AF_INET6: if (!UseIPv6) continue; break; default: continue; } salen = current->ai_addrlen; memcpy(&sa, current->ai_addr, salen); if (family == AF_INET) sa.u.sin.sin_port = htons(port); else sa.u.sin6.sin6_port = htons(port); getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), nullptr, 0, NI_NUMERICHOST); vlog.debug("Connecting to %s [%s] port %d", host, ntop, port); sock = socket (family, SOCK_STREAM, 0); if (sock == -1) { err = errorNumber; freeaddrinfo(ai); throw socket_error("Unable to create socket", err); } /* Attempt to connect to the remote host */ while ((result = connect(sock, &sa.u.sa, salen)) == -1) { err = errorNumber; #ifndef WIN32 if (err == EINTR) continue; #endif vlog.debug("Failed to connect to address %s port %d: %d", ntop, port, err); closesocket(sock); sock = -1; break; } if (result == 0) break; } freeaddrinfo(ai); if (sock == -1) { if (err == 0) throw std::runtime_error("No useful address for host"); else throw socket_error("Unable to connect to socket", err); } // Take proper ownership of the socket setFd(sock); // Disable Nagle's algorithm, to reduce latency enableNagles(false); } const char* TcpSocket::getPeerAddress() { vnc_sockaddr_t sa; socklen_t sa_size = sizeof(sa); if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) { vlog.error("Unable to get peer name for socket"); return "(N/A)"; } if (sa.u.sa.sa_family == AF_INET6) { static char buffer[INET6_ADDRSTRLEN + 2]; int ret; buffer[0] = '['; ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6), buffer + 1, sizeof(buffer) - 2, nullptr, 0, NI_NUMERICHOST); if (ret != 0) { vlog.error("Unable to convert peer name to a string"); return "(N/A)"; } strcat(buffer, "]"); return buffer; } if (sa.u.sa.sa_family == AF_INET) { char *name; name = inet_ntoa(sa.u.sin.sin_addr); if (name == nullptr) { vlog.error("Unable to convert peer name to a string"); return "(N/A)"; } return name; } vlog.error("Unknown address family for socket"); return ""; } const char* TcpSocket::getPeerEndpoint() { static char buffer[INET6_ADDRSTRLEN + 2 + 32]; vnc_sockaddr_t sa; socklen_t sa_size = sizeof(sa); int port; getpeername(getFd(), &sa.u.sa, &sa_size); if (sa.u.sa.sa_family == AF_INET6) port = ntohs(sa.u.sin6.sin6_port); else if (sa.u.sa.sa_family == AF_INET) port = ntohs(sa.u.sin.sin_port); else port = 0; sprintf(buffer, "%s::%d", getPeerAddress(), port); return buffer; } bool TcpSocket::enableNagles(bool enable) { int one = enable ? 0 : 1; if (setsockopt(getFd(), IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)) < 0) { int e = errorNumber; vlog.error("Unable to setsockopt TCP_NODELAY: %d", e); return false; } return true; } TcpListener::TcpListener(int sock) : SocketListener(sock) { } TcpListener::TcpListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen) { int one = 1; vnc_sockaddr_t sa; int sock; if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) throw socket_error("Unable to create listening socket", errorNumber); memcpy (&sa, listenaddr, listenaddrlen); #ifdef IPV6_V6ONLY if (listenaddr->sa_family == AF_INET6) { if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) { int e = errorNumber; closesocket(sock); throw socket_error("Unable to set IPV6_V6ONLY", e); } } #endif /* defined(IPV6_V6ONLY) */ #ifdef FD_CLOEXEC // - By default, close the socket on exec() fcntl(sock, F_SETFD, FD_CLOEXEC); #endif // SO_REUSEADDR is broken on Windows. It allows binding to a port // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE // might do what we want, but requires investigation. #ifndef WIN32 if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) { int e = errorNumber; closesocket(sock); throw socket_error("Unable to create listening socket", e); } #endif if (bind(sock, &sa.u.sa, listenaddrlen) == -1) { int e = errorNumber; closesocket(sock); throw socket_error("Failed to bind socket", e); } listen(sock); } Socket* TcpListener::createSocket(int fd_) { return new TcpSocket(fd_); } std::list TcpListener::getMyAddresses() { struct addrinfo *ai, *current, hints; std::list result; initSockets(); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_canonname = nullptr; hints.ai_addr = nullptr; hints.ai_next = nullptr; // Windows doesn't like NULL for service, so specify something if ((getaddrinfo(nullptr, "1", &hints, &ai)) != 0) return result; for (current= ai; current != nullptr; current = current->ai_next) { char addr[INET6_ADDRSTRLEN]; switch (current->ai_family) { case AF_INET: if (!UseIPv4) continue; break; case AF_INET6: if (!UseIPv6) continue; break; default: continue; } getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN, nullptr, 0, NI_NUMERICHOST); result.push_back(addr); } freeaddrinfo(ai); return result; } int TcpListener::getMyPort() { return getSockPort(getFd()); } void network::createLocalTcpListeners(std::list *listeners, int port) { struct addrinfo ai[2]; vnc_sockaddr_t sa[2]; memset(ai, 0, sizeof(ai)); memset(sa, 0, sizeof(sa)); sa[0].u.sin.sin_family = AF_INET; sa[0].u.sin.sin_port = htons (port); sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); ai[0].ai_family = sa[0].u.sin.sin_family; ai[0].ai_addr = &sa[0].u.sa; ai[0].ai_addrlen = sizeof(sa[0].u.sin); ai[0].ai_next = &ai[1]; sa[1].u.sin6.sin6_family = AF_INET6; sa[1].u.sin6.sin6_port = htons (port); sa[1].u.sin6.sin6_addr = in6addr_loopback; ai[1].ai_family = sa[1].u.sin6.sin6_family; ai[1].ai_addr = &sa[1].u.sa; ai[1].ai_addrlen = sizeof(sa[1].u.sin6); ai[1].ai_next = nullptr; createTcpListeners(listeners, ai); } void network::createTcpListeners(std::list *listeners, const char *addr, int port) { struct addrinfo *ai, hints; char service[16]; int result; initSockets(); memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_canonname = nullptr; hints.ai_addr = nullptr; hints.ai_next = nullptr; snprintf (service, sizeof (service) - 1, "%d", port); service[sizeof (service) - 1] = '\0'; if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0) throw getaddrinfo_error("Unable to resolve listening address", result); try { createTcpListeners(listeners, ai); } catch(...) { freeaddrinfo(ai); throw; } freeaddrinfo(ai); } void network::createTcpListeners(std::list *listeners, const struct addrinfo *ai) { const struct addrinfo *current; std::list new_listeners; initSockets(); for (current = ai; current != nullptr; current = current->ai_next) { switch (current->ai_family) { case AF_INET: if (!UseIPv4) continue; break; case AF_INET6: if (!UseIPv6) continue; break; default: continue; } try { new_listeners.push_back(new TcpListener(current->ai_addr, current->ai_addrlen)); } catch (socket_error& e) { // Ignore this if it is due to lack of address family support on // the interface or on the system if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) { // Otherwise, report the error while (!new_listeners.empty()) { delete new_listeners.back(); new_listeners.pop_back(); } throw; } } } listeners->splice (listeners->end(), new_listeners); } TcpFilter::TcpFilter(const char* spec) { std::vector patterns; patterns = rfb::split(spec, ','); for (size_t i = 0; i < patterns.size(); i++) { if (!patterns[i].empty()) filter.push_back(parsePattern(patterns[i].c_str())); } } TcpFilter::~TcpFilter() { } static bool patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) { switch (pattern.address.u.sa.sa_family) { unsigned long address; case AF_INET: if (sa->u.sa.sa_family != AF_INET) return false; address = sa->u.sin.sin_addr.s_addr; if (address == htonl (INADDR_NONE)) return false; return ((pattern.address.u.sin.sin_addr.s_addr & pattern.mask.u.sin.sin_addr.s_addr) == (address & pattern.mask.u.sin.sin_addr.s_addr)); case AF_INET6: if (sa->u.sa.sa_family != AF_INET6) return false; for (unsigned int n = 0; n < 16; n++) { unsigned int bits = (n + 1) * 8; unsigned int mask; if (pattern.prefixlen > bits) mask = 0xff; else { unsigned int lastbits = 0xff; lastbits <<= bits - pattern.prefixlen; mask = lastbits & 0xff; } if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) != (sa->u.sin6.sin6_addr.s6_addr[n] & mask)) return false; if (mask < 0xff) break; } return true; case AF_UNSPEC: // Any address matches return true; default: break; } return false; } bool TcpFilter::verifyConnection(Socket* s) { vnc_sockaddr_t sa; socklen_t sa_size = sizeof(sa); if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0) return false; std::list::iterator i; for (i=filter.begin(); i!=filter.end(); i++) { if (patternMatchIP(*i, &sa)) { switch ((*i).action) { case Accept: vlog.debug("ACCEPT %s", s->getPeerAddress()); return true; case Query: vlog.debug("QUERY %s", s->getPeerAddress()); s->setRequiresQuery(); return true; case Reject: vlog.debug("REJECT %s", s->getPeerAddress()); return false; } } } vlog.debug("[REJECT] %s", s->getPeerAddress()); return false; } TcpFilter::Pattern TcpFilter::parsePattern(const char* p) { TcpFilter::Pattern pattern; std::vector parts; int family; initSockets(); parts = rfb::split(&p[1], '/'); if (parts.size() > 2) throw std::invalid_argument("Invalid filter specified"); if (parts[0].empty()) { // Match any address memset (&pattern.address, 0, sizeof (pattern.address)); pattern.address.u.sa.sa_family = AF_UNSPEC; pattern.prefixlen = 0; } else { struct addrinfo hints; struct addrinfo *ai; int result; memset (&hints, 0, sizeof (hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; // Take out brackets, if present if (parts[0][0] == '[') { parts[0].erase(0, 1); if (!parts[0].empty() && parts[0][parts.size()-1] == ']') parts[0].erase(parts.size()-1, 1); } if ((result = getaddrinfo (parts[0].c_str(), nullptr, &hints, &ai)) != 0) { throw getaddrinfo_error("Unable to resolve host by name", result); } memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen); freeaddrinfo (ai); family = pattern.address.u.sa.sa_family; if (parts.size() > 1) { if (family == AF_INET && (parts[1].find('.') != std::string::npos)) { throw std::invalid_argument("Mask no longer supported for " "filter, use prefix instead"); } pattern.prefixlen = (unsigned int) atoi(parts[1].c_str()); } else { switch (family) { case AF_INET: pattern.prefixlen = 32; break; case AF_INET6: pattern.prefixlen = 128; break; default: throw std::runtime_error("Unknown address family"); } } } family = pattern.address.u.sa.sa_family; if (pattern.prefixlen > (family == AF_INET ? 32: 128)) throw std::invalid_argument(rfb::format("Invalid prefix length for " "filter address: %u", pattern.prefixlen)); // Compute mask from address and prefix length memset (&pattern.mask, 0, sizeof (pattern.mask)); switch (family) { unsigned long mask; case AF_INET: mask = 0; for (unsigned int i=0; i bits) pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff; else { unsigned int lastbits = 0xff; lastbits <<= bits - pattern.prefixlen; pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff; break; } } break; case AF_UNSPEC: // No mask to compute break; default: ; /* not reached */ } switch(p[0]) { case '+': pattern.action = TcpFilter::Accept; break; case '-': pattern.action = TcpFilter::Reject; break; case '?': pattern.action = TcpFilter::Query; break; }; return pattern; } std::string TcpFilter::patternToStr(const TcpFilter::Pattern& p) { char addr[INET6_ADDRSTRLEN + 2]; if (p.address.u.sa.sa_family == AF_INET) { getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin), addr, sizeof(addr), nullptr, 0, NI_NUMERICHOST); } else if (p.address.u.sa.sa_family == AF_INET6) { addr[0] = '['; getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6), addr + 1, sizeof(addr) - 2, nullptr, 0, NI_NUMERICHOST); strcat(addr, "]"); } else addr[0] = '\0'; char action; switch (p.action) { case Accept: action = '+'; break; case Reject: action = '-'; break; default: case Query: action = '?'; break; }; size_t resultlen = (1 // action + strlen (addr) // address + 1 // slash + 3 // prefix length, max 128 + 1); // terminating nul char* result = new char[resultlen]; if (addr[0] == '\0') snprintf(result, resultlen, "%c", action); else snprintf(result, resultlen, "%c%s/%u", action, addr, p.prefixlen); std::string out = result; delete [] result; return out; }