1d18fd2025c4f3191001173bab55bd7c13d650c4
[libt2n] / src / socket_server.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 <sys/stat.h>
32 #include <arpa/inet.h>
33 #include <netinet/in.h>
34 #include <netdb.h>
35 #include <fcntl.h>
36 #include <time.h>
37 #include <pwd.h>
38 #include <grp.h>
39
40 #include <sstream>
41
42 #include "socket_server.hxx"
43 #include "t2n_exception.hxx"
44 #include "log.hxx"
45
46 using namespace std;
47
48 namespace libt2n
49 {
50
51 /** @brief create a new tcp-based server
52     @param port tcp port you want to listen on
53     @param ip the local ip you want to listen on. "0.0.0.0" means all local ips (default).
54 */
55 socket_server::socket_server(int port, const std::string& ip)
56     : server(), socket_handler(0,tcp_s)
57 {
58     /* Create the socket. */
59     sock = socket (PF_INET, SOCK_STREAM, 0);
60     if (sock < 0)
61         EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
62
63     set_socket_options(sock);
64
65     /* Give the socket a name. */
66     struct sockaddr_in sockaddr;
67     sockaddr.sin_family = AF_INET;
68     sockaddr.sin_port = htons(port);
69
70     if (inet_aton(ip.c_str(),&sockaddr.sin_addr) == 0)
71         EXCEPTIONSTREAM(error,t2n_server_error,"failed listening on invalid ip " << ip);
72
73     if (bind (sock, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
74     {
75         // FIXME: Calls virtual function socket_server::get_logstream() in constructor
76         EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
77     }
78
79     start_listening();
80 }
81
82 /** @brief create a new unix-socked-based server
83     @param path path of the socket
84     @param filemode permissions you want to open the socket with
85     @param user local username for the socket
86     @param group local groupname for the socket
87 */
88 socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
89     : server(), socket_handler(0,unix_s)
90 {
91     unix_path=path;
92
93     // TODO: Every EXCEPTIONSTREAM in here calls virtual function get_logstream()
94
95     /* Create the socket. */
96     sock = socket (PF_UNIX, SOCK_STREAM, 0);
97     if (sock < 0)
98         EXCEPTIONSTREAM(error,t2n_server_error,"error opening socket: " << strerror(errno));
99
100     set_socket_options(sock);
101
102     /* Give the socket a name. */
103     struct sockaddr_un unix_name;
104     unix_name.sun_family = AF_UNIX;
105     strncpy (unix_name.sun_path, unix_path.c_str(),sizeof(unix_name.sun_path));
106
107     /* just to make sure there is no other socket file */
108     unlink (unix_name.sun_path);
109
110     if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0)
111         EXCEPTIONSTREAM(error,t2n_server_error,"error binding socket: " << strerror(errno));
112
113     /* change permissions */
114     if (chmod (unix_name.sun_path, filemode) != 0) 
115         EXCEPTIONSTREAM(error,t2n_server_error,"error changing permission: " << strerror(errno));
116
117     if (!user.empty() && !group.empty())
118     {
119         // TODO maybe use current user/group if one of them is empty
120
121         struct passwd *socket_user = getpwnam (user.c_str());
122         if (socket_user == NULL) 
123             EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket user: " << strerror(errno));
124
125         struct group *socket_group = getgrnam (group.c_str());
126         if (socket_group == NULL) 
127             EXCEPTIONSTREAM(error,t2n_server_error,"error getting socket group: " << strerror(errno));
128
129         if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0) 
130             EXCEPTIONSTREAM(error,t2n_server_error,"error changing socket ownership: " << strerror(errno));
131     }
132
133     start_listening();
134 }
135
136 /**
137  * Destructor
138  */
139 socket_server::~socket_server()
140 {
141     // close all client connections
142     server::close();
143
144     // server socket will be closed by destructor of socket_handler
145
146     if (get_type()==unix_s)
147         unlink(unix_path.c_str());
148
149     // disconnect connection<->server pointer
150     std::map<unsigned int, server_connection*>::iterator it, it_end = connections.end();
151     for (it = connections.begin(); it != it_end; ++it)
152     {
153         socket_server_connection *conn = dynamic_cast<socket_server_connection*>(it->second);
154         if (conn)
155             conn->my_server = NULL;
156     }
157 }
158
159 /// start listening on a new server socket (called by the constructors)
160 void socket_server::start_listening()
161 {
162     if (listen (sock, 5) < 0)
163         EXCEPTIONSTREAM(error,t2n_server_error,"error listening to socket: " << strerror(errno));
164
165     /* clear & insert server sock into the fd_tab to prepare select */
166     FD_ZERO(&connection_set);
167     FD_SET (sock, &connection_set);
168     sockets_set.insert(sock);
169 }
170
171 /// handle a new connection from a client
172 void socket_server::new_connection()
173 {
174     struct sockaddr_un clientname;
175
176     unsigned int size = sizeof (clientname);
177     int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
178     if (newsock < 0)
179     {
180         // return on non-fatal errors (list taken from man-page)
181         if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR ||
182             errno == EMFILE || errno == ENFILE || errno == ENOBUFS || errno == ENOMEM ||
183             errno == EPROTO || errno ==  EPERM || errno == ETIMEDOUT)
184         {
185             LOGSTREAM(error,"non-fatal accept error: " << strerror(errno));
186             return;
187         }
188
189         /* fatal error: will usually kill or restart the server */
190         EXCEPTIONSTREAM(error,t2n_server_error,"fatal error accepting connection: " << strerror(errno));
191     }
192
193     FD_SET (newsock, &connection_set);
194     sockets_set.insert(newsock);
195
196     socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
197     nc->set_socket_options(newsock);
198
199     add_connection(nc);
200
201     return;
202 }
203
204 /** @brief look for new connections and new data in any of the existing connections
205     @param usec_timeout wait until new data is found, max timeout usecs.
206             -1: wait endless
207             0: return instantly
208     @param usec_timeout_remaining if non-NULL the function will write the
209             not used time to the given target
210     @retval true if new data was found (does not mean that the received data 
211             is a complete packet though)
212 */
213 bool socket_server::fill_buffer(long long usec_timeout,long long* usec_timeout_remaining)
214 {
215     fd_set used_fdset=connection_set;
216
217     /* set timeout */
218     struct timeval tval;
219     struct timeval *timeout_ptr;
220
221     if (usec_timeout == -1)
222         timeout_ptr = NULL;
223     else
224     {
225         timeout_ptr = &tval;
226
227         // timeout von long long usec in int sec + int usec umrechnen
228         tval.tv_sec = usec_timeout / 1000000;
229         tval.tv_usec = usec_timeout % 1000000;
230     }
231
232     int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
233
234     // return the timeout we did not use
235     if (usec_timeout > 0 && usec_timeout_remaining != NULL)
236         *usec_timeout_remaining=(tval.tv_sec*1000000)+tval.tv_usec;
237
238     if (ret < 0)
239     {
240         if (errno == EINTR)
241         {
242             // select interrupted by signal
243             ret=0;
244         }
245         else
246             EXCEPTIONSTREAM(error,t2n_server_error,"select error: " << strerror(errno));
247     }
248
249     if (ret > 0)
250     {
251         // we have data pending
252
253         // check for new connection
254         if (FD_ISSET (sock, &used_fdset))
255         {
256             new_connection();
257         }
258
259         // check all connections for pending data
260         return fill_connection_buffers();
261     }
262
263     return false;
264 }
265
266 /// call fill_buffer() on all connections, called from fill_buffer()
267 bool socket_server::fill_connection_buffers()
268 {
269     bool data_found = false;
270
271     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
272     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
273         if (!i->second->server_connection::is_closed())
274         {
275             // shutdown all connections which throw exceptions to protect the server
276             try
277             {
278                 if (i->second->fill_buffer(0))
279                     data_found=true;
280             }
281             catch (t2n_transfer_error &e)
282                 { i->second->close(); }
283         }
284
285     return data_found;
286 }
287
288 /// remove the socket of a connection after the connection has been closed
289 void socket_server::remove_connection_socket(int sock)
290 {
291     FD_CLR(sock, &connection_set);
292     sockets_set.erase(sock);
293 }
294
295 /**
296  * Destructor
297  */
298 socket_server_connection::~socket_server_connection()
299 {
300     // Only notify parent server about going down.
301     // The real socket will be closed by the destructor of the base classes.
302     if (my_server && sock != -1)
303     {
304         socket_server *srv = dynamic_cast<socket_server*>(my_server);
305         if (srv)
306             srv->remove_connection_socket(sock);
307     }
308 }
309
310 /// close this connection. complete data waiting in the buffer can still be retrieved.
311 void socket_server_connection::close()
312 {
313     if (my_server && sock != -1)
314     {
315         socket_server *srv = dynamic_cast<socket_server*>(my_server);
316         if (srv)
317             srv->remove_connection_socket(sock);
318     }
319
320     if (!server_connection::is_closed())
321     {
322         socket_handler::close();
323         server_connection::close();
324     }
325 }
326
327 }