Implemented ODS protocol.
authorBjoern Sikora <bjoern.sikora@intra2net.com>
Tue, 15 Sep 2009 15:36:32 +0000 (17:36 +0200)
committerBjoern Sikora <bjoern.sikora@intra2net.com>
Tue, 15 Sep 2009 15:36:32 +0000 (17:36 +0200)
Added net_helper and corresponding classes.

src/ip_service.h [new file with mode: 0644]
src/main.cpp
src/net_helper.cpp [new file with mode: 0644]
src/net_helper.h [new file with mode: 0644]
src/service_ods.cpp
src/service_ods.h
src/tcp_service.cpp [new file with mode: 0644]
src/tcp_service.h [new file with mode: 0644]

diff --git a/src/ip_service.h b/src/ip_service.h
new file mode 100644 (file)
index 0000000..951f3bb
--- /dev/null
@@ -0,0 +1,33 @@
+/** @file
+ * @brief The abstract IPService interface. This class represents all IP services.
+ *
+ *
+ *
+ * @copyright Intra2net AG
+ * @license GPLv2
+*/
+
+#ifndef IPSERVICE_H
+#define IPSERVICE_H
+
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+
+class IPService
+{
+public:
+
+    typedef boost::shared_ptr<IPService> Ptr;
+
+    virtual int connect(const std::string& _hostname, const std::string& _port) = 0;
+
+    virtual std::string read_from_socket() = 0;
+
+    virtual int write_to_socket(const std::string& data) = 0;
+
+    virtual int close() = 0;
+};
+
+#endif
index 6de0f65..47fc99c 100644 (file)
 
 #include "config.cpp"
 #include "logger.cpp"
-#include "service.cpp"
 #include "serviceholder.cpp"
 #include "updater.cpp"
 #include "ip_addr_helper.cpp"
+#include "net_helper.cpp"
+#include "tcp_service.cpp"
 #include "httphelper.cpp"
 #include "serializeservicecontainer.cpp"
 #include "util.cpp"
 
+#include "service.cpp"
 #include "service_dyndns.cpp"
 #include "service_dyns.cpp"
 #include "service_ods.cpp"
@@ -47,6 +49,7 @@
 #include "service_zoneedit.cpp"
 #include "service_gnudip.cpp"
 
+
 using namespace std;
 
 Updater::Ptr updater;
diff --git a/src/net_helper.cpp b/src/net_helper.cpp
new file mode 100644 (file)
index 0000000..3035660
--- /dev/null
@@ -0,0 +1,122 @@
+/** @file
+ * @brief TcpIpHelper class implementation. This class represents a Helper to easily perform tcp/ip operations.
+ *
+ *
+ *
+ * @copyright Intra2net AG
+ * @license GPLv2
+*/
+
+#include "net_helper.h"
+#include "tcp_service.h"
+
+
+using namespace std;
+
+
+/**
+ * Constructor.
+ * @param _log Logger
+ * @param _host Host
+ * @param _port Port
+ */
+NetHelper::NetHelper( const Logger::Ptr _log )
+    : Log(_log)
+{
+    IPService::Ptr _ip_service_ptr(new TCPService());
+    IPServicePtr.swap(_ip_service_ptr);
+}
+
+
+/**
+ * Default destructor
+ */
+NetHelper::~NetHelper()
+{
+}
+
+
+/**
+ * Open the connection to the peer.
+ * @return 0 if all is fine, -1 on error.
+ */
+int NetHelper::open_connection(const string& hostname, const string& port)
+{
+    try
+    {
+        IPServicePtr->connect(hostname,port);
+    }
+    catch ( boost::system::system_error boost_exception )
+    {
+        ostringstream out;
+        out << "NetHelper::open_connection(): " << boost_exception.what() << " Host: " << hostname << " Port: " << port;
+        Log->print_network_error(out.str());
+        return -1;
+    }
+    return 0;
+}
+
+
+/**
+ * Send the given data
+ * @param data Data to send
+ * @return 0 if all is fine, -1 on error.
+ */
+int NetHelper::send_data(const std::string& data)
+{
+    try
+    {
+        IPServicePtr->write_to_socket(data);
+    }
+    catch ( boost::system::system_error boost_exception )
+    {
+        ostringstream out;
+        out << "NetHelper::send_data(): " << boost_exception.what() << " Data to be send: " << data;
+        Log->print_network_error(out.str());
+        return -1;
+    }
+    return 0;
+}
+
+
+/**
+ * Receive all available data from the peer.
+ * @return The data received.
+ */
+std::string NetHelper::receive_data()
+{
+    string received_data;
+    try
+    {
+        received_data = IPServicePtr->read_from_socket();
+    }
+    catch ( boost::system::system_error boost_exception )
+    {
+        ostringstream out;
+        out << "NetHelper::receive_data(): " << boost_exception.what();
+        Log->print_network_error(out.str());
+        return "";
+    }
+    return received_data;
+}
+
+
+/**
+ * Close the active session.
+ * @return 0 if all is fine, -1 on error
+ */
+int NetHelper::close_connection()
+{
+    try
+    {
+        IPServicePtr->close();
+    }
+    catch ( boost::system::system_error boost_exception )
+    {
+        ostringstream out;
+        out << "NetHelper::close_connection(): " << boost_exception.what();
+        Log->print_network_error(out.str());
+        return -1;
+    }
+    return 0;
+}
diff --git a/src/net_helper.h b/src/net_helper.h
new file mode 100644 (file)
index 0000000..7b3254b
--- /dev/null
@@ -0,0 +1,44 @@
+/** @file
+ * @brief NetHelper class header. This class represents a Helper to easily perform tcp/ip operations.
+ *
+ *
+ *
+ * @copyright Intra2net AG
+ * @license GPLv2
+*/
+
+#ifndef NETHELPER_H
+#define NETHELPER_H
+
+#include "logger.h"
+#include "ip_service.h"
+
+#include <boost/shared_ptr.hpp>
+
+
+class NetHelper
+{
+private:
+
+    Logger::Ptr Log;
+    IPService::Ptr IPServicePtr;
+
+public:
+
+    typedef boost::shared_ptr<NetHelper> Ptr;
+
+    NetHelper(const Logger::Ptr _log);
+
+    ~NetHelper();
+
+    int open_connection(const std::string& _host, const std::string& _port);
+
+    int send_data(const std::string& data);
+
+    std::string receive_data();
+
+    int close_connection();
+
+};
+
+#endif
index 8ecffd7..e69abd8 100644 (file)
@@ -8,6 +8,7 @@
 */
 
 #include "service_ods.h"
+#include "net_helper.h"
 
 #include <time.h>
 
@@ -29,6 +30,8 @@ ServiceOds::ServiceOds()
  * @param _password The corresponding password.
  */
 ServiceOds::ServiceOds(const string& _protocol, const string& _hostname, const string& _login, const string& _password, const Logger::Ptr& _logger, const int _update_interval, const int _max_updates_within_interval, const int _dns_cache_ttl)
+    : UpdateServer("update.ods.org")
+    , Port("7071")
 {
     if ( _update_interval == -1 )        // If _update_interval is default po::option_desc (not specified via config)
         set_update_interval(15);              // use default protocol value
@@ -68,6 +71,74 @@ ServiceOds::~ServiceOds()
  */
 int ServiceOds::perform_update(const std::string& ip)
 {
+    // First of all create a boost shared pointer to the NetHelper.
+    NetHelper::Ptr connection(new NetHelper(get_logger()));
+
+    // Then open the connection to the ODS update server.
+    if (connection->open_connection(UpdateServer,Port) != 0)
+        return -1;
+
+    // Receive the server greeting.
+    string server_greeting = connection->receive_data();
+    if ( server_greeting.empty() )
+        return -1;
+
+    // Send login command
+    ostringstream login;
+    login << "LOGIN " << get_login() << " " << get_password() << endl;
+    if (connection->send_data(login.str()))
+        return -1;
+
+    // Receive login reply
+    string login_reply = connection->receive_data();
+    if ( login_reply.empty() )
+        return -1;
+    else
+    {
+        // Get the return code out of the received data
+        string status_code = Util::parse_status_code(login_reply);
+        if ( status_code == "520" )
+        {
+            // Login failed
+            get_logger()->print_service_not_authorized(UpdateServer,get_login(),get_password());
+            connection->close_connection();
+            return -1;
+        }
+        else if ( status_code != "225" )
+        {
+            // Undefined protocol error, Log login_reply
+            get_logger()->print_undefined_protocol_error("ODS",login_reply);
+            connection->close_connection();
+            return -1;
+        }
+    }
+
+    // Successfully loged in, status_code should be 225
+
+    // Send update request
+    ostringstream update_request;
+    update_request << "ADDRR " << get_hostname() << " NS " << ip << endl;
+    if (connection->send_data(update_request.str()))
+        return -1;
+
+    // Receive update request server reply
+    string update_reply = connection->receive_data();
+    if ( server_greeting.empty() )
+        return -1;
+    else
+    {
+        // Get the return code out of the received data
+        string status_code = Util::parse_status_code(login_reply);
+        if ( status_code != "795" )
+        {
+            get_logger()->print_undefined_protocol_error("ODS",update_reply);
+            connection->close_connection();
+            return -1;
+        }
+    }
+
+    // Successfully updated host
+    connection->close_connection();
     return 0;
 }
 
index ba830c4..b832daf 100644 (file)
@@ -28,6 +28,9 @@ private:
     template<class Archive>
     void serialize(Archive & ar, const unsigned int version);
 
+    std::string UpdateServer;
+    std::string Port;
+
 public:
 
     typedef boost::shared_ptr<ServiceOds> Ptr;
diff --git a/src/tcp_service.cpp b/src/tcp_service.cpp
new file mode 100644 (file)
index 0000000..c8d4d7f
--- /dev/null
@@ -0,0 +1,173 @@
+/** @file
+ * @brief TCPService class header. This class represents a TCP client.
+ *
+ *
+ *
+ * @copyright Intra2net AG
+ * @license GPLv2
+*/
+
+#include "tcp_service.h"
+
+typedef boost::shared_ptr<SSLStream> SocketPtr;
+
+using namespace std;
+
+
+/**
+ * Constructor
+ * @param _log Logger object.
+ * @param io_service Service peer information.
+ */
+TCPService::TCPService()
+    : Socket()
+    , IOService()
+{
+}
+
+
+/**
+ * Default destrucotr.
+ */
+TCPService::~TCPService()
+{
+}
+
+
+/**
+ * Initiate a new session with the peer.
+ * @param endpoint_iterator The enpoint iterator.
+ * @return 0 if all is fine, throws boost::syste::system_error if something went wrong.
+ */
+int TCPService::connect(const std::string& _host, const std::string& _port) throw (boost::system::system_error)
+{
+    // Init boost::system::error_code
+    boost::system::error_code error_code;
+
+    // Create service object. The IO_Service has to be a member
+    //boost::asio::io_service io_service;
+
+    // Init DNS query object
+    boost::asio::ip::tcp::resolver resolver(IOService);
+    boost::asio::ip::tcp::resolver::query query(_host, _port);
+    boost::asio::ip::tcp::resolver::iterator end;
+
+    // Perform the DNS query
+    boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, error_code);
+    if ( error_code )
+        throw boost::system::system_error(error_code);
+
+    // Init ssl context for service object
+    boost::asio::ssl::context ctx(IOService, boost::asio::ssl::context::sslv23);
+    ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
+
+    // Initialize the member Socket
+    SocketPtr _socket_ptr(new SSLStream(IOService,ctx));
+    Socket.swap(_socket_ptr);
+
+    // Try to connect the Socket through all available endpoints
+    if (endpoint_iterator != end)
+    {
+        do
+        {
+            Socket->lowest_layer().close();
+            Socket->lowest_layer().connect(*endpoint_iterator++, error_code);
+        } while ( error_code && (endpoint_iterator != end) );
+    }
+    if ( error_code )
+        throw boost::system::system_error(error_code);
+
+    // Perform SSL handshake
+    Socket->handshake(boost::asio::ssl::stream_base::client,error_code);
+    if ( error_code )
+        throw boost::system::system_error(error_code);
+
+    return 0;
+}
+
+
+/**
+ * Will read all available data and return it as a string.
+ * @return The data read from the socket. Throws boost::syste::system_error if something went wrong.
+ */
+std::string TCPService::read_from_socket() throw (boost::system::system_error)
+{
+    // Limit stream buffer to 1MB
+    //unsigned int max_buff_size = 1024*1024;
+    //boost::asio::streambuf stream_buffer(max_buff_size);
+
+    // Do not limit the receive buffer size for reusability. By default, max_size == 18446744073709551615
+    boost::asio::streambuf stream_buffer;
+
+    // Init bytes_read and the error_code
+    unsigned int bytes_read = 0;
+    boost::system::error_code error_code;
+
+    // Is blocking until all available data was read or buffer is full
+    bytes_read = boost::asio::read(*Socket.get(),stream_buffer,boost::asio::transfer_at_least(1),error_code);
+    if ( error_code == boost::asio::error::eof )
+        throw boost::system::system_error(boost::system::error_code(ECONNABORTED,boost::system::system_category),"Connection closed by peer.");
+    else if (error_code)
+        throw boost::system::system_error(error_code);
+    else if ( bytes_read == stream_buffer.max_size() )
+        throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category),"Could not read all available data into streambuf.");
+    else if ( bytes_read == 0 )
+        throw boost::system::system_error(boost::system::error_code(ENODATA,boost::system::system_category),"No data available on socket.");
+
+    // Copy data from stream buffer to a constant buffer and create a string object from the constant buffer
+    boost::asio::streambuf::const_buffers_type const_buffer = stream_buffer.data();
+    std::string data_read(boost::asio::buffers_begin(const_buffer), boost::asio::buffers_begin(const_buffer) + bytes_read);
+
+    return data_read;
+}
+
+
+/**
+ * Writes out given data to the socket.
+ * @param data The data which will be written to the socket.
+ * @return 0 if all is fine, throws boost::syste::system_error if something went wrong.
+ */
+int TCPService::write_to_socket(const string& data) throw (boost::system::system_error)
+{
+    // Get the data size which will be written
+    unsigned int data_size = data.size();
+
+    // Init stream_buffer with data
+    boost::asio::streambuf stream_buffer(data_size);
+    std::ostream request_stream(&stream_buffer);
+    request_stream << data;
+
+    // Init bytes_read and the error_code
+    unsigned int bytes_wrote = 0;
+    boost::system::error_code error_code;
+
+    bytes_wrote = boost::asio::write(*Socket.get(), stream_buffer, boost::asio::transfer_at_least(data_size), error_code);
+    if (error_code)
+        throw boost::system::system_error(error_code);
+    else if ( bytes_wrote != data_size )
+        throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category),"Could not write all data to the socket.");
+
+    return 0;
+}
+
+
+/**
+ * Will close the session.
+ * @return 0 if all is fine, throws boost::syste::system_error if something went wrong.
+ */
+int TCPService::close() throw (boost::system::system_error)
+{
+    boost::system::error_code error_code;
+
+    // Shutdown send and receive channel on socket. This results in sending FIN flag.
+    Socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both,error_code);
+    if ( error_code )
+        throw boost::system::system_error(error_code);
+
+    // Closing the session.
+    Socket->lowest_layer().close( error_code );
+    if ( error_code )
+        throw boost::system::system_error(error_code);
+
+    return 0;
+}
diff --git a/src/tcp_service.h b/src/tcp_service.h
new file mode 100644 (file)
index 0000000..ef5bdd6
--- /dev/null
@@ -0,0 +1,48 @@
+/** @file
+ * @brief TCPService class header. This class represents a TCP client.
+ *
+ *
+ *
+ * @copyright Intra2net AG
+ * @license GPLv2
+*/
+
+#ifndef TCPSERVICE_H
+#define TCPSERVICE_H
+
+#include "logger.h"
+
+#include <boost/shared_ptr.hpp>
+#include <boost/asio.hpp>
+#include <boost/asio/ssl.hpp>
+
+typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLStream;
+
+class TCPService : public IPService
+{
+
+private:
+
+    boost::shared_ptr<SSLStream> Socket;
+    // IOService has to be a member, otherwise the socket.close() operation will throw "mutex: Invalid argument"
+    // Bug in boost::asio ? Cause no docs found concerning this issue. The socket.close() operation is trying to clean up io_service.
+    boost::asio::io_service IOService;
+
+public:
+
+    typedef boost::shared_ptr<TCPService> Ptr;
+
+    TCPService();
+
+    ~TCPService();
+
+    virtual int connect(const std::string& _hostname, const std::string& _port) throw (boost::system::system_error);
+
+    std::string read_from_socket() throw (boost::system::system_error);
+
+    int write_to_socket(const std::string& data) throw (boost::system::system_error);
+
+    int close() throw (boost::system::system_error);
+};
+
+#endif