2c9fa6ef0b1fd6fc4d1658918ddc6c720d2efb84
[platform/upstream/gstreamer.git] / gst / pcapparse / gstpcapparse.c
1 /*
2  * Copyright 2007 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
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 GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-pcapparse
22  *
23  * Extracts payloads from Ethernet-encapsulated IP packets.
24  * Use #GstPcapParse:src-ip, #GstPcapParse:dst-ip,
25  * #GstPcapParse:src-port and #GstPcapParse:dst-port to restrict which packets
26  * should be included.
27  *
28  * <refsect2>
29  * <title>Example pipelines</title>
30  * |[
31  * gst-launch-0.10 filesrc location=h264crasher.pcap ! pcapparse ! rtph264depay
32  * ! ffdec_h264 ! fakesink
33  * ]| Read from a pcap dump file using filesrc, extract the raw UDP packets,
34  * depayload and decode them.
35  * </refsect2>
36  */
37
38 /* TODO:
39  * - React on state-change and update state accordingly.
40  * - Implement support for timestamping the buffers.
41  */
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #include "gstpcapparse.h"
48
49 #include <string.h>
50
51 #ifndef G_OS_WIN32
52 #include <arpa/inet.h>
53 #include <netinet/in.h>
54 #include <string.h>
55 #else
56 #include <winsock2.h>
57 #endif
58
59 enum
60 {
61   PROP_0,
62   PROP_SRC_IP,
63   PROP_DST_IP,
64   PROP_SRC_PORT,
65   PROP_DST_PORT,
66   PROP_CAPS,
67   PROP_TS_OFFSET,
68   PROP_LAST
69 };
70
71 GST_DEBUG_CATEGORY_STATIC (gst_pcap_parse_debug);
72 #define GST_CAT_DEFAULT gst_pcap_parse_debug
73
74 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
75     GST_PAD_SINK,
76     GST_PAD_ALWAYS,
77     GST_STATIC_CAPS ("raw/x-pcap"));
78
79 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
80     GST_PAD_SRC,
81     GST_PAD_ALWAYS,
82     GST_STATIC_CAPS_ANY);
83
84 static void gst_pcap_parse_finalize (GObject * object);
85 static void gst_pcap_parse_get_property (GObject * object, guint prop_id,
86     GValue * value, GParamSpec * pspec);
87 static void gst_pcap_parse_set_property (GObject * object, guint prop_id,
88     const GValue * value, GParamSpec * pspec);
89
90 static void gst_pcap_parse_reset (GstPcapParse * self);
91
92 static GstFlowReturn gst_pcap_parse_chain (GstPad * pad,
93     GstObject * parent, GstBuffer * buffer);
94 static gboolean gst_pcap_sink_event (GstPad * pad,
95     GstObject * parent, GstEvent * event);
96
97 #define parent_class gst_pcap_parse_parent_class
98 G_DEFINE_TYPE (GstPcapParse, gst_pcap_parse, GST_TYPE_ELEMENT);
99
100 static void
101 gst_pcap_parse_class_init (GstPcapParseClass * klass)
102 {
103   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
105
106   gobject_class->finalize = gst_pcap_parse_finalize;
107   gobject_class->get_property = gst_pcap_parse_get_property;
108   gobject_class->set_property = gst_pcap_parse_set_property;
109
110   g_object_class_install_property (gobject_class,
111       PROP_SRC_IP, g_param_spec_string ("src-ip", "Source IP",
112           "Source IP to restrict to", "",
113           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
114
115   g_object_class_install_property (gobject_class,
116       PROP_DST_IP, g_param_spec_string ("dst-ip", "Destination IP",
117           "Destination IP to restrict to", "",
118           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
119
120   g_object_class_install_property (gobject_class,
121       PROP_SRC_PORT, g_param_spec_int ("src-port", "Source port",
122           "Source port to restrict to", -1, G_MAXUINT16, -1,
123           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
124
125   g_object_class_install_property (gobject_class,
126       PROP_DST_PORT, g_param_spec_int ("dst-port", "Destination port",
127           "Destination port to restrict to", -1, G_MAXUINT16, -1,
128           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
129
130   g_object_class_install_property (gobject_class, PROP_CAPS,
131       g_param_spec_boxed ("caps", "Caps",
132           "The caps of the source pad", GST_TYPE_CAPS,
133           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
134
135   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
136       g_param_spec_int64 ("ts-offset", "Timestamp Offset",
137           "Relative timestamp offset (ns) to apply (-1 = use absolute packet time)",
138           -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139
140   gst_element_class_add_pad_template (element_class,
141       gst_static_pad_template_get (&sink_template));
142   gst_element_class_add_pad_template (element_class,
143       gst_static_pad_template_get (&src_template));
144
145   gst_element_class_set_details_simple (element_class, "PCapParse",
146       "Raw/Parser",
147       "Parses a raw pcap stream",
148       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
149
150   GST_DEBUG_CATEGORY_INIT (gst_pcap_parse_debug, "pcapparse", 0, "pcap parser");
151 }
152
153 static void
154 gst_pcap_parse_init (GstPcapParse * self)
155 {
156   self->sink_pad = gst_pad_new_from_static_template (&sink_template, "sink");
157   gst_pad_set_chain_function (self->sink_pad,
158       GST_DEBUG_FUNCPTR (gst_pcap_parse_chain));
159   gst_pad_use_fixed_caps (self->sink_pad);
160   gst_pad_set_event_function (self->sink_pad,
161       GST_DEBUG_FUNCPTR (gst_pcap_sink_event));
162   gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
163
164   self->src_pad = gst_pad_new_from_static_template (&src_template, "src");
165   gst_pad_use_fixed_caps (self->src_pad);
166   gst_element_add_pad (GST_ELEMENT (self), self->src_pad);
167
168   self->src_ip = -1;
169   self->dst_ip = -1;
170   self->src_port = -1;
171   self->dst_port = -1;
172   self->offset = -1;
173
174   self->adapter = gst_adapter_new ();
175
176   gst_pcap_parse_reset (self);
177 }
178
179 static void
180 gst_pcap_parse_finalize (GObject * object)
181 {
182   GstPcapParse *self = GST_PCAP_PARSE (object);
183
184   g_object_unref (self->adapter);
185   if (self->caps)
186     gst_caps_unref (self->caps);
187
188   G_OBJECT_CLASS (parent_class)->finalize (object);
189 }
190
191 static const gchar *
192 get_ip_address_as_string (gint64 ip_addr)
193 {
194   if (ip_addr >= 0) {
195     struct in_addr addr;
196     addr.s_addr = ip_addr;
197     return inet_ntoa (addr);
198   } else {
199     return "";
200   }
201 }
202
203 static void
204 set_ip_address_from_string (gint64 * ip_addr, const gchar * ip_str)
205 {
206   if (ip_str[0] != '\0') {
207     gulong addr = inet_addr (ip_str);
208     if (addr != INADDR_NONE)
209       *ip_addr = addr;
210   } else {
211     *ip_addr = -1;
212   }
213 }
214
215 static void
216 gst_pcap_parse_get_property (GObject * object, guint prop_id,
217     GValue * value, GParamSpec * pspec)
218 {
219   GstPcapParse *self = GST_PCAP_PARSE (object);
220
221   switch (prop_id) {
222     case PROP_SRC_IP:
223       g_value_set_string (value, get_ip_address_as_string (self->src_ip));
224       break;
225
226     case PROP_DST_IP:
227       g_value_set_string (value, get_ip_address_as_string (self->dst_ip));
228       break;
229
230     case PROP_SRC_PORT:
231       g_value_set_int (value, self->src_port);
232       break;
233
234     case PROP_DST_PORT:
235       g_value_set_int (value, self->dst_port);
236       break;
237
238     case PROP_CAPS:
239       gst_value_set_caps (value, self->caps);
240       break;
241
242     case PROP_TS_OFFSET:
243       g_value_set_int64 (value, self->offset);
244       break;
245
246     default:
247       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248       break;
249   }
250 }
251
252 static void
253 gst_pcap_parse_set_property (GObject * object, guint prop_id,
254     const GValue * value, GParamSpec * pspec)
255 {
256   GstPcapParse *self = GST_PCAP_PARSE (object);
257
258   switch (prop_id) {
259     case PROP_SRC_IP:
260       set_ip_address_from_string (&self->src_ip, g_value_get_string (value));
261       break;
262
263     case PROP_DST_IP:
264       set_ip_address_from_string (&self->dst_ip, g_value_get_string (value));
265       break;
266
267     case PROP_SRC_PORT:
268       self->src_port = g_value_get_int (value);
269       break;
270
271     case PROP_DST_PORT:
272       self->dst_port = g_value_get_int (value);
273       break;
274
275     case PROP_CAPS:
276     {
277       const GstCaps *new_caps_val;
278       GstCaps *new_caps, *old_caps;
279
280       new_caps_val = gst_value_get_caps (value);
281       if (new_caps_val == NULL) {
282         new_caps = gst_caps_new_any ();
283       } else {
284         new_caps = gst_caps_copy (new_caps_val);
285       }
286
287       old_caps = self->caps;
288       self->caps = new_caps;
289       if (old_caps)
290         gst_caps_unref (old_caps);
291
292       gst_pad_set_caps (self->src_pad, new_caps);
293       break;
294     }
295
296     case PROP_TS_OFFSET:
297       self->offset = g_value_get_int64 (value);
298       break;
299
300     default:
301       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
302       break;
303   }
304 }
305
306 static void
307 gst_pcap_parse_reset (GstPcapParse * self)
308 {
309   self->initialized = FALSE;
310   self->swap_endian = FALSE;
311   self->cur_packet_size = -1;
312   self->buffer_offset = 0;
313   self->cur_ts = GST_CLOCK_TIME_NONE;
314   self->base_ts = GST_CLOCK_TIME_NONE;
315   self->newsegment_sent = FALSE;
316
317   gst_adapter_clear (self->adapter);
318 }
319
320 static guint32
321 gst_pcap_parse_read_uint32 (GstPcapParse * self, const guint8 * p)
322 {
323   guint32 val = *((guint32 *) p);
324
325   if (self->swap_endian) {
326 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
327     return GUINT32_FROM_BE (val);
328 #else
329     return GUINT32_FROM_LE (val);
330 #endif
331   } else {
332     return val;
333   }
334 }
335
336 #define ETH_HEADER_LEN    14
337 #define SLL_HEADER_LEN    16
338 #define IP_HEADER_MIN_LEN 20
339 #define UDP_HEADER_LEN     8
340
341 #define IP_PROTO_UDP      17
342 #define IP_PROTO_TCP      6
343
344
345 static gboolean
346 gst_pcap_parse_scan_frame (GstPcapParse * self,
347     const guint8 * buf,
348     gint buf_size, const guint8 ** payload, gint * payload_size)
349 {
350   const guint8 *buf_ip = 0;
351   const guint8 *buf_proto;
352   guint16 eth_type;
353   guint8 b;
354   guint8 ip_header_size;
355   guint8 ip_protocol;
356   guint32 ip_src_addr;
357   guint32 ip_dst_addr;
358   guint16 src_port;
359   guint16 dst_port;
360   guint16 len;
361
362   switch (self->linktype) {
363     case DLT_ETHER:
364       if (buf_size < ETH_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
365         return FALSE;
366
367       eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + 12)));
368       buf_ip = buf + ETH_HEADER_LEN;
369       break;
370     case DLT_SLL:
371       if (buf_size < SLL_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
372         return FALSE;
373
374       eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + 14)));
375       buf_ip = buf + SLL_HEADER_LEN;
376       break;
377     default:
378       return FALSE;
379   }
380
381   if (eth_type != 0x800)
382     return FALSE;
383
384   b = *buf_ip;
385   if (((b >> 4) & 0x0f) != 4)
386     return FALSE;
387
388   ip_header_size = (b & 0x0f) * 4;
389   if (buf_ip + ip_header_size > buf + buf_size)
390     return FALSE;
391
392   ip_protocol = *(buf_ip + 9);
393   GST_LOG_OBJECT (self, "ip proto %d", (gint) ip_protocol);
394
395   if (ip_protocol != IP_PROTO_UDP && ip_protocol != IP_PROTO_TCP)
396     return FALSE;
397
398   /* ip info */
399   ip_src_addr = *((guint32 *) (buf_ip + 12));
400   ip_dst_addr = *((guint32 *) (buf_ip + 16));
401   buf_proto = buf_ip + ip_header_size;
402
403   /* ok for tcp and udp */
404   src_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 0)));
405   dst_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 2)));
406
407   /* extract some params and data according to protocol */
408   if (ip_protocol == IP_PROTO_UDP) {
409     len = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 4)));
410     if (len < UDP_HEADER_LEN || buf_proto + len > buf + buf_size)
411       return FALSE;
412
413     *payload = buf_proto + UDP_HEADER_LEN;
414     *payload_size = len - UDP_HEADER_LEN;
415   } else {
416     if (buf_proto + 12 >= buf + buf_size)
417       return FALSE;
418     len = (buf_proto[12] >> 4) * 4;
419     if (buf_proto + len > buf + buf_size)
420       return FALSE;
421
422     /* all remaining data following tcp header is payload */
423     *payload = buf_proto + len;
424     *payload_size = self->cur_packet_size - (buf_proto - buf) - len;
425   }
426
427   /* but still filter as configured */
428   if (self->src_ip >= 0 && ip_src_addr != self->src_ip)
429     return FALSE;
430
431   if (self->dst_ip >= 0 && ip_dst_addr != self->dst_ip)
432     return FALSE;
433
434   if (self->src_port >= 0 && src_port != self->src_port)
435     return FALSE;
436
437   if (self->dst_port >= 0 && dst_port != self->dst_port)
438     return FALSE;
439
440   return TRUE;
441 }
442
443 static GstFlowReturn
444 gst_pcap_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
445 {
446   GstPcapParse *self = GST_PCAP_PARSE (parent);
447   GstFlowReturn ret = GST_FLOW_OK;
448
449   gst_adapter_push (self->adapter, buffer);
450
451   while (ret == GST_FLOW_OK) {
452     gint avail;
453     const guint8 *data;
454
455     avail = gst_adapter_available (self->adapter);
456
457     if (self->initialized) {
458       if (self->cur_packet_size >= 0) {
459         if (avail < self->cur_packet_size)
460           break;
461
462         if (self->cur_packet_size > 0) {
463           const guint8 *payload_data;
464           gint payload_size;
465
466           data = gst_adapter_map (self->adapter, self->cur_packet_size);
467
468           GST_LOG_OBJECT (self, "examining packet size %" G_GINT64_FORMAT,
469               self->cur_packet_size);
470
471           if (gst_pcap_parse_scan_frame (self, data, self->cur_packet_size,
472                   &payload_data, &payload_size)) {
473             GstBuffer *out_buf;
474             guint8 *data;
475
476             out_buf = gst_buffer_new_and_alloc (payload_size);
477             if (out_buf) {
478
479               if (GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
480                 if (!GST_CLOCK_TIME_IS_VALID (self->base_ts))
481                   self->base_ts = self->cur_ts;
482                 if (self->offset >= 0) {
483                   self->cur_ts -= self->base_ts;
484                   self->cur_ts += self->offset;
485                 }
486               }
487
488               data = gst_buffer_map (out_buf, NULL, NULL, GST_MAP_WRITE);
489               memcpy (data, payload_data, payload_size);
490               gst_buffer_unmap (out_buf, data, -1);
491               GST_BUFFER_TIMESTAMP (out_buf) = self->cur_ts;
492
493               if (!self->newsegment_sent &&
494                   GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
495                 GstSegment segment;
496
497                 gst_pad_set_caps (self->src_pad, self->caps);
498                 gst_segment_init (&segment, GST_FORMAT_TIME);
499                 segment.start = self->cur_ts;
500                 gst_pad_push_event (self->src_pad,
501                     gst_event_new_segment (&segment));
502                 self->newsegment_sent = TRUE;
503               }
504
505               ret = gst_pad_push (self->src_pad, out_buf);
506
507               self->buffer_offset += payload_size;
508             }
509           }
510
511           gst_adapter_unmap (self->adapter);
512           gst_adapter_flush (self->adapter, self->cur_packet_size);
513         }
514
515         self->cur_packet_size = -1;
516       } else {
517         guint32 ts_sec;
518         guint32 ts_usec;
519         guint32 incl_len;
520
521         if (avail < 16)
522           break;
523
524         data = gst_adapter_map (self->adapter, 16);
525
526         ts_sec = gst_pcap_parse_read_uint32 (self, data + 0);
527         ts_usec = gst_pcap_parse_read_uint32 (self, data + 4);
528         incl_len = gst_pcap_parse_read_uint32 (self, data + 8);
529         /* orig_len = gst_pcap_parse_read_uint32 (self, data + 12); */
530
531         gst_adapter_unmap (self->adapter);
532         gst_adapter_flush (self->adapter, 16);
533
534         self->cur_ts = ts_sec * GST_SECOND + ts_usec * GST_USECOND;
535         self->cur_packet_size = incl_len;
536       }
537     } else {
538       guint32 magic;
539       guint32 linktype;
540       guint16 major_version;
541
542       if (avail < 24)
543         break;
544
545       data = gst_adapter_map (self->adapter, 24);
546
547       magic = *((guint32 *) data);
548       major_version = *((guint16 *) (data + 4));
549       linktype = gst_pcap_parse_read_uint32 (self, data + 20);
550       gst_adapter_unmap (self->adapter);
551
552       if (magic == 0xa1b2c3d4) {
553         self->swap_endian = FALSE;
554       } else if (magic == 0xd4c3b2a1) {
555         self->swap_endian = TRUE;
556         major_version = major_version << 8 | major_version >> 8;
557       } else {
558         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
559             ("File is not a libpcap file, magic is %X", magic));
560         ret = GST_FLOW_ERROR;
561         goto out;
562       }
563
564       if (major_version != 2) {
565         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
566             ("File is not a libpcap major version 2, but %u", major_version));
567         ret = GST_FLOW_ERROR;
568         goto out;
569       }
570
571       if (linktype != DLT_ETHER && linktype != DLT_SLL) {
572         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
573             ("Only dumps of type Ethernet or Linux Coooked (SLL) understood,"
574                 " type %d unknown", linktype));
575         ret = GST_FLOW_ERROR;
576         goto out;
577       }
578
579       GST_DEBUG_OBJECT (self, "linktype %u", linktype);
580       self->linktype = linktype;
581
582       gst_adapter_flush (self->adapter, 24);
583       self->initialized = TRUE;
584     }
585   }
586
587 out:
588   if (ret != GST_FLOW_OK)
589     gst_pcap_parse_reset (self);
590
591   return ret;
592 }
593
594 static gboolean
595 gst_pcap_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
596 {
597   gboolean ret = TRUE;
598   GstPcapParse *self = GST_PCAP_PARSE (parent);
599
600   switch (GST_EVENT_TYPE (event)) {
601     case GST_EVENT_SEGMENT:
602       /* Drop it, we'll replace it with our own */
603       gst_event_unref (event);
604       break;
605     default:
606       ret = gst_pad_push_event (self->src_pad, event);
607       break;
608   }
609
610   gst_object_unref (self);
611
612   return ret;
613 }