1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 /**
22  * SECTION:element-tcpserversink
23  * @title: tcpserversink
24  * @see_also: #multifdsink
25  *
26  * ## Example launch line (server):
27  * |[
28  * gst-launch-1.0 fdsrc fd=1 ! tcpserversink port=3000
29  * ]|
30  * ## Example launch line (client):
31  * |[
32  * gst-launch-1.0 tcpclientsrc port=3000 ! fdsink fd=2
33  * ]|
34  *
35  */
36 
37 #ifdef HAVE_CONFIG_H
38 #include "config.h"
39 #endif
40 #include <gst/gst-i18n-plugin.h>
41 #include <string.h>             /* memset */
42 
43 #include "gsttcp.h"
44 #include "gsttcpserversink.h"
45 
46 #define TCP_BACKLOG             5
47 
48 GST_DEBUG_CATEGORY_STATIC (tcpserversink_debug);
49 #define GST_CAT_DEFAULT (tcpserversink_debug)
50 
51 enum
52 {
53   PROP_0,
54   PROP_HOST,
55   PROP_PORT,
56   PROP_CURRENT_PORT
57 };
58 
59 static void gst_tcp_server_sink_finalize (GObject * gobject);
60 
61 static gboolean gst_tcp_server_sink_init_send (GstMultiHandleSink * this);
62 static gboolean gst_tcp_server_sink_close (GstMultiHandleSink * this);
63 static void gst_tcp_server_sink_removed (GstMultiHandleSink * sink,
64     GstMultiSinkHandle handle);
65 
66 static void gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
67     const GValue * value, GParamSpec * pspec);
68 static void gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
69     GValue * value, GParamSpec * pspec);
70 
71 #define gst_tcp_server_sink_parent_class parent_class
72 G_DEFINE_TYPE (GstTCPServerSink, gst_tcp_server_sink,
73     GST_TYPE_MULTI_SOCKET_SINK);
74 
75 static void
gst_tcp_server_sink_class_init(GstTCPServerSinkClass * klass)76 gst_tcp_server_sink_class_init (GstTCPServerSinkClass * klass)
77 {
78   GObjectClass *gobject_class;
79   GstElementClass *gstelement_class;
80   GstMultiHandleSinkClass *gstmultihandlesink_class;
81 
82   gobject_class = (GObjectClass *) klass;
83   gstelement_class = (GstElementClass *) klass;
84   gstmultihandlesink_class = (GstMultiHandleSinkClass *) klass;
85 
86   gobject_class->set_property = gst_tcp_server_sink_set_property;
87   gobject_class->get_property = gst_tcp_server_sink_get_property;
88   gobject_class->finalize = gst_tcp_server_sink_finalize;
89 
90   /* FIXME 2.0: Rename this to bind-address, host does not make much
91    * sense here */
92   g_object_class_install_property (gobject_class, PROP_HOST,
93       g_param_spec_string ("host", "host", "The host/IP to listen on",
94           TCP_DEFAULT_HOST, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
95   g_object_class_install_property (gobject_class, PROP_PORT,
96       g_param_spec_int ("port", "port",
97           "The port to listen to (0=random available port)",
98           0, TCP_HIGHEST_PORT, TCP_DEFAULT_PORT,
99           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
100   /**
101    * GstTCPServerSink:current-port:
102    *
103    * The port number the socket is currently bound to. Applications can use
104    * this property to retrieve the port number actually bound to in case
105    * the port requested was 0 (=allocate a random available port).
106    *
107    * Since: 1.0.2
108    **/
109   g_object_class_install_property (gobject_class, PROP_CURRENT_PORT,
110       g_param_spec_int ("current-port", "current-port",
111           "The port number the socket is currently bound to", 0,
112           TCP_HIGHEST_PORT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
113 
114   gst_element_class_set_static_metadata (gstelement_class,
115       "TCP server sink", "Sink/Network",
116       "Send data as a server over the network via TCP",
117       "Thomas Vander Stichele <thomas at apestaart dot org>");
118 
119   gstmultihandlesink_class->init = gst_tcp_server_sink_init_send;
120   gstmultihandlesink_class->close = gst_tcp_server_sink_close;
121   gstmultihandlesink_class->removed = gst_tcp_server_sink_removed;
122 
123   GST_DEBUG_CATEGORY_INIT (tcpserversink_debug, "tcpserversink", 0, "TCP sink");
124 }
125 
126 static void
gst_tcp_server_sink_init(GstTCPServerSink * this)127 gst_tcp_server_sink_init (GstTCPServerSink * this)
128 {
129   this->server_port = TCP_DEFAULT_PORT;
130   /* should support as minimum 576 for IPV4 and 1500 for IPV6 */
131   /* this->mtu = 1500; */
132   this->host = g_strdup (TCP_DEFAULT_HOST);
133 
134   this->server_socket = NULL;
135 }
136 
137 static void
gst_tcp_server_sink_finalize(GObject * gobject)138 gst_tcp_server_sink_finalize (GObject * gobject)
139 {
140   GstTCPServerSink *this = GST_TCP_SERVER_SINK (gobject);
141 
142   if (this->server_socket)
143     g_object_unref (this->server_socket);
144   this->server_socket = NULL;
145   g_free (this->host);
146   this->host = NULL;
147 
148   G_OBJECT_CLASS (parent_class)->finalize (gobject);
149 }
150 
151 /* handle a read request on the server,
152  * which indicates a new client connection */
153 static gboolean
gst_tcp_server_sink_handle_server_read(GstTCPServerSink * sink)154 gst_tcp_server_sink_handle_server_read (GstTCPServerSink * sink)
155 {
156   GstMultiSinkHandle handle;
157   GSocket *client_socket;
158   GError *err = NULL;
159 
160   /* wait on server socket for connections */
161   client_socket =
162       g_socket_accept (sink->server_socket, sink->element.cancellable, &err);
163   if (!client_socket)
164     goto accept_failed;
165 
166   handle.socket = client_socket;
167   /* gst_multi_handle_sink_add does not take ownership of client_socket */
168   gst_multi_handle_sink_add (GST_MULTI_HANDLE_SINK (sink), handle);
169 
170 #ifndef GST_DISABLE_GST_DEBUG
171   {
172     GInetSocketAddress *addr =
173         G_INET_SOCKET_ADDRESS (g_socket_get_remote_address (client_socket,
174             NULL));
175     gchar *ip =
176         g_inet_address_to_string (g_inet_socket_address_get_address (addr));
177 
178     GST_DEBUG_OBJECT (sink, "added new client ip %s:%u with socket %p",
179         ip, g_inet_socket_address_get_port (addr), client_socket);
180 
181     g_free (ip);
182     g_object_unref (addr);
183   }
184 #endif
185 
186   g_object_unref (client_socket);
187   return TRUE;
188 
189   /* ERRORS */
190 accept_failed:
191   {
192     GST_ELEMENT_ERROR (sink, RESOURCE, OPEN_WRITE, (NULL),
193         ("Could not accept client on server socket %p: %s",
194             sink->server_socket, err->message));
195     g_clear_error (&err);
196     return FALSE;
197   }
198 }
199 
200 static void
gst_tcp_server_sink_removed(GstMultiHandleSink * sink,GstMultiSinkHandle handle)201 gst_tcp_server_sink_removed (GstMultiHandleSink * sink,
202     GstMultiSinkHandle handle)
203 {
204   GError *err = NULL;
205 
206   GST_DEBUG_OBJECT (sink, "closing socket");
207 
208   if (!g_socket_close (handle.socket, &err)) {
209     GST_ERROR_OBJECT (sink, "Failed to close socket: %s", err->message);
210     g_clear_error (&err);
211   }
212 }
213 
214 static gboolean
gst_tcp_server_sink_socket_condition(GSocket * socket,GIOCondition condition,GstTCPServerSink * sink)215 gst_tcp_server_sink_socket_condition (GSocket * socket, GIOCondition condition,
216     GstTCPServerSink * sink)
217 {
218   if ((condition & G_IO_ERR)) {
219     goto error;
220   } else if ((condition & G_IO_IN) || (condition & G_IO_PRI)) {
221     if (!gst_tcp_server_sink_handle_server_read (sink))
222       return FALSE;
223   }
224 
225   return TRUE;
226 
227 error:
228   GST_ELEMENT_ERROR (sink, RESOURCE, READ, (NULL),
229       ("client connection failed"));
230 
231   return FALSE;
232 }
233 
234 static void
gst_tcp_server_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)235 gst_tcp_server_sink_set_property (GObject * object, guint prop_id,
236     const GValue * value, GParamSpec * pspec)
237 {
238   GstTCPServerSink *sink;
239 
240   sink = GST_TCP_SERVER_SINK (object);
241 
242   switch (prop_id) {
243     case PROP_HOST:
244       if (!g_value_get_string (value)) {
245         g_warning ("host property cannot be NULL");
246         break;
247       }
248       g_free (sink->host);
249       sink->host = g_strdup (g_value_get_string (value));
250       break;
251     case PROP_PORT:
252       sink->server_port = g_value_get_int (value);
253       break;
254     default:
255       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
256       break;
257   }
258 }
259 
260 static void
gst_tcp_server_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)261 gst_tcp_server_sink_get_property (GObject * object, guint prop_id,
262     GValue * value, GParamSpec * pspec)
263 {
264   GstTCPServerSink *sink;
265 
266   sink = GST_TCP_SERVER_SINK (object);
267 
268   switch (prop_id) {
269     case PROP_HOST:
270       g_value_set_string (value, sink->host);
271       break;
272     case PROP_PORT:
273       g_value_set_int (value, sink->server_port);
274       break;
275     case PROP_CURRENT_PORT:
276       g_value_set_int (value, g_atomic_int_get (&sink->current_port));
277       break;
278     default:
279       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
280       break;
281   }
282 }
283 
284 
285 /* create a socket for sending to remote machine */
286 static gboolean
gst_tcp_server_sink_init_send(GstMultiHandleSink * parent)287 gst_tcp_server_sink_init_send (GstMultiHandleSink * parent)
288 {
289   GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
290   GError *err = NULL;
291   GInetAddress *addr;
292   GSocketAddress *saddr;
293   GResolver *resolver;
294   gint bound_port;
295 
296   /* look up name if we need to */
297   addr = g_inet_address_new_from_string (this->host);
298   if (!addr) {
299     GList *results;
300 
301     resolver = g_resolver_get_default ();
302 
303     results =
304         g_resolver_lookup_by_name (resolver, this->host,
305         this->element.cancellable, &err);
306     if (!results)
307       goto name_resolve;
308     addr = G_INET_ADDRESS (g_object_ref (results->data));
309 
310     g_resolver_free_addresses (results);
311     g_object_unref (resolver);
312   }
313 #ifndef GST_DISABLE_GST_DEBUG
314   {
315     gchar *ip = g_inet_address_to_string (addr);
316 
317     GST_DEBUG_OBJECT (this, "IP address for host %s is %s", this->host, ip);
318     g_free (ip);
319   }
320 #endif
321   saddr = g_inet_socket_address_new (addr, this->server_port);
322   g_object_unref (addr);
323 
324   /* create the server listener socket */
325   this->server_socket =
326       g_socket_new (g_socket_address_get_family (saddr), G_SOCKET_TYPE_STREAM,
327       G_SOCKET_PROTOCOL_TCP, &err);
328   if (!this->server_socket)
329     goto no_socket;
330 
331   GST_DEBUG_OBJECT (this, "opened sending server socket with socket %p",
332       this->server_socket);
333 
334   g_socket_set_blocking (this->server_socket, FALSE);
335 
336   /* bind it */
337   GST_DEBUG_OBJECT (this, "binding server socket to address");
338   if (!g_socket_bind (this->server_socket, saddr, TRUE, &err))
339     goto bind_failed;
340 
341   g_object_unref (saddr);
342 
343   GST_DEBUG_OBJECT (this, "listening on server socket");
344   g_socket_set_listen_backlog (this->server_socket, TCP_BACKLOG);
345 
346   if (!g_socket_listen (this->server_socket, &err))
347     goto listen_failed;
348 
349   GST_DEBUG_OBJECT (this, "listened on server socket %p", this->server_socket);
350 
351   if (this->server_port == 0) {
352     saddr = g_socket_get_local_address (this->server_socket, NULL);
353     bound_port = g_inet_socket_address_get_port ((GInetSocketAddress *) saddr);
354     g_object_unref (saddr);
355   } else {
356     bound_port = this->server_port;
357   }
358 
359   GST_DEBUG_OBJECT (this, "listening on port %d", bound_port);
360 
361   g_atomic_int_set (&this->current_port, bound_port);
362 
363   g_object_notify (G_OBJECT (this), "current-port");
364 
365   this->server_source =
366       g_socket_create_source (this->server_socket,
367       G_IO_IN | G_IO_OUT | G_IO_PRI | G_IO_ERR | G_IO_HUP,
368       this->element.cancellable);
369   g_source_set_callback (this->server_source,
370       (GSourceFunc) gst_tcp_server_sink_socket_condition, gst_object_ref (this),
371       (GDestroyNotify) gst_object_unref);
372   g_source_attach (this->server_source, this->element.main_context);
373 
374   return TRUE;
375 
376   /* ERRORS */
377 no_socket:
378   {
379     GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
380         ("Failed to create socket: %s", err->message));
381     g_clear_error (&err);
382     g_object_unref (saddr);
383     return FALSE;
384   }
385 name_resolve:
386   {
387     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
388       GST_DEBUG_OBJECT (this, "Cancelled name resolval");
389     } else {
390       GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
391           ("Failed to resolve host '%s': %s", this->host, err->message));
392     }
393     g_clear_error (&err);
394     g_object_unref (resolver);
395     return FALSE;
396   }
397 bind_failed:
398   {
399     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
400       GST_DEBUG_OBJECT (this, "Cancelled binding");
401     } else {
402       GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
403           ("Failed to bind on host '%s:%d': %s", this->host, this->server_port,
404               err->message));
405     }
406     g_clear_error (&err);
407     g_object_unref (saddr);
408     gst_tcp_server_sink_close (GST_MULTI_HANDLE_SINK (&this->element));
409     return FALSE;
410   }
411 listen_failed:
412   {
413     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
414       GST_DEBUG_OBJECT (this, "Cancelled listening");
415     } else {
416       GST_ELEMENT_ERROR (this, RESOURCE, OPEN_READ, (NULL),
417           ("Failed to listen on host '%s:%d': %s", this->host,
418               this->server_port, err->message));
419     }
420     g_clear_error (&err);
421     gst_tcp_server_sink_close (GST_MULTI_HANDLE_SINK (&this->element));
422     return FALSE;
423   }
424 }
425 
426 static gboolean
gst_tcp_server_sink_close(GstMultiHandleSink * parent)427 gst_tcp_server_sink_close (GstMultiHandleSink * parent)
428 {
429   GstTCPServerSink *this = GST_TCP_SERVER_SINK (parent);
430 
431   if (this->server_source) {
432     g_source_destroy (this->server_source);
433     g_source_unref (this->server_source);
434     this->server_source = NULL;
435   }
436 
437   if (this->server_socket) {
438     GError *err = NULL;
439 
440     GST_DEBUG_OBJECT (this, "closing socket");
441 
442     if (!g_socket_close (this->server_socket, &err)) {
443       GST_ERROR_OBJECT (this, "Failed to close socket: %s", err->message);
444       g_clear_error (&err);
445     }
446     g_object_unref (this->server_socket);
447     this->server_socket = NULL;
448 
449     g_atomic_int_set (&this->current_port, 0);
450     g_object_notify (G_OBJECT (this), "current-port");
451   }
452 
453   return TRUE;
454 }
455