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., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-pcapparse
22  * @title: pcapparse
23  *
24  * Extracts payloads from Ethernet-encapsulated IP packets.
25  * Use #GstPcapParse:src-ip, #GstPcapParse:dst-ip,
26  * #GstPcapParse:src-port and #GstPcapParse:dst-port to restrict which packets
27  * should be included.
28  *
29  * The supported data format is the classical <ulink
30  * url="https://wiki.wireshark.org/Development/LibpcapFileFormat">libpcap file
31  * format</ulink>.
32  *
33  * ## Example pipelines
34  * |[
35  * gst-launch-1.0 filesrc location=h264crasher.pcap ! pcapparse ! rtph264depay
36  * ! ffdec_h264 ! fakesink
37  * ]| Read from a pcap dump file using filesrc, extract the raw UDP packets,
38  * depayload and decode them.
39  *
40  */
41 
42 /* TODO:
43  * - Implement support for timestamping the buffers.
44  */
45 
46 #ifdef HAVE_CONFIG_H
47 #include <config.h>
48 #endif
49 
50 #include "gstpcapparse.h"
51 
52 #include <string.h>
53 
54 #ifndef G_OS_WIN32
55 #include <arpa/inet.h>
56 #include <netinet/in.h>
57 #include <string.h>
58 #else
59 #include <winsock2.h>
60 #endif
61 
62 
63 const guint GST_PCAPPARSE_MAGIC_MILLISECOND_NO_SWAP_ENDIAN = 0xa1b2c3d4;
64 const guint GST_PCAPPARSE_MAGIC_NANOSECOND_NO_SWAP_ENDIAN = 0xa1b23c4d;
65 const guint GST_PCAPPARSE_MAGIC_MILLISECOND_SWAP_ENDIAN = 0xd4c3b2a1;
66 const guint GST_PCAPPARSE_MAGIC_NANOSECOND_SWAP_ENDIAN = 0x4d3cb2a1;
67 
68 
69 enum
70 {
71   PROP_0,
72   PROP_SRC_IP,
73   PROP_DST_IP,
74   PROP_SRC_PORT,
75   PROP_DST_PORT,
76   PROP_CAPS,
77   PROP_TS_OFFSET
78 };
79 
80 GST_DEBUG_CATEGORY_STATIC (gst_pcap_parse_debug);
81 #define GST_CAT_DEFAULT gst_pcap_parse_debug
82 
83 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
84     GST_PAD_SINK,
85     GST_PAD_ALWAYS,
86     GST_STATIC_CAPS ("raw/x-pcap"));
87 
88 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
89     GST_PAD_SRC,
90     GST_PAD_ALWAYS,
91     GST_STATIC_CAPS_ANY);
92 
93 static void gst_pcap_parse_finalize (GObject * object);
94 static void gst_pcap_parse_get_property (GObject * object, guint prop_id,
95     GValue * value, GParamSpec * pspec);
96 static void gst_pcap_parse_set_property (GObject * object, guint prop_id,
97     const GValue * value, GParamSpec * pspec);
98 static GstStateChangeReturn
99 gst_pcap_parse_change_state (GstElement * element, GstStateChange transition);
100 
101 static void gst_pcap_parse_reset (GstPcapParse * self);
102 
103 static GstFlowReturn gst_pcap_parse_chain (GstPad * pad,
104     GstObject * parent, GstBuffer * buffer);
105 static gboolean gst_pcap_sink_event (GstPad * pad,
106     GstObject * parent, GstEvent * event);
107 
108 
109 #define parent_class gst_pcap_parse_parent_class
110 G_DEFINE_TYPE (GstPcapParse, gst_pcap_parse, GST_TYPE_ELEMENT);
111 
112 static void
gst_pcap_parse_class_init(GstPcapParseClass * klass)113 gst_pcap_parse_class_init (GstPcapParseClass * klass)
114 {
115   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
116   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
117 
118   gobject_class->finalize = gst_pcap_parse_finalize;
119   gobject_class->get_property = gst_pcap_parse_get_property;
120   gobject_class->set_property = gst_pcap_parse_set_property;
121 
122   g_object_class_install_property (gobject_class,
123       PROP_SRC_IP, g_param_spec_string ("src-ip", "Source IP",
124           "Source IP to restrict to", "",
125           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
126 
127   g_object_class_install_property (gobject_class,
128       PROP_DST_IP, g_param_spec_string ("dst-ip", "Destination IP",
129           "Destination IP to restrict to", "",
130           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
131 
132   g_object_class_install_property (gobject_class,
133       PROP_SRC_PORT, g_param_spec_int ("src-port", "Source port",
134           "Source port to restrict to", -1, G_MAXUINT16, -1,
135           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
136 
137   g_object_class_install_property (gobject_class,
138       PROP_DST_PORT, g_param_spec_int ("dst-port", "Destination port",
139           "Destination port to restrict to", -1, G_MAXUINT16, -1,
140           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
141 
142   g_object_class_install_property (gobject_class, PROP_CAPS,
143       g_param_spec_boxed ("caps", "Caps",
144           "The caps of the source pad", GST_TYPE_CAPS,
145           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
146 
147   g_object_class_install_property (gobject_class, PROP_TS_OFFSET,
148       g_param_spec_int64 ("ts-offset", "Timestamp Offset",
149           "Relative timestamp offset (ns) to apply (-1 = use absolute packet time)",
150           -1, G_MAXINT64, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 
152   gst_element_class_add_static_pad_template (element_class, &sink_template);
153   gst_element_class_add_static_pad_template (element_class, &src_template);
154 
155   element_class->change_state = gst_pcap_parse_change_state;
156 
157   gst_element_class_set_static_metadata (element_class, "PCapParse",
158       "Raw/Parser",
159       "Parses a raw pcap stream",
160       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
161 
162   GST_DEBUG_CATEGORY_INIT (gst_pcap_parse_debug, "pcapparse", 0, "pcap parser");
163 }
164 
165 static void
gst_pcap_parse_init(GstPcapParse * self)166 gst_pcap_parse_init (GstPcapParse * self)
167 {
168   self->sink_pad = gst_pad_new_from_static_template (&sink_template, "sink");
169   gst_pad_set_chain_function (self->sink_pad,
170       GST_DEBUG_FUNCPTR (gst_pcap_parse_chain));
171   gst_pad_use_fixed_caps (self->sink_pad);
172   gst_pad_set_event_function (self->sink_pad,
173       GST_DEBUG_FUNCPTR (gst_pcap_sink_event));
174   gst_element_add_pad (GST_ELEMENT (self), self->sink_pad);
175 
176   self->src_pad = gst_pad_new_from_static_template (&src_template, "src");
177   gst_pad_use_fixed_caps (self->src_pad);
178   gst_element_add_pad (GST_ELEMENT (self), self->src_pad);
179 
180   self->src_ip = -1;
181   self->dst_ip = -1;
182   self->src_port = -1;
183   self->dst_port = -1;
184   self->offset = -1;
185 
186   self->adapter = gst_adapter_new ();
187 
188   gst_pcap_parse_reset (self);
189 }
190 
191 static void
gst_pcap_parse_finalize(GObject * object)192 gst_pcap_parse_finalize (GObject * object)
193 {
194   GstPcapParse *self = GST_PCAP_PARSE (object);
195 
196   g_object_unref (self->adapter);
197   if (self->caps)
198     gst_caps_unref (self->caps);
199 
200   G_OBJECT_CLASS (parent_class)->finalize (object);
201 }
202 
203 static const gchar *
get_ip_address_as_string(gint64 ip_addr)204 get_ip_address_as_string (gint64 ip_addr)
205 {
206   if (ip_addr >= 0) {
207     struct in_addr addr;
208     addr.s_addr = ip_addr;
209     return inet_ntoa (addr);
210   } else {
211     return "";
212   }
213 }
214 
215 static void
set_ip_address_from_string(gint64 * ip_addr,const gchar * ip_str)216 set_ip_address_from_string (gint64 * ip_addr, const gchar * ip_str)
217 {
218   if (ip_str[0] != '\0') {
219     gulong addr = inet_addr (ip_str);
220     if (addr != INADDR_NONE)
221       *ip_addr = addr;
222   } else {
223     *ip_addr = -1;
224   }
225 }
226 
227 static void
gst_pcap_parse_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)228 gst_pcap_parse_get_property (GObject * object, guint prop_id,
229     GValue * value, GParamSpec * pspec)
230 {
231   GstPcapParse *self = GST_PCAP_PARSE (object);
232 
233   switch (prop_id) {
234     case PROP_SRC_IP:
235       g_value_set_string (value, get_ip_address_as_string (self->src_ip));
236       break;
237 
238     case PROP_DST_IP:
239       g_value_set_string (value, get_ip_address_as_string (self->dst_ip));
240       break;
241 
242     case PROP_SRC_PORT:
243       g_value_set_int (value, self->src_port);
244       break;
245 
246     case PROP_DST_PORT:
247       g_value_set_int (value, self->dst_port);
248       break;
249 
250     case PROP_CAPS:
251       gst_value_set_caps (value, self->caps);
252       break;
253 
254     case PROP_TS_OFFSET:
255       g_value_set_int64 (value, self->offset);
256       break;
257 
258     default:
259       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
260       break;
261   }
262 }
263 
264 static void
gst_pcap_parse_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)265 gst_pcap_parse_set_property (GObject * object, guint prop_id,
266     const GValue * value, GParamSpec * pspec)
267 {
268   GstPcapParse *self = GST_PCAP_PARSE (object);
269 
270   switch (prop_id) {
271     case PROP_SRC_IP:
272       set_ip_address_from_string (&self->src_ip, g_value_get_string (value));
273       break;
274 
275     case PROP_DST_IP:
276       set_ip_address_from_string (&self->dst_ip, g_value_get_string (value));
277       break;
278 
279     case PROP_SRC_PORT:
280       self->src_port = g_value_get_int (value);
281       break;
282 
283     case PROP_DST_PORT:
284       self->dst_port = g_value_get_int (value);
285       break;
286 
287     case PROP_CAPS:
288     {
289       const GstCaps *new_caps_val;
290       GstCaps *new_caps, *old_caps;
291 
292       new_caps_val = gst_value_get_caps (value);
293       if (new_caps_val == NULL) {
294         new_caps = gst_caps_new_any ();
295       } else {
296         new_caps = gst_caps_copy (new_caps_val);
297       }
298 
299       old_caps = self->caps;
300       self->caps = new_caps;
301       if (old_caps)
302         gst_caps_unref (old_caps);
303 
304       gst_pad_set_caps (self->src_pad, new_caps);
305       break;
306     }
307 
308     case PROP_TS_OFFSET:
309       self->offset = g_value_get_int64 (value);
310       break;
311 
312     default:
313       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
314       break;
315   }
316 }
317 
318 static void
gst_pcap_parse_reset(GstPcapParse * self)319 gst_pcap_parse_reset (GstPcapParse * self)
320 {
321   self->initialized = FALSE;
322   self->swap_endian = FALSE;
323   self->nanosecond_timestamp = FALSE;
324   self->cur_packet_size = -1;
325   self->cur_ts = GST_CLOCK_TIME_NONE;
326   self->base_ts = GST_CLOCK_TIME_NONE;
327   self->newsegment_sent = FALSE;
328 
329   gst_adapter_clear (self->adapter);
330 }
331 
332 static guint32
gst_pcap_parse_read_uint32(GstPcapParse * self,const guint8 * p)333 gst_pcap_parse_read_uint32 (GstPcapParse * self, const guint8 * p)
334 {
335   guint32 val = *((guint32 *) p);
336 
337   if (self->swap_endian) {
338 #if G_BYTE_ORDER == G_LITTLE_ENDIAN
339     return GUINT32_FROM_BE (val);
340 #else
341     return GUINT32_FROM_LE (val);
342 #endif
343   } else {
344     return val;
345   }
346 }
347 
348 #define ETH_MAC_ADDRESSES_LEN    12
349 #define ETH_HEADER_LEN    14
350 #define ETH_VLAN_HEADER_LEN    4
351 #define SLL_HEADER_LEN    16
352 #define IP_HEADER_MIN_LEN 20
353 #define UDP_HEADER_LEN     8
354 
355 #define IP_PROTO_UDP      17
356 #define IP_PROTO_TCP      6
357 
358 
359 static gboolean
gst_pcap_parse_scan_frame(GstPcapParse * self,const guint8 * buf,gint buf_size,const guint8 ** payload,gint * payload_size)360 gst_pcap_parse_scan_frame (GstPcapParse * self,
361     const guint8 * buf,
362     gint buf_size, const guint8 ** payload, gint * payload_size)
363 {
364   const guint8 *buf_ip = 0;
365   const guint8 *buf_proto;
366   guint16 eth_type;
367   guint8 b;
368   guint8 ip_header_size;
369   guint8 flags;
370   guint16 fragment_offset;
371   guint8 ip_protocol;
372   guint32 ip_src_addr;
373   guint32 ip_dst_addr;
374   guint16 src_port;
375   guint16 dst_port;
376   guint16 len;
377 
378   switch (self->linktype) {
379     case LINKTYPE_ETHER:
380       if (buf_size < ETH_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
381         return FALSE;
382       eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + ETH_MAC_ADDRESSES_LEN)));
383       /* check for vlan 802.1q header (4 bytes, with first two bytes equal to 0x8100)  */
384       if (eth_type == 0x8100) {
385         if (buf_size <
386             ETH_HEADER_LEN + ETH_VLAN_HEADER_LEN + IP_HEADER_MIN_LEN +
387             UDP_HEADER_LEN)
388           return FALSE;
389         eth_type =
390             GUINT16_FROM_BE (*((guint16 *) (buf + ETH_MAC_ADDRESSES_LEN +
391                     ETH_VLAN_HEADER_LEN)));
392         buf_ip = buf + ETH_HEADER_LEN + ETH_VLAN_HEADER_LEN;
393       } else {
394         buf_ip = buf + ETH_HEADER_LEN;
395       }
396       break;
397     case LINKTYPE_SLL:
398       if (buf_size < SLL_HEADER_LEN + IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
399         return FALSE;
400 
401       eth_type = GUINT16_FROM_BE (*((guint16 *) (buf + 14)));
402       buf_ip = buf + SLL_HEADER_LEN;
403       break;
404     case LINKTYPE_RAW:
405       if (buf_size < IP_HEADER_MIN_LEN + UDP_HEADER_LEN)
406         return FALSE;
407 
408       eth_type = 0x800;         /* This is fine since IPv4/IPv6 is parse elsewhere */
409       buf_ip = buf;
410       break;
411 
412     default:
413       return FALSE;
414   }
415 
416   if (eth_type != 0x800) {
417     GST_ERROR_OBJECT (self,
418         "Link type %d: Ethernet type %d is not supported; only type 0x800",
419         (gint) self->linktype, (gint) eth_type);
420     return FALSE;
421   }
422 
423   b = *buf_ip;
424 
425   /* Check that the packet is IPv4 */
426   if (((b >> 4) & 0x0f) != 4)
427     return FALSE;
428 
429   ip_header_size = (b & 0x0f) * 4;
430   if (buf_ip + ip_header_size > buf + buf_size)
431     return FALSE;
432 
433   flags = buf_ip[6] >> 5;
434   fragment_offset =
435       (GUINT16_FROM_BE (*((guint16 *) (buf_ip + 6))) & 0x1fff) * 8;
436   if (flags & 0x1 || fragment_offset > 0) {
437     GST_ERROR_OBJECT (self, "Fragmented packets are not supported");
438     return FALSE;
439   }
440 
441   ip_protocol = *(buf_ip + 9);
442   GST_LOG_OBJECT (self, "ip proto %d", (gint) ip_protocol);
443 
444   if (ip_protocol != IP_PROTO_UDP && ip_protocol != IP_PROTO_TCP)
445     return FALSE;
446 
447   /* ip info */
448   ip_src_addr = *((guint32 *) (buf_ip + 12));
449   ip_dst_addr = *((guint32 *) (buf_ip + 16));
450   buf_proto = buf_ip + ip_header_size;
451 
452   /* ok for tcp and udp */
453   src_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 0)));
454   dst_port = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 2)));
455 
456   /* extract some params and data according to protocol */
457   if (ip_protocol == IP_PROTO_UDP) {
458     len = GUINT16_FROM_BE (*((guint16 *) (buf_proto + 4)));
459     if (len < UDP_HEADER_LEN || buf_proto + len > buf + buf_size)
460       return FALSE;
461 
462     *payload = buf_proto + UDP_HEADER_LEN;
463     *payload_size = len - UDP_HEADER_LEN;
464   } else {
465     if (buf_proto + 12 >= buf + buf_size)
466       return FALSE;
467     len = (buf_proto[12] >> 4) * 4;
468     if (buf_proto + len > buf + buf_size)
469       return FALSE;
470 
471     /* all remaining data following tcp header is payload */
472     *payload = buf_proto + len;
473     *payload_size = self->cur_packet_size - (buf_proto - buf) - len;
474   }
475 
476   /* but still filter as configured */
477   if (self->src_ip >= 0 && ip_src_addr != self->src_ip)
478     return FALSE;
479 
480   if (self->dst_ip >= 0 && ip_dst_addr != self->dst_ip)
481     return FALSE;
482 
483   if (self->src_port >= 0 && src_port != self->src_port)
484     return FALSE;
485 
486   if (self->dst_port >= 0 && dst_port != self->dst_port)
487     return FALSE;
488 
489   return TRUE;
490 }
491 
492 static GstFlowReturn
gst_pcap_parse_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)493 gst_pcap_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
494 {
495   GstPcapParse *self = GST_PCAP_PARSE (parent);
496   GstFlowReturn ret = GST_FLOW_OK;
497   GstBufferList *list = NULL;
498 
499   gst_adapter_push (self->adapter, buffer);
500 
501   while (ret == GST_FLOW_OK) {
502     gint avail;
503     const guint8 *data;
504 
505     avail = gst_adapter_available (self->adapter);
506 
507     if (self->initialized) {
508       if (self->cur_packet_size >= 0) {
509         /* Parse the Packet Data */
510         if (avail < self->cur_packet_size)
511           break;
512 
513         if (self->cur_packet_size > 0) {
514           const guint8 *payload_data;
515           gint payload_size;
516 
517           data = gst_adapter_map (self->adapter, self->cur_packet_size);
518 
519           GST_LOG_OBJECT (self, "examining packet size %" G_GINT64_FORMAT,
520               self->cur_packet_size);
521 
522           if (gst_pcap_parse_scan_frame (self, data, self->cur_packet_size,
523                   &payload_data, &payload_size)) {
524             GstBuffer *out_buf;
525             guintptr offset = payload_data - data;
526 
527             gst_adapter_unmap (self->adapter);
528             gst_adapter_flush (self->adapter, offset);
529             /* we don't use _take_buffer_fast() on purpose here, we need a
530              * buffer with a single memory, since the RTP depayloaders expect
531              * the complete RTP header to be in the first memory if there are
532              * multiple ones and we can't guarantee that with _fast() */
533             if (payload_size > 0) {
534               out_buf = gst_adapter_take_buffer (self->adapter, payload_size);
535             } else {
536               out_buf = gst_buffer_new ();
537             }
538             gst_adapter_flush (self->adapter,
539                 self->cur_packet_size - offset - payload_size);
540 
541             if (GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
542               if (!GST_CLOCK_TIME_IS_VALID (self->base_ts))
543                 self->base_ts = self->cur_ts;
544               if (self->offset >= 0) {
545                 self->cur_ts -= self->base_ts;
546                 self->cur_ts += self->offset;
547               }
548             }
549             GST_BUFFER_TIMESTAMP (out_buf) = self->cur_ts;
550 
551 
552             if (list == NULL)
553               list = gst_buffer_list_new ();
554             gst_buffer_list_add (list, out_buf);
555           } else {
556             gst_adapter_unmap (self->adapter);
557             gst_adapter_flush (self->adapter, self->cur_packet_size);
558           }
559         }
560 
561         self->cur_packet_size = -1;
562       } else {
563         /* Parse the Record (Packet) Header */
564         guint32 ts_sec;
565         guint32 ts_usec;
566         guint32 incl_len;
567 
568         /* sizeof(pcaprec_hdr_t) == 16 */
569         if (avail < 16)
570           break;
571 
572         data = gst_adapter_map (self->adapter, 16);
573 
574         ts_sec = gst_pcap_parse_read_uint32 (self, data + 0);
575         ts_usec = gst_pcap_parse_read_uint32 (self, data + 4);
576         incl_len = gst_pcap_parse_read_uint32 (self, data + 8);
577         /* orig_len = gst_pcap_parse_read_uint32 (self, data + 12); */
578 
579         gst_adapter_unmap (self->adapter);
580         gst_adapter_flush (self->adapter, 16);
581 
582         self->cur_ts =
583             ts_sec * GST_SECOND +
584             ts_usec * (self->nanosecond_timestamp ? 1 : GST_USECOND);
585         self->cur_packet_size = incl_len;
586       }
587     } else {
588       /* Parse the Global Header */
589       guint32 magic;
590       guint32 linktype;
591       guint16 major_version;
592 
593       /* sizeof(pcap_hdr_t) == 24 */
594       if (avail < 24)
595         break;
596 
597       data = gst_adapter_map (self->adapter, 24);
598 
599       magic = *((guint32 *) data);
600       major_version = *((guint16 *) (data + 4));
601       linktype = *((guint32 *) (data + 20));
602       gst_adapter_unmap (self->adapter);
603 
604       if (magic == GST_PCAPPARSE_MAGIC_MILLISECOND_NO_SWAP_ENDIAN ||
605           magic == GST_PCAPPARSE_MAGIC_NANOSECOND_NO_SWAP_ENDIAN) {
606         self->swap_endian = FALSE;
607         if (magic == GST_PCAPPARSE_MAGIC_NANOSECOND_NO_SWAP_ENDIAN)
608           self->nanosecond_timestamp = TRUE;
609       } else if (magic == GST_PCAPPARSE_MAGIC_MILLISECOND_SWAP_ENDIAN ||
610           magic == GST_PCAPPARSE_MAGIC_NANOSECOND_SWAP_ENDIAN) {
611         self->swap_endian = TRUE;
612         if (magic == GST_PCAPPARSE_MAGIC_NANOSECOND_SWAP_ENDIAN)
613           self->nanosecond_timestamp = TRUE;
614         major_version = GUINT16_SWAP_LE_BE (major_version);
615         linktype = GUINT32_SWAP_LE_BE (linktype);
616       } else {
617         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
618             ("File is not a libpcap file, magic is %X", magic));
619         ret = GST_FLOW_ERROR;
620         goto out;
621       }
622 
623       if (major_version != 2) {
624         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
625             ("File is not a libpcap major version 2, but %u", major_version));
626         ret = GST_FLOW_ERROR;
627         goto out;
628       }
629 
630       if (linktype != LINKTYPE_ETHER && linktype != LINKTYPE_SLL &&
631           linktype != LINKTYPE_RAW) {
632         GST_ELEMENT_ERROR (self, STREAM, WRONG_TYPE, (NULL),
633             ("Only dumps of type Ethernet, raw IP or Linux Cooked (SLL) "
634                 "understood; type %d unknown", linktype));
635         ret = GST_FLOW_ERROR;
636         goto out;
637       }
638 
639       GST_DEBUG_OBJECT (self, "linktype %u", linktype);
640       self->linktype = linktype;
641 
642       gst_adapter_flush (self->adapter, 24);
643       self->initialized = TRUE;
644     }
645   }
646 
647   if (list) {
648     if (!self->newsegment_sent && GST_CLOCK_TIME_IS_VALID (self->cur_ts)) {
649       GstSegment segment;
650 
651       if (self->caps)
652         gst_pad_set_caps (self->src_pad, self->caps);
653       gst_segment_init (&segment, GST_FORMAT_TIME);
654       segment.start = self->base_ts;
655       gst_pad_push_event (self->src_pad, gst_event_new_segment (&segment));
656       self->newsegment_sent = TRUE;
657     }
658 
659     ret = gst_pad_push_list (self->src_pad, list);
660     list = NULL;
661   }
662 
663 out:
664 
665   if (list)
666     gst_buffer_list_unref (list);
667 
668   return ret;
669 }
670 
671 static gboolean
gst_pcap_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)672 gst_pcap_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
673 {
674   gboolean ret = TRUE;
675   GstPcapParse *self = GST_PCAP_PARSE (parent);
676 
677   switch (GST_EVENT_TYPE (event)) {
678     case GST_EVENT_SEGMENT:
679       /* Drop it, we'll replace it with our own */
680       gst_event_unref (event);
681       break;
682     case GST_EVENT_FLUSH_STOP:
683       gst_pcap_parse_reset (self);
684       /* Push event down the pipeline so that other elements stop flushing */
685       /* fall through */
686     default:
687       ret = gst_pad_push_event (self->src_pad, event);
688       break;
689   }
690 
691   return ret;
692 }
693 
694 static GstStateChangeReturn
gst_pcap_parse_change_state(GstElement * element,GstStateChange transition)695 gst_pcap_parse_change_state (GstElement * element, GstStateChange transition)
696 {
697   GstPcapParse *self = GST_PCAP_PARSE (element);
698   GstStateChangeReturn ret;
699 
700   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
701 
702   switch (transition) {
703     case GST_STATE_CHANGE_PAUSED_TO_READY:
704       gst_pcap_parse_reset (self);
705       break;
706     default:
707       break;
708   }
709 
710 
711   return ret;
712 }
713