4fa27d08e3ed56099d71c2519ee2b6fc31746792
[libt2n] / src / socket_handler.cpp
1 /***************************************************************************
2  *   Copyright (C) 2006 by Gerd v. Egidy                                   *
3  *   gve@intra2net.com                                                     *
4  *                                                                         *
5  *   This library is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU Lesser General Public License version   *
7  *   2.1 as published by the Free Software Foundation.                     *
8  *                                                                         *
9  *   This library is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU Lesser General Public License for more details.                   *
13  *                                                                         *
14  *   You should have received a copy of the GNU Lesser General Public      *
15  *   License along with this program; if not, write to the                 *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19
20 #include <stdio.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 #include <sys/time.h>
28 #include <arpa/inet.h>
29 #include <netinet/in.h>
30 #include <netdb.h>
31 #include <fcntl.h>
32 #include <time.h>
33 #include <pwd.h>
34 #include <grp.h>
35
36 #include <sstream>
37
38 #include "socket_handler.hxx"
39 #include "t2n_exception.hxx"
40
41 using namespace std;
42
43 namespace libt2n
44 {
45
46 void socket_handler::set_socket_options(int sock)
47 {
48     int i=1;
49
50     /* fast reuse enable */
51     if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
52     {
53         string err="error setting socket option: ";
54         err+=strerror(errno);
55         log(error, err);
56         throw t2n_communication_error(err);
57     }
58
59     /* keepalive enable */
60     if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
61     {
62         string err="error setting socket option: ";
63         err+=strerror(errno);
64         log(error, err);
65         throw t2n_communication_error(err);
66     }
67
68     /* close on exec */
69     int fdflags;
70     fdflags=fcntl(sock,F_GETFD, 0);
71     if (fdflags < 0)
72     {
73         string err="fcntl error on socket: ";
74         err+=strerror(errno);
75         log(error, err);
76         throw t2n_communication_error(err);
77     }
78     fdflags |= FD_CLOEXEC;
79     if (fcntl(sock,F_SETFD,fdflags) < 0)
80     {
81         string err="fcntl error on socket: ";
82         err+=strerror(errno);
83         log(error, err);
84         throw t2n_communication_error(err);
85     }
86
87     /* non-blocking mode */
88     int flflags;
89     flflags=fcntl(sock,F_GETFL,0);
90     if (flflags < 0)
91     {
92         string err="fcntl error on socket: ";
93         err+=strerror(errno);
94         log(error, err);
95         throw t2n_communication_error(err);
96     }
97     flflags |= O_NONBLOCK;
98     if (fcntl(sock,F_SETFL,flflags) < 0)
99     {
100         string err="fcntl error on socket: ";
101         err+=strerror(errno);
102         log(error, err);
103         throw t2n_communication_error(err);
104     }
105 }
106
107 void socket_handler::close()
108 {
109     ::close(sock);
110 }
111
112 bool socket_handler::data_waiting(long long usec_timeout)
113 {
114     // just our socket
115     fd_set active_fd_set;
116     FD_ZERO (&active_fd_set);
117     FD_SET (sock, &active_fd_set);
118
119     /* set timeout */
120     struct timeval tval;
121     struct timeval *timeout_ptr;
122
123     if (usec_timeout == -1)
124         timeout_ptr = NULL;
125     else
126     {
127         timeout_ptr = &tval;
128
129         // timeout von long long usec in int sec + int usec umrechnen
130         tval.tv_sec = usec_timeout / 1000000;
131         tval.tv_usec = usec_timeout % 1000000;
132     }
133
134     if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0)
135         return true;
136     else
137         return false;
138 }
139
140 bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
141 {
142     // fast path for timeout==-1
143     if (usec_timeout==-1 || data_waiting(usec_timeout))
144         return fill_buffer(buffer);
145     else
146         return false;
147 }
148
149 bool socket_handler::fill_buffer(std::string& buffer)
150 {
151     bool try_again=false;
152
153     char socket_buffer[recv_buffer_size];
154
155     int nbytes = read (sock, socket_buffer, recv_buffer_size);
156     if (nbytes < 0)
157     {
158         if (errno == EAGAIN)
159             return false;                // no data was waiting
160         else if (errno == EINTR)
161         {
162             // interrupted, try again
163             try_again=true;
164         }
165         else
166         {
167             log(error,string("error reading from socket : ")+strerror(errno));
168             // TODO: exception?
169             return false;
170         }
171     }
172
173     // End-of-file
174     if (nbytes == 0 && !try_again)
175     {
176         close();
177         return false;
178     }
179
180     // Data read -> store it
181     if (nbytes > 0)
182         buffer.assign(socket_buffer,nbytes);
183
184     // more data waiting -> recurse
185     if (data_waiting())
186         fill_buffer(buffer);
187
188     if (nbytes > 0)
189         return true;
190     else
191         return false;
192 }
193
194 void socket_handler::write(const std::string& data)
195 {
196     int offset = 0;
197     while (offset < data.size())
198     {
199         unsigned int write_size=write_block_size;
200
201         if (offset+write_size > data.size())
202             write_size = data.size()-offset;
203
204         int rtn;
205         while ((rtn=::write(sock, data.data()+offset, write_size)) &&
206                rtn == -1 && (errno == EAGAIN || errno == EINTR))
207         {
208             usleep (80000);
209             log(debug,"resuming write() call after EAGAIN or EINTR");
210         }
211
212         if (rtn == -1)
213         {
214             log(error,string("write() returned ")+strerror(errno));
215             // TODO: exception?
216             return;
217         }
218         else if (rtn != write_size)
219         {
220             ostringstream msg;
221             msg << "write() wrote " << rtn << " bytes, should have been " 
222                 << write_size << " (complete: " << data.size() << ")";
223
224             log(error,msg.str());
225
226             // TODO: exception?
227             return;
228         }
229
230         offset += write_size;
231     }
232
233     return;
234 }
235
236 }