diff options
Diffstat (limited to 'common')
170 files changed, 4102 insertions, 3966 deletions
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 6fd1e106..96b4e6b7 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,4 +1,4 @@ -add_subdirectory(os) +add_subdirectory(core) add_subdirectory(rdr) add_subdirectory(network) add_subdirectory(rfb) @@ -9,6 +9,6 @@ add_subdirectory(rfb) # is passed (additionally, libvnc is not used on Windows.) if(NOT WIN32) - set_target_properties(os rdr network rfb + set_target_properties(core rdr network rfb PROPERTIES COMPILE_FLAGS -fPIC) endif() diff --git a/common/core/CMakeLists.txt b/common/core/CMakeLists.txt new file mode 100644 index 00000000..7e58acc0 --- /dev/null +++ b/common/core/CMakeLists.txt @@ -0,0 +1,32 @@ +add_library(core STATIC + Configuration.cxx + Exception.cxx + Logger.cxx + Logger_file.cxx + Logger_stdio.cxx + LogWriter.cxx + Region.cxx + Timer.cxx + string.cxx + time.cxx + xdgdirs.cxx) + +target_include_directories(core PUBLIC ${CMAKE_SOURCE_DIR}/common) +target_include_directories(core SYSTEM PUBLIC ${PIXMAN_INCLUDE_DIRS}) +target_link_libraries(core ${PIXMAN_LIBRARIES}) + +if(UNIX) + target_sources(core PRIVATE Logger_syslog.cxx) +endif() + +if(WIN32) + target_link_libraries(core ws2_32) +endif() + +if(UNIX) + target_sources(core PRIVATE Logger_syslog.cxx) +endif() + +if(UNIX) + libtool_create_control_file(core) +endif() diff --git a/common/core/Configuration.cxx b/common/core/Configuration.cxx new file mode 100644 index 00000000..e6affb06 --- /dev/null +++ b/common/core/Configuration.cxx @@ -0,0 +1,857 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2004-2005 Cendio AB. + * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB + * Copyright 2011-2025 Pierre Ossman for Cendio AB + * + * 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. + */ + +// -=- Configuration.cxx + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include <algorithm> +#include <stdexcept> + +#include <core/Configuration.h> +#include <core/LogWriter.h> +#include <core/string.h> + +#include <rdr/HexOutStream.h> +#include <rdr/HexInStream.h> + +using namespace core; + +static LogWriter vlog("Config"); + + +// -=- The Global Configuration object +Configuration* Configuration::global_ = nullptr; + +Configuration* Configuration::global() { + if (!global_) + global_ = new Configuration(); + return global_; +} + +// -=- Configuration implementation + +bool Configuration::set(const char* paramName, const char* val, + bool immutable) +{ + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), paramName) == 0) { + bool b = current->setParam(val); + if (b && immutable) + current->setImmutable(); + return b; + } + } + return false; +} + +VoidParameter* Configuration::get(const char* param) +{ + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), param) == 0) + return current; + } + return nullptr; +} + +void Configuration::list(int width, int nameWidth) { + for (VoidParameter* current: params) { + std::string def_str = current->getDefaultStr(); + std::string desc_str = current->getDescription(); + if (!def_str.empty()) + desc_str += " (default=" + def_str + ")"; + const char* desc = desc_str.c_str(); + fprintf(stderr," %-*s -", nameWidth, current->getName()); + int column = strlen(current->getName()); + if (column < nameWidth) column = nameWidth; + column += 4; + while (true) { + if (desc[0] == '\0') + break; + + int wordLen = strcspn(desc, " \f\n\r\t\v,"); + if (wordLen == 0) { + desc++; + continue; + } + + if (desc[wordLen] == ',') + wordLen++; + + if (column + wordLen + 1 > width) { + fprintf(stderr,"\n%*s",nameWidth+4,""); + column = nameWidth+4; + } + fprintf(stderr, " "); + column++; + + fprintf(stderr, "%.*s", wordLen, desc); + column += wordLen; + desc += wordLen; + } + fprintf(stderr,"\n"); + } +} + + +bool Configuration::remove(const char* param) { + std::list<VoidParameter*>::iterator iter; + + iter = std::find_if(params.begin(), params.end(), + [param](VoidParameter* p) { + return strcasecmp(p->getName(), param) == 0; + }); + if (iter == params.end()) + return false; + + params.erase(iter); + return true; +} + +int Configuration::handleArg(int argc, char* argv[], int index) +{ + std::string param, val; + const char* equal = strchr(argv[index], '='); + + if (equal == argv[index]) + return 0; + + if (equal) { + param.assign(argv[index], equal-argv[index]); + val.assign(equal+1); + } else { + param.assign(argv[index]); + } + + if ((param.length() > 0) && (param[0] == '-')) { + // allow gnu-style --<option> + if ((param.length() > 1) && (param[1] == '-')) + param = param.substr(2); + else + param = param.substr(1); + } else { + // All command line arguments need either an initial '-', or an '=' + if (!equal) + return 0; + } + + if (equal) + return set(param.c_str(), val.c_str()) ? 1 : 0; + + for (VoidParameter* current: params) { + if (strcasecmp(current->getName(), param.c_str()) != 0) + continue; + + // We need to resolve an ambiguity for booleans + if (dynamic_cast<BoolParameter*>(current) != nullptr) { + if (index+1 < argc) { + // FIXME: Should not duplicate the list of values here + if ((strcasecmp(argv[index+1], "0") == 0) || + (strcasecmp(argv[index+1], "1") == 0) || + (strcasecmp(argv[index+1], "on") == 0) || + (strcasecmp(argv[index+1], "off") == 0) || + (strcasecmp(argv[index+1], "true") == 0) || + (strcasecmp(argv[index+1], "false") == 0) || + (strcasecmp(argv[index+1], "yes") == 0) || + (strcasecmp(argv[index+1], "no") == 0)) { + return current->setParam(argv[index+1]) ? 2 : 0; + } + } + } + + if (current->setParam()) + return 1; + + if (index+1 >= argc) + return 0; + + return current->setParam(argv[index+1]) ? 2 : 0; + } + + return 0; +} + + +// -=- VoidParameter + +VoidParameter::VoidParameter(const char* name_, const char* desc_) + : immutable(false), name(name_), description(desc_) +{ + Configuration *conf; + + conf = Configuration::global(); + conf->params.push_back(this); + conf->params.sort([](const VoidParameter* a, const VoidParameter* b) { + return strcasecmp(a->getName(), b->getName()) < 0; + }); +} + +VoidParameter::~VoidParameter() { + Configuration *conf; + + conf = Configuration::global(); + conf->params.remove(this); +} + +const char* +VoidParameter::getName() const { + return name; +} + +const char* +VoidParameter::getDescription() const { + return description; +} + +bool VoidParameter::setParam() { + return false; +} + +bool VoidParameter::isDefault() const { + return getDefaultStr() == getValueStr(); +} + +void +VoidParameter::setImmutable() { + vlog.debug("Set immutable %s", getName()); + immutable = true; +} + +// -=- AliasParameter + +AliasParameter::AliasParameter(const char* name_, const char* desc_, + VoidParameter* param_) + : VoidParameter(name_, desc_), param(param_) { +} + +bool +AliasParameter::setParam(const char* v) { + return param->setParam(v); +} + +bool AliasParameter::setParam() { + return param->setParam(); +} + +std::string AliasParameter::getDefaultStr() const { + return ""; +} + +std::string AliasParameter::getValueStr() const { + return param->getValueStr(); +} + +void +AliasParameter::setImmutable() { + vlog.debug("Set immutable %s (Alias)", getName()); + param->setImmutable(); +} + + +// -=- BoolParameter + +BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v) +: VoidParameter(name_, desc_), value(v), def_value(v) { +} + +bool +BoolParameter::setParam(const char* v) { + if (immutable) return true; + + if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0 + || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0) + setParam(true); + else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0 + || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0) + setParam(false); + else { + vlog.error("Bool parameter %s: Invalid value '%s'", getName(), v); + return false; + } + + return true; +} + +bool BoolParameter::setParam() { + setParam(true); + return true; +} + +void BoolParameter::setParam(bool b) { + if (immutable) return; + value = b; + vlog.debug("Set %s(Bool) to %s", getName(), getValueStr().c_str()); +} + +std::string BoolParameter::getDefaultStr() const { + return def_value ? "on" : "off"; +} + +std::string BoolParameter::getValueStr() const { + return value ? "on" : "off"; +} + +BoolParameter::operator bool() const { + return value; +} + +// -=- IntParameter + +IntParameter::IntParameter(const char* name_, const char* desc_, int v, + int minValue_, int maxValue_) + : VoidParameter(name_, desc_), value(v), def_value(v), + minValue(minValue_), maxValue(maxValue_) +{ + if (v < minValue || v > maxValue) { + vlog.error("Invalid default value %d for %s", v, getName()); + throw std::invalid_argument("Invalid default value"); + } +} + +bool +IntParameter::setParam(const char* v) { + char* end; + long n; + if (immutable) return true; + n = strtol(v, &end, 0); + if ((*end != 0) || (n < INT_MIN) || (n > INT_MAX)) { + vlog.error("Int parameter %s: Invalid value '%s'", getName(), v); + return false; + } + return setParam(n); +} + +bool +IntParameter::setParam(int v) { + if (immutable) return true; + if (v < minValue || v > maxValue) { + vlog.error("Int parameter %s: Invalid value '%d'", getName(), v); + return false; + } + vlog.debug("Set %s(Int) to %d", getName(), v); + value = v; + return true; +} + +std::string IntParameter::getDefaultStr() const { + char result[16]; + sprintf(result, "%d", def_value); + return result; +} + +std::string IntParameter::getValueStr() const { + char result[16]; + sprintf(result, "%d", value); + return result; +} + +IntParameter::operator int() const { + return value; +} + +// -=- StringParameter + +StringParameter::StringParameter(const char* name_, const char* desc_, + const char* v) + : VoidParameter(name_, desc_), value(v?v:""), def_value(v?v:"") +{ + if (!v) { + vlog.error("Default value <null> for %s not allowed",name_); + throw std::invalid_argument("Default value <null> not allowed"); + } +} + +bool StringParameter::setParam(const char* v) { + if (immutable) return true; + if (!v) + throw std::invalid_argument("setParam(<null>) not allowed"); + vlog.debug("Set %s(String) to %s", getName(), v); + value = v; + return true; +} + +std::string StringParameter::getDefaultStr() const { + return def_value; +} + +std::string StringParameter::getValueStr() const { + return value; +} + +StringParameter::operator const char *() const { + return value.c_str(); +} + +// -=- EnumParameter + +EnumParameter::EnumParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums_, + const char* v) + : VoidParameter(name_, desc_), value(v?v:""), def_value(v?v:"") +{ + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + + for (const char* e: enums_) { + if (!e) { + vlog.error("Enumeration <null> for %s not allowed", name_); + throw std::invalid_argument("Enumeration <null> not allowed"); + } + enums.insert(e); + } + + if (std::find(enums.begin(), enums.end(), def_value) == enums.end()) { + vlog.error("Default value %s for %s is not in list of valid values", + def_value.c_str(), name_); + throw std::invalid_argument("Default value is not in list of valid values"); + } +} + +bool EnumParameter::setParam(const char* v) +{ + std::set<std::string>::const_iterator iter; + if (immutable) return true; + if (!v) + throw std::invalid_argument("setParam(<null>) not allowed"); + iter = std::find_if(enums.begin(), enums.end(), + [v](const std::string& e) { + return strcasecmp(e.c_str(), v) == 0; + }); + if (iter == enums.end()) { + vlog.error("Enum parameter %s: Invalid value '%s'", getName(), v); + return false; + } + vlog.debug("Set %s(Enum) to %s", getName(), iter->c_str()); + value = *iter; + return true; +} + +std::string EnumParameter::getDefaultStr() const +{ + return def_value; +} + +std::string EnumParameter::getValueStr() const +{ + return value; +} + +bool EnumParameter::operator==(const char* other) const +{ + return strcasecmp(value.c_str(), other) == 0; +} + +bool EnumParameter::operator==(const std::string& other) const +{ + return *this == other.c_str(); +} + +bool EnumParameter::operator!=(const char* other) const +{ + return strcasecmp(value.c_str(), other) != 0; +} + +bool EnumParameter::operator!=(const std::string& other) const +{ + return *this != other.c_str(); +} + +// -=- BinaryParameter + +BinaryParameter::BinaryParameter(const char* name_, const char* desc_, + const uint8_t* v, size_t l) +: VoidParameter(name_, desc_), + value(nullptr), length(0), def_value(nullptr), def_length(0) { + if (l) { + assert(v); + value = new uint8_t[l]; + length = l; + memcpy(value, v, l); + def_value = new uint8_t[l]; + def_length = l; + memcpy(def_value, v, l); + } +} +BinaryParameter::~BinaryParameter() { + delete [] value; + delete [] def_value; +} + +bool BinaryParameter::setParam(const char* v) { + if (immutable) return true; + std::vector<uint8_t> newValue = hexToBin(v, strlen(v)); + if (newValue.empty() && strlen(v) > 0) + return false; + setParam(newValue.data(), newValue.size()); + return true; +} + +void BinaryParameter::setParam(const uint8_t* v, size_t len) { + if (immutable) return; + vlog.debug("Set %s(Binary)", getName()); + delete [] value; + value = nullptr; + length = 0; + if (len) { + assert(v); + value = new uint8_t[len]; + length = len; + memcpy(value, v, len); + } +} + +std::string BinaryParameter::getDefaultStr() const { + return binToHex(def_value, def_length); +} + +std::string BinaryParameter::getValueStr() const { + return binToHex(value, length); +} + +std::vector<uint8_t> BinaryParameter::getData() const { + std::vector<uint8_t> out(length); + memcpy(out.data(), value, length); + return out; +} + +// -=- ListParameter template + +template<typename ValueType> +ListParameter<ValueType>::ListParameter(const char* name_, + const char* desc_, + const ListType& v) + : VoidParameter(name_, desc_), value(v), def_value(v) +{ +} + +template<typename ValueType> +bool ListParameter<ValueType>::setParam(const char* v) +{ + std::vector<std::string> entries; + ListType new_value; + + if (immutable) + return true; + + // setParam({}) ends up as setParam(nullptr) + if (v != nullptr) + entries = split(v, ','); + + for (std::string& entry : entries) { + ValueType e; + + entry.erase(0, entry.find_first_not_of(" \f\n\r\t\v")); + entry.erase(entry.find_last_not_of(" \f\n\r\t\v")+1); + + // Special case, entire v was just whitespace + if (entry.empty() && (entries.size() == 1)) + break; + + if (!decodeEntry(entry.c_str(), &e)) { + vlog.error("List parameter %s: Invalid value '%s'", + getName(), entry.c_str()); + return false; + } + + new_value.push_back(e); + } + + return setParam(new_value); +} + +template<typename ValueType> +bool ListParameter<ValueType>::setParam(const ListType& v) +{ + ListType vnorm; + if (immutable) + return true; + for (const ValueType& entry : v) { + if (!validateEntry(entry)) { + vlog.error("List parameter %s: Invalid value '%s'", getName(), + encodeEntry(entry).c_str()); + return false; + } + vnorm.push_back(normaliseEntry(entry)); + } + value = vnorm; + vlog.debug("set %s(List) to %s", getName(), getValueStr().c_str()); + return true; +} + +template<typename ValueType> +std::string ListParameter<ValueType>::getDefaultStr() const +{ + std::string result; + + for (ValueType entry : def_value) { + // FIXME: Might want to add a space here as well for readability, + // but this would sacrifice backward compatibility + if (!result.empty()) + result += ','; + result += encodeEntry(entry); + } + + return result; +} + +template<typename ValueType> +std::string ListParameter<ValueType>::getValueStr() const +{ + std::string result; + + for (ValueType entry : value) { + // FIXME: Might want to add a space here as well for readability, + // but this would sacrifice backward compatibility + if (!result.empty()) + result += ','; + result += encodeEntry(entry); + } + + return result; +} + +template<typename ValueType> +typename ListParameter<ValueType>::const_iterator ListParameter<ValueType>::begin() const +{ + return value.begin(); +} + +template<typename ValueType> +typename ListParameter<ValueType>::const_iterator ListParameter<ValueType>::end() const +{ + return value.end(); +} + +template<typename ValueType> +bool ListParameter<ValueType>::validateEntry(const ValueType& /*entry*/) const +{ + return true; +} + +template<typename ValueType> +ValueType ListParameter<ValueType>::normaliseEntry(const ValueType& entry) const +{ + return entry; +} + +// -=- IntListParameter + +template class core::ListParameter<int>; + +IntListParameter::IntListParameter(const char* name_, const char* desc_, + const ListType& v, + int minValue_, int maxValue_) + : ListParameter<int>(name_, desc_, v), + minValue(minValue_), maxValue(maxValue_) +{ + for (int entry : v) { + if (!validateEntry(entry)) { + vlog.error("Invalid default value %d for %s", entry, getName()); + throw std::invalid_argument("Invalid default value"); + } + } +} + +bool IntListParameter::decodeEntry(const char* entry, int* out) const +{ + long n; + char *end; + + assert(entry); + assert(out); + + if (entry[0] == '\0') + return false; + + n = strtol(entry, &end, 0); + if ((*end != 0) || (n < INT_MIN) || (n > INT_MAX)) + return false; + + *out = n; + + return true; +} + +std::string IntListParameter::encodeEntry(const int& entry) const +{ + char valstr[16]; + sprintf(valstr, "%d", entry); + return valstr; +} + +bool IntListParameter::validateEntry(const int& entry) const +{ + return (entry >= minValue) && (entry <= maxValue); +} + +// -=- StringListParameter + +template class core::ListParameter<std::string>; + +StringListParameter::StringListParameter(const char* name_, + const char* desc_, + const std::list<const char*>& v_) + : ListParameter<std::string>(name_, desc_, {}) +{ + for (const char* v: v_) { + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + value.push_back(v); + def_value.push_back(v); + } +} + +StringListParameter::const_iterator StringListParameter::begin() const +{ + return ListParameter<std::string>::begin(); +} + +StringListParameter::const_iterator StringListParameter::end() const +{ + return ListParameter<std::string>::end(); +} + +bool StringListParameter::decodeEntry(const char* entry, std::string* out) const +{ + *out = entry; + return true; +} + +std::string StringListParameter::encodeEntry(const std::string& entry) const +{ + return entry; +} + +// -=- EnumListEntry + +EnumListEntry::EnumListEntry(const std::string& v) + : value(v) +{ +} + +std::string EnumListEntry::getValueStr() const +{ + return value; +} + +bool EnumListEntry::operator==(const char* other) const +{ + return strcasecmp(value.c_str(), other) == 0; +} + +bool EnumListEntry::operator==(const std::string& other) const +{ + return *this == other.c_str(); +} + +bool EnumListEntry::operator!=(const char* other) const +{ + return strcasecmp(value.c_str(), other) != 0; +} + +bool EnumListEntry::operator!=(const std::string& other) const +{ + return *this != other.c_str(); +} + +// -=- EnumListParameter + +EnumListParameter::EnumListParameter(const char* name_, + const char* desc_, + const std::set<const char*>& enums_, + const std::list<const char*>& v_) + : ListParameter<std::string>(name_, desc_, {}) +{ + for (const char* v: v_) { + if (!v) { + vlog.error("Default value <null> for %s not allowed", name_); + throw std::invalid_argument("Default value <null> not allowed"); + } + value.push_back(v); + def_value.push_back(v); + } + + for (const char* e: enums_) { + if (!e) { + vlog.error("Enumeration <null> for %s not allowed", name_); + throw std::invalid_argument("Enumeration <null> not allowed"); + } + enums.insert(e); + } + + for (const std::string& def_entry : def_value) { + if (std::find(enums.begin(), enums.end(), def_entry) == enums.end()) { + vlog.error("Default value %s for %s is not in list of valid values", + def_entry.c_str(), name_); + throw std::invalid_argument("Default value is not in list of valid values"); + } + } +} + +EnumListParameter::const_iterator EnumListParameter::begin() const +{ + return ListParameter<std::string>::begin(); +} + +EnumListParameter::const_iterator EnumListParameter::end() const +{ + return ListParameter<std::string>::end(); +} + +bool EnumListParameter::decodeEntry(const char* entry, std::string* out) const +{ + *out = entry; + return true; +} + +std::string EnumListParameter::encodeEntry(const std::string& entry) const +{ + return entry; +} + +bool EnumListParameter::validateEntry(const std::string& entry) const +{ + for (const std::string& e : enums) { + if (strcasecmp(e.c_str(), entry.c_str()) == 0) + return true; + } + return false; +} + +std::string EnumListParameter::normaliseEntry(const std::string& entry) const +{ + for (const std::string& e : enums) { + if (strcasecmp(e.c_str(), entry.c_str()) == 0) + return e; + } + throw std::logic_error("Entry is not in list of valid values"); +} diff --git a/common/rfb/Configuration.h b/common/core/Configuration.h index ec8d789a..431dd0c5 100644 --- a/common/rfb/Configuration.h +++ b/common/core/Configuration.h @@ -1,5 +1,5 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2011-2022 Pierre Ossman for Cendio AB + * Copyright 2011-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,22 +41,20 @@ // NB: NO LOCKING is performed when linking Configurations to groups // or when adding Parameters to Configurations. -#ifndef __RFB_CONFIGURATION_H__ -#define __RFB_CONFIGURATION_H__ +#ifndef __CORE_CONFIGURATION_H__ +#define __CORE_CONFIGURATION_H__ #include <limits.h> #include <stdint.h> +#include <list> +#include <set> #include <string> #include <vector> -namespace os { class Mutex; } +namespace core { -namespace rfb { class VoidParameter; - struct ParameterIterator; - - enum ConfigurationObject { ConfGlobal, ConfServer, ConfViewer }; // -=- Configuration // Class used to access parameters. @@ -64,22 +62,11 @@ namespace rfb { class Configuration { public: // - Create a new Configuration object - Configuration(const char* name_) - : name(name_), head(nullptr), _next(nullptr) {} - - // - Return the buffer containing the Configuration's name - const char* getName() const { return name.c_str(); } + Configuration() {} // - Set named parameter to value bool set(const char* param, const char* value, bool immutable=false); - // - Set parameter to value (separated by "=") - bool set(const char* config, bool immutable=false); - - // - Set named parameter to value, with name truncated at len - bool set(const char* name, int len, - const char* val, bool immutable); - // - Get named parameter VoidParameter* get(const char* param); @@ -89,15 +76,18 @@ namespace rfb { // - Remove a parameter from this Configuration group bool remove(const char* param); - // - readFromFile - // Read configuration parameters from the specified file. - void readFromFile(const char* filename); + // - handleArg + // Parse a command line argument into a parameter, returning how + // many arguments were consumed + int handleArg(int argc, char* argv[], int index); + - // - writeConfigToFile - // Write a new configuration parameters file, then mv it - // over the old file. - void writeToFile(const char* filename); + // - Iterate over all parameters + std::list<VoidParameter*>::iterator begin() { return params.begin(); } + std::list<VoidParameter*>::iterator end() { return params.end(); } + // - Returns the number of parameters + int size() { return params.size(); } // - Get the Global Configuration object // NB: This call does NOT lock the Configuration system. @@ -106,21 +96,10 @@ namespace rfb { // global() is called when only the main thread is running. static Configuration* global(); - // Enable server/viewer specific parameters - static void enableServerParams() { global()->appendConfiguration(server()); } - static void enableViewerParams() { global()->appendConfiguration(viewer()); } - // - Container for process-wide Global parameters static bool setParam(const char* param, const char* value, bool immutable=false) { return global()->set(param, value, immutable); } - static bool setParam(const char* config, bool immutable=false) { - return global()->set(config, immutable); - } - static bool setParam(const char* name, int len, - const char* val, bool immutable) { - return global()->set(name, len, val, immutable); - } static VoidParameter* getParam(const char* param) { return global()->get(param); } static void listParams(int width=79, int nameWidth=10) { global()->list(width, nameWidth); @@ -128,38 +107,18 @@ namespace rfb { static bool removeParam(const char* param) { return global()->remove(param); } + static int handleParamArg(int argc, char* argv[], int index) { + return global()->handleArg(argc, argv, index); + } private: friend class VoidParameter; - friend struct ParameterIterator; - // Name for this Configuration - std::string name; - - // - Pointer to first Parameter in this group - VoidParameter* head; - - // Pointer to next Configuration in this group - Configuration* _next; + // - List of Parameters + std::list<VoidParameter*> params; // The process-wide, Global Configuration object static Configuration* global_; - - // The server only Configuration object - static Configuration* server_; - - // The viewer only Configuration object - static Configuration* viewer_; - - // Get server/viewer specific configuration object - static Configuration* server(); - static Configuration* viewer(); - - // Append configuration object to this instance. - // NOTE: conf instance can be only one configuration object - void appendConfiguration(Configuration *conf) { - conf->_next = _next; _next = conf; - } }; // -=- VoidParameter @@ -167,7 +126,7 @@ namespace rfb { class VoidParameter { public: - VoidParameter(const char* name_, const char* desc_, ConfigurationObject co=ConfGlobal); + VoidParameter(const char* name_, const char* desc_); virtual ~VoidParameter(); const char* getName() const; const char* getDescription() const; @@ -176,31 +135,27 @@ namespace rfb { virtual bool setParam(); virtual std::string getDefaultStr() const = 0; virtual std::string getValueStr() const = 0; - virtual bool isBool() const; + + virtual bool isDefault() const; virtual void setImmutable(); protected: friend class Configuration; - friend struct ParameterIterator; VoidParameter* _next; bool immutable; const char* name; const char* description; - - os::Mutex* mutex; }; class AliasParameter : public VoidParameter { public: - AliasParameter(const char* name_, const char* desc_,VoidParameter* param_, - ConfigurationObject co=ConfGlobal); + AliasParameter(const char* name_, const char* desc_,VoidParameter* param_); bool setParam(const char* value) override; bool setParam() override; std::string getDefaultStr() const override; std::string getValueStr() const override; - bool isBool() const override; void setImmutable() override; private: VoidParameter* param; @@ -208,14 +163,12 @@ namespace rfb { class BoolParameter : public VoidParameter { public: - BoolParameter(const char* name_, const char* desc_, bool v, - ConfigurationObject co=ConfGlobal); + BoolParameter(const char* name_, const char* desc_, bool v); bool setParam(const char* value) override; bool setParam() override; virtual void setParam(bool b); std::string getDefaultStr() const override; std::string getValueStr() const override; - bool isBool() const override; operator bool() const; protected: bool value; @@ -225,8 +178,7 @@ namespace rfb { class IntParameter : public VoidParameter { public: IntParameter(const char* name_, const char* desc_, int v, - int minValue=INT_MIN, int maxValue=INT_MAX, - ConfigurationObject co=ConfGlobal); + int minValue=INT_MIN, int maxValue=INT_MAX); using VoidParameter::setParam; bool setParam(const char* value) override; virtual bool setParam(int v); @@ -241,11 +193,7 @@ namespace rfb { class StringParameter : public VoidParameter { public: - // StringParameter contains a null-terminated string, which CANNOT - // be Null, and so neither can the default value! - StringParameter(const char* name_, const char* desc_, const char* v, - ConfigurationObject co=ConfGlobal); - ~StringParameter() override; + StringParameter(const char* name_, const char* desc_, const char* v); bool setParam(const char* value) override; std::string getDefaultStr() const override; std::string getValueStr() const override; @@ -255,11 +203,29 @@ namespace rfb { std::string def_value; }; + class EnumParameter : public VoidParameter { + public: + EnumParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums, const char* v); + bool setParam(const char* value) override; + std::string getDefaultStr() const override; + std::string getValueStr() const override; + bool operator==(const char* other) const; + bool operator==(const std::string& other) const; + bool operator!=(const char* other) const; + bool operator!=(const std::string& other) const; + // operator const char*() omitted on purpose to force usage of above + // comparison operators + protected: + std::string value; + std::string def_value; + std::set<std::string> enums; + }; + class BinaryParameter : public VoidParameter { public: BinaryParameter(const char* name_, const char* desc_, - const uint8_t* v, size_t l, - ConfigurationObject co=ConfGlobal); + const uint8_t* v, size_t l); using VoidParameter::setParam; ~BinaryParameter() override; bool setParam(const char* value) override; @@ -276,25 +242,107 @@ namespace rfb { size_t def_length; }; - // -=- ParameterIterator - // Iterates over all enabled parameters (global + server/viewer). - // Current Parameter is accessed via param, the current Configuration - // via config. The next() method moves on to the next Parameter. - - struct ParameterIterator { - ParameterIterator() : config(Configuration::global()), param(config->head) {} - void next() { - param = param->_next; - while (!param) { - config = config->_next; - if (!config) break; - param = config->head; - } - } - Configuration* config; - VoidParameter* param; + template<typename ValueType> + class ListParameter : public VoidParameter { + public: + typedef std::list<ValueType> ListType; + typedef typename ListType::const_iterator const_iterator; + + ListParameter(const char* name_, const char* desc_, + const ListType& v); + using VoidParameter::setParam; + bool setParam(const char* value) override; + virtual bool setParam(const ListType& v); + std::string getDefaultStr() const override; + std::string getValueStr() const override; + + const_iterator begin() const; + const_iterator end() const; + + protected: + virtual bool decodeEntry(const char* entry, ValueType* out) const = 0; + virtual std::string encodeEntry(const ValueType& entry) const = 0; + virtual bool validateEntry(const ValueType& entry) const; + virtual ValueType normaliseEntry(const ValueType& entry) const; + + protected: + ListType value; + ListType def_value; + }; + + class IntListParameter : public ListParameter<int> { + public: + IntListParameter(const char* name_, const char* desc_, + const ListType& v, + int minValue=INT_MIN, int maxValue=INT_MAX); + protected: + bool decodeEntry(const char* entry, int* out) const override; + std::string encodeEntry(const int& entry) const override; + bool validateEntry(const int& entry) const override; + + protected: + int minValue, maxValue; + }; + + class StringListParameter : public ListParameter<std::string> { + public: + StringListParameter(const char* name_, const char* desc_, + const std::list<const char*>& v); + + class const_iterator : public ListType::const_iterator { + public: + const_iterator(const ListType::const_iterator& it) : ListType::const_iterator(it) {} + const char* operator*() const { return (ListType::const_iterator::operator*()).c_str(); } + }; + + const_iterator begin() const; + const_iterator end() const; + + protected: + bool decodeEntry(const char* entry, std::string* out) const override; + std::string encodeEntry(const std::string& entry) const override; + }; + + class EnumListEntry { + public: + EnumListEntry(const std::string& v); + std::string getValueStr() const; + bool operator==(const char* other) const; + bool operator==(const std::string& other) const; + bool operator!=(const char* other) const; + bool operator!=(const std::string& other) const; + // operator const char*() omitted on purpose to force usage of above + // comparison operators + protected: + std::string value; + }; + + class EnumListParameter : public ListParameter<std::string> { + public: + EnumListParameter(const char* name_, const char* desc_, + const std::set<const char*>& enums, + const std::list<const char*>& v); + + class const_iterator : public ListType::const_iterator { + public: + const_iterator(const ListType::const_iterator& it) : ListType::const_iterator(it) {} + const EnumListEntry operator*() const { return EnumListEntry(ListType::const_iterator::operator*()); } + const EnumListEntry operator->() const { return EnumListEntry(*ListType::const_iterator::operator->()); } + }; + + const_iterator begin() const; + const_iterator end() const; + + protected: + bool decodeEntry(const char* entry, std::string* out) const override; + std::string encodeEntry(const std::string& entry) const override; + bool validateEntry(const std::string& entry) const override; + std::string normaliseEntry(const std::string& entry) const override; + + protected: + std::set<std::string> enums; }; }; -#endif // __RFB_CONFIGURATION_H__ +#endif // __CORE_CONFIGURATION_H__ diff --git a/common/rdr/Exception.cxx b/common/core/Exception.cxx index f0c04a6a..5b4f0599 100644 --- a/common/rdr/Exception.cxx +++ b/common/core/Exception.cxx @@ -26,9 +26,8 @@ #include <stdio.h> #include <stdarg.h> -#include <rdr/Exception.h> -#include <rdr/TLSException.h> -#include <rfb/util.h> +#include <core/Exception.h> +#include <core/string.h> #ifdef _WIN32 #include <winsock2.h> @@ -40,20 +39,20 @@ #include <string.h> -using namespace rdr; +using namespace core; getaddrinfo_error::getaddrinfo_error(const char* s, int err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", s, - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", s, + strerror(err_).c_str(), err_)), err(err_) { } getaddrinfo_error::getaddrinfo_error(const std::string& s, int err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", s.c_str(), - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", s.c_str(), + strerror(err_).c_str(), err_)), err(err_) { } @@ -73,15 +72,15 @@ std::string getaddrinfo_error::strerror(int err_) const noexcept } posix_error::posix_error(const char* what_arg, int err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", what_arg, - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", what_arg, + strerror(err_).c_str(), err_)), err(err_) { } posix_error::posix_error(const std::string& what_arg, int err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", what_arg.c_str(), - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", what_arg.c_str(), + strerror(err_).c_str(), err_)), err(err_) { } @@ -102,16 +101,16 @@ std::string posix_error::strerror(int err_) const noexcept #ifdef WIN32 win32_error::win32_error(const char* what_arg, unsigned err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", what_arg, - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", what_arg, + strerror(err_).c_str(), err_)), err(err_) { } win32_error::win32_error(const std::string& what_arg, unsigned err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", what_arg.c_str(), - strerror(err_).c_str(), err_)), + : std::runtime_error(core::format("%s: %s (%d)", what_arg.c_str(), + strerror(err_).c_str(), err_)), err(err_) { } diff --git a/common/rdr/Exception.h b/common/core/Exception.h index d3cecc18..04463a17 100644 --- a/common/rdr/Exception.h +++ b/common/core/Exception.h @@ -19,13 +19,13 @@ * USA. */ -#ifndef __RDR_EXCEPTION_H__ -#define __RDR_EXCEPTION_H__ +#ifndef __CORE_EXCEPTION_H__ +#define __CORE_EXCEPTION_H__ #include <stdexcept> #include <string> -namespace rdr { +namespace core { class posix_error : public std::runtime_error { public: @@ -70,11 +70,6 @@ namespace rdr { std::string strerror(int err_) const noexcept; }; - class end_of_stream : public std::runtime_error { - public: - end_of_stream() noexcept : std::runtime_error("End of stream") {} - }; - } #endif diff --git a/common/rfb/LogWriter.cxx b/common/core/LogWriter.cxx index 8e39d544..98e5201c 100644 --- a/common/rfb/LogWriter.cxx +++ b/common/core/LogWriter.cxx @@ -22,17 +22,16 @@ #include <config.h> #endif -#include <string.h> - -#include <rfb/LogWriter.h> -#include <rfb/Configuration.h> -#include <rfb/util.h> #include <stdlib.h> +#include <string.h> -rfb::LogParameter rfb::logParams; +#include <core/Configuration.h> +#include <core/LogWriter.h> +#include <core/string.h> -using namespace rfb; +using namespace core; +LogParameter core::logParams; LogWriter::LogWriter(const char* name) : m_name(name), m_level(0), m_log(nullptr), m_next(log_writers) { @@ -113,23 +112,23 @@ bool LogWriter::setLogParams(const char* params) { LogParameter::LogParameter() - : StringParameter("Log", + : StringListParameter("Log", "Specifies which log output should be directed to " "which target logger, and the level of output to log. " "Format is <log>:<target>:<level>[, ...].", - "") { + {}) +{ } bool LogParameter::setParam(const char* v) { if (immutable) return true; LogWriter::setLogParams("*::0"); - StringParameter::setParam(v); - std::vector<std::string> parts; - parts = split(v, ','); - for (size_t i = 0; i < parts.size(); i++) { - if (parts[i].empty()) + if (!StringListParameter::setParam(v)) + return false; + for (const char* part : *this) { + if (part[0] == '\0') continue; - if (!LogWriter::setLogParams(parts[i].c_str())) + if (!LogWriter::setLogParams(part)) return false; } return true; diff --git a/common/rfb/LogWriter.h b/common/core/LogWriter.h index d1fd4990..58dcc063 100644 --- a/common/rfb/LogWriter.h +++ b/common/core/LogWriter.h @@ -18,12 +18,13 @@ // -=- LogWriter.h - The Log writer class. -#ifndef __RFB_LOG_WRITER_H__ -#define __RFB_LOG_WRITER_H__ +#ifndef __CORE_LOG_WRITER_H__ +#define __CORE_LOG_WRITER_H__ #include <stdarg.h> -#include <rfb/Logger.h> -#include <rfb/Configuration.h> + +#include <core/Configuration.h> +#include <core/Logger.h> // Each log writer instance has a unique textual name, // and is attached to a particular Log instance and @@ -46,9 +47,7 @@ } \ } -namespace rfb { - - class LogWriter; +namespace core { class LogWriter { public: @@ -101,13 +100,15 @@ namespace rfb { LogWriter* m_next; }; - class LogParameter : public StringParameter { + class LogParameter : public StringListParameter { public: LogParameter(); bool setParam(const char* v) override; + // We explicitly don't inherit setParam(std::list) as we want to + // force callers to use the above method to parse the values }; extern LogParameter logParams; }; -#endif // __RFB_LOG_WRITER_H__ +#endif // __CORE_LOG_WRITER_H__ diff --git a/common/rfb/Logger.cxx b/common/core/Logger.cxx index 25f7ccb7..ad9a3d63 100644 --- a/common/rfb/Logger.cxx +++ b/common/core/Logger.cxx @@ -26,10 +26,10 @@ #include <stdio.h> #include <string.h> -#include <rfb/Logger.h> -#include <rfb/LogWriter.h> +#include <core/Logger.h> +#include <core/LogWriter.h> -using namespace rfb; +using namespace core; Logger* Logger::loggers = nullptr; diff --git a/common/rfb/Logger.h b/common/core/Logger.h index 76f03535..5291915f 100644 --- a/common/rfb/Logger.h +++ b/common/core/Logger.h @@ -18,8 +18,8 @@ // -=- Logger.h - The Logger class. -#ifndef __RFB_LOGGER_H__ -#define __RFB_LOGGER_H__ +#ifndef __CORE_LOGGER_H__ +#define __CORE_LOGGER_H__ #include <stdarg.h> #include <stdio.h> @@ -28,7 +28,7 @@ // and is attached to a particular Logger instance and // is assigned a particular log level. -namespace rfb { +namespace core { class Logger { public: @@ -68,4 +68,4 @@ namespace rfb { }; -#endif // __RFB_LOGGER_H__ +#endif // __CORE_LOGGER_H__ diff --git a/common/rfb/Logger_file.cxx b/common/core/Logger_file.cxx index eabe420a..b8c72270 100644 --- a/common/rfb/Logger_file.cxx +++ b/common/core/Logger_file.cxx @@ -26,30 +26,24 @@ #include <stdlib.h> #include <string.h> -#include <os/Mutex.h> +#include <core/Logger_file.h> -#include <rfb/Logger_file.h> - -using namespace rfb; +using namespace core; Logger_File::Logger_File(const char* loggerName) : Logger(loggerName), indent(13), width(79), m_file(nullptr), m_lastLogTime(0) { m_filename[0] = '\0'; - mutex = new os::Mutex(); } Logger_File::~Logger_File() { closeFile(); - delete mutex; } void Logger_File::write(int /*level*/, const char *logname, const char *message) { - os::AutoMutex a(mutex); - if (!m_file) { if (m_filename[0] == '\0') return; @@ -121,7 +115,8 @@ void Logger_File::closeFile() static Logger_File logger("file"); -bool rfb::initFileLogger(const char* filename) { +bool core::initFileLogger(const char* filename) +{ logger.setFilename(filename); logger.registerLogger(); return true; diff --git a/common/rfb/Logger_file.h b/common/core/Logger_file.h index 6f2a4ef6..6bce3e2d 100644 --- a/common/rfb/Logger_file.h +++ b/common/core/Logger_file.h @@ -18,17 +18,15 @@ // -=- Logger_file - log to a file -#ifndef __RFB_LOGGER_FILE_H__ -#define __RFB_LOGGER_FILE_H__ +#ifndef __CORE_LOGGER_FILE_H__ +#define __CORE_LOGGER_FILE_H__ #include <time.h> #include <limits.h> -#include <rfb/Logger.h> +#include <core/Logger.h> -namespace os { class Mutex; } - -namespace rfb { +namespace core { class Logger_File : public Logger { public: @@ -47,7 +45,6 @@ namespace rfb { char m_filename[PATH_MAX]; FILE* m_file; time_t m_lastLogTime; - os::Mutex* mutex; }; bool initFileLogger(const char* filename); diff --git a/common/rfb/Logger_stdio.cxx b/common/core/Logger_stdio.cxx index 5e5c6dea..f27fc35d 100644 --- a/common/rfb/Logger_stdio.cxx +++ b/common/core/Logger_stdio.cxx @@ -22,14 +22,15 @@ #include <config.h> #endif -#include <rfb/Logger_stdio.h> +#include <core/Logger_stdio.h> -using namespace rfb; +using namespace core; static Logger_StdIO logStdErr("stderr", stderr); static Logger_StdIO logStdOut("stdout", stdout); -bool rfb::initStdIOLoggers() { +bool core::initStdIOLoggers() +{ logStdErr.registerLogger(); logStdOut.registerLogger(); return true; diff --git a/common/rfb/Logger_stdio.h b/common/core/Logger_stdio.h index a1d17a0f..7613a20b 100644 --- a/common/rfb/Logger_stdio.h +++ b/common/core/Logger_stdio.h @@ -18,12 +18,12 @@ // -=- Logger_stdio - standard output logger instances -#ifndef __RFB_LOGGER_STDIO_H__ -#define __RFB_LOGGER_STDIO_H__ +#ifndef __CORE_LOGGER_STDIO_H__ +#define __CORE_LOGGER_STDIO_H__ -#include <rfb/Logger_file.h> +#include <core/Logger_file.h> -namespace rfb { +namespace core { class Logger_StdIO : public Logger_File { public: diff --git a/common/rfb/Logger_syslog.cxx b/common/core/Logger_syslog.cxx index de9e425e..cf7d065d 100644 --- a/common/rfb/Logger_syslog.cxx +++ b/common/core/Logger_syslog.cxx @@ -26,10 +26,10 @@ #include <string.h> #include <syslog.h> -#include <rfb/Logger_syslog.h> -#include <rfb/LogWriter.h> +#include <core/Logger_syslog.h> +#include <core/LogWriter.h> -using namespace rfb; +using namespace core; Logger_Syslog::Logger_Syslog(const char* loggerName) @@ -62,6 +62,7 @@ void Logger_Syslog::write(int level, const char *logname, const char *message) static Logger_Syslog logger("syslog"); -void rfb::initSyslogLogger() { +void core::initSyslogLogger() +{ logger.registerLogger(); } diff --git a/common/rfb/Logger_syslog.h b/common/core/Logger_syslog.h index 20c46a5f..46adf932 100644 --- a/common/rfb/Logger_syslog.h +++ b/common/core/Logger_syslog.h @@ -18,13 +18,14 @@ // -=- Logger_syslog - log to syslog -#ifndef __RFB_LOGGER_SYSLOG_H__ -#define __RFB_LOGGER_SYSLOG_H__ +#ifndef __CORE_LOGGER_SYSLOG_H__ +#define __CORE_LOGGER_SYSLOG_H__ #include <time.h> -#include <rfb/Logger.h> -namespace rfb { +#include <core/Logger.h> + +namespace core { class Logger_Syslog : public Logger { public: diff --git a/common/rfb/Rect.h b/common/core/Rect.h index b82ed274..e4cb1634 100644 --- a/common/rfb/Rect.h +++ b/common/core/Rect.h @@ -16,26 +16,16 @@ * USA. */ -// rfb::Rect and rfb::Point structures +// core::Rect and core::Point structures -#ifndef __RFB_RECT_INCLUDED__ -#define __RFB_RECT_INCLUDED__ +#ifndef __CORE_RECT_INCLUDED__ +#define __CORE_RECT_INCLUDED__ -// Some platforms (e.g. Windows) include max() and min() macros in their -// standard headers, but they are also standard C++ template functions, so some -// C++ headers will undefine them. So we steer clear of the names min and max -// and define __rfbmin and __rfbmax instead. +#include <algorithm> -#ifndef __rfbmax -#define __rfbmax(a,b) (((a) > (b)) ? (a) : (b)) -#endif -#ifndef __rfbmin -#define __rfbmin(a,b) (((a) < (b)) ? (a) : (b)) -#endif +namespace core { -namespace rfb { - - // rfb::Point + // core::Point // // Represents a point in 2D space, by X and Y coordinates. // Can also be used to represent a delta, or offset, between @@ -61,7 +51,7 @@ namespace rfb { int x, y; }; - // rfb::Rect + // core::Rect // // Represents a rectangular region defined by its top-left (tl) // and bottom-right (br) Points. @@ -83,10 +73,10 @@ namespace rfb { __attribute__ ((warn_unused_result)) { Rect result; - result.tl.x = __rfbmax(tl.x, r.tl.x); - result.tl.y = __rfbmax(tl.y, r.tl.y); - result.br.x = __rfbmax(__rfbmin(br.x, r.br.x), result.tl.x); - result.br.y = __rfbmax(__rfbmin(br.y, r.br.y), result.tl.y); + result.tl.x = std::max(tl.x, r.tl.x); + result.tl.y = std::max(tl.y, r.tl.y); + result.br.x = std::max(std::min(br.x, r.br.x), result.tl.x); + result.br.y = std::max(std::min(br.y, r.br.y), result.tl.y); return result; } inline Rect union_boundary(const Rect &r) const @@ -95,10 +85,10 @@ namespace rfb { if (r.is_empty()) return *this; if (is_empty()) return r; Rect result; - result.tl.x = __rfbmin(tl.x, r.tl.x); - result.tl.y = __rfbmin(tl.y, r.tl.y); - result.br.x = __rfbmax(br.x, r.br.x); - result.br.y = __rfbmax(br.y, r.br.y); + result.tl.x = std::min(tl.x, r.tl.x); + result.tl.y = std::min(tl.y, r.tl.y); + result.br.x = std::max(br.x, r.br.x); + result.br.y = std::max(br.y, r.br.y); return result; } inline Rect translate(const Point &p) const @@ -127,4 +117,4 @@ namespace rfb { Point br; }; } -#endif // __RFB_RECT_INCLUDED__ +#endif // __CORE_RECT_INCLUDED__ diff --git a/common/rfb/Region.cxx b/common/core/Region.cxx index cfdf0ca2..03d788a9 100644 --- a/common/rfb/Region.cxx +++ b/common/core/Region.cxx @@ -21,100 +21,119 @@ #include <config.h> #endif -#include <rfb/Region.h> -#include <rfb/LogWriter.h> +#include <core/LogWriter.h> +#include <core/Region.h> extern "C" { #include <pixman.h> } -static rfb::LogWriter vlog("Region"); +using namespace core; -rfb::Region::Region() { +static LogWriter vlog("Region"); + +Region::Region() +{ rgn = new struct pixman_region16; pixman_region_init(rgn); } -rfb::Region::Region(const Rect& r) { +Region::Region(const Rect& r) +{ rgn = new struct pixman_region16; pixman_region_init_rect(rgn, r.tl.x, r.tl.y, r.width(), r.height()); } -rfb::Region::Region(const rfb::Region& r) { +Region::Region(const Region& r) +{ rgn = new struct pixman_region16; pixman_region_init(rgn); pixman_region_copy(rgn, r.rgn); } -rfb::Region::~Region() { +Region::~Region() +{ pixman_region_fini(rgn); delete rgn; } -rfb::Region& rfb::Region::operator=(const rfb::Region& r) { +Region& Region::operator=(const Region& r) +{ pixman_region_copy(rgn, r.rgn); return *this; } -void rfb::Region::clear() { +void Region::clear() +{ // pixman_region_clear() isn't available on some older systems pixman_region_fini(rgn); pixman_region_init(rgn); } -void rfb::Region::reset(const Rect& r) { +void Region::reset(const Rect& r) +{ pixman_region_fini(rgn); pixman_region_init_rect(rgn, r.tl.x, r.tl.y, r.width(), r.height()); } -void rfb::Region::translate(const Point& delta) { +void Region::translate(const Point& delta) +{ pixman_region_translate(rgn, delta.x, delta.y); } -void rfb::Region::assign_intersect(const rfb::Region& r) { +void Region::assign_intersect(const Region& r) +{ pixman_region_intersect(rgn, rgn, r.rgn); } -void rfb::Region::assign_union(const rfb::Region& r) { +void Region::assign_union(const Region& r) +{ pixman_region_union(rgn, rgn, r.rgn); } -void rfb::Region::assign_subtract(const rfb::Region& r) { +void Region::assign_subtract(const Region& r) +{ pixman_region_subtract(rgn, rgn, r.rgn); } -rfb::Region rfb::Region::intersect(const rfb::Region& r) const { - rfb::Region ret; +Region Region::intersect(const Region& r) const +{ + Region ret; pixman_region_intersect(ret.rgn, rgn, r.rgn); return ret; } -rfb::Region rfb::Region::union_(const rfb::Region& r) const { - rfb::Region ret; +Region Region::union_(const Region& r) const +{ + Region ret; pixman_region_union(ret.rgn, rgn, r.rgn); return ret; } -rfb::Region rfb::Region::subtract(const rfb::Region& r) const { - rfb::Region ret; +Region Region::subtract(const Region& r) const +{ + Region ret; pixman_region_subtract(ret.rgn, rgn, r.rgn); return ret; } -bool rfb::Region::operator==(const rfb::Region& r) const { +bool Region::operator==(const Region& r) const +{ return pixman_region_equal(rgn, r.rgn); } -bool rfb::Region::operator!=(const rfb::Region& r) const { +bool Region::operator!=(const Region& r) const +{ return !pixman_region_equal(rgn, r.rgn); } -int rfb::Region::numRects() const { +int Region::numRects() const +{ return pixman_region_n_rects(rgn); } -bool rfb::Region::get_rects(std::vector<Rect>* rects, - bool left2right, bool topdown) const +bool Region::get_rects(std::vector<Rect>* rects, + bool left2right, bool topdown) const { int nRects; const pixman_box16_t* boxes; @@ -156,14 +175,15 @@ bool rfb::Region::get_rects(std::vector<Rect>* rects, return !rects->empty(); } -rfb::Rect rfb::Region::get_bounding_rect() const { +Rect Region::get_bounding_rect() const +{ const pixman_box16_t* extents; extents = pixman_region_extents(rgn); return Rect(extents->x1, extents->y1, extents->x2, extents->y2); } -void rfb::Region::debug_print(const char* prefix) const +void Region::debug_print(const char* prefix) const { Rect extents; std::vector<Rect> rects; diff --git a/common/rfb/Region.h b/common/core/Region.h index 38de67ce..729f1475 100644 --- a/common/rfb/Region.h +++ b/common/core/Region.h @@ -19,15 +19,19 @@ // Region class wrapper around pixman's region operations -#ifndef __RFB_REGION_INCLUDED__ -#define __RFB_REGION_INCLUDED__ +#ifndef __CORE_REGION_INCLUDED__ +#define __CORE_REGION_INCLUDED__ -#include <rfb/Rect.h> #include <vector> +#include <core/Rect.h> + struct pixman_region16; -namespace rfb { +namespace core { + + struct Point; + struct Rect; class Region { public: @@ -45,7 +49,7 @@ namespace rfb { void clear(); void reset(const Rect& r); - void translate(const rfb::Point& delta); + void translate(const Point& delta); void assign_intersect(const Region& r); void assign_union(const Region& r); @@ -78,4 +82,4 @@ namespace rfb { }; -#endif // __RFB_REGION_INCLUDED__ +#endif // __CORE_REGION_INCLUDED__ diff --git a/common/rfb/Timer.cxx b/common/core/Timer.cxx index 6f7ec7ba..77e98daf 100644 --- a/common/rfb/Timer.cxx +++ b/common/core/Timer.cxx @@ -28,38 +28,16 @@ #include <algorithm> -#include <rfb/Timer.h> -#include <rfb/util.h> -#include <rfb/LogWriter.h> +#include <core/LogWriter.h> +#include <core/Timer.h> +#include <core/time.h> -using namespace rfb; +using namespace core; #ifndef __NO_DEFINE_VLOG__ static LogWriter vlog("Timer"); #endif - -// Millisecond timeout processing helper functions - -inline static timeval addMillis(timeval inTime, int millis) { - int secs = millis / 1000; - millis = millis % 1000; - inTime.tv_sec += secs; - inTime.tv_usec += millis * 1000; - if (inTime.tv_usec >= 1000000) { - inTime.tv_sec++; - inTime.tv_usec -= 1000000; - } - return inTime; -} - -inline static int diffTimeMillis(timeval later, timeval earlier) { - long udiff; - udiff = ((later.tv_sec - earlier.tv_sec) * 1000000) + - (later.tv_usec - earlier.tv_usec); - return (udiff + 999) / 1000; -} - std::list<Timer*> Timer::pending; int Timer::checkTimeouts() { @@ -167,9 +145,7 @@ int Timer::getTimeoutMs() { } int Timer::getRemainingMs() { - timeval now; - gettimeofday(&now, nullptr); - return __rfbmax(0, diffTimeMillis(dueTime, now)); + return msUntil(&dueTime); } bool Timer::isBefore(timeval other) { diff --git a/common/rfb/Timer.h b/common/core/Timer.h index 362cb84e..cde672b2 100644 --- a/common/rfb/Timer.h +++ b/common/core/Timer.h @@ -17,13 +17,13 @@ * USA. */ -#ifndef __RFB_TIMER_H__ -#define __RFB_TIMER_H__ +#ifndef __CORE_TIMER_H__ +#define __CORE_TIMER_H__ #include <list> #include <sys/time.h> -namespace rfb { +namespace core { /* Timer diff --git a/common/rfb/util.cxx b/common/core/string.cxx index 3c62b1df..091836db 100644 --- a/common/rfb/util.cxx +++ b/common/core/string.cxx @@ -26,11 +26,10 @@ #include <stdarg.h> #include <stdio.h> #include <string.h> -#include <sys/time.h> -#include <rfb/util.h> +#include <core/string.h> -namespace rfb { +namespace core { std::string format(const char *fmt, ...) { @@ -65,6 +64,9 @@ namespace rfb { std::vector<std::string> out; const char *start, *stop; + if (src[0] == '\0') + return out; + start = src; do { stop = strchr(start, delimiter); @@ -604,40 +606,6 @@ namespace rfb { return true; } - unsigned msBetween(const struct timeval *first, - const struct timeval *second) - { - unsigned diff; - - diff = (second->tv_sec - first->tv_sec) * 1000; - - diff += second->tv_usec / 1000; - diff -= first->tv_usec / 1000; - - return diff; - } - - unsigned msSince(const struct timeval *then) - { - struct timeval now; - - gettimeofday(&now, nullptr); - - return msBetween(then, &now); - } - - bool isBefore(const struct timeval *first, - const struct timeval *second) - { - if (first->tv_sec < second->tv_sec) - return true; - if (first->tv_sec > second->tv_sec) - return false; - if (first->tv_usec < second->tv_usec) - return true; - return false; - } - static std::string doPrefix(long long value, const char *unit, unsigned divisor, const char **prefixes, size_t prefixCount, int precision) { diff --git a/common/rfb/util.h b/common/core/string.h index b47ac4c9..1465484d 100644 --- a/common/rfb/util.h +++ b/common/core/string.h @@ -18,21 +18,18 @@ */ // -// util.h - miscellaneous useful bits +// string.h - string utility functions // -#ifndef __RFB_UTIL_H__ -#define __RFB_UTIL_H__ +#ifndef __CORE_STRING_H__ +#define __CORE_STRING_H__ -#include <limits.h> #include <stdint.h> #include <string> #include <vector> -struct timeval; - -namespace rfb { +namespace core { // Formats according to printf(), with a dynamic allocation std::string format(const char *fmt, ...) @@ -71,24 +68,8 @@ namespace rfb { bool isValidUTF8(const char* str, size_t bytes = (size_t)-1); bool isValidUTF16(const wchar_t* wstr, size_t units = (size_t)-1); - // HELPER functions for timeout handling - - // secsToMillis() turns seconds into milliseconds, capping the value so it - // can't wrap round and become -ve - inline int secsToMillis(int secs) { - return (secs < 0 || secs > (INT_MAX/1000) ? INT_MAX : secs * 1000); - } - - // Returns time elapsed between two moments in milliseconds. - unsigned msBetween(const struct timeval *first, - const struct timeval *second); - - // Returns time elapsed since given moment in milliseconds. - unsigned msSince(const struct timeval *then); - - // Returns true if first happened before seconds - bool isBefore(const struct timeval *first, - const struct timeval *second); + // Convert a value to a string using the correct prefix to reduce + // the length of the string std::string siPrefix(long long value, const char *unit, int precision=6); @@ -96,16 +77,4 @@ namespace rfb { int precision=6); } -// Some platforms (e.g. Windows) include max() and min() macros in their -// standard headers, but they are also standard C++ template functions, so some -// C++ headers will undefine them. So we steer clear of the names min and max -// and define __rfbmin and __rfbmax instead. - -#ifndef __rfbmax -#define __rfbmax(a,b) (((a) > (b)) ? (a) : (b)) -#endif -#ifndef __rfbmin -#define __rfbmin(a,b) (((a) < (b)) ? (a) : (b)) -#endif - #endif diff --git a/common/core/time.cxx b/common/core/time.cxx new file mode 100644 index 00000000..47a0ff8a --- /dev/null +++ b/common/core/time.cxx @@ -0,0 +1,88 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB + * + * 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 <config.h> +#endif + +#include <stddef.h> +#include <sys/time.h> + +#include <core/time.h> + +namespace core { + + unsigned msBetween(const struct timeval *first, + const struct timeval *second) + { + unsigned udiff; + + if (isBefore(second, first)) + return 0; + + udiff = (second->tv_sec - first->tv_sec) * 1000000 + + (second->tv_usec - first->tv_usec); + + return (udiff + 999) / 1000; + } + + unsigned msSince(const struct timeval *then) + { + struct timeval now; + + gettimeofday(&now, nullptr); + + return msBetween(then, &now); + } + + unsigned msUntil(const struct timeval *then) + { + struct timeval now; + + gettimeofday(&now, nullptr); + + return msBetween(&now, then); + } + + bool isBefore(const struct timeval *first, + const struct timeval *second) + { + if (first->tv_sec < second->tv_sec) + return true; + if (first->tv_sec > second->tv_sec) + return false; + if (first->tv_usec < second->tv_usec) + return true; + return false; + } + + struct timeval addMillis(struct timeval inTime, int millis) + { + int secs = millis / 1000; + millis = millis % 1000; + inTime.tv_sec += secs; + inTime.tv_usec += millis * 1000; + if (inTime.tv_usec >= 1000000) { + inTime.tv_sec++; + inTime.tv_usec -= 1000000; + } + return inTime; + } + +} diff --git a/common/core/time.h b/common/core/time.h new file mode 100644 index 00000000..319f336f --- /dev/null +++ b/common/core/time.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2019 Pierre Ossman for Cendio AB + * + * 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. + */ + +// +// time.h - time helper functions +// + +#ifndef __CORE_TIME_H__ +#define __CORE_TIME_H__ + +#include <limits.h> + +struct timeval; + +namespace core { + + // secsToMillis() turns seconds into milliseconds, capping the value so it + // can't wrap round and become -ve + inline int secsToMillis(int secs) { + return (secs < 0 || secs > (INT_MAX/1000) ? INT_MAX : secs * 1000); + } + + // Returns time elapsed between two moments in milliseconds. + unsigned msBetween(const struct timeval *first, + const struct timeval *second); + + // Returns time elapsed since given moment in milliseconds. + unsigned msSince(const struct timeval *then); + + // Returns time until the given moment in milliseconds. + unsigned msUntil(const struct timeval *then); + + // Returns true if first happened before seconds + bool isBefore(const struct timeval *first, + const struct timeval *second); + + // Returns a new timeval a specified number of milliseconds later than + // the given timeval + struct timeval addMillis(struct timeval inTime, int millis); +} + +#endif diff --git a/common/os/winerrno.h b/common/core/winerrno.h index a81fdc94..a81fdc94 100644 --- a/common/os/winerrno.h +++ b/common/core/winerrno.h diff --git a/common/os/os.cxx b/common/core/xdgdirs.cxx index 2ac70550..2628f317 100644 --- a/common/os/os.cxx +++ b/common/core/xdgdirs.cxx @@ -21,8 +21,6 @@ #include <config.h> #endif -#include <os/os.h> - #include <assert.h> #include <errno.h> #include <sys/types.h> @@ -43,6 +41,8 @@ #define mkdir(path, mode) mkdir(path) #endif +#include <core/xdgdirs.h> + static const char* getvncdir(bool userDir, const char *xdg_env, const char *xdg_def) { static char dir[PATH_MAX], legacy[PATH_MAX]; @@ -109,27 +109,27 @@ static const char* getvncdir(bool userDir, const char *xdg_env, const char *xdg_ return (stat(dir, &st) != 0 && stat(legacy, &st) == 0) ? legacy : dir; } -const char* os::getuserhomedir() +const char* core::getuserhomedir() { return getvncdir(true, nullptr, nullptr); } -const char* os::getvncconfigdir() +const char* core::getvncconfigdir() { return getvncdir(false, "XDG_CONFIG_HOME", ".config"); } -const char* os::getvncdatadir() +const char* core::getvncdatadir() { return getvncdir(false, "XDG_DATA_HOME", ".local/share"); } -const char* os::getvncstatedir() +const char* core::getvncstatedir() { return getvncdir(false, "XDG_STATE_HOME", ".local/state"); } -int os::mkdir_p(const char *path_, mode_t mode) +int core::mkdir_p(const char *path_, mode_t mode) { char *path = strdup(path_); char *p; diff --git a/common/os/os.h b/common/core/xdgdirs.h index a3448070..0769ba8b 100644 --- a/common/os/os.h +++ b/common/core/xdgdirs.h @@ -17,12 +17,12 @@ * USA. */ -#ifndef OS_OS_H -#define OS_OS_H +#ifndef CORE_XDGDIRS_H +#define CORE_XDGDIRS_H #include <sys/stat.h> -namespace os { +namespace core { /* * Get user home directory. @@ -71,4 +71,4 @@ namespace os { int mkdir_p(const char *path, mode_t mode); } -#endif /* OS_OS_H */ +#endif /* CORE_XDGDIRS_H */ diff --git a/common/network/CMakeLists.txt b/common/network/CMakeLists.txt index f08eaa31..42472b8d 100644 --- a/common/network/CMakeLists.txt +++ b/common/network/CMakeLists.txt @@ -7,7 +7,7 @@ if(NOT WIN32) endif() target_include_directories(network PUBLIC ${CMAKE_SOURCE_DIR}/common) -target_link_libraries(network os rdr rfb) +target_link_libraries(network core rdr) if(WIN32) target_link_libraries(network ws2_32) diff --git a/common/network/Socket.cxx b/common/network/Socket.cxx index 49abbc84..7fc39d1e 100644 --- a/common/network/Socket.cxx +++ b/common/network/Socket.cxx @@ -32,22 +32,23 @@ #define errorNumber errno #define closesocket close #include <sys/socket.h> -#endif - #include <unistd.h> #include <signal.h> #include <fcntl.h> #include <errno.h> +#endif -#include <rdr/Exception.h> +#include <core/Exception.h> +#include <core/LogWriter.h> -#include <network/Socket.h> +#include <rdr/FdInStream.h> +#include <rdr/FdOutStream.h> -#include <rfb/LogWriter.h> +#include <network/Socket.h> using namespace network; -static rfb::LogWriter vlog("Socket"); +static core::LogWriter vlog("Socket"); // -=- Socket initialisation static bool socketsInitialised = false; @@ -59,7 +60,7 @@ void network::initSockets() { WSADATA initResult; if (WSAStartup(requiredVersion, &initResult) != 0) - throw rdr::socket_error("Unable to initialise Winsock2", errorNumber); + throw core::socket_error("Unable to initialise Winsock2", errorNumber); #else signal(SIGPIPE, SIG_IGN); #endif @@ -99,6 +100,11 @@ Socket::~Socket() delete outstream; } +int Socket::getFd() +{ + return outstream->getFd(); +} + // if shutdown() is overridden then the override MUST call on to here void Socket::shutdown() { @@ -114,7 +120,7 @@ void Socket::shutdown() } isShutdown_ = true; - ::shutdown(getFd(), SHUT_RDWR); + ::shutdown(getFd(), SHUT_WR); } bool Socket::isShutdown() const @@ -122,6 +128,11 @@ bool Socket::isShutdown() const return isShutdown_; } +void Socket::cork(bool enable) +{ + outstream->cork(enable); +} + // Was there a "?" in the ConnectionFilter used to accept this Socket? void Socket::setRequiresQuery() { @@ -178,7 +189,7 @@ Socket* SocketListener::accept() { // Accept an incoming connection if ((new_sock = ::accept(fd, nullptr, nullptr)) < 0) - throw rdr::socket_error("Unable to accept new connection", errorNumber); + throw core::socket_error("Unable to accept new connection", errorNumber); // Create the socket object & check connection is allowed Socket* s = createSocket(new_sock); @@ -196,7 +207,7 @@ void SocketListener::listen(int sock) if (::listen(sock, 5) < 0) { int e = errorNumber; closesocket(sock); - throw rdr::socket_error("Unable to set socket to listening mode", e); + throw core::socket_error("Unable to set socket to listening mode", e); } fd = sock; diff --git a/common/network/Socket.h b/common/network/Socket.h index 34b8db8e..f1688c72 100644 --- a/common/network/Socket.h +++ b/common/network/Socket.h @@ -24,8 +24,11 @@ #include <list> #include <limits.h> -#include <rdr/FdInStream.h> -#include <rdr/FdOutStream.h> + +namespace rdr { + class FdInStream; + class FdOutStream; +} namespace network { @@ -40,12 +43,12 @@ namespace network { rdr::FdInStream &inStream() {return *instream;} rdr::FdOutStream &outStream() {return *outstream;} - int getFd() {return outstream->getFd();} + int getFd(); void shutdown(); bool isShutdown() const; - void cork(bool enable) { outstream->cork(enable); } + void cork(bool enable); // information about the remote end of the socket virtual const char* getPeerAddress() = 0; // a string e.g. "192.168.0.1" diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx index c5b86543..bf3a224c 100644 --- a/common/network/TcpSocket.cxx +++ b/common/network/TcpSocket.cxx @@ -35,19 +35,21 @@ #include <errno.h> #endif +#include <assert.h> +#include <ctype.h> #include <stdlib.h> +#include <string.h> #include <unistd.h> -#include <rdr/Exception.h> +#include <core/Configuration.h> +#include <core/Exception.h> +#include <core/LogWriter.h> +#include <core/string.h> #include <network/TcpSocket.h> -#include <rfb/LogWriter.h> -#include <rfb/Configuration.h> -#include <rfb/util.h> - #ifdef WIN32 -#include <os/winerrno.h> +#include <core/winerrno.h> #endif #ifndef INADDR_NONE @@ -68,12 +70,11 @@ #endif using namespace network; -using namespace rdr; -static rfb::LogWriter vlog("TcpSocket"); +static core::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); +static core::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true); +static core::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true); /* Tunnelling support. */ int network::findFreeTcpPort (void) @@ -85,20 +86,105 @@ int network::findFreeTcpPort (void) addr.sin_addr.s_addr = INADDR_ANY; if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) - throw socket_error("Unable to create socket", errorNumber); + throw core::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); + throw core::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); + throw core::socket_error("Unable to get port number", errorNumber); closesocket (sock); return ntohs(addr.sin_port); } +static bool isAllSpace(const char *string) { + if (string == nullptr) + return false; + while(*string != '\0') { + if (! isspace(*string)) + return false; + string++; + } + return true; +} + +void network::getHostAndPort(const char* hi, std::string* host, + int* port, int basePort) +{ + const char* hostStart; + const char* hostEnd; + const char* portStart; + + if (hi == nullptr) + throw std::invalid_argument("NULL host specified"); + + // Trim leading whitespace + while(isspace(*hi)) + hi++; + + assert(host); + assert(port); + + if (hi[0] == '[') { + hostStart = &hi[1]; + hostEnd = strchr(hostStart, ']'); + if (hostEnd == nullptr) + throw std::invalid_argument("Unmatched [ in host"); + + portStart = hostEnd + 1; + if (isAllSpace(portStart)) + portStart = nullptr; + } else { + hostStart = &hi[0]; + hostEnd = strrchr(hostStart, ':'); + + if (hostEnd == nullptr) { + hostEnd = hostStart + strlen(hostStart); + portStart = nullptr; + } else { + if ((hostEnd > hostStart) && (hostEnd[-1] == ':')) + hostEnd--; + portStart = strchr(hostStart, ':'); + if (portStart != hostEnd) { + // We found more : in the host. This is probably an IPv6 address + hostEnd = hostStart + strlen(hostStart); + portStart = nullptr; + } + } + } + + // Back up past trailing space + while(isspace(*(hostEnd - 1)) && hostEnd > hostStart) + hostEnd--; + + if (hostStart == hostEnd) + *host = "localhost"; + else + *host = std::string(hostStart, hostEnd - hostStart); + + if (portStart == nullptr) + *port = basePort; + else { + char* end; + + if (portStart[0] != ':') + throw std::invalid_argument("Invalid port specified"); + + if (portStart[1] != ':') + *port = strtol(portStart + 1, &end, 10); + else + *port = strtol(portStart + 2, &end, 10); + if (*end != '\0' && ! isAllSpace(end)) + throw std::invalid_argument("Invalid port specified"); + + if ((portStart[1] != ':') && (*port < 100)) + *port += basePort; + } +} + int network::getSockPort(int sock) { vnc_sockaddr_t sa; @@ -137,7 +223,7 @@ TcpSocket::TcpSocket(const char *host, int port) hints.ai_next = nullptr; if ((result = getaddrinfo(host, nullptr, &hints, &ai)) != 0) { - throw getaddrinfo_error("Unable to resolve host by name", result); + throw core::getaddrinfo_error("Unable to resolve host by name", result); } sock = -1; @@ -178,7 +264,7 @@ TcpSocket::TcpSocket(const char *host, int port) if (sock == -1) { err = errorNumber; freeaddrinfo(ai); - throw socket_error("Unable to create socket", err); + throw core::socket_error("Unable to create socket", err); } /* Attempt to connect to the remote host */ @@ -205,7 +291,7 @@ TcpSocket::TcpSocket(const char *host, int port) if (err == 0) throw std::runtime_error("No useful address for host"); else - throw socket_error("Unable to connect to socket", err); + throw core::socket_error("Unable to connect to socket", err); } // Take proper ownership of the socket @@ -302,7 +388,7 @@ TcpListener::TcpListener(const struct sockaddr *listenaddr, int sock; if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) - throw socket_error("Unable to create listening socket", errorNumber); + throw core::socket_error("Unable to create listening socket", errorNumber); memcpy (&sa, listenaddr, listenaddrlen); #ifdef IPV6_V6ONLY @@ -310,7 +396,7 @@ TcpListener::TcpListener(const struct sockaddr *listenaddr, 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); + throw core::socket_error("Unable to set IPV6_V6ONLY", e); } } #endif /* defined(IPV6_V6ONLY) */ @@ -328,14 +414,14 @@ TcpListener::TcpListener(const struct sockaddr *listenaddr, (char *)&one, sizeof(one)) < 0) { int e = errorNumber; closesocket(sock); - throw socket_error("Unable to create listening socket", e); + throw core::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); + throw core::socket_error("Failed to bind socket", e); } listen(sock); @@ -446,7 +532,7 @@ void network::createTcpListeners(std::list<SocketListener*> *listeners, 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); + throw core::getaddrinfo_error("Unable to resolve listening address", result); try { createTcpListeners(listeners, ai); @@ -485,7 +571,7 @@ void network::createTcpListeners(std::list<SocketListener*> *listeners, try { new_listeners.push_back(new TcpListener(current->ai_addr, current->ai_addrlen)); - } catch (socket_error& e) { + } catch (core::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) { @@ -506,7 +592,7 @@ void network::createTcpListeners(std::list<SocketListener*> *listeners, TcpFilter::TcpFilter(const char* spec) { std::vector<std::string> patterns; - patterns = rfb::split(spec, ','); + patterns = core::split(spec, ','); for (size_t i = 0; i < patterns.size(); i++) { if (!patterns[i].empty()) @@ -608,11 +694,11 @@ TcpFilter::Pattern TcpFilter::parsePattern(const char* p) { initSockets(); - parts = rfb::split(&p[1], '/'); + parts = core::split(&p[1], '/'); if (parts.size() > 2) throw std::invalid_argument("Invalid filter specified"); - if (parts[0].empty()) { + if (parts.empty() || parts[0].empty()) { // Match any address memset (&pattern.address, 0, sizeof (pattern.address)); pattern.address.u.sa.sa_family = AF_UNSPEC; @@ -633,7 +719,7 @@ TcpFilter::Pattern TcpFilter::parsePattern(const char* p) { } if ((result = getaddrinfo (parts[0].c_str(), nullptr, &hints, &ai)) != 0) { - throw getaddrinfo_error("Unable to resolve host by name", result); + throw core::getaddrinfo_error("Unable to resolve host by name", result); } memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen); @@ -666,9 +752,9 @@ TcpFilter::Pattern TcpFilter::parsePattern(const char* p) { 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)); + throw std::invalid_argument( + core::format("Invalid prefix length for filter address: %u", + pattern.prefixlen)); // Compute mask from address and prefix length memset (&pattern.mask, 0, sizeof (pattern.mask)); diff --git a/common/network/TcpSocket.h b/common/network/TcpSocket.h index b029bff2..17854e10 100644 --- a/common/network/TcpSocket.h +++ b/common/network/TcpSocket.h @@ -49,6 +49,9 @@ namespace network { /* Tunnelling support. */ int findFreeTcpPort (void); + void getHostAndPort(const char* hi, std::string* host, + int* port, int basePort=5900); + int getSockPort(int sock); class TcpSocket : public Socket { diff --git a/common/network/UnixSocket.cxx b/common/network/UnixSocket.cxx index 48561245..9691cb23 100644 --- a/common/network/UnixSocket.cxx +++ b/common/network/UnixSocket.cxx @@ -28,17 +28,16 @@ #include <errno.h> #include <stdlib.h> #include <stddef.h> +#include <string.h> -#include <rdr/Exception.h> +#include <core/Exception.h> +#include <core/LogWriter.h> #include <network/UnixSocket.h> -#include <rfb/LogWriter.h> - using namespace network; -using namespace rdr; -static rfb::LogWriter vlog("UnixSocket"); +static core::LogWriter vlog("UnixSocket"); // -=- UnixSocket @@ -53,12 +52,12 @@ UnixSocket::UnixSocket(const char *path) socklen_t salen; if (strlen(path) >= sizeof(addr.sun_path)) - throw socket_error("Socket path is too long", ENAMETOOLONG); + throw core::socket_error("Socket path is too long", ENAMETOOLONG); // - Create a socket sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock == -1) - throw socket_error("Unable to create socket", errno); + throw core::socket_error("Unable to create socket", errno); // - Attempt to connect memset(&addr, 0, sizeof(addr)); @@ -72,7 +71,7 @@ UnixSocket::UnixSocket(const char *path) } if (result == -1) - throw socket_error("Unable to connect to socket", err); + throw core::socket_error("Unable to connect to socket", err); setFd(sock); } @@ -119,11 +118,11 @@ UnixListener::UnixListener(const char *path, int mode) int err, result; if (strlen(path) >= sizeof(addr.sun_path)) - throw socket_error("Socket path is too long", ENAMETOOLONG); + throw core::socket_error("Socket path is too long", ENAMETOOLONG); // - Create a socket if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) - throw socket_error("Unable to create listening socket", errno); + throw core::socket_error("Unable to create listening socket", errno); // - Delete existing socket (ignore result) unlink(path); @@ -138,14 +137,14 @@ UnixListener::UnixListener(const char *path, int mode) umask(saved_umask); if (result < 0) { close(fd); - throw socket_error("Unable to bind listening socket", err); + throw core::socket_error("Unable to bind listening socket", err); } // - Set socket mode if (chmod(path, mode) < 0) { err = errno; close(fd); - throw socket_error("Unable to set socket mode", err); + throw core::socket_error("Unable to set socket mode", err); } listen(fd); diff --git a/common/os/CMakeLists.txt b/common/os/CMakeLists.txt deleted file mode 100644 index 2573d088..00000000 --- a/common/os/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -add_library(os STATIC - Mutex.cxx - Thread.cxx - os.cxx) - -target_include_directories(os PUBLIC ${CMAKE_SOURCE_DIR}/common) -target_link_libraries(os rdr) - -if(UNIX) - target_link_libraries(os pthread) -endif() - -if(UNIX) - libtool_create_control_file(os) -endif() diff --git a/common/os/Mutex.cxx b/common/os/Mutex.cxx deleted file mode 100644 index 1889e66b..00000000 --- a/common/os/Mutex.cxx +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright 2015 Pierre Ossman for Cendio AB - * - * 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 <config.h> -#endif - -#ifdef WIN32 -#include <windows.h> -#else -#include <pthread.h> -#endif - -#include <rdr/Exception.h> - -#include <os/Mutex.h> - -using namespace os; - -Mutex::Mutex() -{ -#ifdef WIN32 - systemMutex = new CRITICAL_SECTION; - InitializeCriticalSection((CRITICAL_SECTION*)systemMutex); -#else - int ret; - - systemMutex = new pthread_mutex_t; - ret = pthread_mutex_init((pthread_mutex_t*)systemMutex, nullptr); - if (ret != 0) - throw rdr::posix_error("Failed to create mutex", ret); -#endif -} - -Mutex::~Mutex() -{ -#ifdef WIN32 - DeleteCriticalSection((CRITICAL_SECTION*)systemMutex); - delete (CRITICAL_SECTION*)systemMutex; -#else - pthread_mutex_destroy((pthread_mutex_t*)systemMutex); - delete (pthread_mutex_t*)systemMutex; -#endif -} - -void Mutex::lock() -{ -#ifdef WIN32 - EnterCriticalSection((CRITICAL_SECTION*)systemMutex); -#else - int ret; - - ret = pthread_mutex_lock((pthread_mutex_t*)systemMutex); - if (ret != 0) - throw rdr::posix_error("Failed to lock mutex", ret); -#endif -} - -void Mutex::unlock() -{ -#ifdef WIN32 - LeaveCriticalSection((CRITICAL_SECTION*)systemMutex); -#else - int ret; - - ret = pthread_mutex_unlock((pthread_mutex_t*)systemMutex); - if (ret != 0) - throw rdr::posix_error("Failed to unlock mutex", ret); -#endif -} - -Condition::Condition(Mutex* mutex_) -{ - this->mutex = mutex_; - -#ifdef WIN32 - systemCondition = new CONDITION_VARIABLE; - InitializeConditionVariable((CONDITION_VARIABLE*)systemCondition); -#else - int ret; - - systemCondition = new pthread_cond_t; - ret = pthread_cond_init((pthread_cond_t*)systemCondition, nullptr); - if (ret != 0) - throw rdr::posix_error("Failed to create condition variable", ret); -#endif -} - -Condition::~Condition() -{ -#ifdef WIN32 - delete (CONDITION_VARIABLE*)systemCondition; -#else - pthread_cond_destroy((pthread_cond_t*)systemCondition); - delete (pthread_cond_t*)systemCondition; -#endif -} - -void Condition::wait() -{ -#ifdef WIN32 - BOOL ret; - - ret = SleepConditionVariableCS((CONDITION_VARIABLE*)systemCondition, - (CRITICAL_SECTION*)mutex->systemMutex, - INFINITE); - if (!ret) - throw rdr::win32_error("Failed to wait on condition variable", GetLastError()); -#else - int ret; - - ret = pthread_cond_wait((pthread_cond_t*)systemCondition, - (pthread_mutex_t*)mutex->systemMutex); - if (ret != 0) - throw rdr::posix_error("Failed to wait on condition variable", ret); -#endif -} - -void Condition::signal() -{ -#ifdef WIN32 - WakeConditionVariable((CONDITION_VARIABLE*)systemCondition); -#else - int ret; - - ret = pthread_cond_signal((pthread_cond_t*)systemCondition); - if (ret != 0) - throw rdr::posix_error("Failed to signal condition variable", ret); -#endif -} - -void Condition::broadcast() -{ -#ifdef WIN32 - WakeAllConditionVariable((CONDITION_VARIABLE*)systemCondition); -#else - int ret; - - ret = pthread_cond_broadcast((pthread_cond_t*)systemCondition); - if (ret != 0) - throw rdr::posix_error("Failed to broadcast condition variable", ret); -#endif -} diff --git a/common/os/Mutex.h b/common/os/Mutex.h deleted file mode 100644 index 63c7e0cc..00000000 --- a/common/os/Mutex.h +++ /dev/null @@ -1,64 +0,0 @@ -/* Copyright 2015 Pierre Ossman for Cendio AB - * - * 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. - */ - -#ifndef __OS_MUTEX_H__ -#define __OS_MUTEX_H__ - -namespace os { - class Condition; - - class Mutex { - public: - Mutex(); - ~Mutex(); - - void lock(); - void unlock(); - - private: - friend class Condition; - - void* systemMutex; - }; - - class AutoMutex { - public: - AutoMutex(Mutex* mutex) { m = mutex; m->lock(); } - ~AutoMutex() { m->unlock(); } - private: - Mutex* m; - }; - - class Condition { - public: - Condition(Mutex* mutex); - ~Condition(); - - void wait(); - - void signal(); - void broadcast(); - - private: - Mutex* mutex; - void* systemCondition; - }; - -} - -#endif diff --git a/common/os/Thread.cxx b/common/os/Thread.cxx deleted file mode 100644 index 6dca75a1..00000000 --- a/common/os/Thread.cxx +++ /dev/null @@ -1,173 +0,0 @@ -/* Copyright 2015 Pierre Ossman for Cendio AB - * - * 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 <config.h> -#endif - -#ifdef WIN32 -#include <windows.h> -#else -#include <pthread.h> -#include <signal.h> -#include <unistd.h> -#endif - -#include <rdr/Exception.h> - -#include <os/Mutex.h> -#include <os/Thread.h> - -using namespace os; - -Thread::Thread() : running(false), threadId(nullptr) -{ - mutex = new Mutex; - -#ifdef WIN32 - threadId = new HANDLE; -#else - threadId = new pthread_t; -#endif -} - -Thread::~Thread() -{ -#ifdef WIN32 - delete (HANDLE*)threadId; -#else - if (isRunning()) - pthread_cancel(*(pthread_t*)threadId); - delete (pthread_t*)threadId; -#endif - - delete mutex; -} - -void Thread::start() -{ - AutoMutex a(mutex); - -#ifdef WIN32 - *(HANDLE*)threadId = CreateThread(nullptr, 0, startRoutine, this, 0, nullptr); - if (*(HANDLE*)threadId == nullptr) - throw rdr::win32_error("Failed to create thread", GetLastError()); -#else - int ret; - sigset_t all, old; - - // Creating threads from libraries is a bit evil, so mitigate the - // issue by at least avoiding signals on these threads - sigfillset(&all); - ret = pthread_sigmask(SIG_SETMASK, &all, &old); - if (ret != 0) - throw rdr::posix_error("Failed to mask signals", ret); - - ret = pthread_create((pthread_t*)threadId, nullptr, startRoutine, this); - - pthread_sigmask(SIG_SETMASK, &old, nullptr); - - if (ret != 0) - throw rdr::posix_error("Failed to create thread", ret); -#endif - - running = true; -} - -void Thread::wait() -{ - if (!isRunning()) - return; - -#ifdef WIN32 - DWORD ret; - - ret = WaitForSingleObject(*(HANDLE*)threadId, INFINITE); - if (ret != WAIT_OBJECT_0) - throw rdr::win32_error("Failed to join thread", GetLastError()); -#else - int ret; - - ret = pthread_join(*(pthread_t*)threadId, nullptr); - if (ret != 0) - throw rdr::posix_error("Failed to join thread", ret); -#endif -} - -bool Thread::isRunning() -{ - AutoMutex a(mutex); - - return running; -} - -size_t Thread::getSystemCPUCount() -{ -#ifdef WIN32 - SYSTEM_INFO si; - size_t count; - DWORD mask; - - GetSystemInfo(&si); - - count = 0; - for (mask = si.dwActiveProcessorMask;mask != 0;mask >>= 1) { - if (mask & 0x1) - count++; - } - - if (count > si.dwNumberOfProcessors) - count = si.dwNumberOfProcessors; - - return count; -#else - long ret; - - ret = sysconf(_SC_NPROCESSORS_ONLN); - if (ret == -1) - return 0; - - return ret; -#endif -} - -#ifdef WIN32 -long unsigned __stdcall Thread::startRoutine(void* data) -#else -void* Thread::startRoutine(void* data) -#endif -{ - Thread *self; - - self = (Thread*)data; - - try { - self->worker(); - } catch(...) { - } - - self->mutex->lock(); - self->running = false; - self->mutex->unlock(); - -#ifdef WIN32 - return 0; -#else - return nullptr; -#endif -} diff --git a/common/os/Thread.h b/common/os/Thread.h deleted file mode 100644 index 4c39884b..00000000 --- a/common/os/Thread.h +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright 2015 Pierre Ossman for Cendio AB - * - * 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. - */ - -#ifndef __OS_THREAD_H__ -#define __OS_THREAD_H__ - -#include <stddef.h> - -namespace os { - class Mutex; - - class Thread { - public: - Thread(); - virtual ~Thread(); - - void start(); - void wait(); - - bool isRunning(); - - public: - static size_t getSystemCPUCount(); - - protected: - virtual void worker() = 0; - - private: -#ifdef WIN32 - static long unsigned __stdcall startRoutine(void* data); -#else - static void* startRoutine(void* data); -#endif - - private: - Mutex *mutex; - bool running; - - void *threadId; - }; -} - -#endif diff --git a/common/rdr/BufferedInStream.cxx b/common/rdr/BufferedInStream.cxx index bf94a950..bcdeef4a 100644 --- a/common/rdr/BufferedInStream.cxx +++ b/common/rdr/BufferedInStream.cxx @@ -23,9 +23,9 @@ #include <assert.h> -#include <rdr/BufferedInStream.h> +#include <core/string.h> -#include <rfb/util.h> +#include <rdr/BufferedInStream.h> using namespace rdr; @@ -64,12 +64,10 @@ void BufferedInStream::ensureSpace(size_t needed) uint8_t* newBuffer; if (needed > MAX_BUF_SIZE) - throw std::out_of_range(rfb::format("BufferedInStream overrun: " - "requested size of %lu bytes " - "exceeds maximum of %lu " - "bytes", - (long unsigned)needed, - (long unsigned)MAX_BUF_SIZE)); + throw std::out_of_range(core::format( + "BufferedInStream overrun: requested size of %lu bytes exceeds " + "maximum of %lu bytes", + (long unsigned)needed, (long unsigned)MAX_BUF_SIZE)); newSize = DEFAULT_BUF_SIZE; while (newSize < needed) diff --git a/common/rdr/BufferedOutStream.cxx b/common/rdr/BufferedOutStream.cxx index efb71dd7..e2b756bb 100644 --- a/common/rdr/BufferedOutStream.cxx +++ b/common/rdr/BufferedOutStream.cxx @@ -22,9 +22,9 @@ #include <config.h> #endif -#include <rdr/BufferedOutStream.h> +#include <core/string.h> -#include <rfb/util.h> +#include <rdr/BufferedOutStream.h> using namespace rdr; @@ -138,11 +138,10 @@ void BufferedOutStream::overrun(size_t needed) // We'll need to allocate more buffer space... if (totalNeeded > MAX_BUF_SIZE) - throw std::out_of_range(rfb::format("BufferedOutStream overrun: " - "requested size of %lu bytes " - "exceeds maximum of %lu bytes", - (long unsigned)totalNeeded, - (long unsigned)MAX_BUF_SIZE)); + throw std::out_of_range(core::format( + "BufferedOutStream overrun: requested size of %lu bytes exceeds " + "maximum of %lu bytes", + (long unsigned)totalNeeded, (long unsigned)MAX_BUF_SIZE)); newSize = DEFAULT_BUF_SIZE; while (newSize < totalNeeded) diff --git a/common/rdr/CMakeLists.txt b/common/rdr/CMakeLists.txt index 2897119b..526b2971 100644 --- a/common/rdr/CMakeLists.txt +++ b/common/rdr/CMakeLists.txt @@ -3,7 +3,6 @@ add_library(rdr STATIC AESOutStream.cxx BufferedInStream.cxx BufferedOutStream.cxx - Exception.cxx FdInStream.cxx FdOutStream.cxx FileInStream.cxx @@ -13,17 +12,14 @@ add_library(rdr STATIC TLSException.cxx TLSInStream.cxx TLSOutStream.cxx + TLSSocket.cxx ZlibInStream.cxx ZlibOutStream.cxx) target_include_directories(rdr PUBLIC ${CMAKE_SOURCE_DIR}/common) target_include_directories(rdr SYSTEM PUBLIC ${ZLIB_INCLUDE_DIRS}) -target_link_libraries(rdr ${ZLIB_LIBRARIES} os rfb) - -if(MSVC) - # undef min and max macro - target_compile_definitions(rfb PRIVATE NOMINMAX) -endif() +target_link_libraries(rdr core) +target_link_libraries(rdr ${ZLIB_LIBRARIES}) if(GNUTLS_FOUND) target_include_directories(rdr SYSTEM PUBLIC ${GNUTLS_INCLUDE_DIR}) @@ -32,7 +28,6 @@ endif() if (NETTLE_FOUND) target_include_directories(rdr SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS}) target_link_libraries(rdr ${NETTLE_LIBRARIES}) - target_link_directories(rdr PUBLIC ${NETTLE_LIBRARY_DIRS}) endif() if(WIN32) target_link_libraries(rdr ws2_32) diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx index 23ea2f8c..25542a01 100644 --- a/common/rdr/FdInStream.cxx +++ b/common/rdr/FdInStream.cxx @@ -28,7 +28,7 @@ #include <winsock2.h> #define errorNumber WSAGetLastError() #define close closesocket -#include <os/winerrno.h> +#include <core/winerrno.h> #else #include <sys/types.h> #include <sys/socket.h> @@ -41,8 +41,9 @@ #include <sys/select.h> #endif +#include <core/Exception.h> + #include <rdr/FdInStream.h> -#include <rdr/Exception.h> using namespace rdr; @@ -92,7 +93,7 @@ size_t FdInStream::readFd(uint8_t* buf, size_t len) } while (n < 0 && errorNumber == EINTR); if (n < 0) - throw socket_error("select", errorNumber); + throw core::socket_error("select", errorNumber); if (n == 0) return 0; @@ -102,7 +103,7 @@ size_t FdInStream::readFd(uint8_t* buf, size_t len) } while (n < 0 && errorNumber == EINTR); if (n < 0) - throw socket_error("read", errorNumber); + throw core::socket_error("read", errorNumber); if (n == 0) throw end_of_stream(); diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx index 6db8c0bb..416926c1 100644 --- a/common/rdr/FdOutStream.cxx +++ b/common/rdr/FdOutStream.cxx @@ -28,7 +28,7 @@ #ifdef _WIN32 #include <winsock2.h> #define errorNumber WSAGetLastError() -#include <os/winerrno.h> +#include <core/winerrno.h> #else #include <sys/types.h> #include <unistd.h> @@ -44,10 +44,10 @@ #include <sys/select.h> #endif -#include <rdr/FdOutStream.h> -#include <rdr/Exception.h> -#include <rfb/util.h> +#include <core/Exception.h> +#include <core/time.h> +#include <rdr/FdOutStream.h> using namespace rdr; @@ -68,7 +68,7 @@ FdOutStream::~FdOutStream() unsigned FdOutStream::getIdleTime() { - return rfb::msSince(&lastWrite); + return core::msSince(&lastWrite); } void FdOutStream::cork(bool enable) @@ -117,7 +117,7 @@ size_t FdOutStream::writeFd(const uint8_t* data, size_t length) } while (n < 0 && errorNumber == EINTR); if (n < 0) - throw socket_error("select", errorNumber); + throw core::socket_error("select", errorNumber); if (n == 0) return 0; @@ -134,7 +134,7 @@ size_t FdOutStream::writeFd(const uint8_t* data, size_t length) } while (n < 0 && (errorNumber == EINTR)); if (n < 0) - throw socket_error("write", errorNumber); + throw core::socket_error("write", errorNumber); gettimeofday(&lastWrite, nullptr); diff --git a/common/rdr/FileInStream.cxx b/common/rdr/FileInStream.cxx index df09ea76..4dbe2d1f 100644 --- a/common/rdr/FileInStream.cxx +++ b/common/rdr/FileInStream.cxx @@ -24,7 +24,8 @@ #include <errno.h> -#include <rdr/Exception.h> +#include <core/Exception.h> + #include <rdr/FileInStream.h> using namespace rdr; @@ -33,7 +34,7 @@ FileInStream::FileInStream(const char *fileName) { file = fopen(fileName, "rb"); if (!file) - throw posix_error("fopen", errno); + throw core::posix_error("fopen", errno); } FileInStream::~FileInStream(void) { @@ -48,7 +49,7 @@ bool FileInStream::fillBuffer() size_t n = fread((uint8_t*)end, 1, availSpace(), file); if (n == 0) { if (ferror(file)) - throw posix_error("fread", errno); + throw core::posix_error("fread", errno); if (feof(file)) throw end_of_stream(); return false; diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx index 69c3e260..b5a8826c 100644 --- a/common/rdr/HexInStream.cxx +++ b/common/rdr/HexInStream.cxx @@ -22,8 +22,10 @@ #endif #include <algorithm> + +#include <core/string.h> + #include <rdr/HexInStream.h> -#include <rfb/util.h> using namespace rdr; @@ -44,7 +46,7 @@ bool HexInStream::fillBuffer() { uint8_t* optr = (uint8_t*) end; for (size_t i=0; i<length; i++) { - if (!rfb::hexToBin((const char*)&iptr[i*2], 2, &optr[i], 1)) + if (!core::hexToBin((const char*)&iptr[i*2], 2, &optr[i], 1)) throw std::runtime_error("HexInStream: Invalid input data"); } diff --git a/common/rdr/HexOutStream.cxx b/common/rdr/HexOutStream.cxx index efab77f8..b3749c0d 100644 --- a/common/rdr/HexOutStream.cxx +++ b/common/rdr/HexOutStream.cxx @@ -20,9 +20,12 @@ #ifdef HAVE_CONFIG_H #include <config.h> #endif + #include <algorithm> + +#include <core/string.h> + #include <rdr/HexOutStream.h> -#include <rfb/util.h> using namespace rdr; @@ -42,7 +45,7 @@ bool HexOutStream::flushBuffer() size_t length = std::min((size_t)(ptr-sentUpTo), out_stream.avail()/2); for (size_t i=0; i<length; i++) - rfb::binToHex(&sentUpTo[i], 1, (char*)&optr[i*2], 2); + core::binToHex(&sentUpTo[i], 1, (char*)&optr[i*2], 2); out_stream.setptr(length*2); sentUpTo += length; diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h index 5623142c..7ad4996f 100644 --- a/common/rdr/InStream.h +++ b/common/rdr/InStream.h @@ -38,6 +38,11 @@ namespace rdr { + class end_of_stream : public std::runtime_error { + public: + end_of_stream() noexcept : std::runtime_error("End of stream") {} + }; + class InStream { public: @@ -182,9 +187,7 @@ namespace rdr { private: const uint8_t* restorePoint; -#ifdef RFB_INSTREAM_CHECK size_t checkedBytes; -#endif inline void check(size_t bytes) { #ifdef RFB_INSTREAM_CHECK @@ -204,11 +207,7 @@ namespace rdr { protected: - InStream() : restorePoint(nullptr) -#ifdef RFB_INSTREAM_CHECK - ,checkedBytes(0) -#endif - {} + InStream() : restorePoint(nullptr), checkedBytes(0) {} const uint8_t* ptr; const uint8_t* end; }; diff --git a/common/rdr/MemInStream.h b/common/rdr/MemInStream.h index 78ee2dee..a92e18f9 100644 --- a/common/rdr/MemInStream.h +++ b/common/rdr/MemInStream.h @@ -28,7 +28,6 @@ #define __RDR_MEMINSTREAM_H__ #include <rdr/InStream.h> -#include <rdr/Exception.h> namespace rdr { diff --git a/common/rdr/RandomStream.cxx b/common/rdr/RandomStream.cxx index 3a524102..9784c220 100644 --- a/common/rdr/RandomStream.cxx +++ b/common/rdr/RandomStream.cxx @@ -20,9 +20,11 @@ #include <config.h> #endif +#include <core/Exception.h> +#include <core/LogWriter.h> + #include <rdr/RandomStream.h> -#include <rdr/Exception.h> -#include <rfb/LogWriter.h> + #include <time.h> #include <stdlib.h> #ifndef WIN32 @@ -35,7 +37,7 @@ #endif #endif -static rfb::LogWriter vlog("RandomStream"); +static core::LogWriter vlog("RandomStream"); using namespace rdr; @@ -89,7 +91,7 @@ bool RandomStream::fillBuffer() { #ifdef RFB_HAVE_WINCRYPT if (provider) { if (!CryptGenRandom(provider, availSpace(), (uint8_t*)end)) - throw rdr::win32_error("Unable to CryptGenRandom", GetLastError()); + throw core::win32_error("Unable to CryptGenRandom", GetLastError()); end += availSpace(); } else { #else @@ -97,8 +99,8 @@ bool RandomStream::fillBuffer() { if (fp) { size_t n = fread((uint8_t*)end, 1, availSpace(), fp); if (n <= 0) - throw rdr::posix_error("Reading /dev/urandom or /dev/random " - "failed", errno); + throw core::posix_error( + "Reading /dev/urandom or /dev/random failed", errno); end += n; } else { #else diff --git a/common/rdr/TLSException.cxx b/common/rdr/TLSException.cxx index ee4f587b..8c93a3d3 100644 --- a/common/rdr/TLSException.cxx +++ b/common/rdr/TLSException.cxx @@ -22,9 +22,9 @@ #include <config.h> #endif -#include <rdr/TLSException.h> +#include <core/string.h> -#include <rfb/util.h> +#include <rdr/TLSException.h> #include <string.h> #include <stdio.h> @@ -35,11 +35,28 @@ using namespace rdr; #ifdef HAVE_GNUTLS -tls_error::tls_error(const char* s, int err_) noexcept - : std::runtime_error(rfb::format("%s: %s (%d)", s, - gnutls_strerror(err_), err_)), - err(err_) +tls_error::tls_error(const char* s, int err_, int alert_) noexcept + : std::runtime_error(core::format("%s: %s (%d)", s, + strerror(err_, alert_), err_)), + err(err_), alert(alert_) +{ +} + +const char* tls_error::strerror(int err_, int alert_) const noexcept { + const char* msg; + + msg = nullptr; + + if ((alert_ != -1) && + ((err_ == GNUTLS_E_WARNING_ALERT_RECEIVED) || + (err_ == GNUTLS_E_FATAL_ALERT_RECEIVED))) + msg = gnutls_alert_get_name((gnutls_alert_description_t)alert_); + + if (msg == nullptr) + msg = gnutls_strerror(err_); + + return msg; } #endif /* HAVE_GNUTLS */ diff --git a/common/rdr/TLSException.h b/common/rdr/TLSException.h index 62b090ba..75ee94f5 100644 --- a/common/rdr/TLSException.h +++ b/common/rdr/TLSException.h @@ -21,14 +21,16 @@ #ifndef __RDR_TLSEXCEPTION_H__ #define __RDR_TLSEXCEPTION_H__ -#include <rdr/Exception.h> +#include <stdexcept> namespace rdr { class tls_error : public std::runtime_error { public: - int err; - tls_error(const char* s, int err_) noexcept; + int err, alert; + tls_error(const char* s, int err_, int alert_=-1) noexcept; + private: + const char* strerror(int err_, int alert_) const noexcept; }; } diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx index ee2739f4..3e5ea2be 100644 --- a/common/rdr/TLSInStream.cxx +++ b/common/rdr/TLSInStream.cxx @@ -1,7 +1,7 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team - * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,74 +23,25 @@ #include <config.h> #endif -#include <rdr/Exception.h> -#include <rdr/TLSException.h> #include <rdr/TLSInStream.h> -#include <rfb/LogWriter.h> -#include <errno.h> +#include <rdr/TLSSocket.h> -#ifdef HAVE_GNUTLS -using namespace rdr; - -static rfb::LogWriter vlog("TLSInStream"); - -ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) -{ - TLSInStream* self= (TLSInStream*) str; - InStream *in = self->in; - - self->streamEmpty = false; - delete self->saved_exception; - self->saved_exception = nullptr; +#ifdef HAVE_GNUTLS - try { - if (!in->hasData(1)) { - self->streamEmpty = true; - gnutls_transport_set_errno(self->session, EAGAIN); - return -1; - } - - if (in->avail() < size) - size = in->avail(); - - in->readBytes((uint8_t*)data, size); - } catch (end_of_stream&) { - return 0; - } catch (socket_error& e) { - vlog.error("Failure reading TLS data: %s", e.what()); - gnutls_transport_set_errno(self->session, e.err); - self->saved_exception = new socket_error(e); - return -1; - } catch (std::exception& e) { - vlog.error("Failure reading TLS data: %s", e.what()); - gnutls_transport_set_errno(self->session, EINVAL); - self->saved_exception = new std::runtime_error(e.what()); - return -1; - } - - return size; -} +using namespace rdr; -TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session) - : session(_session), in(_in), saved_exception(nullptr) +TLSInStream::TLSInStream(TLSSocket* sock_) + : sock(sock_) { - gnutls_transport_ptr_t recv, send; - - gnutls_transport_set_pull_function(session, pull); - gnutls_transport_get_ptr2(session, &recv, &send); - gnutls_transport_set_ptr2(session, this, send); } TLSInStream::~TLSInStream() { - gnutls_transport_set_pull_function(session, nullptr); - - delete saved_exception; } bool TLSInStream::fillBuffer() { - size_t n = readTLS((uint8_t*) end, availSpace()); + size_t n = sock->readTLS((uint8_t*) end, availSpace()); if (n == 0) return false; end += n; @@ -98,39 +49,4 @@ bool TLSInStream::fillBuffer() return true; } -size_t TLSInStream::readTLS(uint8_t* buf, size_t len) -{ - int n; - - while (true) { - streamEmpty = false; - n = gnutls_record_recv(session, (void *) buf, len); - if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) { - // GnuTLS returns GNUTLS_E_AGAIN for a bunch of other scenarios - // other than the pull function returning EAGAIN, so we have to - // double check that the underlying stream really is empty - if (!streamEmpty) - continue; - else - return 0; - } - break; - }; - - if (n == GNUTLS_E_PULL_ERROR) { - if (dynamic_cast<socket_error*>(saved_exception)) - throw *dynamic_cast<socket_error*>(saved_exception); - else - throw std::runtime_error(saved_exception->what()); - } - - if (n < 0) - throw tls_error("readTLS", n); - - if (n == 0) - throw end_of_stream(); - - return n; -} - #endif diff --git a/common/rdr/TLSInStream.h b/common/rdr/TLSInStream.h index 2269b09d..94266e50 100644 --- a/common/rdr/TLSInStream.h +++ b/common/rdr/TLSInStream.h @@ -1,5 +1,6 @@ /* Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,28 +23,25 @@ #ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> #include <rdr/BufferedInStream.h> namespace rdr { + class TLSSocket; + class TLSInStream : public BufferedInStream { public: - TLSInStream(InStream* in, gnutls_session_t session); + TLSInStream(TLSSocket* sock); virtual ~TLSInStream(); private: bool fillBuffer() override; - size_t readTLS(uint8_t* buf, size_t len); - static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size); - - gnutls_session_t session; - InStream* in; - bool streamEmpty; - std::exception* saved_exception; + TLSSocket* sock; }; -}; + +} #endif + #endif diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx index 365ffd60..ba9d182f 100644 --- a/common/rdr/TLSOutStream.cxx +++ b/common/rdr/TLSOutStream.cxx @@ -1,7 +1,7 @@ /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team - * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,108 +23,42 @@ #include <config.h> #endif -#include <rdr/Exception.h> -#include <rdr/TLSException.h> #include <rdr/TLSOutStream.h> -#include <rfb/LogWriter.h> -#include <errno.h> +#include <rdr/TLSSocket.h> #ifdef HAVE_GNUTLS -using namespace rdr; - -static rfb::LogWriter vlog("TLSOutStream"); - -ssize_t TLSOutStream::push(gnutls_transport_ptr_t str, const void* data, - size_t size) -{ - TLSOutStream* self= (TLSOutStream*) str; - OutStream *out = self->out; - - delete self->saved_exception; - self->saved_exception = nullptr; - try { - out->writeBytes((const uint8_t*)data, size); - out->flush(); - } catch (socket_error& e) { - vlog.error("Failure sending TLS data: %s", e.what()); - gnutls_transport_set_errno(self->session, e.err); - self->saved_exception = new socket_error(e); - return -1; - } catch (std::exception& e) { - vlog.error("Failure sending TLS data: %s", e.what()); - gnutls_transport_set_errno(self->session, EINVAL); - self->saved_exception = new std::runtime_error(e.what()); - return -1; - } - - return size; -} +using namespace rdr; -TLSOutStream::TLSOutStream(OutStream* _out, gnutls_session_t _session) - : session(_session), out(_out), saved_exception(nullptr) +TLSOutStream::TLSOutStream(TLSSocket* sock_) + : sock(sock_) { - gnutls_transport_ptr_t recv, send; - - gnutls_transport_set_push_function(session, push); - gnutls_transport_get_ptr2(session, &recv, &send); - gnutls_transport_set_ptr2(session, recv, this); } TLSOutStream::~TLSOutStream() { -#if 0 - try { -// flush(); - } catch (Exception&) { - } -#endif - gnutls_transport_set_push_function(session, nullptr); - - delete saved_exception; } void TLSOutStream::flush() { BufferedOutStream::flush(); - out->flush(); + sock->out->flush(); } void TLSOutStream::cork(bool enable) { BufferedOutStream::cork(enable); - out->cork(enable); + sock->out->cork(enable); } bool TLSOutStream::flushBuffer() { while (sentUpTo < ptr) { - size_t n = writeTLS(sentUpTo, ptr - sentUpTo); + size_t n = sock->writeTLS(sentUpTo, ptr - sentUpTo); sentUpTo += n; } return true; } -size_t TLSOutStream::writeTLS(const uint8_t* data, size_t length) -{ - int n; - - n = gnutls_record_send(session, data, length); - if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) - return 0; - - if (n == GNUTLS_E_PUSH_ERROR) { - if (dynamic_cast<socket_error*>(saved_exception)) - throw *dynamic_cast<socket_error*>(saved_exception); - else - throw std::runtime_error(saved_exception->what()); - } - - if (n < 0) - throw tls_error("writeTLS", n); - - return n; -} - #endif diff --git a/common/rdr/TLSOutStream.h b/common/rdr/TLSOutStream.h index 659f16f0..aa9572ba 100644 --- a/common/rdr/TLSOutStream.h +++ b/common/rdr/TLSOutStream.h @@ -21,14 +21,16 @@ #define __RDR_TLSOUTSTREAM_H__ #ifdef HAVE_GNUTLS -#include <gnutls/gnutls.h> + #include <rdr/BufferedOutStream.h> namespace rdr { + class TLSSocket; + class TLSOutStream : public BufferedOutStream { public: - TLSOutStream(OutStream* out, gnutls_session_t session); + TLSOutStream(TLSSocket* out); virtual ~TLSOutStream(); void flush() override; @@ -36,15 +38,12 @@ namespace rdr { private: bool flushBuffer() override; - size_t writeTLS(const uint8_t* data, size_t length); - static ssize_t push(gnutls_transport_ptr_t str, const void* data, size_t size); - - gnutls_session_t session; - OutStream* out; - std::exception* saved_exception; + TLSSocket* sock; }; -}; + +} #endif + #endif diff --git a/common/rdr/TLSSocket.cxx b/common/rdr/TLSSocket.cxx new file mode 100644 index 00000000..a29e41c1 --- /dev/null +++ b/common/rdr/TLSSocket.cxx @@ -0,0 +1,228 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * Copyright (C) 2012-2025 Pierre Ossman for Cendio AB + * + * 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 <config.h> +#endif + +#include <core/Exception.h> +#include <core/LogWriter.h> + +#include <rdr/InStream.h> +#include <rdr/OutStream.h> +#include <rdr/TLSException.h> +#include <rdr/TLSSocket.h> + +#include <errno.h> + +#ifdef HAVE_GNUTLS + +using namespace rdr; + +static core::LogWriter vlog("TLSSocket"); + +TLSSocket::TLSSocket(InStream* in_, OutStream* out_, + gnutls_session_t session_) + : session(session_), in(in_), out(out_), tlsin(this), tlsout(this) +{ + gnutls_transport_set_pull_function( + session, [](gnutls_transport_ptr_t sock, void* data, size_t size) { + return ((TLSSocket*)sock)->pull(data, size); + }); + gnutls_transport_set_push_function( + session, [](gnutls_transport_ptr_t sock, const void* data, size_t size) { + return ((TLSSocket*)sock)->push(data, size); + }); + gnutls_transport_set_ptr(session, this); +} + +TLSSocket::~TLSSocket() +{ + gnutls_transport_set_pull_function(session, nullptr); + gnutls_transport_set_push_function(session, nullptr); + gnutls_transport_set_ptr(session, nullptr); +} + +bool TLSSocket::handshake() +{ + int err; + + err = gnutls_handshake(session); + if (err != GNUTLS_E_SUCCESS) { + gnutls_alert_description_t alert; + const char* msg; + + if ((err == GNUTLS_E_PULL_ERROR) || (err == GNUTLS_E_PUSH_ERROR)) + std::rethrow_exception(saved_exception); + + alert = gnutls_alert_get(session); + msg = nullptr; + + if ((err == GNUTLS_E_WARNING_ALERT_RECEIVED) || + (err == GNUTLS_E_FATAL_ALERT_RECEIVED)) + msg = gnutls_alert_get_name(alert); + + if (msg == nullptr) + msg = gnutls_strerror(err); + + if (!gnutls_error_is_fatal(err)) { + vlog.debug("Deferring completion of TLS handshake: %s", msg); + return false; + } + + vlog.error("TLS Handshake failed: %s\n", msg); + gnutls_alert_send_appropriate(session, err); + throw rdr::tls_error("TLS Handshake failed", err, alert); + } + + return true; +} + +void TLSSocket::shutdown() +{ + int ret; + + try { + if (tlsout.hasBufferedData()) { + tlsout.cork(false); + tlsout.flush(); + if (tlsout.hasBufferedData()) + vlog.error("Failed to flush remaining socket data on close"); + } + } catch (std::exception& e) { + vlog.error("Failed to flush remaining socket data on close: %s", e.what()); + } + + // FIXME: We can't currently wait for the response, so we only send + // our close and hope for the best + ret = gnutls_bye(session, GNUTLS_SHUT_WR); + if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION)) + vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret)); +} + +size_t TLSSocket::readTLS(uint8_t* buf, size_t len) +{ + int n; + + while (true) { + streamEmpty = false; + n = gnutls_record_recv(session, (void *) buf, len); + if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) { + // GnuTLS returns GNUTLS_E_AGAIN for a bunch of other scenarios + // other than the pull function returning EAGAIN, so we have to + // double check that the underlying stream really is empty + if (!streamEmpty) + continue; + else + return 0; + } + break; + }; + + if (n == GNUTLS_E_PULL_ERROR) + std::rethrow_exception(saved_exception); + + if (n < 0) { + gnutls_alert_send_appropriate(session, n); + throw tls_error("readTLS", n, gnutls_alert_get(session)); + } + + if (n == 0) + throw end_of_stream(); + + return n; +} + +size_t TLSSocket::writeTLS(const uint8_t* data, size_t length) +{ + int n; + + n = gnutls_record_send(session, data, length); + if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) + return 0; + + if (n == GNUTLS_E_PUSH_ERROR) + std::rethrow_exception(saved_exception); + + if (n < 0) { + gnutls_alert_send_appropriate(session, n); + throw tls_error("writeTLS", n, gnutls_alert_get(session)); + } + + return n; +} + +ssize_t TLSSocket::pull(void* data, size_t size) +{ + streamEmpty = false; + saved_exception = nullptr; + + try { + if (!in->hasData(1)) { + streamEmpty = true; + gnutls_transport_set_errno(session, EAGAIN); + return -1; + } + + if (in->avail() < size) + size = in->avail(); + + in->readBytes((uint8_t*)data, size); + } catch (end_of_stream&) { + return 0; + } catch (std::exception& e) { + core::socket_error* se; + vlog.error("Failure reading TLS data: %s", e.what()); + se = dynamic_cast<core::socket_error*>(&e); + if (se) + gnutls_transport_set_errno(session, se->err); + else + gnutls_transport_set_errno(session, EINVAL); + saved_exception = std::current_exception(); + return -1; + } + + return size; +} + +ssize_t TLSSocket::push(const void* data, size_t size) +{ + saved_exception = nullptr; + + try { + out->writeBytes((const uint8_t*)data, size); + out->flush(); + } catch (std::exception& e) { + core::socket_error* se; + vlog.error("Failure sending TLS data: %s", e.what()); + se = dynamic_cast<core::socket_error*>(&e); + if (se) + gnutls_transport_set_errno(session, se->err); + else + gnutls_transport_set_errno(session, EINVAL); + saved_exception = std::current_exception(); + return -1; + } + + return size; +} + +#endif diff --git a/common/rdr/TLSSocket.h b/common/rdr/TLSSocket.h new file mode 100644 index 00000000..ca29f8bc --- /dev/null +++ b/common/rdr/TLSSocket.h @@ -0,0 +1,81 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * Copyright 2012-2025 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifndef __RDR_TLSSOCKET_H__ +#define __RDR_TLSSOCKET_H__ + +#ifdef HAVE_GNUTLS + +#include <exception> + +#include <gnutls/gnutls.h> + +#include <rdr/TLSInStream.h> +#include <rdr/TLSOutStream.h> + +namespace rdr { + + class InStream; + class OutStream; + + class TLSInStream; + class TLSOutStream; + + class TLSSocket { + public: + TLSSocket(InStream* in, OutStream* out, gnutls_session_t session); + virtual ~TLSSocket(); + + TLSInStream& inStream() { return tlsin; } + TLSOutStream& outStream() { return tlsout; } + + bool handshake(); + void shutdown(); + + protected: + /* Used by the stream classes */ + size_t readTLS(uint8_t* buf, size_t len); + size_t writeTLS(const uint8_t* data, size_t length); + + friend TLSInStream; + friend TLSOutStream; + + private: + ssize_t pull(void* data, size_t size); + ssize_t push(const void* data, size_t size); + + gnutls_session_t session; + + InStream* in; + OutStream* out; + + TLSInStream tlsin; + TLSOutStream tlsout; + + bool streamEmpty; + + std::exception_ptr saved_exception; + }; + +} + +#endif + +#endif diff --git a/common/rdr/ZlibOutStream.cxx b/common/rdr/ZlibOutStream.cxx index 73b5d459..1b8ac307 100644 --- a/common/rdr/ZlibOutStream.cxx +++ b/common/rdr/ZlibOutStream.cxx @@ -23,14 +23,15 @@ #include <stdio.h> +#include <core/LogWriter.h> + #include <rdr/ZlibOutStream.h> -#include <rfb/LogWriter.h> #include <zlib.h> #undef ZLIBOUT_DEBUG -static rfb::LogWriter vlog("ZlibOutStream"); +static core::LogWriter vlog("ZlibOutStream"); using namespace rdr; diff --git a/common/rfb/Blacklist.cxx b/common/rfb/Blacklist.cxx index 68420ae2..19a9ed39 100644 --- a/common/rfb/Blacklist.cxx +++ b/common/rfb/Blacklist.cxx @@ -21,24 +21,26 @@ #endif #include <rfb/Blacklist.h> -#include <rfb/Configuration.h> +#include <core/Configuration.h> using namespace rfb; -BoolParameter enabled("UseBlacklist", - "Temporarily reject connections from a host if it " - "repeatedly fails to authenticate.", - true); -IntParameter threshold("BlacklistThreshold", - "The number of unauthenticated connection attempts " - "allowed from any individual host before that host " - "is black-listed", - 5); -IntParameter initialTimeout("BlacklistTimeout", - "The initial timeout applied when a host is " - "first black-listed. The host cannot re-attempt " - "a connection until the timeout expires.", - 10); +core::BoolParameter enabled("UseBlacklist", + "Temporarily reject connections from a " + "host if it repeatedly fails to " + "authenticate.", + true); +core::IntParameter threshold("BlacklistThreshold", + "The number of unauthenticated connection " + "attempts allowed from any individual " + "host before that host is black-listed", + 5, 0, INT_MAX); +core::IntParameter initialTimeout("BlacklistTimeout", + "The initial timeout applied when a " + "host is first black-listed. The " + "host cannot re-attempt a connection " + "until the timeout expires.", + 10, 0, INT_MAX); Blacklist::Blacklist() { diff --git a/common/rfb/Blacklist.h b/common/rfb/Blacklist.h index c1699f29..3c9660cc 100644 --- a/common/rfb/Blacklist.h +++ b/common/rfb/Blacklist.h @@ -27,13 +27,11 @@ #ifndef __RFB_BLACKLIST_H__ #define __RFB_BLACKLIST_H__ -#include <string.h> #include <time.h> + #include <map> #include <string> -#include <rfb/Configuration.h> - namespace rfb { // diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx index 5e7530c8..bbeef385 100644 --- a/common/rfb/CConnection.cxx +++ b/common/rfb/CConnection.cxx @@ -27,31 +27,34 @@ #include <algorithm> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rfb/Exception.h> #include <rfb/clipboardTypes.h> #include <rfb/fenceTypes.h> +#include <rfb/screenTypes.h> #include <rfb/CMsgReader.h> #include <rfb/CMsgWriter.h> #include <rfb/CSecurity.h> +#include <rfb/Cursor.h> #include <rfb/Decoder.h> #include <rfb/KeysymStr.h> +#include <rfb/PixelBuffer.h> #include <rfb/Security.h> #include <rfb/SecurityClient.h> #include <rfb/CConnection.h> -#include <rfb/util.h> #define XK_MISCELLANY #define XK_XKB_KEYS #include <rfb/keysymdef.h> -#include <rfb/LogWriter.h> - #include <rdr/InStream.h> #include <rdr/OutStream.h> using namespace rfb; -static LogWriter vlog("CConnection"); +static core::LogWriter vlog("CConnection"); CConnection::CConnection() : csecurity(nullptr), @@ -98,7 +101,7 @@ void CConnection::setFramebuffer(ModifiablePixelBuffer* fb) } if ((framebuffer != nullptr) && (fb != nullptr)) { - Rect rect; + core::Rect rect; const uint8_t* data; int stride; @@ -107,9 +110,8 @@ void CConnection::setFramebuffer(ModifiablePixelBuffer* fb) // Copy still valid area - rect.setXYWH(0, 0, - __rfbmin(fb->width(), framebuffer->width()), - __rfbmin(fb->height(), framebuffer->height())); + rect = fb->getRect(); + rect = rect.intersect(framebuffer->getRect()); data = framebuffer->getBuffer(framebuffer->getRect(), &stride); fb->imageRect(rect, data, stride); @@ -189,10 +191,9 @@ bool CConnection::processVersionMsg() vlog.error("Server gave unsupported RFB protocol version %d.%d", server.majorVersion, server.minorVersion); state_ = RFBSTATE_INVALID; - throw protocol_error(format("Server gave unsupported RFB protocol " - "version %d.%d", - server.majorVersion, - server.minorVersion)); + throw protocol_error( + core::format("Server gave unsupported RFB protocol version %d.%d", + server.majorVersion, server.minorVersion)); } else if (server.beforeVersion(3,7)) { server.setVersion(3,3); } else if (server.afterVersion(3,8)) { @@ -379,7 +380,6 @@ void CConnection::securityCompleted() reader_ = new CMsgReader(this, is); writer_ = new CMsgWriter(&server, os); vlog.debug("Authentication success!"); - authSuccess(); writer_->writeClientInit(shared); } @@ -410,7 +410,7 @@ void CConnection::setDesktopSize(int w, int h) { decoder.flush(); - CMsgHandler::setDesktopSize(w,h); + server.setDimensions(w, h); if (continuousUpdates) writer()->writeEnableContinuousUpdates(true, 0, 0, @@ -430,7 +430,15 @@ void CConnection::setExtendedDesktopSize(unsigned reason, { decoder.flush(); - CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout); + server.supportsSetDesktopSize = true; + + if ((reason != reasonClient) || (result == resultSuccess)) + server.setDimensions(w, h, layout); + + if ((reason == reasonClient) && (result != resultSuccess)) { + vlog.error("SetDesktopSize failed: %d", result); + return; + } if (continuousUpdates) writer()->writeEnableContinuousUpdates(true, 0, 0, @@ -443,9 +451,41 @@ void CConnection::setExtendedDesktopSize(unsigned reason, assert(framebuffer->height() == server.height()); } +void CConnection::setCursor(int width, int height, + const core::Point& hotspot, + const uint8_t* data) +{ + Cursor cursor(width, height, hotspot, data); + server.setCursor(cursor); +} + +void CConnection::setCursorPos(const core::Point& /*pos*/) +{ +} + +void CConnection::setName(const char* name) +{ + server.setName(name); +} + +void CConnection::fence(uint32_t flags, unsigned len, const uint8_t data[]) +{ + server.supportsFence = true; + + if (flags & fenceFlagRequest) { + // FIXME: We handle everything synchronously, and we assume anything + // using us also does so, which means we automatically handle + // these flags + flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter); + + writer()->writeFence(flags, len, data); + return; + } +} + void CConnection::endOfContinuousUpdates() { - CMsgHandler::endOfContinuousUpdates(); + server.supportsContinuousUpdates = true; // We've gotten the marker for a format change, so make the pending // one active @@ -459,11 +499,23 @@ void CConnection::endOfContinuousUpdates() } } +void CConnection::supportsQEMUKeyEvent() +{ + server.supportsQEMUKeyEvent = true; +} + +void CConnection::supportsExtendedMouseButtons() +{ + server.supportsExtendedMouseButtons = true; +} + void CConnection::serverInit(int width, int height, const PixelFormat& pf, const char* name) { - CMsgHandler::serverInit(width, height, pf, name); + server.setDimensions(width, height); + server.setPF(pf); + server.setName(name); state_ = RFBSTATE_NORMAL; vlog.debug("Initialisation done"); @@ -486,7 +538,7 @@ void CConnection::serverInit(int width, int height, } } -bool CConnection::readAndDecodeRect(const Rect& r, int encoding, +bool CConnection::readAndDecodeRect(const core::Rect& r, int encoding, ModifiablePixelBuffer* pb) { if (!decoder.decodeRect(r, encoding, pb)) @@ -497,8 +549,6 @@ bool CConnection::readAndDecodeRect(const Rect& r, int encoding, void CConnection::framebufferUpdateStart() { - CMsgHandler::framebufferUpdateStart(); - assert(framebuffer != nullptr); // Note: This might not be true if continuous updates are supported @@ -511,8 +561,6 @@ void CConnection::framebufferUpdateEnd() { decoder.flush(); - CMsgHandler::framebufferUpdateEnd(); - // A format change has been scheduled and we are now past the update // with the old format. Time to active the new one. if (pendingPFChange && !continuousUpdates) { @@ -533,11 +581,18 @@ void CConnection::framebufferUpdateEnd() } } -bool CConnection::dataRect(const Rect& r, int encoding) +bool CConnection::dataRect(const core::Rect& r, int encoding) { return decoder.decodeRect(r, encoding, framebuffer); } +void CConnection::setColourMapEntries(int /*firstColour*/, + int /*nColours*/, + uint16_t* /*rgbs*/) +{ + vlog.error("Invalid SetColourMapEntries from server!"); +} + void CConnection::serverCutText(const char* str) { hasLocalClipboard = false; @@ -548,12 +603,53 @@ void CConnection::serverCutText(const char* str) handleClipboardAnnounce(true); } +void CConnection::setLEDState(unsigned int state) +{ + server.setLEDState(state); +} + void CConnection::handleClipboardCaps(uint32_t flags, const uint32_t* lengths) { + int i; uint32_t sizes[] = { 0 }; - CMsgHandler::handleClipboardCaps(flags, lengths); + vlog.debug("Got server clipboard capabilities:"); + for (i = 0;i < 16;i++) { + if (flags & (1 << i)) { + const char *type; + + switch (1 << i) { + case clipboardUTF8: + type = "Plain text"; + break; + case clipboardRTF: + type = "Rich text"; + break; + case clipboardHTML: + type = "HTML"; + break; + case clipboardDIB: + type = "Images"; + break; + case clipboardFiles: + type = "Files"; + break; + default: + vlog.debug(" Unknown format 0x%x", 1 << i); + continue; + } + + if (lengths[i] == 0) + vlog.debug(" %s (only notify)", type); + else { + vlog.debug(" %s (automatically send up to %s)", + type, core::iecPrefix(lengths[i], "B").c_str()); + } + } + } + + server.setClipboardCaps(flags, lengths); writer()->writeClipboardCaps(rfb::clipboardUTF8 | rfb::clipboardRequest | @@ -604,21 +700,17 @@ void CConnection::handleClipboardProvide(uint32_t flags, } // FIXME: This conversion magic should be in CMsgReader - if (!isValidUTF8((const char*)data[0], lengths[0])) { + if (!core::isValidUTF8((const char*)data[0], lengths[0])) { vlog.error("Invalid UTF-8 sequence in clipboard - ignoring"); return; } - serverClipboard = convertLF((const char*)data[0], lengths[0]); + serverClipboard = core::convertLF((const char*)data[0], lengths[0]); hasRemoteClipboard = true; // FIXME: Should probably verify that this data was actually requested handleClipboardData(serverClipboard.c_str()); } -void CConnection::authSuccess() -{ -} - void CConnection::initDone() { } @@ -679,7 +771,7 @@ void CConnection::sendClipboardData(const char* data) { if (server.clipboardFlags() & rfb::clipboardProvide) { // FIXME: This conversion magic should be in CMsgWriter - std::string filtered(convertCRLF(data)); + std::string filtered(core::convertCRLF(data)); size_t sizes[1] = { filtered.size() + 1 }; const uint8_t* datas[1] = { (const uint8_t*)filtered.c_str() }; @@ -815,6 +907,11 @@ void CConnection::setCompressLevel(int level) encodingChange = true; } +int CConnection::getCompressLevel() +{ + return compressLevel; +} + void CConnection::setQualityLevel(int level) { if (qualityLevel == level) @@ -824,6 +921,11 @@ void CConnection::setQualityLevel(int level) encodingChange = true; } +int CConnection::getQualityLevel() +{ + return qualityLevel; +} + void CConnection::setPF(const PixelFormat& pf) { if (server.pf() == pf && !formatChange) @@ -833,17 +935,9 @@ void CConnection::setPF(const PixelFormat& pf) formatChange = true; } -void CConnection::fence(uint32_t flags, unsigned len, const uint8_t data[]) +bool CConnection::isSecure() const { - CMsgHandler::fence(flags, len, data); - - if (!(flags & fenceFlagRequest)) - return; - - // We cannot guarantee any synchronisation at this level - flags = 0; - - writer()->writeFence(flags, len, data); + return csecurity ? csecurity->isSecure() : false; } // requestNewUpdate() requests an update from the server, having set the @@ -884,9 +978,9 @@ void CConnection::requestNewUpdate() if (forceNonincremental || !continuousUpdates) { pendingUpdate = true; - writer()->writeFramebufferUpdateRequest(Rect(0, 0, - server.width(), - server.height()), + writer()->writeFramebufferUpdateRequest({0, 0, + server.width(), + server.height()}, !forceNonincremental); } diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h index 07e47e39..b28c5aa9 100644 --- a/common/rfb/CConnection.h +++ b/common/rfb/CConnection.h @@ -29,8 +29,14 @@ #include <rfb/CMsgHandler.h> #include <rfb/DecodeManager.h> +#include <rfb/PixelFormat.h> #include <rfb/SecurityClient.h> +namespace rdr { + class InStream; + class OutStream; +} + namespace rfb { class CMsgReader; @@ -99,89 +105,6 @@ namespace rfb { // connection void close(); - - // Methods overridden from CMsgHandler - - // Note: These must be called by any deriving classes - - void setDesktopSize(int w, int h) override; - void setExtendedDesktopSize(unsigned reason, unsigned result, - int w, int h, - const ScreenSet& layout) override; - - void endOfContinuousUpdates() override; - - void serverInit(int width, int height, const PixelFormat& pf, - const char* name) override; - - bool readAndDecodeRect(const Rect& r, int encoding, - ModifiablePixelBuffer* pb) override; - - void framebufferUpdateStart() override; - void framebufferUpdateEnd() override; - bool dataRect(const Rect& r, int encoding) override; - - void serverCutText(const char* str) override; - - void handleClipboardCaps(uint32_t flags, - const uint32_t* lengths) override; - void handleClipboardRequest(uint32_t flags) override; - void handleClipboardPeek() override; - void handleClipboardNotify(uint32_t flags) override; - void handleClipboardProvide(uint32_t flags, const size_t* lengths, - const uint8_t* const* data) override; - - - // Methods to be overridden in a derived class - - // getUserPasswd() gets the username and password. This might - // involve a dialog, getpass(), etc. The user buffer pointer can be - // null, in which case no user name will be retrieved. - virtual void getUserPasswd(bool secure, std::string* user, - std::string* password) = 0; - - // showMsgBox() displays a message box with the specified style and - // contents. The return value is true if the user clicked OK/Yes. - virtual bool showMsgBox(MsgBoxFlags flags, const char *title, - const char *text) = 0; - - // authSuccess() is called when authentication has succeeded. - virtual void authSuccess(); - - // initDone() is called when the connection is fully established - // and standard messages can be sent. This is called before the - // initial FramebufferUpdateRequest giving a derived class the - // chance to modify pixel format and settings. The derived class - // must also make sure it has provided a valid framebuffer before - // returning. - virtual void initDone() = 0; - - // resizeFramebuffer() is called whenever the framebuffer - // dimensions or the screen layout changes. A subclass must make - // sure the pixel buffer has been updated once this call returns. - virtual void resizeFramebuffer(); - - // handleClipboardRequest() is called whenever the server requests - // the client to send over its clipboard data. It will only be - // called after the client has first announced a clipboard change - // via announceClipboard(). - virtual void handleClipboardRequest(); - - // handleClipboardAnnounce() is called to indicate a change in the - // clipboard on the server. Call requestClipboard() to access the - // actual data. - virtual void handleClipboardAnnounce(bool available); - - // handleClipboardData() is called when the server has sent over - // the clipboard data as a result of a previous call to - // requestClipboard(). Note that this function might never be - // called if the clipboard data was no longer available when the - // server received the request. - virtual void handleClipboardData(const char* data); - - - // Other methods - // requestClipboard() will result in a request to the server to // transfer its clipboard data. A call to handleClipboardData() // will be made once the data is available. @@ -221,7 +144,9 @@ namespace rfb { // setCompressLevel()/setQualityLevel() controls the encoding hints // sent to the server void setCompressLevel(int level); + int getCompressLevel(); void setQualityLevel(int level); + int getQualityLevel(); // setPF() controls the pixel format requested from the server. // server.pf() will automatically be adjusted once the new format // is active. @@ -237,7 +162,7 @@ namespace rfb { // Identities, to determine the unique(ish) name of the server. const char* getServerName() const { return serverName.c_str(); } - bool isSecure() const { return csecurity ? csecurity->isSecure() : false; } + bool isSecure() const; enum stateEnum { RFBSTATE_UNINITIALISED, @@ -254,8 +179,107 @@ namespace rfb { stateEnum state() { return state_; } + // Methods used by SSecurity classes + + // getUserPasswd() gets the username and password. This might + // involve a dialog, getpass(), etc. The user buffer pointer can be + // null, in which case no user name will be retrieved. + virtual void getUserPasswd(bool secure, std::string* user, + std::string* password) = 0; + + // showMsgBox() displays a message box with the specified style and + // contents. The return value is true if the user clicked OK/Yes. + virtual bool showMsgBox(MsgBoxFlags flags, const char *title, + const char *text) = 0; + + protected: + + // Methods overridden from CMsgHandler + + // Note: These must be called by any deriving classes + + void setDesktopSize(int w, int h) override; + void setExtendedDesktopSize(unsigned reason, unsigned result, + int w, int h, + const ScreenSet& layout) override; + + void setCursor(int width, int height, const core::Point& hotspot, + const uint8_t* data) override; + void setCursorPos(const core::Point& pos) override; + + void setName(const char* name) override; + + void fence(uint32_t flags, unsigned len, const uint8_t data[]) override; + + void endOfContinuousUpdates() override; + + void supportsQEMUKeyEvent() override; + + void supportsExtendedMouseButtons() override; + + void serverInit(int width, int height, const PixelFormat& pf, + const char* name) override; + + bool readAndDecodeRect(const core::Rect& r, int encoding, + ModifiablePixelBuffer* pb) override; + + void framebufferUpdateStart() override; + void framebufferUpdateEnd() override; + bool dataRect(const core::Rect& r, int encoding) override; + + void setColourMapEntries(int firstColour, int nColours, + uint16_t* rgbs) override; + + void serverCutText(const char* str) override; + + void setLEDState(unsigned int state) override; + + void handleClipboardCaps(uint32_t flags, + const uint32_t* lengths) override; + void handleClipboardRequest(uint32_t flags) override; + void handleClipboardPeek() override; + void handleClipboardNotify(uint32_t flags) override; + void handleClipboardProvide(uint32_t flags, const size_t* lengths, + const uint8_t* const* data) override; + + + // Methods to be overridden in a derived class + + // initDone() is called when the connection is fully established + // and standard messages can be sent. This is called before the + // initial FramebufferUpdateRequest giving a derived class the + // chance to modify pixel format and settings. The derived class + // must also make sure it has provided a valid framebuffer before + // returning. + virtual void initDone() = 0; + + // resizeFramebuffer() is called whenever the framebuffer + // dimensions or the screen layout changes. A subclass must make + // sure the pixel buffer has been updated once this call returns. + virtual void resizeFramebuffer(); + + // handleClipboardRequest() is called whenever the server requests + // the client to send over its clipboard data. It will only be + // called after the client has first announced a clipboard change + // via announceClipboard(). + virtual void handleClipboardRequest(); + + // handleClipboardAnnounce() is called to indicate a change in the + // clipboard on the server. Call requestClipboard() to access the + // actual data. + virtual void handleClipboardAnnounce(bool available); + + // handleClipboardData() is called when the server has sent over + // the clipboard data as a result of a previous call to + // requestClipboard(). Note that this function might never be + // called if the clipboard data was no longer available when the + // server received the request. + virtual void handleClipboardData(const char* data); + + protected: CSecurity *csecurity; SecurityClient security; + protected: void setState(stateEnum s) { state_ = s; } @@ -273,13 +297,6 @@ namespace rfb { bool supportsLEDState; private: - // This is a default implementation of fences that automatically - // responds to requests, stating no support for synchronisation. - // When overriding, call CMsgHandler::fence() directly in order to - // state correct support for fence flags. - void fence(uint32_t flags, unsigned len, const uint8_t data[]) override; - - private: bool processVersionMsg(); bool processSecurityTypesMsg(); bool processSecurityMsg(); diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt index 36535448..d7467421 100644 --- a/common/rfb/CMakeLists.txt +++ b/common/rfb/CMakeLists.txt @@ -3,7 +3,6 @@ add_library(rfb STATIC Blacklist.cxx Congestion.cxx CConnection.cxx - CMsgHandler.cxx CMsgReader.cxx CMsgWriter.cxx CSecurityPlain.cxx @@ -12,7 +11,6 @@ add_library(rfb STATIC CSecurityVncAuth.cxx ClientParams.cxx ComparingUpdateTracker.cxx - Configuration.cxx CopyRectDecoder.cxx Cursor.cxx DecodeManager.cxx @@ -26,19 +24,13 @@ add_library(rfb STATIC JpegDecompressor.cxx KeyRemapper.cxx KeysymStr.c - LogWriter.cxx - Logger.cxx - Logger_file.cxx - Logger_stdio.cxx PixelBuffer.cxx PixelFormat.cxx RREEncoder.cxx RREDecoder.cxx RawDecoder.cxx RawEncoder.cxx - Region.cxx SConnection.cxx - SMsgHandler.cxx SMsgReader.cxx SMsgWriter.cxx ServerCore.cxx @@ -50,7 +42,6 @@ add_library(rfb STATIC SSecurityStack.cxx SSecurityVncAuth.cxx SSecurityVeNCrypt.cxx - Timer.cxx TightDecoder.cxx TightEncoder.cxx TightJPEGEncoder.cxx @@ -60,15 +51,12 @@ add_library(rfb STATIC ZRLEEncoder.cxx ZRLEDecoder.cxx encodings.cxx - obfuscate.cxx - util.cxx) + obfuscate.cxx) target_include_directories(rfb PUBLIC ${CMAKE_SOURCE_DIR}/common) target_include_directories(rfb SYSTEM PUBLIC ${JPEG_INCLUDE_DIR}) -target_include_directories(rfb SYSTEM PUBLIC ${PIXMAN_INCLUDE_DIRS}) -target_link_libraries(rfb os rdr network) +target_link_libraries(rfb core rdr network) target_link_libraries(rfb ${JPEG_LIBRARIES} ${PIXMAN_LIBRARIES}) -target_link_directories(rfb PUBLIC ${PIXMAN_LIBRARY_DIRS}) if(ENABLE_H264 AND NOT H264_LIBS STREQUAL "NONE") target_sources(rfb PRIVATE H264Decoder.cxx H264DecoderContext.cxx) @@ -79,11 +67,6 @@ if(ENABLE_H264 AND NOT H264_LIBS STREQUAL "NONE") endif() target_include_directories(rfb SYSTEM PUBLIC ${H264_INCLUDE_DIRS}) target_link_libraries(rfb ${H264_LIBRARIES}) - target_link_directories(rfb PUBLIC ${H264_LIBRARY_DIRS}) -endif() - -if(UNIX) - target_sources(rfb PRIVATE Logger_syslog.cxx) endif() if(WIN32) @@ -92,8 +75,9 @@ if(WIN32) endif(WIN32) if(UNIX AND NOT APPLE) - target_sources(rfb PRIVATE UnixPasswordValidator.cxx pam.c) - target_link_libraries(rfb ${PAM_LIBS}) + target_sources(rfb PRIVATE UnixPasswordValidator.cxx) + target_include_directories(rfb SYSTEM PRIVATE ${PAM_INCLUDE_DIRS}) + target_link_libraries(rfb ${PAM_LIBRARIES}) endif() if(GNUTLS_FOUND) @@ -109,12 +93,8 @@ endif() if (NETTLE_FOUND) target_sources(rfb PRIVATE CSecurityDH.cxx CSecurityMSLogonII.cxx CSecurityRSAAES.cxx SSecurityRSAAES.cxx) - target_include_directories(rfb SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS} - ${GMP_INCLUDE_DIRS}) - target_link_libraries(rfb ${HOGWEED_LIBRARIES} - ${NETTLE_LIBRARIES} ${GMP_LIBRARIES}) - target_link_directories(rfb PUBLIC ${HOGWEED_LIBRARY_DIRS} - ${NETTLE_LIBRARY_DIRS} ${GMP_LIBRARY_DIRS}) + target_include_directories(rfb SYSTEM PUBLIC ${NETTLE_INCLUDE_DIRS}) + target_link_libraries(rfb ${HOGWEED_LIBRARIES} ${NETTLE_LIBRARIES}) endif() if(UNIX) diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx deleted file mode 100644 index 0f3f6cd5..00000000 --- a/common/rfb/CMsgHandler.cxx +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2019 Pierre Ossman for Cendio AB - * - * 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 <config.h> -#endif - -#include <stdio.h> - -#include <rfb/Exception.h> -#include <rfb/LogWriter.h> -#include <rfb/CMsgHandler.h> -#include <rfb/clipboardTypes.h> -#include <rfb/screenTypes.h> -#include <rfb/util.h> - -static rfb::LogWriter vlog("CMsgHandler"); - -using namespace rfb; - -CMsgHandler::CMsgHandler() -{ -} - -CMsgHandler::~CMsgHandler() -{ -} - -void CMsgHandler::setDesktopSize(int width, int height) -{ - server.setDimensions(width, height); -} - -void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result, - int width, int height, - const ScreenSet& layout) -{ - server.supportsSetDesktopSize = true; - - if ((reason == reasonClient) && (result != resultSuccess)) - return; - - server.setDimensions(width, height, layout); -} - -void CMsgHandler::setName(const char* name) -{ - server.setName(name); -} - -void CMsgHandler::fence(uint32_t /*flags*/, unsigned /*len*/, - const uint8_t /*data*/ []) -{ - server.supportsFence = true; -} - -void CMsgHandler::endOfContinuousUpdates() -{ - server.supportsContinuousUpdates = true; -} - -void CMsgHandler::supportsExtendedMouseButtons() -{ - server.supportsExtendedMouseButtons = true; -} - -void CMsgHandler::supportsQEMUKeyEvent() -{ - server.supportsQEMUKeyEvent = true; -} - -void CMsgHandler::serverInit(int width, int height, - const PixelFormat& pf, - const char* name) -{ - server.setDimensions(width, height); - server.setPF(pf); - server.setName(name); -} - -void CMsgHandler::framebufferUpdateStart() -{ -} - -void CMsgHandler::framebufferUpdateEnd() -{ -} - -void CMsgHandler::setLEDState(unsigned int state) -{ - server.setLEDState(state); -} - -void CMsgHandler::handleClipboardCaps(uint32_t flags, const uint32_t* lengths) -{ - int i; - - vlog.debug("Got server clipboard capabilities:"); - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) { - const char *type; - - switch (1 << i) { - case clipboardUTF8: - type = "Plain text"; - break; - case clipboardRTF: - type = "Rich text"; - break; - case clipboardHTML: - type = "HTML"; - break; - case clipboardDIB: - type = "Images"; - break; - case clipboardFiles: - type = "Files"; - break; - default: - vlog.debug(" Unknown format 0x%x", 1 << i); - continue; - } - - if (lengths[i] == 0) - vlog.debug(" %s (only notify)", type); - else { - vlog.debug(" %s (automatically send up to %s)", - type, iecPrefix(lengths[i], "B").c_str()); - } - } - } - - server.setClipboardCaps(flags, lengths); -} - -void CMsgHandler::handleClipboardRequest(uint32_t /*flags*/) -{ -} - -void CMsgHandler::handleClipboardPeek() -{ -} - -void CMsgHandler::handleClipboardNotify(uint32_t /*flags*/) -{ -} - -void CMsgHandler::handleClipboardProvide(uint32_t /*flags*/, - const size_t* /*lengths*/, - const uint8_t* const* /*data*/) -{ -} diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h index b484b695..d267ae47 100644 --- a/common/rfb/CMsgHandler.h +++ b/common/rfb/CMsgHandler.h @@ -27,63 +27,62 @@ #include <stdint.h> #include <rfb/ServerParams.h> -#include <rfb/Rect.h> -#include <rfb/ScreenSet.h> -namespace rdr { class InStream; } +namespace core { + struct Point; + struct Rect; +} namespace rfb { + class ModifiablePixelBuffer; + struct ScreenSet; + class CMsgHandler { public: - CMsgHandler(); - virtual ~CMsgHandler(); - // The following methods are called as corresponding messages are - // read. A derived class should override these methods as desired. - // Note that for the setDesktopSize(), setExtendedDesktopSize(), - // setName(), serverInit() and handleClipboardCaps() methods, a - // derived class should call on to CMsgHandler's methods to set the - // members of "server" appropriately. + // read. A derived class must override these methods. - virtual void setDesktopSize(int w, int h); + virtual void setDesktopSize(int w, int h) = 0; virtual void setExtendedDesktopSize(unsigned reason, unsigned result, int w, int h, - const ScreenSet& layout); - virtual void setCursor(int width, int height, const Point& hotspot, + const ScreenSet& layout) = 0; + virtual void setCursor(int width, int height, const + core::Point& hotspot, const uint8_t* data) = 0; - virtual void setCursorPos(const Point& pos) = 0; - virtual void setName(const char* name); - virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]); - virtual void endOfContinuousUpdates(); - virtual void supportsQEMUKeyEvent(); - virtual void supportsExtendedMouseButtons(); + virtual void setCursorPos(const core::Point& pos) = 0; + virtual void setName(const char* name) = 0; + virtual void fence(uint32_t flags, unsigned len, + const uint8_t data[]) = 0; + virtual void endOfContinuousUpdates() = 0; + virtual void supportsQEMUKeyEvent() = 0; + virtual void supportsExtendedMouseButtons() = 0; virtual void serverInit(int width, int height, const PixelFormat& pf, const char* name) = 0; - virtual bool readAndDecodeRect(const Rect& r, int encoding, + virtual bool readAndDecodeRect(const core::Rect& r, int encoding, ModifiablePixelBuffer* pb) = 0; - virtual void framebufferUpdateStart(); - virtual void framebufferUpdateEnd(); - virtual bool dataRect(const Rect& r, int encoding) = 0; + virtual void framebufferUpdateStart() = 0; + virtual void framebufferUpdateEnd() = 0; + virtual bool dataRect(const core::Rect& r, int encoding) = 0; virtual void setColourMapEntries(int firstColour, int nColours, uint16_t* rgbs) = 0; virtual void bell() = 0; virtual void serverCutText(const char* str) = 0; - virtual void setLEDState(unsigned int state); + virtual void setLEDState(unsigned int state) = 0; virtual void handleClipboardCaps(uint32_t flags, - const uint32_t* lengths); - virtual void handleClipboardRequest(uint32_t flags); - virtual void handleClipboardPeek(); - virtual void handleClipboardNotify(uint32_t flags); + const uint32_t* lengths) = 0; + virtual void handleClipboardRequest(uint32_t flags) = 0; + virtual void handleClipboardPeek() = 0; + virtual void handleClipboardNotify(uint32_t flags) = 0; virtual void handleClipboardProvide(uint32_t flags, const size_t* lengths, - const uint8_t* const* data); + const uint8_t* const* data) = 0; ServerParams server; }; diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx index a10f7c47..a5e28ea5 100644 --- a/common/rfb/CMsgReader.cxx +++ b/common/rfb/CMsgReader.cxx @@ -26,20 +26,27 @@ #include <vector> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rdr/InStream.h> #include <rdr/ZlibInStream.h> #include <rfb/msgTypes.h> #include <rfb/clipboardTypes.h> -#include <rfb/util.h> #include <rfb/Exception.h> -#include <rfb/LogWriter.h> #include <rfb/CMsgHandler.h> #include <rfb/CMsgReader.h> +#include <rfb/PixelBuffer.h> +#include <rfb/ScreenSet.h> +#include <rfb/encodings.h> -static rfb::LogWriter vlog("CMsgReader"); +static core::LogWriter vlog("CMsgReader"); -static rfb::IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024); +static core::IntParameter maxCutText("MaxCutText", + "Maximum permitted length of an " + "incoming clipboard update", + 256*1024, 0, INT_MAX); using namespace rfb; @@ -77,11 +84,11 @@ bool CMsgReader::readServerInit() is->readBytes((uint8_t*)name.data(), len); name[len] = '\0'; - if (isValidUTF8(name.data())) + if (core::isValidUTF8(name.data())) handler->serverInit(width, height, pf, name.data()); else handler->serverInit(width, height, pf, - latin1ToUTF8(name.data()).c_str()); + core::latin1ToUTF8(name.data()).c_str()); return true; } @@ -119,7 +126,7 @@ bool CMsgReader::readMsg() ret = readEndOfContinuousUpdates(); break; default: - throw protocol_error(format("Unknown message type %d", currentMsgType)); + throw protocol_error(core::format("Unknown message type %d", currentMsgType)); } if (ret) @@ -288,8 +295,8 @@ bool CMsgReader::readServerCutText() std::vector<char> ca(len); is->readBytes((uint8_t*)ca.data(), len); - std::string utf8(latin1ToUTF8(ca.data(), ca.size())); - std::string filtered(convertLF(utf8.data(), utf8.size())); + std::string utf8(core::latin1ToUTF8(ca.data(), ca.size())); + std::string filtered(core::convertLF(utf8.data(), utf8.size())); handler->serverCutText(filtered.c_str()); @@ -470,7 +477,7 @@ bool CMsgReader::readFramebufferUpdate() return true; } -bool CMsgReader::readRect(const Rect& r, int encoding) +bool CMsgReader::readRect(const core::Rect& r, int encoding) { if ((r.br.x > handler->server.width()) || (r.br.y > handler->server.height())) { @@ -486,7 +493,8 @@ bool CMsgReader::readRect(const Rect& r, int encoding) return handler->dataRect(r, encoding); } -bool CMsgReader::readSetXCursor(int width, int height, const Point& hotspot) +bool CMsgReader::readSetXCursor(int width, int height, + const core::Point& hotspot) { if (width > maxCursorSize || height > maxCursorSize) throw protocol_error("Too big cursor"); @@ -550,7 +558,8 @@ bool CMsgReader::readSetXCursor(int width, int height, const Point& hotspot) return true; } -bool CMsgReader::readSetCursor(int width, int height, const Point& hotspot) +bool CMsgReader::readSetCursor(int width, int height, + const core::Point& hotspot) { if (width > maxCursorSize || height > maxCursorSize) throw protocol_error("Too big cursor"); @@ -596,7 +605,8 @@ bool CMsgReader::readSetCursor(int width, int height, const Point& hotspot) return true; } -bool CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot) +bool CMsgReader::readSetCursorWithAlpha(int width, int height, + const core::Point& hotspot) { if (width > maxCursorSize || height > maxCursorSize) throw protocol_error("Too big cursor"); @@ -657,7 +667,8 @@ bool CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hots return true; } -bool CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot) +bool CMsgReader::readSetVMwareCursor(int width, int height, + const core::Point& hotspot) { if (width > maxCursorSize || height > maxCursorSize) throw protocol_error("Too big cursor"); @@ -784,7 +795,7 @@ bool CMsgReader::readSetDesktopName(int x, int y, int w, int h) return true; } - if (!isValidUTF8(name.data())) { + if (!core::isValidUTF8(name.data())) { vlog.error("Ignoring DesktopName rect with invalid UTF-8 sequence"); return true; } diff --git a/common/rfb/CMsgReader.h b/common/rfb/CMsgReader.h index 3b1c0ddb..e33f701d 100644 --- a/common/rfb/CMsgReader.h +++ b/common/rfb/CMsgReader.h @@ -26,14 +26,13 @@ #include <stdint.h> -#include <rfb/Rect.h> -#include <rfb/encodings.h> +#include <core/Rect.h> namespace rdr { class InStream; } namespace rfb { + class CMsgHandler; - struct Rect; class CMsgReader { public: @@ -59,12 +58,16 @@ namespace rfb { bool readFramebufferUpdate(); - bool readRect(const Rect& r, int encoding); + bool readRect(const core::Rect& r, int encoding); - bool readSetXCursor(int width, int height, const Point& hotspot); - bool readSetCursor(int width, int height, const Point& hotspot); - bool readSetCursorWithAlpha(int width, int height, const Point& hotspot); - bool readSetVMwareCursor(int width, int height, const Point& hotspot); + bool readSetXCursor(int width, int height, + const core::Point& hotspot); + bool readSetCursor(int width, int height, + const core::Point& hotspot); + bool readSetCursorWithAlpha(int width, int height, + const core::Point& hotspot); + bool readSetVMwareCursor(int width, int height, + const core::Point& hotspot); bool readSetDesktopName(int x, int y, int w, int h); bool readExtendedDesktopSize(int x, int y, int w, int h); bool readLEDState(); @@ -85,12 +88,14 @@ namespace rfb { uint8_t currentMsgType; int nUpdateRectsLeft; - Rect dataRect; + core::Rect dataRect; int rectEncoding; int cursorEncoding; static const int maxCursorSize = 256; }; + } + #endif diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx index 0128c431..c592a25e 100644 --- a/common/rfb/CMsgWriter.cxx +++ b/common/rfb/CMsgWriter.cxx @@ -24,6 +24,9 @@ #include <stdio.h> #include <assert.h> +#include <core/Rect.h> +#include <core/string.h> + #include <rdr/OutStream.h> #include <rdr/MemOutStream.h> #include <rdr/ZlibOutStream.h> @@ -33,10 +36,9 @@ #include <rfb/qemuTypes.h> #include <rfb/clipboardTypes.h> #include <rfb/PixelFormat.h> -#include <rfb/Rect.h> +#include <rfb/ScreenSet.h> #include <rfb/ServerParams.h> #include <rfb/CMsgWriter.h> -#include <rfb/util.h> using namespace rfb; @@ -101,7 +103,8 @@ void CMsgWriter::writeSetDesktopSize(int width, int height, endMsg(); } -void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental) +void CMsgWriter::writeFramebufferUpdateRequest(const core::Rect& r, + bool incremental) { startMsg(msgTypeFramebufferUpdateRequest); os->writeU8(incremental); @@ -173,9 +176,10 @@ void CMsgWriter::writeKeyEvent(uint32_t keysym, uint32_t keycode, bool down) } -void CMsgWriter::writePointerEvent(const Point& pos, uint16_t buttonMask) +void CMsgWriter::writePointerEvent(const core::Point& pos, + uint16_t buttonMask) { - Point p(pos); + core::Point p(pos); bool extendedMouseButtons; if (p.x < 0) p.x = 0; @@ -223,7 +227,7 @@ void CMsgWriter::writeClientCutText(const char* str) if (strchr(str, '\r') != nullptr) throw std::invalid_argument("Invalid carriage return in clipboard data"); - std::string latin1(utf8ToLatin1(str)); + std::string latin1(core::utf8ToLatin1(str)); startMsg(msgTypeClientCutText); os->pad(3); diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h index 9cb4adec..d0378e62 100644 --- a/common/rfb/CMsgWriter.h +++ b/common/rfb/CMsgWriter.h @@ -27,6 +27,11 @@ #include <stdint.h> +namespace core { + struct Point; + struct Rect; +} + namespace rdr { class OutStream; } namespace rfb { @@ -34,8 +39,6 @@ namespace rfb { class PixelFormat; class ServerParams; struct ScreenSet; - struct Point; - struct Rect; class CMsgWriter { public: @@ -48,13 +51,14 @@ namespace rfb { void writeSetEncodings(const std::list<uint32_t> encodings); void writeSetDesktopSize(int width, int height, const ScreenSet& layout); - void writeFramebufferUpdateRequest(const Rect& r,bool incremental); + void writeFramebufferUpdateRequest(const core::Rect& r, + bool incremental); void writeEnableContinuousUpdates(bool enable, int x, int y, int w, int h); void writeFence(uint32_t flags, unsigned len, const uint8_t data[]); void writeKeyEvent(uint32_t keysym, uint32_t keycode, bool down); - void writePointerEvent(const Point& pos, uint16_t buttonMask); + void writePointerEvent(const core::Point& pos, uint16_t buttonMask); void writeClientCutText(const char* str); diff --git a/common/rfb/CSecurityDH.cxx b/common/rfb/CSecurityDH.cxx index 2f0365a6..93cf6b26 100644 --- a/common/rfb/CSecurityDH.cxx +++ b/common/rfb/CSecurityDH.cxx @@ -40,7 +40,6 @@ #include <rdr/OutStream.h> #include <rdr/RandomStream.h> #include <rfb/Exception.h> -#include <os/os.h> using namespace rfb; diff --git a/common/rfb/CSecurityMSLogonII.cxx b/common/rfb/CSecurityMSLogonII.cxx index a5a99286..dfc0b658 100644 --- a/common/rfb/CSecurityMSLogonII.cxx +++ b/common/rfb/CSecurityMSLogonII.cxx @@ -39,7 +39,6 @@ #include <rdr/InStream.h> #include <rdr/OutStream.h> #include <rdr/RandomStream.h> -#include <os/os.h> using namespace rfb; diff --git a/common/rfb/CSecurityRSAAES.cxx b/common/rfb/CSecurityRSAAES.cxx index 0985d0f2..513d5605 100644 --- a/common/rfb/CSecurityRSAAES.cxx +++ b/common/rfb/CSecurityRSAAES.cxx @@ -34,14 +34,17 @@ #include <nettle/bignum.h> #include <nettle/sha1.h> #include <nettle/sha2.h> + +#include <core/LogWriter.h> +#include <core/string.h> + #include <rfb/CSecurityRSAAES.h> #include <rfb/CConnection.h> -#include <rfb/LogWriter.h> #include <rfb/Exception.h> -#include <rfb/util.h> + #include <rdr/AESInStream.h> #include <rdr/AESOutStream.h> -#include <os/os.h> +#include <rdr/RandomStream.h> enum { ReadPublicKey, @@ -55,7 +58,7 @@ const int MaxKeyLength = 8192; using namespace rfb; -static LogWriter vlog("CSecurityRSAAES"); +static core::LogWriter vlog("CSecurityRSAAES"); CSecurityRSAAES::CSecurityRSAAES(CConnection* cc_, uint32_t _secType, int _keySize, bool _isAllEncrypted) @@ -147,12 +150,12 @@ bool CSecurityRSAAES::processMsg() return false; } -static void random_func(void* ctx, size_t length, uint8_t* dst) +static void random_func(void*, size_t length, uint8_t* dst) { - rdr::RandomStream* rs = (rdr::RandomStream*)ctx; - if (!rs->hasData(length)) + rdr::RandomStream rs; + if (!rs.hasData(length)) throw std::runtime_error("Failed to generate random"); - rs->readBytes(dst, length); + rs.readBytes(dst, length); } void CSecurityRSAAES::writePublicKey() @@ -170,7 +173,7 @@ void CSecurityRSAAES::writePublicKey() // set e = 65537 mpz_set_ui(clientPublicKey.e, 65537); if (!rsa_generate_keypair(&clientPublicKey, &clientKey, - &rs, random_func, nullptr, nullptr, + nullptr, random_func, nullptr, nullptr, clientKeyLength, 0)) throw std::runtime_error("Failed to generate key"); clientKeyN = new uint8_t[rsaKeySize]; @@ -226,7 +229,7 @@ void CSecurityRSAAES::verifyServer() sha1_update(&ctx, serverKey.size, serverKeyE); sha1_digest(&ctx, sizeof(f), f); const char *title = "Server key fingerprint"; - std::string text = format( + std::string text = core::format( "The server has provided the following identifying information:\n" "Fingerprint: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n" "Please verify that the information is correct and press \"Yes\". " @@ -237,6 +240,7 @@ void CSecurityRSAAES::verifyServer() void CSecurityRSAAES::writeRandom() { + rdr::RandomStream rs; rdr::OutStream* os = cc->getOutStream(); if (!rs.hasData(keySize / 8)) throw std::runtime_error("Failed to generate random"); diff --git a/common/rfb/CSecurityRSAAES.h b/common/rfb/CSecurityRSAAES.h index af380bd3..ecbfdc4f 100644 --- a/common/rfb/CSecurityRSAAES.h +++ b/common/rfb/CSecurityRSAAES.h @@ -29,7 +29,7 @@ #include <rfb/CSecurity.h> #include <rfb/Security.h> -#include <rdr/RandomStream.h> +namespace core { class IntParameter; } namespace rdr { class InStream; @@ -39,6 +39,7 @@ namespace rdr { } namespace rfb { + class CSecurityRSAAES : public CSecurity { public: CSecurityRSAAES(CConnection* cc, uint32_t secType, @@ -48,7 +49,7 @@ namespace rfb { int getType() const override { return secType; } bool isSecure() const override { return secType == secTypeRA256; } - static IntParameter RSAKeyLength; + static core::IntParameter RSAKeyLength; private: void cleanup(); @@ -86,9 +87,8 @@ namespace rfb { rdr::InStream* rawis; rdr::OutStream* rawos; - - rdr::RandomStream rs; }; + } #endif diff --git a/common/rfb/CSecurityStack.h b/common/rfb/CSecurityStack.h index 521597ec..aec800f9 100644 --- a/common/rfb/CSecurityStack.h +++ b/common/rfb/CSecurityStack.h @@ -21,7 +21,6 @@ #define __RFB_CSECURITYSTACK_H__ #include <rfb/CSecurity.h> -#include <rfb/Security.h> namespace rfb { diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx index 0c10a85d..6eefe73b 100644 --- a/common/rfb/CSecurityTLS.cxx +++ b/common/rfb/CSecurityTLS.cxx @@ -3,7 +3,7 @@ * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team * Copyright (C) 2010 m-privacy GmbH - * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,15 +34,16 @@ #include <unistd.h> #endif +#include <core/LogWriter.h> +#include <core/string.h> +#include <core/xdgdirs.h> + #include <rfb/CSecurityTLS.h> #include <rfb/CConnection.h> -#include <rfb/LogWriter.h> #include <rfb/Exception.h> -#include <rfb/util.h> + #include <rdr/TLSException.h> -#include <rdr/TLSInStream.h> -#include <rdr/TLSOutStream.h> -#include <os/os.h> +#include <rdr/TLSSocket.h> #include <gnutls/x509.h> @@ -50,21 +51,19 @@ using namespace rfb; static const char* configdirfn(const char* fn); -StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate", - configdirfn("x509_ca.pem"), - ConfViewer); -StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file", - configdirfn("x509_crl.pem"), - ConfViewer); +core::StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate", + configdirfn("x509_ca.pem")); +core::StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file", + configdirfn("x509_crl.pem")); -static LogWriter vlog("TLS"); +static core::LogWriter vlog("TLS"); static const char* configdirfn(const char* fn) { static char full_path[PATH_MAX]; const char* configdir; - configdir = os::getvncconfigdir(); + configdir = core::getvncconfigdir(); if (configdir == nullptr) return ""; @@ -75,7 +74,7 @@ static const char* configdirfn(const char* fn) CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon) : CSecurity(cc_), session(nullptr), anon_cred(nullptr), cert_cred(nullptr), - anon(_anon), tlsis(nullptr), tlsos(nullptr), + anon(_anon), tlssock(nullptr), rawis(nullptr), rawos(nullptr) { int err = gnutls_global_init(); @@ -85,27 +84,8 @@ CSecurityTLS::CSecurityTLS(CConnection* cc_, bool _anon) void CSecurityTLS::shutdown() { - if (tlsos) { - try { - if (tlsos->hasBufferedData()) { - tlsos->cork(false); - tlsos->flush(); - if (tlsos->hasBufferedData()) - vlog.error("Failed to flush remaining socket data on close"); - } - } catch (std::exception& e) { - vlog.error("Failed to flush remaining socket data on close: %s", e.what()); - } - } - - if (session) { - int ret; - // FIXME: We can't currently wait for the response, so we only send - // our close and hope for the best - ret = gnutls_bye(session, GNUTLS_SHUT_WR); - if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION)) - vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret)); - } + if (tlssock) + tlssock->shutdown(); if (anon_cred) { gnutls_anon_free_client_credentials(anon_cred); @@ -123,13 +103,9 @@ void CSecurityTLS::shutdown() rawos = nullptr; } - if (tlsis) { - delete tlsis; - tlsis = nullptr; - } - if (tlsos) { - delete tlsos; - tlsos = nullptr; + if (tlssock) { + delete tlssock; + tlssock = nullptr; } if (session) { @@ -171,26 +147,18 @@ bool CSecurityTLS::processMsg() setParam(); - // Create these early as they set up the push/pull functions - // for GnuTLS - tlsis = new rdr::TLSInStream(is, session); - tlsos = new rdr::TLSOutStream(os, session); + tlssock = new rdr::TLSSocket(is, os, session); rawis = is; rawos = os; } - int err; - err = gnutls_handshake(session); - if (err != GNUTLS_E_SUCCESS) { - if (!gnutls_error_is_fatal(err)) { - vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err)); + try { + if (!tlssock->handshake()) return false; - } - - vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err)); + } catch (std::exception&) { shutdown(); - throw rdr::tls_error("TLS Handshake failed", err); + throw; } vlog.debug("TLS handshake completed with %s", @@ -198,33 +166,29 @@ bool CSecurityTLS::processMsg() checkSession(); - cc->setStreams(tlsis, tlsos); + cc->setStreams(&tlssock->inStream(), &tlssock->outStream()); return true; } void CSecurityTLS::setParam() { - static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH"; + static const char kx_anon_priority[] = "+ANON-ECDH:+ANON-DH"; int ret; // Custom priority string specified? if (strcmp(Security::GnuTLSPriority, "") != 0) { - char *prio; + std::string prio; const char *err; - prio = new char[strlen(Security::GnuTLSPriority) + - strlen(kx_anon_priority) + 1]; - - strcpy(prio, Security::GnuTLSPriority); - if (anon) - strcat(prio, kx_anon_priority); - - ret = gnutls_priority_set_direct(session, prio, &err); - - delete [] prio; + prio = (const char*)Security::GnuTLSPriority; + if (anon) { + prio += ":"; + prio += kx_anon_priority; + } + ret = gnutls_priority_set_direct(session, prio.c_str(), &err); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); @@ -234,30 +198,22 @@ void CSecurityTLS::setParam() const char *err; #if GNUTLS_VERSION_NUMBER >= 0x030603 - // gnutls_set_default_priority_appends() expects a normal priority string that - // doesn't start with ":". - ret = gnutls_set_default_priority_append(session, kx_anon_priority + 1, &err, 0); + ret = gnutls_set_default_priority_append(session, kx_anon_priority, &err, 0); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); throw rdr::tls_error("gnutls_set_default_priority_append()", ret); } #else + std::string prio; + // We don't know what the system default priority is, so we guess // it's what upstream GnuTLS has - static const char gnutls_default_priority[] = "NORMAL"; - char *prio; - - prio = new char[malloc(strlen(gnutls_default_priority) + - strlen(kx_anon_priority) + 1]; - - strcpy(prio, gnutls_default_priority); - strcat(prio, kx_anon_priority); - - ret = gnutls_priority_set_direct(session, prio, &err); - - delete [] prio; + prio = "NORMAL"; + prio += ":"; + prio += kx_anon_priority; + ret = gnutls_priority_set_direct(session, prio.c_str(), &err); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); @@ -277,6 +233,10 @@ void CSecurityTLS::setParam() vlog.debug("Anonymous session has been set"); } else { + const char* hostname; + size_t len; + bool valid; + ret = gnutls_certificate_allocate_credentials(&cert_cred); if (ret != GNUTLS_E_SUCCESS) throw rdr::tls_error("gnutls_certificate_allocate_credentials()", ret); @@ -294,10 +254,22 @@ void CSecurityTLS::setParam() if (ret != GNUTLS_E_SUCCESS) throw rdr::tls_error("gnutls_credentials_set()", ret); - if (gnutls_server_name_set(session, GNUTLS_NAME_DNS, - client->getServerName(), - strlen(client->getServerName())) != GNUTLS_E_SUCCESS) - vlog.error("Failed to configure the server name for TLS handshake"); + // Only DNS hostnames are allowed, and some servers will reject the + // connection if we provide anything else (e.g. an IPv6 address) + hostname = client->getServerName(); + len = strlen(hostname); + valid = true; + for (size_t i = 0; i < len; i++) { + if (!isalnum(hostname[i]) && hostname[i] != '.') + valid = false; + } + + if (valid) { + if (gnutls_server_name_set(session, GNUTLS_NAME_DNS, + client->getServerName(), + strlen(client->getServerName())) != GNUTLS_E_SUCCESS) + vlog.error("Failed to configure the server name for TLS handshake"); + } vlog.debug("X509 session has been set"); } @@ -324,12 +296,16 @@ void CSecurityTLS::checkSession() if (anon) return; - if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_UNSUPPORTED_CERTIFICATE); throw protocol_error("Unsupported certificate type"); + } err = gnutls_certificate_verify_peers2(session, &status); if (err != 0) { vlog.error("Server certificate verification failed: %s", gnutls_strerror(err)); + gnutls_alert_send_appropriate(session, err); throw rdr::tls_error("Server certificate verification()", err); } @@ -346,23 +322,29 @@ void CSecurityTLS::checkSession() GNUTLS_CRT_X509, &status_str, 0); - if (err != GNUTLS_E_SUCCESS) + if (err != GNUTLS_E_SUCCESS) { + gnutls_alert_send_appropriate(session, err); throw rdr::tls_error("Failed to get certificate error description", err); + } error = (const char*)status_str.data; gnutls_free(status_str.data); - throw protocol_error(format("Invalid server certificate: %s", - error.c_str())); + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); + throw protocol_error( + core::format("Invalid server certificate: %s", error.c_str())); } err = gnutls_certificate_verification_status_print(status, GNUTLS_CRT_X509, &status_str, 0); - if (err != GNUTLS_E_SUCCESS) + if (err != GNUTLS_E_SUCCESS) { + gnutls_alert_send_appropriate(session, err); throw rdr::tls_error("Failed to get certificate error description", err); + } vlog.info("Server certificate errors: %s", status_str.data); @@ -372,16 +354,21 @@ void CSecurityTLS::checkSession() /* Process overridable errors later */ cert_list = gnutls_certificate_get_peers(session, &cert_list_size); - if (!cert_list_size) + if (!cert_list_size) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_UNSUPPORTED_CERTIFICATE); throw protocol_error("Empty certificate chain"); + } /* Process only server's certificate, not issuer's certificate */ gnutls_x509_crt_t crt; gnutls_x509_crt_init(&crt); err = gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER); - if (err != GNUTLS_E_SUCCESS) + if (err != GNUTLS_E_SUCCESS) { + gnutls_alert_send_appropriate(session, err); throw rdr::tls_error("Failed to decode server certificate", err); + } if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) { vlog.info("Server certificate doesn't match given server name"); @@ -398,7 +385,7 @@ void CSecurityTLS::checkSession() /* Certificate has some user overridable problems, so TOFU time */ - hostsDir = os::getvncstatedir(); + hostsDir = core::getvncstatedir(); if (hostsDir == nullptr) { throw std::runtime_error("Could not obtain VNC state directory " "path for known hosts storage"); @@ -420,12 +407,15 @@ void CSecurityTLS::checkSession() if ((known != GNUTLS_E_NO_CERTIFICATE_FOUND) && (known != GNUTLS_E_CERTIFICATE_KEY_MISMATCH)) { + gnutls_alert_send_appropriate(session, known); throw rdr::tls_error("Could not load known hosts database", known); } err = gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info); - if (err != GNUTLS_E_SUCCESS) + if (err != GNUTLS_E_SUCCESS) { + gnutls_alert_send_appropriate(session, known); throw rdr::tls_error("Could not find certificate to display", err); + } len = strlen((char*)info.data); for (size_t i = 0; i < len - 1; i++) { @@ -443,21 +433,24 @@ void CSecurityTLS::checkSession() if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA)) { - text = format("This certificate has been signed by an unknown " - "authority:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This certificate has been signed by an unknown authority:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unknown certificate issuer", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_UNKNOWN_CA); throw auth_cancelled(); + } status &= ~(GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | @@ -465,82 +458,101 @@ void CSecurityTLS::checkSession() } if (status & GNUTLS_CERT_NOT_ACTIVATED) { - text = format("This certificate is not yet valid:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This certificate is not yet valid:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); + if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Certificate is not yet valid", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_NOT_ACTIVATED; } if (status & GNUTLS_CERT_EXPIRED) { - text = format("This certificate has expired:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This certificate has expired:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Expired certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_EXPIRED; } if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { - text = format("This certificate uses an insecure algorithm:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This certificate uses an insecure algorithm:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Insecure certificate algorithm", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_INSECURE_ALGORITHM; } if (status != 0) { vlog.error("Unhandled certificate problems: 0x%x", status); + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw std::logic_error("Unhandled certificate problems"); } if (!hostname_match) { - text = format("The specified hostname \"%s\" does not match the " - "certificate provided by the server:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", client->getServerName(), info.data); + text = core::format( + "The specified hostname \"%s\" does not match the certificate " + "provided by the server:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + client->getServerName(), info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Certificate hostname mismatch", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } } } else if (known == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) { std::string text; @@ -551,22 +563,26 @@ void CSecurityTLS::checkSession() if (status & (GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_SIGNER_NOT_CA)) { - text = format("This host is previously known with a different " - "certificate, and the new certificate has been " - "signed by an unknown authority:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This host is previously known with a different certificate, " + "and the new certificate has been signed by an unknown " + "authority:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unexpected server certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_UNKNOWN_CA); throw auth_cancelled(); + } status &= ~(GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNER_NOT_FOUND | @@ -574,91 +590,105 @@ void CSecurityTLS::checkSession() } if (status & GNUTLS_CERT_NOT_ACTIVATED) { - text = format("This host is previously known with a different " - "certificate, and the new certificate is not yet " - "valid:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This host is previously known with a different certificate, " + "and the new certificate is not yet valid:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unexpected server certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_NOT_ACTIVATED; } if (status & GNUTLS_CERT_EXPIRED) { - text = format("This host is previously known with a different " - "certificate, and the new certificate has " - "expired:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This host is previously known with a different certificate, " + "and the new certificate has expired:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unexpected server certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_EXPIRED; } if (status & GNUTLS_CERT_INSECURE_ALGORITHM) { - text = format("This host is previously known with a different " - "certificate, and the new certificate uses an " - "insecure algorithm:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", info.data); + text = core::format( + "This host is previously known with a different certificate, " + "and the new certificate uses an insecure algorithm:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unexpected server certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } status &= ~GNUTLS_CERT_INSECURE_ALGORITHM; } if (status != 0) { vlog.error("Unhandled certificate problems: 0x%x", status); + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw std::logic_error("Unhandled certificate problems"); } if (!hostname_match) { - text = format("This host is previously known with a different " - "certificate, and the specified hostname \"%s\" " - "does not match the new certificate provided by " - "the server:\n" - "\n" - "%s\n" - "\n" - "Someone could be trying to impersonate the site " - "and you should not continue.\n" - "\n" - "Do you want to make an exception for this " - "server?", client->getServerName(), info.data); + text = core::format( + "This host is previously known with a different certificate, " + "and the specified hostname \"%s\" does not match the new " + "certificate provided by the server:\n" + "\n" + "%s\n" + "\n" + "Someone could be trying to impersonate the site and you " + "should not continue.\n" + "\n" + "Do you want to make an exception for this server?", + client->getServerName(), info.data); if (!cc->showMsgBox(MsgBoxFlags::M_YESNO, "Unexpected server certificate", - text.c_str())) + text.c_str())) { + gnutls_alert_send(session, GNUTLS_AL_FATAL, + GNUTLS_A_BAD_CERTIFICATE); throw auth_cancelled(); + } } } diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h index 2464cb6c..51b7dac1 100644 --- a/common/rfb/CSecurityTLS.h +++ b/common/rfb/CSecurityTLS.h @@ -2,6 +2,7 @@ * Copyright (C) 2004 Red Hat Inc. * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,8 +35,7 @@ namespace rdr { class InStream; class OutStream; - class TLSInStream; - class TLSOutStream; + class TLSSocket; } namespace rfb { @@ -47,8 +47,8 @@ namespace rfb { int getType() const override { return anon ? secTypeTLSNone : secTypeX509None; } bool isSecure() const override { return !anon; } - static StringParameter X509CA; - static StringParameter X509CRL; + static core::StringParameter X509CA; + static core::StringParameter X509CRL; protected: void shutdown(); @@ -63,8 +63,7 @@ namespace rfb { gnutls_certificate_credentials_t cert_cred; bool anon; - rdr::TLSInStream* tlsis; - rdr::TLSOutStream* tlsos; + rdr::TLSSocket* tlssock; rdr::InStream* rawis; rdr::OutStream* rawos; diff --git a/common/rfb/CSecurityVeNCrypt.cxx b/common/rfb/CSecurityVeNCrypt.cxx index 1b6ecf22..a6e30947 100644 --- a/common/rfb/CSecurityVeNCrypt.cxx +++ b/common/rfb/CSecurityVeNCrypt.cxx @@ -29,16 +29,17 @@ #include <algorithm> #include <list> +#include <core/LogWriter.h> + #include <rfb/Exception.h> #include <rdr/InStream.h> #include <rdr/OutStream.h> #include <rfb/CConnection.h> #include <rfb/CSecurityVeNCrypt.h> -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("CVeNCrypt"); +static core::LogWriter vlog("CVeNCrypt"); CSecurityVeNCrypt::CSecurityVeNCrypt(CConnection* cc_, SecurityClient* sec) diff --git a/common/rfb/CSecurityVeNCrypt.h b/common/rfb/CSecurityVeNCrypt.h index f73e7927..8e2c6d5e 100644 --- a/common/rfb/CSecurityVeNCrypt.h +++ b/common/rfb/CSecurityVeNCrypt.h @@ -28,10 +28,11 @@ #include <stdint.h> #include <rfb/CSecurity.h> -#include <rfb/SecurityClient.h> namespace rfb { + class SecurityClient; + class CSecurityVeNCrypt : public CSecurity { public: diff --git a/common/rfb/ClientParams.cxx b/common/rfb/ClientParams.cxx index e5fd105e..514b0b4e 100644 --- a/common/rfb/ClientParams.cxx +++ b/common/rfb/ClientParams.cxx @@ -24,14 +24,20 @@ #include <stdexcept> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rfb/encodings.h> #include <rfb/ledStates.h> #include <rfb/clipboardTypes.h> #include <rfb/ClientParams.h> -#include <rfb/util.h> +#include <rfb/Cursor.h> +#include <rfb/ScreenSet.h> using namespace rfb; +static core::LogWriter vlog("ClientParams"); + ClientParams::ClientParams() : majorVersion(0), minorVersion(0), compressLevel(2), qualityLevel(-1), fineQualityLevel(-1), @@ -41,7 +47,11 @@ ClientParams::ClientParams() { setName(""); - cursor_ = new Cursor(0, 0, Point(), nullptr); + screenLayout_ = new ScreenSet(); + + pf_ = new PixelFormat(); + + cursor_ = new Cursor(0, 0, {}, nullptr); clipFlags = clipboardUTF8 | clipboardRTF | clipboardHTML | clipboardRequest | clipboardNotify | clipboardProvide; @@ -51,7 +61,9 @@ ClientParams::ClientParams() ClientParams::~ClientParams() { + delete screenLayout_; delete cursor_; + delete pf_; } void ClientParams::setDimensions(int width, int height) @@ -63,17 +75,25 @@ void ClientParams::setDimensions(int width, int height) void ClientParams::setDimensions(int width, int height, const ScreenSet& layout) { - if (!layout.validate(width, height)) + if (!layout.validate(width, height)) { + char buffer[2048]; + vlog.debug("Invalid screen layout for %dx%d:", width, height); + layout.print(buffer, sizeof(buffer)); + vlog.debug("%s", buffer); + throw std::invalid_argument("Attempted to configure an invalid screen layout"); + } width_ = width; height_ = height; - screenLayout_ = layout; + delete screenLayout_; + screenLayout_ = new ScreenSet(layout); } void ClientParams::setPF(const PixelFormat& pf) { - pf_ = pf; + delete pf_; + pf_ = new PixelFormat(pf); if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) throw std::invalid_argument("setPF: Not 8, 16 or 32 bpp?"); @@ -90,7 +110,7 @@ void ClientParams::setCursor(const Cursor& other) cursor_ = new Cursor(other); } -void ClientParams::setCursorPos(const Point& pos) +void ClientParams::setCursorPos(const core::Point& pos) { cursorPos_ = pos; } @@ -162,7 +182,7 @@ uint32_t ClientParams::clipboardSize(unsigned int format) const return clipSizes[i]; } - throw std::invalid_argument(rfb::format("Invalid clipboard format 0x%x", format)); + throw std::invalid_argument(core::format("Invalid clipboard format 0x%x", format)); } void ClientParams::setClipboardCaps(uint32_t flags, const uint32_t* lengths) @@ -236,4 +256,4 @@ bool ClientParams::supportsExtendedMouseButtons() const if (supportsEncoding(pseudoEncodingExtendedMouseButtons)) return true; return false; -}
\ No newline at end of file +} diff --git a/common/rfb/ClientParams.h b/common/rfb/ClientParams.h index f715c47f..0910181b 100644 --- a/common/rfb/ClientParams.h +++ b/common/rfb/ClientParams.h @@ -28,12 +28,14 @@ #include <stdint.h> -#include <rfb/Cursor.h> -#include <rfb/PixelFormat.h> -#include <rfb/ScreenSet.h> +#include <core/Rect.h> namespace rfb { + class Cursor; + class PixelFormat; + struct ScreenSet; + const int subsampleUndefined = -1; const int subsampleNone = 0; const int subsampleGray = 1; @@ -66,11 +68,11 @@ namespace rfb { int width() const { return width_; } int height() const { return height_; } - const ScreenSet& screenLayout() const { return screenLayout_; } + const ScreenSet& screenLayout() const { return *screenLayout_; } void setDimensions(int width, int height); void setDimensions(int width, int height, const ScreenSet& layout); - const PixelFormat& pf() const { return pf_; } + const PixelFormat& pf() const { return *pf_; } void setPF(const PixelFormat& pf); const char* name() const { return name_.c_str(); } @@ -79,8 +81,8 @@ namespace rfb { const Cursor& cursor() const { return *cursor_; } void setCursor(const Cursor& cursor); - const Point& cursorPos() const { return cursorPos_; } - void setCursorPos(const Point& pos); + const core::Point& cursorPos() const { return cursorPos_; } + void setCursorPos(const core::Point& pos); bool supportsEncoding(int32_t encoding) const; @@ -112,12 +114,12 @@ namespace rfb { int width_; int height_; - ScreenSet screenLayout_; + ScreenSet* screenLayout_; - PixelFormat pf_; + PixelFormat* pf_; std::string name_; Cursor* cursor_; - Point cursorPos_; + core::Point cursorPos_; std::set<int32_t> encodings_; unsigned int ledState_; uint32_t clipFlags; diff --git a/common/rfb/ComparingUpdateTracker.cxx b/common/rfb/ComparingUpdateTracker.cxx index dab5e6aa..a89c3ec3 100644 --- a/common/rfb/ComparingUpdateTracker.cxx +++ b/common/rfb/ComparingUpdateTracker.cxx @@ -22,17 +22,18 @@ #include <stdio.h> #include <string.h> + +#include <algorithm> #include <vector> -#include <rfb/Exception.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> +#include <core/LogWriter.h> +#include <core/string.h> #include <rfb/ComparingUpdateTracker.h> using namespace rfb; -static LogWriter vlog("ComparingUpdateTracker"); +static core::LogWriter vlog("ComparingUpdateTracker"); ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer) : fb(buffer), oldFb(fb->getPF(), 0, 0), firstCompare(true), @@ -50,8 +51,8 @@ ComparingUpdateTracker::~ComparingUpdateTracker() bool ComparingUpdateTracker::compare() { - std::vector<Rect> rects; - std::vector<Rect>::iterator i; + std::vector<core::Rect> rects; + std::vector<core::Rect>::iterator i; if (!enabled) return false; @@ -62,7 +63,7 @@ bool ComparingUpdateTracker::compare() oldFb.setSize(fb->width(), fb->height()); for (int y=0; y<fb->height(); y+=BLOCK_SIZE) { - Rect pos(0, y, fb->width(), __rfbmin(fb->height(), y+BLOCK_SIZE)); + core::Rect pos(0, y, fb->width(), std::min(fb->height(), y+BLOCK_SIZE)); int srcStride; const uint8_t* srcData = fb->getBuffer(pos, &srcStride); oldFb.imageRect(pos, srcData, srcStride); @@ -79,7 +80,7 @@ bool ComparingUpdateTracker::compare() changed.get_rects(&rects); - Region newChanged; + core::Region newChanged; for (i = rects.begin(); i != rects.end(); i++) compareRect(*i, &newChanged); @@ -111,10 +112,11 @@ void ComparingUpdateTracker::disable() firstCompare = true; } -void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged) +void ComparingUpdateTracker::compareRect(const core::Rect& r, + core::Region* newChanged) { if (!r.enclosed_by(fb->getRect())) { - Rect safe; + core::Rect safe; // Crop the rect and try again safe = r.intersect(fb->getRect()); if (!safe.is_empty()) @@ -134,20 +136,20 @@ void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged) for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE) { // Get a strip of the source buffer - Rect pos(r.tl.x, blockTop, r.br.x, __rfbmin(r.br.y, blockTop+BLOCK_SIZE)); + core::Rect pos(r.tl.x, blockTop, r.br.x, std::min(r.br.y, blockTop+BLOCK_SIZE)); int fbStride; const uint8_t* newBlockPtr = fb->getBuffer(pos, &fbStride); int newStrideBytes = fbStride * bytesPerPixel; uint8_t* oldBlockPtr = oldData; - int blockBottom = __rfbmin(blockTop+BLOCK_SIZE, r.br.y); + int blockBottom = std::min(blockTop+BLOCK_SIZE, r.br.y); for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE) { const uint8_t* newPtr = newBlockPtr; uint8_t* oldPtr = oldBlockPtr; - int blockRight = __rfbmin(blockLeft+BLOCK_SIZE, r.br.x); + int blockRight = std::min(blockLeft+BLOCK_SIZE, r.br.x); int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel; // Scan the block top to bottom, to identify the first row of change @@ -223,8 +225,10 @@ void ComparingUpdateTracker::compareRect(const Rect& r, Region* newChanged) } endOfChangeRight: - // Block change extends from (changeLeft, y) to (changeRight, y + changeHeight) - newChanged->assign_union(Region(Rect(changeLeft, y, changeRight, y + changeHeight))); + // Block change extends from (changeLeft, y) to (changeRight, + // y + changeHeight) + newChanged->assign_union({{changeLeft, y, + changeRight, y + changeHeight}}); // Copy the change from fb to oldFb to allow future changes to be identified for (int row = 0; row < changeHeight; row++) @@ -258,10 +262,12 @@ void ComparingUpdateTracker::logStats() ratio = (double)totalPixels / missedPixels; - vlog.info("%s in / %s out", - siPrefix(totalPixels, "pixels").c_str(), - siPrefix(missedPixels, "pixels").c_str()); - vlog.info("(1:%g ratio)", ratio); + // FIXME: This gets spammed on each session resize, so we'll have to + // keep it on a debug level for now + vlog.debug("%s in / %s out", + core::siPrefix(totalPixels, "pixels").c_str(), + core::siPrefix(missedPixels, "pixels").c_str()); + vlog.debug("(1:%g ratio)", ratio); totalPixels = missedPixels = 0; } diff --git a/common/rfb/ComparingUpdateTracker.h b/common/rfb/ComparingUpdateTracker.h index ca1dcc30..dbe7a4ef 100644 --- a/common/rfb/ComparingUpdateTracker.h +++ b/common/rfb/ComparingUpdateTracker.h @@ -19,6 +19,7 @@ #ifndef __RFB_COMPARINGUPDATETRACKER_H__ #define __RFB_COMPARINGUPDATETRACKER_H__ +#include <rfb/PixelBuffer.h> #include <rfb/UpdateTracker.h> namespace rfb { @@ -44,7 +45,7 @@ namespace rfb { void logStats(); private: - void compareRect(const Rect& r, Region* newchanged); + void compareRect(const core::Rect& r, core::Region* newchanged); PixelBuffer* fb; ManagedPixelBuffer oldFb; bool firstCompare; diff --git a/common/rfb/Configuration.cxx b/common/rfb/Configuration.cxx deleted file mode 100644 index 72947df1..00000000 --- a/common/rfb/Configuration.cxx +++ /dev/null @@ -1,469 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2004-2005 Cendio AB. - * Copyright 2017 Peter Astrand <astrand@cendio.se> for Cendio AB - * Copyright 2011-2022 Pierre Ossman for Cendio AB - * - * 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. - */ - -// -=- Configuration.cxx - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <assert.h> -#include <stdlib.h> -#include <ctype.h> -#include <string.h> - -#include <stdexcept> - -#include <os/Mutex.h> - -#include <rfb/util.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> - -#define LOCK_CONFIG os::AutoMutex a(mutex) - -#include <rdr/HexOutStream.h> -#include <rdr/HexInStream.h> - -using namespace rfb; - -static LogWriter vlog("Config"); - - -// -=- The Global/server/viewer Configuration objects -Configuration* Configuration::global_ = nullptr; -Configuration* Configuration::server_ = nullptr; -Configuration* Configuration::viewer_ = nullptr; - -Configuration* Configuration::global() { - if (!global_) - global_ = new Configuration("Global"); - return global_; -} - -Configuration* Configuration::server() { - if (!server_) - server_ = new Configuration("Server"); - return server_; -} - -Configuration* Configuration::viewer() { - if (!viewer_) - viewer_ = new Configuration("Viewer"); - return viewer_; -} - -// -=- Configuration implementation - -bool Configuration::set(const char* n, const char* v, bool immutable) { - return set(n, strlen(n), v, immutable); -} - -bool Configuration::set(const char* paramName, int len, - const char* val, bool immutable) -{ - VoidParameter* current = head; - while (current) { - if ((int)strlen(current->getName()) == len && - strncasecmp(current->getName(), paramName, len) == 0) - { - bool b = current->setParam(val); - if (b && immutable) - current->setImmutable(); - return b; - } - current = current->_next; - } - return _next ? _next->set(paramName, len, val, immutable) : false; -} - -bool Configuration::set(const char* config, bool immutable) { - bool hyphen = false; - if (config[0] == '-') { - hyphen = true; - config++; - if (config[0] == '-') config++; // allow gnu-style --<option> - } - const char* equal = strchr(config, '='); - if (equal) { - return set(config, equal-config, equal+1, immutable); - } else if (hyphen) { - VoidParameter* current = head; - while (current) { - if (strcasecmp(current->getName(), config) == 0) { - bool b = current->setParam(); - if (b && immutable) - current->setImmutable(); - return b; - } - current = current->_next; - } - } - return _next ? _next->set(config, immutable) : false; -} - -VoidParameter* Configuration::get(const char* param) -{ - VoidParameter* current = head; - while (current) { - if (strcasecmp(current->getName(), param) == 0) - return current; - current = current->_next; - } - return _next ? _next->get(param) : nullptr; -} - -void Configuration::list(int width, int nameWidth) { - VoidParameter* current = head; - - fprintf(stderr, "%s Parameters:\n", name.c_str()); - while (current) { - std::string def_str = current->getDefaultStr(); - const char* desc = current->getDescription(); - fprintf(stderr," %-*s -", nameWidth, current->getName()); - int column = strlen(current->getName()); - if (column < nameWidth) column = nameWidth; - column += 4; - while (true) { - const char* s = strchr(desc, ' '); - int wordLen; - if (s) wordLen = s-desc; - else wordLen = strlen(desc); - - if (column + wordLen + 1 > width) { - fprintf(stderr,"\n%*s",nameWidth+4,""); - column = nameWidth+4; - } - fprintf(stderr," %.*s",wordLen,desc); - column += wordLen + 1; - desc += wordLen + 1; - if (!s) break; - } - - if (!def_str.empty()) { - if (column + (int)def_str.size() + 11 > width) - fprintf(stderr,"\n%*s",nameWidth+4,""); - fprintf(stderr," (default=%s)\n",def_str.c_str()); - } else { - fprintf(stderr,"\n"); - } - current = current->_next; - } - - if (_next) - _next->list(width, nameWidth); -} - - -bool Configuration::remove(const char* param) { - VoidParameter *current = head; - VoidParameter **prevnext = &head; - - while (current) { - if (strcasecmp(current->getName(), param) == 0) { - *prevnext = current->_next; - return true; - } - prevnext = ¤t->_next; - current = current->_next; - } - - return false; -} - - -// -=- VoidParameter - -VoidParameter::VoidParameter(const char* name_, const char* desc_, - ConfigurationObject co) - : immutable(false), name(name_), description(desc_) -{ - Configuration *conf = nullptr; - - switch (co) { - case ConfGlobal: conf = Configuration::global(); - break; - case ConfServer: conf = Configuration::server(); - break; - case ConfViewer: conf = Configuration::viewer(); - break; - } - - _next = conf->head; - conf->head = this; - - mutex = new os::Mutex(); -} - -VoidParameter::~VoidParameter() { - delete mutex; -} - -const char* -VoidParameter::getName() const { - return name; -} - -const char* -VoidParameter::getDescription() const { - return description; -} - -bool VoidParameter::setParam() { - return false; -} - -bool VoidParameter::isBool() const { - return false; -} - -void -VoidParameter::setImmutable() { - vlog.debug("Set immutable %s", getName()); - immutable = true; -} - -// -=- AliasParameter - -AliasParameter::AliasParameter(const char* name_, const char* desc_, - VoidParameter* param_, ConfigurationObject co) - : VoidParameter(name_, desc_, co), param(param_) { -} - -bool -AliasParameter::setParam(const char* v) { - return param->setParam(v); -} - -bool AliasParameter::setParam() { - return param->setParam(); -} - -std::string AliasParameter::getDefaultStr() const { - return ""; -} - -std::string AliasParameter::getValueStr() const { - return param->getValueStr(); -} - -bool AliasParameter::isBool() const { - return param->isBool(); -} - -void -AliasParameter::setImmutable() { - vlog.debug("Set immutable %s (Alias)", getName()); - param->setImmutable(); -} - - -// -=- BoolParameter - -BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v, - ConfigurationObject co) -: VoidParameter(name_, desc_, co), value(v), def_value(v) { -} - -bool -BoolParameter::setParam(const char* v) { - if (immutable) return true; - - if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0 - || strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0) - setParam(true); - else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0 - || strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0) - setParam(false); - else { - vlog.error("Bool parameter %s: Invalid value '%s'", getName(), v); - return false; - } - - return true; -} - -bool BoolParameter::setParam() { - setParam(true); - return true; -} - -void BoolParameter::setParam(bool b) { - if (immutable) return; - value = b; - vlog.debug("Set %s(Bool) to %d", getName(), value); -} - -std::string BoolParameter::getDefaultStr() const { - return def_value ? "1" : "0"; -} - -std::string BoolParameter::getValueStr() const { - return value ? "1" : "0"; -} - -bool BoolParameter::isBool() const { - return true; -} - -BoolParameter::operator bool() const { - return value; -} - -// -=- IntParameter - -IntParameter::IntParameter(const char* name_, const char* desc_, int v, - int minValue_, int maxValue_, ConfigurationObject co) - : VoidParameter(name_, desc_, co), value(v), def_value(v), - minValue(minValue_), maxValue(maxValue_) -{ -} - -bool -IntParameter::setParam(const char* v) { - if (immutable) return true; - return setParam(strtol(v, nullptr, 0)); -} - -bool -IntParameter::setParam(int v) { - if (immutable) return true; - vlog.debug("Set %s(Int) to %d", getName(), v); - if (v < minValue || v > maxValue) - return false; - value = v; - return true; -} - -std::string IntParameter::getDefaultStr() const { - char result[16]; - sprintf(result, "%d", def_value); - return result; -} - -std::string IntParameter::getValueStr() const { - char result[16]; - sprintf(result, "%d", value); - return result; -} - -IntParameter::operator int() const { - return value; -} - -// -=- StringParameter - -StringParameter::StringParameter(const char* name_, const char* desc_, - const char* v, ConfigurationObject co) - : VoidParameter(name_, desc_, co), value(v), def_value(v) -{ - if (!v) { - vlog.error("Default value <null> for %s not allowed",name_); - throw std::invalid_argument("Default value <null> not allowed"); - } -} - -StringParameter::~StringParameter() { -} - -bool StringParameter::setParam(const char* v) { - LOCK_CONFIG; - if (immutable) return true; - if (!v) - throw std::invalid_argument("setParam(<null>) not allowed"); - vlog.debug("Set %s(String) to %s", getName(), v); - value = v; - return true; -} - -std::string StringParameter::getDefaultStr() const { - return def_value; -} - -std::string StringParameter::getValueStr() const { - LOCK_CONFIG; - return value; -} - -StringParameter::operator const char *() const { - return value.c_str(); -} - -// -=- BinaryParameter - -BinaryParameter::BinaryParameter(const char* name_, const char* desc_, - const uint8_t* v, size_t l, ConfigurationObject co) -: VoidParameter(name_, desc_, co), - value(nullptr), length(0), def_value(nullptr), def_length(0) { - if (l) { - assert(v); - value = new uint8_t[l]; - length = l; - memcpy(value, v, l); - def_value = new uint8_t[l]; - def_length = l; - memcpy(def_value, v, l); - } -} -BinaryParameter::~BinaryParameter() { - delete [] value; - delete [] def_value; -} - -bool BinaryParameter::setParam(const char* v) { - if (immutable) return true; - std::vector<uint8_t> newValue = hexToBin(v, strlen(v)); - if (newValue.empty() && strlen(v) > 0) - return false; - setParam(newValue.data(), newValue.size()); - return true; -} - -void BinaryParameter::setParam(const uint8_t* v, size_t len) { - LOCK_CONFIG; - if (immutable) return; - vlog.debug("Set %s(Binary)", getName()); - delete [] value; - value = nullptr; - length = 0; - if (len) { - assert(v); - value = new uint8_t[len]; - length = len; - memcpy(value, v, len); - } -} - -std::string BinaryParameter::getDefaultStr() const { - return binToHex(def_value, def_length); -} - -std::string BinaryParameter::getValueStr() const { - LOCK_CONFIG; - return binToHex(value, length); -} - -std::vector<uint8_t> BinaryParameter::getData() const { - LOCK_CONFIG; - std::vector<uint8_t> out(length); - memcpy(out.data(), value, length); - return out; -} diff --git a/common/rfb/Congestion.cxx b/common/rfb/Congestion.cxx index 94f07055..46bae00d 100644 --- a/common/rfb/Congestion.cxx +++ b/common/rfb/Congestion.cxx @@ -49,9 +49,10 @@ #include <linux/sockios.h> #endif +#include <core/LogWriter.h> +#include <core/time.h> + #include <rfb/Congestion.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> // Debug output on what the congestion control is up to #undef CONGESTION_DEBUG @@ -78,7 +79,7 @@ static inline bool isAfter(unsigned a, unsigned b) { return a != b && a - b <= UINT_MAX / 2; } -static LogWriter vlog("Congestion"); +static core::LogWriter vlog("Congestion"); Congestion::Congestion() : lastPosition(0), extraBuffer(0), @@ -99,7 +100,7 @@ Congestion::~Congestion() void Congestion::updatePosition(unsigned pos) { struct timeval now; - unsigned delta, consumed; + unsigned idle, delta, consumed; gettimeofday(&now, nullptr); @@ -110,15 +111,17 @@ void Congestion::updatePosition(unsigned pos) // Idle for too long? // We use a very crude RTO calculation in order to keep things simple // FIXME: should implement RFC 2861 - if (msBetween(&lastSent, &now) > __rfbmax(baseRTT*2, 100)) { + idle = core::msBetween(&lastSent, &now); + if (idle > 100 && idle > baseRTT*2) { #ifdef CONGESTION_DEBUG vlog.debug("Connection idle for %d ms, resetting congestion control", - msBetween(&lastSent, &now)); + idle); #endif // Close congestion window and redo wire latency measurement - congWindow = __rfbmin(INITIAL_WINDOW, congWindow); + if (congWindow > INITIAL_WINDOW) + congWindow = INITIAL_WINDOW; baseRTT = -1; measurements = 0; gettimeofday(&lastAdjustment, nullptr); @@ -132,7 +135,7 @@ void Congestion::updatePosition(unsigned pos) // (we cannot do this until we have a RTT measurement though) if (baseRTT != (unsigned)-1) { extraBuffer += delta; - consumed = msBetween(&lastUpdate, &now) * congWindow / baseRTT; + consumed = core::msBetween(&lastUpdate, &now) * congWindow / baseRTT; if (extraBuffer < consumed) extraBuffer = 0; else @@ -174,7 +177,7 @@ void Congestion::gotPong() lastPong = rttInfo; lastPongArrival = now; - rtt = msBetween(&rttInfo.tv, &now); + rtt = core::msBetween(&rttInfo.tv, &now); if (rtt < 1) rtt = 1; @@ -184,7 +187,7 @@ void Congestion::gotPong() // Pings sent before the last adjustment aren't interesting as they // aren't a measurement of the current congestion window - if (isBefore(&rttInfo.tv, &lastAdjustment)) + if (core::isBefore(&rttInfo.tv, &lastAdjustment)) return; // Estimate added delay because of overtaxed buffers (see above) @@ -249,7 +252,7 @@ int Congestion::getUncongestedETA() prevPing = &lastPong; eta = 0; - elapsed = msSince(&lastPongArrival); + elapsed = core::msSince(&lastPongArrival); // Walk the ping queue and figure out which one we are waiting for to // get to an uncongested state @@ -268,7 +271,7 @@ int Congestion::getUncongestedETA() curPing = *iter; } - etaNext = msBetween(&prevPing->tv, &curPing.tv); + etaNext = core::msBetween(&prevPing->tv, &curPing.tv); // Compensate for buffering delays delay = curPing.extra * baseRTT / congWindow; etaNext += delay; @@ -349,7 +352,7 @@ unsigned Congestion::getExtraBuffer() if (baseRTT == (unsigned)-1) return 0; - elapsed = msSince(&lastUpdate); + elapsed = core::msSince(&lastUpdate); consumed = elapsed * congWindow / baseRTT; if (consumed >= extraBuffer) @@ -389,7 +392,7 @@ unsigned Congestion::getInFlight() // completely. Look at the next ping that should arrive and figure // out how far behind it should be and interpolate the positions. - etaNext = msBetween(&lastPong.tv, &nextPong.tv); + etaNext = core::msBetween(&lastPong.tv, &nextPong.tv); // Compensate for buffering delays delay = nextPong.extra * baseRTT / congWindow; etaNext += delay; @@ -399,7 +402,7 @@ unsigned Congestion::getInFlight() else etaNext -= delay; - elapsed = msSince(&lastPongArrival); + elapsed = core::msSince(&lastPongArrival); // The pong should be here any second. Be optimistic and assume // we can already use its value. @@ -430,7 +433,7 @@ void Congestion::updateCongestion() diff = minRTT - baseRTT; - if (diff > __rfbmax(100, baseRTT/2)) { + if (diff > 100 && diff > baseRTT/2) { // We have no way of detecting loss, so assume massive latency // spike means packet loss. Adjust the window and go directly // to congestion avoidance. diff --git a/common/rfb/CopyRectDecoder.cxx b/common/rfb/CopyRectDecoder.cxx index a7383881..efc77c99 100644 --- a/common/rfb/CopyRectDecoder.cxx +++ b/common/rfb/CopyRectDecoder.cxx @@ -20,10 +20,12 @@ #include <config.h> #endif +#include <core/Region.h> + #include <rdr/MemInStream.h> #include <rdr/OutStream.h> + #include <rfb/PixelBuffer.h> -#include <rfb/Region.h> #include <rfb/CopyRectDecoder.h> using namespace rfb; @@ -36,7 +38,7 @@ CopyRectDecoder::~CopyRectDecoder() { } -bool CopyRectDecoder::readRect(const Rect& /*r*/, +bool CopyRectDecoder::readRect(const core::Rect& /*r*/, rdr::InStream* is, const ServerParams& /*server*/, rdr::OutStream* os) @@ -48,11 +50,11 @@ bool CopyRectDecoder::readRect(const Rect& /*r*/, } -void CopyRectDecoder::getAffectedRegion(const Rect& rect, +void CopyRectDecoder::getAffectedRegion(const core::Rect& rect, const uint8_t* buffer, size_t buflen, const ServerParams& server, - Region* region) + core::Region* region) { rdr::MemInStream is(buffer, buflen); int srcX = is.readU16(); @@ -60,11 +62,12 @@ void CopyRectDecoder::getAffectedRegion(const Rect& rect, Decoder::getAffectedRegion(rect, buffer, buflen, server, region); - region->assign_union(Region(rect.translate(Point(srcX-rect.tl.x, - srcY-rect.tl.y)))); + region->assign_union(rect.translate({srcX-rect.tl.x, + srcY-rect.tl.y})); } -void CopyRectDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void CopyRectDecoder::decodeRect(const core::Rect& r, + const uint8_t* buffer, size_t buflen, const ServerParams& /*server*/, ModifiablePixelBuffer* pb) @@ -72,5 +75,5 @@ void CopyRectDecoder::decodeRect(const Rect& r, const uint8_t* buffer, rdr::MemInStream is(buffer, buflen); int srcX = is.readU16(); int srcY = is.readU16(); - pb->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY)); + pb->copyRect(r, {r.tl.x-srcX, r.tl.y-srcY}); } diff --git a/common/rfb/CopyRectDecoder.h b/common/rfb/CopyRectDecoder.h index 51651196..b1d0d38d 100644 --- a/common/rfb/CopyRectDecoder.h +++ b/common/rfb/CopyRectDecoder.h @@ -26,13 +26,13 @@ namespace rfb { public: CopyRectDecoder(); virtual ~CopyRectDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void getAffectedRegion(const Rect& rect, const uint8_t* buffer, + void getAffectedRegion(const core::Rect& rect, const uint8_t* buffer, size_t buflen, const ServerParams& server, - Region* region) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + core::Region* region) override; + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; }; diff --git a/common/rfb/Cursor.cxx b/common/rfb/Cursor.cxx index 94844144..e094e4ed 100644 --- a/common/rfb/Cursor.cxx +++ b/common/rfb/Cursor.cxx @@ -26,14 +26,15 @@ #include <stdexcept> +#include <core/LogWriter.h> + #include <rfb/Cursor.h> -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("Cursor"); +static core::LogWriter vlog("Cursor"); -Cursor::Cursor(int width, int height, const Point& hotspot, +Cursor::Cursor(int width, int height, const core::Point& hotspot, const uint8_t* data_) : width_(width), height_(height), hotspot_(hotspot) { @@ -215,9 +216,9 @@ std::vector<uint8_t> Cursor::getMask() const void Cursor::crop() { - Rect busy = Rect(0, 0, width_, height_); - busy = busy.intersect(Rect(hotspot_.x, hotspot_.y, - hotspot_.x+1, hotspot_.y+1)); + core::Rect busy(0, 0, width_, height_); + busy = busy.intersect({hotspot_.x, hotspot_.y, + hotspot_.x+1, hotspot_.y+1}); int x, y; uint8_t *data_ptr = data; for (y = 0; y < height(); y++) { @@ -255,9 +256,10 @@ RenderedCursor::RenderedCursor() { } -const uint8_t* RenderedCursor::getBuffer(const Rect& _r, int* stride) const +const uint8_t* RenderedCursor::getBuffer(const core::Rect& _r, + int* stride) const { - Rect r; + core::Rect r; r = _r.translate(offset.negate()); if (!r.enclosed_by(buffer.getRect())) @@ -267,10 +269,10 @@ const uint8_t* RenderedCursor::getBuffer(const Rect& _r, int* stride) const } void RenderedCursor::update(PixelBuffer* framebuffer, - Cursor* cursor, const Point& pos) + Cursor* cursor, const core::Point& pos) { - Point rawOffset, diff; - Rect clippedRect; + core::Point rawOffset, diff; + core::Rect clippedRect; const uint8_t* data; int stride; @@ -282,7 +284,7 @@ void RenderedCursor::update(PixelBuffer* framebuffer, setSize(framebuffer->width(), framebuffer->height()); rawOffset = pos.subtract(cursor->hotspot()); - clippedRect = Rect(0, 0, cursor->width(), cursor->height()) + clippedRect = core::Rect(0, 0, cursor->width(), cursor->height()) .translate(rawOffset) .intersect(framebuffer->getRect()); offset = clippedRect.tl; @@ -313,7 +315,7 @@ void RenderedCursor::update(PixelBuffer* framebuffer, else if (fg[3] == 0xff) { memcpy(rgb, fg, 3); } else { - buffer.getImage(bg, Rect(x, y, x+1, y+1)); + buffer.getImage(bg, {x, y, x+1, y+1}); format.rgbFromBuffer(rgb, bg, 1); // FIXME: Gamma aware blending for (int i = 0;i < 3;i++) { @@ -323,7 +325,7 @@ void RenderedCursor::update(PixelBuffer* framebuffer, } format.bufferFromRGB(bg, rgb, 1); - buffer.imageRect(Rect(x, y, x+1, y+1), bg); + buffer.imageRect({x, y, x+1, y+1}, bg); } } } diff --git a/common/rfb/Cursor.h b/common/rfb/Cursor.h index c71f5a77..ef3c1b80 100644 --- a/common/rfb/Cursor.h +++ b/common/rfb/Cursor.h @@ -26,19 +26,22 @@ #include <vector> +#include <core/Rect.h> + #include <rfb/PixelBuffer.h> namespace rfb { class Cursor { public: - Cursor(int width, int height, const Point& hotspot, const uint8_t* data); + Cursor(int width, int height, const core::Point& hotspot, + const uint8_t* data); Cursor(const Cursor& other); ~Cursor(); int width() const { return width_; }; int height() const { return height_; }; - const Point& hotspot() const { return hotspot_; }; + const core::Point& hotspot() const { return hotspot_; }; const uint8_t* getBuffer() const { return data; }; // getBitmap() returns a monochrome version of the cursor @@ -52,7 +55,7 @@ namespace rfb { protected: int width_, height_; - Point hotspot_; + core::Point hotspot_; uint8_t* data; }; @@ -60,15 +63,16 @@ namespace rfb { public: RenderedCursor(); - Rect getEffectiveRect() const { return buffer.getRect(offset); } + core::Rect getEffectiveRect() const { return buffer.getRect(offset); } - const uint8_t* getBuffer(const Rect& r, int* stride) const override; + const uint8_t* getBuffer(const core::Rect& r, int* stride) const override; - void update(PixelBuffer* framebuffer, Cursor* cursor, const Point& pos); + void update(PixelBuffer* framebuffer, Cursor* cursor, + const core::Point& pos); protected: ManagedPixelBuffer buffer; - Point offset; + core::Point offset; }; } diff --git a/common/rfb/DecodeManager.cxx b/common/rfb/DecodeManager.cxx index 4effe985..48181f94 100644 --- a/common/rfb/DecodeManager.cxx +++ b/common/rfb/DecodeManager.cxx @@ -23,22 +23,20 @@ #include <assert.h> #include <string.h> +#include <core/LogWriter.h> +#include <core/Region.h> +#include <core/string.h> + #include <rfb/CConnection.h> #include <rfb/DecodeManager.h> #include <rfb/Decoder.h> #include <rfb/Exception.h> -#include <rfb/Region.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> -#include <rdr/Exception.h> #include <rdr/MemOutStream.h> -#include <os/Mutex.h> - using namespace rfb; -static LogWriter vlog("DecodeManager"); +static core::LogWriter vlog("DecodeManager"); DecodeManager::DecodeManager(CConnection *conn_) : conn(conn_), threadException(nullptr) @@ -49,11 +47,7 @@ DecodeManager::DecodeManager(CConnection *conn_) : memset(stats, 0, sizeof(stats)); - queueMutex = new os::Mutex(); - producerCond = new os::Condition(queueMutex); - consumerCond = new os::Condition(queueMutex); - - cpuCount = os::Thread::getSystemCPUCount(); + cpuCount = std::thread::hardware_concurrency(); if (cpuCount == 0) { vlog.error("Unable to determine the number of CPU cores on this system"); cpuCount = 1; @@ -86,22 +80,16 @@ DecodeManager::~DecodeManager() threads.pop_back(); } - delete threadException; - while (!freeBuffers.empty()) { delete freeBuffers.back(); freeBuffers.pop_back(); } - delete consumerCond; - delete producerCond; - delete queueMutex; - for (Decoder* decoder : decoders) delete decoder; } -bool DecodeManager::decodeRect(const Rect& r, int encoding, +bool DecodeManager::decodeRect(const core::Rect& r, int encoding, ModifiablePixelBuffer* pb) { Decoder *decoder; @@ -128,29 +116,25 @@ bool DecodeManager::decodeRect(const Rect& r, int encoding, decoder = decoders[encoding]; // Wait for an available memory buffer - queueMutex->lock(); + std::unique_lock<std::mutex> lock(queueMutex); // FIXME: Should we return and let other things run here? while (freeBuffers.empty()) - producerCond->wait(); + producerCond.wait(lock); // Don't pop the buffer in case we throw an exception // whilst reading bufferStream = freeBuffers.front(); - queueMutex->unlock(); + lock.unlock(); // First check if any thread has encountered a problem throwThreadException(); // Read the rect bufferStream->clear(); - try { - if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream)) - return false; - } catch (std::exception& e) { - throw std::runtime_error(format("Error reading rect: %s", e.what())); - } + if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream)) + return false; stats[encoding].rects++; stats[encoding].bytes += 12 + bufferStream->length(); @@ -173,7 +157,7 @@ bool DecodeManager::decodeRect(const Rect& r, int encoding, bufferStream->length(), conn->server, &entry->affectedRegion); - queueMutex->lock(); + lock.lock(); // The workers add buffers to the end so it's safe to assume // the front is still the same buffer @@ -183,21 +167,21 @@ bool DecodeManager::decodeRect(const Rect& r, int encoding, // We only put a single entry on the queue so waking a single // thread is sufficient - consumerCond->signal(); + consumerCond.notify_one(); - queueMutex->unlock(); + lock.unlock(); return true; } void DecodeManager::flush() { - queueMutex->lock(); + std::unique_lock<std::mutex> lock(queueMutex); while (!workQueue.empty()) - producerCond->wait(); + producerCond.wait(lock); - queueMutex->unlock(); + lock.unlock(); throwThreadException(); } @@ -227,49 +211,49 @@ void DecodeManager::logStats() ratio = (double)stats[i].equivalent / stats[i].bytes; vlog.info(" %s: %s, %s", encodingName(i), - siPrefix(stats[i].rects, "rects").c_str(), - siPrefix(stats[i].pixels, "pixels").c_str()); + core::siPrefix(stats[i].rects, "rects").c_str(), + core::siPrefix(stats[i].pixels, "pixels").c_str()); vlog.info(" %*s %s (1:%g ratio)", (int)strlen(encodingName(i)), "", - iecPrefix(stats[i].bytes, "B").c_str(), ratio); + core::iecPrefix(stats[i].bytes, "B").c_str(), ratio); } ratio = (double)equivalent / bytes; vlog.info(" Total: %s, %s", - siPrefix(rects, "rects").c_str(), - siPrefix(pixels, "pixels").c_str()); + core::siPrefix(rects, "rects").c_str(), + core::siPrefix(pixels, "pixels").c_str()); vlog.info(" %s (1:%g ratio)", - iecPrefix(bytes, "B").c_str(), ratio); + core::iecPrefix(bytes, "B").c_str(), ratio); } -void DecodeManager::setThreadException(const std::exception& e) +void DecodeManager::setThreadException() { - os::AutoMutex a(queueMutex); + const std::lock_guard<std::mutex> lock(queueMutex); - if (threadException != nullptr) + if (threadException) return; - threadException = new std::runtime_error(format("Exception on worker thread: %s", e.what())); + threadException = std::current_exception(); } void DecodeManager::throwThreadException() { - os::AutoMutex a(queueMutex); + const std::lock_guard<std::mutex> lock(queueMutex); - if (threadException == nullptr) + if (!threadException) return; - std::runtime_error e(threadException->what()); - - delete threadException; - threadException = nullptr; - - throw e; + try { + std::rethrow_exception(threadException); + } catch (...) { + threadException = nullptr; + throw; + } } DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager_) - : manager(manager_), stopRequested(false) + : manager(manager_), thread(nullptr), stopRequested(false) { start(); } @@ -277,25 +261,35 @@ DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager_) DecodeManager::DecodeThread::~DecodeThread() { stop(); - wait(); + if (thread != nullptr) { + thread->join(); + delete thread; + } +} + +void DecodeManager::DecodeThread::start() +{ + assert(thread == nullptr); + + thread = new std::thread(&DecodeThread::worker, this); } void DecodeManager::DecodeThread::stop() { - os::AutoMutex a(manager->queueMutex); + const std::lock_guard<std::mutex> lock(manager->queueMutex); - if (!isRunning()) + if (thread == nullptr) return; stopRequested = true; // We can't wake just this thread, so wake everyone - manager->consumerCond->broadcast(); + manager->consumerCond.notify_all(); } void DecodeManager::DecodeThread::worker() { - manager->queueMutex->lock(); + std::unique_lock<std::mutex> lock(manager->queueMutex); while (!stopRequested) { DecodeManager::QueueEntry *entry; @@ -304,14 +298,14 @@ void DecodeManager::DecodeThread::worker() entry = findEntry(); if (entry == nullptr) { // Wait and try again - manager->consumerCond->wait(); + manager->consumerCond.wait(lock); continue; } // This is ours now entry->active = true; - manager->queueMutex->unlock(); + lock.unlock(); // Do the actual decoding try { @@ -319,12 +313,12 @@ void DecodeManager::DecodeThread::worker() entry->bufferStream->length(), *entry->server, entry->pb); } catch (std::exception& e) { - manager->setThreadException(e); + manager->setThreadException(); } catch(...) { assert(false); } - manager->queueMutex->lock(); + lock.lock(); // Remove the entry from the queue and give back the memory buffer manager->freeBuffers.push_back(entry->bufferStream); @@ -332,19 +326,17 @@ void DecodeManager::DecodeThread::worker() delete entry; // Wake the main thread in case it is waiting for a memory buffer - manager->producerCond->signal(); + manager->producerCond.notify_one(); // This rect might have been blocking multiple other rects, so // wake up every worker thread if (manager->workQueue.size() > 1) - manager->consumerCond->broadcast(); + manager->consumerCond.notify_all(); } - - manager->queueMutex->unlock(); } DecodeManager::QueueEntry* DecodeManager::DecodeThread::findEntry() { - Region lockedRegion; + core::Region lockedRegion; if (manager->workQueue.empty()) return nullptr; diff --git a/common/rfb/DecodeManager.h b/common/rfb/DecodeManager.h index b11b7044..146bf8ae 100644 --- a/common/rfb/DecodeManager.h +++ b/common/rfb/DecodeManager.h @@ -19,16 +19,18 @@ #ifndef __RFB_DECODEMANAGER_H__ #define __RFB_DECODEMANAGER_H__ +#include <condition_variable> +#include <exception> #include <list> +#include <mutex> +#include <thread> -#include <os/Thread.h> +#include <core/Region.h> -#include <rfb/Region.h> #include <rfb/encodings.h> -namespace os { - class Condition; - class Mutex; +namespace core { + struct Rect; } namespace rdr { @@ -36,17 +38,17 @@ namespace rdr { } namespace rfb { + class CConnection; class Decoder; class ModifiablePixelBuffer; - struct Rect; class DecodeManager { public: DecodeManager(CConnection *conn); ~DecodeManager(); - bool decodeRect(const Rect& r, int encoding, + bool decodeRect(const core::Rect& r, int encoding, ModifiablePixelBuffer* pb); void flush(); @@ -54,7 +56,7 @@ namespace rfb { private: void logStats(); - void setThreadException(const std::exception& e); + void setThreadException(); void throwThreadException(); private: @@ -72,43 +74,46 @@ namespace rfb { struct QueueEntry { bool active; - Rect rect; + core::Rect rect; int encoding; Decoder* decoder; const ServerParams* server; ModifiablePixelBuffer* pb; rdr::MemOutStream* bufferStream; - Region affectedRegion; + core::Region affectedRegion; }; std::list<rdr::MemOutStream*> freeBuffers; std::list<QueueEntry*> workQueue; - os::Mutex* queueMutex; - os::Condition* producerCond; - os::Condition* consumerCond; + std::mutex queueMutex; + std::condition_variable producerCond; + std::condition_variable consumerCond; private: - class DecodeThread : public os::Thread { + class DecodeThread { public: DecodeThread(DecodeManager* manager); ~DecodeThread(); + void start(); void stop(); protected: - void worker() override; + void worker(); DecodeManager::QueueEntry* findEntry(); private: DecodeManager* manager; + std::thread* thread; bool stopRequested; }; std::list<DecodeThread*> threads; - std::exception *threadException; + std::exception_ptr threadException; }; + } #endif diff --git a/common/rfb/Decoder.cxx b/common/rfb/Decoder.cxx index e9bc9a4f..0f1cde89 100644 --- a/common/rfb/Decoder.cxx +++ b/common/rfb/Decoder.cxx @@ -22,8 +22,10 @@ #endif #include <stdio.h> + +#include <core/Region.h> + #include <rfb/encodings.h> -#include <rfb/Region.h> #include <rfb/Decoder.h> #include <rfb/RawDecoder.h> #include <rfb/CopyRectDecoder.h> @@ -45,19 +47,19 @@ Decoder::~Decoder() { } -void Decoder::getAffectedRegion(const Rect& rect, +void Decoder::getAffectedRegion(const core::Rect& rect, const uint8_t* /*buffer*/, size_t /*buflen*/, const ServerParams& /*server*/, - Region* region) + core::Region* region) { region->reset(rect); } -bool Decoder::doRectsConflict(const Rect& /*rectA*/, +bool Decoder::doRectsConflict(const core::Rect& /*rectA*/, const uint8_t* /*bufferA*/, size_t /*buflenA*/, - const Rect& /*rectB*/, + const core::Rect& /*rectB*/, const uint8_t* /*bufferB*/, size_t /*buflenB*/, const ServerParams& /*server*/) diff --git a/common/rfb/Decoder.h b/common/rfb/Decoder.h index 77987737..17d5296b 100644 --- a/common/rfb/Decoder.h +++ b/common/rfb/Decoder.h @@ -19,19 +19,23 @@ #ifndef __RFB_DECODER_H__ #define __RFB_DECODER_H__ +#include <stddef.h> #include <stdint.h> +namespace core { + class Region; + struct Rect; +} + namespace rdr { class InStream; class OutStream; } namespace rfb { + class ServerParams; class ModifiablePixelBuffer; - class Region; - - struct Rect; enum DecoderFlags { // A constant for decoders that don't need anything special @@ -54,7 +58,7 @@ namespace rfb { // InStream to the OutStream, possibly changing it along the way to // make it easier to decode. This function will always be called in // a serial manner on the main thread. - virtual bool readRect(const Rect& r, rdr::InStream* is, + virtual bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os)=0; // These functions will be called from any of the worker threads. @@ -64,17 +68,17 @@ namespace rfb { // getAffectedRegion() returns the parts of the frame buffer will // be either read from or written do when decoding this rect. The // default implementation simply returns the given rectangle. - virtual void getAffectedRegion(const Rect& rect, const uint8_t* buffer, + virtual void getAffectedRegion(const core::Rect& rect, const uint8_t* buffer, size_t buflen, const ServerParams& server, - Region* region); + core::Region* region); // doesRectsConflict() determines if two rectangles must be decoded // in the order they were received. This will only be called if the // DecoderPartiallyOrdered flag has been set. - virtual bool doRectsConflict(const Rect& rectA, + virtual bool doRectsConflict(const core::Rect& rectA, const uint8_t* bufferA, size_t buflenA, - const Rect& rectB, + const core::Rect& rectB, const uint8_t* bufferB, size_t buflenB, const ServerParams& server); @@ -83,7 +87,7 @@ namespace rfb { // given buffer, onto the ModifiablePixelBuffer. The PixelFormat of // the PixelBuffer might not match the ConnParams and it is up to // the decoder to do any necessary conversion. - virtual void decodeRect(const Rect& r, const uint8_t* buffer, + virtual void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb)=0; @@ -94,6 +98,7 @@ namespace rfb { public: const enum DecoderFlags flags; }; + } #endif diff --git a/common/rfb/EncodeManager.cxx b/common/rfb/EncodeManager.cxx index 67a32f5b..6a63fa6f 100644 --- a/common/rfb/EncodeManager.cxx +++ b/common/rfb/EncodeManager.cxx @@ -25,14 +25,17 @@ #include <stdlib.h> +#include <core/LogWriter.h> +#include <core/string.h> + +#include <rfb/Cursor.h> #include <rfb/EncodeManager.h> #include <rfb/Encoder.h> #include <rfb/Palette.h> #include <rfb/SConnection.h> #include <rfb/SMsgWriter.h> #include <rfb/UpdateTracker.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> +#include <rfb/encodings.h> #include <rfb/RawEncoder.h> #include <rfb/RREEncoder.h> @@ -43,7 +46,7 @@ using namespace rfb; -static LogWriter vlog("EncodeManager"); +static core::LogWriter vlog("EncodeManager"); // Split each rectangle into smaller ones no larger than this area, // and no wider than this width. @@ -191,11 +194,11 @@ void EncodeManager::logStats() ratio = (double)copyStats.equivalent / copyStats.bytes; vlog.info(" %s: %s, %s", "Copies", - siPrefix(copyStats.rects, "rects").c_str(), - siPrefix(copyStats.pixels, "pixels").c_str()); + core::siPrefix(copyStats.rects, "rects").c_str(), + core::siPrefix(copyStats.pixels, "pixels").c_str()); vlog.info(" %*s %s (1:%g ratio)", (int)strlen("Copies"), "", - iecPrefix(copyStats.bytes, "B").c_str(), ratio); + core::iecPrefix(copyStats.bytes, "B").c_str(), ratio); } for (i = 0;i < stats.size();i++) { @@ -221,21 +224,21 @@ void EncodeManager::logStats() ratio = (double)stats[i][j].equivalent / stats[i][j].bytes; vlog.info(" %s: %s, %s", encoderTypeName((EncoderType)j), - siPrefix(stats[i][j].rects, "rects").c_str(), - siPrefix(stats[i][j].pixels, "pixels").c_str()); + core::siPrefix(stats[i][j].rects, "rects").c_str(), + core::siPrefix(stats[i][j].pixels, "pixels").c_str()); vlog.info(" %*s %s (1:%g ratio)", (int)strlen(encoderTypeName((EncoderType)j)), "", - iecPrefix(stats[i][j].bytes, "B").c_str(), ratio); + core::iecPrefix(stats[i][j].bytes, "B").c_str(), ratio); } } ratio = (double)equivalent / bytes; vlog.info(" Total: %s, %s", - siPrefix(rects, "rects").c_str(), - siPrefix(pixels, "pixels").c_str()); + core::siPrefix(rects, "rects").c_str(), + core::siPrefix(pixels, "pixels").c_str()); vlog.info(" %s (1:%g ratio)", - iecPrefix(bytes, "B").c_str(), ratio); + core::iecPrefix(bytes, "B").c_str(), ratio); } bool EncodeManager::supported(int encoding) @@ -252,12 +255,12 @@ bool EncodeManager::supported(int encoding) } } -bool EncodeManager::needsLosslessRefresh(const Region& req) +bool EncodeManager::needsLosslessRefresh(const core::Region& req) { return !lossyRegion.intersect(req).is_empty(); } -int EncodeManager::getNextLosslessRefresh(const Region& req) +int EncodeManager::getNextLosslessRefresh(const core::Region& req) { // Do we have something we can send right away? if (!pendingRefreshRegion.intersect(req).is_empty()) @@ -269,12 +272,19 @@ int EncodeManager::getNextLosslessRefresh(const Region& req) return recentChangeTimer.getNextTimeout(); } -void EncodeManager::pruneLosslessRefresh(const Region& limits) +void EncodeManager::pruneLosslessRefresh(const core::Region& limits) { lossyRegion.assign_intersect(limits); pendingRefreshRegion.assign_intersect(limits); } +void EncodeManager::forceRefresh(const core::Region& req) +{ + lossyRegion.assign_union(req); + if (!recentChangeTimer.isStarted()) + pendingRefreshRegion.assign_union(req); +} + void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, const RenderedCursor* renderedCursor) { @@ -286,15 +296,16 @@ void EncodeManager::writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, recentChangeTimer.start(RecentChangeTimeout); } -void EncodeManager::writeLosslessRefresh(const Region& req, const PixelBuffer* pb, +void EncodeManager::writeLosslessRefresh(const core::Region& req, + const PixelBuffer* pb, const RenderedCursor* renderedCursor, size_t maxUpdateSize) { doUpdate(false, getLosslessRefresh(req, maxUpdateSize), - Region(), Point(), pb, renderedCursor); + {}, {}, pb, renderedCursor); } -void EncodeManager::handleTimeout(Timer* t) +void EncodeManager::handleTimeout(core::Timer* t) { if (t == &recentChangeTimer) { // Any lossy region that wasn't recently updated can @@ -308,13 +319,15 @@ void EncodeManager::handleTimeout(Timer* t) } } -void EncodeManager::doUpdate(bool allowLossy, const Region& changed_, - const Region& copied, const Point& copyDelta, +void EncodeManager::doUpdate(bool allowLossy, const + core::Region& changed_, + const core::Region& copied, + const core::Point& copyDelta, const PixelBuffer* pb, const RenderedCursor* renderedCursor) { int nRects; - Region changed, cursorRegion; + core::Region changed, cursorRegion; updates++; @@ -475,19 +488,20 @@ void EncodeManager::prepareEncoders(bool allowLossy) encoder->setFineQualityLevel(conn->client.fineQualityLevel, conn->client.subsampling); } else { - int level = __rfbmax(conn->client.qualityLevel, - encoder->losslessQuality); - encoder->setQualityLevel(level); + if (conn->client.qualityLevel < encoder->losslessQuality) + encoder->setQualityLevel(encoder->losslessQuality); + else + encoder->setQualityLevel(conn->client.qualityLevel); encoder->setFineQualityLevel(-1, subsampleUndefined); } } } -Region EncodeManager::getLosslessRefresh(const Region& req, - size_t maxUpdateSize) +core::Region EncodeManager::getLosslessRefresh(const core::Region& req, + size_t maxUpdateSize) { - std::vector<Rect> rects; - Region refresh; + std::vector<core::Rect> rects; + core::Region refresh; size_t area; // We make a conservative guess at the compression ratio at 2:1 @@ -500,7 +514,7 @@ Region EncodeManager::getLosslessRefresh(const Region& req, pendingRefreshRegion.intersect(req).get_rects(&rects); while (!rects.empty()) { size_t idx; - Rect rect; + core::Rect rect; // Grab a random rect so we don't keep damaging and restoring the // same rect over and over @@ -514,17 +528,21 @@ Region EncodeManager::getLosslessRefresh(const Region& req, // Use the narrowest axis to avoid getting to thin rects if (rect.width() > rect.height()) { int width = (maxUpdateSize - area) / rect.height(); - rect.br.x = rect.tl.x + __rfbmax(1, width); + if (width < 1) + width = 1; + rect.br.x = rect.tl.x + width; } else { int height = (maxUpdateSize - area) / rect.width(); - rect.br.y = rect.tl.y + __rfbmax(1, height); + if (height < 1) + height = 1; + rect.br.y = rect.tl.y + height; } - refresh.assign_union(Region(rect)); + refresh.assign_union(rect); break; } area += rect.area(); - refresh.assign_union(Region(rect)); + refresh.assign_union(rect); rects.erase(rects.begin() + idx); } @@ -532,11 +550,11 @@ Region EncodeManager::getLosslessRefresh(const Region& req, return refresh; } -int EncodeManager::computeNumRects(const Region& changed) +int EncodeManager::computeNumRects(const core::Region& changed) { int numRects; - std::vector<Rect> rects; - std::vector<Rect>::const_iterator rect; + std::vector<core::Rect> rects; + std::vector<core::Rect>::const_iterator rect; numRects = 0; changed.get_rects(&rects); @@ -566,7 +584,7 @@ int EncodeManager::computeNumRects(const Region& changed) return numRects; } -Encoder *EncodeManager::startRect(const Rect& rect, int type) +Encoder* EncodeManager::startRect(const core::Rect& rect, int type) { Encoder *encoder; int klass, equiv; @@ -587,13 +605,13 @@ Encoder *EncodeManager::startRect(const Rect& rect, int type) if ((encoder->flags & EncoderLossy) && ((encoder->losslessQuality == -1) || (encoder->getQualityLevel() < encoder->losslessQuality))) - lossyRegion.assign_union(Region(rect)); + lossyRegion.assign_union(rect); else - lossyRegion.assign_subtract(Region(rect)); + lossyRegion.assign_subtract(rect); // This was either a rect getting refreshed, or a rect that just got // new content. Either way we should not try to refresh it anymore. - pendingRefreshRegion.assign_subtract(Region(rect)); + pendingRefreshRegion.assign_subtract(rect); return encoder; } @@ -611,12 +629,13 @@ void EncodeManager::endRect() stats[klass][activeType].bytes += length; } -void EncodeManager::writeCopyRects(const Region& copied, const Point& delta) +void EncodeManager::writeCopyRects(const core::Region& copied, + const core::Point& delta) { - std::vector<Rect> rects; - std::vector<Rect>::const_iterator rect; + std::vector<core::Rect> rects; + std::vector<core::Rect>::const_iterator rect; - Region lossyCopy; + core::Region lossyCopy; beforeLength = conn->getOutStream()->length(); @@ -645,20 +664,22 @@ void EncodeManager::writeCopyRects(const Region& copied, const Point& delta) pendingRefreshRegion.assign_subtract(copied); } -void EncodeManager::writeSolidRects(Region *changed, const PixelBuffer* pb) +void EncodeManager::writeSolidRects(core::Region* changed, + const PixelBuffer* pb) { - std::vector<Rect> rects; - std::vector<Rect>::const_iterator rect; + std::vector<core::Rect> rects; + std::vector<core::Rect>::const_iterator rect; changed->get_rects(&rects); for (rect = rects.begin(); rect != rects.end(); ++rect) findSolidRect(*rect, changed, pb); } -void EncodeManager::findSolidRect(const Rect& rect, Region *changed, +void EncodeManager::findSolidRect(const core::Rect& rect, + core::Region* changed, const PixelBuffer* pb) { - Rect sr; + core::Rect sr; int dx, dy, dw, dh; // We start by finding a solid 16x16 block @@ -677,11 +698,11 @@ void EncodeManager::findSolidRect(const Rect& rect, Region *changed, if (dx + dw > rect.br.x) dw = rect.br.x - dx; - pb->getImage(colourValue, Rect(dx, dy, dx+1, dy+1)); + pb->getImage(colourValue, {dx, dy, dx+1, dy+1}); sr.setXYWH(dx, dy, dw, dh); if (checkSolidTile(sr, colourValue, pb)) { - Rect erb, erp; + core::Rect erb, erp; Encoder *encoder; @@ -721,7 +742,7 @@ void EncodeManager::findSolidRect(const Rect& rect, Region *changed, } endRect(); - changed->assign_subtract(Region(erp)); + changed->assign_subtract(erp); // Search remaining areas by recursion // FIXME: Is this the best way to divide things up? @@ -752,15 +773,16 @@ void EncodeManager::findSolidRect(const Rect& rect, Region *changed, } } -void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb) +void EncodeManager::writeRects(const core::Region& changed, + const PixelBuffer* pb) { - std::vector<Rect> rects; - std::vector<Rect>::const_iterator rect; + std::vector<core::Rect> rects; + std::vector<core::Rect>::const_iterator rect; changed.get_rects(&rects); for (rect = rects.begin(); rect != rects.end(); ++rect) { int w, h, sw, sh; - Rect sr; + core::Rect sr; w = rect->width(); h = rect->height(); @@ -794,7 +816,8 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb) } } -void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb) +void EncodeManager::writeSubRect(const core::Rect& rect, + const PixelBuffer* pb) { PixelBuffer *ppb; @@ -878,7 +901,8 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb) endRect(); } -bool EncodeManager::checkSolidTile(const Rect& r, const uint8_t* colourValue, +bool EncodeManager::checkSolidTile(const core::Rect& r, + const uint8_t* colourValue, const PixelBuffer *pb) { const uint8_t* buffer; @@ -902,13 +926,14 @@ bool EncodeManager::checkSolidTile(const Rect& r, const uint8_t* colourValue, } } -void EncodeManager::extendSolidAreaByBlock(const Rect& r, +void EncodeManager::extendSolidAreaByBlock(const core::Rect& r, const uint8_t* colourValue, - const PixelBuffer *pb, Rect* er) + const PixelBuffer* pb, + core::Rect* er) { int dx, dy, dw, dh; int w_prev; - Rect sr; + core::Rect sr; int w_best = 0, h_best = 0; w_prev = r.width(); @@ -958,12 +983,14 @@ void EncodeManager::extendSolidAreaByBlock(const Rect& r, er->br.y = er->tl.y + h_best; } -void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr, +void EncodeManager::extendSolidAreaByPixel(const core::Rect& r, + const core::Rect& sr, const uint8_t* colourValue, - const PixelBuffer *pb, Rect* er) + const PixelBuffer* pb, + core::Rect* er) { int cx, cy; - Rect tr; + core::Rect tr; // Try to extend the area upwards. for (cy = sr.tl.y - 1; cy >= r.tl.y; cy--) { @@ -998,7 +1025,7 @@ void EncodeManager::extendSolidAreaByPixel(const Rect& r, const Rect& sr, er->br.x = cx; } -PixelBuffer* EncodeManager::preparePixelBuffer(const Rect& rect, +PixelBuffer* EncodeManager::preparePixelBuffer(const core::Rect& rect, const PixelBuffer *pb, bool convert) { @@ -1063,7 +1090,7 @@ void EncodeManager::OffsetPixelBuffer::update(const PixelFormat& pf, setBuffer(width, height, (uint8_t*)data_, stride_); } -uint8_t* EncodeManager::OffsetPixelBuffer::getBufferRW(const Rect& /*r*/, int* /*stride*/) +uint8_t* EncodeManager::OffsetPixelBuffer::getBufferRW(const core::Rect& /*r*/, int* /*stride*/) { throw std::logic_error("Invalid write attempt to OffsetPixelBuffer"); } diff --git a/common/rfb/EncodeManager.h b/common/rfb/EncodeManager.h index 7ae9b5b8..4ce6d0ce 100644 --- a/common/rfb/EncodeManager.h +++ b/common/rfb/EncodeManager.h @@ -24,21 +24,22 @@ #include <stdint.h> +#include <core/Region.h> +#include <core/Timer.h> + #include <rfb/PixelBuffer.h> -#include <rfb/Region.h> -#include <rfb/Timer.h> namespace rfb { + class SConnection; class Encoder; class UpdateInfo; class PixelBuffer; class RenderedCursor; - struct Rect; struct RectInfo; - class EncodeManager : public Timer::Callback { + class EncodeManager : public core::Timer::Callback { public: EncodeManager(SConnection* conn); ~EncodeManager(); @@ -48,51 +49,60 @@ namespace rfb { // Hack to let ConnParams calculate the client's preferred encoding static bool supported(int encoding); - bool needsLosslessRefresh(const Region& req); - int getNextLosslessRefresh(const Region& req); + bool needsLosslessRefresh(const core::Region& req); + int getNextLosslessRefresh(const core::Region& req); + + void pruneLosslessRefresh(const core::Region& limits); - void pruneLosslessRefresh(const Region& limits); + void forceRefresh(const core::Region& req); void writeUpdate(const UpdateInfo& ui, const PixelBuffer* pb, const RenderedCursor* renderedCursor); - void writeLosslessRefresh(const Region& req, const PixelBuffer* pb, + void writeLosslessRefresh(const core::Region& req, + const PixelBuffer* pb, const RenderedCursor* renderedCursor, size_t maxUpdateSize); protected: - void handleTimeout(Timer* t) override; + void handleTimeout(core::Timer* t) override; - void doUpdate(bool allowLossy, const Region& changed, - const Region& copied, const Point& copy_delta, + void doUpdate(bool allowLossy, const core::Region& changed, + const core::Region& copied, + const core::Point& copy_delta, const PixelBuffer* pb, const RenderedCursor* renderedCursor); void prepareEncoders(bool allowLossy); - Region getLosslessRefresh(const Region& req, size_t maxUpdateSize); + core::Region getLosslessRefresh(const core::Region& req, + size_t maxUpdateSize); - int computeNumRects(const Region& changed); + int computeNumRects(const core::Region& changed); - Encoder *startRect(const Rect& rect, int type); + Encoder* startRect(const core::Rect& rect, int type); void endRect(); - void writeCopyRects(const Region& copied, const Point& delta); - void writeSolidRects(Region *changed, const PixelBuffer* pb); - void findSolidRect(const Rect& rect, Region *changed, const PixelBuffer* pb); - void writeRects(const Region& changed, const PixelBuffer* pb); + void writeCopyRects(const core::Region& copied, + const core::Point& delta); + void writeSolidRects(core::Region* changed, const PixelBuffer* pb); + void findSolidRect(const core::Rect& rect, core::Region* changed, + const PixelBuffer* pb); + void writeRects(const core::Region& changed, const PixelBuffer* pb); - void writeSubRect(const Rect& rect, const PixelBuffer *pb); + void writeSubRect(const core::Rect& rect, const PixelBuffer* pb); - bool checkSolidTile(const Rect& r, const uint8_t* colourValue, + bool checkSolidTile(const core::Rect& r, const uint8_t* colourValue, const PixelBuffer *pb); - void extendSolidAreaByBlock(const Rect& r, const uint8_t* colourValue, - const PixelBuffer *pb, Rect* er); - void extendSolidAreaByPixel(const Rect& r, const Rect& sr, + void extendSolidAreaByBlock(const core::Rect& r, const uint8_t* colourValue, - const PixelBuffer *pb, Rect* er); + const PixelBuffer* pb, core::Rect* er); + void extendSolidAreaByPixel(const core::Rect& r, + const core::Rect& sr, + const uint8_t* colourValue, + const PixelBuffer* pb, core::Rect* er); - PixelBuffer* preparePixelBuffer(const Rect& rect, - const PixelBuffer *pb, bool convert); + PixelBuffer* preparePixelBuffer(const core::Rect& rect, + const PixelBuffer* pb, bool convert); bool analyseRect(const PixelBuffer *pb, struct RectInfo *info, int maxColours); @@ -114,11 +124,11 @@ namespace rfb { std::vector<Encoder*> encoders; std::vector<int> activeEncoders; - Region lossyRegion; - Region recentlyChangedRegion; - Region pendingRefreshRegion; + core::Region lossyRegion; + core::Region recentlyChangedRegion; + core::Region pendingRefreshRegion; - Timer recentChangeTimer; + core::Timer recentChangeTimer; struct EncoderStats { unsigned rects; @@ -143,12 +153,13 @@ namespace rfb { const uint8_t* data_, int stride); private: - uint8_t* getBufferRW(const Rect& r, int* stride) override; + uint8_t* getBufferRW(const core::Rect& r, int* stride) override; }; OffsetPixelBuffer offsetPixelBuffer; ManagedPixelBuffer convertedPixelBuffer; }; + } #endif diff --git a/common/rfb/Encoder.h b/common/rfb/Encoder.h index 5e066323..7fa2cc75 100644 --- a/common/rfb/Encoder.h +++ b/common/rfb/Encoder.h @@ -22,8 +22,6 @@ #include <stdint.h> -#include <rfb/Rect.h> - namespace rfb { class SConnection; class PixelBuffer; diff --git a/common/rfb/H264Decoder.cxx b/common/rfb/H264Decoder.cxx index 89850ba4..d0d9b4df 100644 --- a/common/rfb/H264Decoder.cxx +++ b/common/rfb/H264Decoder.cxx @@ -29,14 +29,11 @@ #include <rdr/MemInStream.h> #include <rdr/InStream.h> #include <rdr/OutStream.h> -#include <rfb/LogWriter.h> #include <rfb/H264Decoder.h> #include <rfb/H264DecoderContext.h> using namespace rfb; -static LogWriter vlog("H264Decoder"); - enum rectFlags { resetContext = 0x1, resetAllContexts = 0x2, @@ -53,22 +50,20 @@ H264Decoder::~H264Decoder() void H264Decoder::resetContexts() { - os::AutoMutex lock(&mutex); for (H264DecoderContext* context : contexts) delete context; contexts.clear(); } -H264DecoderContext* H264Decoder::findContext(const Rect& r) +H264DecoderContext* H264Decoder::findContext(const core::Rect& r) { - os::AutoMutex m(&mutex); for (H264DecoderContext* context : contexts) if (context->isEqualRect(r)) return context; return nullptr; } -bool H264Decoder::readRect(const Rect& /*r*/, +bool H264Decoder::readRect(const core::Rect& /*r*/, rdr::InStream* is, const ServerParams& /*server*/, rdr::OutStream* os) @@ -96,7 +91,7 @@ bool H264Decoder::readRect(const Rect& /*r*/, return true; } -void H264Decoder::decodeRect(const Rect& r, const uint8_t* buffer, +void H264Decoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& /*server*/, ModifiablePixelBuffer* pb) @@ -116,9 +111,14 @@ void H264Decoder::decodeRect(const Rect& r, const uint8_t* buffer, ctx = findContext(r); } + if (ctx && (reset & resetContext)) { + contexts.remove(ctx); + delete ctx; + ctx = nullptr; + } + if (!ctx) { - os::AutoMutex lock(&mutex); if (contexts.size() >= MAX_H264_INSTANCES) { H264DecoderContext* excess_ctx = contexts.front(); @@ -131,12 +131,6 @@ void H264Decoder::decodeRect(const Rect& r, const uint8_t* buffer, contexts.push_back(ctx); } - if (!ctx->isReady()) - throw std::runtime_error("H264Decoder: Context is not ready"); - - if (reset & resetContext) - ctx->reset(); - if (!len) return; diff --git a/common/rfb/H264Decoder.h b/common/rfb/H264Decoder.h index 8ba47799..bc1b9281 100644 --- a/common/rfb/H264Decoder.h +++ b/common/rfb/H264Decoder.h @@ -21,9 +21,8 @@ #ifndef __RFB_H264DECODER_H__ #define __RFB_H264DECODER_H__ -#include <deque> +#include <list> -#include <os/Mutex.h> #include <rfb/Decoder.h> namespace rfb { @@ -33,19 +32,18 @@ namespace rfb { public: H264Decoder(); virtual ~H264Decoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; private: void resetContexts(); - H264DecoderContext* findContext(const Rect& r); + H264DecoderContext* findContext(const core::Rect& r); - os::Mutex mutex; - std::deque<H264DecoderContext*> contexts; + std::list<H264DecoderContext*> contexts; }; } diff --git a/common/rfb/H264DecoderContext.cxx b/common/rfb/H264DecoderContext.cxx index b2054554..3ad8c343 100644 --- a/common/rfb/H264DecoderContext.cxx +++ b/common/rfb/H264DecoderContext.cxx @@ -22,11 +22,6 @@ #include <config.h> #endif -#include <stdexcept> - -#include <os/Mutex.h> -#include <rfb/LogWriter.h> - #include <rfb/H264DecoderContext.h> #ifdef H264_LIBAV @@ -39,31 +34,11 @@ using namespace rfb; -static LogWriter vlog("H264DecoderContext"); - -H264DecoderContext *H264DecoderContext::createContext(const Rect &r) +H264DecoderContext *H264DecoderContext::createContext(const core::Rect &r) { - H264DecoderContext *ret = new H264DecoderContextType(r); - if (!ret->initCodec()) - { - throw std::runtime_error("H264DecoderContext: Unable to create context"); - } - - return ret; + return new H264DecoderContextType(r); } H264DecoderContext::~H264DecoderContext() { } - -bool H264DecoderContext::isReady() -{ - os::AutoMutex lock(&mutex); - return initialized; -} - -void H264DecoderContext::reset() -{ - freeCodec(); - initCodec(); -} diff --git a/common/rfb/H264DecoderContext.h b/common/rfb/H264DecoderContext.h index 88c2396c..5ef46662 100644 --- a/common/rfb/H264DecoderContext.h +++ b/common/rfb/H264DecoderContext.h @@ -23,35 +23,30 @@ #include <stdint.h> -#include <os/Mutex.h> -#include <rfb/Rect.h> -#include <rfb/Decoder.h> +#include <core/Rect.h> namespace rfb { + + class ModifiablePixelBuffer; + class H264DecoderContext { public: - static H264DecoderContext *createContext(const Rect &r); + static H264DecoderContext* createContext(const core::Rect& r); virtual ~H264DecoderContext() = 0; virtual void decode(const uint8_t* /*h264_buffer*/, uint32_t /*len*/, ModifiablePixelBuffer* /*pb*/) {} - void reset(); - inline bool isEqualRect(const Rect &r) const { return r == rect; } - bool isReady(); + inline bool isEqualRect(const core::Rect &r) const { return r == rect; } protected: - os::Mutex mutex; - rfb::Rect rect; - bool initialized; + core::Rect rect; - H264DecoderContext(const Rect &r) : rect(r) { initialized = false; } - - virtual bool initCodec() { return false; } - virtual void freeCodec() {} + H264DecoderContext(const core::Rect &r) : rect(r) {} }; + } #endif diff --git a/common/rfb/H264LibavDecoderContext.cxx b/common/rfb/H264LibavDecoderContext.cxx index 2d8d03e7..ba1b1b6d 100644 --- a/common/rfb/H264LibavDecoderContext.cxx +++ b/common/rfb/H264LibavDecoderContext.cxx @@ -22,6 +22,9 @@ #include <config.h> #endif +#include <new> +#include <stdexcept> + extern "C" { #include <libavutil/imgutils.h> #include <libavcodec/version.h> @@ -33,41 +36,31 @@ extern "C" { #define FFMPEG_INIT_PACKET_DEPRECATED #endif -#include <rfb/LogWriter.h> #include <rfb/PixelBuffer.h> #include <rfb/H264LibavDecoderContext.h> using namespace rfb; -static LogWriter vlog("H264LibavDecoderContext"); - -bool H264LibavDecoderContext::initCodec() { - os::AutoMutex lock(&mutex); - +H264LibavDecoderContext::H264LibavDecoderContext(const core::Rect& r) + : H264DecoderContext(r) +{ sws = nullptr; h264WorkBuffer = nullptr; h264WorkBufferLength = 0; const AVCodec *codec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!codec) - { - vlog.error("Codec not found"); - return false; - } + throw std::runtime_error("Codec not found"); parser = av_parser_init(codec->id); if (!parser) - { - vlog.error("Could not create H264 parser"); - return false; - } + throw std::runtime_error("Could not create H264 parser"); avctx = avcodec_alloc_context3(codec); if (!avctx) { av_parser_close(parser); - vlog.error("Could not allocate video codec context"); - return false; + throw std::runtime_error("Could not allocate video codec context"); } frame = av_frame_alloc(); @@ -75,8 +68,7 @@ bool H264LibavDecoderContext::initCodec() { { av_parser_close(parser); avcodec_free_context(&avctx); - vlog.error("Could not allocate video frame"); - return false; + throw std::runtime_error("Could not allocate video frame"); } if (avcodec_open2(avctx, codec, nullptr) < 0) @@ -84,26 +76,18 @@ bool H264LibavDecoderContext::initCodec() { av_parser_close(parser); avcodec_free_context(&avctx); av_frame_free(&frame); - vlog.error("Could not open codec"); - return false; + throw std::runtime_error("Could not open video codec"); } - - initialized = true; - return true; } -void H264LibavDecoderContext::freeCodec() { - os::AutoMutex lock(&mutex); - - if (!initialized) - return; +H264LibavDecoderContext::~H264LibavDecoderContext() +{ av_parser_close(parser); avcodec_free_context(&avctx); av_frame_free(&rgbFrame); av_frame_free(&frame); sws_freeContext(sws); free(h264WorkBuffer); - initialized = false; } // We need to reallocate buffer because AVPacket uses non-const pointer. @@ -130,9 +114,6 @@ uint8_t* H264LibavDecoderContext::makeH264WorkBuffer(const uint8_t* buffer, uint void H264LibavDecoderContext::decode(const uint8_t* h264_in_buffer, uint32_t len, ModifiablePixelBuffer* pb) { - os::AutoMutex lock(&mutex); - if (!initialized) - return; uint8_t* h264_work_buffer = makeH264WorkBuffer(h264_in_buffer, len); #ifdef FFMPEG_INIT_PACKET_DEPRECATED @@ -147,19 +128,15 @@ void H264LibavDecoderContext::decode(const uint8_t* h264_in_buffer, while (len) { ret = av_parser_parse2(parser, avctx, &packet->data, &packet->size, h264_work_buffer, len, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + // Silently ignore errors, hoping its a temporary encoding glitch if (ret < 0) - { - vlog.error("Error while parsing"); break; - } // We need to slap on tv to make it work here (don't ask me why) if (!packet->size && len == static_cast<uint32_t>(ret)) ret = av_parser_parse2(parser, avctx, &packet->data, &packet->size, h264_work_buffer, len, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); + // Silently ignore errors, hoping its a temporary encoding glitch if (ret < 0) - { - vlog.error("Error while parsing"); break; - } h264_work_buffer += ret; len -= ret; @@ -176,27 +153,21 @@ void H264LibavDecoderContext::decode(const uint8_t* h264_in_buffer, #ifndef FFMPEG_DECODE_VIDEO2_DEPRECATED int got_frame; ret = avcodec_decode_video2(avctx, frame, &got_frame, packet); + // Silently ignore errors, hoping its a temporary encoding glitch if (ret < 0 || !got_frame) - { - vlog.error("Error during decoding"); break; - } #else ret = avcodec_send_packet(avctx, packet); + // Silently ignore errors, hoping its a temporary encoding glitch if (ret < 0) - { - vlog.error("Error sending a packet to decoding"); break; - } ret = avcodec_receive_frame(avctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; - else if (ret < 0) - { - vlog.error("Error during decoding"); + // Silently ignore errors, hoping its a temporary encoding glitch + if (ret < 0) break; - } #endif frames_received++; } diff --git a/common/rfb/H264LibavDecoderContext.h b/common/rfb/H264LibavDecoderContext.h index 96558bee..c23fdf79 100644 --- a/common/rfb/H264LibavDecoderContext.h +++ b/common/rfb/H264LibavDecoderContext.h @@ -31,16 +31,12 @@ extern "C" { namespace rfb { class H264LibavDecoderContext : public H264DecoderContext { public: - H264LibavDecoderContext(const Rect &r) : H264DecoderContext(r) {} - ~H264LibavDecoderContext() { freeCodec(); } + H264LibavDecoderContext(const core::Rect &r); + ~H264LibavDecoderContext(); void decode(const uint8_t* h264_buffer, uint32_t len, ModifiablePixelBuffer* pb) override; - protected: - bool initCodec() override; - void freeCodec() override; - private: uint8_t* makeH264WorkBuffer(const uint8_t* buffer, uint32_t len); diff --git a/common/rfb/H264WinDecoderContext.cxx b/common/rfb/H264WinDecoderContext.cxx index a9b13942..71086fad 100644 --- a/common/rfb/H264WinDecoderContext.cxx +++ b/common/rfb/H264WinDecoderContext.cxx @@ -22,48 +22,38 @@ #include <config.h> #endif +#include <stdexcept> + #include <mfapi.h> #include <mferror.h> #include <wmcodecdsp.h> #define SAFE_RELEASE(obj) if (obj) { obj->Release(); obj = nullptr; } -#include <os/Mutex.h> -#include <rfb/LogWriter.h> #include <rfb/PixelBuffer.h> #include <rfb/H264WinDecoderContext.h> using namespace rfb; -static LogWriter vlog("H264WinDecoderContext"); - // Older MinGW lacks this definition #ifndef HAVE_VIDEO_PROCESSOR_MFT static GUID CLSID_VideoProcessorMFT = { 0x88753b26, 0x5b24, 0x49bd, { 0xb2, 0xe7, 0xc, 0x44, 0x5c, 0x78, 0xc9, 0x82 } }; #endif -bool H264WinDecoderContext::initCodec() { - os::AutoMutex lock(&mutex); - +H264WinDecoderContext::H264WinDecoderContext(const core::Rect &r) + : H264DecoderContext(r) +{ if (FAILED(MFStartup(MF_VERSION, MFSTARTUP_LITE))) - { - vlog.error("Could not initialize MediaFoundation"); - return false; - } + throw std::runtime_error("Could not initialize MediaFoundation"); if (FAILED(CoCreateInstance(CLSID_CMSH264DecoderMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&decoder))) - { - vlog.error("MediaFoundation H264 codec not found"); - return false; - } + throw std::runtime_error("MediaFoundation H264 codec not found"); if (FAILED(CoCreateInstance(CLSID_VideoProcessorMFT, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&converter))) { - vlog.error("Cannot create MediaFoundation Video Processor (available only on Windows 8+). Trying ColorConvert DMO."); if (FAILED(CoCreateInstance(CLSID_CColorConvertDMO, nullptr, CLSCTX_INPROC_SERVER, IID_IMFTransform, (LPVOID*)&converter))) { decoder->Release(); - vlog.error("ColorConvert DMO not found"); - return false; + throw std::runtime_error("MediaFoundation H264 codec not found"); } } @@ -72,10 +62,7 @@ bool H264WinDecoderContext::initCodec() { if (SUCCEEDED(decoder->GetAttributes(&attributes))) { GUID MF_LOW_LATENCY = { 0x9c27891a, 0xed7a, 0x40e1, { 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee } }; - if (SUCCEEDED(attributes->SetUINT32(MF_LOW_LATENCY, TRUE))) - { - vlog.info("Enabled low latency mode"); - } + attributes->SetUINT32(MF_LOW_LATENCY, TRUE); attributes->Release(); } @@ -85,8 +72,7 @@ bool H264WinDecoderContext::initCodec() { { decoder->Release(); converter->Release(); - vlog.error("Could not create MF MediaType"); - return false; + throw std::runtime_error("Could not create MF MediaType"); } input_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video); input_type->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_H264); @@ -113,8 +99,7 @@ bool H264WinDecoderContext::initCodec() { decoder->Release(); converter->Release(); input_type->Release(); - vlog.error("Could not start H264 decoder"); - return false; + throw std::runtime_error("Could not start H264 decoder"); } MFT_OUTPUT_STREAM_INFO info; @@ -134,22 +119,14 @@ bool H264WinDecoderContext::initCodec() { SAFE_RELEASE(converted_sample); SAFE_RELEASE(input_buffer); SAFE_RELEASE(decoded_buffer); - vlog.error("Could not allocate media samples/buffers"); - return false; + throw std::runtime_error("Could not allocate media samples/buffers"); } input_sample->AddBuffer(input_buffer); decoded_sample->AddBuffer(decoded_buffer); - - initialized = true; - return true; } -void H264WinDecoderContext::freeCodec() { - os::AutoMutex lock(&mutex); - - if (!initialized) - return; +H264WinDecoderContext::~H264WinDecoderContext() { SAFE_RELEASE(decoder) SAFE_RELEASE(converter) SAFE_RELEASE(input_sample) @@ -159,24 +136,16 @@ void H264WinDecoderContext::freeCodec() { SAFE_RELEASE(decoded_buffer) SAFE_RELEASE(converted_buffer) MFShutdown(); - initialized = false; } void H264WinDecoderContext::decode(const uint8_t* h264_buffer, uint32_t len, ModifiablePixelBuffer* pb) { - os::AutoMutex lock(&mutex); - if (!initialized) - return; - if (FAILED(input_buffer->SetCurrentLength(len))) { input_buffer->Release(); if (FAILED(MFCreateMemoryBuffer(len, &input_buffer))) - { - vlog.error("Could not allocate media buffer"); - return; - } + throw std::runtime_error("Could not allocate media buffer"); input_buffer->SetCurrentLength(len); input_sample->RemoveAllBuffers(); input_sample->AddBuffer(input_buffer); @@ -187,14 +156,12 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, memcpy(locked, h264_buffer, len); input_buffer->Unlock(); - vlog.debug("Received %u bytes, decoding", len); - // extract actual size, including possible cropping ParseSPS(h264_buffer, len); if (FAILED(decoder->ProcessInput(0, input_sample, 0))) { - vlog.error("Error sending a packet to decoding"); + // Silently ignore errors, hoping its a temporary encoding glitch return; } @@ -219,7 +186,6 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, if (SUCCEEDED(hr)) { - vlog.debug("Frame decoded"); // successfully decoded next frame // but do not exit loop, try again if there is next frame decoded = true; @@ -259,7 +225,7 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, UINT32 width, height; if FAILED(MFGetAttributeSize(output_type, MF_MT_FRAME_SIZE, &width, &height)) { - vlog.error("Error getting output type size"); + // Silently ignore errors, hoping its a temporary encoding glitch output_type->Release(); break; } @@ -279,13 +245,11 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, crop_height = height; } - vlog.debug("Setting up decoded output with %ux%u size", crop_width, crop_height); - // input type to converter, BGRX pixel format IMFMediaType* converted_type; if (FAILED(MFCreateMediaType(&converted_type))) { - vlog.error("Error creating media type"); + // Silently ignore errors, hoping its a temporary encoding glitch } else { @@ -310,7 +274,7 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, if (FAILED(MFCreateMemoryBuffer(info.cbSize, &converted_buffer))) { - vlog.error("Error creating media buffer"); + // Silently ignore errors, hoping its a temporary encoding glitch } else { @@ -327,7 +291,7 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, { if (FAILED(converter->ProcessInput(0, decoded_sample, 0))) { - vlog.error("Error sending a packet to converter"); + // Silently ignore errors, hoping its a temporary encoding glitch return; } @@ -343,12 +307,10 @@ void H264WinDecoderContext::decode(const uint8_t* h264_buffer, if (FAILED(hr)) { - vlog.error("Error converting to RGB"); + // Silently ignore errors, hoping its a temporary encoding glitch } else { - vlog.debug("Frame converted to RGB"); - BYTE* out; DWORD buflen; converted_buffer->Lock(&out, nullptr, &buflen); @@ -528,8 +490,6 @@ void H264WinDecoderContext::ParseSPS(const uint8_t* buffer, int length) offset_x = frame_crop_left_offset; offset_y = frame_crop_bottom_offset; - vlog.debug("SPS parsing - full=%dx%d, cropped=%dx%d, offset=%d,%d", full_width, full_height, crop_width, crop_height, offset_x, offset_y); - #undef SKIP_BITS #undef SKIP_UE #undef GET_BITS diff --git a/common/rfb/H264WinDecoderContext.h b/common/rfb/H264WinDecoderContext.h index 92041781..b1dbf0e1 100644 --- a/common/rfb/H264WinDecoderContext.h +++ b/common/rfb/H264WinDecoderContext.h @@ -30,16 +30,12 @@ namespace rfb { class H264WinDecoderContext : public H264DecoderContext { public: - H264WinDecoderContext(const Rect &r) : H264DecoderContext(r) {}; - ~H264WinDecoderContext() { freeCodec(); } + H264WinDecoderContext(const core::Rect &r); + ~H264WinDecoderContext(); void decode(const uint8_t* h264_buffer, uint32_t len, ModifiablePixelBuffer* pb) override; - protected: - bool initCodec() override; - void freeCodec() override; - private: LONG stride; uint32_t full_width = 0; diff --git a/common/rfb/HextileDecoder.cxx b/common/rfb/HextileDecoder.cxx index 35ec7928..c6eb428b 100644 --- a/common/rfb/HextileDecoder.cxx +++ b/common/rfb/HextileDecoder.cxx @@ -21,6 +21,8 @@ #include <config.h> #endif +#include <algorithm> + #include <rdr/InStream.h> #include <rdr/MemInStream.h> #include <rdr/OutStream.h> @@ -41,10 +43,10 @@ HextileDecoder::~HextileDecoder() { } -bool HextileDecoder::readRect(const Rect& r, rdr::InStream* is, +bool HextileDecoder::readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) { - Rect t; + core::Rect t; size_t bytesPerPixel; is->setRestorePoint(); @@ -53,12 +55,12 @@ bool HextileDecoder::readRect(const Rect& r, rdr::InStream* is, for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) { - t.br.y = __rfbmin(r.br.y, t.tl.y + 16); + t.br.y = std::min(r.br.y, t.tl.y + 16); for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) { uint8_t tileType; - t.br.x = __rfbmin(r.br.x, t.tl.x + 16); + t.br.x = std::min(r.br.x, t.tl.x + 16); if (!is->hasDataOrRestore(1)) return false; @@ -113,7 +115,7 @@ bool HextileDecoder::readRect(const Rect& r, rdr::InStream* is, return true; } -void HextileDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void HextileDecoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) { @@ -138,22 +140,22 @@ inline T HextileDecoder::readPixel(rdr::InStream* is) } template<class T> -void HextileDecoder::hextileDecode(const Rect& r, rdr::InStream* is, +void HextileDecoder::hextileDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb) { - Rect t; + core::Rect t; T bg = 0; T fg = 0; T buf[16 * 16]; for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) { - t.br.y = __rfbmin(r.br.y, t.tl.y + 16); + t.br.y = std::min(r.br.y, t.tl.y + 16); for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 16) { - t.br.x = __rfbmin(r.br.x, t.tl.x + 16); + t.br.x = std::min(r.br.x, t.tl.x + 16); int tileType = is->readU8(); diff --git a/common/rfb/HextileDecoder.h b/common/rfb/HextileDecoder.h index 38e8b776..6ff94a1f 100644 --- a/common/rfb/HextileDecoder.h +++ b/common/rfb/HextileDecoder.h @@ -29,17 +29,17 @@ namespace rfb { public: HextileDecoder(); virtual ~HextileDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; private: template<class T> inline T readPixel(rdr::InStream* is); template<class T> - void hextileDecode(const Rect& r, rdr::InStream* is, + void hextileDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb); }; diff --git a/common/rfb/HextileEncoder.cxx b/common/rfb/HextileEncoder.cxx index 0666d02d..5ee07a2d 100644 --- a/common/rfb/HextileEncoder.cxx +++ b/common/rfb/HextileEncoder.cxx @@ -22,21 +22,28 @@ #include <config.h> #endif +#include <algorithm> + +#include <core/Configuration.h> + +#include <rdr/OutStream.h> + #include <rfb/encodings.h> #include <rfb/SConnection.h> #include <rfb/HextileEncoder.h> #include <rfb/Palette.h> #include <rfb/PixelBuffer.h> -#include <rfb/Configuration.h> #include <rfb/hextileConstants.h> using namespace rfb; -BoolParameter improvedHextile("ImprovedHextile", - "Use improved compression algorithm for Hextile " - "encoding which achieves better compression " - "ratios by the cost of using more CPU time", - true); +core::BoolParameter improvedHextile("ImprovedHextile", + "Use improved compression " + "algorithm for Hextile encoding " + "which achieves better compression " + "ratios by the cost of using more " + "CPU time", + true); HextileEncoder::HextileEncoder(SConnection* conn_) : Encoder(conn_, encodingHextile, EncoderPlain) @@ -115,7 +122,7 @@ template<class T> void HextileEncoder::hextileEncode(rdr::OutStream* os, const PixelBuffer* pb) { - Rect t; + core::Rect t; T buf[256]; T oldBg = 0, oldFg = 0; bool oldBgValid = false; @@ -124,11 +131,11 @@ void HextileEncoder::hextileEncode(rdr::OutStream* os, for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) { - t.br.y = __rfbmin(pb->height(), t.tl.y + 16); + t.br.y = std::min(pb->height(), t.tl.y + 16); for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) { - t.br.x = __rfbmin(pb->width(), t.tl.x + 16); + t.br.x = std::min(pb->width(), t.tl.x + 16); pb->getImage(buf, t); @@ -532,7 +539,7 @@ template<class T> void HextileEncoder::hextileEncodeBetter(rdr::OutStream* os, const PixelBuffer* pb) { - Rect t; + core::Rect t; T buf[256]; T oldBg = 0, oldFg = 0; bool oldBgValid = false; @@ -543,11 +550,11 @@ void HextileEncoder::hextileEncodeBetter(rdr::OutStream* os, for (t.tl.y = 0; t.tl.y < pb->height(); t.tl.y += 16) { - t.br.y = __rfbmin(pb->height(), t.tl.y + 16); + t.br.y = std::min(pb->height(), t.tl.y + 16); for (t.tl.x = 0; t.tl.x < pb->width(); t.tl.x += 16) { - t.br.x = __rfbmin(pb->width(), t.tl.x + 16); + t.br.x = std::min(pb->width(), t.tl.x + 16); pb->getImage(buf, t); diff --git a/common/rfb/Hostname.h b/common/rfb/Hostname.h deleted file mode 100644 index f43e5067..00000000 --- a/common/rfb/Hostname.h +++ /dev/null @@ -1,120 +0,0 @@ -/* 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. - */ - -#ifndef __RFB_HOSTNAME_H__ -#define __RFB_HOSTNAME_H__ - -#include <assert.h> -#include <ctype.h> -#include <stdlib.h> -#include <string.h> - -#include <stdexcept> - -#include <rfb/util.h> - -namespace rfb { - - static bool isAllSpace(const char *string) { - if (string == nullptr) - return false; - while(*string != '\0') { - if (! isspace(*string)) - return false; - string++; - } - return true; - } - - static inline void getHostAndPort(const char* hi, std::string* host, - int* port, int basePort=5900) - { - const char* hostStart; - const char* hostEnd; - const char* portStart; - - if (hi == nullptr) - throw std::invalid_argument("NULL host specified"); - - // Trim leading whitespace - while(isspace(*hi)) - hi++; - - assert(host); - assert(port); - - if (hi[0] == '[') { - hostStart = &hi[1]; - hostEnd = strchr(hostStart, ']'); - if (hostEnd == nullptr) - throw std::invalid_argument("Unmatched [ in host"); - - portStart = hostEnd + 1; - if (isAllSpace(portStart)) - portStart = nullptr; - } else { - hostStart = &hi[0]; - hostEnd = strrchr(hostStart, ':'); - - if (hostEnd == nullptr) { - hostEnd = hostStart + strlen(hostStart); - portStart = nullptr; - } else { - if ((hostEnd > hostStart) && (hostEnd[-1] == ':')) - hostEnd--; - portStart = strchr(hostStart, ':'); - if (portStart != hostEnd) { - // We found more : in the host. This is probably an IPv6 address - hostEnd = hostStart + strlen(hostStart); - portStart = nullptr; - } - } - } - - // Back up past trailing space - while(isspace(*(hostEnd - 1)) && hostEnd > hostStart) - hostEnd--; - - if (hostStart == hostEnd) - *host = "localhost"; - else - *host = std::string(hostStart, hostEnd - hostStart); - - if (portStart == nullptr) - *port = basePort; - else { - char* end; - - if (portStart[0] != ':') - throw std::invalid_argument("Invalid port specified"); - - if (portStart[1] != ':') - *port = strtol(portStart + 1, &end, 10); - else - *port = strtol(portStart + 2, &end, 10); - if (*end != '\0' && ! isAllSpace(end)) - throw std::invalid_argument("Invalid port specified"); - - if ((portStart[1] != ':') && (*port < 100)) - *port += basePort; - } - } - -}; - -#endif // __RFB_HOSTNAME_H__ diff --git a/common/rfb/JpegCompressor.cxx b/common/rfb/JpegCompressor.cxx index 67a86cd9..d8216c99 100644 --- a/common/rfb/JpegCompressor.cxx +++ b/common/rfb/JpegCompressor.cxx @@ -24,8 +24,9 @@ #include <stdexcept> +#include <core/Rect.h> + #include <rfb/JpegCompressor.h> -#include <rfb/Rect.h> #include <rfb/PixelFormat.h> #include <rfb/ClientParams.h> @@ -157,7 +158,8 @@ JpegCompressor::~JpegCompressor(void) } void JpegCompressor::compress(const uint8_t *buf, volatile int stride, - const Rect& r, const PixelFormat& pf, + const core::Rect& r, + const PixelFormat& pf, int quality, int subsamp) { int w = r.width(); diff --git a/common/rfb/JpegCompressor.h b/common/rfb/JpegCompressor.h index 26194204..2460f62b 100644 --- a/common/rfb/JpegCompressor.h +++ b/common/rfb/JpegCompressor.h @@ -25,9 +25,9 @@ #ifndef __RFB_JPEGCOMPRESSOR_H__ #define __RFB_JPEGCOMPRESSOR_H__ +#include <core/Rect.h> + #include <rdr/MemOutStream.h> -#include <rfb/PixelFormat.h> -#include <rfb/Rect.h> struct jpeg_compress_struct; @@ -36,6 +36,9 @@ struct JPEG_DEST_MGR; namespace rfb { + class PixelFormat; + struct Rect; + class JpegCompressor : public rdr::MemOutStream { public: @@ -43,7 +46,8 @@ namespace rfb { JpegCompressor(int bufferLen = 128*1024); virtual ~JpegCompressor(); - void compress(const uint8_t *, int, const Rect&, const PixelFormat&, int, int); + void compress(const uint8_t*, int, const core::Rect&, + const PixelFormat&, int, int); void writeBytes(const uint8_t*, int); diff --git a/common/rfb/JpegDecompressor.cxx b/common/rfb/JpegDecompressor.cxx index 10c9e49c..ef548be0 100644 --- a/common/rfb/JpegDecompressor.cxx +++ b/common/rfb/JpegDecompressor.cxx @@ -23,9 +23,10 @@ #include <config.h> #endif +#include <core/Rect.h> + #include <rfb/JpegDecompressor.h> #include <rfb/Exception.h> -#include <rfb/Rect.h> #include <rfb/PixelFormat.h> #include <stdio.h> @@ -153,7 +154,8 @@ JpegDecompressor::~JpegDecompressor(void) void JpegDecompressor::decompress(const uint8_t *jpegBuf, int jpegBufLen, uint8_t *buf, volatile int stride, - const Rect& r, const PixelFormat& pf) + const core::Rect& r, + const PixelFormat& pf) { int w = r.width(); int h = r.height(); diff --git a/common/rfb/JpegDecompressor.h b/common/rfb/JpegDecompressor.h index 5d4f0c21..8e651b1c 100644 --- a/common/rfb/JpegDecompressor.h +++ b/common/rfb/JpegDecompressor.h @@ -26,16 +26,19 @@ #ifndef __RFB_JPEGDECOMPRESSOR_H__ #define __RFB_JPEGDECOMPRESSOR_H__ -#include <rfb/PixelFormat.h> -#include <rfb/Rect.h> +#include <stdint.h> struct jpeg_decompress_struct; struct JPEG_ERROR_MGR; struct JPEG_SRC_MGR; +namespace core { struct Rect; } + namespace rfb { + class PixelFormat; + class JpegDecompressor { public: @@ -43,8 +46,8 @@ namespace rfb { JpegDecompressor(void); virtual ~JpegDecompressor(); - void decompress(const uint8_t *, int, uint8_t *, int, const Rect&, - const PixelFormat&); + void decompress(const uint8_t*, int, uint8_t*, int, + const core::Rect&, const PixelFormat&); private: diff --git a/common/rfb/KeyRemapper.cxx b/common/rfb/KeyRemapper.cxx index 1c478178..24740321 100644 --- a/common/rfb/KeyRemapper.cxx +++ b/common/rfb/KeyRemapper.cxx @@ -23,59 +23,31 @@ #include <stdio.h> #include <string.h> -#include <os/Mutex.h> +#include <core/Configuration.h> +#include <core/LogWriter.h> #include <rfb/KeyRemapper.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("KeyRemapper"); +static core::LogWriter vlog("KeyRemapper"); KeyRemapper KeyRemapper::defInstance; -KeyRemapper::KeyRemapper(const char* m) +KeyRemapper::KeyRemapper() { - mutex = new os::Mutex; - - setMapping(m); } KeyRemapper::~KeyRemapper() { - delete mutex; } -void KeyRemapper::setMapping(const char* m) { - os::AutoMutex a(mutex); - - mapping.clear(); - while (m[0]) { - int from, to; - char bidi; - const char* nextComma = strchr(m, ','); - if (!nextComma) - nextComma = m + strlen(m); - if (sscanf(m, "0x%x%c>0x%x", &from, - &bidi, &to) == 3) { - if (bidi != '-' && bidi != '<') - vlog.error("Warning: Unknown operation %c>, assuming ->", bidi); - mapping[from] = to; - if (bidi == '<') - mapping[to] = from; - } else { - vlog.error("Warning: Bad mapping %.*s", (int)(nextComma-m), m); - } - m = nextComma; - if (nextComma[0]) - m++; - } +void KeyRemapper::setMapping(const std::map<uint32_t,uint32_t>& m) +{ + mapping = m; } uint32_t KeyRemapper::remapKey(uint32_t key) const { - os::AutoMutex a(mutex); - std::map<uint32_t,uint32_t>::const_iterator i = mapping.find(key); if (i != mapping.end()) return i->second; @@ -83,15 +55,41 @@ uint32_t KeyRemapper::remapKey(uint32_t key) const { } -class KeyMapParameter : public StringParameter { +class KeyMapParameter : public core::StringListParameter { public: KeyMapParameter() - : StringParameter("RemapKeys", "Comma-separated list of incoming keysyms to remap. Mappings are expressed as two hex values, prefixed by 0x, and separated by ->", "") { - KeyRemapper::defInstance.setMapping(""); + : core::StringListParameter("RemapKeys", + "Comma-separated list of incoming " + "keysyms to remap. Mappings are " + "expressed as two hex values, prefixed " + "by 0x, and separated by ->", + {}) { + KeyRemapper::defInstance.setMapping({}); } bool setParam(const char* v) override { - KeyRemapper::defInstance.setMapping(v); - return StringParameter::setParam(v); + std::map<uint32_t,uint32_t> mapping; + + if (!core::StringListParameter::setParam(v)) + return false; + + for (const char* m : *this) { + int from, to; + char bidi; + if (sscanf(m, "0x%x%c>0x%x", &from, + &bidi, &to) == 3) { + if (bidi != '-' && bidi != '<') + vlog.error("Warning: Unknown operation %c>, assuming ->", bidi); + mapping[from] = to; + if (bidi == '<') + mapping[to] = from; + } else { + vlog.error("Warning: Bad mapping %s", m); + } + } + + KeyRemapper::defInstance.setMapping(mapping); + + return true; } } defaultParam; diff --git a/common/rfb/KeyRemapper.h b/common/rfb/KeyRemapper.h index 89853721..71ef0a87 100644 --- a/common/rfb/KeyRemapper.h +++ b/common/rfb/KeyRemapper.h @@ -23,20 +23,17 @@ #include <stdint.h> -namespace os { class Mutex; } - namespace rfb { class KeyRemapper { public: - KeyRemapper(const char* m=""); + KeyRemapper(); ~KeyRemapper(); - void setMapping(const char* m); + void setMapping(const std::map<uint32_t,uint32_t>& m); uint32_t remapKey(uint32_t key) const; static KeyRemapper defInstance; private: std::map<uint32_t,uint32_t> mapping; - os::Mutex* mutex; }; }; diff --git a/common/rfb/PixelBuffer.cxx b/common/rfb/PixelBuffer.cxx index 5590c214..32b0ce2f 100644 --- a/common/rfb/PixelBuffer.cxx +++ b/common/rfb/PixelBuffer.cxx @@ -30,13 +30,14 @@ #include <stdexcept> -#include <rfb/LogWriter.h> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rfb/PixelBuffer.h> -#include <rfb/util.h> using namespace rfb; -static LogWriter vlog("PixelBuffer"); +static core::LogWriter vlog("PixelBuffer"); // We do a lot of byte offset calculations that assume the result fits // inside a signed 32 bit integer. Limit the maximum size of pixel @@ -63,7 +64,8 @@ PixelBuffer::~PixelBuffer() {} void -PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const +PixelBuffer::getImage(void* imageBuf, const core::Rect& r, + int outStride) const { int inStride; const uint8_t* data; @@ -72,10 +74,9 @@ PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const const uint8_t* end; if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); data = getBuffer(r, &inStride); @@ -98,7 +99,7 @@ PixelBuffer::getImage(void* imageBuf, const Rect& r, int outStride) const } void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf, - const Rect& r, int stride) const + const core::Rect& r, int stride) const { const uint8_t* srcBuffer; int srcStride; @@ -109,10 +110,9 @@ void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf, } if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); if (stride == 0) stride = r.width(); @@ -126,9 +126,11 @@ void PixelBuffer::getImage(const PixelFormat& pf, void* imageBuf, void PixelBuffer::setSize(int width, int height) { if ((width < 0) || (width > maxPixelBufferWidth)) - throw std::out_of_range(rfb::format("Invalid PixelBuffer width of %d pixels requested", width)); + throw std::out_of_range(core::format( + "Invalid PixelBuffer width of %d pixels requested", width)); if ((height < 0) || (height > maxPixelBufferHeight)) - throw std::out_of_range(rfb::format("Invalid PixelBuffer height of %d pixels requested", height)); + throw std::out_of_range(core::format( + "Invalid PixelBuffer height of %d pixels requested", height)); width_ = width; height_ = height; @@ -150,17 +152,17 @@ ModifiablePixelBuffer::~ModifiablePixelBuffer() { } -void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix) +void ModifiablePixelBuffer::fillRect(const core::Rect& r, + const void* pix) { int stride; uint8_t *buf; int w, h, b; if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); w = r.width(); h = r.height(); @@ -199,7 +201,7 @@ void ModifiablePixelBuffer::fillRect(const Rect& r, const void* pix) commitBufferRW(r); } -void ModifiablePixelBuffer::imageRect(const Rect& r, +void ModifiablePixelBuffer::imageRect(const core::Rect& r, const void* pixels, int srcStride) { uint8_t* dest; @@ -209,10 +211,9 @@ void ModifiablePixelBuffer::imageRect(const Rect& r, uint8_t* end; if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); bytesPerPixel = getPF().bpp/8; @@ -237,29 +238,29 @@ void ModifiablePixelBuffer::imageRect(const Rect& r, commitBufferRW(r); } -void ModifiablePixelBuffer::copyRect(const Rect &rect, - const Point &move_by_delta) +void ModifiablePixelBuffer::copyRect(const core::Rect& rect, + const core::Point& move_by_delta) { int srcStride, dstStride; int bytesPerPixel; const uint8_t* srcData; uint8_t* dstData; - Rect drect, srect; + core::Rect drect, srect; drect = rect; if (!drect.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", - drect.width(), drect.height(), - drect.tl.x, drect.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", + drect.width(), drect.height(), drect.tl.x, drect.tl.y, + width(), height())); srect = drect.translate(move_by_delta.negate()); if (!srect.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", - srect.width(), srect.height(), - srect.tl.x, srect.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Source rect %dx%d at %d,%d exceeds framebuffer %dx%d", + srect.width(), srect.height(), srect.tl.x, srect.tl.y, + width(), height())); bytesPerPixel = format.bpp/8; @@ -297,7 +298,8 @@ void ModifiablePixelBuffer::copyRect(const Rect &rect, commitBufferRW(drect); } -void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest, +void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, + const core::Rect& dest, const void* pix) { uint8_t buf[4]; @@ -305,17 +307,18 @@ void ModifiablePixelBuffer::fillRect(const PixelFormat& pf, const Rect &dest, fillRect(dest, buf); } -void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, const Rect &dest, +void ModifiablePixelBuffer::imageRect(const PixelFormat& pf, + const core::Rect& dest, const void* pixels, int stride) { uint8_t* dstBuffer; int dstStride; if (!dest.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", - dest.width(), dest.height(), - dest.tl.x, dest.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Destination rect %dx%d at %d,%d exceeds framebuffer %dx%d", + dest.width(), dest.height(), dest.tl.x, dest.tl.y, + width(), height())); if (stride == 0) stride = dest.width(); @@ -339,29 +342,29 @@ FullFramePixelBuffer::FullFramePixelBuffer() : data(nullptr) {} FullFramePixelBuffer::~FullFramePixelBuffer() {} -uint8_t* FullFramePixelBuffer::getBufferRW(const Rect& r, int* stride_) +uint8_t* FullFramePixelBuffer::getBufferRW(const core::Rect& r, + int* stride_) { if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); *stride_ = stride; return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)]; } -void FullFramePixelBuffer::commitBufferRW(const Rect& /*r*/) +void FullFramePixelBuffer::commitBufferRW(const core::Rect& /*r*/) { } -const uint8_t* FullFramePixelBuffer::getBuffer(const Rect& r, int* stride_) const +const uint8_t* FullFramePixelBuffer::getBuffer(const core::Rect& r, + int* stride_) const { if (!r.enclosed_by(getRect())) - throw std::out_of_range(rfb::format("Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d", - r.width(), r.height(), - r.tl.x, r.tl.y, - width(), height())); + throw std::out_of_range(core::format( + "Pixel buffer request %dx%d at %d,%d exceeds framebuffer %dx%d", + r.width(), r.height(), r.tl.x, r.tl.y, width(), height())); *stride_ = stride; return &data[(r.tl.x + (r.tl.y * stride)) * (format.bpp/8)]; @@ -371,13 +374,17 @@ void FullFramePixelBuffer::setBuffer(int width, int height, uint8_t* data_, int stride_) { if ((width < 0) || (width > maxPixelBufferWidth)) - throw std::out_of_range(rfb::format("Invalid PixelBuffer width of %d pixels requested", width)); + throw std::out_of_range(core::format( + "Invalid PixelBuffer width of %d pixels requested", width)); if ((height < 0) || (height > maxPixelBufferHeight)) - throw std::out_of_range(rfb::format("Invalid PixelBuffer height of %d pixels requested", height)); + throw std::out_of_range(core::format( + "Invalid PixelBuffer height of %d pixels requested", height)); if ((stride_ < 0) || (stride_ > maxPixelBufferStride) || (stride_ < width)) - throw std::invalid_argument(rfb::format("Invalid PixelBuffer stride of %d pixels requested", stride_)); + throw std::invalid_argument(core::format( + "Invalid PixelBuffer stride of %d pixels requested", stride_)); if ((width != 0) && (height != 0) && (data_ == nullptr)) - throw std::logic_error(rfb::format("PixelBuffer requested without a valid memory area")); + throw std::logic_error(core::format( + "PixelBuffer requested without a valid memory area")); ModifiablePixelBuffer::setSize(width, height); stride = stride_; diff --git a/common/rfb/PixelBuffer.h b/common/rfb/PixelBuffer.h index 963fbbf6..9fbea611 100644 --- a/common/rfb/PixelBuffer.h +++ b/common/rfb/PixelBuffer.h @@ -25,12 +25,13 @@ #ifndef __RFB_PIXEL_BUFFER_H__ #define __RFB_PIXEL_BUFFER_H__ +#include <core/Rect.h> + #include <rfb/PixelFormat.h> -#include <rfb/Rect.h> -namespace rfb { +namespace core { class Region; } - class Region; +namespace rfb { class PixelBuffer { public: @@ -52,9 +53,9 @@ namespace rfb { // Get rectangle encompassing this buffer // Top-left of rectangle is either at (0,0), or the specified point. - Rect getRect() const { return Rect(0, 0, width_, height_); } - Rect getRect(const Point& pos) const { - return Rect(pos, pos.translate(Point(width_, height_))); + core::Rect getRect() const { return {0, 0, width_, height_}; } + core::Rect getRect(const core::Point& pos) const { + return {pos, pos.translate({width_, height_})}; } /////////////////////////////////////////////// @@ -64,18 +65,20 @@ namespace rfb { // Get a pointer into the buffer // The pointer is to the top-left pixel of the specified Rect. // The buffer stride (in pixels) is returned. - virtual const uint8_t* getBuffer(const Rect& r, int* stride) const = 0; + virtual const uint8_t* getBuffer(const core::Rect& r, + int* stride) const = 0; // Get pixel data for a given part of the buffer // Data is copied into the supplied buffer, with the specified // stride. Try to avoid using this though as getBuffer() will in // most cases avoid the extra memory copy. - void getImage(void* imageBuf, const Rect& r, int stride=0) const; + void getImage(void* imageBuf, const core::Rect& r, + int stride=0) const; // Get pixel data in a given format // Works just the same as getImage(), but guaranteed to be in a // specific format. void getImage(const PixelFormat& pf, void* imageBuf, - const Rect& r, int stride=0) const; + const core::Rect& r, int stride=0) const; /////////////////////////////////////////////// // Framebuffer update methods @@ -84,7 +87,7 @@ namespace rfb { // Ensure that the specified rectangle of buffer is up to date. // Overridden by derived classes implementing framebuffer access // to copy the required display data into place. - virtual void grabRegion(const Region& /*region*/) {} + virtual void grabRegion(const core::Region& /*region*/) {} protected: PixelBuffer(); @@ -110,32 +113,35 @@ namespace rfb { // Get a writeable pointer into the buffer // Like getBuffer(), the pointer is to the top-left pixel of the // specified Rect and the stride in pixels is returned. - virtual uint8_t* getBufferRW(const Rect& r, int* stride) = 0; + virtual uint8_t* getBufferRW(const core::Rect& r, int* stride) = 0; // Commit the modified contents // Ensures that the changes to the specified Rect is properly // stored away and any temporary buffers are freed. The Rect given // here needs to match the Rect given to the earlier call to // getBufferRW(). - virtual void commitBufferRW(const Rect& r) = 0; + virtual void commitBufferRW(const core::Rect& r) = 0; /////////////////////////////////////////////// // Basic rendering operations // These operations DO NOT clip to the pixelbuffer area, or trap overruns. // Fill a rectangle - void fillRect(const Rect &dest, const void* pix); + void fillRect(const core::Rect& dest, const void* pix); // Copy pixel data to the buffer - void imageRect(const Rect &dest, const void* pixels, int stride=0); + void imageRect(const core::Rect& dest, const void* pixels, + int stride=0); // Copy pixel data from one PixelBuffer location to another - void copyRect(const Rect &dest, const Point& move_by_delta); + void copyRect(const core::Rect& dest, + const core::Point& move_by_delta); // Render in a specific format // Does the exact same thing as the above methods, but the given // pixel values are defined by the given PixelFormat. - void fillRect(const PixelFormat& pf, const Rect &dest, const void* pix); - void imageRect(const PixelFormat& pf, const Rect &dest, + void fillRect(const PixelFormat& pf, const core::Rect& dest, + const void* pix); + void imageRect(const PixelFormat& pf, const core::Rect& dest, const void* pixels, int stride=0); protected: @@ -151,9 +157,10 @@ namespace rfb { virtual ~FullFramePixelBuffer(); public: - const uint8_t* getBuffer(const Rect& r, int* stride) const override; - uint8_t* getBufferRW(const Rect& r, int* stride) override; - void commitBufferRW(const Rect& r) override; + const uint8_t* getBuffer(const core::Rect& r, + int* stride) const override; + uint8_t* getBufferRW(const core::Rect& r, int* stride) override; + void commitBufferRW(const core::Rect& r) override; protected: FullFramePixelBuffer(); diff --git a/common/rfb/PixelFormat.h b/common/rfb/PixelFormat.h index f0a16767..7b754da8 100644 --- a/common/rfb/PixelFormat.h +++ b/common/rfb/PixelFormat.h @@ -136,7 +136,7 @@ namespace rfb { /* Only for testing this class */ friend void makePixel(const rfb::PixelFormat &, uint8_t *); - friend bool verifyPixel(const rfb::PixelFormat &, + friend void verifyPixel(const rfb::PixelFormat &, const rfb::PixelFormat &, const uint8_t *); }; diff --git a/common/rfb/RREDecoder.cxx b/common/rfb/RREDecoder.cxx index 53ddc2da..d2c3d3e6 100644 --- a/common/rfb/RREDecoder.cxx +++ b/common/rfb/RREDecoder.cxx @@ -40,7 +40,7 @@ RREDecoder::~RREDecoder() { } -bool RREDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, +bool RREDecoder::readRect(const core::Rect& /*r*/, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) { uint32_t numRects; @@ -66,7 +66,7 @@ bool RREDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, return true; } -void RREDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void RREDecoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) { @@ -91,7 +91,7 @@ inline T RREDecoder::readPixel(rdr::InStream* is) } template<class T> -void RREDecoder::rreDecode(const Rect& r, rdr::InStream* is, +void RREDecoder::rreDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb) { @@ -109,6 +109,6 @@ void RREDecoder::rreDecode(const Rect& r, rdr::InStream* is, if (((x+w) > r.width()) || ((y+h) > r.height())) throw protocol_error("RRE decode error"); - pb->fillRect(pf, Rect(r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h), &pix); + pb->fillRect(pf, {r.tl.x+x, r.tl.y+y, r.tl.x+x+w, r.tl.y+y+h}, &pix); } } diff --git a/common/rfb/RREDecoder.h b/common/rfb/RREDecoder.h index 8490146c..3fdcdd85 100644 --- a/common/rfb/RREDecoder.h +++ b/common/rfb/RREDecoder.h @@ -29,17 +29,17 @@ namespace rfb { public: RREDecoder(); virtual ~RREDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; private: template<class T> inline T readPixel(rdr::InStream* is); template<class T> - void rreDecode(const Rect& r, rdr::InStream* is, + void rreDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb); }; } diff --git a/common/rfb/RawDecoder.cxx b/common/rfb/RawDecoder.cxx index f2ea586b..43ce15a4 100644 --- a/common/rfb/RawDecoder.cxx +++ b/common/rfb/RawDecoder.cxx @@ -37,7 +37,7 @@ RawDecoder::~RawDecoder() { } -bool RawDecoder::readRect(const Rect& r, rdr::InStream* is, +bool RawDecoder::readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) { if (!is->hasData(r.area() * (server.pf().bpp/8))) @@ -46,7 +46,7 @@ bool RawDecoder::readRect(const Rect& r, rdr::InStream* is, return true; } -void RawDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void RawDecoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) { diff --git a/common/rfb/RawDecoder.h b/common/rfb/RawDecoder.h index 2ac8b0bd..6c3a6357 100644 --- a/common/rfb/RawDecoder.h +++ b/common/rfb/RawDecoder.h @@ -25,10 +25,10 @@ namespace rfb { public: RawDecoder(); virtual ~RawDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; }; diff --git a/common/rfb/SConnection.cxx b/common/rfb/SConnection.cxx index a0a1c373..c698b991 100644 --- a/common/rfb/SConnection.cxx +++ b/common/rfb/SConnection.cxx @@ -26,6 +26,11 @@ #include <algorithm> +#include <core/LogWriter.h> +#include <core/string.h> + +#include <rdr/OutStream.h> + #include <rfb/Exception.h> #include <rfb/Security.h> #include <rfb/clipboardTypes.h> @@ -38,13 +43,10 @@ #include <rfb/encodings.h> #include <rfb/EncodeManager.h> #include <rfb/SSecurity.h> -#include <rfb/util.h> - -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("SConnection"); +static core::LogWriter vlog("SConnection"); SConnection::SConnection(AccessRights accessRights_) : readyForSetColourMapEntries(false), is(nullptr), os(nullptr), @@ -133,10 +135,10 @@ bool SConnection::processVersionMsg() if (client.majorVersion != 3) { // unknown protocol version - failConnection(format("Client needs protocol version %d.%d, " - "server has %d.%d", - client.majorVersion, client.minorVersion, - defaultMajorVersion, defaultMinorVersion)); + failConnection(core::format( + "Client needs protocol version %d.%d, server has %d.%d", + client.majorVersion, client.minorVersion, + defaultMajorVersion, defaultMinorVersion)); } if (client.minorVersion != 3 && client.minorVersion != 7 && client.minorVersion != 8) { @@ -152,8 +154,6 @@ bool SConnection::processVersionMsg() client.majorVersion,client.minorVersion); } - versionReceived(); - std::list<uint8_t> secTypes; std::list<uint8_t>::iterator i; secTypes = security.GetEnabledSecTypes(); @@ -166,9 +166,9 @@ bool SConnection::processVersionMsg() if (*i == secTypeNone || *i == secTypeVncAuth) break; } if (i == secTypes.end()) { - failConnection(format("No supported security type for " - "%d.%d client", - client.majorVersion, client.minorVersion)); + failConnection( + core::format("No supported security type for %d.%d client", + client.majorVersion, client.minorVersion)); } os->writeU32(*i); @@ -278,7 +278,7 @@ bool SConnection::processInitMsg() return reader_->readClientInit(); } -void SConnection::handleAuthFailureTimeout(Timer* /*t*/) +void SConnection::handleAuthFailureTimeout(core::Timer* /*t*/) { if (state_ != RFBSTATE_SECURITY_FAILURE) { close("SConnection::handleAuthFailureTimeout: Invalid state"); @@ -344,6 +344,8 @@ bool SConnection::accessCheck(AccessRights ar) const void SConnection::setEncodings(int nEncodings, const int32_t* encodings) { int i; + bool firstFence, firstContinuousUpdates, firstLEDState, + firstQEMUKeyEvent, firstExtMouseButtonsEvent; preferredEncoding = encodingRaw; for (i = 0;i < nEncodings;i++) { @@ -353,7 +355,26 @@ void SConnection::setEncodings(int nEncodings, const int32_t* encodings) } } - SMsgHandler::setEncodings(nEncodings, encodings); + firstFence = !client.supportsFence(); + firstContinuousUpdates = !client.supportsContinuousUpdates(); + firstLEDState = !client.supportsLEDState(); + firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent); + firstExtMouseButtonsEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons); + + client.setEncodings(nEncodings, encodings); + + supportsLocalCursor(); + + if (client.supportsFence() && firstFence) + supportsFence(); + if (client.supportsContinuousUpdates() && firstContinuousUpdates) + supportsContinuousUpdates(); + if (client.supportsLEDState() && firstLEDState) + supportsLEDState(); + if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) + writer()->writeQEMUKeyEvent(); + if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent) + writer()->writeExtendedMouseButtonsSupport(); if (client.supportsEncoding(pseudoEncodingExtendedClipboard)) { uint32_t sizes[] = { 0 }; @@ -373,9 +394,54 @@ void SConnection::clientCutText(const char* str) clientClipboard = str; hasRemoteClipboard = true; + if (!accessCheck(AccessCutText)) + return; + handleClipboardAnnounce(true); } +void SConnection::handleClipboardCaps(uint32_t flags, const uint32_t* lengths) +{ + int i; + + vlog.debug("Got client clipboard capabilities:"); + for (i = 0;i < 16;i++) { + if (flags & (1 << i)) { + const char *type; + + switch (1 << i) { + case clipboardUTF8: + type = "Plain text"; + break; + case clipboardRTF: + type = "Rich text"; + break; + case clipboardHTML: + type = "HTML"; + break; + case clipboardDIB: + type = "Images"; + break; + case clipboardFiles: + type = "Files"; + break; + default: + vlog.debug(" Unknown format 0x%x", 1 << i); + continue; + } + + if (lengths[i] == 0) + vlog.debug(" %s (only notify)", type); + else { + vlog.debug(" %s (automatically send up to %s)", + type, core::iecPrefix(lengths[i], "B").c_str()); + } + } + } + + client.setClipboardCaps(flags, lengths); +} + void SConnection::handleClipboardRequest(uint32_t flags) { if (!(flags & rfb::clipboardUTF8)) { @@ -386,6 +452,8 @@ void SConnection::handleClipboardRequest(uint32_t flags) vlog.debug("Ignoring unexpected clipboard request"); return; } + if (!accessCheck(AccessCutText)) + return; handleClipboardRequest(); } @@ -401,10 +469,15 @@ void SConnection::handleClipboardNotify(uint32_t flags) if (flags & rfb::clipboardUTF8) { hasLocalClipboard = false; + if (!accessCheck(AccessCutText)) + return; handleClipboardAnnounce(true); } else { + if (!accessCheck(AccessCutText)) + return; handleClipboardAnnounce(false); } + } void SConnection::handleClipboardProvide(uint32_t flags, @@ -417,28 +490,33 @@ void SConnection::handleClipboardProvide(uint32_t flags, } // FIXME: This conversion magic should be in SMsgReader - if (!isValidUTF8((const char*)data[0], lengths[0])) { + if (!core::isValidUTF8((const char*)data[0], lengths[0])) { vlog.error("Invalid UTF-8 sequence in clipboard - ignoring"); return; } - clientClipboard = convertLF((const char*)data[0], lengths[0]); + clientClipboard = core::convertLF((const char*)data[0], lengths[0]); hasRemoteClipboard = true; + if (!accessCheck(AccessCutText)) + return; + // FIXME: Should probably verify that this data was actually requested handleClipboardData(clientClipboard.c_str()); } -void SConnection::supportsQEMUKeyEvent() +void SConnection::supportsLocalCursor() { - writer()->writeQEMUKeyEvent(); } -void SConnection::supportsExtendedMouseButtons() +void SConnection::supportsFence() { - writer()->writeExtendedMouseButtonsSupport(); } -void SConnection::versionReceived() +void SConnection::supportsContinuousUpdates() +{ +} + +void SConnection::supportsLEDState() { } @@ -500,13 +578,13 @@ void SConnection::close(const char* /*reason*/) void SConnection::setPixelFormat(const PixelFormat& pf) { - SMsgHandler::setPixelFormat(pf); + client.setPF(pf); readyForSetColourMapEntries = true; if (!pf.trueColour) writeFakeColourMap(); } -void SConnection::framebufferUpdateRequest(const Rect& /*r*/, +void SConnection::framebufferUpdateRequest(const core::Rect& /*r*/, bool /*incremental*/) { if (!readyForSetColourMapEntries) { @@ -549,6 +627,9 @@ void SConnection::handleClipboardData(const char* /*data*/) void SConnection::requestClipboard() { + if (!accessCheck(AccessCutText)) + return; + if (hasRemoteClipboard) { handleClipboardData(clientClipboard.c_str()); return; @@ -561,6 +642,9 @@ void SConnection::requestClipboard() void SConnection::announceClipboard(bool available) { + if (!accessCheck(AccessCutText)) + return; + hasLocalClipboard = available; unsolicitedClipboardAttempt = false; @@ -587,10 +671,13 @@ void SConnection::announceClipboard(bool available) void SConnection::sendClipboardData(const char* data) { + if (!accessCheck(AccessCutText)) + return; + if (client.supportsEncoding(pseudoEncodingExtendedClipboard) && (client.clipboardFlags() & rfb::clipboardProvide)) { // FIXME: This conversion magic should be in SMsgWriter - std::string filtered(convertCRLF(data)); + std::string filtered(core::convertCRLF(data)); size_t sizes[1] = { filtered.size() + 1 }; const uint8_t* datas[1] = { (const uint8_t*)filtered.c_str() }; diff --git a/common/rfb/SConnection.h b/common/rfb/SConnection.h index f030ae05..a90b37ca 100644 --- a/common/rfb/SConnection.h +++ b/common/rfb/SConnection.h @@ -26,13 +26,16 @@ #include <string> -#include <rdr/InStream.h> -#include <rdr/OutStream.h> +#include <core/Timer.h> #include <rfb/AccessRights.h> #include <rfb/SMsgHandler.h> #include <rfb/SecurityServer.h> -#include <rfb/Timer.h> + +namespace rdr { + class InStream; + class OutStream; +} namespace rfb { @@ -84,6 +87,59 @@ namespace rfb { // cleanup of the SConnection object by the server virtual void close(const char* reason); + // requestClipboard() will result in a request to the client to + // transfer its clipboard data. A call to handleClipboardData() + // will be made once the data is available. + virtual void requestClipboard(); + + // announceClipboard() informs the client of changes to the + // clipboard on the server. The client may later request the + // clipboard data via handleClipboardRequest(). + virtual void announceClipboard(bool available); + + // sendClipboardData() transfers the clipboard data to the client + // and should be called whenever the client has requested the + // clipboard via handleClipboardRequest(). + virtual void sendClipboardData(const char* data); + + // getAccessRights() returns the access rights of a SConnection to the server. + AccessRights getAccessRights() { return accessRights; } + + // setAccessRights() allows a security package to limit the access rights + // of a SConnection to the server. How the access rights are treated + // is up to the derived class. + virtual void setAccessRights(AccessRights ar); + virtual bool accessCheck(AccessRights ar) const; + + // authenticated() returns true if the client has authenticated + // successfully. + bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || + state_ == RFBSTATE_NORMAL); } + + SMsgReader* reader() { return reader_; } + SMsgWriter* writer() { return writer_; } + + rdr::InStream* getInStream() { return is; } + rdr::OutStream* getOutStream() { return os; } + + enum stateEnum { + RFBSTATE_UNINITIALISED, + RFBSTATE_PROTOCOL_VERSION, + RFBSTATE_SECURITY_TYPE, + RFBSTATE_SECURITY, + RFBSTATE_SECURITY_FAILURE, + RFBSTATE_QUERYING, + RFBSTATE_INITIALISATION, + RFBSTATE_NORMAL, + RFBSTATE_CLOSING, + RFBSTATE_INVALID + }; + + stateEnum state() { return state_; } + + int32_t getPreferredEncoding() { return preferredEncoding; } + + protected: // Overridden from SMsgHandler @@ -91,23 +147,38 @@ namespace rfb { void clientCutText(const char* str) override; + void handleClipboardCaps(uint32_t flags, + const uint32_t* lengths) override; void handleClipboardRequest(uint32_t flags) override; void handleClipboardPeek() override; void handleClipboardNotify(uint32_t flags) override; void handleClipboardProvide(uint32_t flags, const size_t* lengths, const uint8_t* const* data) override; - void supportsQEMUKeyEvent() override; - - virtual void supportsExtendedMouseButtons() override; - - // Methods to be overridden in a derived class - // versionReceived() indicates that the version number has just been read - // from the client. The version will already have been "cooked" - // to deal with unknown/bogus viewer protocol numbers. - virtual void versionReceived(); + // supportsLocalCursor() is called whenever the status of + // cp.supportsLocalCursor has changed. At the moment this happens on a + // setEncodings message, but in the future this may be due to a message + // specially for this purpose. + virtual void supportsLocalCursor(); + + // supportsFence() is called the first time we detect support for fences + // in the client. A fence message should be sent at this point to notify + // the client of server support. + virtual void supportsFence(); + + // supportsContinuousUpdates() is called the first time we detect that + // the client wants the continuous updates extension. A + // EndOfContinuousUpdates message should be sent back to the client at + // this point if it is supported. + virtual void supportsContinuousUpdates(); + + // supportsLEDState() is called the first time we detect that the + // client supports the LED state extension. A LEDState message + // should be sent back to the client to inform it of the current + // server state. + virtual void supportsLEDState(); // authSuccess() is called when authentication has succeeded. virtual void authSuccess(); @@ -132,7 +203,7 @@ namespace rfb { // framebufferUpdateRequest() is called when a FramebufferUpdateRequest // message is received. The derived class must call on to // SConnection::framebufferUpdateRequest(). - void framebufferUpdateRequest(const Rect& r, bool incremental) override; + void framebufferUpdateRequest(const core::Rect& r, bool incremental) override; // fence() is called when we get a fence request or response. By default // it responds directly to requests (stating it doesn't support any @@ -163,62 +234,6 @@ namespace rfb { // client received the request. virtual void handleClipboardData(const char* data); - - // Other methods - - // requestClipboard() will result in a request to the client to - // transfer its clipboard data. A call to handleClipboardData() - // will be made once the data is available. - virtual void requestClipboard(); - - // announceClipboard() informs the client of changes to the - // clipboard on the server. The client may later request the - // clipboard data via handleClipboardRequest(). - virtual void announceClipboard(bool available); - - // sendClipboardData() transfers the clipboard data to the client - // and should be called whenever the client has requested the - // clipboard via handleClipboardRequest(). - virtual void sendClipboardData(const char* data); - - // getAccessRights() returns the access rights of a SConnection to the server. - AccessRights getAccessRights() { return accessRights; } - - // setAccessRights() allows a security package to limit the access rights - // of a SConnection to the server. How the access rights are treated - // is up to the derived class. - virtual void setAccessRights(AccessRights ar); - virtual bool accessCheck(AccessRights ar) const; - - // authenticated() returns true if the client has authenticated - // successfully. - bool authenticated() { return (state_ == RFBSTATE_INITIALISATION || - state_ == RFBSTATE_NORMAL); } - - SMsgReader* reader() { return reader_; } - SMsgWriter* writer() { return writer_; } - - rdr::InStream* getInStream() { return is; } - rdr::OutStream* getOutStream() { return os; } - - enum stateEnum { - RFBSTATE_UNINITIALISED, - RFBSTATE_PROTOCOL_VERSION, - RFBSTATE_SECURITY_TYPE, - RFBSTATE_SECURITY, - RFBSTATE_SECURITY_FAILURE, - RFBSTATE_QUERYING, - RFBSTATE_INITIALISATION, - RFBSTATE_NORMAL, - RFBSTATE_CLOSING, - RFBSTATE_INVALID - }; - - stateEnum state() { return state_; } - - int32_t getPreferredEncoding() { return preferredEncoding; } - - protected: // failConnection() prints a message to the log, sends a connection // failed message to the client (if possible) and throws an // Exception. @@ -243,7 +258,7 @@ namespace rfb { bool processSecurityFailure(); bool processInitMsg(); - void handleAuthFailureTimeout(Timer* t); + void handleAuthFailureTimeout(core::Timer* t); int defaultMajorVersion, defaultMinorVersion; @@ -256,7 +271,7 @@ namespace rfb { SecurityServer security; SSecurity* ssecurity; - MethodTimer<SConnection> authFailureTimer; + core::MethodTimer<SConnection> authFailureTimer; std::string authFailureMsg; stateEnum state_; diff --git a/common/rfb/SDesktop.h b/common/rfb/SDesktop.h index c97e788a..402a13af 100644 --- a/common/rfb/SDesktop.h +++ b/common/rfb/SDesktop.h @@ -38,14 +38,19 @@ #ifndef __RFB_SDESKTOP_H__ #define __RFB_SDESKTOP_H__ -#include <rfb/PixelBuffer.h> -#include <rfb/VNCServer.h> +#include <stdint.h> + #include <rfb/screenTypes.h> +namespace core { struct Point; } + namespace network { class Socket; } namespace rfb { + struct ScreenSet; + class VNCServer; + class SDesktop { public: // init() is called immediately when the VNCServer gets a reference @@ -97,7 +102,7 @@ namespace rfb { // pointerEvent() is called whenever a client sends an event that // the pointer moved, or a button was pressed or released. - virtual void pointerEvent(const Point& /*pos*/, + virtual void pointerEvent(const core::Point& /*pos*/, uint16_t /*buttonMask*/) {}; // handleClipboardRequest() is called whenever a client requests @@ -122,47 +127,6 @@ namespace rfb { virtual ~SDesktop() {} }; - // -=- SStaticDesktop - // Trivial implementation of the SDesktop interface, which provides - // dummy input handlers and event processing routine, and exports - // a plain black desktop of the specified format. - class SStaticDesktop : public SDesktop { - public: - SStaticDesktop(const Point& size) - : server(nullptr), buffer(nullptr) - { - PixelFormat pf; - const uint8_t black[4] = { 0, 0, 0, 0 }; - buffer = new ManagedPixelBuffer(pf, size.x, size.y); - if (buffer) - buffer->fillRect(buffer->getRect(), black); - } - SStaticDesktop(const Point& size, const PixelFormat& pf) - : buffer(nullptr) - { - const uint8_t black[4] = { 0, 0, 0, 0 }; - buffer = new ManagedPixelBuffer(pf, size.x, size.y); - if (buffer) - buffer->fillRect(buffer->getRect(), black); - } - virtual ~SStaticDesktop() { - if (buffer) delete buffer; - } - - void init(VNCServer* vs) override { - server = vs; - server->setPixelBuffer(buffer); - } - void queryConnection(network::Socket* sock, - const char* /*userName*/) override { - server->approveConnection(sock, true, nullptr); - } - - protected: - VNCServer* server; - ManagedPixelBuffer* buffer; - }; - }; #endif // __RFB_SDESKTOP_H__ diff --git a/common/rfb/SMsgHandler.cxx b/common/rfb/SMsgHandler.cxx deleted file mode 100644 index 1dce634d..00000000 --- a/common/rfb/SMsgHandler.cxx +++ /dev/null @@ -1,176 +0,0 @@ -/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. - * Copyright 2009-2019 Pierre Ossman for Cendio AB - * - * 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 <config.h> -#endif - -#include <rfb/Exception.h> -#include <rfb/LogWriter.h> -#include <rfb/SMsgHandler.h> -#include <rfb/ScreenSet.h> -#include <rfb/clipboardTypes.h> -#include <rfb/encodings.h> -#include <rfb/util.h> - -using namespace rfb; - -static LogWriter vlog("SMsgHandler"); - -SMsgHandler::SMsgHandler() -{ -} - -SMsgHandler::~SMsgHandler() -{ -} - -void SMsgHandler::clientInit(bool /*shared*/) -{ -} - -void SMsgHandler::setPixelFormat(const PixelFormat& pf) -{ - client.setPF(pf); -} - -void SMsgHandler::setEncodings(int nEncodings, const int32_t* encodings) -{ - bool firstFence, firstContinuousUpdates, firstLEDState, - firstQEMUKeyEvent, firstExtMouseButtonsEvent; - - firstFence = !client.supportsFence(); - firstContinuousUpdates = !client.supportsContinuousUpdates(); - firstLEDState = !client.supportsLEDState(); - firstQEMUKeyEvent = !client.supportsEncoding(pseudoEncodingQEMUKeyEvent); - firstExtMouseButtonsEvent = !client.supportsEncoding(pseudoEncodingExtendedMouseButtons); - - client.setEncodings(nEncodings, encodings); - - supportsLocalCursor(); - - if (client.supportsFence() && firstFence) - supportsFence(); - if (client.supportsContinuousUpdates() && firstContinuousUpdates) - supportsContinuousUpdates(); - if (client.supportsLEDState() && firstLEDState) - supportsLEDState(); - if (client.supportsEncoding(pseudoEncodingQEMUKeyEvent) && firstQEMUKeyEvent) - supportsQEMUKeyEvent(); - if (client.supportsEncoding(pseudoEncodingExtendedMouseButtons) && firstExtMouseButtonsEvent) - supportsExtendedMouseButtons(); -} - -void SMsgHandler::keyEvent(uint32_t /*keysym*/, uint32_t /*keycode*/, - bool /*down*/) -{ -} - -void SMsgHandler::pointerEvent(const Point& /*pos*/, - uint16_t /*buttonMask*/) -{ -} - -void SMsgHandler::clientCutText(const char* /*str*/) -{ -} - -void SMsgHandler::handleClipboardCaps(uint32_t flags, const uint32_t* lengths) -{ - int i; - - vlog.debug("Got client clipboard capabilities:"); - for (i = 0;i < 16;i++) { - if (flags & (1 << i)) { - const char *type; - - switch (1 << i) { - case clipboardUTF8: - type = "Plain text"; - break; - case clipboardRTF: - type = "Rich text"; - break; - case clipboardHTML: - type = "HTML"; - break; - case clipboardDIB: - type = "Images"; - break; - case clipboardFiles: - type = "Files"; - break; - default: - vlog.debug(" Unknown format 0x%x", 1 << i); - continue; - } - - if (lengths[i] == 0) - vlog.debug(" %s (only notify)", type); - else { - vlog.debug(" %s (automatically send up to %s)", - type, iecPrefix(lengths[i], "B").c_str()); - } - } - } - - client.setClipboardCaps(flags, lengths); -} - -void SMsgHandler::handleClipboardRequest(uint32_t /*flags*/) -{ -} - -void SMsgHandler::handleClipboardPeek() -{ -} - -void SMsgHandler::handleClipboardNotify(uint32_t /*flags*/) -{ -} - -void SMsgHandler::handleClipboardProvide(uint32_t /*flags*/, - const size_t* /*lengths*/, - const uint8_t* const* /*data*/) -{ -} - -void SMsgHandler::supportsLocalCursor() -{ -} - -void SMsgHandler::supportsFence() -{ -} - -void SMsgHandler::supportsContinuousUpdates() -{ -} - -void SMsgHandler::supportsLEDState() -{ -} - -void SMsgHandler::supportsQEMUKeyEvent() -{ -} - -void SMsgHandler::supportsExtendedMouseButtons() -{ -}
\ No newline at end of file diff --git a/common/rfb/SMsgHandler.h b/common/rfb/SMsgHandler.h index c5d13d78..d14a21c0 100644 --- a/common/rfb/SMsgHandler.h +++ b/common/rfb/SMsgHandler.h @@ -25,83 +25,47 @@ #include <stdint.h> -#include <rfb/PixelFormat.h> #include <rfb/ClientParams.h> -#include <rfb/ScreenSet.h> - -namespace rdr { class InStream; } namespace rfb { class SMsgHandler { public: - SMsgHandler(); - virtual ~SMsgHandler(); + virtual ~SMsgHandler() {} - // The following methods are called as corresponding messages are read. A - // derived class should override these methods as desired. Note that for - // the setPixelFormat(), setEncodings() and clipboardCaps() methods, a - // derived class must call on to SMsgHandler's methods. + // The following methods are called as corresponding messages are + // read. A derived class must override these methods. - virtual void clientInit(bool shared); + virtual void clientInit(bool shared) = 0; - virtual void setPixelFormat(const PixelFormat& pf); - virtual void setEncodings(int nEncodings, const int32_t* encodings); - virtual void framebufferUpdateRequest(const Rect& r, bool incremental) = 0; + virtual void setPixelFormat(const PixelFormat& pf) = 0; + virtual void setEncodings(int nEncodings, + const int32_t* encodings) = 0; + virtual void framebufferUpdateRequest(const core::Rect& r, + bool incremental) = 0; virtual void setDesktopSize(int fb_width, int fb_height, const ScreenSet& layout) = 0; - virtual void fence(uint32_t flags, unsigned len, const uint8_t data[]) = 0; + virtual void fence(uint32_t flags, unsigned len, + const uint8_t data[]) = 0; virtual void enableContinuousUpdates(bool enable, - int x, int y, int w, int h) = 0; + int x, int y, + int w, int h) = 0; virtual void keyEvent(uint32_t keysym, uint32_t keycode, - bool down); - virtual void pointerEvent(const Point& pos, - uint16_t buttonMask); + bool down) = 0; + virtual void pointerEvent(const core::Point& pos, + uint16_t buttonMask) = 0; - virtual void clientCutText(const char* str); + virtual void clientCutText(const char* str) = 0; virtual void handleClipboardCaps(uint32_t flags, - const uint32_t* lengths); - virtual void handleClipboardRequest(uint32_t flags); - virtual void handleClipboardPeek(); - virtual void handleClipboardNotify(uint32_t flags); + const uint32_t* lengths) = 0; + virtual void handleClipboardRequest(uint32_t flags) = 0; + virtual void handleClipboardPeek() = 0; + virtual void handleClipboardNotify(uint32_t flags) = 0; virtual void handleClipboardProvide(uint32_t flags, const size_t* lengths, - const uint8_t* const* data); - - // supportsLocalCursor() is called whenever the status of - // cp.supportsLocalCursor has changed. At the moment this happens on a - // setEncodings message, but in the future this may be due to a message - // specially for this purpose. - virtual void supportsLocalCursor(); - - // supportsFence() is called the first time we detect support for fences - // in the client. A fence message should be sent at this point to notify - // the client of server support. - virtual void supportsFence(); - - // supportsContinuousUpdates() is called the first time we detect that - // the client wants the continuous updates extension. A - // EndOfContinuousUpdates message should be sent back to the client at - // this point if it is supported. - virtual void supportsContinuousUpdates(); - - // supportsLEDState() is called the first time we detect that the - // client supports the LED state extension. A LEDState message - // should be sent back to the client to inform it of the current - // server state. - virtual void supportsLEDState(); - - // supportsQEMUKeyEvent() is called the first time we detect that the - // client wants the QEMU Extended Key Event extension. The default - // handler will send a pseudo-rect back, signalling server support. - virtual void supportsQEMUKeyEvent(); - - // supportsExtendedMouseButtons() is called the first time we detect that the - // client supports sending 16 bit mouse button state. This lets us pass more button - // states between server and client. - virtual void supportsExtendedMouseButtons(); + const uint8_t* const* data) = 0; ClientParams client; }; diff --git a/common/rfb/SMsgReader.cxx b/common/rfb/SMsgReader.cxx index 5df11153..9054bcc3 100644 --- a/common/rfb/SMsgReader.cxx +++ b/common/rfb/SMsgReader.cxx @@ -25,6 +25,10 @@ #include <vector> +#include <core/Configuration.h> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rdr/InStream.h> #include <rdr/ZlibInStream.h> @@ -32,17 +36,19 @@ #include <rfb/qemuTypes.h> #include <rfb/clipboardTypes.h> #include <rfb/Exception.h> +#include <rfb/PixelFormat.h> +#include <rfb/ScreenSet.h> #include <rfb/SMsgHandler.h> #include <rfb/SMsgReader.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> -#include <rfb/util.h> using namespace rfb; -static LogWriter vlog("SMsgReader"); +static core::LogWriter vlog("SMsgReader"); -static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024); +static core::IntParameter maxCutText("MaxCutText", + "Maximum permitted length of an " + "incoming clipboard update", + 256*1024, 0, INT_MAX); SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_) : handler(handler_), is(is_), state(MSGSTATE_IDLE) @@ -201,7 +207,7 @@ bool SMsgReader::readFramebufferUpdateRequest() int y = is->readU16(); int w = is->readU16(); int h = is->readU16(); - handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc); + handler->framebufferUpdateRequest({x, y, x+w, y+h}, inc); return true; } @@ -298,7 +304,7 @@ bool SMsgReader::readPointerEvent() } is->clearRestorePoint(); - handler->pointerEvent(Point(x, y), mask); + handler->pointerEvent({x, y}, mask); return true; } @@ -338,8 +344,8 @@ bool SMsgReader::readClientCutText() std::vector<char> ca(len); is->readBytes((uint8_t*)ca.data(), len); - std::string utf8(latin1ToUTF8(ca.data(), ca.size())); - std::string filtered(convertLF(utf8.data(), utf8.size())); + std::string utf8(core::latin1ToUTF8(ca.data(), ca.size())); + std::string filtered(core::convertLF(utf8.data(), utf8.size())); handler->clientCutText(filtered.c_str()); @@ -485,7 +491,7 @@ bool SMsgReader::readQEMUMessage() ret = readQEMUKeyEvent(); break; default: - throw protocol_error(format("Unknown QEMU submessage type %d", subType)); + throw protocol_error(core::format("Unknown QEMU submessage type %d", subType)); } if (!ret) { diff --git a/common/rfb/SMsgWriter.cxx b/common/rfb/SMsgWriter.cxx index 5ee0905b..d52df511 100644 --- a/common/rfb/SMsgWriter.cxx +++ b/common/rfb/SMsgWriter.cxx @@ -24,6 +24,9 @@ #include <stdio.h> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rdr/OutStream.h> #include <rdr/MemOutStream.h> #include <rdr/ZlibOutStream.h> @@ -32,16 +35,17 @@ #include <rfb/fenceTypes.h> #include <rfb/clipboardTypes.h> #include <rfb/ClientParams.h> +#include <rfb/Cursor.h> #include <rfb/UpdateTracker.h> #include <rfb/Encoder.h> +#include <rfb/ScreenSet.h> #include <rfb/SMsgWriter.h> -#include <rfb/LogWriter.h> +#include <rfb/encodings.h> #include <rfb/ledStates.h> -#include <rfb/util.h> using namespace rfb; -static LogWriter vlog("SMsgWriter"); +static core::LogWriter vlog("SMsgWriter"); SMsgWriter::SMsgWriter(ClientParams* client_, rdr::OutStream* os_) : client(client_), os(os_), @@ -95,7 +99,7 @@ void SMsgWriter::writeServerCutText(const char* str) if (strchr(str, '\r') != nullptr) throw std::invalid_argument("Invalid carriage return in clipboard data"); - std::string latin1(utf8ToLatin1(str)); + std::string latin1(core::utf8ToLatin1(str)); startMsg(msgTypeServerCutText); os->pad(3); @@ -405,7 +409,7 @@ void SMsgWriter::writeFramebufferUpdateEnd() endMsg(); } -void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY) +void SMsgWriter::writeCopyRect(const core::Rect& r, int srcX, int srcY) { startRect(r,encodingCopyRect); os->writeU16(srcX); @@ -413,7 +417,7 @@ void SMsgWriter::writeCopyRect(const Rect& r, int srcX, int srcY) endRect(); } -void SMsgWriter::startRect(const Rect& r, int encoding) +void SMsgWriter::startRect(const core::Rect& r, int encoding) { if (++nRectsInUpdate > nRectsInHeader && nRectsInHeader) throw std::logic_error("SMsgWriter::startRect: nRects out of sync"); @@ -488,7 +492,7 @@ void SMsgWriter::writePseudoRects() } if (needCursorPos) { - const Point& cursorPos = client->cursorPos(); + const core::Point& cursorPos = client->cursorPos(); if (client->supportsEncoding(pseudoEncodingVMwareCursorPosition)) { writeSetVMwareCursorPositionRect(cursorPos.x, cursorPos.y); diff --git a/common/rfb/SMsgWriter.h b/common/rfb/SMsgWriter.h index 7bc0ed6a..113a9370 100644 --- a/common/rfb/SMsgWriter.h +++ b/common/rfb/SMsgWriter.h @@ -25,8 +25,7 @@ #include <stdint.h> -#include <rfb/encodings.h> -#include <rfb/ScreenSet.h> +namespace core { struct Rect; } namespace rdr { class OutStream; } @@ -117,11 +116,11 @@ namespace rfb { void writeFramebufferUpdateEnd(); // There is no explicit encoder for CopyRect rects. - void writeCopyRect(const Rect& r, int srcX, int srcY); + void writeCopyRect(const core::Rect& r, int srcX, int srcY); // Encoders should call these to mark the start and stop of individual // rects. - void startRect(const Rect& r, int enc); + void startRect(const core::Rect& r, int enc); void endRect(); protected: diff --git a/common/rfb/SSecurity.h b/common/rfb/SSecurity.h index 0911ecd8..edbe8185 100644 --- a/common/rfb/SSecurity.h +++ b/common/rfb/SSecurity.h @@ -44,12 +44,12 @@ #ifndef __RFB_SSECURITY_H__ #define __RFB_SSECURITY_H__ -#include <rfb/SConnection.h> - -#include <list> +#include <rfb/AccessRights.h> namespace rfb { + class SConnection; + class SSecurity { public: SSecurity(SConnection* sc_) : sc(sc_) {} diff --git a/common/rfb/SSecurityPlain.cxx b/common/rfb/SSecurityPlain.cxx index e62e6d60..4fa63250 100644 --- a/common/rfb/SSecurityPlain.cxx +++ b/common/rfb/SSecurityPlain.cxx @@ -21,10 +21,12 @@ #include <config.h> #endif +#include <core/Configuration.h> +#include <core/string.h> + #include <rfb/SSecurityPlain.h> #include <rfb/SConnection.h> #include <rfb/Exception.h> -#include <rfb/util.h> #include <rdr/InStream.h> #if !defined(WIN32) && !defined(__APPLE__) #include <rfb/UnixPasswordValidator.h> @@ -37,32 +39,30 @@ using namespace rfb; -StringParameter PasswordValidator::plainUsers +core::StringListParameter PasswordValidator::plainUsers ("PlainUsers", "Users permitted to access via Plain security type (including TLSPlain, X509Plain etc.)" #ifdef HAVE_NETTLE " or RSA-AES security types (RA2, RA2ne, RA2_256, RA2ne_256)" #endif , - ""); + {}); bool PasswordValidator::validUser(const char* username) { - std::vector<std::string> users; - - users = split(plainUsers, ','); - - for (size_t i = 0; i < users.size(); i++) { - if (users[i] == "*") + for (const char* user : plainUsers) { + if (strcmp(user, "*") == 0) return true; #if !defined(WIN32) && !defined(__APPLE__) - if (users[i] == "%u") { + if (strcmp(user, "%u") == 0) { struct passwd *pw = getpwnam(username); if (pw && pw->pw_uid == getuid()) return true; } #endif - if (users[i] == username) + // FIXME: We should compare uid, as the usernames might not be case + // sensitive, or have other normalisation + if (strcmp(user, username) == 0) return true; } return false; @@ -113,8 +113,9 @@ bool SSecurityPlain::processMsg() password[plen] = 0; username[ulen] = 0; plen = 0; - if (!valid->validate(sc, username, password)) - throw auth_error("Authentication failed"); + std::string msg = "Authentication failed"; + if (!valid->validate(sc, username, password, msg)) + throw auth_error(msg); } return true; diff --git a/common/rfb/SSecurityPlain.h b/common/rfb/SSecurityPlain.h index c0ac049b..4c030455 100644 --- a/common/rfb/SSecurityPlain.h +++ b/common/rfb/SSecurityPlain.h @@ -20,23 +20,29 @@ #ifndef __RFB_SSECURITYPLAIN_H__ #define __RFB_SSECURITYPLAIN_H__ -#include <rfb/SConnection.h> +#include <rfb/Security.h> #include <rfb/SSecurity.h> -#include <rfb/SSecurityVeNCrypt.h> -#include <rfb/Configuration.h> + +namespace core { class StringListParameter; } namespace rfb { class PasswordValidator { public: - bool validate(SConnection* sc, const char *username, const char *password) - { return validUser(username) ? validateInternal(sc, username, password) : false; } - static StringParameter plainUsers; + bool validate(SConnection* sc, + const char *username, + const char *password, + std::string &msg) + { return validUser(username) ? validateInternal(sc, username, password, msg) : false; } + static core::StringListParameter plainUsers; virtual ~PasswordValidator() { } protected: - virtual bool validateInternal(SConnection* sc, const char *username, const char *password)=0; + virtual bool validateInternal(SConnection* sc, + const char *username, + const char *password, + std::string &msg) = 0; static bool validUser(const char* username); }; diff --git a/common/rfb/SSecurityRSAAES.cxx b/common/rfb/SSecurityRSAAES.cxx index fed213ad..405005ab 100644 --- a/common/rfb/SSecurityRSAAES.cxx +++ b/common/rfb/SSecurityRSAAES.cxx @@ -37,13 +37,15 @@ #include <nettle/base64.h> #include <nettle/asn1.h> +#include <core/Exception.h> +#include <core/LogWriter.h> + #include <rdr/AESInStream.h> #include <rdr/AESOutStream.h> -#include <rdr/Exception.h> +#include <rdr/RandomStream.h> #include <rfb/SSecurityRSAAES.h> #include <rfb/SConnection.h> -#include <rfb/LogWriter.h> #include <rfb/Exception.h> #if !defined(WIN32) && !defined(__APPLE__) #include <rfb/UnixPasswordValidator.h> @@ -67,14 +69,14 @@ const size_t MaxKeyFileSize = 32 * 1024; using namespace rfb; -StringParameter SSecurityRSAAES::keyFile +core::StringParameter SSecurityRSAAES::keyFile ("RSAKey", "Path to the RSA key for the RSA-AES security types in " - "PEM format", "", ConfServer); -BoolParameter SSecurityRSAAES::requireUsername + "PEM format", ""); +core::BoolParameter SSecurityRSAAES::requireUsername ("RequireUsername", "Require username for the RSA-AES security types", - false, ConfServer); + false); -static LogWriter vlog("CSecurityRSAAES"); +static core::LogWriter vlog("SSecurityRSAAES"); SSecurityRSAAES::SSecurityRSAAES(SConnection* sc_, uint32_t _secType, int _keySize, bool _isAllEncrypted) @@ -174,7 +176,7 @@ void SSecurityRSAAES::loadPrivateKey() { FILE* file = fopen(keyFile, "rb"); if (!file) - throw rdr::posix_error("Failed to open key file", errno); + throw core::posix_error("Failed to open key file", errno); fseek(file, 0, SEEK_END); size_t size = ftell(file); if (size == 0 || size > MaxKeyFileSize) { @@ -185,7 +187,7 @@ void SSecurityRSAAES::loadPrivateKey() std::vector<uint8_t> data(size); if (fread(data.data(), 1, data.size(), file) != size) { fclose(file); - throw rdr::posix_error("Failed to read key", errno); + throw core::posix_error("Failed to read key", errno); } fclose(file); @@ -345,6 +347,7 @@ static void random_func(void* ctx, size_t length, uint8_t* dst) void SSecurityRSAAES::writeRandom() { + rdr::RandomStream rs; rdr::OutStream* os = sc->getOutStream(); if (!rs.hasData(keySize / 8)) throw std::runtime_error("Failed to generate random"); @@ -580,9 +583,10 @@ void SSecurityRSAAES::verifyUserPass() #elif !defined(__APPLE__) UnixPasswordValidator *valid = new UnixPasswordValidator(); #endif - if (!valid->validate(sc, username, password)) { + std::string msg = "Authentication failed"; + if (!valid->validate(sc, username, password, msg)) { delete valid; - throw auth_error("Authentication failed"); + throw auth_error(msg); } delete valid; #else diff --git a/common/rfb/SSecurityRSAAES.h b/common/rfb/SSecurityRSAAES.h index e3300cb7..283134db 100644 --- a/common/rfb/SSecurityRSAAES.h +++ b/common/rfb/SSecurityRSAAES.h @@ -27,7 +27,10 @@ #include <rfb/SSecurity.h> -#include <rdr/RandomStream.h> +namespace core { + class BoolParameter; + class StringParameter; +} namespace rdr { class InStream; @@ -51,8 +54,8 @@ namespace rfb { return accessRights; } - static StringParameter keyFile; - static BoolParameter requireUsername; + static core::StringParameter keyFile; + static core::BoolParameter requireUsername; private: void cleanup(); @@ -96,8 +99,6 @@ namespace rfb { rdr::InStream* rawis; rdr::OutStream* rawos; - - rdr::RandomStream rs; }; } diff --git a/common/rfb/SSecurityTLS.cxx b/common/rfb/SSecurityTLS.cxx index b297242b..17497b8e 100644 --- a/common/rfb/SSecurityTLS.cxx +++ b/common/rfb/SSecurityTLS.cxx @@ -2,7 +2,7 @@ * Copyright (C) 2004 Red Hat Inc. * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team - * Copyright (C) 2012-2021 Pierre Ossman for Cendio AB + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,13 +30,15 @@ #include <stdlib.h> +#include <core/LogWriter.h> + #include <rfb/SSecurityTLS.h> #include <rfb/SConnection.h> -#include <rfb/LogWriter.h> #include <rfb/Exception.h> + #include <rdr/TLSException.h> -#include <rdr/TLSInStream.h> -#include <rdr/TLSOutStream.h> +#include <rdr/TLSSocket.h> + #include <gnutls/x509.h> #if defined (SSECURITYTLS__USE_DEPRECATED_DH) @@ -59,17 +61,17 @@ static const gnutls_datum_t ffdhe_pkcs3_param = { using namespace rfb; -StringParameter SSecurityTLS::X509_CertFile -("X509Cert", "Path to the X509 certificate in PEM format", "", ConfServer); +core::StringParameter SSecurityTLS::X509_CertFile +("X509Cert", "Path to the X509 certificate in PEM format", ""); -StringParameter SSecurityTLS::X509_KeyFile -("X509Key", "Path to the key of the X509 certificate in PEM format", "", ConfServer); +core::StringParameter SSecurityTLS::X509_KeyFile +("X509Key", "Path to the key of the X509 certificate in PEM format", ""); -static LogWriter vlog("TLS"); +static core::LogWriter vlog("TLS"); SSecurityTLS::SSecurityTLS(SConnection* sc_, bool _anon) : SSecurity(sc_), session(nullptr), anon_cred(nullptr), - cert_cred(nullptr), anon(_anon), tlsis(nullptr), tlsos(nullptr), + cert_cred(nullptr), anon(_anon), tlssock(nullptr), rawis(nullptr), rawos(nullptr) { int ret; @@ -85,32 +87,13 @@ SSecurityTLS::SSecurityTLS(SConnection* sc_, bool _anon) void SSecurityTLS::shutdown() { - if (tlsos) { - try { - if (tlsos->hasBufferedData()) { - tlsos->cork(false); - tlsos->flush(); - if (tlsos->hasBufferedData()) - vlog.error("Failed to flush remaining socket data on close"); - } - } catch (std::exception& e) { - vlog.error("Failed to flush remaining socket data on close: %s", e.what()); - } - } - - if (session) { - int ret; - // FIXME: We can't currently wait for the response, so we only send - // our close and hope for the best - ret = gnutls_bye(session, GNUTLS_SHUT_WR); - if ((ret != GNUTLS_E_SUCCESS) && (ret != GNUTLS_E_INVALID_SESSION)) - vlog.error("TLS shutdown failed: %s", gnutls_strerror(ret)); - } + if (tlssock) + tlssock->shutdown(); #if defined (SSECURITYTLS__USE_DEPRECATED_DH) if (dh_params) { gnutls_dh_params_deinit(dh_params); - dh_params = 0; + dh_params = nullptr; } #endif @@ -130,13 +113,9 @@ void SSecurityTLS::shutdown() rawos = nullptr; } - if (tlsis) { - delete tlsis; - tlsis = nullptr; - } - if (tlsos) { - delete tlsos; - tlsos = nullptr; + if (tlssock) { + delete tlssock; + tlssock = nullptr; } if (session) { @@ -182,56 +161,46 @@ bool SSecurityTLS::processMsg() os->writeU8(1); os->flush(); - // Create these early as they set up the push/pull functions - // for GnuTLS - tlsis = new rdr::TLSInStream(is, session); - tlsos = new rdr::TLSOutStream(os, session); + tlssock = new rdr::TLSSocket(is, os, session); rawis = is; rawos = os; } - err = gnutls_handshake(session); - if (err != GNUTLS_E_SUCCESS) { - if (!gnutls_error_is_fatal(err)) { - vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err)); + try { + if (!tlssock->handshake()) return false; - } - vlog.error("TLS Handshake failed: %s", gnutls_strerror (err)); + } catch (std::exception&) { shutdown(); - throw rdr::tls_error("TLS Handshake failed", err); + throw; } vlog.debug("TLS handshake completed with %s", gnutls_session_get_desc(session)); - sc->setStreams(tlsis, tlsos); + sc->setStreams(&tlssock->inStream(), &tlssock->outStream()); return true; } void SSecurityTLS::setParams() { - static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH"; + static const char kx_anon_priority[] = "+ANON-ECDH:+ANON-DH"; int ret; // Custom priority string specified? if (strcmp(Security::GnuTLSPriority, "") != 0) { - char *prio; + std::string prio; const char *err; - prio = new char[strlen(Security::GnuTLSPriority) + - strlen(kx_anon_priority) + 1]; - - strcpy(prio, Security::GnuTLSPriority); - if (anon) - strcat(prio, kx_anon_priority); - - ret = gnutls_priority_set_direct(session, prio, &err); - - delete [] prio; + prio = (const char*)Security::GnuTLSPriority; + if (anon) { + prio += ":"; + prio += kx_anon_priority; + } + ret = gnutls_priority_set_direct(session, prio.c_str(), &err); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); @@ -241,30 +210,22 @@ void SSecurityTLS::setParams() const char *err; #if GNUTLS_VERSION_NUMBER >= 0x030603 - // gnutls_set_default_priority_appends() expects a normal priority string that - // doesn't start with ":". - ret = gnutls_set_default_priority_append(session, kx_anon_priority + 1, &err, 0); + ret = gnutls_set_default_priority_append(session, kx_anon_priority, &err, 0); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); throw rdr::tls_error("gnutls_set_default_priority_append()", ret); } #else + std::string prio; + // We don't know what the system default priority is, so we guess // it's what upstream GnuTLS has - static const char gnutls_default_priority[] = "NORMAL"; - char *prio; - - prio = new char[strlen(gnutls_default_priority) + - strlen(kx_anon_priority) + 1]; - - strcpy(prio, gnutls_default_priority); - strcat(prio, kx_anon_priority); - - ret = gnutls_priority_set_direct(session, prio, &err); - - delete [] prio; + prio = "NORMAL"; + prio += ":"; + prio += kx_anon_priority; + ret = gnutls_priority_set_direct(session, prio.c_str(), &err); if (ret != GNUTLS_E_SUCCESS) { if (ret == GNUTLS_E_INVALID_REQUEST) vlog.error("GnuTLS priority syntax error at: %s", err); diff --git a/common/rfb/SSecurityTLS.h b/common/rfb/SSecurityTLS.h index 1dc33cfd..61eec2a8 100644 --- a/common/rfb/SSecurityTLS.h +++ b/common/rfb/SSecurityTLS.h @@ -2,6 +2,7 @@ * Copyright (C) 2004 Red Hat Inc. * Copyright (C) 2005 Martin Koegler * Copyright (C) 2010 TigerVNC Team + * Copyright 2012-2025 Pierre Ossman for Cendio AB * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +27,7 @@ #error "This header should not be included without HAVE_GNUTLS defined" #endif +#include <rfb/Security.h> #include <rfb/SSecurity.h> #include <gnutls/gnutls.h> @@ -40,8 +42,7 @@ namespace rdr { class InStream; class OutStream; - class TLSInStream; - class TLSOutStream; + class TLSSocket; } namespace rfb { @@ -54,8 +55,8 @@ namespace rfb { const char* getUserName() const override {return nullptr;} int getType() const override { return anon ? secTypeTLSNone : secTypeX509None;} - static StringParameter X509_CertFile; - static StringParameter X509_KeyFile; + static core::StringParameter X509_CertFile; + static core::StringParameter X509_KeyFile; protected: void shutdown(); @@ -71,8 +72,7 @@ namespace rfb { bool anon; - rdr::TLSInStream* tlsis; - rdr::TLSOutStream* tlsos; + rdr::TLSSocket* tlssock; rdr::InStream* rawis; rdr::OutStream* rawos; diff --git a/common/rfb/SSecurityVeNCrypt.cxx b/common/rfb/SSecurityVeNCrypt.cxx index 4617fddb..757acc06 100644 --- a/common/rfb/SSecurityVeNCrypt.cxx +++ b/common/rfb/SSecurityVeNCrypt.cxx @@ -26,15 +26,20 @@ #include <config.h> #endif +#include <core/LogWriter.h> + +#include <rfb/SConnection.h> +#include <rfb/SecurityServer.h> #include <rfb/SSecurityVeNCrypt.h> #include <rfb/Exception.h> -#include <rfb/LogWriter.h> +#include <rfb/Security.h> + #include <rdr/InStream.h> #include <rdr/OutStream.h> using namespace rfb; -static LogWriter vlog("SVeNCrypt"); +static core::LogWriter vlog("SVeNCrypt"); SSecurityVeNCrypt::SSecurityVeNCrypt(SConnection* sc_, SecurityServer *sec) diff --git a/common/rfb/SSecurityVeNCrypt.h b/common/rfb/SSecurityVeNCrypt.h index ea2bb6fb..534f94a6 100644 --- a/common/rfb/SSecurityVeNCrypt.h +++ b/common/rfb/SSecurityVeNCrypt.h @@ -25,11 +25,13 @@ #ifndef __SSECURITYVENCRYPT_H__ #define __SSECURITYVENCRYPT_H__ -#include <rfb/SSecurityStack.h> -#include <rfb/SConnection.h> +#include <rfb/SSecurity.h> namespace rfb { + class SConnection; + class SecurityServer; + class SSecurityVeNCrypt : public SSecurity { public: SSecurityVeNCrypt(SConnection* sc, SecurityServer *sec); diff --git a/common/rfb/SSecurityVncAuth.cxx b/common/rfb/SSecurityVncAuth.cxx index 22d88079..b267d5a0 100644 --- a/common/rfb/SSecurityVncAuth.cxx +++ b/common/rfb/SSecurityVncAuth.cxx @@ -25,13 +25,17 @@ #include <config.h> #endif +#include <core/Configuration.h> +#include <core/LogWriter.h> + +#include <rdr/OutStream.h> + #include <rfb/SSecurityVncAuth.h> #include <rdr/RandomStream.h> #include <rfb/SConnection.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> #include <rfb/Exception.h> #include <rfb/obfuscate.h> + #include <assert.h> #include <string.h> #include <stdio.h> @@ -42,12 +46,12 @@ extern "C" { using namespace rfb; -static LogWriter vlog("SVncAuth"); +static core::LogWriter vlog("SVncAuth"); -StringParameter SSecurityVncAuth::vncAuthPasswdFile -("PasswordFile", "Password file for VNC authentication", "", ConfServer); -AliasParameter rfbauth("rfbauth", "Alias for PasswordFile", - &SSecurityVncAuth::vncAuthPasswdFile, ConfServer); +core::StringParameter SSecurityVncAuth::vncAuthPasswdFile +("PasswordFile", "Password file for VNC authentication", ""); +core::AliasParameter rfbauth("rfbauth", "Alias for PasswordFile", + &SSecurityVncAuth::vncAuthPasswdFile); VncAuthPasswdParameter SSecurityVncAuth::vncAuthPasswd ("Password", "Obfuscated binary encoding of the password which clients must supply to " "access the server", &SSecurityVncAuth::vncAuthPasswdFile); @@ -118,8 +122,8 @@ bool SSecurityVncAuth::processMsg() VncAuthPasswdParameter::VncAuthPasswdParameter(const char* name_, const char* desc, - StringParameter* passwdFile_) -: BinaryParameter(name_, desc, nullptr, 0, ConfServer), + core::StringParameter* passwdFile_) +: core::BinaryParameter(name_, desc, nullptr, 0), passwdFile(passwdFile_) { } diff --git a/common/rfb/SSecurityVncAuth.h b/common/rfb/SSecurityVncAuth.h index 532abe0a..e2845337 100644 --- a/common/rfb/SSecurityVncAuth.h +++ b/common/rfb/SSecurityVncAuth.h @@ -26,7 +26,8 @@ #include <stdint.h> -#include <rfb/Configuration.h> +#include <core/Configuration.h> + #include <rfb/SSecurity.h> #include <rfb/Security.h> @@ -41,12 +42,12 @@ namespace rfb { virtual ~VncAuthPasswdGetter() { } }; - class VncAuthPasswdParameter : public VncAuthPasswdGetter, BinaryParameter { + class VncAuthPasswdParameter : public VncAuthPasswdGetter, core::BinaryParameter { public: - VncAuthPasswdParameter(const char* name, const char* desc, StringParameter* passwdFile_); + VncAuthPasswdParameter(const char* name, const char* desc, core::StringParameter* passwdFile_); void getVncAuthPasswd(std::string *password, std::string *readOnlyPassword) override; protected: - StringParameter* passwdFile; + core::StringParameter* passwdFile; }; class SSecurityVncAuth : public SSecurity { @@ -56,7 +57,7 @@ namespace rfb { int getType() const override {return secTypeVncAuth;} const char* getUserName() const override {return nullptr;} AccessRights getAccessRights() const override { return accessRights; } - static StringParameter vncAuthPasswdFile; + static core::StringParameter vncAuthPasswdFile; static VncAuthPasswdParameter vncAuthPasswd; private: bool verifyResponse(const char* password); diff --git a/common/rfb/ScreenSet.h b/common/rfb/ScreenSet.h index fb93e5c2..10753b17 100644 --- a/common/rfb/ScreenSet.h +++ b/common/rfb/ScreenSet.h @@ -25,10 +25,11 @@ #include <string.h> #include <stdint.h> -#include <rfb/Rect.h> #include <list> #include <set> +#include <core/Rect.h> + namespace rfb { // rfb::Screen @@ -52,7 +53,7 @@ namespace rfb { } uint32_t id; - Rect dimensions; + core::Rect dimensions; uint32_t flags; }; @@ -87,7 +88,7 @@ namespace rfb { inline bool validate(int fb_width, int fb_height) const { std::list<Screen>::const_iterator iter; std::set<uint32_t> seen_ids; - Rect fb_rect; + core::Rect fb_rect; if (screens.empty()) return false; diff --git a/common/rfb/Security.cxx b/common/rfb/Security.cxx index 3b0d95bf..1d4124f9 100644 --- a/common/rfb/Security.cxx +++ b/common/rfb/Security.cxx @@ -24,17 +24,19 @@ #include <string.h> #include <algorithm> +#include <stdexcept> + +#include <core/LogWriter.h> +#include <core/string.h> -#include <rfb/LogWriter.h> #include <rfb/Security.h> -#include <rfb/util.h> using namespace rfb; -static LogWriter vlog("Security"); +static core::LogWriter vlog("Security"); #ifdef HAVE_GNUTLS -StringParameter Security::GnuTLSPriority("GnuTLSPriority", +core::StringParameter Security::GnuTLSPriority("GnuTLSPriority", "GnuTLS priority string that controls the TLS session’s handshake algorithms", ""); #endif @@ -43,9 +45,16 @@ Security::Security() { } -Security::Security(StringParameter &secTypes) +Security::Security(core::EnumListParameter &secTypes) { - enabledSecTypes = parseSecTypes(secTypes); + for (core::EnumListEntry type : secTypes) { + uint32_t typeNum = secTypeNum(type.getValueStr().c_str()); + // Should have been filtered by EnumListParameter, but let's have + // a safety net + if (typeNum == secTypeInvalid) + throw std::logic_error("Unknown security type"); + enabledSecTypes.push_back(typeNum); + } } const std::list<uint8_t> Security::GetEnabledSecTypes(void) @@ -179,16 +188,3 @@ const char* rfb::secTypeName(uint32_t num) default: return "[unknown secType]"; } } - -std::list<uint32_t> rfb::parseSecTypes(const char* types_) -{ - std::list<uint32_t> result; - std::vector<std::string> types; - types = split(types_, ','); - for (size_t i = 0; i < types.size(); i++) { - uint32_t typeNum = secTypeNum(types[i].c_str()); - if (typeNum != secTypeInvalid) - result.push_back(typeNum); - } - return result; -} diff --git a/common/rfb/Security.h b/common/rfb/Security.h index 430a1d89..40f54c36 100644 --- a/common/rfb/Security.h +++ b/common/rfb/Security.h @@ -24,11 +24,15 @@ #include <stdint.h> -#include <rfb/Configuration.h> - #include <list> +namespace core { + class EnumListParameter; + class StringParameter; +} + namespace rfb { + const uint8_t secTypeInvalid = 0; const uint8_t secTypeNone = 1; const uint8_t secTypeVncAuth = 2; @@ -76,7 +80,7 @@ namespace rfb { * Create Security instance. */ Security(); - Security(StringParameter &secTypes); + Security(core::EnumListParameter& secTypes); /* * Note about security types. @@ -105,7 +109,7 @@ namespace rfb { char *ToString(void); #ifdef HAVE_GNUTLS - static StringParameter GnuTLSPriority; + static core::StringParameter GnuTLSPriority; #endif private: @@ -114,7 +118,7 @@ namespace rfb { const char* secTypeName(uint32_t num); uint32_t secTypeNum(const char* name); - std::list<uint32_t> parseSecTypes(const char* types); + } #endif diff --git a/common/rfb/SecurityClient.cxx b/common/rfb/SecurityClient.cxx index 027d47df..d71941b5 100644 --- a/common/rfb/SecurityClient.cxx +++ b/common/rfb/SecurityClient.cxx @@ -25,12 +25,15 @@ #include <stdexcept> +#include <core/Configuration.h> + #include <rfb/CSecurityNone.h> #include <rfb/CSecurityStack.h> #include <rfb/CSecurityVeNCrypt.h> #include <rfb/CSecurityVncAuth.h> #include <rfb/CSecurityPlain.h> #include <rfb/Security.h> +#include <rfb/SecurityClient.h> #ifdef HAVE_GNUTLS #include <rfb/CSecurityTLS.h> #endif @@ -42,7 +45,7 @@ using namespace rfb; -StringParameter SecurityClient::secTypes +core::EnumListParameter SecurityClient::secTypes ("SecurityTypes", "Specify which security scheme to use (None, VncAuth, Plain" #ifdef HAVE_GNUTLS @@ -52,14 +55,22 @@ StringParameter SecurityClient::secTypes ", RA2, RA2ne, RA2_256, RA2ne_256, DH, MSLogonII" #endif ")", + { "None", "VncAuth", "Plain", +#ifdef HAVE_GNUTLS + "TLSNone", "TLSVnc", "TLSPlain", "X509None", "X509Vnc", "X509Plain", +#endif +#ifdef HAVE_NETTLE + "RA2", "RA2ne", "RA2_256", "RA2ne_256", "DH", "MSLogonII", +#endif + }, + { "None", "VncAuth", "Plain", #ifdef HAVE_GNUTLS - "X509Plain,TLSPlain,X509Vnc,TLSVnc,X509None,TLSNone," + "TLSNone", "TLSVnc", "TLSPlain", "X509None", "X509Vnc", "X509Plain", #endif #ifdef HAVE_NETTLE - "RA2,RA2_256,RA2ne,RA2ne_256,DH,MSLogonII," + "RA2", "RA2ne", "RA2_256", "RA2ne_256", "DH", "MSLogonII", #endif - "VncAuth,None", -ConfViewer); + }); CSecurity* SecurityClient::GetCSecurity(CConnection* cc, uint32_t secType) { diff --git a/common/rfb/SecurityClient.h b/common/rfb/SecurityClient.h index b86fcb35..11fea417 100644 --- a/common/rfb/SecurityClient.h +++ b/common/rfb/SecurityClient.h @@ -22,12 +22,13 @@ #ifndef __RFB_SECURITYCLIENT_H__ #define __RFB_SECURITYCLIENT_H__ -#include <rfb/Configuration.h> #include <rfb/Security.h> -#include <rfb/CSecurity.h> namespace rfb { + class CConnection; + class CSecurity; + class SecurityClient : public Security { public: SecurityClient(void) : Security(secTypes) {} @@ -35,7 +36,7 @@ namespace rfb { /* Create client side CSecurity class instance */ CSecurity* GetCSecurity(CConnection* cc, uint32_t secType); - static StringParameter secTypes; + static core::EnumListParameter secTypes; }; } diff --git a/common/rfb/SecurityServer.cxx b/common/rfb/SecurityServer.cxx index d692f4fc..207e70f8 100644 --- a/common/rfb/SecurityServer.cxx +++ b/common/rfb/SecurityServer.cxx @@ -21,7 +21,11 @@ #include <config.h> #endif +#include <stdexcept> + #include <rfb/Security.h> +#include <rfb/SecurityServer.h> + #include <rfb/SSecurityNone.h> #include <rfb/SSecurityStack.h> #include <rfb/SSecurityPlain.h> @@ -36,7 +40,7 @@ using namespace rfb; -StringParameter SecurityServer::secTypes +core::EnumListParameter SecurityServer::secTypes ("SecurityTypes", "Specify which security scheme to use (None, VncAuth, Plain" #ifdef HAVE_GNUTLS @@ -46,11 +50,19 @@ StringParameter SecurityServer::secTypes ", RA2, RA2ne, RA2_256, RA2ne_256" #endif ")", + { "None", "VncAuth", "Plain", +#ifdef HAVE_GNUTLS + "TLSNone", "TLSVnc", "TLSPlain", "X509None", "X509Vnc", "X509Plain", +#endif +#ifdef HAVE_NETTLE + "RA2", "RA2ne", "RA2_256", "RA2ne_256", +#endif + }, + { #ifdef HAVE_GNUTLS - "TLSVnc," + "TLSVnc", #endif - "VncAuth", -ConfServer); + "VncAuth"}); SSecurity* SecurityServer::GetSSecurity(SConnection* sc, uint32_t secType) { diff --git a/common/rfb/SecurityServer.h b/common/rfb/SecurityServer.h index a51ee23c..0239b36d 100644 --- a/common/rfb/SecurityServer.h +++ b/common/rfb/SecurityServer.h @@ -20,7 +20,6 @@ #ifndef __RFB_SECURITYSERVER_H__ #define __RFB_SECURITYSERVER_H__ -#include <rfb/Configuration.h> #include <rfb/Security.h> namespace rfb { @@ -35,7 +34,7 @@ namespace rfb { /* Create server side SSecurity class instance */ SSecurity* GetSSecurity(SConnection* sc, uint32_t secType); - static StringParameter secTypes; + static core::EnumListParameter secTypes; }; } diff --git a/common/rfb/ServerCore.cxx b/common/rfb/ServerCore.cxx index 1a80dc0c..302dad71 100644 --- a/common/rfb/ServerCore.cxx +++ b/common/rfb/ServerCore.cxx @@ -28,74 +28,74 @@ #include <string.h> #include <rfb/ServerCore.h> -rfb::IntParameter rfb::Server::idleTimeout +core::IntParameter rfb::Server::idleTimeout ("IdleTimeout", "The number of seconds after which an idle VNC connection will be dropped " "(zero means no timeout)", - 0, 0); -rfb::IntParameter rfb::Server::maxDisconnectionTime + 0, 0, INT_MAX); +core::IntParameter rfb::Server::maxDisconnectionTime ("MaxDisconnectionTime", "Terminate when no client has been connected for s seconds", - 0, 0); -rfb::IntParameter rfb::Server::maxConnectionTime + 0, 0, INT_MAX); +core::IntParameter rfb::Server::maxConnectionTime ("MaxConnectionTime", "Terminate when a client has been connected for s seconds", - 0, 0); -rfb::IntParameter rfb::Server::maxIdleTime + 0, 0, INT_MAX); +core::IntParameter rfb::Server::maxIdleTime ("MaxIdleTime", "Terminate after s seconds of user inactivity", - 0, 0); -rfb::IntParameter rfb::Server::compareFB + 0, 0, INT_MAX); +core::IntParameter rfb::Server::compareFB ("CompareFB", "Perform pixel comparison on framebuffer to reduce unnecessary updates " "(0: never, 1: always, 2: auto)", - 2); -rfb::IntParameter rfb::Server::frameRate + 2, 0, 2); +core::IntParameter rfb::Server::frameRate ("FrameRate", "The maximum number of updates per second sent to each client", - 60); -rfb::BoolParameter rfb::Server::protocol3_3 + 60, 0, INT_MAX); +core::BoolParameter rfb::Server::protocol3_3 ("Protocol3.3", "Always use protocol version 3.3 for backwards compatibility with " "badly-behaved clients", false); -rfb::BoolParameter rfb::Server::alwaysShared +core::BoolParameter rfb::Server::alwaysShared ("AlwaysShared", "Always treat incoming connections as shared, regardless of the client-" "specified setting", false); -rfb::BoolParameter rfb::Server::neverShared +core::BoolParameter rfb::Server::neverShared ("NeverShared", "Never treat incoming connections as shared, regardless of the client-" "specified setting", false); -rfb::BoolParameter rfb::Server::disconnectClients +core::BoolParameter rfb::Server::disconnectClients ("DisconnectClients", "Disconnect existing clients if an incoming connection is non-shared. " "If combined with NeverShared then new connections will be refused " "while there is a client active", true); -rfb::BoolParameter rfb::Server::acceptKeyEvents +core::BoolParameter rfb::Server::acceptKeyEvents ("AcceptKeyEvents", "Accept key press and release events from clients.", true); -rfb::BoolParameter rfb::Server::acceptPointerEvents +core::BoolParameter rfb::Server::acceptPointerEvents ("AcceptPointerEvents", "Accept pointer movement and button events from clients.", true); -rfb::BoolParameter rfb::Server::acceptCutText +core::BoolParameter rfb::Server::acceptCutText ("AcceptCutText", "Accept clipboard updates from clients.", true); -rfb::BoolParameter rfb::Server::sendCutText +core::BoolParameter rfb::Server::sendCutText ("SendCutText", "Send clipboard changes to clients.", true); -rfb::BoolParameter rfb::Server::acceptSetDesktopSize +core::BoolParameter rfb::Server::acceptSetDesktopSize ("AcceptSetDesktopSize", "Accept set desktop size events from clients.", true); -rfb::BoolParameter rfb::Server::queryConnect +core::BoolParameter rfb::Server::queryConnect ("QueryConnect", "Prompt the local user to accept or reject incoming connections.", false); diff --git a/common/rfb/ServerCore.h b/common/rfb/ServerCore.h index 69cad39f..a7c7f309 100644 --- a/common/rfb/ServerCore.h +++ b/common/rfb/ServerCore.h @@ -24,29 +24,29 @@ #ifndef __RFB_SERVER_CORE_H__ #define __RFB_SERVER_CORE_H__ -#include <rfb/Configuration.h> +#include <core/Configuration.h> namespace rfb { class Server { public: - static IntParameter idleTimeout; - static IntParameter maxDisconnectionTime; - static IntParameter maxConnectionTime; - static IntParameter maxIdleTime; - static IntParameter compareFB; - static IntParameter frameRate; - static BoolParameter protocol3_3; - static BoolParameter alwaysShared; - static BoolParameter neverShared; - static BoolParameter disconnectClients; - static BoolParameter acceptKeyEvents; - static BoolParameter acceptPointerEvents; - static BoolParameter acceptCutText; - static BoolParameter sendCutText; - static BoolParameter acceptSetDesktopSize; - static BoolParameter queryConnect; + static core::IntParameter idleTimeout; + static core::IntParameter maxDisconnectionTime; + static core::IntParameter maxConnectionTime; + static core::IntParameter maxIdleTime; + static core::IntParameter compareFB; + static core::IntParameter frameRate; + static core::BoolParameter protocol3_3; + static core::BoolParameter alwaysShared; + static core::BoolParameter neverShared; + static core::BoolParameter disconnectClients; + static core::BoolParameter acceptKeyEvents; + static core::BoolParameter acceptPointerEvents; + static core::BoolParameter acceptCutText; + static core::BoolParameter sendCutText; + static core::BoolParameter acceptSetDesktopSize; + static core::BoolParameter queryConnect; }; diff --git a/common/rfb/ServerParams.cxx b/common/rfb/ServerParams.cxx index b7432b8f..4b8f6136 100644 --- a/common/rfb/ServerParams.cxx +++ b/common/rfb/ServerParams.cxx @@ -24,12 +24,18 @@ #include <stdexcept> +#include <core/LogWriter.h> +#include <core/string.h> + #include <rfb/ledStates.h> +#include <rfb/Cursor.h> +#include <rfb/ScreenSet.h> #include <rfb/ServerParams.h> -#include <rfb/util.h> using namespace rfb; +static core::LogWriter vlog("ServerParams"); + ServerParams::ServerParams() : majorVersion(0), minorVersion(0), supportsQEMUKeyEvent(false), @@ -40,7 +46,11 @@ ServerParams::ServerParams() { setName(""); - cursor_ = new Cursor(0, 0, Point(), nullptr); + screenLayout_ = new ScreenSet(); + + pf_ = new PixelFormat(); + + cursor_ = new Cursor(0, 0, {}, nullptr); clipFlags = 0; memset(clipSizes, 0, sizeof(clipSizes)); @@ -60,17 +70,25 @@ void ServerParams::setDimensions(int width, int height) void ServerParams::setDimensions(int width, int height, const ScreenSet& layout) { - if (!layout.validate(width, height)) + if (!layout.validate(width, height)) { + char buffer[2048]; + vlog.debug("Invalid screen layout for %dx%d:", width, height); + layout.print(buffer, sizeof(buffer)); + vlog.debug("%s", buffer); + throw std::invalid_argument("Attempted to configure an invalid screen layout"); + } width_ = width; height_ = height; - screenLayout_ = layout; + delete screenLayout_; + screenLayout_ = new ScreenSet(layout); } void ServerParams::setPF(const PixelFormat& pf) { - pf_ = pf; + delete pf_; + pf_ = new PixelFormat(pf); if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32) throw std::invalid_argument("setPF: Not 8, 16 or 32 bpp?"); @@ -101,7 +119,8 @@ uint32_t ServerParams::clipboardSize(unsigned int format) const return clipSizes[i]; } - throw std::invalid_argument(rfb::format("Invalid clipboard format 0x%x", format)); + throw std::invalid_argument( + core::format("Invalid clipboard format 0x%x", format)); } void ServerParams::setClipboardCaps(uint32_t flags, const uint32_t* lengths) diff --git a/common/rfb/ServerParams.h b/common/rfb/ServerParams.h index d730b891..6be9acd6 100644 --- a/common/rfb/ServerParams.h +++ b/common/rfb/ServerParams.h @@ -25,12 +25,12 @@ #include <string> -#include <rfb/Cursor.h> -#include <rfb/PixelFormat.h> -#include <rfb/ScreenSet.h> - namespace rfb { + class Cursor; + class PixelFormat; + struct ScreenSet; + class ServerParams { public: ServerParams(); @@ -55,11 +55,11 @@ namespace rfb { int width() const { return width_; } int height() const { return height_; } - const ScreenSet& screenLayout() const { return screenLayout_; } + const ScreenSet& screenLayout() const { return *screenLayout_; } void setDimensions(int width, int height); void setDimensions(int width, int height, const ScreenSet& layout); - const PixelFormat& pf() const { return pf_; } + const PixelFormat& pf() const { return *pf_; } void setPF(const PixelFormat& pf); const char* name() const { return name_.c_str(); } @@ -85,9 +85,9 @@ namespace rfb { int width_; int height_; - ScreenSet screenLayout_; + ScreenSet* screenLayout_; - PixelFormat pf_; + PixelFormat* pf_; std::string name_; Cursor* cursor_; unsigned int ledState_; diff --git a/common/rfb/TightDecoder.cxx b/common/rfb/TightDecoder.cxx index a26c0bfe..da0d5865 100644 --- a/common/rfb/TightDecoder.cxx +++ b/common/rfb/TightDecoder.cxx @@ -27,16 +27,18 @@ #include <vector> +#include <core/string.h> + #include <rdr/InStream.h> #include <rdr/MemInStream.h> #include <rdr/OutStream.h> #include <rfb/ServerParams.h> #include <rfb/Exception.h> +#include <rfb/JpegDecompressor.h> #include <rfb/PixelBuffer.h> #include <rfb/TightConstants.h> #include <rfb/TightDecoder.h> -#include <rfb/util.h> using namespace rfb; @@ -51,7 +53,7 @@ TightDecoder::~TightDecoder() { } -bool TightDecoder::readRect(const Rect& r, rdr::InStream* is, +bool TightDecoder::readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) { uint8_t comp_ctl; @@ -111,7 +113,8 @@ bool TightDecoder::readRect(const Rect& r, rdr::InStream* is, int palSize = 0; if (r.width() > TIGHT_MAX_WIDTH) - throw protocol_error(format("TightDecoder: Too large rectangle (%d pixels)", r.width())); + throw protocol_error(core::format( + "TightDecoder: Too large rectangle (%d pixels)", r.width())); // Possible palette if ((comp_ctl & tightExplicitFilter) != 0) { @@ -192,10 +195,10 @@ bool TightDecoder::readRect(const Rect& r, rdr::InStream* is, return true; } -bool TightDecoder::doRectsConflict(const Rect& /*rectA*/, +bool TightDecoder::doRectsConflict(const core::Rect& /*rectA*/, const uint8_t* bufferA, size_t buflenA, - const Rect& /*rectB*/, + const core::Rect& /*rectB*/, const uint8_t* bufferB, size_t buflenB, const ServerParams& /*server*/) @@ -220,7 +223,7 @@ bool TightDecoder::doRectsConflict(const Rect& /*rectA*/, return false; } -void TightDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void TightDecoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) { @@ -506,7 +509,7 @@ uint32_t TightDecoder::readCompact(rdr::InStream* is) void TightDecoder::FilterGradient24(const uint8_t *inbuf, const PixelFormat& pf, uint32_t* outbuf, - int stride, const Rect& r) + int stride, const core::Rect& r) { int x, y, c; uint8_t prevRow[TIGHT_MAX_WIDTH*3]; @@ -552,7 +555,7 @@ TightDecoder::FilterGradient24(const uint8_t *inbuf, template<class T> void TightDecoder::FilterGradient(const uint8_t* inbuf, const PixelFormat& pf, T* outbuf, - int stride, const Rect& r) + int stride, const core::Rect& r) { int x, y, c; static uint8_t prevRow[TIGHT_MAX_WIDTH*3]; @@ -606,7 +609,7 @@ void TightDecoder::FilterGradient(const uint8_t* inbuf, template<class T> void TightDecoder::FilterPalette(const T* palette, int palSize, const uint8_t* inbuf, T* outbuf, - int stride, const Rect& r) + int stride, const core::Rect& r) { // Indexed color int x, h = r.height(), w = r.width(), b, pad = stride - w; diff --git a/common/rfb/TightDecoder.h b/common/rfb/TightDecoder.h index d569a7fd..a75fc7da 100644 --- a/common/rfb/TightDecoder.h +++ b/common/rfb/TightDecoder.h @@ -22,7 +22,6 @@ #include <rdr/ZlibInStream.h> #include <rfb/Decoder.h> -#include <rfb/JpegDecompressor.h> namespace rfb { @@ -31,15 +30,15 @@ namespace rfb { public: TightDecoder(); virtual ~TightDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - bool doRectsConflict(const Rect& rectA, + bool doRectsConflict(const core::Rect& rectA, const uint8_t* bufferA, size_t buflenA, - const Rect& rectB, + const core::Rect& rectB, const uint8_t* bufferB, size_t buflenB, const ServerParams& server) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; @@ -47,16 +46,16 @@ namespace rfb { uint32_t readCompact(rdr::InStream* is); void FilterGradient24(const uint8_t* inbuf, const PixelFormat& pf, - uint32_t* outbuf, int stride, const Rect& r); + uint32_t* outbuf, int stride, const core::Rect& r); template<class T> void FilterGradient(const uint8_t* inbuf, const PixelFormat& pf, - T* outbuf, int stride, const Rect& r); + T* outbuf, int stride, const core::Rect& r); template<class T> void FilterPalette(const T* palette, int palSize, const uint8_t* inbuf, T* outbuf, - int stride, const Rect& r); + int stride, const core::Rect& r); private: rdr::ZlibInStream zis[4]; diff --git a/common/rfb/UnixPasswordValidator.cxx b/common/rfb/UnixPasswordValidator.cxx index 57fa9b39..8239463a 100644 --- a/common/rfb/UnixPasswordValidator.cxx +++ b/common/rfb/UnixPasswordValidator.cxx @@ -22,24 +22,119 @@ #include <config.h> #endif -#include <rfb/Configuration.h> -#include <rfb/Exception.h> +#include <assert.h> +#include <string.h> +#include <security/pam_appl.h> + +#include <core/Configuration.h> +#include <core/LogWriter.h> + #include <rfb/UnixPasswordValidator.h> -#include <rfb/pam.h> using namespace rfb; -static StringParameter pamService +static core::LogWriter vlog("UnixPasswordValidator"); + +static core::StringParameter pamService ("PAMService", "Service name for PAM password validation", "vnc"); -AliasParameter pam_service("pam_service", "Alias for PAMService", - &pamService); +core::AliasParameter pam_service("pam_service", "Alias for PAMService", + &pamService); -int do_pam_auth(const char *service, const char *username, - const char *password); +std::string UnixPasswordValidator::displayName; -bool UnixPasswordValidator::validateInternal(SConnection * /*sc*/, +typedef struct +{ + const char *username; + const char *password; + std::string &msg; +} AuthData; + +#if defined(__sun) +static int pam_callback(int count, struct pam_message **in, + struct pam_response **out, void *ptr) +#else +static int pam_callback(int count, const struct pam_message **in, + struct pam_response **out, void *ptr) +#endif +{ + int i; + AuthData *auth = (AuthData *) ptr; + struct pam_response *resp = + (struct pam_response *) malloc (sizeof (struct pam_response) * count); + + if (!resp && count) + return PAM_CONV_ERR; + + for (i = 0; i < count; i++) { + resp[i].resp_retcode = PAM_SUCCESS; + switch (in[i]->msg_style) { + case PAM_TEXT_INFO: + vlog.info("%s info: %s", (const char *) pamService, in[i]->msg); + auth->msg = in[i]->msg; + resp[i].resp = nullptr; + break; + case PAM_ERROR_MSG: + vlog.error("%s error: %s", (const char *) pamService, in[i]->msg); + auth->msg = in[i]->msg; + resp[i].resp = nullptr; + break; + case PAM_PROMPT_ECHO_ON: /* Send Username */ + resp[i].resp = strdup(auth->username); + break; + case PAM_PROMPT_ECHO_OFF: /* Send Password */ + resp[i].resp = strdup(auth->password); + break; + default: + free(resp); + return PAM_CONV_ERR; + } + } + + *out = resp; + return PAM_SUCCESS; +} + +bool UnixPasswordValidator::validateInternal(SConnection * /* sc */, const char *username, - const char *password) + const char *password, + std::string &msg) { - return do_pam_auth(pamService, username, password); + int ret; + AuthData auth = { username, password, msg }; + struct pam_conv conv = { + pam_callback, + &auth + }; + pam_handle_t *pamh = nullptr; + ret = pam_start(pamService, username, &conv, &pamh); + if (ret != PAM_SUCCESS) { + /* Can't call pam_strerror() here because the content of pamh undefined */ + vlog.error("pam_start(%s) failed: %d", (const char *) pamService, ret); + return false; + } +#ifdef PAM_XDISPLAY + /* At this point, displayName should never be empty */ + assert(displayName.length() > 0); + /* Pass the display name to PAM modules but PAM_XDISPLAY may not be + * recognized by modules built with old versions of PAM */ + ret = pam_set_item(pamh, PAM_XDISPLAY, displayName.c_str()); + if (ret != PAM_SUCCESS && ret != PAM_BAD_ITEM) { + vlog.error("pam_set_item(PAM_XDISPLAY) failed: %d (%s)", ret, pam_strerror(pamh, ret)); + goto error; + } +#endif + ret = pam_authenticate(pamh, 0); + if (ret != PAM_SUCCESS) { + vlog.error("pam_authenticate() failed: %d (%s)", ret, pam_strerror(pamh, ret)); + goto error; + } + ret = pam_acct_mgmt(pamh, 0); + if (ret != PAM_SUCCESS) { + vlog.error("pam_acct_mgmt() failed: %d (%s)", ret, pam_strerror(pamh, ret)); + goto error; + } + return true; +error: + pam_end(pamh, ret); + return false; } diff --git a/common/rfb/UnixPasswordValidator.h b/common/rfb/UnixPasswordValidator.h index 4d623d6c..a2cc89c5 100644 --- a/common/rfb/UnixPasswordValidator.h +++ b/common/rfb/UnixPasswordValidator.h @@ -26,9 +26,19 @@ namespace rfb { class UnixPasswordValidator: public PasswordValidator { + public: + static void setDisplayName(const std::string& display) { + displayName = display; + } + protected: - bool validateInternal(SConnection * sc, const char *username, - const char *password) override; + bool validateInternal(SConnection *sc, + const char *username, + const char *password, + std::string &msg) override; + + private: + static std::string displayName; }; } diff --git a/common/rfb/UpdateTracker.cxx b/common/rfb/UpdateTracker.cxx index 7c97a6b8..1aedf491 100644 --- a/common/rfb/UpdateTracker.cxx +++ b/common/rfb/UpdateTracker.cxx @@ -25,27 +25,31 @@ #include <config.h> #endif +#include <core/LogWriter.h> + #include <rfb/UpdateTracker.h> -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("UpdateTracker"); +static core::LogWriter vlog("UpdateTracker"); // -=- ClippingUpdateTracker -void ClippingUpdateTracker::add_changed(const Region ®ion) { +void ClippingUpdateTracker::add_changed(const core::Region& region) +{ ut->add_changed(region.intersect(clipRect)); } -void ClippingUpdateTracker::add_copied(const Region &dest, const Point &delta) { +void ClippingUpdateTracker::add_copied(const core::Region& dest, + const core::Point& delta) +{ // Clip the destination to the display area - Region clipdest = dest.intersect(clipRect); + core::Region clipdest = dest.intersect(clipRect); if (clipdest.is_empty()) return; // Clip the source to the screen - Region tmp = clipdest; + core::Region tmp = clipdest; tmp.translate(delta.negate()); tmp.assign_intersect(clipRect); if (!tmp.is_empty()) { @@ -70,25 +74,28 @@ SimpleUpdateTracker::SimpleUpdateTracker() { SimpleUpdateTracker::~SimpleUpdateTracker() { } -void SimpleUpdateTracker::add_changed(const Region ®ion) { +void SimpleUpdateTracker::add_changed(const core::Region& region) +{ changed.assign_union(region); } -void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { +void SimpleUpdateTracker::add_copied(const core::Region& dest, + const core::Point& delta) +{ // Is there anything to do? if (dest.is_empty()) return; // Calculate whether any of this copy can be treated as a continuation // of an earlier one - Region src = dest; + core::Region src = dest; src.translate(delta.negate()); - Region overlap = src.intersect(copied); + core::Region overlap = src.intersect(copied); if (overlap.is_empty()) { // There is no overlap - Rect newbr = dest.get_bounding_rect(); - Rect oldbr = copied.get_bounding_rect(); + core::Rect newbr = dest.get_bounding_rect(); + core::Rect oldbr = copied.get_bounding_rect(); if (oldbr.area() > newbr.area()) { // Old copyrect is (probably) bigger - use it changed.assign_union(dest); @@ -97,7 +104,7 @@ void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { // Use the new one // But be careful not to copy stuff that still needs // to be updated. - Region invalid_src = src.intersect(changed); + core::Region invalid_src = src.intersect(changed); invalid_src.translate(delta); changed.assign_union(invalid_src); changed.assign_union(copied); @@ -107,13 +114,13 @@ void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { return; } - Region invalid_src = overlap.intersect(changed); + core::Region invalid_src = overlap.intersect(changed); invalid_src.translate(delta); changed.assign_union(invalid_src); overlap.translate(delta); - Region nonoverlapped_copied = dest.union_(copied).subtract(overlap); + core::Region nonoverlapped_copied = dest.union_(copied).subtract(overlap); changed.assign_union(nonoverlapped_copied); copied = overlap; @@ -122,12 +129,14 @@ void SimpleUpdateTracker::add_copied(const Region &dest, const Point &delta) { return; } -void SimpleUpdateTracker::subtract(const Region& region) { +void SimpleUpdateTracker::subtract(const core::Region& region) +{ copied.assign_subtract(region); changed.assign_subtract(region); } -void SimpleUpdateTracker::getUpdateInfo(UpdateInfo* info, const Region& clip) +void SimpleUpdateTracker::getUpdateInfo(UpdateInfo* info, + const core::Region& clip) { copied.assign_subtract(changed); info->changed = changed.intersect(clip); diff --git a/common/rfb/UpdateTracker.h b/common/rfb/UpdateTracker.h index e91b9621..3d7a2fcd 100644 --- a/common/rfb/UpdateTracker.h +++ b/common/rfb/UpdateTracker.h @@ -19,17 +19,16 @@ #ifndef __RFB_UPDATETRACKER_INCLUDED__ #define __RFB_UPDATETRACKER_INCLUDED__ -#include <rfb/Rect.h> -#include <rfb/Region.h> -#include <rfb/PixelBuffer.h> +#include <core/Rect.h> +#include <core/Region.h> namespace rfb { class UpdateInfo { public: - Region changed; - Region copied; - Point copy_delta; + core::Region changed; + core::Region copied; + core::Point copy_delta; bool is_empty() const { return copied.is_empty() && changed.is_empty(); } @@ -47,23 +46,25 @@ namespace rfb { UpdateTracker() {}; virtual ~UpdateTracker() {}; - virtual void add_changed(const Region ®ion) = 0; - virtual void add_copied(const Region &dest, const Point &delta) = 0; + virtual void add_changed(const core::Region& region) = 0; + virtual void add_copied(const core::Region& dest, + const core::Point& delta) = 0; }; class ClippingUpdateTracker : public UpdateTracker { public: ClippingUpdateTracker() : ut(nullptr) {} - ClippingUpdateTracker(UpdateTracker* ut_, const Rect& r=Rect()) : ut(ut_), clipRect(r) {} + ClippingUpdateTracker(UpdateTracker* ut_, const core::Rect& r={}) : ut(ut_), clipRect(r) {} void setUpdateTracker(UpdateTracker* ut_) {ut = ut_;} - void setClipRect(const Rect& cr) {clipRect = cr;} + void setClipRect(const core::Rect& cr) {clipRect = cr;} - void add_changed(const Region ®ion) override; - void add_copied(const Region &dest, const Point &delta) override; + void add_changed(const core::Region& region) override; + void add_copied(const core::Region& dest, + const core::Point& delta) override; protected: UpdateTracker* ut; - Rect clipRect; + core::Rect clipRect; }; class SimpleUpdateTracker : public UpdateTracker { @@ -71,27 +72,29 @@ namespace rfb { SimpleUpdateTracker(); virtual ~SimpleUpdateTracker(); - void add_changed(const Region ®ion) override; - void add_copied(const Region &dest, const Point &delta) override; - virtual void subtract(const Region& region); + void add_changed(const core::Region& region) override; + void add_copied(const core::Region& dest, + const core::Point& delta) override; + virtual void subtract(const core::Region& region); // Fill the supplied UpdateInfo structure with update information // FIXME: Provide getUpdateInfo() with no clipping, for better efficiency. - virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn); + virtual void getUpdateInfo(UpdateInfo* info, + const core::Region& cliprgn); // Copy the contained updates to another tracker virtual void copyTo(UpdateTracker* to) const; // Move the entire update region by an offset - void translate(const Point& p) {changed.translate(p); copied.translate(p);} + void translate(const core::Point& p) {changed.translate(p); copied.translate(p);} virtual bool is_empty() const {return changed.is_empty() && copied.is_empty();} virtual void clear() {changed.clear(); copied.clear();}; protected: - Region changed; - Region copied; - Point copy_delta; + core::Region changed; + core::Region copied; + core::Point copy_delta; }; } diff --git a/common/rfb/VNCSConnectionST.cxx b/common/rfb/VNCSConnectionST.cxx index a354f636..2d77fae6 100644 --- a/common/rfb/VNCSConnectionST.cxx +++ b/common/rfb/VNCSConnectionST.cxx @@ -22,7 +22,12 @@ #include <config.h> #endif -#include <rdr/Exception.h> +#include <core/LogWriter.h> +#include <core/string.h> +#include <core/time.h> + +#include <rdr/FdInStream.h> +#include <rdr/FdOutStream.h> #include <network/TcpSocket.h> @@ -31,12 +36,12 @@ #include <rfb/Exception.h> #include <rfb/KeyRemapper.h> #include <rfb/KeysymStr.h> -#include <rfb/LogWriter.h> #include <rfb/Security.h> #include <rfb/ServerCore.h> #include <rfb/SMsgWriter.h> #include <rfb/VNCServerST.h> #include <rfb/VNCSConnectionST.h> +#include <rfb/encodings.h> #include <rfb/screenTypes.h> #include <rfb/fenceTypes.h> #include <rfb/ledStates.h> @@ -44,13 +49,12 @@ #define XK_MISCELLANY #define XK_XKB_KEYS #include <rfb/keysymdef.h> -#include <rfb/util.h> using namespace rfb; -static LogWriter vlog("VNCSConnST"); +static core::LogWriter vlog("VNCSConnST"); -static Cursor emptyCursor(0, 0, Point(0, 0), nullptr); +static Cursor emptyCursor(0, 0, {0, 0}, nullptr); VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, bool reverse, AccessRights ar) @@ -71,9 +75,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s, if (rfb::Server::idleTimeout) { // minimum of 15 seconds while authenticating if (rfb::Server::idleTimeout < 15) - idleTimer.start(secsToMillis(15)); + idleTimer.start(core::secsToMillis(15)); else - idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); + idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout)); } } @@ -216,11 +220,11 @@ void VNCSConnectionST::pixelBufferChange() //updates.intersect(server->pb->getRect()); // //if (server->pb->width() > client.width()) - // updates.add_changed(Rect(client.width(), 0, server->pb->width(), - // server->pb->height())); + // updates.add_changed({client.width(), 0, server->pb->width(), + // server->pb->height()}); //if (server->pb->height() > client.height()) - // updates.add_changed(Rect(0, client.height(), client.width(), - // server->pb->height())); + // updates.add_changed({0, client.height(), client.width(), + // server->pb->height()}); damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect()); @@ -236,7 +240,7 @@ void VNCSConnectionST::pixelBufferChange() } // Drop any lossy tracking that is now outside the framebuffer - encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect())); + encodeManager.pruneLosslessRefresh(server->getPixelBuffer()->getRect()); } // Just update the whole screen at the moment because we're too lazy to // work out what's actually changed. @@ -310,8 +314,6 @@ void VNCSConnectionST::requestClipboardOrClose() { try { if (state() != RFBSTATE_NORMAL) return; - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; requestClipboard(); } catch(std::exception& e) { close(e.what()); @@ -322,8 +324,6 @@ void VNCSConnectionST::announceClipboardOrClose(bool available) { try { if (state() != RFBSTATE_NORMAL) return; - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::sendCutText) return; announceClipboard(available); } catch(std::exception& e) { close(e.what()); @@ -334,8 +334,6 @@ void VNCSConnectionST::sendClipboardDataOrClose(const char* data) { try { if (state() != RFBSTATE_NORMAL) return; - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::sendCutText) return; sendClipboardData(data); } catch(std::exception& e) { close(e.what()); @@ -421,7 +419,7 @@ void VNCSConnectionST::approveConnectionOrClose(bool accept, void VNCSConnectionST::authSuccess() { if (rfb::Server::idleTimeout) - idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); + idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout)); // - Set the connection parameters appropriately client.setDimensions(server->getPixelBuffer()->width(), @@ -448,7 +446,7 @@ void VNCSConnectionST::queryConnection(const char* userName) void VNCSConnectionST::clientInit(bool shared) { if (rfb::Server::idleTimeout) - idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); + idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout)); if (rfb::Server::alwaysShared || reverseConnection) shared = true; if (!accessCheck(AccessNonShared)) shared = true; if (rfb::Server::neverShared) shared = false; @@ -463,15 +461,16 @@ void VNCSConnectionST::setPixelFormat(const PixelFormat& pf) pf.print(buffer, 256); vlog.info("Client pixel format %s", buffer); setCursor(); + encodeManager.forceRefresh(server->getPixelBuffer()->getRect()); } -void VNCSConnectionST::pointerEvent(const Point& pos, uint16_t buttonMask) +void VNCSConnectionST::pointerEvent(const core::Point& pos, + uint16_t buttonMask) { if (rfb::Server::idleTimeout) - idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); + idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout)); pointerEventTime = time(nullptr); if (!accessCheck(AccessPtrEvents)) return; - if (!rfb::Server::acceptPointerEvents) return; pointerEventPos = pos; server->pointerEvent(this, pointerEventPos, buttonMask); } @@ -502,8 +501,10 @@ void VNCSConnectionST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) { uint32_t lookup; if (rfb::Server::idleTimeout) - idleTimer.start(secsToMillis(rfb::Server::idleTimeout)); + idleTimer.start(core::secsToMillis(rfb::Server::idleTimeout)); if (!accessCheck(AccessKeyEvents)) return; + // FIXME: This check isn't strictly needed, but we get a lot of + // confusing debug logging without it if (!rfb::Server::acceptKeyEvents) return; if (down) @@ -605,27 +606,28 @@ void VNCSConnectionST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) { server->keyEvent(keysym, keycode, down); } -void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental) +void VNCSConnectionST::framebufferUpdateRequest(const core::Rect& r, + bool incremental) { - Rect safeRect; + core::Rect safeRect; if (!accessCheck(AccessView)) return; SConnection::framebufferUpdateRequest(r, incremental); // Check that the client isn't sending crappy requests - if (!r.enclosed_by(Rect(0, 0, client.width(), client.height()))) { + if (!r.enclosed_by({0, 0, client.width(), client.height()})) { vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d", r.width(), r.height(), r.tl.x, r.tl.y, client.width(), client.height()); - safeRect = r.intersect(Rect(0, 0, client.width(), client.height())); + safeRect = r.intersect({0, 0, client.width(), client.height()}); } else { safeRect = r; } // Just update the requested region. // Framebuffer update will be sent a bit later, see processMessages(). - Region reqRgn(safeRect); + core::Region reqRgn(safeRect); if (!incremental || !continuousUpdates) requested.assign_union(reqRgn); @@ -656,8 +658,7 @@ void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height, layout.print(buffer, sizeof(buffer)); vlog.debug("%s", buffer); - if (!accessCheck(AccessSetDesktopSize) || - !rfb::Server::acceptSetDesktopSize) { + if (!accessCheck(AccessSetDesktopSize)) { vlog.debug("Rejecting unauthorized framebuffer resize request"); result = resultProhibited; } else { @@ -716,7 +717,10 @@ void VNCSConnectionST::fence(uint32_t flags, unsigned len, const uint8_t data[]) void VNCSConnectionST::enableContinuousUpdates(bool enable, int x, int y, int w, int h) { - Rect rect; + core::Rect rect; + + if (!accessCheck(AccessView)) + return; if (!client.supportsFence() || !client.supportsContinuousUpdates()) throw protocol_error("Client tried to enable continuous updates when not allowed"); @@ -735,21 +739,16 @@ void VNCSConnectionST::enableContinuousUpdates(bool enable, void VNCSConnectionST::handleClipboardRequest() { - if (!accessCheck(AccessCutText)) return; server->handleClipboardRequest(this); } void VNCSConnectionST::handleClipboardAnnounce(bool available) { - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; server->handleClipboardAnnounce(this, available); } void VNCSConnectionST::handleClipboardData(const char* data) { - if (!accessCheck(AccessCutText)) return; - if (!rfb::Server::acceptCutText) return; server->handleClipboardData(this, data); } @@ -790,7 +789,7 @@ void VNCSConnectionST::supportsLEDState() writer()->writeLEDState(); } -void VNCSConnectionST::handleTimeout(Timer* t) +void VNCSConnectionST::handleTimeout(core::Timer* t) { try { if ((t == &congestionTimer) || @@ -921,7 +920,7 @@ void VNCSConnectionST::writeNoDataUpdate() void VNCSConnectionST::writeDataUpdate() { - Region req; + core::Region req; UpdateInfo ui; bool needNewUpdateInfo; const RenderedCursor *cursor; @@ -946,7 +945,7 @@ void VNCSConnectionST::writeDataUpdate() // destination will be wrong, so add it to the changed region. if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) { - Region bogusCopiedCursor; + core::Region bogusCopiedCursor; bogusCopiedCursor = damagedCursorRegion; bogusCopiedCursor.translate(ui.copy_delta); @@ -993,7 +992,7 @@ void VNCSConnectionST::writeDataUpdate() cursor = nullptr; if (needRenderedCursor()) { - Rect renderedCursorRect; + core::Rect renderedCursorRect; cursor = server->getRenderedCursor(); renderedCursorRect = cursor->getEffectiveRect(); @@ -1033,7 +1032,7 @@ void VNCSConnectionST::writeDataUpdate() void VNCSConnectionST::writeLosslessRefresh() { - Region req, pending; + core::Region req, pending; const RenderedCursor *cursor; int nextRefresh, nextUpdate; diff --git a/common/rfb/VNCSConnectionST.h b/common/rfb/VNCSConnectionST.h index 17de9d01..b618923f 100644 --- a/common/rfb/VNCSConnectionST.h +++ b/common/rfb/VNCSConnectionST.h @@ -29,16 +29,17 @@ #include <map> +#include <core/Timer.h> + #include <rfb/Congestion.h> #include <rfb/EncodeManager.h> #include <rfb/SConnection.h> -#include <rfb/Timer.h> namespace rfb { class VNCServerST; class VNCSConnectionST : private SConnection, - public Timer::Callback { + public core::Timer::Callback { public: VNCSConnectionST(VNCServerST* server_, network::Socket* s, bool reverse, AccessRights ar); @@ -108,8 +109,8 @@ namespace rfb { // Change tracking - void add_changed(const Region& region) { updates.add_changed(region); } - void add_copied(const Region& dest, const Point& delta) { + void add_changed(const core::Region& region) { updates.add_changed(region); } + void add_copied(const core::Region& dest, const core::Point& delta) { updates.add_copied(dest, delta); } @@ -123,10 +124,11 @@ namespace rfb { void queryConnection(const char* userName) override; void clientInit(bool shared) override; void setPixelFormat(const PixelFormat& pf) override; - void pointerEvent(const Point& pos, uint16_t buttonMask) override; + void pointerEvent(const core::Point& pos, + uint16_t buttonMask) override; void keyEvent(uint32_t keysym, uint32_t keycode, bool down) override; - void framebufferUpdateRequest(const Rect& r, + void framebufferUpdateRequest(const core::Rect& r, bool incremental) override; void setDesktopSize(int fb_width, int fb_height, const ScreenSet& layout) override; @@ -143,7 +145,7 @@ namespace rfb { void supportsLEDState() override; // Timer callbacks - void handleTimeout(Timer* t) override; + void handleTimeout(core::Timer* t) override; // Internal methods @@ -180,24 +182,24 @@ namespace rfb { uint8_t *fenceData; Congestion congestion; - Timer congestionTimer; - Timer losslessTimer; + core::Timer congestionTimer; + core::Timer losslessTimer; VNCServerST* server; SimpleUpdateTracker updates; - Region requested; + core::Region requested; bool updateRenderedCursor, removeRenderedCursor; - Region damagedCursorRegion; + core::Region damagedCursorRegion; bool continuousUpdates; - Region cuRegion; + core::Region cuRegion; EncodeManager encodeManager; std::map<uint32_t, uint32_t> pressedKeys; - Timer idleTimer; + core::Timer idleTimer; time_t pointerEventTime; - Point pointerEventPos; + core::Point pointerEventPos; bool clientHasCursor; std::string closeReason; diff --git a/common/rfb/VNCServer.h b/common/rfb/VNCServer.h index 4e3a5b23..4d9b31ed 100644 --- a/common/rfb/VNCServer.h +++ b/common/rfb/VNCServer.h @@ -23,14 +23,19 @@ #ifndef __RFB_VNCSERVER_H__ #define __RFB_VNCSERVER_H__ +#include <list> + +#include <rfb/AccessRights.h> #include <rfb/UpdateTracker.h> -#include <rfb/SSecurity.h> -#include <rfb/ScreenSet.h> namespace network { class Socket; } namespace rfb { + class PixelBuffer; + class SConnection; + struct ScreenSet; + class VNCServer : public UpdateTracker { public: // addSocket() tells the server to serve the Socket. The caller @@ -128,13 +133,14 @@ namespace rfb { // setCursor() tells the server that the cursor has changed. The // cursorData argument contains width*height rgba quadruplets with // non-premultiplied alpha. - virtual void setCursor(int width, int height, const Point& hotspot, + virtual void setCursor(int width, int height, + const core::Point& hotspot, const uint8_t* cursorData) = 0; // setCursorPos() tells the server the current position of the cursor, and // whether the server initiated that change (e.g. through another X11 // client calling XWarpPointer()). - virtual void setCursorPos(const Point& p, bool warped) = 0; + virtual void setCursorPos(const core::Point& p, bool warped) = 0; // setName() tells the server what desktop title to supply to clients virtual void setName(const char* name) = 0; diff --git a/common/rfb/VNCServerST.cxx b/common/rfb/VNCServerST.cxx index b99d33b0..77d652b9 100644 --- a/common/rfb/VNCServerST.cxx +++ b/common/rfb/VNCServerST.cxx @@ -55,24 +55,28 @@ #include <assert.h> #include <stdlib.h> +#include <core/LogWriter.h> +#include <core/time.h> + +#include <rdr/FdOutStream.h> + #include <network/Socket.h> #include <rfb/ComparingUpdateTracker.h> #include <rfb/KeyRemapper.h> #include <rfb/KeysymStr.h> -#include <rfb/LogWriter.h> +#include <rfb/SDesktop.h> #include <rfb/Security.h> #include <rfb/ServerCore.h> #include <rfb/VNCServerST.h> #include <rfb/VNCSConnectionST.h> -#include <rfb/util.h> #include <rfb/ledStates.h> using namespace rfb; -static LogWriter slog("VNCServerST"); -static LogWriter connectionsLog("Connections"); +static core::LogWriter slog("VNCServerST"); +static core::LogWriter connectionsLog("Connections"); // // -=- VNCServerST Implementation @@ -85,7 +89,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) blockCounter(0), pb(nullptr), ledState(ledUnknown), name(name_), pointerClient(nullptr), clipboardClient(nullptr), pointerClientTime(0), - comparer(nullptr), cursor(new Cursor(0, 0, Point(), nullptr)), + comparer(nullptr), cursor(new Cursor(0, 0, {}, nullptr)), renderedCursorInvalid(false), keyRemapper(&KeyRemapper::defInstance), idleTimer(this), disconnectTimer(this), connectTimer(this), @@ -97,9 +101,9 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_) // FIXME: Do we really want to kick off these right away? if (rfb::Server::maxIdleTime) - idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); + idleTimer.start(core::secsToMillis(rfb::Server::maxIdleTime)); if (rfb::Server::maxDisconnectionTime) - disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); + disconnectTimer.start(core::secsToMillis(rfb::Server::maxDisconnectionTime)); } VNCServerST::~VNCServerST() @@ -161,12 +165,18 @@ void VNCServerST::addSocket(network::Socket* sock, bool outgoing, AccessRights a // Adjust the exit timers if (rfb::Server::maxConnectionTime && clients.empty()) - connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime)); + connectTimer.start(core::secsToMillis(rfb::Server::maxConnectionTime)); disconnectTimer.stop(); - VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights); - clients.push_front(client); - client->init(); + try { + VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights); + clients.push_front(client); + client->init(); + } catch (std::exception& e) { + connectionsLog.error("Error accepting client: %s", e.what()); + sock->shutdown(); + closingSockets.push_back(sock); + } } void VNCServerST::removeSocket(network::Socket* sock) { @@ -203,7 +213,7 @@ void VNCServerST::removeSocket(network::Socket* sock) { // Adjust the exit timers connectTimer.stop(); if (rfb::Server::maxDisconnectionTime && clients.empty()) - disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime)); + disconnectTimer.start(core::secsToMillis(rfb::Server::maxDisconnectionTime)); return; } @@ -313,7 +323,7 @@ void VNCServerST::setPixelBuffer(PixelBuffer* pb_) // Check that the screen layout is still valid if (pb_ && !layout.validate(pb_->width(), pb_->height())) { - Rect fbRect; + core::Rect fbRect; ScreenSet::iterator iter, iter_next; fbRect.setXYWH(0, 0, pb_->width(), pb_->height()); @@ -354,6 +364,9 @@ void VNCServerST::setScreenLayout(const ScreenSet& layout) void VNCServerST::requestClipboard() { + if (!rfb::Server::acceptCutText) + return; + if (clipboardClient == nullptr) { slog.debug("Got request for client clipboard but no client currently owns the clipboard"); return; @@ -368,6 +381,9 @@ void VNCServerST::announceClipboard(bool available) clipboardRequestors.clear(); + if (!rfb::Server::sendCutText) + return; + for (ci = clients.begin(); ci != clients.end(); ++ci) (*ci)->announceClipboardOrClose(available); } @@ -376,6 +392,9 @@ void VNCServerST::sendClipboardData(const char* data) { std::list<VNCSConnectionST*>::iterator ci; + if (!rfb::Server::sendCutText) + return; + if (strchr(data, '\r') != nullptr) throw std::invalid_argument("Invalid carriage return in clipboard data"); @@ -401,7 +420,7 @@ void VNCServerST::setName(const char* name_) (*ci)->setDesktopNameOrClose(name_); } -void VNCServerST::add_changed(const Region& region) +void VNCServerST::add_changed(const core::Region& region) { if (comparer == nullptr) return; @@ -410,7 +429,8 @@ void VNCServerST::add_changed(const Region& region) startFrameClock(); } -void VNCServerST::add_copied(const Region& dest, const Point& delta) +void VNCServerST::add_copied(const core::Region& dest, + const core::Point& delta) { if (comparer == nullptr) return; @@ -419,7 +439,8 @@ void VNCServerST::add_copied(const Region& dest, const Point& delta) startFrameClock(); } -void VNCServerST::setCursor(int width, int height, const Point& newHotspot, +void VNCServerST::setCursor(int width, int height, + const core::Point& newHotspot, const uint8_t* data) { delete cursor; @@ -435,7 +456,7 @@ void VNCServerST::setCursor(int width, int height, const Point& newHotspot, } } -void VNCServerST::setCursorPos(const Point& pos, bool warped) +void VNCServerST::setCursorPos(const core::Point& pos, bool warped) { if (cursorPos != pos) { cursorPos = pos; @@ -466,8 +487,11 @@ void VNCServerST::setLEDState(unsigned int state) void VNCServerST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) { + if (!rfb::Server::acceptKeyEvents) + return; + if (rfb::Server::maxIdleTime) - idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); + idleTimer.start(core::secsToMillis(rfb::Server::maxIdleTime)); // Remap the key if required if (keyRemapper) { @@ -484,11 +508,16 @@ void VNCServerST::keyEvent(uint32_t keysym, uint32_t keycode, bool down) } void VNCServerST::pointerEvent(VNCSConnectionST* client, - const Point& pos, uint16_t buttonMask) + const core::Point& pos, + uint16_t buttonMask) { time_t now = time(nullptr); + + if (!rfb::Server::acceptPointerEvents) + return; + if (rfb::Server::maxIdleTime) - idleTimer.start(secsToMillis(rfb::Server::maxIdleTime)); + idleTimer.start(core::secsToMillis(rfb::Server::maxIdleTime)); // Let one client own the cursor whilst buttons are pressed in order // to provide a bit more sane user experience. But limit the time to @@ -516,9 +545,11 @@ void VNCServerST::handleClipboardRequest(VNCSConnectionST* client) void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, bool available) { - if (available) + if (available) { + if (!rfb::Server::acceptCutText) + return; clipboardClient = client; - else { + } else { if (client != clipboardClient) return; clipboardClient = nullptr; @@ -529,6 +560,8 @@ void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client, void VNCServerST::handleClipboardData(VNCSConnectionST* client, const char* data) { + if (!rfb::Server::acceptCutText) + return; if (client != clipboardClient) { slog.debug("Ignoring unexpected clipboard data"); return; @@ -543,6 +576,11 @@ unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester, unsigned int result; std::list<VNCSConnectionST*>::iterator ci; + if (!rfb::Server::acceptSetDesktopSize) { + slog.debug("Rejecting unauthorized framebuffer resize request"); + return resultProhibited; + } + // We can't handle a framebuffer larger than this, so don't let a // client set one (see PixelBuffer.cxx) if ((fb_width > 16384) || (fb_height > 16384)) { @@ -622,7 +660,7 @@ SConnection* VNCServerST::getConnection(network::Socket* sock) { return nullptr; } -void VNCServerST::handleTimeout(Timer* t) +void VNCServerST::handleTimeout(core::Timer* t) { if (t == &frameTimer) { int timeout; @@ -822,7 +860,7 @@ int VNCServerST::msToNextUpdate() void VNCServerST::writeUpdate() { UpdateInfo ui; - Region toCheck; + core::Region toCheck; std::list<VNCSConnectionST*>::iterator ci; @@ -834,9 +872,9 @@ void VNCServerST::writeUpdate() toCheck = ui.changed.union_(ui.copied); if (needRenderedCursor()) { - Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height()) - .translate(cursorPos.subtract(cursor->hotspot())) - .intersect(pb->getRect()); + core::Rect clippedCursorRect = core::Rect(0, 0, cursor->width(), cursor->height()) + .translate(cursorPos.subtract(cursor->hotspot())) + .intersect(pb->getRect()); if (!toCheck.intersect(clippedCursorRect).is_empty()) renderedCursorInvalid = true; @@ -864,7 +902,7 @@ void VNCServerST::writeUpdate() // checkUpdate() is called by clients to see if it is safe to read from // the framebuffer at this time. -Region VNCServerST::getPendingRegion() +core::Region VNCServerST::getPendingRegion() { UpdateInfo ui; @@ -876,7 +914,7 @@ Region VNCServerST::getPendingRegion() // Block client from updating if there are pending updates if (comparer->is_empty()) - return Region(); + return {}; comparer->getUpdateInfo(&ui, pb->getRect()); diff --git a/common/rfb/VNCServerST.h b/common/rfb/VNCServerST.h index dc4f9aad..5db4513a 100644 --- a/common/rfb/VNCServerST.h +++ b/common/rfb/VNCServerST.h @@ -26,11 +26,11 @@ #include <sys/time.h> -#include <rfb/SDesktop.h> +#include <core/Timer.h> + #include <rfb/VNCServer.h> #include <rfb/Blacklist.h> #include <rfb/Cursor.h> -#include <rfb/Timer.h> #include <rfb/ScreenSet.h> namespace rfb { @@ -40,9 +40,10 @@ namespace rfb { class ListConnInfo; class PixelBuffer; class KeyRemapper; + class SDesktop; class VNCServerST : public VNCServer, - public Timer::Callback { + public core::Timer::Callback { public: // -=- Constructors @@ -95,11 +96,12 @@ namespace rfb { void closeClients(const char* reason) override {closeClients(reason, nullptr);} SConnection* getConnection(network::Socket* sock) override; - void add_changed(const Region ®ion) override; - void add_copied(const Region &dest, const Point &delta) override; - void setCursor(int width, int height, const Point& hotspot, + void add_changed(const core::Region& region) override; + void add_copied(const core::Region& dest, + const core::Point& delta) override; + void setCursor(int width, int height, const core::Point& hotspot, const uint8_t* data) override; - void setCursorPos(const Point& p, bool warped) override; + void setCursorPos(const core::Point& p, bool warped) override; void setName(const char* name_) override; void setLEDState(unsigned state) override; @@ -111,13 +113,14 @@ namespace rfb { const ScreenSet& getScreenLayout() const { return screenLayout; } const Cursor* getCursor() const { return cursor; } - const Point& getCursorPos() const { return cursorPos; } + const core::Point& getCursorPos() const { return cursorPos; } const char* getName() const { return name.c_str(); } unsigned getLEDState() const { return ledState; } // Event handlers void keyEvent(uint32_t keysym, uint32_t keycode, bool down); - void pointerEvent(VNCSConnectionST* client, const Point& pos, uint16_t buttonMask); + void pointerEvent(VNCSConnectionST* client, + const core::Point& pos, uint16_t buttonMask); void handleClipboardRequest(VNCSConnectionST* client); void handleClipboardAnnounce(VNCSConnectionST* client, bool available); @@ -146,7 +149,7 @@ namespace rfb { // Part of the framebuffer that has been modified but is not yet // ready to be sent to clients - Region getPendingRegion(); + core::Region getPendingRegion(); // getRenderedCursor() returns an up to date version of the server // side rendered cursor buffer @@ -155,7 +158,7 @@ namespace rfb { protected: // Timer callbacks - void handleTimeout(Timer* t) override; + void handleTimeout(core::Timer* t) override; // - Internal methods @@ -195,19 +198,19 @@ namespace rfb { ComparingUpdateTracker* comparer; - Point cursorPos; + core::Point cursorPos; Cursor* cursor; RenderedCursor renderedCursor; bool renderedCursorInvalid; KeyRemapper* keyRemapper; - Timer idleTimer; - Timer disconnectTimer; - Timer connectTimer; + core::Timer idleTimer; + core::Timer disconnectTimer; + core::Timer connectTimer; uint64_t msc, queuedMsc; - Timer frameTimer; + core::Timer frameTimer; }; }; diff --git a/common/rfb/WinPasswdValidator.cxx b/common/rfb/WinPasswdValidator.cxx index 84832e81..a6281950 100644 --- a/common/rfb/WinPasswdValidator.cxx +++ b/common/rfb/WinPasswdValidator.cxx @@ -30,7 +30,8 @@ using namespace rfb; // This method will only work for Windows NT, 2000, and XP (and possibly Vista) bool WinPasswdValidator::validateInternal(rfb::SConnection* /*sc*/, const char* username, - const char* password) + const char* password, + std::string & /* msg */) { HANDLE handle; diff --git a/common/rfb/WinPasswdValidator.h b/common/rfb/WinPasswdValidator.h index 340a6234..993cafea 100644 --- a/common/rfb/WinPasswdValidator.h +++ b/common/rfb/WinPasswdValidator.h @@ -21,6 +21,7 @@ #ifndef __RFB_WINPASSWDVALIDATOR_H__ #define __RFB_WINPASSWDVALIDATOR_H__ +#include <string> #include <rfb/SSecurityPlain.h> namespace rfb @@ -30,7 +31,10 @@ namespace rfb WinPasswdValidator() {}; virtual ~WinPasswdValidator() {}; protected: - bool validateInternal(SConnection *sc, const char* username, const char* password) override; + bool validateInternal(SConnection *sc, + const char *username, + const char *password, + std::string &msg) override; }; } diff --git a/common/rfb/ZRLEDecoder.cxx b/common/rfb/ZRLEDecoder.cxx index 633d1c36..845bf4dc 100644 --- a/common/rfb/ZRLEDecoder.cxx +++ b/common/rfb/ZRLEDecoder.cxx @@ -21,6 +21,8 @@ #include <config.h> #endif +#include <algorithm> + #include <rdr/InStream.h> #include <rdr/MemInStream.h> #include <rdr/OutStream.h> @@ -75,7 +77,7 @@ ZRLEDecoder::~ZRLEDecoder() { } -bool ZRLEDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, +bool ZRLEDecoder::readRect(const core::Rect& /*r*/, rdr::InStream* is, const ServerParams& /*server*/, rdr::OutStream* os) { @@ -99,7 +101,7 @@ bool ZRLEDecoder::readRect(const Rect& /*r*/, rdr::InStream* is, return true; } -void ZRLEDecoder::decodeRect(const Rect& r, const uint8_t* buffer, +void ZRLEDecoder::decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) { @@ -113,13 +115,13 @@ void ZRLEDecoder::decodeRect(const Rect& r, const uint8_t* buffer, } template<class T> -void ZRLEDecoder::zrleDecode(const Rect& r, rdr::InStream* is, +void ZRLEDecoder::zrleDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb) { int length = is->readU32(); zis.setUnderlying(is, length); - Rect t; + core::Rect t; T buf[64 * 64]; Pixel maxPixel = pf.pixelFromRGB((uint16_t)-1, (uint16_t)-1, (uint16_t)-1); @@ -134,11 +136,11 @@ void ZRLEDecoder::zrleDecode(const Rect& r, rdr::InStream* is, for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 64) { - t.br.y = __rfbmin(r.br.y, t.tl.y + 64); + t.br.y = std::min(r.br.y, t.tl.y + 64); for (t.tl.x = r.tl.x; t.tl.x < r.br.x; t.tl.x += 64) { - t.br.x = __rfbmin(r.br.x, t.tl.x + 64); + t.br.x = std::min(r.br.x, t.tl.x + 64); zlibHasData(&zis, 1); int mode = zis.readU8(); diff --git a/common/rfb/ZRLEDecoder.h b/common/rfb/ZRLEDecoder.h index facf0adc..b9ebf771 100644 --- a/common/rfb/ZRLEDecoder.h +++ b/common/rfb/ZRLEDecoder.h @@ -30,16 +30,16 @@ namespace rfb { public: ZRLEDecoder(); virtual ~ZRLEDecoder(); - bool readRect(const Rect& r, rdr::InStream* is, + bool readRect(const core::Rect& r, rdr::InStream* is, const ServerParams& server, rdr::OutStream* os) override; - void decodeRect(const Rect& r, const uint8_t* buffer, + void decodeRect(const core::Rect& r, const uint8_t* buffer, size_t buflen, const ServerParams& server, ModifiablePixelBuffer* pb) override; private: template<class T> - void zrleDecode(const Rect& r, rdr::InStream* is, + void zrleDecode(const core::Rect& r, rdr::InStream* is, const PixelFormat& pf, ModifiablePixelBuffer* pb); private: diff --git a/common/rfb/ZRLEEncoder.cxx b/common/rfb/ZRLEEncoder.cxx index 1e2c6ef4..1908d7e3 100644 --- a/common/rfb/ZRLEEncoder.cxx +++ b/common/rfb/ZRLEEncoder.cxx @@ -21,20 +21,23 @@ #include <config.h> #endif +#include <core/Configuration.h> +#include <core/LogWriter.h> + #include <rdr/OutStream.h> -#include <rfb/Exception.h> #include <rfb/encodings.h> #include <rfb/Palette.h> +#include <rfb/PixelBuffer.h> #include <rfb/SConnection.h> #include <rfb/ZRLEEncoder.h> -#include <rfb/Configuration.h> -#include <rfb/LogWriter.h> using namespace rfb; -static LogWriter vlog("ZRLEEncoder"); +static core::LogWriter vlog("ZRLEEncoder"); -IntParameter zlibLevel("ZlibLevel","[DEPRECATED] Zlib compression level",-1); +core::IntParameter zlibLevel("ZlibLevel", + "[DEPRECATED] Zlib compression level", + -1, -1, -1); ZRLEEncoder::ZRLEEncoder(SConnection* conn_) : Encoder(conn_, encodingZRLE, EncoderPlain, 127), @@ -66,7 +69,7 @@ void ZRLEEncoder::setCompressLevel(int level) void ZRLEEncoder::writeRect(const PixelBuffer* pb, const Palette& palette) { int x, y; - Rect tile; + core::Rect tile; rdr::OutStream* os; @@ -132,7 +135,8 @@ void ZRLEEncoder::writeSolidRect(int width, int height, mos.clear(); } -void ZRLEEncoder::writePaletteTile(const Rect& tile, const PixelBuffer* pb, +void ZRLEEncoder::writePaletteTile(const core::Rect& tile, + const PixelBuffer* pb, const Palette& palette) { const uint8_t* buffer; @@ -158,7 +162,8 @@ void ZRLEEncoder::writePaletteTile(const Rect& tile, const PixelBuffer* pb, } } -void ZRLEEncoder::writePaletteRLETile(const Rect& tile, const PixelBuffer* pb, +void ZRLEEncoder::writePaletteRLETile(const core::Rect& tile, + const PixelBuffer* pb, const Palette& palette) { const uint8_t* buffer; @@ -184,7 +189,8 @@ void ZRLEEncoder::writePaletteRLETile(const Rect& tile, const PixelBuffer* pb, } } -void ZRLEEncoder::writeRawTile(const Rect& tile, const PixelBuffer* pb) +void ZRLEEncoder::writeRawTile(const core::Rect& tile, + const PixelBuffer* pb) { const uint8_t* buffer; int stride; diff --git a/common/rfb/ZRLEEncoder.h b/common/rfb/ZRLEEncoder.h index 87d87e94..3be81ba3 100644 --- a/common/rfb/ZRLEEncoder.h +++ b/common/rfb/ZRLEEncoder.h @@ -40,11 +40,13 @@ namespace rfb { const uint8_t* colour) override; protected: - void writePaletteTile(const Rect& tile, const PixelBuffer* pb, + void writePaletteTile(const core::Rect& tile, + const PixelBuffer* pb, const Palette& palette); - void writePaletteRLETile(const Rect& tile, const PixelBuffer* pb, + void writePaletteRLETile(const core::Rect& tile, + const PixelBuffer* pb, const Palette& palette); - void writeRawTile(const Rect& tile, const PixelBuffer* pb); + void writeRawTile(const core::Rect& tile, const PixelBuffer* pb); void writePalette(const PixelFormat& pf, const Palette& palette); diff --git a/common/rfb/pam.c b/common/rfb/pam.c deleted file mode 100644 index f9e5ce74..00000000 --- a/common/rfb/pam.c +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2006 Martin Koegler - * Copyright (C) 2010 TigerVNC Team - * - * 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 <config.h> -#endif - -#include <stdlib.h> -#include <string.h> -#include <security/pam_appl.h> - -#include <rfb/pam.h> - -typedef struct -{ - const char *username; - const char *password; -} AuthData; - -#if defined(__sun) -static int pam_callback(int count, struct pam_message **in, - struct pam_response **out, void *ptr) -#else -static int pam_callback(int count, const struct pam_message **in, - struct pam_response **out, void *ptr) -#endif -{ - int i; - AuthData *auth = (AuthData *) ptr; - struct pam_response *resp = - (struct pam_response *) malloc (sizeof (struct pam_response) * count); - - if (!resp && count) - return PAM_CONV_ERR; - - for (i = 0; i < count; i++) { - resp[i].resp_retcode = PAM_SUCCESS; - switch (in[i]->msg_style) { - case PAM_TEXT_INFO: - case PAM_ERROR_MSG: - resp[i].resp = 0; - break; - case PAM_PROMPT_ECHO_ON: /* Send Username */ - resp[i].resp = strdup(auth->username); - break; - case PAM_PROMPT_ECHO_OFF: /* Send Password */ - resp[i].resp = strdup(auth->password); - break; - default: - free(resp); - return PAM_CONV_ERR; - } - } - - *out = resp; - return PAM_SUCCESS; -} - - -int do_pam_auth(const char *service, const char *username, const char *password) -{ - int ret; - AuthData auth = { username, password }; - struct pam_conv conv = { - pam_callback, - &auth - }; - pam_handle_t *h = 0; - ret = pam_start(service, username, &conv, &h); - if (ret == PAM_SUCCESS) - ret = pam_authenticate(h, 0); - if (ret == PAM_SUCCESS) - ret = pam_acct_mgmt(h, 0); - pam_end(h, ret); - - return ret == PAM_SUCCESS ? 1 : 0; -} - diff --git a/common/rfb/pam.h b/common/rfb/pam.h deleted file mode 100644 index d378d19c..00000000 --- a/common/rfb/pam.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2006 Martin Koegler - * Copyright (C) 2010 TigerVNC Team - * - * 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. - */ - -#ifndef __RFB_PAM_H__ -#define __RFB_PAM_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -int do_pam_auth(const char *service, const char *username, const char *password); - -#ifdef __cplusplus -} -#endif - -#endif |