Fix 'occurred' typo
[bpdyndnsd] / src / tcp_service.cpp
1 /** @file
2  * @brief TCPService class header. This class represents a TCP client.
3  *
4  *
5  *
6  * @copyright Intra2net AG
7  * @license GPLv2
8 */
9
10 #include "tcp_service.hpp"
11
12 typedef boost::shared_ptr<SSLStream> SocketPtr;
13
14 using namespace std;
15
16
17 /**
18  * Constructor
19  * @param _log Logger object.
20  * @param io_service Service peer information.
21  */
22 TCPService::TCPService()
23     : Socket()
24     , IOService()
25 {
26 }
27
28
29 /**
30  * Default destrucotr.
31  */
32 TCPService::~TCPService()
33 {
34 }
35
36
37 /**
38  * Initiate a new session with the peer.
39  * @param endpoint_iterator The enpoint iterator.
40  * @return throws boost::syste::system_error if something went wrong.
41  */
42 void TCPService::connect(const std::string& _host, const std::string& _port)
43 {
44     // Init boost::system::error_code
45     boost::system::error_code err_code;
46
47     // Create service object. The IO_Service has to be a member
48     //boost::asio::io_service io_service;
49
50     // Init DNS query object
51     boost::asio::ip::tcp::resolver service_resolver(IOService);
52     boost::asio::ip::tcp::resolver::query dns_query(_host, _port);
53     boost::asio::ip::tcp::resolver::iterator end_iter;
54
55     // Perform the DNS query
56     boost::asio::ip::tcp::resolver::iterator endpoint_iterator = service_resolver.resolve(dns_query, err_code);
57     if ( err_code )
58         throw boost::system::system_error(err_code);
59
60     // Init ssl context for service object
61     boost::asio::ssl::context ctx(IOService, boost::asio::ssl::context::sslv23);
62     ctx.set_verify_mode(boost::asio::ssl::context::verify_none);
63
64     // Initialize the member Socket
65     Socket = SocketPtr(new SSLStream(IOService,ctx));
66
67     // Try to connect the Socket through all available endpoints
68     if (endpoint_iterator != end_iter)
69     {
70         do
71         {
72             Socket->lowest_layer().close();                                 /*lint !e534 */
73             Socket->lowest_layer().connect(*endpoint_iterator++, err_code); /*lint !e534 */
74         } while ( err_code && (endpoint_iterator != end_iter) );
75     }
76     if ( err_code )
77         throw boost::system::system_error(err_code);
78
79     // Perform SSL handshake
80     Socket->handshake(boost::asio::ssl::stream_base::client,err_code); /*lint !e534*/
81     if ( err_code )
82         throw boost::system::system_error(err_code);
83
84     return;
85 }
86
87
88 /**
89  * Will read all available data and return it as a string.
90  * @return The data read from the socket. Throws boost::syste::system_error if something went wrong.
91  */
92 std::string TCPService::read_from_socket()
93 {
94     // Limit stream buffer to 1MB
95     //unsigned int max_buff_size = 1024*1024;
96     //boost::asio::streambuf stream_buffer(max_buff_size);
97
98     // Do not limit the receive buffer size for reusability. By default, max_size == 18446744073709551615
99     boost::asio::streambuf stream_buffer;
100
101     // Init error_code
102     boost::system::error_code error_code;
103
104     // Is blocking until all available data was read or buffer is full
105     size_t bytes_read = boost::asio::read(*Socket.get(),stream_buffer,boost::asio::transfer_at_least(1),error_code); /*lint !e747 */
106     if ( error_code == boost::asio::error::eof )
107         throw boost::system::system_error(boost::system::error_code(ECONNABORTED,boost::system::system_category()),"Connection closed by peer.");
108     else if (error_code)
109         throw boost::system::system_error(error_code);
110     else if ( bytes_read == stream_buffer.max_size() )
111         throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not read all available data into streambuf.");
112     else if ( bytes_read == 0 )
113         throw boost::system::system_error(boost::system::error_code(ENODATA,boost::system::system_category()),"No data available on socket.");
114
115     // Copy data from stream buffer to a constant buffer and create a string object from the constant buffer
116     boost::asio::streambuf::const_buffers_type const_buffer = stream_buffer.data();
117     std::string data_read(boost::asio::buffers_begin(const_buffer), boost::asio::buffers_begin(const_buffer) + bytes_read); /*lint !e713*/
118
119     return data_read;
120 }
121
122
123 /**
124  * Writes out given data to the socket.
125  * @param data The data which will be written to the socket.
126  * @return Throws boost::syste::system_error if something went wrong.
127  */
128 void TCPService::write_to_socket(const string& data)
129 {
130     // Get the data size which will be written
131     size_t data_size = data.size();
132
133     // Init stream_buffer with data
134     boost::asio::streambuf stream_buffer(data_size);
135     std::ostream request_stream(&stream_buffer);
136     request_stream << data;
137
138     // Init error_code
139     boost::system::error_code error_code;
140
141     size_t bytes_wrote = boost::asio::write(*Socket.get(), stream_buffer, boost::asio::transfer_at_least(data_size), error_code);
142     if (error_code)
143         throw boost::system::system_error(error_code);
144     else if ( bytes_wrote != data_size )
145         throw boost::system::system_error(boost::system::error_code(ENOBUFS,boost::system::system_category()),"Could not write all data to the socket.");
146
147     return;
148 }
149
150
151 /**
152  * Will close the session.
153  * @return 0 if all is fine, throws boost::syste::system_error if something went wrong.
154  */
155 void TCPService::close()
156 {
157     boost::system::error_code error_code;
158
159     // Shutdown send and receive channel on socket. This results in sending FIN flag.
160     Socket->lowest_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both,error_code); /*lint !e534*/
161     if ( error_code )
162         throw boost::system::system_error(error_code);
163
164     // Closing the session.
165     Socket->lowest_layer().close( error_code ); /*lint !e534*/
166     if ( error_code )
167         throw boost::system::system_error(error_code);
168
169     return;
170 }