get GDK window geometry only, if widget is visible
[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 "tap.h"
38 #include "register.h"
39 #include <epan/dissectors/packet-rtp.h>
40
41 #include "alert_box.h"
42 #include "simple_dialog.h"
43
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47
48 #ifdef HAVE_SYS_TYPES_H
49 # include <sys/types.h>
50 #endif
51
52 #include <string.h>
53
54
55 /****************************************************************************/
56 /* the one and only global rtpstream_tapinfo_t structure */
57 static rtpstream_tapinfo_t the_tapinfo_struct =
58         {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
59
60
61 /****************************************************************************/
62 /* GCompareFunc style comparison function for _rtp_stream_info */
63 gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
64 {
65         const struct _rtp_stream_info* a = aa;
66         const struct _rtp_stream_info* b = bb;
67
68         if (a==b)
69                 return 0;
70         if (a==NULL || b==NULL)
71                 return 1;
72         if ((a->src_addr == b->src_addr)
73                 && (a->src_port == b->src_port)
74                 && (a->dest_addr == b->dest_addr)
75                 && (a->dest_port == b->dest_port)
76                 && (a->ssrc == b->ssrc))
77                 return 0;
78         else
79                 return 1;
80 }
81
82
83 /****************************************************************************/
84 /* when there is a [re]reading of packet's */
85 void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
86 {
87         GList* list;
88
89         if (tapinfo->mode == TAP_ANALYSE) {
90                 /* free the data items first */
91                 list = g_list_first(tapinfo->strinfo_list);
92                 while (list)
93                 {
94                         g_free(list->data);
95                         list = g_list_next(list);
96                 }
97                 g_list_free(tapinfo->strinfo_list);
98                 tapinfo->strinfo_list = NULL;
99                 tapinfo->nstreams = 0;
100                 tapinfo->npackets = 0;
101         }
102
103         ++(tapinfo->launch_count);
104
105         return;
106 }
107
108 /****************************************************************************/
109 /* redraw the output */
110 void rtpstream_draw(rtpstream_tapinfo_t *tapinfo _U_)
111 {
112 /* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
113         gtk_signal_emit_by_name(top_level, "signal_rtpstream_update");
114 */
115         rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
116         return;
117 }
118
119
120 /*
121 * rtpdump file format
122 *
123 * The file starts with the tool to be used for playing this file,
124 * the multicast/unicast receive address and the port.
125 *
126 * #!rtpplay1.0 224.2.0.1/3456\n
127 *
128 * This is followed by one binary header (RD_hdr_t) and one RD_packet_t
129 * structure for each received packet.  All fields are in network byte
130 * order.  We don't need the source IP address since we can do mapping
131 * based on SSRC.  This saves (a little) space, avoids non-IPv4
132 * problems and privacy/security concerns. The header is followed by
133 * the RTP/RTCP header and (optionally) the actual payload.
134 */
135
136 #define RTPFILE_VERSION "1.0"
137
138 /*
139 * Write a header to the current output file.
140 * The header consists of an identifying string, followed
141 * by a binary structure.
142 */
143 static void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file)
144 {
145         guint32 start_sec;     /* start of recording (GMT) (seconds) */
146         guint32 start_usec;    /* start of recording (GMT) (microseconds)*/
147         guint32 source;        /* network source (multicast address) */
148         guint16 port;          /* UDP port */
149         guint16 padding;       /* 2 padding bytes */
150         
151         fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
152                 ip_to_str((guint8*) &strinfo->dest_addr),
153                 strinfo->dest_port);
154
155         start_sec = g_htonl(strinfo->start_sec);
156         start_usec = g_htonl(strinfo->start_usec);
157         source = strinfo->src_addr; /* already is in network order */
158         port = g_htons(strinfo->src_port);
159         padding = 0;
160
161         fwrite(&start_sec, 4, 1, file);
162         fwrite(&start_usec, 4, 1, file);
163         fwrite(&source, 4, 1, file);
164         fwrite(&port, 2, 1, file);
165         fwrite(&padding, 2, 1, file);
166 }
167
168 /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/
169 static void rtp_write_sample(rtp_sample_t* sample, FILE* file)
170 {
171         guint16 length;    /* length of packet, including this header (may
172                              be smaller than plen if not whole packet recorded) */
173         guint16 plen;      /* actual header+payload length for RTP, 0 for RTCP */
174         guint32 offset;    /* milliseconds since the start of recording */
175
176         length = g_htons(sample->header.frame_length + 8);
177         plen = g_htons(sample->header.frame_length);
178         offset = g_htonl(sample->header.rec_time);
179
180         fwrite(&length, 2, 1, file);
181         fwrite(&plen, 2, 1, file);
182         fwrite(&offset, 4, 1, file);
183         fwrite(sample->frame, sample->header.frame_length, 1, file);
184 }
185
186
187 /****************************************************************************/
188 /* whenever a RTP packet is seen by the tap listener */
189 int rtpstream_packet(rtpstream_tapinfo_t *tapinfo _U_, packet_info *pinfo, epan_dissect_t *edt _U_, struct _rtp_info *rtpinfo _U_)
190 {
191         rtp_stream_info_t tmp_strinfo;
192         rtp_stream_info_t *strinfo = NULL;
193         GList* list;
194
195         rtp_sample_t sample;
196
197         /* gather infos on the stream this packet is part of */
198         g_memmove(&(tmp_strinfo.src_addr), pinfo->src.data, 4);
199         tmp_strinfo.src_port = pinfo->srcport;
200         g_memmove(&(tmp_strinfo.dest_addr), pinfo->dst.data, 4);
201         tmp_strinfo.dest_port = pinfo->destport;
202         tmp_strinfo.ssrc = rtpinfo->info_sync_src;
203         tmp_strinfo.pt = rtpinfo->info_payload_type;
204
205         if (tapinfo->mode == TAP_ANALYSE) {
206                 /* check wether we already have a stream with these parameters in the list */
207                 list = g_list_first(tapinfo->strinfo_list);
208                 while (list)
209                 {
210                         if (rtp_stream_info_cmp(&tmp_strinfo, (rtp_stream_info_t*)(list->data))==0)
211                         {
212                                 strinfo = (rtp_stream_info_t*)(list->data);  /*found!*/
213                                 break;
214                         }
215                         list = g_list_next(list);
216                 }
217
218                 /* not in the list? then create a new entry */
219                 if (!strinfo) {
220                         tmp_strinfo.npackets = 0;
221                         tmp_strinfo.first_frame_num = pinfo->fd->num;
222                         tmp_strinfo.start_sec = pinfo->fd->abs_secs;
223                         tmp_strinfo.start_usec = pinfo->fd->abs_usecs;
224                         tmp_strinfo.tag_vlan_error = 0;
225                         tmp_strinfo.tag_diffserv_error = 0;
226                         tmp_strinfo.vlan_id = 0;
227                         strinfo = g_malloc(sizeof(rtp_stream_info_t));
228                         *strinfo = tmp_strinfo;  /* memberwise copy of struct */
229                         tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, strinfo);
230                 }
231
232                 /* increment the packets counter for this stream */
233                 ++(strinfo->npackets);
234
235                 /* increment the packets counter of all streams */
236                 ++(tapinfo->npackets);
237                 
238                 return 1;  /* refresh output */
239         }
240         else if (tapinfo->mode == TAP_SAVE) {
241                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
242                         /* XXX - what if rtpinfo->info_all_data_present is
243                            FALSE, so that we don't *have* all the data? */
244                         sample.header.rec_time = 
245                                 (pinfo->fd->abs_usecs + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
246                                 + (pinfo->fd->abs_secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
247                         sample.header.frame_length = rtpinfo->info_data_len;
248                         sample.frame = rtpinfo->info_data;
249                         rtp_write_sample(&sample, tapinfo->save_file);
250                 }
251         }
252         else if (tapinfo->mode == TAP_MARK) {
253                 if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
254                         || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
255                 {
256                         mark_frame(&cfile, pinfo->fd);
257                 }
258         }
259
260         return 0;
261 }
262
263 /****************************************************************************/
264 /* scan for RTP streams */
265 void rtpstream_scan(void)
266 {
267         gboolean was_registered = the_tapinfo_struct.is_registered;
268         if (!the_tapinfo_struct.is_registered)
269                 register_tap_listener_rtp_stream();
270
271         the_tapinfo_struct.mode = TAP_ANALYSE;
272         retap_packets(&cfile);
273
274         if (!was_registered)
275                 remove_tap_listener_rtp_stream();
276 }
277
278
279 /****************************************************************************/
280 /* save rtp dump of stream_fwd */
281 gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
282 {
283         gboolean was_registered = the_tapinfo_struct.is_registered;
284         /* open file for saving */
285         the_tapinfo_struct.save_file = fopen(filename, "wb");
286         if (the_tapinfo_struct.save_file==NULL) {
287                 open_failure_alert_box(filename, errno, TRUE);
288                 return FALSE;
289         }
290
291         rtp_write_header(stream, the_tapinfo_struct.save_file);
292         if (ferror(the_tapinfo_struct.save_file)) {
293                 write_failure_alert_box(filename, errno);
294                 fclose(the_tapinfo_struct.save_file);
295                 return FALSE;
296         }
297
298         if (!the_tapinfo_struct.is_registered)
299                 register_tap_listener_rtp_stream();
300
301         the_tapinfo_struct.mode = TAP_SAVE;
302         the_tapinfo_struct.filter_stream_fwd = stream;
303         retap_packets(&cfile);
304         the_tapinfo_struct.mode = TAP_ANALYSE;
305
306         if (!was_registered)
307                 remove_tap_listener_rtp_stream();
308
309         if (ferror(the_tapinfo_struct.save_file)) {
310                 write_failure_alert_box(filename, errno);
311                 fclose(the_tapinfo_struct.save_file);
312                 return FALSE;
313         }
314
315         if (fclose(the_tapinfo_struct.save_file) == EOF) {
316                 write_failure_alert_box(filename, errno);
317                 return FALSE;
318         }
319         return TRUE;
320 }
321
322
323 /****************************************************************************/
324 /* mark packets in stream_fwd or stream_rev */
325 void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
326 {
327         gboolean was_registered = the_tapinfo_struct.is_registered;
328         if (!the_tapinfo_struct.is_registered)
329                 register_tap_listener_rtp_stream();
330
331         the_tapinfo_struct.mode = TAP_MARK;
332         the_tapinfo_struct.filter_stream_fwd = stream_fwd;
333         the_tapinfo_struct.filter_stream_rev = stream_rev;
334         retap_packets(&cfile);
335         the_tapinfo_struct.mode = TAP_ANALYSE;
336
337         if (!was_registered)
338                 remove_tap_listener_rtp_stream();
339 }
340
341
342 /****************************************************************************/
343 const rtpstream_tapinfo_t* rtpstream_get_info(void)
344 {
345         return &the_tapinfo_struct;
346 }
347
348
349 /****************************************************************************/
350 /* TAP INTERFACE */
351 /****************************************************************************/
352
353 /****************************************************************************/
354 static void
355 rtpstream_init_tap(char *dummy _U_)
356 {
357         /* XXX: never called? */
358 }
359
360
361 /* XXX just copied from gtk/rpc_stat.c */
362 void protect_thread_critical_region(void);
363 void unprotect_thread_critical_region(void);
364
365 /****************************************************************************/
366 void
367 remove_tap_listener_rtp_stream(void)
368 {
369         if (the_tapinfo_struct.is_registered) {
370                 protect_thread_critical_region();
371                 remove_tap_listener(&the_tapinfo_struct);
372                 unprotect_thread_critical_region();
373
374                 the_tapinfo_struct.is_registered = FALSE;
375         }
376 }
377
378
379 /****************************************************************************/
380 void
381 register_tap_listener_rtp_stream(void)
382 {
383         GString *error_string;
384
385         if (!the_tapinfo_struct.is_registered) {
386                 register_ethereal_tap("rtp", rtpstream_init_tap);
387
388                 error_string = register_tap_listener("rtp", &the_tapinfo_struct,
389                         NULL,
390                         (void*)rtpstream_reset, (void*)rtpstream_packet, (void*)rtpstream_draw);
391
392                 if (error_string != NULL) {
393                         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
394                                       error_string->str);
395                         g_string_free(error_string, TRUE);
396                         exit(1);
397                 }
398
399                 the_tapinfo_struct.is_registered = TRUE;
400         }
401 }