libt2n: (gerd) add ip communication
[libt2n] / src / socket_server.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_server.hxx"
39 #include "t2n_exception.hxx"
40
41 using namespace std;
42
43 namespace libt2n
44 {
45
46 /** @brief create a new tcp-based server
47     @param port tcp port you want to listen on
48     @param ip the local ip you want to listen on. "0.0.0.0" means all local ips (default).
49 */
50 socket_server::socket_server(int port, const std::string& ip)
51     : server(), socket_handler(0,tcp_s)
52 {
53     /* Create the socket. */
54     sock = socket (PF_INET, SOCK_STREAM, 0);
55     if (sock < 0)
56     {
57         string err="error opening socket: ";
58         err+=strerror(errno);
59         log(error, err);
60         throw t2n_server_error(err);
61     }
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     {
72         string err="failed listening on invalid ip ";
73         err+=ip;
74         log(error, err);
75         throw t2n_server_error(err);
76     }
77
78     if (bind (sock, (struct sockaddr *) &sockaddr, sizeof (sockaddr)) < 0)
79     {
80         string err="error binding socket: ";
81         err+=strerror(errno);
82         log(error, err);
83         throw t2n_server_error(err);
84     }
85
86     start_listening();
87 }
88
89 /** @brief create a new unix-socked-based server
90     @param path path of the socket
91     @param filemode permissions you want to open the socket with
92     @param user local username for the socket
93     @param group local groupname for the socket
94 */
95 socket_server::socket_server(const std::string& path, mode_t filemode, const std::string& user, const std::string& group)
96     : server(), socket_handler(0,unix_s)
97 {
98     unix_path=path;
99
100     /* Create the socket. */
101     sock = socket (PF_UNIX, SOCK_STREAM, 0);
102     if (sock < 0)
103     {
104         string err="error opening socket: ";
105         err+=strerror(errno);
106         log(error, err);
107         throw t2n_server_error(err);
108     }
109
110     set_socket_options(sock);
111
112     /* Give the socket a name. */
113     struct sockaddr_un unix_name;
114     unix_name.sun_family = AF_UNIX;
115     strncpy (unix_name.sun_path, unix_path.c_str(),sizeof(unix_name.sun_path));
116
117     /* just to make sure there is no other socket file */
118     unlink (unix_name.sun_path);
119
120     if (bind (sock, (struct sockaddr *) &unix_name, sizeof (unix_name)) < 0)
121     {
122         string err="error binding socket: ";
123         err+=strerror(errno);
124         log(error, err);
125         throw t2n_server_error(err);
126     }
127
128     /* change permissions */
129     if (chmod (unix_name.sun_path, filemode) != 0) 
130     {
131         string err="error changing permission: ";
132         err+=strerror(errno);
133         log(error, err);
134         throw t2n_server_error(err);
135     }
136
137     if (!user.empty() && !group.empty())
138     {
139         // TODO maybe use current user/group if one of them is empty
140
141         struct passwd *socket_user = getpwnam (user.c_str());
142         if (socket_user == NULL) 
143         {
144             string err="error getting socket user: ";
145             err+=strerror(errno);
146             log(error, err);
147             throw t2n_server_error(err);
148         }
149     
150         struct group *socket_group = getgrnam (group.c_str());
151         if (socket_group == NULL) 
152         {
153             string err="error getting socket group: ";
154             err+=strerror(errno);
155             log(error, err);
156             throw t2n_server_error(err);
157         }
158     
159         if (chown (unix_name.sun_path, socket_user->pw_uid, socket_group->gr_gid) != 0) 
160         {
161             string err="error changing socket ownership: ";
162             err+=strerror(errno);
163             log(error, err);
164             throw t2n_server_error(err);
165         }
166     }
167
168     start_listening();
169 }
170
171 socket_server::~socket_server()
172 {
173     socket_handler::close();
174
175     if (get_type()==unix_s)
176         unlink(unix_path.c_str());
177 }
178
179 /// start listening on a new server socket (called by the constructors)
180 void socket_server::start_listening()
181 {
182     if (listen (sock, 5) < 0)
183     {
184         string err="error listening to socket: ";
185         err+=strerror(errno);
186         log(error, err);
187         throw t2n_server_error(err);
188     }
189
190     /* clear & insert server sock into the fd_tab to prepare select */
191     FD_ZERO(&connection_set);
192     FD_SET (sock, &connection_set);
193 }
194
195 /// handle a new connection from a client
196 void socket_server::new_connection()
197 {
198     struct sockaddr_un clientname;
199
200     unsigned int size = sizeof (clientname);
201     int newsock = accept (sock,(struct sockaddr *) &clientname,&size);
202     if (newsock < 0)
203     {
204         if (errno == EAGAIN)
205         {
206             log(error, "accept error (EAGAIN): no connection waiting");
207             return;
208         }
209
210         /* default: break */
211         string err="error accepting connection: ";
212         err+=strerror(errno);
213         log(error, err);
214         throw t2n_server_error(err);
215     }
216
217     FD_SET (newsock, &connection_set);
218
219     socket_server_connection *nc=new socket_server_connection(newsock, get_type(), get_default_timeout());
220     nc->set_socket_options(newsock);
221
222     add_connection(nc);
223
224     return;
225 }
226
227 bool socket_server::fill_buffer(long long usec_timeout)
228 {
229     fd_set used_fdset=connection_set;
230
231     /* set timeout */
232     struct timeval tval;
233     struct timeval *timeout_ptr;
234
235     if (usec_timeout == -1)
236         timeout_ptr = NULL;
237     else
238     {
239         timeout_ptr = &tval;
240
241         // timeout von long long usec in int sec + int usec umrechnen
242         tval.tv_sec = usec_timeout / 1000000;
243         tval.tv_usec = usec_timeout % 1000000;
244     }
245
246     int ret=select (FD_SETSIZE, &used_fdset, NULL, NULL, timeout_ptr);
247
248     if (ret < 0)
249     {
250         if (errno == EINTR)
251         {
252             // select interrupted by signal
253             ret=0;
254         }
255         else
256         {
257             string err="select error: ";
258             err+=strerror(errno);
259             log(error, err);
260             throw t2n_server_error(err);
261         }
262     }
263
264     if (ret > 0)
265     {
266         // we have data pending
267
268         // check for new connection
269         if (FD_ISSET (sock, &used_fdset))
270         {
271             new_connection();
272         }
273
274         // check all connections for pending data
275         return fill_connection_buffers();
276     }
277
278     return false;
279 }
280
281 /// call fill_buffer() on all connections, called from fill_buffer()
282 bool socket_server::fill_connection_buffers()
283 {
284     bool data_found;
285
286     std::map<unsigned int, server_connection*>::iterator ie=connections.end();
287     for(std::map<unsigned int, server_connection*>::iterator i=connections.begin(); i != ie; i++)
288         if (!i->second->server_connection::is_closed())
289             if (i->second->fill_buffer(0))
290                 data_found=true;
291
292     return data_found;
293 }
294
295 /// remove the socket of a connection after the connection has been closed
296 void socket_server::remove_connection_socket(int sock)
297 {
298     FD_CLR(sock, &connection_set);
299 }
300
301 void socket_server_connection::log(log_level_values level, const char* message)
302 {
303     if(my_server)
304     {
305         ostringstream msg;
306         msg << "connection id " << get_id() << ": " << message;
307         my_server->log(level,msg.str().c_str());
308     }
309 }
310
311 /// close this connection. complete data waiting in the buffer can still be retrieved.
312 void socket_server_connection::close()
313 {
314     if (!server_connection::is_closed())
315     {
316         socket_handler::close();
317         server_connection::close();
318     }
319
320     if (my_server)
321     {
322         dynamic_cast<socket_server*>(my_server)->remove_connection_socket(sock);
323     }
324 }
325
326 }