EVERYTHING IN THE BUILDBOT IS GOING TO BE RED!!! Sorry!
[obnox/wireshark/wip.git] / gtk / rtp_stream.c
1 /* rtp_stream.c
2  * RTP streams summary addition for ethereal
3  *
4  * $Id$
5  *
6  * Copyright 2003, Alcatel Business Systems
7  * By Lars Ruoff <lars.ruoff@gmx.net>
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation,  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include "rtp_stream.h"
33 #include "rtp_stream_dlg.h"
34
35 #include "globals.h"
36
37 #include <epan/tap.h>
38 #include "register.h"
39 #include <epan/dissectors/packet-rtp.h>
40
41
42 #include "alert_box.h"
43 #include "simple_dialog.h"
44
45 #ifdef HAVE_FCNTL_H
46 #include <fcntl.h>
47 #endif
48
49 #ifdef HAVE_SYS_TYPES_H
50 # include <sys/types.h>
51 #endif
52
53 #include <string.h>
54 #include <epan/addr_resolv.h>
55
56
57 /****************************************************************************/
58 /* the one and only global rtpstream_tapinfo_t structure */
59 static rtpstream_tapinfo_t the_tapinfo_struct =
60         {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
61
62
63 /****************************************************************************/
64 /* GCompareFunc style comparison function for _rtp_stream_info */
65 static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
66 {
67         const struct _rtp_stream_info* a = aa;
68         const struct _rtp_stream_info* b = bb;
69
70         if (a==b)
71                 return 0;
72         if (a==NULL || b==NULL)
73                 return 1;
74         if (ADDRESSES_EQUAL(&(a->src_addr), &(b->src_addr))
75                 && (a->src_port == b->src_port)
76                 && ADDRESSES_EQUAL(&(a->dest_addr), &(b->dest_addr))
77                 && (a->dest_port == b->dest_port)
78                 && (a->ssrc == b->ssrc))
79                 return 0;
80         else
81                 return 1;
82 }
83
84
85 /****************************************************************************/
86 /* when there is a [re]reading of packet's */
87 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
88 {
89         GList* list;
90
91         if (tapinfo->mode == TAP_ANALYSE) {
92                 /* free the data items first */
93                 list = g_list_first(tapinfo->strinfo_list);
94                 while (list)
95                 {
96                         g_free(list->data);
97                         list = g_list_next(list);
98                 }
99                 g_list_free(tapinfo->strinfo_list);
100                 tapinfo->strinfo_list = NULL;
101                 tapinfo->nstreams = 0;
102                 tapinfo->npackets = 0;
103         }
104
105         ++(tapinfo->launch_count);
106
107         return;
108 }
109
110 static void rtpstream_reset_cb(void *arg)
111 {
112         rtpstream_reset(arg);
113 }
114
115 /****************************************************************************/
116 /* redraw the output */
117 static void rtpstream_draw(void *arg _U_)
118 {
119 /* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
120         gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
121 */
122         rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
123         return;
124 }
125
126
127 /*
128 * rtpdump file format
129 *
130 * The file starts with the tool to be used for playing this file,
131 * the multicast/unicast receive address and the port.
132 *
133 * #!rtpplay1.0 224.2.0.1/3456\n
134 *
135 * This is followed by one binary header (RD_hdr_t) and one RD_packet_t
136 * structure for each received packet.  All fields are in network byte
137 * order.  We don't need the source IP address since we can do mapping
138 * based on SSRC.  This saves (a little) space, avoids non-IPv4
139 * problems and privacy/security concerns. The header is followed by
140 * the RTP/RTCP header and (optionally) the actual payload.
141 */
142
143 #define RTPFILE_VERSION "1.0"
144
145 /*
146 * Write a header to the current output file.
147 * The header consists of an identifying string, followed
148 * by a binary structure.
149 */
150 static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
151 {
152         guint32 start_sec;     /* start of recording (GMT) (seconds) */
153         guint32 start_usec;    /* start of recording (GMT) (microseconds)*/
154         guint32 source;        /* network source (multicast address) */
155         guint16 port;          /* UDP port */
156         guint16 padding;       /* 2 padding bytes */
157         
158         fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
159                 get_addr_name(&(strinfo->dest_addr)),
160                 strinfo->dest_port);
161
162         start_sec = g_htonl(strinfo->start_sec);
163         start_usec = g_htonl(strinfo->start_usec);
164         source = *(strinfo->src_addr.data); /* rtpdump only accepts guint32 as source, will be fake for IPv6 */
165         port = g_htons(strinfo->src_port);
166         padding = 0;
167
168         fwrite(&start_sec, 4, 1, file);
169         fwrite(&start_usec, 4, 1, file);
170         fwrite(&source, 4, 1, file);
171         fwrite(&port, 2, 1, file);
172         fwrite(&padding, 2, 1, file);
173 }
174
175 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
176 static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
177 {
178         guint16 length;    /* length of packet, including this header (may
179                              be smaller than plen if not whole packet recorded) */
180         guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
181         guint32 offset;    /* milliseconds since the start of recording */
182
183         length = g_htons(sample->header.frame_length + 8);
184         plen = g_htons(sample->header.frame_length);
185         offset = g_htonl(sample->header.rec_time);
186
187         fwrite(&length, 2, 1, file);
188         fwrite(&plen, 2, 1, file);
189         fwrite(&offset, 4, 1, file);
190         fwrite(sample->frame, sample->header.frame_length, 1, file);
191 }
192
193
194 /****************************************************************************/
195 /* whenever a RTP packet is seen by the tap listener */
196 static int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2)
197 {
198         rtpstream_tapinfo_t *tapinfo = arg;
199         const struct _rtp_info *rtpinfo = arg2;
200         rtp_stream_info_t tmp_strinfo;
201         rtp_stream_info_t *strinfo = NULL;
202         GList* list;
203         rtp_sample_t sample;
204
205         struct _rtp_conversation_info *p_conv_data = NULL;
206
207         /* gather infos on the stream this packet is part of */
208         COPY_ADDRESS(&(tmp_strinfo.src_addr), &(pinfo->src));
209         tmp_strinfo.src_port = pinfo->srcport;
210         COPY_ADDRESS(&(tmp_strinfo.dest_addr), &(pinfo->dst));
211         tmp_strinfo.dest_port = pinfo->destport;
212         tmp_strinfo.ssrc = rtpinfo->info_sync_src;
213         tmp_strinfo.pt = rtpinfo->info_payload_type;
214
215         if (tapinfo->mode == TAP_ANALYSE) {
216                 /* check wether we already have a stream with these parameters in the list */
217                 list = g_list_first(tapinfo->strinfo_list);
218                 while (list)
219                 {
220                         if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
221                         {
222                                 strinfo = (rtp_stream_info_t*)(list->data);  /*found!*/
223                                 break;
224                         }
225                         list = g_list_next(list);
226                 }
227
228                 /* not in the list? then create a new entry */
229                 if (!strinfo) {
230                         tmp_strinfo.npackets = 0;
231                         tmp_strinfo.first_frame_num = pinfo->fd->num;
232                         tmp_strinfo.start_sec = pinfo->fd->abs_ts.secs;
233                         tmp_strinfo.start_usec = pinfo->fd->abs_ts.nsecs/1000;
234                         tmp_strinfo.start_rel_sec = pinfo->fd->rel_ts.secs;
235                         tmp_strinfo.start_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
236                         tmp_strinfo.tag_vlan_error = 0;
237                         tmp_strinfo.tag_diffserv_error = 0;
238                         tmp_strinfo.vlan_id = 0;
239                         tmp_strinfo.problem = FALSE;
240
241                         /* reset RTP stats */
242                         tmp_strinfo.rtp_stats.first_packet = TRUE;
243                         tmp_strinfo.rtp_stats.max_delta = 0;
244                         tmp_strinfo.rtp_stats.max_jitter = 0;
245                         tmp_strinfo.rtp_stats.mean_jitter = 0;
246                         tmp_strinfo.rtp_stats.delta = 0;
247                         tmp_strinfo.rtp_stats.diff = 0;
248                         tmp_strinfo.rtp_stats.jitter = 0;
249                         tmp_strinfo.rtp_stats.bandwidth = 0;
250                         tmp_strinfo.rtp_stats.total_bytes = 0;
251                         tmp_strinfo.rtp_stats.bw_start_index = 0;
252                         tmp_strinfo.rtp_stats.bw_index = 0;
253                         tmp_strinfo.rtp_stats.timestamp = 0;
254                         tmp_strinfo.rtp_stats.max_nr = 0;
255                         tmp_strinfo.rtp_stats.total_nr = 0;
256                         tmp_strinfo.rtp_stats.sequence = 0;
257                         tmp_strinfo.rtp_stats.start_seq_nr = 0;
258                         tmp_strinfo.rtp_stats.stop_seq_nr = 0;
259                         tmp_strinfo.rtp_stats.cycles = 0;
260                         tmp_strinfo.rtp_stats.under = FALSE;
261                         tmp_strinfo.rtp_stats.start_time = 0;
262                         tmp_strinfo.rtp_stats.time = 0;
263                         tmp_strinfo.rtp_stats.reg_pt = PT_UNDEFINED;
264
265             /* Get the Setup frame number who set this RTP stream */
266             p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
267             if (p_conv_data)
268                                 tmp_strinfo.setup_frame_number = p_conv_data->frame_number;
269             else
270                 tmp_strinfo.setup_frame_number = 0xFFFFFFFF;
271
272                         strinfo = g_malloc(sizeof(rtp_stream_info_t));
273                         *strinfo = tmp_strinfo;  /* memberwise copy of struct */
274                         tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
275                 }
276
277                 /* get RTP stats for the packet */
278                 rtp_packet_analyse(&(strinfo->rtp_stats), pinfo, rtpinfo);
279                 if (strinfo->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP
280                         || strinfo->rtp_stats.flags & STAT_FLAG_WRONG_SEQ)
281                         strinfo->problem = TRUE;
282
283
284                 /* increment the packets counter for this stream */
285                 ++(strinfo->npackets);
286                 strinfo->stop_rel_sec = pinfo->fd->rel_ts.secs;
287                 strinfo->stop_rel_usec = pinfo->fd->rel_ts.nsecs/1000;
288
289                 /* increment the packets counter of all streams */
290                 ++(tapinfo->npackets);
291                 
292                 return 1;  /* refresh output */
293         }
294         else if (tapinfo->mode == TAP_SAVE) {
295                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
296                         /* XXX - what if rtpinfo->info_all_data_present is
297                            FALSE, so that we don't *have* all the data? */
298                         sample.header.rec_time = 
299                                 (pinfo->fd->abs_ts.nsecs/1000 + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
300                                 + (pinfo->fd->abs_ts.secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
301                         sample.header.frame_length = rtpinfo->info_data_len;
302                         sample.frame = rtpinfo->info_data;
303                         rtp_write_sample(&sample, tapinfo->save_file);
304                 }
305         }
306         else if (tapinfo->mode == TAP_MARK) {
307
308                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
309                         || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
310                 {
311                         cf_mark_frame(&cfile, pinfo->fd);
312                 }
313         }
314
315         return 0;
316 }
317
318 /****************************************************************************/
319 /* scan for RTP streams */
320 void rtpstream_scan(void)
321 {
322         gboolean was_registered = the_tapinfo_struct.is_registered;
323         if (!the_tapinfo_struct.is_registered)
324                 register_tap_listener_rtp_stream();
325
326         the_tapinfo_struct.mode = TAP_ANALYSE;
327         cf_retap_packets(&cfile);
328
329         if (!was_registered)
330                 remove_tap_listener_rtp_stream();
331 }
332
333
334 /****************************************************************************/
335 /* save rtp dump of stream_fwd */
336 gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
337 {
338         gboolean was_registered = the_tapinfo_struct.is_registered;
339         /* open file for saving */
340         the_tapinfo_struct.save_file = fopen(filename, "wb");
341         if (the_tapinfo_struct.save_file==NULL) {
342                 open_failure_alert_box(filename, errno, TRUE);
343                 return FALSE;
344         }
345
346         rtp_write_header(stream, the_tapinfo_struct.save_file);
347         if (ferror(the_tapinfo_struct.save_file)) {
348                 write_failure_alert_box(filename, errno);
349                 fclose(the_tapinfo_struct.save_file);
350                 return FALSE;
351         }
352
353         if (!the_tapinfo_struct.is_registered)
354                 register_tap_listener_rtp_stream();
355
356         the_tapinfo_struct.mode = TAP_SAVE;
357         the_tapinfo_struct.filter_stream_fwd = stream;
358         cf_retap_packets(&cfile);
359         the_tapinfo_struct.mode = TAP_ANALYSE;
360
361         if (!was_registered)
362                 remove_tap_listener_rtp_stream();
363
364         if (ferror(the_tapinfo_struct.save_file)) {
365                 write_failure_alert_box(filename, errno);
366                 fclose(the_tapinfo_struct.save_file);
367                 return FALSE;
368         }
369
370         if (fclose(the_tapinfo_struct.save_file) == EOF) {
371                 write_failure_alert_box(filename, errno);
372                 return FALSE;
373         }
374         return TRUE;
375 }
376
377
378 /****************************************************************************/
379 /* mark packets in stream_fwd or stream_rev */
380 void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
381 {
382         gboolean was_registered = the_tapinfo_struct.is_registered;
383         if (!the_tapinfo_struct.is_registered)
384                 register_tap_listener_rtp_stream();
385
386         the_tapinfo_struct.mode = TAP_MARK;
387         the_tapinfo_struct.filter_stream_fwd = stream_fwd;
388         the_tapinfo_struct.filter_stream_rev = stream_rev;
389         cf_retap_packets(&cfile);
390         the_tapinfo_struct.mode = TAP_ANALYSE;
391
392         if (!was_registered)
393                 remove_tap_listener_rtp_stream();
394 }
395
396
397 /****************************************************************************/
398 const rtpstream_tapinfo_t* rtpstream_get_info(void)
399 {
400         return &the_tapinfo_struct;
401 }
402
403
404 /****************************************************************************/
405 /* TAP INTERFACE */
406 /****************************************************************************/
407
408 /* XXX just copied from gtk/rpc_stat.c */
409 void protect_thread_critical_region(void);
410 void unprotect_thread_critical_region(void);
411
412 /****************************************************************************/
413 void
414 remove_tap_listener_rtp_stream(void)
415 {
416         if (the_tapinfo_struct.is_registered) {
417                 protect_thread_critical_region();
418                 remove_tap_listener(&the_tapinfo_struct);
419                 unprotect_thread_critical_region();
420
421                 the_tapinfo_struct.is_registered = FALSE;
422         }
423 }
424
425
426 /****************************************************************************/
427 void
428 register_tap_listener_rtp_stream(void)
429 {
430         GString *error_string;
431
432         if (!the_tapinfo_struct.is_registered) {
433                 error_string = register_tap_listener("rtp", &the_tapinfo_struct,
434                         NULL, rtpstream_reset_cb, rtpstream_packet,
435                         rtpstream_draw);
436
437                 if (error_string != NULL) {
438                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
439                                       error_string->str);
440                         g_string_free(error_string, TRUE);
441                         exit(1);
442                 }
443
444                 the_tapinfo_struct.is_registered = TRUE;
445         }
446 }