Adding DOCUMENTATION flag to the CMakeLists.txt
[libt2n] / src / socket_handler.cpp
CommitLineData
19facd85
TJ
1/*
2Copyright (C) 2006 by Intra2net AG - Gerd v. Egidy
3
4The software in this package is distributed under the GNU General
5Public License version 2 (with a special exception described below).
6
7A copy of GNU General Public License (GPL) is included in this distribution,
8in the file COPYING.GPL.
9
10As a special exception, if other files instantiate templates or use macros
11or inline functions from this file, or you compile this file and link it
12with other works to produce a work based on this file, this file
13does not by itself cause the resulting work to be covered
14by the GNU General Public License.
15
16However the source code for this file must still be made available
17in accordance with section (3) of the GNU General Public License.
18
19This exception does not invalidate any other reasons why a work based
20on this file might be covered by the GNU General Public License.
21*/
a11e19b7
GE
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>
644c4d26 40#include <iostream>
517d1214 41#include <algorithm>
a11e19b7
GE
42
43#include "socket_handler.hxx"
44#include "t2n_exception.hxx"
a7170401 45#include "log.hxx"
a11e19b7
GE
46
47using namespace std;
48
49namespace libt2n
50{
51
517d1214
RP
52socket_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 )
c7857475 56, write_timeout( default_write_timeout )
517d1214
RP
57, socket_type(_socket_type)
58{
59}
60
56f3994d
TJ
61/**
62 * Destructor. Closes open socket
63 */
64socket_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.
77void 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}
517d1214 86
94247295 87/// set options like fast reuse and keepalive every socket should have
a11e19b7
GE
88void 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)
a7170401 94 EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
a11e19b7
GE
95
96 /* keepalive enable */
97 if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
a7170401 98 EXCEPTIONSTREAM(error,t2n_communication_error,"error setting socket option: " << strerror(errno));
a11e19b7
GE
99
100 /* close on exec */
101 int fdflags;
102 fdflags=fcntl(sock,F_GETFD, 0);
103 if (fdflags < 0)
a7170401
GE
104 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
105
a11e19b7
GE
106 fdflags |= FD_CLOEXEC;
107 if (fcntl(sock,F_SETFD,fdflags) < 0)
a7170401 108 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
a11e19b7
GE
109
110 /* non-blocking mode */
111 int flflags;
112 flflags=fcntl(sock,F_GETFL,0);
113 if (flflags < 0)
a7170401
GE
114 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
115
a11e19b7
GE
116 flflags |= O_NONBLOCK;
117 if (fcntl(sock,F_SETFL,flflags) < 0)
a7170401 118 EXCEPTIONSTREAM(error,t2n_communication_error,"fcntl error on socket: " << strerror(errno));
a11e19b7
GE
119}
120
94247295 121/// is the underlying socket connection still open?
644c4d26
GE
122bool socket_handler::is_closed()
123{
124 int r=fcntl(sock,F_GETFL);
125
126 return !(r & O_ACCMODE);
127}
128
517d1214
RP
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 */
139void 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 *
c7857475 149 * The write block size determines the amount of data which is tried to write
517d1214 150 * to the socket when data needs to be sended.
c7857475 151 * Since writing data is done in a loop, this does not limit the amunt of data which can
517d1214
RP
152 * be written.
153 *
154 * The value is normalized to be at least 512 bytes and at max 32K bytes.
155 */
156void 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
c7857475
GE
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 */
170void socket_handler::set_write_timeout(long long new_write_timeout)
171{
172 write_block_size=new_write_timeout;
173} //
174
175
94247295 176/** @brief check if new data is waiting on the raw socket
45a2ebc9 177 @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
94247295 178 -1: wait endless
45a2ebc9 179 0: return instantly
9a5d7790 180 @param[out] usec_timeout_remaining microseconds from the timeout that were not used
94247295 181*/
45a2ebc9 182bool socket_handler::data_waiting(long long usec_timeout,long long* usec_timeout_remaining)
a11e19b7
GE
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
45a2ebc9 199 // convert timeout from long long usec to int sec + int usec
a11e19b7
GE
200 tval.tv_sec = usec_timeout / 1000000;
201 tval.tv_usec = usec_timeout % 1000000;
202 }
203
45a2ebc9
GE
204 int ret=select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr);
205
206 // return the timeout we did not use
6f6d24c0 207 // todo: this is linux specific according to man 2 select
45a2ebc9
GE
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)
a11e19b7
GE
212 return true;
213 else
214 return false;
215}
216
94247295
GE
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
45a2ebc9 219 @param[in,out] usec_timeout wait until new data is found, max timeout usecs.
94247295 220 -1: wait endless
45a2ebc9 221 0: return instantly
9a5d7790 222 @param[out] usec_timeout_remaining microseconds from the timeout that were not used
94247295 223*/
9a5d7790 224bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout, long long *usec_timeout_remaining)
a11e19b7 225{
07e98688 226 // fast path for timeout==0
9a5d7790 227 if (usec_timeout==0 || data_waiting(usec_timeout,usec_timeout_remaining))
a11e19b7
GE
228 return fill_buffer(buffer);
229 else
230 return false;
231}
232
94247295
GE
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*/
a11e19b7
GE
237bool socket_handler::fill_buffer(std::string& buffer)
238{
a11e19b7
GE
239 char socket_buffer[recv_buffer_size];
240
6f59dcf5
TJ
241 const int loop_max = 32; /* limit is 32 * (default) 2048 bytes -> 65536 bytes in one go */
242 int loop_count = 0;
fb3345ad 243
6f59dcf5
TJ
244 bool read_something = false;
245 while (loop_count < loop_max)
a11e19b7 246 {
6f59dcf5
TJ
247 const int nbytes = read(sock, socket_buffer, recv_buffer_size);
248
249 if (nbytes < 0)
a11e19b7 250 {
08059d7f 251 if (errno == EAGAIN || errno == EWOULDBLOCK)
6f59dcf5
TJ
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));
a11e19b7 259 }
a11e19b7 260
6f59dcf5
TJ
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 }
a11e19b7 268
6f59dcf5
TJ
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 }
a11e19b7 276
6f59dcf5
TJ
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 }
a11e19b7 286
6f59dcf5 287 return read_something;
a11e19b7
GE
288}
289
94247295
GE
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.
644c4d26 292void socket_handler::socket_write(const std::string& data)
a11e19b7 293{
4b995a82 294 unsigned int offset = 0;
a11e19b7
GE
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;
c7857475 303 while ((rtn=::write(sock, data.data()+offset, write_size)) == -1 &&
08059d7f 304 (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
a11e19b7 305 {
c7857475 306 wait_ready_to_write(sock,write_timeout);
08059d7f 307 LOGSTREAM(debug,"resuming write() call after EAGAIN or EINTR or EWOULDBLOCK");
a11e19b7
GE
308 }
309
310 if (rtn == -1)
c7857475 311 EXCEPTIONSTREAM(error,t2n_transfer_error,"write() returned " << strerror(errno));
a11e19b7
GE
312 else if (rtn != write_size)
313 {
517d1214 314 LOGSTREAM(debug,"write() wrote " << rtn << " bytes, should have been "
a7170401 315 << write_size << " (complete: " << data.size() << ")");
a11e19b7 316
517d1214 317 write_size = rtn;
a11e19b7
GE
318 }
319
320 offset += write_size;
321 }
322
a7170401
GE
323 LOGSTREAM(debug,"wrote " << data.size() << " bytes");
324
a11e19b7 325 return;
517d1214 326} // eo socket_handler::socket_write(const std::string&)
a11e19b7 327
c7857475
GE
328/// wait until the socket is ready to write again
329void 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
a11e19b7 373}