1 // GnetSocketDriver.cc
2 //
3 // Copyright (C) 2009, 2010, 2011 Rob Caelers <robc@krandor.nl>
4 // All rights reserved.
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #if defined(HAVE_GIO_NET) && defined(HAVE_DISTRIBUTION)
25 
26 #include "debug.hh"
27 #include "GIOSocketDriver.hh"
28 
29 using namespace std;
30 
31 
32 //! Creates a new listen socket.
GIOSocketServer()33 GIOSocketServer::GIOSocketServer() :
34   service(NULL)
35 {
36 }
37 
38 
39 //! Destructs the listen socket.
~GIOSocketServer()40 GIOSocketServer::~GIOSocketServer()
41 {
42   if (service != NULL)
43     {
44       g_socket_service_stop(service);
45       g_object_unref(service);
46       service = NULL;
47     }
48 }
49 
50 
51 //! Listen at the specified port.
52 void
listen(int port)53 GIOSocketServer::listen(int port)
54 {
55   GError *error = NULL;
56 
57   service = g_socket_service_new();
58   if (service == NULL)
59     {
60       throw SocketException("Failed to create server");
61     }
62 
63   gboolean rc = g_socket_listener_add_inet_port(G_SOCKET_LISTENER(service), port, NULL, &error);
64   if (!rc)
65     {
66       g_object_unref(service);
67       service = NULL;
68 
69       throw SocketException(string("Failed to listen: ") + error->message);
70     }
71 
72   g_signal_connect(service, "incoming", G_CALLBACK(static_socket_incoming), this);
73   g_socket_service_start(service);
74 
75 }
76 
77 gboolean
static_socket_incoming(GSocketService * service,GSocketConnection * connection,GObject * src_object,gpointer user_data)78 GIOSocketServer::static_socket_incoming(GSocketService *service,
79                                         GSocketConnection *connection,
80                                         GObject *src_object,
81                                         gpointer user_data)
82 {
83   TRACE_ENTER("GIOSocketServer::static_socket_incoming");
84   (void) service;
85   (void) src_object;
86 
87   try
88     {
89       GIOSocketServer *ss = (GIOSocketServer *) user_data;
90       GIOSocket *socket =  new GIOSocket(connection);
91       ss->listener->socket_accepted(ss, socket);
92     }
93   catch(...)
94     {
95       // Make sure that no exception reach the glib mainloop.
96     }
97 
98   TRACE_EXIT();
99 	return FALSE;
100 }
101 
102 
103 
104 void
static_connected_callback(GObject * source_object,GAsyncResult * result,gpointer user_data)105 GIOSocket::static_connected_callback(GObject *source_object,
106                                      GAsyncResult *result,
107                                      gpointer user_data)
108 {
109   TRACE_ENTER("GIOSocketServer::static_connected_callback");
110 
111   GIOSocket *socket = (GIOSocket *)user_data;
112   GError *error = NULL;
113 
114   GSocketConnection *socket_connection =
115     g_socket_client_connect_finish(G_SOCKET_CLIENT(source_object), result, &error);
116 
117   if (error != NULL)
118     {
119       TRACE_MSG("failed to connect");
120       g_error_free(error);
121     }
122   else if (socket_connection != NULL)
123     {
124       socket->connection = socket_connection;
125       socket->socket = g_socket_connection_get_socket(socket->connection);
126       g_socket_set_blocking(socket->socket, FALSE);
127       g_socket_set_keepalive(socket->socket, TRUE);
128 
129       socket->source = g_socket_create_source(socket->socket,
130                                               (GIOCondition) (G_IO_IN | G_IO_ERR | G_IO_HUP),
131                                               NULL);
132       g_source_set_callback(socket->source, reinterpret_cast<GSourceFunc>(static_data_callback), (void*)socket, NULL);
133       g_source_attach(socket->source, NULL);
134       // g_source_unref(source);
135 
136       if (socket->listener != NULL)
137         {
138           socket->listener->socket_connected(socket, socket->user_data);
139         }
140     }
141   TRACE_EXIT();
142 }
143 
144 
145 gboolean
static_data_callback(GSocket * socket,GIOCondition condition,gpointer user_data)146 GIOSocket::static_data_callback(GSocket *socket,
147                                 GIOCondition condition,
148                                 gpointer user_data)
149 {
150   TRACE_ENTER_MSG("GIOSocket::static_data_callback", (int)condition);
151 
152   GIOSocket *giosocket = (GIOSocket *)user_data;
153   gboolean ret = TRUE;
154 
155   (void) socket;
156 
157   try
158     {
159       // check for socket error
160       if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
161         {
162           if (giosocket->listener != NULL)
163             {
164               TRACE_MSG("Closing socket");
165               // giosocket->close();
166               giosocket->listener->socket_closed(giosocket, giosocket->user_data);
167             }
168           ret = FALSE;
169         }
170 
171       // process input
172       if (ret && (condition & G_IO_IN))
173         {
174           if (giosocket->listener != NULL)
175             {
176               giosocket->listener->socket_io(giosocket, giosocket->user_data);
177             }
178         }
179     }
180   catch(...)
181     {
182       // Make sure that no exception reach the glib mainloop.
183       TRACE_MSG("Exception. Closing socket");
184       giosocket->close();
185       ret = FALSE;
186     }
187   TRACE_EXIT();
188   return ret;
189 }
190 
191 //! Creates a new connection.
GIOSocket(GSocketConnection * connection)192 GIOSocket::GIOSocket(GSocketConnection *connection) :
193   connection(connection),
194   resolver(NULL)
195 {
196   TRACE_ENTER("GIOSocket::GIOSocket(con)");
197   socket = g_socket_connection_get_socket(connection);
198   g_object_ref(connection);
199 
200   g_socket_set_blocking(socket, FALSE);
201   g_socket_set_keepalive(socket, TRUE);
202 
203   source = g_socket_create_source(socket, (GIOCondition)G_IO_IN, NULL);
204   g_source_set_callback(source, reinterpret_cast<GSourceFunc>(static_data_callback), (void*)this, NULL);
205   g_source_attach(source, NULL);
206   g_source_unref(source);
207   TRACE_EXIT();
208 }
209 
210 
211 //! Creates a new connection.
GIOSocket()212 GIOSocket::GIOSocket() :
213   connection(NULL),
214   socket(NULL),
215   resolver(NULL),
216   source(NULL),
217   port(0)
218 {
219   TRACE_ENTER("GIOSocket::GIOSocket()");
220   TRACE_EXIT();
221 }
222 
223 
224 //! Destructs the connection.
~GIOSocket()225 GIOSocket::~GIOSocket()
226 {
227   TRACE_ENTER("GIOSocket::~GIOSocket");
228   if (connection != NULL)
229     {
230       g_object_unref(connection);
231     }
232   if (resolver != NULL)
233     {
234       g_object_unref(resolver);
235     }
236   if (source != NULL)
237     {
238       g_source_destroy(source);
239     }
240   TRACE_EXIT();
241 }
242 
243 
244 //! Connects to the specified host.
245 void
connect(const string & host,int port)246 GIOSocket::connect(const string &host, int port)
247 {
248   TRACE_ENTER_MSG("GIOSocket::connect", host << " " << port);
249   this->port = port;
250 
251   GInetAddress *inet_addr = g_inet_address_new_from_string(host.c_str());
252   if (inet_addr != NULL)
253     {
254       connect(inet_addr, port);
255       g_object_unref(inet_addr);
256     }
257   else
258     {
259       resolver = g_resolver_get_default();
260       g_resolver_lookup_by_name_async(resolver,
261                                       host.c_str(),
262                                       NULL,
263                                       static_connect_after_resolve,
264                                       this);
265 
266     }
267   TRACE_EXIT();
268 }
269 
270 void
connect(GInetAddress * inet_addr,int port)271 GIOSocket::connect(GInetAddress *inet_addr, int port)
272 {
273   TRACE_ENTER_MSG("GIOSocket::connect", port);
274   GSocketAddress *socket_address = g_inet_socket_address_new(inet_addr, port);
275   GSocketClient *socket_client = g_socket_client_new();
276 
277   g_socket_client_connect_async(socket_client,
278                                 G_SOCKET_CONNECTABLE(socket_address),
279                                 NULL,
280                                 static_connected_callback,
281                                 this);
282   TRACE_EXIT();
283 }
284 
285 void
static_connect_after_resolve(GObject * source_object,GAsyncResult * res,gpointer user_data)286 GIOSocket::static_connect_after_resolve(GObject *source_object, GAsyncResult *res, gpointer user_data)
287 {
288   TRACE_ENTER("GIOSocket::static_connect_after_resolve");
289   GError *error = NULL;
290   GList *addresses = g_resolver_lookup_by_name_finish((GResolver *)source_object, res, &error);
291 
292   if (error != NULL)
293     {
294       TRACE_MSG("failed");
295       g_error_free(error);
296     }
297 
298   if (addresses != NULL)
299     {
300       // Take first result
301       if (addresses->data != NULL)
302         {
303           GInetAddress *a = (GInetAddress *) addresses->data;
304           GIOSocket *socket = (GIOSocket *) user_data;
305           socket->connect(a, socket->port);
306         }
307        g_resolver_free_addresses(addresses);
308     }
309   TRACE_EXIT();
310 }
311 
312 
313 //! Read from the connection.
314 void
read(void * buf,int count,int & bytes_read)315 GIOSocket::read(void *buf, int count, int &bytes_read)
316 {
317   TRACE_ENTER_MSG("GIOSocket::read", count);
318 
319   GError *error = NULL;
320   gsize num_read = 0;
321 
322   if (socket != NULL)
323     {
324       num_read = g_socket_receive(socket, (char *)buf, count, NULL, &error);
325 
326       if (error != NULL)
327         {
328           throw SocketException(string("socket read error: ") + error->message);
329         }
330     }
331 
332   bytes_read = (int)num_read;
333   TRACE_RETURN(bytes_read);
334 }
335 
336 
337 //! Write to the connection.
338 void
write(void * buf,int count,int & bytes_written)339 GIOSocket::write(void *buf, int count, int &bytes_written)
340 {
341   GError *error = NULL;
342   gsize num_written = 0;
343   if (socket != NULL)
344     {
345       num_written = g_socket_send(socket, (char *)buf, count, NULL, &error);
346       if (error != NULL)
347         {
348           throw SocketException(string("socket write error: ") + error->message);
349         }
350     }
351   bytes_written = (int) num_written;
352 }
353 
354 
355 //! Close the connection.
356 void
close()357 GIOSocket::close()
358 {
359   TRACE_ENTER("GIOSocket::close");
360   GError *error = NULL;
361   if (socket != NULL)
362     {
363       g_socket_shutdown(socket, TRUE, TRUE, &error);
364       g_socket_close(socket, &error);
365       socket = NULL;
366     }
367   TRACE_EXIT();
368 }
369 
370 //! Create a new socket
371 ISocket *
create_socket()372 GIOSocketDriver::create_socket()
373 {
374   return new GIOSocket();
375 }
376 
377 
378 //! Create a new listen socket
379 ISocketServer *
create_server()380 GIOSocketDriver::create_server()
381 {
382   return new GIOSocketServer();
383 }
384 
385 #endif
386