4572dd8d0f2dd608ad4c2fd43299998ac00b0323
[obnox/wireshark/wip.git] / epan / dissectors / 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$
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 #ifdef HAVE_WINSOCK2_H
48 #include <winsock2.h>           /* needed to define AF_ values on Windows */
49 #endif
50
51 #ifdef NEED_INET_V6DEFS_H
52 # include "inet_v6defs.h"
53 #endif
54
55 #include <glib.h> 
56 #include <epan/packet.h>
57 #include <epan/conversation.h>
58 #include <epan/strutil.h>
59 #include <epan/emem.h>
60
61 #include "tap.h"
62 #include "packet-sdp.h"
63
64 #include "packet-rtp.h"
65 #include "rtp_pt.h"
66 #include "packet-rtcp.h"
67
68 #include "packet-t38.h"
69
70 static dissector_handle_t rtp_handle=NULL;
71 static dissector_handle_t rtcp_handle=NULL;
72
73 static dissector_handle_t t38_handle=NULL;
74
75 static int sdp_tap = -1;
76
77 static int proto_sdp = -1;
78
79 /* Top level fields */
80 static int hf_protocol_version = -1;
81 static int hf_owner = -1;
82 static int hf_session_name = -1;
83 static int hf_session_info = -1;
84 static int hf_uri = -1;
85 static int hf_email = -1;
86 static int hf_phone = -1;
87 static int hf_connection_info = -1;
88 static int hf_bandwidth = -1;
89 static int hf_timezone = -1;
90 static int hf_encryption_key = -1;
91 static int hf_session_attribute = -1;
92 static int hf_media_attribute = -1;
93 static int hf_time = -1;
94 static int hf_repeat_time = -1;
95 static int hf_media = -1;
96 static int hf_media_title = -1;
97 static int hf_unknown = -1;
98 static int hf_invalid = -1;
99 static int hf_ipbcp_version = -1;
100 static int hf_ipbcp_type = -1;
101
102 /* hf_owner subfields*/
103 static int hf_owner_username = -1;
104 static int hf_owner_sessionid = -1;
105 static int hf_owner_version = -1;
106 static int hf_owner_network_type = -1;
107 static int hf_owner_address_type = -1;
108 static int hf_owner_address = -1;
109
110 /* hf_connection_info subfields */
111 static int hf_connection_info_network_type = -1;
112 static int hf_connection_info_address_type = -1;
113 static int hf_connection_info_connection_address = -1;
114 static int hf_connection_info_ttl = -1;
115 static int hf_connection_info_num_addr = -1;
116
117 /* hf_bandwidth subfields */
118 static int hf_bandwidth_modifier = -1;
119 static int hf_bandwidth_value = -1;
120
121 /* hf_time subfields */
122 static int hf_time_start = -1;
123 static int hf_time_stop = -1;
124
125 /* hf_repeat_time subfield */
126 static int hf_repeat_time_interval = -1;
127 static int hf_repeat_time_duration = -1;
128 static int hf_repeat_time_offset = -1;
129
130 /* hf_timezone subfields */
131 static int hf_timezone_time = -1;
132 static int hf_timezone_offset = -1;
133
134 /* hf_encryption_key subfields */
135 static int hf_encryption_key_type = -1;
136 static int hf_encryption_key_data = -1;
137
138 /* hf_session_attribute subfields */
139 static int hf_session_attribute_field = -1;
140 static int hf_session_attribute_value = -1;
141
142 /* hf_media subfields */
143 static int hf_media_media = -1;
144 static int hf_media_port = -1;
145 static int hf_media_portcount = -1;
146 static int hf_media_proto = -1;
147 static int hf_media_format = -1;
148
149 /* hf_session_attribute subfields */
150 static int hf_media_attribute_field = -1;
151 static int hf_media_attribute_value = -1;
152
153 /* trees */
154 static int ett_sdp = -1;
155 static int ett_sdp_owner = -1;
156 static int ett_sdp_connection_info = -1;
157 static int ett_sdp_bandwidth = -1;
158 static int ett_sdp_time = -1;
159 static int ett_sdp_repeat_time = -1;
160 static int ett_sdp_timezone = -1;
161 static int ett_sdp_encryption_key = -1;
162 static int ett_sdp_session_attribute = -1;
163 static int ett_sdp_media = -1;
164 static int ett_sdp_media_attribute = -1;
165
166
167 #define SDP_MAX_RTP_CHANNELS 4
168 #define SDP_MAX_RTP_PAYLOAD_TYPES 20
169
170 typedef struct {
171         gint32 pt[SDP_MAX_RTP_PAYLOAD_TYPES];
172         gint8 pt_count;
173         GHashTable *rtp_dyn_payload;
174 } transport_media_pt_t;
175
176 typedef struct {
177         char *connection_address;
178         char *connection_type;
179         char *media_port[SDP_MAX_RTP_CHANNELS];
180         char *media_proto[SDP_MAX_RTP_CHANNELS];
181         transport_media_pt_t media[SDP_MAX_RTP_CHANNELS];
182         gint8 media_count;
183 } transport_info_t;
184
185 /* static functions */
186
187 static void call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti,
188                                   transport_info_t *transport_info);
189
190 /* Subdissector functions */
191 static void dissect_sdp_owner(tvbuff_t *tvb, proto_item* ti);
192 static void dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti,
193                                         transport_info_t *transport_info);
194 static void dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti);
195 static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti);
196 static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti);
197 static void dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti);
198 static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti);
199 static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item *ti);
200 static void dissect_sdp_media(tvbuff_t *tvb, proto_item *ti,
201                               transport_info_t *transport_info);
202 static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item *ti, transport_info_t *transport_info);
203
204 static void
205 dissect_sdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
206 {
207         proto_tree      *sdp_tree;
208         proto_item      *ti, *sub_ti;
209         gint            offset = 0;
210         gint            next_offset;
211         int             linelen;
212         gboolean        in_media_description;
213         guchar          type;
214         guchar          delim;
215         int             datalen;
216         int             tokenoffset;
217         int             hf = -1;
218         char            *string;
219
220         address         src_addr;
221
222         transport_info_t transport_info;
223
224         guint32         port=0;
225         gboolean        is_rtp=FALSE;
226         gboolean        is_t38=FALSE;
227         gboolean        set_rtp=FALSE;
228         gboolean        is_ipv4_addr=FALSE;
229         gboolean        is_ipv6_addr=FALSE;
230     guint32     ipaddr[4];
231         gint            n,i;
232         sdp_packet_info *sdp_pi;
233
234     /* Initialise packet info for passing to tap */
235     sdp_pi = ep_alloc(sizeof(sdp_packet_info));
236     sdp_pi->summary_str[0] = '\0';
237
238         /* Initialise RTP channel info */
239         transport_info.connection_address=NULL;
240         transport_info.connection_type=NULL;
241         for (n=0; n < SDP_MAX_RTP_CHANNELS; n++)
242         {
243             transport_info.media_port[n]=NULL;
244             transport_info.media_proto[n]=NULL;
245                 transport_info.media[n].pt_count = 0;
246                 transport_info.media[n].rtp_dyn_payload = g_hash_table_new( g_int_hash, g_int_equal);
247         }
248         transport_info.media_count = 0;
249
250         /*
251          * As RFC 2327 says, "SDP is purely a format for session
252          * description - it does not incorporate a transport protocol,
253          * and is intended to use different transport protocols as
254          * appropriate including the Session Announcement Protocol,
255          * Session Initiation Protocol, Real-Time Streaming Protocol,
256          * electronic mail using the MIME extensions, and the
257          * Hypertext Transport Protocol."
258          *
259          * We therefore don't set the protocol or info columns;
260          * instead, we append to them, so that we don't erase
261          * what the protocol inside which the SDP stuff resides
262          * put there.
263          */
264         if (check_col(pinfo->cinfo, COL_PROTOCOL))
265                 col_append_str(pinfo->cinfo, COL_PROTOCOL, "/SDP");
266
267         if (check_col(pinfo->cinfo, COL_INFO)) {
268                 /* XXX: Needs description. */
269                 col_append_str(pinfo->cinfo, COL_INFO, ", with session description");
270         }
271
272         ti = proto_tree_add_item(tree, proto_sdp, tvb, offset, -1, FALSE);
273         sdp_tree = proto_item_add_subtree(ti, ett_sdp);
274
275         /*
276          * Show the SDP message a line at a time.
277          */
278         in_media_description = FALSE;
279         while (tvb_reported_length_remaining(tvb, offset) > 0) {
280                 /*
281                  * Find the end of the line.
282                  */
283                 linelen = tvb_find_line_end_unquoted(tvb, offset, -1,
284                     &next_offset);
285
286                 /*
287                  * Line must contain at least e.g. "v=".
288                  */
289                 if (linelen < 2)
290                         break;
291
292                 type = tvb_get_guint8(tvb,offset);
293                 delim = tvb_get_guint8(tvb,offset + 1);
294                 if (delim != '=') {
295                         proto_tree_add_item(sdp_tree,hf_invalid,tvb, offset,
296                                               linelen, FALSE);
297                         offset = next_offset;
298                         continue;
299                 }
300
301                 /*
302                  * Attributes.
303                  */
304                 switch (type) {
305                 case 'v':
306                         hf = hf_protocol_version;
307                         break;
308                 case 'o':
309                         hf = hf_owner;
310                         break;
311                 case 's':
312                         hf = hf_session_name;
313                         break;
314                 case 'i':
315                         if (in_media_description) {
316                                 hf = hf_media_title;
317                         }
318                         else{
319                                 hf = hf_session_info;
320                         }
321                         break;
322                 case 'u':
323                         hf = hf_uri;
324                         break;
325                 case 'e':
326                         hf = hf_email;
327                         break;
328                 case 'p':
329                         hf = hf_phone;
330                         break;
331                 case 'c':
332                         hf = hf_connection_info;
333                         break;
334                 case 'b':
335                         hf = hf_bandwidth;
336                         break;
337                 case 't':
338                         hf = hf_time;
339                         break;
340                 case 'r':
341                         hf = hf_repeat_time;
342                         break;
343                 case 'm':
344                         hf = hf_media;
345                         in_media_description = TRUE;
346                         break;
347                 case 'k':
348                         hf = hf_encryption_key;
349                         break;
350                 case 'a':
351                         if (in_media_description) {
352                                 hf = hf_media_attribute;
353                         }
354                         else{
355                                 hf = hf_session_attribute;
356                         }
357                         break;
358                 case 'z':
359                         hf = hf_timezone;
360                         break;
361                 default:
362                         hf = hf_unknown;
363                         break;
364                 }
365                 tokenoffset = 2;
366                 if (hf == hf_unknown)
367                         tokenoffset = 0;
368                 string = tvb_get_ephemeral_string(tvb, offset + tokenoffset,
369                     linelen - tokenoffset);
370                 sub_ti = proto_tree_add_string_format(sdp_tree,hf,tvb, offset,
371                                                linelen, string,
372                                                "%s: %s",
373                                                proto_registrar_get_name(hf),
374                                                format_text(string,
375                                                  linelen - tokenoffset));
376                 call_sdp_subdissector(tvb_new_subset(tvb,offset+tokenoffset,
377                                                      linelen-tokenoffset,
378                                                      linelen-tokenoffset),
379                                       hf,sub_ti,&transport_info),
380                 offset = next_offset;
381         }
382
383
384         /* Now look, if we have strings collected.
385          * Try to convert ipv4 addresses and ports into binary format,
386          * so we can use them to detect rtp and rtcp streams.
387          * Don't forget to free the strings!
388          */
389
390         for (n = 0; n < transport_info.media_count; n++)
391         {
392             if(transport_info.media_port[n]!=NULL) {
393                     port = atol(transport_info.media_port[n]);
394             }
395             if(transport_info.media_proto[n]!=NULL) {
396                     /* Check if media protocol is RTP */
397                     is_rtp = (strcmp(transport_info.media_proto[n],"RTP/AVP")==0);
398                     /* Check if media protocol is T38 */
399                     is_t38 = ( (strcmp(transport_info.media_proto[n],"UDPTL")==0) || (strcmp(transport_info.media_proto[n],"udptl")==0) );
400
401             }
402             if(transport_info.connection_address!=NULL) {
403                     if(transport_info.connection_type!=NULL) {
404                         if (strcmp(transport_info.connection_type,"IP4")==0) {
405                                     if(inet_pton(AF_INET,transport_info.connection_address, &ipaddr)==1 ) {
406                                             /* connection_address could be converted to a valid ipv4 address*/
407                                             is_ipv4_addr=TRUE;
408                                         src_addr.type=AT_IPv4;
409                                         src_addr.len=4;
410                                     }
411                                 }
412                                 else if (strcmp(transport_info.connection_type,"IP6")==0){
413                                         if (inet_pton(AF_INET6, transport_info.connection_address, &ipaddr)==1){
414                                             /* connection_address could be converted to a valid ipv6 address*/
415                                                 is_ipv6_addr=TRUE;
416                                             src_addr.type=AT_IPv6;
417                                             src_addr.len=16;
418                                         }
419                                 }
420                     }
421             }
422                 set_rtp = FALSE;
423             /* Add rtp and rtcp conversation, if available (overrides t38 if conversation already set) */
424             if((!pinfo->fd->flags.visited) && port!=0 && is_rtp && (is_ipv4_addr || is_ipv6_addr)){
425                     src_addr.data=(char *)&ipaddr;
426                     if(rtp_handle){
427                                 rtp_add_address(pinfo, &src_addr, port, 0,
428                         "SDP", pinfo->fd->num, transport_info.media[n].rtp_dyn_payload);
429                                 set_rtp = TRUE;
430                     }
431                     if(rtcp_handle){
432                                 port++;
433                                 rtcp_add_address(pinfo, &src_addr, port, 0,
434                          "SDP", pinfo->fd->num);
435                     }
436                 } 
437                         
438             /* Add t38 conversation, if available and only if no rtp */
439             if((!pinfo->fd->flags.visited) && port!=0 && !set_rtp && is_t38 && is_ipv4_addr){
440                     src_addr.data=(char *)&ipaddr;
441                     if(t38_handle){
442                                 t38_add_address(pinfo, &src_addr, port, 0, "SDP", pinfo->fd->num);
443                     }
444             }
445
446                 /* Create the summary str for the Voip Call analysis */
447                 for (i = 0; i < transport_info.media[n].pt_count; i++)
448                 {
449                         /* if the payload type is dynamic (96 to 127), check the hash table to add the desc in the SDP summary */
450                         if ( (transport_info.media[n].pt[i] >=96) && (transport_info.media[n].pt[i] <=127) ) {
451                                 gchar *str_dyn_pt = g_hash_table_lookup(transport_info.media[n].rtp_dyn_payload, &transport_info.media[n].pt[i]);
452                                 if (str_dyn_pt)
453                                         g_snprintf(sdp_pi->summary_str, 50, "%s %s", sdp_pi->summary_str, str_dyn_pt);
454                                 else
455                                         g_snprintf(sdp_pi->summary_str, 50, "%s %d", sdp_pi->summary_str, transport_info.media[n].pt[i]);
456                         } else 
457                                 g_snprintf(sdp_pi->summary_str, 50, "%s %s", sdp_pi->summary_str, val_to_str(transport_info.media[n].pt[i], rtp_payload_type_short_vals, "%u"));
458                 }
459
460                 /* Free the hash table if we did't assigned it to a conv use it */
461                 if (set_rtp == FALSE) 
462                         rtp_free_hash_dyn_payload(transport_info.media[n].rtp_dyn_payload);
463         }
464
465         /* Free the remainded hash tables not used */
466         for (n = transport_info.media_count; n < SDP_MAX_RTP_CHANNELS; n++)
467         {
468                 rtp_free_hash_dyn_payload(transport_info.media[n].rtp_dyn_payload);
469         }
470
471
472         datalen = tvb_length_remaining(tvb, offset);
473         if (datalen > 0) {
474                 proto_tree_add_text(sdp_tree, tvb, offset, datalen,
475                     "Data (%d bytes)", datalen);
476         }
477
478     /* Report this packet to the tap */
479     tap_queue_packet(sdp_tap, pinfo, sdp_pi);
480 }
481
482 static void
483 call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti, transport_info_t *transport_info){
484   if(hf == hf_owner){
485     dissect_sdp_owner(tvb,ti);
486   } else if ( hf == hf_connection_info) {
487     dissect_sdp_connection_info(tvb,ti,transport_info);
488   } else if ( hf == hf_bandwidth) {
489     dissect_sdp_bandwidth(tvb,ti);
490   } else if ( hf == hf_time) {
491     dissect_sdp_time(tvb,ti);
492   } else if ( hf == hf_repeat_time ){
493     dissect_sdp_repeat_time(tvb,ti);
494   } else if ( hf == hf_timezone ) {
495     dissect_sdp_timezone(tvb,ti);
496   } else if ( hf == hf_encryption_key ) {
497     dissect_sdp_encryption_key(tvb,ti);
498   } else if ( hf == hf_session_attribute ){
499     dissect_sdp_session_attribute(tvb,ti);
500   } else if ( hf == hf_media ) {
501     dissect_sdp_media(tvb,ti,transport_info);
502   } else if ( hf == hf_media_attribute ){
503     dissect_sdp_media_attribute(tvb,ti,transport_info);
504   }
505 }
506
507 static void
508 dissect_sdp_owner(tvbuff_t *tvb, proto_item *ti){
509   proto_tree *sdp_owner_tree;
510   gint offset,next_offset,tokenlen;
511
512   offset = 0;
513   next_offset = 0;
514   tokenlen = 0;
515
516   sdp_owner_tree = proto_item_add_subtree(ti,ett_sdp_owner);
517
518   /* Find the username */
519   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
520   if( next_offset == -1 )
521     return;
522   tokenlen = next_offset - offset;
523
524   proto_tree_add_item(sdp_owner_tree,hf_owner_username,tvb, offset,tokenlen,
525                       FALSE);
526   offset = next_offset  + 1;
527
528   /* Find the session id */
529   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
530   if( next_offset == -1 )
531     return;
532   tokenlen = next_offset - offset;
533
534   proto_tree_add_item(sdp_owner_tree,hf_owner_sessionid, tvb,
535                       offset,tokenlen,FALSE);
536   offset = next_offset + 1;
537
538   /* Find the version */
539   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
540   if( next_offset == -1 )
541     return;
542   tokenlen = next_offset - offset;
543
544   proto_tree_add_item(sdp_owner_tree,hf_owner_version, tvb,
545                       offset,tokenlen,FALSE);
546   offset = next_offset + 1;
547
548   /* Find the network type */
549   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
550   if( next_offset == -1 )
551     return;
552   tokenlen = next_offset - offset;
553
554   proto_tree_add_item(sdp_owner_tree,hf_owner_network_type, tvb,
555                       offset,tokenlen,FALSE);
556   offset = next_offset + 1;
557
558   /* Find the address type */
559   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
560   if( next_offset == -1 )
561     return;
562   tokenlen = next_offset - offset;
563
564   proto_tree_add_item(sdp_owner_tree,hf_owner_address_type, tvb,
565                       offset,tokenlen,FALSE);
566   offset = next_offset + 1;
567
568   /* Find the address */
569   proto_tree_add_item(sdp_owner_tree,hf_owner_address, tvb, offset, -1, FALSE);
570 }
571
572 /*
573  * XXX - this can leak memory if an exception is thrown after we've fetched
574  * a string.
575  */
576 static void
577 dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti,
578                             transport_info_t *transport_info){
579   proto_tree *sdp_connection_info_tree;
580   gint offset,next_offset,tokenlen;
581
582   offset = 0;
583   next_offset = 0;
584   tokenlen = 0;
585
586   sdp_connection_info_tree = proto_item_add_subtree(ti,
587                                                     ett_sdp_connection_info);
588
589   /* Find the network type */
590   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
591   if( next_offset == -1 )
592     return;
593   tokenlen = next_offset - offset;
594
595   proto_tree_add_item(sdp_connection_info_tree,
596                       hf_connection_info_network_type,tvb,
597                       offset,tokenlen,FALSE);
598   offset = next_offset + 1;
599
600   /* Find the address type */
601   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
602   if( next_offset == -1 )
603     return;
604   tokenlen = next_offset - offset;
605   /* Save connection address type */
606   transport_info->connection_type = tvb_get_ephemeral_string(tvb, offset, tokenlen);
607
608
609   proto_tree_add_item(sdp_connection_info_tree,
610                       hf_connection_info_address_type,tvb,
611                       offset,tokenlen,FALSE);
612   offset = next_offset + 1;
613
614   /* Find the connection address */
615   /* XXX - what if there's a <number of addresses> value? */
616   next_offset = tvb_find_guint8(tvb,offset,-1,'/');
617   if( next_offset == -1){
618     tokenlen = -1;      /* end of tvbuff */
619     /* Save connection address */
620     transport_info->connection_address =
621         tvb_get_ephemeral_string(tvb, offset, tvb_length_remaining(tvb, offset));
622   } else {
623     tokenlen = next_offset - offset;
624     /* Save connection address */
625     transport_info->connection_address = tvb_get_ephemeral_string(tvb, offset, tokenlen);
626   }
627
628   proto_tree_add_item(sdp_connection_info_tree,
629                       hf_connection_info_connection_address, tvb,
630                       offset,tokenlen,FALSE);
631   if(next_offset != -1){
632     offset = next_offset + 1;
633     next_offset = tvb_find_guint8(tvb,offset,-1,'/');
634     if( next_offset == -1){
635       tokenlen = -1;    /* end of tvbuff */
636     } else {
637       tokenlen = next_offset - offset;
638     }
639     proto_tree_add_item(sdp_connection_info_tree,
640                         hf_connection_info_ttl,tvb,offset,tokenlen,FALSE);
641     if(next_offset != -1){
642       offset = next_offset + 1;
643       proto_tree_add_item(sdp_connection_info_tree,
644                           hf_connection_info_num_addr, tvb,
645                           offset, -1, FALSE);
646     }
647   }
648 }
649
650 static void
651 dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti){
652   proto_tree * sdp_bandwidth_tree;
653   gint offset, next_offset, tokenlen;
654
655   offset = 0;
656   next_offset = 0;
657   tokenlen = 0;
658
659   sdp_bandwidth_tree = proto_item_add_subtree(ti,ett_sdp_bandwidth);
660
661   /* find the modifier */
662   next_offset = tvb_find_guint8(tvb,offset,-1,':');
663
664   if( next_offset == -1)
665     return;
666
667   tokenlen = next_offset - offset;
668
669   proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_modifier,
670                       tvb, offset, tokenlen, FALSE);
671
672   offset = next_offset + 1;
673
674   proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_value,
675                       tvb, offset, -1, FALSE);
676
677 }
678
679 static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti){
680   proto_tree *sdp_time_tree;
681   gint offset,next_offset, tokenlen;
682
683   offset = 0;
684   next_offset = 0;
685   tokenlen = 0;
686
687   sdp_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
688
689   /* get start time */
690   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
691   if( next_offset == -1 )
692     return;
693
694   tokenlen = next_offset - offset;
695   proto_tree_add_item(sdp_time_tree, hf_time_start, tvb,
696                       offset, tokenlen, FALSE);
697
698   /* get stop time */
699   offset = next_offset + 1;
700   proto_tree_add_item(sdp_time_tree, hf_time_stop, tvb,
701                       offset, -1, FALSE);
702 }
703
704 static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti){
705   proto_tree *sdp_repeat_time_tree;
706   gint offset,next_offset, tokenlen;
707
708   offset = 0;
709   next_offset = 0;
710   tokenlen = 0;
711
712   sdp_repeat_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
713
714   /* get interval */
715   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
716   if( next_offset == -1 )
717     return;
718
719   tokenlen = next_offset - offset;
720   proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_interval, tvb,
721                       offset, tokenlen, FALSE);
722
723   /* get duration */
724   offset = next_offset + 1;
725   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
726   if( next_offset == -1 )
727     return;
728
729   tokenlen = next_offset - offset;
730   proto_tree_add_item(sdp_repeat_time_tree,hf_repeat_time_duration, tvb,
731                       offset, tokenlen, FALSE);
732
733   /* get offsets */
734   do{
735     offset = next_offset +1;
736     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
737     if(next_offset != -1){
738       tokenlen = next_offset - offset;
739     } else {
740       tokenlen = -1;    /* end of tvbuff */
741     }
742     proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_offset,
743                         tvb, offset, tokenlen, FALSE);
744   } while( next_offset != -1 );
745
746 }
747 static void
748 dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti){
749   proto_tree* sdp_timezone_tree;
750   gint offset, next_offset, tokenlen;
751   offset = 0;
752   next_offset = 0;
753   tokenlen = 0;
754
755   sdp_timezone_tree = proto_item_add_subtree(ti,ett_sdp_timezone);
756
757   do{
758     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
759     if(next_offset == -1)
760       break;
761     tokenlen = next_offset - offset;
762
763     proto_tree_add_item(sdp_timezone_tree,hf_timezone_time,tvb,
764                         offset, tokenlen, FALSE);
765     offset = next_offset + 1;
766     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
767     if(next_offset != -1){
768       tokenlen = next_offset - offset;
769     } else {
770       tokenlen = -1;    /* end of tvbuff */
771     }
772     proto_tree_add_item(sdp_timezone_tree,hf_timezone_offset,tvb,
773                         offset, tokenlen, FALSE);
774     offset = next_offset + 1;
775   } while (next_offset != -1);
776
777 }
778
779
780 static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti){
781   proto_tree *sdp_encryption_key_tree;
782   gint offset, next_offset, tokenlen;
783
784   offset = 0;
785   next_offset = 0;
786   tokenlen = 0;
787
788   sdp_encryption_key_tree = proto_item_add_subtree(ti,ett_sdp_encryption_key);
789
790   next_offset = tvb_find_guint8(tvb,offset,-1,':');
791
792   if(next_offset == -1)
793     return;
794
795   tokenlen = next_offset - offset;
796
797   proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_type,
798                       tvb, offset, tokenlen, FALSE);
799
800   offset = next_offset + 1;
801   proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_data,
802                       tvb, offset, -1, FALSE);
803
804 }
805
806
807
808 static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item * ti){
809   proto_tree *sdp_session_attribute_tree;
810   gint offset, next_offset, tokenlen;
811   guint8 *field_name;
812   
813   offset = 0;
814   next_offset = 0;
815   tokenlen = 0;
816
817   sdp_session_attribute_tree = proto_item_add_subtree(ti,
818                                                       ett_sdp_session_attribute);
819
820   next_offset = tvb_find_guint8(tvb,offset,-1,':');
821
822   if(next_offset == -1)
823     return;
824
825   tokenlen = next_offset - offset;
826
827   proto_tree_add_item(sdp_session_attribute_tree,
828                       hf_session_attribute_field,
829                       tvb, offset, tokenlen, FALSE);
830
831   field_name = tvb_get_ephemeral_string(tvb, offset, tokenlen);
832   
833   offset = next_offset + 1;
834
835   if (strcmp(field_name, "ipbcp") == 0) {
836           
837           offset = tvb_pbrk_guint8(tvb,offset,-1,"0123456789");
838           
839           if (offset == -1)
840                   return;
841           
842           next_offset = tvb_find_guint8(tvb,offset,-1,' ');
843           
844           if (next_offset == -1)
845                   return;
846           
847           tokenlen = next_offset - offset;
848           
849           proto_tree_add_item(sdp_session_attribute_tree,hf_ipbcp_version,tvb,offset,tokenlen,FALSE);
850
851           offset = tvb_pbrk_guint8(tvb,offset,-1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
852
853           if (offset == -1)
854                   return;
855
856           tokenlen = tvb_find_line_end(tvb,offset,-1, &next_offset, FALSE);
857           
858           if (tokenlen == -1) 
859                   return;
860           
861           proto_tree_add_item(sdp_session_attribute_tree,hf_ipbcp_type,tvb,offset,tokenlen,FALSE);
862           
863   } else {
864           proto_tree_add_item(sdp_session_attribute_tree,
865                                                   hf_session_attribute_value,
866                                                   tvb, offset, -1, FALSE);
867
868   }
869 }
870
871 static void
872 dissect_sdp_media(tvbuff_t *tvb, proto_item *ti,
873                   transport_info_t *transport_info){
874   proto_tree *sdp_media_tree;
875   gint offset, next_offset, tokenlen, index;
876   guint8 *media_format;
877
878   offset = 0;
879   next_offset = 0;
880   tokenlen = 0;
881
882   sdp_media_tree = proto_item_add_subtree(ti,ett_sdp_media);
883
884   next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
885
886   if(next_offset == -1)
887     return;
888
889   tokenlen = next_offset - offset;
890
891   proto_tree_add_item(sdp_media_tree, hf_media_media, tvb,
892                       offset, tokenlen, FALSE);
893
894   offset = next_offset + 1;
895
896   next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
897   if(next_offset == -1)
898     return;
899   tokenlen = next_offset - offset;
900   next_offset = tvb_find_guint8(tvb,offset, tokenlen, '/');
901
902   if(next_offset != -1){
903     tokenlen = next_offset - offset;
904     /* Save port info */
905     transport_info->media_port[transport_info->media_count] = tvb_get_ephemeral_string(tvb, offset, tokenlen);
906
907     proto_tree_add_item(sdp_media_tree, hf_media_port, tvb,
908                         offset, tokenlen, FALSE);
909     offset = next_offset + 1;
910     next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
911     if(next_offset == -1)
912       return;
913     tokenlen = next_offset - offset;
914     proto_tree_add_item(sdp_media_tree, hf_media_portcount, tvb,
915                         offset, tokenlen, FALSE);
916     offset = next_offset + 1;
917   } else {
918     next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
919
920     if(next_offset == -1)
921       return;
922     tokenlen = next_offset - offset;
923     /* Save port info */
924     transport_info->media_port[transport_info->media_count] = tvb_get_ephemeral_string(tvb, offset, tokenlen);
925
926     /* XXX Remember Port */
927     proto_tree_add_item(sdp_media_tree, hf_media_port, tvb,
928                         offset, tokenlen, FALSE);
929     offset = next_offset + 1;
930   }
931
932   next_offset = tvb_find_guint8(tvb,offset,-1,' ');
933
934   if( next_offset == -1)
935     return;
936
937   tokenlen = next_offset - offset;
938   /* Save port protocol */
939   transport_info->media_proto[transport_info->media_count] = tvb_get_ephemeral_string(tvb, offset, tokenlen);
940
941   /* XXX Remember Protocol */
942   proto_tree_add_item(sdp_media_tree, hf_media_proto, tvb,
943                       offset, tokenlen, FALSE);
944
945   do{
946     offset = next_offset + 1;
947     next_offset = tvb_find_guint8(tvb,offset,-1,' ');
948
949     if(next_offset == -1){
950       tokenlen = tvb_length_remaining(tvb, offset);     /* End of tvbuff */
951       if (tokenlen == 0)
952         break;  /* Nothing more left */
953     } else {
954       tokenlen = next_offset - offset;
955     }
956     
957     if (strcmp(transport_info->media_proto[transport_info->media_count],
958                "RTP/AVP") == 0) {
959       media_format = tvb_get_ephemeral_string(tvb, offset, tokenlen);
960       proto_tree_add_string(sdp_media_tree, hf_media_format, tvb, offset,
961                              tokenlen, val_to_str(atol(media_format), rtp_payload_type_vals, "%u"));
962           index = transport_info->media[transport_info->media_count].pt_count;
963           transport_info->media[transport_info->media_count].pt[index] = atol(media_format);
964           if (index < (SDP_MAX_RTP_PAYLOAD_TYPES-1))
965                   transport_info->media[transport_info->media_count].pt_count++;
966     } else {
967       proto_tree_add_item(sdp_media_tree, hf_media_format, tvb,
968                           offset, tokenlen, FALSE);
969     }
970   } while (next_offset != -1);
971
972   /* Increase the count of media channels, but don't walk off the end
973      of the arrays. */
974   if (transport_info->media_count < (SDP_MAX_RTP_CHANNELS-1)){
975     transport_info->media_count++;
976   }
977
978   /* XXX Dissect traffic to "Port" as "Protocol"
979    *     Remember this Port/Protocol pair so we can tear it down again later
980    *     Actually, it's harder than that:
981    *         We need to find out the address of the other side first and it
982    *         looks like that info can be found in SIP headers only.
983    */
984
985 }
986
987 static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item * ti, transport_info_t *transport_info){
988   proto_tree *sdp_media_attribute_tree;
989   gint offset, next_offset, tokenlen, n;
990   guint8 *field_name;
991   guint8 *payload_type;
992   guint8 *encoding_name;
993
994   offset = 0;
995   next_offset = 0;
996   tokenlen = 0;
997
998   sdp_media_attribute_tree = proto_item_add_subtree(ti,
999                                                       ett_sdp_media_attribute);
1000
1001   next_offset = tvb_find_guint8(tvb,offset,-1,':');
1002
1003   if(next_offset == -1)
1004     return;
1005
1006   tokenlen = next_offset - offset;
1007
1008   proto_tree_add_item(sdp_media_attribute_tree,
1009                       hf_media_attribute_field,
1010                       tvb, offset, tokenlen, FALSE);
1011
1012   field_name = tvb_get_ephemeral_string(tvb, offset, tokenlen);
1013
1014   offset = next_offset + 1;
1015   proto_tree_add_item(sdp_media_attribute_tree,
1016                       hf_media_attribute_value,
1017                       tvb, offset, -1, FALSE);
1018
1019   /* decode the rtpmap to see if it is DynamicPayload to dissect them automatic */
1020   if (strcmp(field_name, "rtpmap") == 0) {
1021         gint *key;
1022
1023         next_offset = tvb_find_guint8(tvb,offset,-1,' ');
1024
1025     if(next_offset == -1)
1026                 return;
1027
1028     tokenlen = next_offset - offset;
1029
1030     payload_type = tvb_get_ephemeral_string(tvb, offset, tokenlen);
1031
1032     offset = next_offset + 1;
1033
1034     next_offset = tvb_find_guint8(tvb,offset,-1,'/');
1035
1036     if(next_offset == -1){
1037         return;
1038     }
1039
1040     tokenlen = next_offset - offset;
1041
1042     encoding_name = tvb_get_string(tvb, offset, tokenlen);
1043
1044         key=g_malloc( sizeof(gint) );
1045         *key=atol(payload_type);
1046
1047         /* As per RFC2327 it is possible to have multiple Media Descriptions ("m=") For example:
1048
1049                         a=rtpmap:101 G726-32/8000
1050                         m=audio 49170 RTP/AVP 0 97
1051                         a=rtpmap:97 telephone-event/8000
1052                         m=audio 49172 RTP/AVP 97 101
1053                         a=rtpmap:97 G726-24/8000
1054
1055         The Media attributes ("a="s) after the "m=" only apply for that "m=". 
1056         If there is an "a=" before the first "m=", that attribute apply for all the session     (all the "m="s).
1057         */
1058
1059         /* so, if this "a=" appear before any "m=", we add it to all the dynamic hash tables */ 
1060         if (transport_info->media_count == 0) {
1061                 for (n=0; n < SDP_MAX_RTP_CHANNELS; n++) {
1062                         if (n==0)
1063                                 g_hash_table_insert(transport_info->media[n].rtp_dyn_payload, key, encoding_name);
1064                         else {  /* we create a new key and encoding_name to assign to the other hash tables */
1065                                 gint *key2;
1066                                 key2=g_malloc( sizeof(gint) );
1067                                 *key2=atol(payload_type);
1068                                 g_hash_table_insert(transport_info->media[n].rtp_dyn_payload, key2, encoding_name);
1069                         }
1070                 }
1071
1072         /* if the "a=" is after an "m=", only apply to this "m=" */
1073         } else 
1074                 /* in case there is an overflow in SDP_MAX_RTP_CHANNELS, we keep always the last "m=" */
1075                 if (transport_info->media_count == SDP_MAX_RTP_CHANNELS-1)
1076                         g_hash_table_insert(transport_info->media[ transport_info->media_count ].rtp_dyn_payload, key, encoding_name);          
1077                 else
1078                         g_hash_table_insert(transport_info->media[ transport_info->media_count-1 ].rtp_dyn_payload, key, encoding_name);
1079
1080   }
1081 }
1082
1083 void
1084 proto_register_sdp(void)
1085 {
1086   static hf_register_info hf[] = {
1087     { &hf_protocol_version,
1088       { "Session Description Protocol Version (v)",
1089         "sdp.version", FT_STRING, BASE_NONE,NULL,0x0,
1090         "Session Description Protocol Version", HFILL }},
1091     { &hf_owner,
1092       { "Owner/Creator, Session Id (o)",
1093         "sdp.owner", FT_STRING, BASE_NONE, NULL, 0x0,
1094         "Owner/Creator, Session Id", HFILL}},
1095     { &hf_session_name,
1096       { "Session Name (s)",
1097         "sdp.session_name", FT_STRING, BASE_NONE,NULL, 0x0,
1098         "Session Name", HFILL }},
1099     { &hf_session_info,
1100       { "Session Information (i)",
1101         "sdp.session_info", FT_STRING, BASE_NONE, NULL, 0x0,
1102         "Session Information", HFILL }},
1103     { &hf_uri,
1104       { "URI of Description (u)",
1105         "sdp.uri", FT_STRING, BASE_NONE,NULL, 0x0,
1106         "URI of Description", HFILL }},
1107     { &hf_email,
1108       { "E-mail Address (e)",
1109         "sdp.email", FT_STRING, BASE_NONE, NULL, 0x0,
1110         "E-mail Address", HFILL }},
1111     { &hf_phone,
1112       { "Phone Number (p)",
1113         "sdp.phone", FT_STRING, BASE_NONE, NULL, 0x0,
1114         "Phone Number", HFILL }},
1115     { &hf_connection_info,
1116       { "Connection Information (c)",
1117         "sdp.connection_info", FT_STRING, BASE_NONE, NULL, 0x0,
1118         "Connection Information", HFILL }},
1119     { &hf_bandwidth,
1120       { "Bandwidth Information (b)",
1121         "sdp.bandwidth", FT_STRING, BASE_NONE, NULL, 0x0,
1122         "Bandwidth Information", HFILL }},
1123     { &hf_timezone,
1124       { "Time Zone Adjustments (z)",
1125         "sdp.timezone", FT_STRING, BASE_NONE, NULL, 0x0,
1126         "Time Zone Adjustments", HFILL }},
1127     { &hf_encryption_key,
1128       { "Encryption Key (k)",
1129         "sdp.encryption_key", FT_STRING, BASE_NONE, NULL, 0x0,
1130         "Encryption Key", HFILL }},
1131     { &hf_session_attribute,
1132       { "Session Attribute (a)",
1133         "sdp.session_attr", FT_STRING, BASE_NONE, NULL, 0x0,
1134         "Session Attribute", HFILL }},
1135     { &hf_media_attribute,
1136       { "Media Attribute (a)",
1137         "sdp.media_attr", FT_STRING, BASE_NONE, NULL, 0x0,
1138         "Media Attribute", HFILL }},
1139     { &hf_time,
1140       { "Time Description, active time (t)",
1141         "sdp.time", FT_STRING, BASE_NONE, NULL, 0x0,
1142         "Time Description, active time", HFILL }},
1143     { &hf_repeat_time,
1144       { "Repeat Time (r)",
1145         "sdp.repeat_time", FT_STRING, BASE_NONE, NULL, 0x0,
1146         "Repeat Time", HFILL }},
1147     { &hf_media,
1148       { "Media Description, name and address (m)",
1149         "sdp.media", FT_STRING, BASE_NONE, NULL, 0x0,
1150         "Media Description, name and address", HFILL }},
1151     { &hf_media_title,
1152       { "Media Title (i)",
1153         "sdp.media_title",FT_STRING, BASE_NONE, NULL, 0x0,
1154         "Media Title", HFILL }},
1155     { &hf_unknown,
1156       { "Unknown",
1157         "sdp.unknown",FT_STRING, BASE_NONE, NULL, 0x0,
1158         "Unknown", HFILL }},
1159     { &hf_invalid,
1160       { "Invalid line",
1161         "sdp.invalid",FT_STRING, BASE_NONE, NULL, 0x0,
1162         "Invalid line", HFILL }},
1163     { &hf_owner_username,
1164       { "Owner Username",
1165         "sdp.owner.username",FT_STRING, BASE_NONE, NULL, 0x0,
1166         "Owner Username", HFILL }},
1167     { &hf_owner_sessionid,
1168       { "Session ID",
1169         "sdp.owner.sessionid",FT_STRING, BASE_NONE, NULL, 0x0,
1170         "Session ID", HFILL }},
1171     { &hf_owner_version,
1172       { "Session Version",
1173         "sdp.owner.version",FT_STRING, BASE_NONE, NULL, 0x0,
1174         "Session Version", HFILL }},
1175     { &hf_owner_network_type,
1176       { "Owner Network Type",
1177         "sdp.owner.network_type",FT_STRING, BASE_NONE, NULL, 0x0,
1178         "Owner Network Type", HFILL }},
1179     { &hf_owner_address_type,
1180       { "Owner Address Type",
1181         "sdp.owner.address_type",FT_STRING, BASE_NONE, NULL, 0x0,
1182         "Owner Address Type", HFILL }},
1183     { &hf_owner_address,
1184       { "Owner Address",
1185         "sdp.owner.address",FT_STRING, BASE_NONE, NULL, 0x0,
1186         "Owner Address", HFILL }},
1187     { &hf_connection_info_network_type,
1188       { "Connection Network Type",
1189         "sdp.connection_info.network_type",FT_STRING, BASE_NONE, NULL, 0x0,
1190         "Connection Network Type", HFILL }},
1191     { &hf_connection_info_address_type,
1192       { "Connection Address Type",
1193         "sdp.connection_info.address_type",FT_STRING, BASE_NONE, NULL, 0x0,
1194         "Connection Address Type", HFILL }},
1195     { &hf_connection_info_connection_address,
1196       { "Connection Address",
1197         "sdp.connection_info.address",FT_STRING, BASE_NONE, NULL, 0x0,
1198         "Connection Address", HFILL }},
1199     { &hf_connection_info_ttl,
1200       { "Connection TTL",
1201         "sdp.connection_info.ttl",FT_STRING, BASE_NONE, NULL, 0x0,
1202         "Connection TTL", HFILL }},
1203     { &hf_connection_info_num_addr,
1204       { "Connection Number of Addresses",
1205         "sdp.connection_info.num_addr",FT_STRING, BASE_NONE, NULL, 0x0,
1206         "Connection Number of Addresses", HFILL }},
1207     { &hf_bandwidth_modifier,
1208       { "Bandwidth Modifier",
1209         "sdp.bandwidth.modifier",FT_STRING, BASE_NONE, NULL, 0x0,
1210         "Bandwidth Modifier", HFILL }},
1211     { &hf_bandwidth_value,
1212       { "Bandwidth Value",
1213         "sdp.bandwidth.value",FT_STRING, BASE_NONE, NULL, 0x0,
1214         "Bandwidth Value (in kbits/s)", HFILL }},
1215     { &hf_time_start,
1216       { "Session Start Time",
1217         "sdp.time.start",FT_STRING, BASE_NONE, NULL, 0x0,
1218         "Session Start Time", HFILL }},
1219     { &hf_time_stop,
1220       { "Session Stop Time",
1221         "sdp.time.stop",FT_STRING, BASE_NONE, NULL, 0x0,
1222         "Session Stop Time", HFILL }},
1223     { &hf_repeat_time_interval,
1224       { "Repeat Interval",
1225         "sdp.repeat_time.interval",FT_STRING, BASE_NONE, NULL, 0x0,
1226         "Repeat Interval", HFILL }},
1227     { &hf_repeat_time_duration,
1228       { "Repeat Duration",
1229         "sdp.repeat_time.duration",FT_STRING, BASE_NONE, NULL, 0x0,
1230         "Repeat Duration", HFILL }},
1231     { &hf_repeat_time_offset,
1232       { "Repeat Offset",
1233         "sdp.repeat_time.offset",FT_STRING, BASE_NONE, NULL, 0x0,
1234         "Repeat Offset", HFILL }},
1235     { &hf_timezone_time,
1236       { "Timezone Time",
1237         "sdp.timezone.time",FT_STRING, BASE_NONE, NULL, 0x0,
1238         "Timezone Time", HFILL }},
1239     { &hf_timezone_offset,
1240       { "Timezone Offset",
1241         "sdp.timezone.offset",FT_STRING, BASE_NONE, NULL, 0x0,
1242         "Timezone Offset", HFILL }},
1243     { &hf_encryption_key_type,
1244       { "Key Type",
1245         "sdp.encryption_key.type",FT_STRING, BASE_NONE, NULL, 0x0,
1246         "Type", HFILL }},
1247     { &hf_encryption_key_data,
1248       { "Key Data",
1249         "sdp.encryption_key.data",FT_STRING, BASE_NONE, NULL, 0x0,
1250         "Data", HFILL }},
1251     { &hf_session_attribute_field,
1252       { "Session Attribute Fieldname",
1253         "sdp.session_attr.field",FT_STRING, BASE_NONE, NULL, 0x0,
1254         "Session Attribute Fieldname", HFILL }},
1255     { &hf_session_attribute_value,
1256       { "Session Attribute Value",
1257         "sdp.session_attr.value",FT_STRING, BASE_NONE, NULL, 0x0,
1258         "Session Attribute Value", HFILL }},
1259     { &hf_media_media,
1260       { "Media Type",
1261         "sdp.media.media",FT_STRING, BASE_NONE, NULL, 0x0,
1262         "Media Type", HFILL }},
1263     { &hf_media_port,
1264       { "Media Port",
1265         "sdp.media.port",FT_STRING, BASE_NONE, NULL, 0x0,
1266         "Media Port", HFILL }},
1267     { &hf_media_portcount,
1268       { "Media Port Count",
1269         "sdp.media.portcount",FT_STRING, BASE_NONE, NULL, 0x0,
1270         "Media Port Count", HFILL }},
1271     { &hf_media_proto,
1272       { "Media Proto",
1273         "sdp.media.proto",FT_STRING, BASE_NONE, NULL, 0x0,
1274         "Media Protocol", HFILL }},
1275     { &hf_media_format,
1276       { "Media Format",
1277         "sdp.media.format",FT_STRING, BASE_NONE, NULL, 0x0,
1278         "Media Format", HFILL }},
1279     { &hf_media_attribute_field,
1280       { "Media Attribute Fieldname",
1281         "sdp.media_attribute.field",FT_STRING, BASE_NONE, NULL, 0x0,
1282         "Media Attribute Fieldname", HFILL }},
1283     { &hf_media_attribute_value,
1284       { "Media Attribute Value",
1285         "sdp.media_attribute.value",FT_STRING, BASE_NONE, NULL, 0x0,
1286         "Media Attribute Value", HFILL }},
1287     { &hf_ipbcp_version,
1288       { "IPBCP Protocol Version",
1289         "ipbcp.version",FT_STRING, BASE_NONE, NULL, 0x0,
1290         "IPBCP Protocol Version", HFILL }},
1291     { &hf_ipbcp_type,
1292       { "IPBCP Command Type",
1293         "ipbcp.command",FT_STRING, BASE_NONE, NULL, 0x0,
1294         "IPBCP Command Type", HFILL }},
1295   };
1296   static gint *ett[] = {
1297     &ett_sdp,
1298     &ett_sdp_owner,
1299     &ett_sdp_connection_info,
1300     &ett_sdp_bandwidth,
1301     &ett_sdp_time,
1302     &ett_sdp_repeat_time,
1303     &ett_sdp_timezone,
1304     &ett_sdp_encryption_key,
1305     &ett_sdp_session_attribute,
1306     &ett_sdp_media,
1307     &ett_sdp_media_attribute,
1308   };
1309
1310   proto_sdp = proto_register_protocol("Session Description Protocol",
1311                                       "SDP", "sdp");
1312   proto_register_field_array(proto_sdp, hf, array_length(hf));
1313   proto_register_subtree_array(ett, array_length(ett));
1314
1315   /*
1316    * Register the dissector by name, so other dissectors can
1317    * grab it by name rather than just referring to it directly
1318    * (you can't refer to it directly from a plugin dissector
1319    * on Windows without stuffing it into the Big Transfer Vector).
1320    */
1321   register_dissector("sdp", dissect_sdp, proto_sdp);
1322
1323   /* Register for tapping */
1324   sdp_tap = register_tap("sdp");
1325 }
1326
1327 void
1328 proto_reg_handoff_sdp(void)
1329 {
1330   dissector_handle_t sdp_handle;
1331
1332   rtp_handle = find_dissector("rtp");
1333   rtcp_handle = find_dissector("rtcp");
1334
1335   t38_handle = find_dissector("t38");
1336
1337   sdp_handle = find_dissector("sdp");
1338   dissector_add_string("media_type", "application/sdp", sdp_handle);
1339 }