Add routines to process IPv{4,6}-address-and-prefix-length pairs,
[obnox/wireshark/wip.git] / packet-sdp.c
1 /* packet-sdp.c
2  * Routines for SDP packet disassembly (RFC 2327)
3  *
4  * Jason Lango <jal@netapp.com>
5  * Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu>
6  *
7  * $Id: packet-sdp.c,v 1.45 2004/01/16 19:51:55 guy Exp $
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 #include "config.h"
29
30 #include <string.h>
31 #include <ctype.h>
32
33 #include <sys/types.h>
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
36 #endif
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
39 #endif
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
42 #endif
43 #ifdef NEED_INET_ATON_H
44 # include <epan/inet_aton.h>
45 #endif
46
47 #include <glib.h>
48 #include <epan/packet.h>
49 #include <epan/conversation.h>
50 #include <epan/strutil.h>
51
52 static dissector_handle_t rtp_handle=NULL;
53 static dissector_handle_t rtcp_handle=NULL;
54
55 static int proto_sdp = -1;
56
57 /* Top level fields */
58 static int hf_protocol_version = -1;
59 static int hf_owner = -1;
60 static int hf_session_name = -1;
61 static int hf_session_info = -1;
62 static int hf_uri = -1;
63 static int hf_email = -1;
64 static int hf_phone = -1;
65 static int hf_connection_info = -1;
66 static int hf_bandwidth = -1;
67 static int hf_timezone = -1;
68 static int hf_encryption_key = -1;
69 static int hf_session_attribute = -1;
70 static int hf_media_attribute = -1;
71 static int hf_time = -1;
72 static int hf_repeat_time = -1;
73 static int hf_media = -1;
74 static int hf_media_title = -1;
75 static int hf_unknown = -1;
76 static int hf_invalid = -1;
77
78 /* hf_owner subfields*/
79 static int hf_owner_username = -1;
80 static int hf_owner_sessionid = -1;
81 static int hf_owner_version = -1;
82 static int hf_owner_network_type = -1;
83 static int hf_owner_address_type = -1;
84 static int hf_owner_address = -1;
85
86 /* hf_connection_info subfields */
87 static int hf_connection_info_network_type = -1;
88 static int hf_connection_info_address_type = -1;
89 static int hf_connection_info_connection_address = -1;
90 static int hf_connection_info_ttl = -1;
91 static int hf_connection_info_num_addr = -1;
92
93 /* hf_bandwidth subfields */
94 static int hf_bandwidth_modifier = -1;
95 static int hf_bandwidth_value = -1;
96
97 /* hf_time subfields */
98 static int hf_time_start = -1;
99 static int hf_time_stop = -1;
100
101 /* hf_repeat_time subfield */
102 static int hf_repeat_time_interval = -1;
103 static int hf_repeat_time_duration = -1;
104 static int hf_repeat_time_offset = -1;
105
106 /* hf_timezone subfields */
107 static int hf_timezone_time = -1;
108 static int hf_timezone_offset = -1;
109
110 /* hf_encryption_key subfields */
111 static int hf_encryption_key_type = -1;
112 static int hf_encryption_key_data = -1;
113
114 /* hf_session_attribute subfields */
115 static int hf_session_attribute_field = -1;
116 static int hf_session_attribute_value = -1;
117
118 /* hf_media subfields */
119 static int hf_media_media = -1;
120 static int hf_media_port = -1;
121 static int hf_media_portcount = -1;
122 static int hf_media_proto = -1;
123 static int hf_media_format = -1;
124
125 /* hf_session_attribute subfields */
126 static int hf_media_attribute_field = -1;
127 static int hf_media_attribute_value = -1;
128
129 /* trees */
130 static int ett_sdp = -1;
131 static int ett_sdp_owner = -1;
132 static int ett_sdp_connection_info = -1;
133 static int ett_sdp_bandwidth = -1;
134 static int ett_sdp_time = -1;
135 static int ett_sdp_repeat_time = -1;
136 static int ett_sdp_timezone = -1;
137 static int ett_sdp_encryption_key = -1;
138 static int ett_sdp_session_attribute = -1;
139 static int ett_sdp_media = -1;
140 static int ett_sdp_media_attribute = -1;
141
142
143 #define SDP_MAX_RTP_CHANNELS 4
144
145 typedef struct {
146         char *connection_address;
147         char *connection_type;
148         char *media_port[SDP_MAX_RTP_CHANNELS];
149         char *media_proto[SDP_MAX_RTP_CHANNELS];
150         gint8 media_count;
151 } transport_info_t;
152
153 /* static functions */
154
155 static void call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti,
156                                   transport_info_t *transport_info);
157
158 /* Subdissector functions */
159 static void dissect_sdp_owner(tvbuff_t *tvb, proto_item* ti);
160 static void dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti,
161                                         transport_info_t *transport_info);
162 static void dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti);
163 static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti);
164 static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti);
165 static void dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti);
166 static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti);
167 static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item *ti);
168 static void dissect_sdp_media(tvbuff_t *tvb, proto_item *ti,
169                               transport_info_t *transport_info);
170 static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item *ti);
171
172 static void
173 dissect_sdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
174 {
175         proto_tree      *sdp_tree;
176         proto_item      *ti, *sub_ti;
177         gint            offset = 0;
178         gint            next_offset;
179         int             linelen;
180         gboolean        in_media_description;
181         guchar          type;
182         guchar          delim;
183         int             datalen;
184         int             tokenoffset;
185         int             hf = -1;
186         char            *string;
187
188         address         src_addr;
189         conversation_t  *conv=NULL;
190
191         transport_info_t transport_info;
192
193         guint32         ipv4_address=0;
194         guint32         ipv4_port=0;
195         gboolean        is_rtp=FALSE;
196         gboolean        is_ipv4_addr=FALSE;
197         struct in_addr  ipaddr;
198         gint            n;
199
200         /* Initialise RTP channel info */
201         transport_info.connection_address=NULL;
202         transport_info.connection_type=NULL;
203         for (n=0; n < SDP_MAX_RTP_CHANNELS; n++)
204         {
205             transport_info.media_port[n]=NULL;
206             transport_info.media_proto[n]=NULL;
207         }
208         transport_info.media_count = 0;
209
210
211         /*
212          * As RFC 2327 says, "SDP is purely a format for session
213          * description - it does not incorporate a transport protocol,
214          * and is intended to use different transport protocols as
215          * appropriate including the Session Announcement Protocol,
216          * Session Initiation Protocol, Real-Time Streaming Protocol,
217          * electronic mail using the MIME extensions, and the
218          * Hypertext Transport Protocol."
219          *
220          * We therefore don't set the protocol or info columns;
221          * instead, we append to them, so that we don't erase
222          * what the protocol inside which the SDP stuff resides
223          * put there.
224          */
225         if (check_col(pinfo->cinfo, COL_PROTOCOL))
226                 col_append_str(pinfo->cinfo, COL_PROTOCOL, "/SDP");
227
228         if (check_col(pinfo->cinfo, COL_INFO)) {
229                 /* XXX: Needs description. */
230                 col_append_str(pinfo->cinfo, COL_INFO, ", with session description");
231         }
232
233         ti = proto_tree_add_item(tree, proto_sdp, tvb, offset, -1, FALSE);
234         sdp_tree = proto_item_add_subtree(ti, ett_sdp);
235
236         /*
237          * Show the SDP message a line at a time.
238          */
239         in_media_description = FALSE;
240         while (tvb_reported_length_remaining(tvb, offset) > 0) {
241                 /*
242                  * Find the end of the line.
243                  */
244                 linelen = tvb_find_line_end_unquoted(tvb, offset, -1,
245                     &next_offset);
246
247                 /*
248                  * Line must contain at least e.g. "v=".
249                  */
250                 if (linelen < 2)
251                         break;
252
253                 type = tvb_get_guint8(tvb,offset);
254                 delim = tvb_get_guint8(tvb,offset + 1);
255                 if (delim != '=') {
256                         proto_tree_add_item(sdp_tree,hf_invalid,tvb, offset,
257                                               linelen, FALSE);
258                         offset = next_offset;
259                         continue;
260                 }
261
262                 /*
263                  * Attributes.
264                  */
265                 switch (type) {
266                 case 'v':
267                         hf = hf_protocol_version;
268                         break;
269                 case 'o':
270                         hf = hf_owner;
271                         break;
272                 case 's':
273                         hf = hf_session_name;
274                         break;
275                 case 'i':
276                         if (in_media_description) {
277                                 hf = hf_media_title;
278                         }
279                         else{
280                                 hf = hf_session_info;
281                         }
282                         break;
283                 case 'u':
284                         hf = hf_uri;
285                         break;
286                 case 'e':
287                         hf = hf_email;
288                         break;
289                 case 'p':
290                         hf = hf_phone;
291                         break;
292                 case 'c':
293                         hf = hf_connection_info;
294                         break;
295                 case 'b':
296                         hf = hf_bandwidth;
297                         break;
298                 case 't':
299                         hf = hf_time;
300                         break;
301                 case 'r':
302                         hf = hf_repeat_time;
303                         break;
304                 case 'm':
305                         hf = hf_media;
306                         in_media_description = TRUE;
307                         break;
308                 case 'k':
309                         hf = hf_encryption_key;
310                         break;
311                 case 'a':
312                         if (in_media_description) {
313                                 hf = hf_media_attribute;
314                         }
315                         else{
316                                 hf = hf_session_attribute;
317                         }
318                         break;
319                 case 'z':
320                         hf = hf_timezone;
321                         break;
322                 default:
323                         hf = hf_unknown;
324                         break;
325                 }
326                 tokenoffset = 2;
327                 if (hf == hf_unknown)
328                         tokenoffset = 0;
329                 string = tvb_get_string(tvb, offset + tokenoffset,
330                     linelen - tokenoffset);
331                 sub_ti = proto_tree_add_string_format(sdp_tree,hf,tvb, offset,
332                                                linelen, string,
333                                                "%s: %s",
334                                                proto_registrar_get_name(hf),
335                                                format_text(string,
336                                                  linelen - tokenoffset));
337                 g_free(string);
338                 call_sdp_subdissector(tvb_new_subset(tvb,offset+tokenoffset,
339                                                      linelen-tokenoffset,
340                                                      linelen-tokenoffset),
341                                       hf,sub_ti,&transport_info),
342                 offset = next_offset;
343         }
344
345
346         /* Now look, if we have strings collected.
347          * Try to convert ipv4 addresses and ports into binary format,
348          * so we can use them to detect rtp and rtcp streams.
349          * Don't forget to free the strings!
350          */
351
352         for (n = 0; n < transport_info.media_count; n++)
353         {
354             if(transport_info.media_port[n]!=NULL) {
355                     ipv4_port = atol(transport_info.media_port[n]);
356                     g_free(transport_info.media_port[n]);
357             }
358             if(transport_info.media_proto[n]!=NULL) {
359                     /* Check if media protocol is RTP */
360                     is_rtp = (strcmp(transport_info.media_proto[n],"RTP/AVP")==0);
361                     g_free(transport_info.media_proto[n]);
362             }
363             if(transport_info.connection_address!=NULL) {
364                     if(transport_info.connection_type!=NULL &&
365                         strcmp(transport_info.connection_type,"IP4")==0) {
366                             if(inet_aton(transport_info.connection_address, &ipaddr)
367                                 !=0 ) {
368                                     /* connection_address could be converted to a valid ipv4 address*/
369                                     is_ipv4_addr=TRUE;
370                                     ipv4_address = ipaddr.s_addr;
371                             }
372                     }
373             }
374
375             /* Add rtp and rtcp conversation, if available */
376             if((!pinfo->fd->flags.visited) && ipv4_address!=0 && ipv4_port!=0 && is_rtp && is_ipv4_addr){
377                     src_addr.type=AT_IPv4;
378                     src_addr.len=4;
379                     src_addr.data=(char *)&ipv4_address;
380
381                     if(rtp_handle){
382                             conv=find_conversation(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR_B|NO_PORT_B);
383                             if(!conv){
384                                     conv=conversation_new(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR2|NO_PORT2);
385                                     conversation_set_dissector(conv, rtp_handle);
386                             }
387                     }
388
389                     if(rtcp_handle){
390                             ipv4_port++;
391                             conv=find_conversation(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR_B|NO_PORT_B);
392                             if(!conv){
393                                     conv=conversation_new(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR2|NO_PORT2);
394                                     conversation_set_dissector(conv, rtcp_handle);
395                             }
396                     }
397             }
398         }
399
400         /* Free up 'connection info' strings */
401         if(transport_info.connection_address) {
402                 g_free(transport_info.connection_address);
403         }
404         if(transport_info.connection_type!=NULL) {
405                 g_free(transport_info.connection_type);
406         }
407
408
409         datalen = tvb_length_remaining(tvb, offset);
410         if (datalen > 0) {
411                 proto_tree_add_text(sdp_tree, tvb, offset, datalen,
412                     "Data (%d bytes)", datalen);
413         }
414 }
415
416 static void
417 call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti,
418                       transport_info_t *transport_info){
419   if(hf == hf_owner){
420     dissect_sdp_owner(tvb,ti);
421   } else if ( hf == hf_connection_info) {
422     dissect_sdp_connection_info(tvb,ti,transport_info);
423   } else if ( hf == hf_bandwidth) {
424     dissect_sdp_bandwidth(tvb,ti);
425   } else if ( hf == hf_time) {
426     dissect_sdp_time(tvb,ti);
427   } else if ( hf == hf_repeat_time ){
428     dissect_sdp_repeat_time(tvb,ti);
429   } else if ( hf == hf_timezone ) {
430     dissect_sdp_timezone(tvb,ti);
431   } else if ( hf == hf_encryption_key ) {
432     dissect_sdp_encryption_key(tvb,ti);
433   } else if ( hf == hf_session_attribute ){
434     dissect_sdp_session_attribute(tvb,ti);
435   } else if ( hf == hf_media ) {
436     dissect_sdp_media(tvb,ti,transport_info);
437   } else if ( hf == hf_media_attribute ){
438     dissect_sdp_media_attribute(tvb,ti);
439   }
440 }
441
442 static void
443 dissect_sdp_owner(tvbuff_t *tvb, proto_item *ti){
444   proto_tree *sdp_owner_tree;
445   gint offset,next_offset,tokenlen;
446
447   offset = 0;
448   next_offset = 0;
449   tokenlen = 0;
450
451   sdp_owner_tree = proto_item_add_subtree(ti,ett_sdp_owner);
452
453   /* Find the username */
454   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
455   if( next_offset == -1 )
456     return;
457   tokenlen = next_offset - offset;
458
459   proto_tree_add_item(sdp_owner_tree,hf_owner_username,tvb, offset,tokenlen,
460                       FALSE);
461   offset = next_offset  + 1;
462
463   /* Find the session id */
464   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
465   if( next_offset == -1 )
466     return;
467   tokenlen = next_offset - offset;
468
469   proto_tree_add_item(sdp_owner_tree,hf_owner_sessionid, tvb,
470                       offset,tokenlen,FALSE);
471   offset = next_offset + 1;
472
473   /* Find the version */
474   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
475   if( next_offset == -1 )
476     return;
477   tokenlen = next_offset - offset;
478
479   proto_tree_add_item(sdp_owner_tree,hf_owner_version, tvb,
480                       offset,tokenlen,FALSE);
481   offset = next_offset + 1;
482
483   /* Find the network type */
484   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
485   if( next_offset == -1 )
486     return;
487   tokenlen = next_offset - offset;
488
489   proto_tree_add_item(sdp_owner_tree,hf_owner_network_type, tvb,
490                       offset,tokenlen,FALSE);
491   offset = next_offset + 1;
492
493   /* Find the address type */
494   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
495   if( next_offset == -1 )
496     return;
497   tokenlen = next_offset - offset;
498
499   proto_tree_add_item(sdp_owner_tree,hf_owner_address_type, tvb,
500                       offset,tokenlen,FALSE);
501   offset = next_offset + 1;
502
503   /* Find the address */
504   proto_tree_add_item(sdp_owner_tree,hf_owner_address, tvb, offset, -1, FALSE);
505 }
506
507 /*
508  * XXX - this can leak memory if an exception is thrown after we've fetched
509  * a string.
510  */
511 static void
512 dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti,
513                             transport_info_t *transport_info){
514   proto_tree *sdp_connection_info_tree;
515   gint offset,next_offset,tokenlen;
516
517   offset = 0;
518   next_offset = 0;
519   tokenlen = 0;
520
521   sdp_connection_info_tree = proto_item_add_subtree(ti,
522                                                     ett_sdp_connection_info);
523
524   /* Find the network type */
525   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
526   if( next_offset == -1 )
527     return;
528   tokenlen = next_offset - offset;
529
530   proto_tree_add_item(sdp_connection_info_tree,
531                       hf_connection_info_network_type,tvb,
532                       offset,tokenlen,FALSE);
533   offset = next_offset + 1;
534
535   /* Find the address type */
536   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
537   if( next_offset == -1 )
538     return;
539   tokenlen = next_offset - offset;
540   /* Save connection address type */
541   transport_info->connection_type = tvb_get_string(tvb, offset, tokenlen);
542
543
544   proto_tree_add_item(sdp_connection_info_tree,
545                       hf_connection_info_address_type,tvb,
546                       offset,tokenlen,FALSE);
547   offset = next_offset + 1;
548
549   /* Find the connection address */
550   /* XXX - what if there's a <number of addresses> value? */
551   next_offset = tvb_find_guint8(tvb,offset,-1,'/');
552   if( next_offset == -1){
553     tokenlen = -1;      /* end of tvbuff */
554     /* Save connection address */
555     transport_info->connection_address =
556         tvb_get_string(tvb, offset, tvb_length_remaining(tvb, offset));
557   } else {
558     tokenlen = next_offset - offset;
559     /* Save connection address */
560     transport_info->connection_address = tvb_get_string(tvb, offset, tokenlen);
561   }
562
563   proto_tree_add_item(sdp_connection_info_tree,
564                       hf_connection_info_connection_address, tvb,
565                       offset,tokenlen,FALSE);
566   if(next_offset != -1){
567     offset = next_offset + 1;
568     next_offset = tvb_find_guint8(tvb,offset,-1,'/');
569     if( next_offset == -1){
570       tokenlen = -1;    /* end of tvbuff */
571     } else {
572       tokenlen = next_offset - offset;
573     }
574     proto_tree_add_item(sdp_connection_info_tree,
575                         hf_connection_info_ttl,tvb,offset,tokenlen,FALSE);
576     if(next_offset != -1){
577       offset = next_offset + 1;
578       proto_tree_add_item(sdp_connection_info_tree,
579                           hf_connection_info_num_addr, tvb,
580                           offset, -1, FALSE);
581     }
582   }
583 }
584
585 static void
586 dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti){
587   proto_tree * sdp_bandwidth_tree;
588   gint offset, next_offset, tokenlen;
589
590   offset = 0;
591   next_offset = 0;
592   tokenlen = 0;
593
594   sdp_bandwidth_tree = proto_item_add_subtree(ti,ett_sdp_bandwidth);
595
596   /* find the modifier */
597   next_offset = tvb_find_guint8(tvb,offset,-1,':');
598
599   if( next_offset == -1)
600     return;
601
602   tokenlen = next_offset - offset;
603
604   proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_modifier,
605                       tvb, offset, tokenlen, FALSE);
606
607   offset = next_offset + 1;
608
609   proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_value,
610                       tvb, offset, -1, FALSE);
611
612 }
613
614 static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti){
615   proto_tree *sdp_time_tree;
616   gint offset,next_offset, tokenlen;
617
618   offset = 0;
619   next_offset = 0;
620   tokenlen = 0;
621
622   sdp_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
623
624   /* get start time */
625   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
626   if( next_offset == -1 )
627     return;
628
629   tokenlen = next_offset - offset;
630   proto_tree_add_item(sdp_time_tree, hf_time_start, tvb,
631                       offset, tokenlen, FALSE);
632
633   /* get stop time */
634   offset = next_offset + 1;
635   proto_tree_add_item(sdp_time_tree, hf_time_stop, tvb,
636                       offset, -1, FALSE);
637 }
638
639 static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti){
640   proto_tree *sdp_repeat_time_tree;
641   gint offset,next_offset, tokenlen;
642
643   offset = 0;
644   next_offset = 0;
645   tokenlen = 0;
646
647   sdp_repeat_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
648
649   /* get interval */
650   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
651   if( next_offset == -1 )
652     return;
653
654   tokenlen = next_offset - offset;
655   proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_interval, tvb,
656                       offset, tokenlen, FALSE);
657
658   /* get duration */
659   offset = next_offset + 1;
660   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
661   if( next_offset == -1 )
662     return;
663
664   tokenlen = next_offset - offset;
665   proto_tree_add_item(sdp_repeat_time_tree,hf_repeat_time_duration, tvb,
666                       offset, tokenlen, FALSE);
667
668   /* get offsets */
669   do{
670     offset = next_offset +1;
671     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
672     if(next_offset != -1){
673       tokenlen = next_offset - offset;
674     } else {
675       tokenlen = -1;    /* end of tvbuff */
676     }
677     proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_offset,
678                         tvb, offset, tokenlen, FALSE);
679   } while( next_offset != -1 );
680
681 }
682 static void
683 dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti){
684   proto_tree* sdp_timezone_tree;
685   gint offset, next_offset, tokenlen;
686   offset = 0;
687   next_offset = 0;
688   tokenlen = 0;
689
690   sdp_timezone_tree = proto_item_add_subtree(ti,ett_sdp_timezone);
691
692   do{
693     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
694     if(next_offset == -1)
695       break;
696     tokenlen = next_offset - offset;
697
698     proto_tree_add_item(sdp_timezone_tree,hf_timezone_time,tvb,
699                         offset, tokenlen, FALSE);
700     offset = next_offset + 1;
701     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
702     if(next_offset != -1){
703       tokenlen = next_offset - offset;
704     } else {
705       tokenlen = -1;    /* end of tvbuff */
706     }
707     proto_tree_add_item(sdp_timezone_tree,hf_timezone_offset,tvb,
708                         offset, tokenlen, FALSE);
709     offset = next_offset + 1;
710   } while (next_offset != -1);
711
712 }
713
714
715 static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti){
716   proto_tree *sdp_encryption_key_tree;
717   gint offset, next_offset, tokenlen;
718
719   offset = 0;
720   next_offset = 0;
721   tokenlen = 0;
722
723   sdp_encryption_key_tree = proto_item_add_subtree(ti,ett_sdp_encryption_key);
724
725   next_offset = tvb_find_guint8(tvb,offset,-1,':');
726
727   if(next_offset == -1)
728     return;
729
730   tokenlen = next_offset - offset;
731
732   proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_type,
733                       tvb, offset, tokenlen, FALSE);
734
735   offset = next_offset + 1;
736   proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_data,
737                       tvb, offset, -1, FALSE);
738
739 }
740
741
742
743 static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item * ti){
744   proto_tree *sdp_session_attribute_tree;
745   gint offset, next_offset, tokenlen;
746
747   offset = 0;
748   next_offset = 0;
749   tokenlen = 0;
750
751   sdp_session_attribute_tree = proto_item_add_subtree(ti,
752                                                       ett_sdp_session_attribute);
753
754   next_offset = tvb_find_guint8(tvb,offset,-1,':');
755
756   if(next_offset == -1)
757     return;
758
759   tokenlen = next_offset - offset;
760
761   proto_tree_add_item(sdp_session_attribute_tree,
762                       hf_session_attribute_field,
763                       tvb, offset, tokenlen, FALSE);
764
765   offset = next_offset + 1;
766   proto_tree_add_item(sdp_session_attribute_tree,
767                       hf_session_attribute_value,
768                       tvb, offset, -1, FALSE);
769
770 }
771
772 static void
773 dissect_sdp_media(tvbuff_t *tvb, proto_item *ti,
774                   transport_info_t *transport_info){
775   proto_tree *sdp_media_tree;
776   gint offset, next_offset, tokenlen;
777
778   offset = 0;
779   next_offset = 0;
780   tokenlen = 0;
781
782   sdp_media_tree = proto_item_add_subtree(ti,ett_sdp_media);
783
784   next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
785
786   if(next_offset == -1)
787     return;
788
789   tokenlen = next_offset - offset;
790
791   proto_tree_add_item(sdp_media_tree, hf_media_media, tvb,
792                       offset, tokenlen, FALSE);
793
794   offset = next_offset + 1;
795
796   next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
797   if(next_offset == -1)
798     return;
799   tokenlen = next_offset - offset;
800   next_offset = tvb_find_guint8(tvb,offset, tokenlen, '/');
801
802   if(next_offset != -1){
803     tokenlen = next_offset - offset;
804     /* Save port info */
805     transport_info->media_port[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen);
806
807     proto_tree_add_item(sdp_media_tree, hf_media_port, tvb,
808                         offset, tokenlen, FALSE);
809     offset = next_offset + 1;
810     next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
811     if(next_offset == -1)
812       return;
813     tokenlen = next_offset - offset;
814     proto_tree_add_item(sdp_media_tree, hf_media_portcount, tvb,
815                         offset, tokenlen, FALSE);
816     offset = next_offset + 1;
817   } else {
818     next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
819
820     if(next_offset == -1)
821       return;
822     tokenlen = next_offset - offset;
823     /* Save port info */
824     transport_info->media_port[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen);
825
826     /* XXX Remember Port */
827     proto_tree_add_item(sdp_media_tree, hf_media_port, tvb,
828                         offset, tokenlen, FALSE);
829     offset = next_offset + 1;
830   }
831
832   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
833
834   if( next_offset == -1)
835     return;
836
837   tokenlen = next_offset - offset;
838   /* Save port protocol */
839   transport_info->media_proto[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen);
840
841   /* XXX Remember Protocol */
842   proto_tree_add_item(sdp_media_tree, hf_media_proto, tvb,
843                       offset, tokenlen, FALSE);
844
845   do{
846     offset = next_offset + 1;
847     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
848
849     if(next_offset == -1){
850       tokenlen = tvb_length_remaining(tvb, offset);     /* End of tvbuff */
851       if (tokenlen == 0)
852         break;  /* Nothing more left */
853     } else {
854       tokenlen = next_offset - offset;
855     }
856
857     proto_tree_add_item(sdp_media_tree, hf_media_format, tvb,
858                         offset, tokenlen, FALSE);
859   } while (next_offset != -1);
860
861   /* Increase the count of media channels, but don't walk off the end
862      of the arrays. */
863   if (transport_info->media_count < (SDP_MAX_RTP_CHANNELS-1)){
864     transport_info->media_count++;
865   }
866
867   /* XXX Dissect traffic to "Port" as "Protocol"
868    *     Remember this Port/Protocol pair so we can tear it down again later
869    *     Actually, it's harder than that:
870    *         We need to find out the address of the other side first and it
871    *         looks like that info can be found in SIP headers only.
872    */
873
874 }
875
876 static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item * ti){
877   proto_tree *sdp_media_attribute_tree;
878   gint offset, next_offset, tokenlen;
879
880   offset = 0;
881   next_offset = 0;
882   tokenlen = 0;
883
884   sdp_media_attribute_tree = proto_item_add_subtree(ti,
885                                                       ett_sdp_media_attribute);
886
887   next_offset = tvb_find_guint8(tvb,offset,-1,':');
888
889   if(next_offset == -1)
890     return;
891
892   tokenlen = next_offset - offset;
893
894   proto_tree_add_item(sdp_media_attribute_tree,
895                       hf_media_attribute_field,
896                       tvb, offset, tokenlen, FALSE);
897
898   offset = next_offset + 1;
899   proto_tree_add_item(sdp_media_attribute_tree,
900                       hf_media_attribute_value,
901                       tvb, offset, -1, FALSE);
902
903 }
904
905 void
906 proto_register_sdp(void)
907 {
908   static hf_register_info hf[] = {
909     { &hf_protocol_version,
910       { "Session Description Protocol Version (v)",
911         "sdp.version", FT_STRING, BASE_NONE,NULL,0x0,
912         "Session Description Protocol Version", HFILL }},
913     { &hf_owner,
914       { "Owner/Creator, Session Id (o)",
915         "sdp.owner", FT_STRING, BASE_NONE, NULL, 0x0,
916         "Owner/Creator, Session Id", HFILL}},
917     { &hf_session_name,
918       { "Session Name (s)",
919         "sdp.session_name", FT_STRING, BASE_NONE,NULL, 0x0,
920         "Session Name", HFILL }},
921     { &hf_session_info,
922       { "Session Information (i)",
923         "sdp.session_info", FT_STRING, BASE_NONE, NULL, 0x0,
924         "Session Information", HFILL }},
925     { &hf_uri,
926       { "URI of Description (u)",
927         "sdp.uri", FT_STRING, BASE_NONE,NULL, 0x0,
928         "URI of Description", HFILL }},
929     { &hf_email,
930       { "E-mail Address (e)",
931         "sdp.email", FT_STRING, BASE_NONE, NULL, 0x0,
932         "E-mail Address", HFILL }},
933     { &hf_phone,
934       { "Phone Number (p)",
935         "sdp.phone", FT_STRING, BASE_NONE, NULL, 0x0,
936         "Phone Number", HFILL }},
937     { &hf_connection_info,
938       { "Connection Information (c)",
939         "sdp.connection_info", FT_STRING, BASE_NONE, NULL, 0x0,
940         "Connection Information", HFILL }},
941     { &hf_bandwidth,
942       { "Bandwidth Information (b)",
943         "sdp.bandwidth", FT_STRING, BASE_NONE, NULL, 0x0,
944         "Bandwidth Information", HFILL }},
945     { &hf_timezone,
946       { "Time Zone Adjustments (z)",
947         "sdp.timezone", FT_STRING, BASE_NONE, NULL, 0x0,
948         "Time Zone Adjustments", HFILL }},
949     { &hf_encryption_key,
950       { "Encryption Key (k)",
951         "sdp.encryption_key", FT_STRING, BASE_NONE, NULL, 0x0,
952         "Encryption Key", HFILL }},
953     { &hf_session_attribute,
954       { "Session Attribute (a)",
955         "sdp.session_attr", FT_STRING, BASE_NONE, NULL, 0x0,
956         "Session Attribute", HFILL }},
957     { &hf_media_attribute,
958       { "Media Attribute (a)",
959         "sdp.media_attr", FT_STRING, BASE_NONE, NULL, 0x0,
960         "Media Attribute", HFILL }},
961     { &hf_time,
962       { "Time Description, active time (t)",
963         "sdp.time", FT_STRING, BASE_NONE, NULL, 0x0,
964         "Time Description, active time", HFILL }},
965     { &hf_repeat_time,
966       { "Repeat Time (r)",
967         "sdp.repeat_time", FT_STRING, BASE_NONE, NULL, 0x0,
968         "Repeat Time", HFILL }},
969     { &hf_media,
970       { "Media Description, name and address (m)",
971         "sdp.media", FT_STRING, BASE_NONE, NULL, 0x0,
972         "Media Description, name and address", HFILL }},
973     { &hf_media_title,
974       { "Media Title (i)",
975         "sdp.media_title",FT_STRING, BASE_NONE, NULL, 0x0,
976         "Media Title", HFILL }},
977     { &hf_unknown,
978       { "Unknown",
979         "sdp.unknown",FT_STRING, BASE_NONE, NULL, 0x0,
980         "Unknown", HFILL }},
981     { &hf_invalid,
982       { "Invalid line",
983         "sdp.invalid",FT_STRING, BASE_NONE, NULL, 0x0,
984         "Invalid line", HFILL }},
985     { &hf_owner_username,
986       { "Owner Username",
987         "sdp.owner.username",FT_STRING, BASE_NONE, NULL, 0x0,
988         "Owner Username", HFILL }},
989     { &hf_owner_sessionid,
990       { "Session ID",
991         "sdp.owner.sessionid",FT_STRING, BASE_NONE, NULL, 0x0,
992         "Session ID", HFILL }},
993     { &hf_owner_version,
994       { "Session Version",
995         "sdp.owner.version",FT_STRING, BASE_NONE, NULL, 0x0,
996         "Session Version", HFILL }},
997     { &hf_owner_network_type,
998       { "Owner Network Type",
999         "sdp.owner.network_type",FT_STRING, BASE_NONE, NULL, 0x0,
1000         "Owner Network Type", HFILL }},
1001     { &hf_owner_address_type,
1002       { "Owner Address Type",
1003         "sdp.owner.address_type",FT_STRING, BASE_NONE, NULL, 0x0,
1004         "Owner Address Type", HFILL }},
1005     { &hf_owner_address,
1006       { "Owner Address",
1007         "sdp.owner.address",FT_STRING, BASE_NONE, NULL, 0x0,
1008         "Owner Address", HFILL }},
1009     { &hf_connection_info_network_type,
1010       { "Connection Network Type",
1011         "sdp.connection_info.network_type",FT_STRING, BASE_NONE, NULL, 0x0,
1012         "Connection Network Type", HFILL }},
1013     { &hf_connection_info_address_type,
1014       { "Connection Address Type",
1015         "sdp.connection_info.address_type",FT_STRING, BASE_NONE, NULL, 0x0,
1016         "Connection Address Type", HFILL }},
1017     { &hf_connection_info_connection_address,
1018       { "Connection Address",
1019         "sdp.connection_info.address",FT_STRING, BASE_NONE, NULL, 0x0,
1020         "Connection Address", HFILL }},
1021     { &hf_connection_info_ttl,
1022       { "Connection TTL",
1023         "sdp.connection_info.ttl",FT_STRING, BASE_NONE, NULL, 0x0,
1024         "Connection TTL", HFILL }},
1025     { &hf_connection_info_num_addr,
1026       { "Connection Number of Addresses",
1027         "sdp.connection_info.num_addr",FT_STRING, BASE_NONE, NULL, 0x0,
1028         "Connection Number of Addresses", HFILL }},
1029     { &hf_bandwidth_modifier,
1030       { "Bandwidth Modifier",
1031         "sdp.bandwidth.modifier",FT_STRING, BASE_NONE, NULL, 0x0,
1032         "Bandwidth Modifier", HFILL }},
1033     { &hf_bandwidth_value,
1034       { "Bandwidth Value",
1035         "sdp.bandwidth.value",FT_STRING, BASE_NONE, NULL, 0x0,
1036         "Bandwidth Value", HFILL }},
1037     { &hf_time_start,
1038       { "Session Start Time",
1039         "sdp.time.start",FT_STRING, BASE_NONE, NULL, 0x0,
1040         "Session Start Time", HFILL }},
1041     { &hf_time_stop,
1042       { "Session Stop Time",
1043         "sdp.time.stop",FT_STRING, BASE_NONE, NULL, 0x0,
1044         "Session Stop Time", HFILL }},
1045     { &hf_repeat_time_interval,
1046       { "Repeat Interval",
1047         "sdp.repeat_time.interval",FT_STRING, BASE_NONE, NULL, 0x0,
1048         "Repeat Interval", HFILL }},
1049     { &hf_repeat_time_duration,
1050       { "Repeat Duration",
1051         "sdp.repeat_time.duration",FT_STRING, BASE_NONE, NULL, 0x0,
1052         "Repeat Duration", HFILL }},
1053     { &hf_repeat_time_offset,
1054       { "Repeat Offset",
1055         "sdp.repeat_time.offset",FT_STRING, BASE_NONE, NULL, 0x0,
1056         "Repeat Offset", HFILL }},
1057     { &hf_timezone_time,
1058       { "Timezone Time",
1059         "sdp.timezone.time",FT_STRING, BASE_NONE, NULL, 0x0,
1060         "Timezone Time", HFILL }},
1061     { &hf_timezone_offset,
1062       { "Timezone Offset",
1063         "sdp.timezone.offset",FT_STRING, BASE_NONE, NULL, 0x0,
1064         "Timezone Offset", HFILL }},
1065     { &hf_encryption_key_type,
1066       { "Key Type",
1067         "sdp.encryption_key.type",FT_STRING, BASE_NONE, NULL, 0x0,
1068         "Type", HFILL }},
1069     { &hf_encryption_key_data,
1070       { "Key Data",
1071         "sdp.encryption_key.data",FT_STRING, BASE_NONE, NULL, 0x0,
1072         "Data", HFILL }},
1073     { &hf_session_attribute_field,
1074       { "Session Attribute Fieldname",
1075         "sdp.session_attr.field",FT_STRING, BASE_NONE, NULL, 0x0,
1076         "Session Attribute Fieldname", HFILL }},
1077     { &hf_session_attribute_value,
1078       { "Session Attribute Value",
1079         "sdp.session_attr.value",FT_STRING, BASE_NONE, NULL, 0x0,
1080         "Session Attribute Value", HFILL }},
1081     { &hf_media_media,
1082       { "Media Type",
1083         "sdp.media.media",FT_STRING, BASE_NONE, NULL, 0x0,
1084         "Media Type", HFILL }},
1085     { &hf_media_port,
1086       { "Media Port",
1087         "sdp.media.port",FT_STRING, BASE_NONE, NULL, 0x0,
1088         "Media Port", HFILL }},
1089     { &hf_media_portcount,
1090       { "Media Port Count",
1091         "sdp.media.portcount",FT_STRING, BASE_NONE, NULL, 0x0,
1092         "Media Port Count", HFILL }},
1093     { &hf_media_proto,
1094       { "Media Proto",
1095         "sdp.media.proto",FT_STRING, BASE_NONE, NULL, 0x0,
1096         "Media Proto", HFILL }},
1097     { &hf_media_format,
1098       { "Media Format",
1099         "sdp.media.format",FT_STRING, BASE_NONE, NULL, 0x0,
1100         "Media Format", HFILL }},
1101     { &hf_media_attribute_field,
1102       { "Media Attribute Fieldname",
1103         "sdp.media_attribute.field",FT_STRING, BASE_NONE, NULL, 0x0,
1104         "Media Attribute Fieldname", HFILL }},
1105     { &hf_media_attribute_value,
1106       { "Media Attribute Value",
1107         "sdp.media_attribute.value",FT_STRING, BASE_NONE, NULL, 0x0,
1108         "Media Attribute Value", HFILL }},
1109
1110   };
1111   static gint *ett[] = {
1112     &ett_sdp,
1113     &ett_sdp_owner,
1114     &ett_sdp_connection_info,
1115     &ett_sdp_bandwidth,
1116     &ett_sdp_time,
1117     &ett_sdp_repeat_time,
1118     &ett_sdp_timezone,
1119     &ett_sdp_encryption_key,
1120     &ett_sdp_session_attribute,
1121     &ett_sdp_media,
1122     &ett_sdp_media_attribute,
1123   };
1124
1125   proto_sdp = proto_register_protocol("Session Description Protocol",
1126                                       "SDP", "sdp");
1127   proto_register_field_array(proto_sdp, hf, array_length(hf));
1128   proto_register_subtree_array(ett, array_length(ett));
1129
1130   /*
1131    * Register the dissector by name, so other dissectors can
1132    * grab it by name rather than just referring to it directly
1133    * (you can't refer to it directly from a plugin dissector
1134    * on Windows without stuffing it into the Big Transfer Vector).
1135    */
1136   register_dissector("sdp", dissect_sdp, proto_sdp);
1137 }
1138
1139 void
1140 proto_reg_handoff_sdp(void)
1141 {
1142   dissector_handle_t sdp_handle;
1143
1144   rtp_handle = find_dissector("rtp");
1145   rtcp_handle = find_dissector("rtcp");
1146
1147   sdp_handle = find_dissector("sdp");
1148   dissector_add_string("media_type", "application/sdp", sdp_handle);
1149 }