Replace socket_handler::fill_buffer() recursion with loop (#8389)
[libt2n] / src / socket_handler.cpp
1 /*
2 Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22
23 #include <stdio.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <sys/time.h>
31 #include <arpa/inet.h>
32 #include <netinet/in.h>
33 #include <netdb.h>
34 #include <fcntl.h>
35 #include <time.h>
36 #include <pwd.h>
37 #include <grp.h>
38
39 #include <sstream>
40 #include <iostream>
41 #include <algorithm>
42
43 #include "socket_handler.hxx"
44 #include "t2n_exception.hxx"
45 #include "log.hxx"
46
47 using namespace std;
48
49 namespace libt2n
50 {
51
52 socket_handler::socket_handler(int _sock, socket_type_value _socket_type)
53 : sock(_sock)
54 , recv_buffer_size( default_recv_buffer_size )
55 , write_block_size( default_write_block_size )
56 , write_timeout( default_write_timeout )
57 , socket_type(_socket_type)
58 {
59 }
60
61 /**
62  * Destructor. Closes open socket
63  */
64 socket_handler::~socket_handler()
65 {
66     if (sock != -1)
67     {
68         shutdown(sock,SHUT_RDWR);
69         ::close(sock);
70
71         sock = -1;
72     }
73 }
74
75 /// close the underlying socket connection. Don't call directly, use the version provided
76 /// by the connection class you are using.
77 void socket_handler::close()
78 {
79     LOGSTREAM(debug,"close connection");
80     // graceful shutdown
81     shutdown(sock,SHUT_RDWR);
82     ::close(sock);
83
84     sock = -1;
85 }
86
87 /// set options like fast reuse and keepalive every socket should have
88 void socket_handler::set_socket_options(int sock)
89 {
90     int i=1;
91
92     /* fast reuse enable */
93     if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
94         EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
95
96     /* keepalive enable */
97     if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
98         EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
99
100     /* close on exec */
101     int fdflags;
102     fdflags=fcntl(sock,F_GETFD, 0);
103     if (fdflags < 0)
104         EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
105
106     fdflags |= FD_CLOEXEC;
107     if (fcntl(sock,F_SETFD,fdflags) < 0)
108         EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
109
110     /* non-blocking mode */
111     int flflags;
112     flflags=fcntl(sock,F_GETFL,0);
113     if (flflags < 0)
114         EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
115
116     flflags |= O_NONBLOCK;
117     if (fcntl(sock,F_SETFL,flflags) < 0)
118         EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
119 }
120
121 /// is the underlying socket connection still open?
122 bool socket_handler::is_closed()
123 {
124     int r=fcntl(sock,F_GETFL);
125
126     return !(r & O_ACCMODE);
127 }
128
129
130 /**
131  * @brief set a new size for the receive buffer.
132  * @param new_recv_buffer_size the new size for the receive buffer.
133  *
134  * The receive buffer determines the amount of data which is tried to read at once
135  * from the underlying socket.
136  *
137  * The value is normalized to be at least 512 bytes and at max 32K bytes.
138  */
139 void socket_handler::set_recv_buffer_size(unsigned int new_recv_buffer_size)
140 {
141     recv_buffer_size= std::max( 512u, std::min( 32u * 1024u, new_recv_buffer_size ));
142 } //
143
144
145 /**
146  * @brief set new size for the data chunks when writeing.
147  * @param new_write_block_size the new chunk size.
148  *
149  * The write block size determines the amount of data which is tried to write
150  * to the socket when data needs to be sended.
151  * Since writing data is done in a loop, this does not limit the amunt of data which can
152  * be written.
153  *
154  * The value is normalized to be at least 512 bytes and at max 32K bytes.
155  */
156 void socket_handler::set_write_block_size(unsigned int new_write_block_size)
157 {
158     write_block_size= std::max( 512u, std::min( 32u * 1024u, new_write_block_size ));
159 } //
160
161
162 /**
163  * @brief set new timeout for writing a block
164  * @param new_write_timeout the new timeout in usecs, -1: wait endless
165  *
166  * The write timeout determines the maximum amount of time that is waited
167  * between writing each block. If the timeout is exceeded, write will
168  * throw t2n_transfer_error
169  */
170 void socket_handler::set_write_timeout(long long new_write_timeout)
171 {
172     write_block_size=new_write_timeout;
173 } //
174
175
176 /** @brief check if new data is waiting on the raw socket
177     @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
178             -1: wait endless
179             0: return instantly
180     @param[out] usec_timeout_remaining microseconds from the timeout that were not used
181 */
182 bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout_remaining)
183 {
184     // just our socket
185     fd_set active_fd_set;
186     FD_ZERO (&active_fd_set);
187     FD_SET (sock, &active_fd_set);
188
189     /* set timeout */
190     struct timeval tval;
191     struct timeval *timeout_ptr;
192
193     if (usec_timeout == -1)
194         timeout_ptr = NULL;
195     else
196     {
197         timeout_ptr = &tval;
198
199         // convert timeout from long long usec to int sec + int usec
200         tval.tv_sec = usec_timeout / 1000000;
201         tval.tv_usec = usec_timeout % 1000000;
202     }
203
204     int ret=select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr);
205
206     // return the timeout we did not use
207     // todo: this is linux specific according to man 2 select
208     if (usec_timeout > 0 && usec_timeout_remaining != NULL)
209         *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
210
211     if (ret > 0)
212         return true;
213     else
214         return false;
215 }
216
217 /** @brief read data from the raw socket and copy it into the provided buffer
218     @param buffer the buffer where to append the new data
219     @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
220             -1: wait endless
221             0: return instantly
222     @param[out] usec_timeout_remaining microseconds from the timeout that were not used
223 */
224 bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *usec_timeout_remaining)
225 {
226     // fast path for timeout==0
227     if (usec_timeout==0 || data_waiting(usec_timeout,usec_timeout_remaining))
228         return fill_buffer(buffer);
229     else
230         return false;
231 }
232
233 /** @brief read data from the raw socket and copy it into the provided buffer. Returns
234            instantly if no data is waiting.
235     @param buffer the buffer where to append the new data
236 */
237 bool socket_handler::fill_buffer(std::string& buffer)
238 {
239     char socket_buffer[recv_buffer_size];
240
241     const int loop_max = 32;            /* limit is 32 * (default) 2048 bytes -> 65536 bytes in one go */
242     int loop_count = 0;
243
244     bool read_something = false;
245     while (loop_count < loop_max)
246     {
247         const int nbytes = read(sock, socket_buffer, recv_buffer_size);
248
249         if (nbytes < 0)
250         {
251             if (errno == EAGAIN)
252                 return read_something;                // no (more) data was waiting
253             else if (errno == EINTR)
254             {
255                 // interrupted, try again
256                 LOGSTREAM(debug, "EINTR received on read(), trying again");
257             } else
258                 EXCEPTIONSTREAM(error, t2n_transfer_error, "error reading from socket : " << strerror(errno));
259         }
260
261         // End-of-file
262         if (nbytes == 0)
263         {
264             LOGSTREAM(debug, "0 bytes received on read(), closing connection");
265             close();
266             return read_something;
267         }
268
269         // Data read -> store it
270         if (nbytes > 0)
271         {
272             buffer.append(socket_buffer, nbytes);
273             LOGSTREAM(debug, nbytes << " bytes read");
274             read_something = true;
275         }
276
277         // more data waiting -> loop once more (up to loop_max)
278         if (data_waiting(0))
279         {
280             ++loop_count;
281         } else
282         {
283             break;
284         }
285     }
286
287     return read_something;
288 }
289
290 /// writes raw data to the socket. Don't use directly, use the write() function provided by the 
291 /// connection because it encapsulates the data.
292 void socket_handler::socket_write(const std::string& data)
293 {
294     unsigned int offset = 0;
295     while (offset < data.size())
296     {
297         unsigned int write_size=write_block_size;
298
299         if (offset+write_size > data.size())
300             write_size = data.size()-offset;
301
302         int rtn;
303         while ((rtn=::write(sock, data.data()+offset, write_size)) == -1 &&
304                (errno == EAGAIN || errno == EINTR))
305         {
306             wait_ready_to_write(sock,write_timeout);
307             LOGSTREAM(debug,"resuming write() call after EAGAIN or EINTR");
308         }
309
310         if (rtn == -1)
311             EXCEPTIONSTREAM(error,t2n_transfer_error,"write() returned " << strerror(errno));
312         else if (rtn != write_size)
313         {
314             LOGSTREAM(debug,"write() wrote " << rtn << " bytes, should have been "
315                 << write_size << " (complete: " << data.size() << ")");
316
317             write_size = rtn;
318         }
319
320         offset += write_size;
321     }
322
323     LOGSTREAM(debug,"wrote " << data.size() << " bytes");
324
325     return;
326 } // eo socket_handler::socket_write(const std::string&)
327
328 /// wait until the socket is ready to write again
329 void socket_handler::wait_ready_to_write(int socket, long long write_block_timeout)
330 {
331     // prepare socket sets
332     fd_set write_set[1];
333     fd_set except_set[1];
334     FD_ZERO(write_set);
335     FD_ZERO(except_set);
336     FD_SET(socket, write_set);
337     FD_SET(socket, except_set);
338
339     // prepare timeout struct
340     struct timeval tval;
341     struct timeval *timeout_ptr;
342
343     if (write_block_timeout == -1)
344         timeout_ptr = NULL;
345     else
346     {
347         timeout_ptr = &tval;
348
349         // convert timeout from long long usec to int sec + int usec
350         tval.tv_sec = write_block_timeout / 1000000;
351         tval.tv_usec = write_block_timeout % 1000000;
352     }
353
354     // let's wait for the socket to become writable again...
355     int rtn;
356     while ((rtn=::select(socket+1, NULL, write_set, except_set, timeout_ptr)) ==-1 && errno == EINTR);
357
358     if (rtn > 0 && (!FD_ISSET(socket,write_set)) && FD_ISSET(socket, except_set))
359     {
360         // if we are selected but cannot write and have an exception
361         // we have serious trouble...
362         EXCEPTIONSTREAM(error,t2n_transfer_error,"exception on socket; cannot write any more.");
363     }
364
365     if (rtn==0)
366         EXCEPTIONSTREAM(error,t2n_transfer_error,"timeout on select() for write");
367
368     if (rtn==-1)
369         EXCEPTIONSTREAM(error,t2n_transfer_error,"cannot select() for write: " << strerror(errno));
370 }
371
372
373 }