From 979010de4d3ef0116c49a1e8acb15d1b22bdf479 Mon Sep 17 00:00:00 2001
From: Torsten Dreyer <torsten@t3r.de>
Date: Tue, 8 Nov 2016 10:41:49 +0100
Subject: [PATCH 1/2] Initial commit of mpserver discovery via DNS

---
 src/Main/fg_init.cxx                 |   2 +
 src/MultiPlayer/CMakeLists.txt       |   4 +-
 src/MultiPlayer/MPServerResolver.cxx | 185 +++++++++++++++++++++++++++
 src/MultiPlayer/MPServerResolver.hxx |  83 ++++++++++++
 src/MultiPlayer/multiplaymgr.cxx     |  84 +++++++-----
 src/Network/CMakeLists.txt           |   2 +
 src/Network/DNSClient.cxx            |  71 ++++++++++
 src/Network/DNSClient.hxx            |  52 ++++++++
 8 files changed, 450 insertions(+), 33 deletions(-)
 create mode 100644 src/MultiPlayer/MPServerResolver.cxx
 create mode 100644 src/MultiPlayer/MPServerResolver.hxx
 create mode 100644 src/Network/DNSClient.cxx
 create mode 100644 src/Network/DNSClient.hxx

diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx
index ea9d9b5ef..4072ae9ba 100644
--- a/src/Main/fg_init.cxx
+++ b/src/Main/fg_init.cxx
@@ -131,6 +131,7 @@
 #include <Instrumentation/HUD/HUD.hxx>
 #include <Cockpit/cockpitDisplayManager.hxx>
 #include <Network/HTTPClient.hxx>
+#include <Network/DNSClient.hxx>
 #include <Network/fgcom.hxx>
 #include <Network/http/httpd.hxx>
 #include <Include/version.h>
@@ -735,6 +736,7 @@ void fgCreateSubsystems(bool duringReset) {
     if (!globals->get_subsystem<FGHTTPClient>()) {
         globals->add_new_subsystem<FGHTTPClient>();
     }
+    globals->add_new_subsystem<FGDNSClient>();
 
     ////////////////////////////////////////////////////////////////////
     // Initialize the flight model subsystem.
diff --git a/src/MultiPlayer/CMakeLists.txt b/src/MultiPlayer/CMakeLists.txt
index 1d5c7e992..c2d0f59b5 100644
--- a/src/MultiPlayer/CMakeLists.txt
+++ b/src/MultiPlayer/CMakeLists.txt
@@ -3,11 +3,13 @@ include(FlightGearComponent)
 set(SOURCES
 	multiplaymgr.cxx
 	tiny_xdr.cxx
+        MPServerResolver.cxx
 	)
 
 set(HEADERS
 	multiplaymgr.hxx
 	tiny_xdr.hxx
+        MPServerResolver.hxx
 	)
     	
-flightgear_component(MultiPlayer "${SOURCES}" "${HEADERS}")
\ No newline at end of file
+flightgear_component(MultiPlayer "${SOURCES}" "${HEADERS}")
diff --git a/src/MultiPlayer/MPServerResolver.cxx b/src/MultiPlayer/MPServerResolver.cxx
new file mode 100644
index 000000000..e7cf24c1d
--- /dev/null
+++ b/src/MultiPlayer/MPServerResolver.cxx
@@ -0,0 +1,185 @@
+
+/*
+ MPServerResolver.cxx - mpserver names lookup via DNS
+ Written and copyright by Torsten Dreyer - November 2016
+
+ This file is part of FlightGear.
+
+ FlightGear 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.
+
+ FlightGear 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 FlightGear.  If not, see <http://www.gnu.org/licenses/>.
+ */#include "MPServerResolver.hxx"
+
+#include <Network/DNSClient.hxx>
+#include <Main/fg_props.hxx>
+#include <3rdparty/cjson/cJSON.h>
+
+using namespace simgear;
+
+/**
+ * Build a name=value map from base64 encoded JSON string
+ */
+class MPServerProperties : public std::map<string, string> {
+public:
+  MPServerProperties (string b64)
+  {
+    std::vector<unsigned char> b64dec;
+    simgear::strutils::decodeBase64 (b64, b64dec);
+    auto jsonString = string ((char*) b64dec.data (), b64dec.size ());
+    cJSON * json = ::cJSON_Parse (jsonString.c_str ());
+    if (json) {
+      for (int i = 0; i < ::cJSON_GetArraySize (json); i++) {
+        cJSON * cj = ::cJSON_GetArrayItem (json, i);
+        if (cj->string && cj->valuestring)
+          emplace (cj->string, cj->valuestring);
+      }
+      ::cJSON_Delete (json);
+    } else {
+      SG_LOG(SG_NETWORK,SG_WARN, "MPServerResolver: Can't parse JSON string '" << jsonString << "'" );
+    }
+  }
+};
+
+class MPServerResolver::MPServerResolver_priv {
+public:
+  enum {
+    INIT, LOADING_SRV_RECORDS, LOAD_NEXT_TXT_RECORD, LOADING_TXT_RECORDS, DONE,
+  } _state = INIT;
+
+  FGDNSClient * _dnsClient = globals->get_subsystem<FGDNSClient> ();
+  DNS::Request_ptr _dnsRequest;
+  PropertyList _serverNodes;
+  PropertyList::const_iterator _serverNodes_it;
+};
+
+MPServerResolver::~MPServerResolver ()
+{
+  delete _priv;
+}
+
+MPServerResolver::MPServerResolver () :
+    _priv (new MPServerResolver_priv ())
+{
+}
+
+void
+MPServerResolver::run ()
+{
+  //SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver::run() with state=" << _priv->_state );
+  switch (_priv->_state) {
+    // First call - fire DNS lookup for SRV records
+    case MPServerResolver_priv::INIT:
+      if (!_priv->_dnsClient) {
+        SG_LOG(SG_NETWORK, SG_WARN, "DNS subsystem not available.");
+        onFailure ();
+        return;
+      }
+
+      _priv->_dnsRequest = new DNS::SRVRequest (_dnsName, _service, _protocol);
+      SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
+      _priv->_dnsClient->makeRequest (_priv->_dnsRequest);
+      _priv->_state = MPServerResolver_priv::LOADING_SRV_RECORDS;
+      break;
+
+    // Check if response from SRV Query
+    case MPServerResolver_priv::LOADING_SRV_RECORDS:
+      if (_priv->_dnsRequest->isTimeout ()) {
+        SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
+        onFailure ();
+        return;
+      }
+      if (_priv->_dnsRequest->isComplete ()) {
+        // Create a child node under _targetNode for each SRV entry of the response
+        SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: got DNS response for " << _priv->_dnsRequest->getDn());
+        int idx = 0;
+        for (DNS::SRVRequest::SRV_ptr entry : dynamic_cast<DNS::SRVRequest*> (_priv->_dnsRequest.get ())->entries) {
+          SG_LOG(SG_NETWORK, SG_DEBUG,
+                 "multiplaymgr: SRV " << entry->priority << " " << entry->weight << " " << entry->port << " " << entry->target);
+          SGPropertyNode * serverNode = _targetNode->getNode ("server", idx++, true);
+          serverNode->getNode ("hostname", true)->setStringValue (entry->target);
+          serverNode->getNode ("priority", true)->setIntValue (entry->priority);
+          serverNode->getNode ("weight", true)->setIntValue (entry->weight);
+          serverNode->getNode ("port", true)->setIntValue (entry->port);
+        }
+
+        // prepare an iterator over the server-nodes to be used later when loading the TXT records
+        _priv->_serverNodes = _targetNode->getChildren ("server");
+        _priv->_serverNodes_it = _priv->_serverNodes.begin ();
+        if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
+          // No SRV records found - flag failure
+          SG_LOG(SG_NETWORK, SG_WARN, "multiplaymgr: no multiplayer servers defined via DNS");
+          onFailure ();
+          return;
+        }
+        _priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
+        break;
+      }
+      break;
+
+    // get the next TXT record
+    case MPServerResolver_priv::LOAD_NEXT_TXT_RECORD:
+      if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
+        // we are done with all servers
+        _priv->_state = MPServerResolver_priv::DONE;
+        break;
+      }
+
+      // send the DNS query for the hostnames TXT record
+      _priv->_dnsRequest = new DNS::TXTRequest ((*_priv->_serverNodes_it)->getStringValue ("hostname"));
+      SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: sending DNS request for " << _priv->_dnsRequest->getDn());
+      _priv->_dnsClient->makeRequest (_priv->_dnsRequest);
+      _priv->_state = MPServerResolver_priv::LOADING_TXT_RECORDS;
+      break;
+
+    // check if response for TXT query
+    case MPServerResolver_priv::LOADING_TXT_RECORDS:
+      if (_priv->_dnsRequest->isTimeout ()) {
+        // on timeout, try proceeding with next server
+        SG_LOG(SG_NETWORK, SG_WARN, "Timeout waiting for DNS response. Query was: " << _priv->_dnsRequest->getDn());
+        _priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
+        ++_priv->_serverNodes_it;
+        break;
+      }
+      if (_priv->_dnsRequest->isComplete ()) {
+        SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: got DNS response for " << _priv->_dnsRequest->getDn());
+        // DNS::TXTRequest automatically extracts name=value entries for us, lets retrieve them
+        auto attributes = dynamic_cast<DNS::TXTRequest*> (_priv->_dnsRequest.get ())->attributes;
+        auto mpserverAttribute = attributes["flightgear-mpserver"];
+        if (!mpserverAttribute.empty ()) {
+          // we are only interested in the 'flightgear-mpserver=something' entry, this is a base64 encoded
+          // JSON string, convert this into a map<string,string>
+          MPServerProperties mpserverProperties (mpserverAttribute);
+          for (auto prop : mpserverProperties) {
+            // and store each as a node under our servers node.
+            SG_LOG(SG_NETWORK, SG_DEBUG, "multiplaymgr: TXT record attribute " << prop.first << "=" << prop.second);
+            (*_priv->_serverNodes_it)->setStringValue (prop.first, prop.second);
+          }
+        } else {
+          SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: TXT record attributes empty");
+        }
+
+        // procede with the net node
+        ++_priv->_serverNodes_it;
+        _priv->_state = MPServerResolver_priv::LOAD_NEXT_TXT_RECORD;
+        break;
+      }
+      break;
+
+    case MPServerResolver_priv::DONE:
+      onSuccess ();
+      return;
+  }
+
+  // Relinguish control, call me back on the next frame
+  globals->get_event_mgr ()->addEvent ("MPServerResolver_update", this, &MPServerResolver::run, .0);
+}
+
diff --git a/src/MultiPlayer/MPServerResolver.hxx b/src/MultiPlayer/MPServerResolver.hxx
new file mode 100644
index 000000000..942426dba
--- /dev/null
+++ b/src/MultiPlayer/MPServerResolver.hxx
@@ -0,0 +1,83 @@
+/*
+ MPServerResolver.hxx - mpserver names lookup via DNS
+ Written and copyright by Torsten Dreyer - November 2016
+
+ This file is part of FlightGear.
+
+ FlightGear 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.
+
+ FlightGear 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 FlightGear.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __FG_MPSERVERRESOLVER_HXX
+#define __FG_MPSERVERRESOLVER_HXX
+
+#include <string>
+#include <simgear/props/props.hxx>
+
+class MPServerResolver {
+public:
+  MPServerResolver();
+  virtual ~MPServerResolver();
+  void run();
+
+  /**
+   * Set the target property where the server-list gets stored
+   *
+   * \param value the property node to use as a target
+   */
+  void setTarget( SGPropertyNode_ptr value ) { _targetNode = value; }
+
+  /**
+   * Set the dns domain name to query. This could be either a full qualified name including the
+   * service and the protocol like _fgms._udp.flightgear.org or just the domain name like
+   * flightgear.org. Use setService() and setProtocol() in the latter case.
+   *
+   * \param value the dnsname to use for the query.
+   */
+  void setDnsName( const std::string & value ) { _dnsName = value; }
+
+  /** Set the service name to use for the query. Don't add the underscore, this gets added
+   * automatically. This builds the fully qualified DNS name to query, together with
+   * setProtocol() and setDnsName().
+   *
+   * \param value the service name to use for the query sans the leading underscore
+   */
+  void setService( const std::string & value ) { _service = value; }
+
+  /** Set the protocol name to use for the query. Don't add the underscore, this gets added
+    * automatically. This builds the fully qualified DNS name to query, together with
+    * setService() and setDnsName().
+    *
+    * \param value the protocol name to use for the query sans the leading underscore
+    */
+  void setProtocol( const std::string & value ) { _protocol = value; }
+
+  /** Handler to be called if the resolver process finishes with success. Does nothing by
+   * default and should be overridden be the user.
+   */
+  virtual void onSuccess() {};
+
+  /** Handler to be called if the resolver process terminates with an error. Does nothing by
+    * default and should be overridden be the user.
+    */
+  virtual void onFailure() {};
+
+private:
+  class MPServerResolver_priv;
+  std::string _dnsName;
+  std::string _service;
+  std::string _protocol;
+  SGPropertyNode_ptr _targetNode;
+  MPServerResolver_priv * _priv;
+};
+
+#endif // __FG_MPSERVERRESOLVER_HXX
diff --git a/src/MultiPlayer/multiplaymgr.cxx b/src/MultiPlayer/multiplaymgr.cxx
index b78c452a0..64cf5ec5f 100644
--- a/src/MultiPlayer/multiplaymgr.cxx
+++ b/src/MultiPlayer/multiplaymgr.cxx
@@ -41,14 +41,14 @@
 #include <simgear/debug/logstream.hxx>
 #include <simgear/props/props.hxx>
 #include <simgear/structure/commands.hxx>
+#include <simgear/structure/event_mgr.hxx>
 
 #include <AIModel/AIManager.hxx>
 #include <AIModel/AIMultiplayer.hxx>
 #include <Main/fg_props.hxx>
-#include <Network/RemoteXMLRequest.hxx>
-#include <Network/HTTPClient.hxx>
 #include "multiplaymgr.hxx"
 #include "mpmessages.hxx"
+#include "MPServerResolver.hxx"
 #include <FDM/flightProperties.hxx>
 
 using namespace std;
@@ -428,40 +428,60 @@ static bool do_multiplayer_disconnect(const SGPropertyNode * arg) {
 //  none
 //
 //////////////////////////////////////////////////////////////////////
-static bool do_multiplayer_refreshserverlist(const SGPropertyNode * arg) {
-	FGMultiplayMgr * self = (FGMultiplayMgr*) globals->get_subsystem("mp");
-	if (!self) {
-		SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
-		return false;
-	}
 
-    FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
-	if (!http) {
-		SG_LOG(SG_IO, SG_ALERT,
-				"do_multiplayer.refreshserverlist: HTTP client not running");
-		return false;
-	}
+static bool
+do_multiplayer_refreshserverlist (const SGPropertyNode * arg)
+{
+  using namespace simgear;
 
-	string url(
-			fgGetString("/sim/multiplay/serverlist-url",
-					"http://liveries.flightgear.org/mpstatus/mpservers.xml"));
+  FGMultiplayMgr * self = (FGMultiplayMgr*) globals->get_subsystem ("mp");
+  if (!self) {
+    SG_LOG(SG_NETWORK, SG_WARN, "Multiplayer subsystem not available.");
+    return false;
+  }
 
-	if (url.empty()) {
-		SG_LOG(SG_IO, SG_ALERT,
-				"do_multiplayer.refreshserverlist: no URL given");
-		return false;
-	}
+  // MPServerResolver implementation to fill the mp server list
+  // deletes itself when done
+  class MyMPServerResolver : public MPServerResolver {
+  public:
+    MyMPServerResolver () :
+        MPServerResolver ()
+    {
+      setTarget (fgGetNode ("/sim/multiplay/server-list", true));
+      setDnsName (fgGetString ("/sim/multiplay/dns/query-dn", "flightgear.org"));
+      setService (fgGetString ("/sim/multiplay/dns/query-srv-service", "fgms"));
+      setProtocol (fgGetString ("/sim/multiplay/dns/query-srv-protocol", "udp"));
+      _completeNode->setBoolValue (false);
+      _failureNode->setBoolValue (false);
+    }
 
-	SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
-	SGPropertyNode *completeNode = fgGetNode("/sim/multiplay/got-servers", true);
-	SGPropertyNode *failureNode = fgGetNode("/sim/multiplay/get-servers-failure", true);
-	RemoteXMLRequest* req = new RemoteXMLRequest(url, targetnode);
-	req->setCompletionProp(completeNode);
-	req->setFailedProp(failureNode);
-	completeNode->setBoolValue(false);
-	failureNode->setBoolValue(false);
-	http->makeRequest(req);
-	return true;
+    ~MyMPServerResolver ()
+    {
+    }
+
+    virtual void
+    onSuccess ()
+    {
+      SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger success");
+      _completeNode->setBoolValue (true);
+      delete this;
+    }
+    virtual void
+    onFailure ()
+    {
+      SG_LOG(SG_NETWORK, SG_DEBUG, "MyMPServerResolver: trigger failure");
+      _failureNode->setBoolValue (true);
+      delete this;
+    }
+
+  private:
+    SGPropertyNode *_completeNode = fgGetNode ("/sim/multiplay/got-servers", true);
+    SGPropertyNode *_failureNode = fgGetNode ("/sim/multiplay/get-servers-failure", true);
+  };
+
+  MyMPServerResolver * mpServerResolver = new MyMPServerResolver ();
+  mpServerResolver->run ();
+  return true;
 }
 
 //////////////////////////////////////////////////////////////////////
diff --git a/src/Network/CMakeLists.txt b/src/Network/CMakeLists.txt
index 3a53cbfd7..f36cfafe5 100644
--- a/src/Network/CMakeLists.txt
+++ b/src/Network/CMakeLists.txt
@@ -11,6 +11,7 @@ set(SOURCES
 	garmin.cxx
 	generic.cxx
 	HTTPClient.cxx
+	DNSClient.cxx
 	igc.cxx
 	joyclient.cxx
 	jsclient.cxx
@@ -39,6 +40,7 @@ set(HEADERS
 	garmin.hxx
 	generic.hxx
 	HTTPClient.hxx
+	DNSClient.hxx
 	igc.hxx
 	joyclient.hxx
 	jsclient.hxx
diff --git a/src/Network/DNSClient.cxx b/src/Network/DNSClient.cxx
new file mode 100644
index 000000000..fff953a20
--- /dev/null
+++ b/src/Network/DNSClient.cxx
@@ -0,0 +1,71 @@
+// HDNSClient.cxx -- Singleton DNS client object
+//
+// Written by James Turner, started April 2012.
+// Based on HTTPClient from James Turner
+// Copyright (C) 2012  James Turner
+//
+// This program 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 program 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 program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "DNSClient.hxx"
+
+#include <cassert>
+
+#include <Main/fg_props.hxx>
+#include <Include/version.h>
+
+#include <simgear/sg_inlines.h>
+
+
+using namespace simgear;
+
+FGDNSClient::FGDNSClient() :
+    _inited(false)
+{
+}
+
+FGDNSClient::~FGDNSClient()
+{
+}
+
+void FGDNSClient::init()
+{
+    if (_inited) {
+        return;
+    }
+
+    _dns.reset( new simgear::DNS::Client() );
+
+    _inited = true;
+}
+
+void FGDNSClient::postinit()
+{
+}
+
+void FGDNSClient::shutdown()
+{
+    _dns.reset();
+    _inited = false;
+}
+
+void FGDNSClient::update(double)
+{
+  _dns->update();
+}
+
+void FGDNSClient::makeRequest(const simgear::DNS::Request_ptr& req)
+{
+  _dns->makeRequest(req);
+}
diff --git a/src/Network/DNSClient.hxx b/src/Network/DNSClient.hxx
new file mode 100644
index 000000000..902d781a3
--- /dev/null
+++ b/src/Network/DNSClient.hxx
@@ -0,0 +1,52 @@
+// DNSClient.hxx -- Singleton DNS client object
+//
+// Written by Torsten Dreyer, started November 2016.
+// Based on HTTPClient from James Turner
+// Copyright (C) 2012  James Turner
+//
+// This program 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 program 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 program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef FG_DNS_CLIENT_HXX
+#define FG_DNS_CLIENT_HXX
+
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/io/DNSClient.hxx>
+#include <memory>
+
+class FGDNSClient : public SGSubsystem
+{
+public:
+    FGDNSClient();
+    virtual ~FGDNSClient();
+    
+    void makeRequest(const simgear::DNS::Request_ptr& req);
+
+//    simgear::HTTP::Client* client() { return _http.get(); }
+//    simgear::HTTP::Client const* client() const { return _http.get(); }
+    
+    virtual void init();
+    virtual void postinit();
+    virtual void shutdown();
+    virtual void update(double);
+
+    static const char* subsystemName() { return "dns"; }
+private:    
+    bool _inited;
+    std::auto_ptr<simgear::DNS::Client> _dns;
+};
+
+#endif // FG_DNS_CLIENT_HXX
+
+

From 09949ff3e3d94b4e122f95133f2032dad1c3e0da Mon Sep 17 00:00:00 2001
From: Torsten Dreyer <torsten@t3r.de>
Date: Mon, 14 Nov 2016 21:57:50 +0100
Subject: [PATCH 2/2] Some logging cleanup and ignore SRV records with port #0

---
 src/MultiPlayer/MPServerResolver.cxx | 27 ++++++++++++++++++---------
 src/Network/DNSClient.hxx            |  2 +-
 2 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/src/MultiPlayer/MPServerResolver.cxx b/src/MultiPlayer/MPServerResolver.cxx
index e7cf24c1d..dae17975d 100644
--- a/src/MultiPlayer/MPServerResolver.cxx
+++ b/src/MultiPlayer/MPServerResolver.cxx
@@ -22,6 +22,7 @@
 #include <Network/DNSClient.hxx>
 #include <Main/fg_props.hxx>
 #include <3rdparty/cjson/cJSON.h>
+#include <cstdlib>
 
 using namespace simgear;
 
@@ -79,7 +80,7 @@ MPServerResolver::run ()
     // First call - fire DNS lookup for SRV records
     case MPServerResolver_priv::INIT:
       if (!_priv->_dnsClient) {
-        SG_LOG(SG_NETWORK, SG_WARN, "DNS subsystem not available.");
+        SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: DNS subsystem not available.");
         onFailure ();
         return;
       }
@@ -99,11 +100,15 @@ MPServerResolver::run ()
       }
       if (_priv->_dnsRequest->isComplete ()) {
         // Create a child node under _targetNode for each SRV entry of the response
-        SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: got DNS response for " << _priv->_dnsRequest->getDn());
+        SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
         int idx = 0;
         for (DNS::SRVRequest::SRV_ptr entry : dynamic_cast<DNS::SRVRequest*> (_priv->_dnsRequest.get ())->entries) {
           SG_LOG(SG_NETWORK, SG_DEBUG,
-                 "multiplaymgr: SRV " << entry->priority << " " << entry->weight << " " << entry->port << " " << entry->target);
+                 "MPServerResolver: SRV " << entry->priority << " " << entry->weight << " " << entry->port << " " << entry->target);
+          if( 0 == entry->port ) {
+            SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: Skipping offline host " << entry->target );
+            continue;
+          }
           SGPropertyNode * serverNode = _targetNode->getNode ("server", idx++, true);
           serverNode->getNode ("hostname", true)->setStringValue (entry->target);
           serverNode->getNode ("priority", true)->setIntValue (entry->priority);
@@ -116,7 +121,7 @@ MPServerResolver::run ()
         _priv->_serverNodes_it = _priv->_serverNodes.begin ();
         if (_priv->_serverNodes_it == _priv->_serverNodes.end ()) {
           // No SRV records found - flag failure
-          SG_LOG(SG_NETWORK, SG_WARN, "multiplaymgr: no multiplayer servers defined via DNS");
+          SG_LOG(SG_NETWORK, SG_WARN, "MPServerResolver: no multiplayer servers defined via DNS");
           onFailure ();
           return;
         }
@@ -135,7 +140,7 @@ MPServerResolver::run ()
 
       // send the DNS query for the hostnames TXT record
       _priv->_dnsRequest = new DNS::TXTRequest ((*_priv->_serverNodes_it)->getStringValue ("hostname"));
-      SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: sending DNS request for " << _priv->_dnsRequest->getDn());
+      SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: sending DNS request for " << _priv->_dnsRequest->getDn());
       _priv->_dnsClient->makeRequest (_priv->_dnsRequest);
       _priv->_state = MPServerResolver_priv::LOADING_TXT_RECORDS;
       break;
@@ -150,7 +155,7 @@ MPServerResolver::run ()
         break;
       }
       if (_priv->_dnsRequest->isComplete ()) {
-        SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: got DNS response for " << _priv->_dnsRequest->getDn());
+        SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: got DNS response for " << _priv->_dnsRequest->getDn());
         // DNS::TXTRequest automatically extracts name=value entries for us, lets retrieve them
         auto attributes = dynamic_cast<DNS::TXTRequest*> (_priv->_dnsRequest.get ())->attributes;
         auto mpserverAttribute = attributes["flightgear-mpserver"];
@@ -160,11 +165,15 @@ MPServerResolver::run ()
           MPServerProperties mpserverProperties (mpserverAttribute);
           for (auto prop : mpserverProperties) {
             // and store each as a node under our servers node.
-            SG_LOG(SG_NETWORK, SG_DEBUG, "multiplaymgr: TXT record attribute " << prop.first << "=" << prop.second);
-            (*_priv->_serverNodes_it)->setStringValue (prop.first, prop.second);
+            SG_LOG(SG_NETWORK, SG_DEBUG, "MPServerResolver: TXT record attribute " << prop.first << "=" << prop.second);
+            // sanitize property name, don't allow dots or forward slash
+            auto propertyName = prop.first;
+            std::replace( propertyName.begin(), propertyName.end(), '.', '_');
+            std::replace( propertyName.begin(), propertyName.end(), '/', '_');
+            (*_priv->_serverNodes_it)->setStringValue (propertyName, prop.second);
           }
         } else {
-          SG_LOG(SG_NETWORK, SG_INFO, "multiplaymgr: TXT record attributes empty");
+          SG_LOG(SG_NETWORK, SG_INFO, "MPServerResolver: TXT record attributes empty");
         }
 
         // procede with the net node
diff --git a/src/Network/DNSClient.hxx b/src/Network/DNSClient.hxx
index 902d781a3..cdccb4e79 100644
--- a/src/Network/DNSClient.hxx
+++ b/src/Network/DNSClient.hxx
@@ -44,7 +44,7 @@ public:
     static const char* subsystemName() { return "dns"; }
 private:    
     bool _inited;
-    std::auto_ptr<simgear::DNS::Client> _dns;
+    std::unique_ptr<simgear::DNS::Client> _dns;
 };
 
 #endif // FG_DNS_CLIENT_HXX