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