From: Bjoern Sikora Date: Tue, 15 Sep 2009 15:36:32 +0000 (+0200) Subject: Implemented ODS protocol. X-Git-Tag: v1.1~185 X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=6650af146049bd62742390b1a120960661ba5655;p=bpdyndnsd Implemented ODS protocol. Added net_helper and corresponding classes. --- diff --git a/src/ip_service.h b/src/ip_service.h new file mode 100644 index 0000000..951f3bb --- /dev/null +++ b/src/ip_service.h @@ -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 + +#include + + +class IPService +{ +public: + + typedef boost::shared_ptr 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 diff --git a/src/main.cpp b/src/main.cpp index 6de0f65..47fc99c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -30,14 +30,16 @@ #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 index 0000000..3035660 --- /dev/null +++ b/src/net_helper.cpp @@ -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 index 0000000..7b3254b --- /dev/null +++ b/src/net_helper.h @@ -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 + + +class NetHelper +{ +private: + + Logger::Ptr Log; + IPService::Ptr IPServicePtr; + +public: + + typedef boost::shared_ptr 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 diff --git a/src/service_ods.cpp b/src/service_ods.cpp index 8ecffd7..e69abd8 100644 --- a/src/service_ods.cpp +++ b/src/service_ods.cpp @@ -8,6 +8,7 @@ */ #include "service_ods.h" +#include "net_helper.h" #include @@ -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; } diff --git a/src/service_ods.h b/src/service_ods.h index ba830c4..b832daf 100644 --- a/src/service_ods.h +++ b/src/service_ods.h @@ -28,6 +28,9 @@ private: template void serialize(Archive & ar, const unsigned int version); + std::string UpdateServer; + std::string Port; + public: typedef boost::shared_ptr Ptr; diff --git a/src/tcp_service.cpp b/src/tcp_service.cpp new file mode 100644 index 0000000..c8d4d7f --- /dev/null +++ b/src/tcp_service.cpp @@ -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 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 index 0000000..ef5bdd6 --- /dev/null +++ b/src/tcp_service.h @@ -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 +#include +#include + +typedef boost::asio::ssl::stream SSLStream; + +class TCPService : public IPService +{ + +private: + + boost::shared_ptr 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 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