2 * Routines for SDP packet disassembly (RFC 2327)
4 * Jason Lango <jal@netapp.com>
5 * Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu>
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
11 * Copyright 1998 Gerald Combs
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.
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.
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.
33 #include <sys/types.h>
34 #ifdef HAVE_SYS_SOCKET_H
35 #include <sys/socket.h>
37 #ifdef HAVE_NETINET_IN_H
38 # include <netinet/in.h>
40 #ifdef HAVE_ARPA_INET_H
41 #include <arpa/inet.h>
43 #ifdef NEED_INET_ATON_H
44 # include <epan/inet_aton.h>
47 #ifdef HAVE_WINSOCK2_H
48 #include <winsock2.h> /* needed to define AF_ values on Windows */
51 #ifdef NEED_INET_V6DEFS_H
52 # include "inet_v6defs.h"
56 #include <epan/packet.h>
57 #include <epan/conversation.h>
58 #include <epan/strutil.h>
59 #include <epan/emem.h>
62 #include "packet-sdp.h"
64 #include "packet-rtp.h"
66 #include "packet-rtcp.h"
68 #include "packet-t38.h"
70 static dissector_handle_t rtp_handle=NULL;
71 static dissector_handle_t rtcp_handle=NULL;
73 static dissector_handle_t t38_handle=NULL;
75 static int sdp_tap = -1;
77 static int proto_sdp = -1;
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;
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;
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;
117 /* hf_bandwidth subfields */
118 static int hf_bandwidth_modifier = -1;
119 static int hf_bandwidth_value = -1;
121 /* hf_time subfields */
122 static int hf_time_start = -1;
123 static int hf_time_stop = -1;
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;
130 /* hf_timezone subfields */
131 static int hf_timezone_time = -1;
132 static int hf_timezone_offset = -1;
134 /* hf_encryption_key subfields */
135 static int hf_encryption_key_type = -1;
136 static int hf_encryption_key_data = -1;
138 /* hf_session_attribute subfields */
139 static int hf_session_attribute_field = -1;
140 static int hf_session_attribute_value = -1;
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;
149 /* hf_session_attribute subfields */
150 static int hf_media_attribute_field = -1;
151 static int hf_media_attribute_value = -1;
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;
167 #define SDP_MAX_RTP_CHANNELS 4
168 #define SDP_MAX_RTP_PAYLOAD_TYPES 20
171 gint32 pt[SDP_MAX_RTP_PAYLOAD_TYPES];
173 GHashTable *rtp_dyn_payload;
174 } transport_media_pt_t;
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];
185 /* static functions */
187 static void call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti,
188 transport_info_t *transport_info);
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);
205 dissect_sdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
207 proto_tree *sdp_tree;
208 proto_item *ti, *sub_ti;
212 gboolean in_media_description;
222 transport_info_t transport_info;
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;
232 sdp_packet_info *sdp_pi;
234 /* Initialise packet info for passing to tap */
235 sdp_pi = ep_alloc(sizeof(sdp_packet_info));
236 sdp_pi->summary_str[0] = '\0';
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++)
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);
248 transport_info.media_count = 0;
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."
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
264 if (check_col(pinfo->cinfo, COL_PROTOCOL))
265 col_append_str(pinfo->cinfo, COL_PROTOCOL, "/SDP");
267 if (check_col(pinfo->cinfo, COL_INFO)) {
268 /* XXX: Needs description. */
269 col_append_str(pinfo->cinfo, COL_INFO, ", with session description");
272 ti = proto_tree_add_item(tree, proto_sdp, tvb, offset, -1, FALSE);
273 sdp_tree = proto_item_add_subtree(ti, ett_sdp);
276 * Show the SDP message a line at a time.
278 in_media_description = FALSE;
279 while (tvb_reported_length_remaining(tvb, offset) > 0) {
281 * Find the end of the line.
283 linelen = tvb_find_line_end_unquoted(tvb, offset, -1,
287 * Line must contain at least e.g. "v=".
292 type = tvb_get_guint8(tvb,offset);
293 delim = tvb_get_guint8(tvb,offset + 1);
295 proto_tree_add_item(sdp_tree,hf_invalid,tvb, offset,
297 offset = next_offset;
306 hf = hf_protocol_version;
312 hf = hf_session_name;
315 if (in_media_description) {
319 hf = hf_session_info;
332 hf = hf_connection_info;
345 in_media_description = TRUE;
348 hf = hf_encryption_key;
351 if (in_media_description) {
352 hf = hf_media_attribute;
355 hf = hf_session_attribute;
366 if (hf == hf_unknown)
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,
373 proto_registrar_get_name(hf),
375 linelen - tokenoffset));
376 call_sdp_subdissector(tvb_new_subset(tvb,offset+tokenoffset,
378 linelen-tokenoffset),
379 hf,sub_ti,&transport_info),
380 offset = next_offset;
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!
390 for (n = 0; n < transport_info.media_count; n++)
392 if(transport_info.media_port[n]!=NULL) {
393 port = atol(transport_info.media_port[n]);
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) );
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*/
408 src_addr.type=AT_IPv4;
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*/
416 src_addr.type=AT_IPv6;
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;
427 rtp_add_address(pinfo, &src_addr, port, 0,
428 "SDP", pinfo->fd->num, transport_info.media[n].rtp_dyn_payload);
433 rtcp_add_address(pinfo, &src_addr, port, 0,
434 "SDP", pinfo->fd->num);
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;
442 t38_add_address(pinfo, &src_addr, port, 0, "SDP", pinfo->fd->num);
446 /* Create the summary str for the Voip Call analysis */
447 for (i = 0; i < transport_info.media[n].pt_count; i++)
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]);
453 g_snprintf(sdp_pi->summary_str, 50, "%s %s", sdp_pi->summary_str, str_dyn_pt);
455 g_snprintf(sdp_pi->summary_str, 50, "%s %d", sdp_pi->summary_str, transport_info.media[n].pt[i]);
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"));
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);
465 /* Free the remainded hash tables not used */
466 for (n = transport_info.media_count; n < SDP_MAX_RTP_CHANNELS; n++)
468 rtp_free_hash_dyn_payload(transport_info.media[n].rtp_dyn_payload);
472 datalen = tvb_length_remaining(tvb, offset);
474 proto_tree_add_text(sdp_tree, tvb, offset, datalen,
475 "Data (%d bytes)", datalen);
478 /* Report this packet to the tap */
479 tap_queue_packet(sdp_tap, pinfo, sdp_pi);
483 call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti, transport_info_t *transport_info){
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);
508 dissect_sdp_owner(tvbuff_t *tvb, proto_item *ti){
509 proto_tree *sdp_owner_tree;
510 gint offset,next_offset,tokenlen;
516 sdp_owner_tree = proto_item_add_subtree(ti,ett_sdp_owner);
518 /* Find the username */
519 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
520 if( next_offset == -1 )
522 tokenlen = next_offset - offset;
524 proto_tree_add_item(sdp_owner_tree,hf_owner_username,tvb, offset,tokenlen,
526 offset = next_offset + 1;
528 /* Find the session id */
529 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
530 if( next_offset == -1 )
532 tokenlen = next_offset - offset;
534 proto_tree_add_item(sdp_owner_tree,hf_owner_sessionid, tvb,
535 offset,tokenlen,FALSE);
536 offset = next_offset + 1;
538 /* Find the version */
539 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
540 if( next_offset == -1 )
542 tokenlen = next_offset - offset;
544 proto_tree_add_item(sdp_owner_tree,hf_owner_version, tvb,
545 offset,tokenlen,FALSE);
546 offset = next_offset + 1;
548 /* Find the network type */
549 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
550 if( next_offset == -1 )
552 tokenlen = next_offset - offset;
554 proto_tree_add_item(sdp_owner_tree,hf_owner_network_type, tvb,
555 offset,tokenlen,FALSE);
556 offset = next_offset + 1;
558 /* Find the address type */
559 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
560 if( next_offset == -1 )
562 tokenlen = next_offset - offset;
564 proto_tree_add_item(sdp_owner_tree,hf_owner_address_type, tvb,
565 offset,tokenlen,FALSE);
566 offset = next_offset + 1;
568 /* Find the address */
569 proto_tree_add_item(sdp_owner_tree,hf_owner_address, tvb, offset, -1, FALSE);
573 * XXX - this can leak memory if an exception is thrown after we've fetched
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;
586 sdp_connection_info_tree = proto_item_add_subtree(ti,
587 ett_sdp_connection_info);
589 /* Find the network type */
590 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
591 if( next_offset == -1 )
593 tokenlen = next_offset - offset;
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;
600 /* Find the address type */
601 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
602 if( next_offset == -1 )
604 tokenlen = next_offset - offset;
605 /* Save connection address type */
606 transport_info->connection_type = tvb_get_ephemeral_string(tvb, offset, tokenlen);
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;
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));
623 tokenlen = next_offset - offset;
624 /* Save connection address */
625 transport_info->connection_address = tvb_get_ephemeral_string(tvb, offset, tokenlen);
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 */
637 tokenlen = next_offset - offset;
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,
651 dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti){
652 proto_tree * sdp_bandwidth_tree;
653 gint offset, next_offset, tokenlen;
659 sdp_bandwidth_tree = proto_item_add_subtree(ti,ett_sdp_bandwidth);
661 /* find the modifier */
662 next_offset = tvb_find_guint8(tvb,offset,-1,':');
664 if( next_offset == -1)
667 tokenlen = next_offset - offset;
669 proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_modifier,
670 tvb, offset, tokenlen, FALSE);
672 offset = next_offset + 1;
674 proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_value,
675 tvb, offset, -1, FALSE);
679 static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti){
680 proto_tree *sdp_time_tree;
681 gint offset,next_offset, tokenlen;
687 sdp_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
690 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
691 if( next_offset == -1 )
694 tokenlen = next_offset - offset;
695 proto_tree_add_item(sdp_time_tree, hf_time_start, tvb,
696 offset, tokenlen, FALSE);
699 offset = next_offset + 1;
700 proto_tree_add_item(sdp_time_tree, hf_time_stop, tvb,
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;
712 sdp_repeat_time_tree = proto_item_add_subtree(ti,ett_sdp_time);
715 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
716 if( next_offset == -1 )
719 tokenlen = next_offset - offset;
720 proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_interval, tvb,
721 offset, tokenlen, FALSE);
724 offset = next_offset + 1;
725 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
726 if( next_offset == -1 )
729 tokenlen = next_offset - offset;
730 proto_tree_add_item(sdp_repeat_time_tree,hf_repeat_time_duration, tvb,
731 offset, tokenlen, FALSE);
735 offset = next_offset +1;
736 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
737 if(next_offset != -1){
738 tokenlen = next_offset - offset;
740 tokenlen = -1; /* end of tvbuff */
742 proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_offset,
743 tvb, offset, tokenlen, FALSE);
744 } while( next_offset != -1 );
748 dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti){
749 proto_tree* sdp_timezone_tree;
750 gint offset, next_offset, tokenlen;
755 sdp_timezone_tree = proto_item_add_subtree(ti,ett_sdp_timezone);
758 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
759 if(next_offset == -1)
761 tokenlen = next_offset - offset;
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;
770 tokenlen = -1; /* end of tvbuff */
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);
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;
788 sdp_encryption_key_tree = proto_item_add_subtree(ti,ett_sdp_encryption_key);
790 next_offset = tvb_find_guint8(tvb,offset,-1,':');
792 if(next_offset == -1)
795 tokenlen = next_offset - offset;
797 proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_type,
798 tvb, offset, tokenlen, FALSE);
800 offset = next_offset + 1;
801 proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_data,
802 tvb, offset, -1, FALSE);
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;
817 sdp_session_attribute_tree = proto_item_add_subtree(ti,
818 ett_sdp_session_attribute);
820 next_offset = tvb_find_guint8(tvb,offset,-1,':');
822 if(next_offset == -1)
825 tokenlen = next_offset - offset;
827 proto_tree_add_item(sdp_session_attribute_tree,
828 hf_session_attribute_field,
829 tvb, offset, tokenlen, FALSE);
831 field_name = tvb_get_ephemeral_string(tvb, offset, tokenlen);
833 offset = next_offset + 1;
835 if (strcmp(field_name, "ipbcp") == 0) {
837 offset = tvb_pbrk_guint8(tvb,offset,-1,"0123456789");
842 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
844 if (next_offset == -1)
847 tokenlen = next_offset - offset;
849 proto_tree_add_item(sdp_session_attribute_tree,hf_ipbcp_version,tvb,offset,tokenlen,FALSE);
851 offset = tvb_pbrk_guint8(tvb,offset,-1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
856 tokenlen = tvb_find_line_end(tvb,offset,-1, &next_offset, FALSE);
861 proto_tree_add_item(sdp_session_attribute_tree,hf_ipbcp_type,tvb,offset,tokenlen,FALSE);
864 proto_tree_add_item(sdp_session_attribute_tree,
865 hf_session_attribute_value,
866 tvb, offset, -1, FALSE);
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;
882 sdp_media_tree = proto_item_add_subtree(ti,ett_sdp_media);
884 next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
886 if(next_offset == -1)
889 tokenlen = next_offset - offset;
891 proto_tree_add_item(sdp_media_tree, hf_media_media, tvb,
892 offset, tokenlen, FALSE);
894 offset = next_offset + 1;
896 next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
897 if(next_offset == -1)
899 tokenlen = next_offset - offset;
900 next_offset = tvb_find_guint8(tvb,offset, tokenlen, '/');
902 if(next_offset != -1){
903 tokenlen = next_offset - offset;
905 transport_info->media_port[transport_info->media_count] = tvb_get_ephemeral_string(tvb, offset, tokenlen);
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)
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;
918 next_offset = tvb_find_guint8(tvb,offset, -1, ' ');
920 if(next_offset == -1)
922 tokenlen = next_offset - offset;
924 transport_info->media_port[transport_info->media_count] = tvb_get_ephemeral_string(tvb, offset, tokenlen);
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;
932 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
934 if( next_offset == -1)
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);
941 /* XXX Remember Protocol */
942 proto_tree_add_item(sdp_media_tree, hf_media_proto, tvb,
943 offset, tokenlen, FALSE);
946 offset = next_offset + 1;
947 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
949 if(next_offset == -1){
950 tokenlen = tvb_length_remaining(tvb, offset); /* End of tvbuff */
952 break; /* Nothing more left */
954 tokenlen = next_offset - offset;
957 if (strcmp(transport_info->media_proto[transport_info->media_count],
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++;
967 proto_tree_add_item(sdp_media_tree, hf_media_format, tvb,
968 offset, tokenlen, FALSE);
970 } while (next_offset != -1);
972 /* Increase the count of media channels, but don't walk off the end
974 if (transport_info->media_count < (SDP_MAX_RTP_CHANNELS-1)){
975 transport_info->media_count++;
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.
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;
991 guint8 *payload_type;
992 guint8 *encoding_name;
998 sdp_media_attribute_tree = proto_item_add_subtree(ti,
999 ett_sdp_media_attribute);
1001 next_offset = tvb_find_guint8(tvb,offset,-1,':');
1003 if(next_offset == -1)
1006 tokenlen = next_offset - offset;
1008 proto_tree_add_item(sdp_media_attribute_tree,
1009 hf_media_attribute_field,
1010 tvb, offset, tokenlen, FALSE);
1012 field_name = tvb_get_ephemeral_string(tvb, offset, tokenlen);
1014 offset = next_offset + 1;
1015 proto_tree_add_item(sdp_media_attribute_tree,
1016 hf_media_attribute_value,
1017 tvb, offset, -1, FALSE);
1019 /* decode the rtpmap to see if it is DynamicPayload to dissect them automatic */
1020 if (strcmp(field_name, "rtpmap") == 0) {
1023 next_offset = tvb_find_guint8(tvb,offset,-1,' ');
1025 if(next_offset == -1)
1028 tokenlen = next_offset - offset;
1030 payload_type = tvb_get_ephemeral_string(tvb, offset, tokenlen);
1032 offset = next_offset + 1;
1034 next_offset = tvb_find_guint8(tvb,offset,-1,'/');
1036 if(next_offset == -1){
1040 tokenlen = next_offset - offset;
1042 encoding_name = tvb_get_string(tvb, offset, tokenlen);
1044 key=g_malloc( sizeof(gint) );
1045 *key=atol(payload_type);
1047 /* As per RFC2327 it is possible to have multiple Media Descriptions ("m=") For example:
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
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).
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++) {
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 */
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);
1072 /* if the "a=" is after an "m=", only apply to this "m=" */
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);
1078 g_hash_table_insert(transport_info->media[ transport_info->media_count-1 ].rtp_dyn_payload, key, encoding_name);
1084 proto_register_sdp(void)
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 }},
1092 { "Owner/Creator, Session Id (o)",
1093 "sdp.owner", FT_STRING, BASE_NONE, NULL, 0x0,
1094 "Owner/Creator, Session Id", HFILL}},
1096 { "Session Name (s)",
1097 "sdp.session_name", FT_STRING, BASE_NONE,NULL, 0x0,
1098 "Session Name", HFILL }},
1100 { "Session Information (i)",
1101 "sdp.session_info", FT_STRING, BASE_NONE, NULL, 0x0,
1102 "Session Information", HFILL }},
1104 { "URI of Description (u)",
1105 "sdp.uri", FT_STRING, BASE_NONE,NULL, 0x0,
1106 "URI of Description", HFILL }},
1108 { "E-mail Address (e)",
1109 "sdp.email", FT_STRING, BASE_NONE, NULL, 0x0,
1110 "E-mail Address", HFILL }},
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 }},
1120 { "Bandwidth Information (b)",
1121 "sdp.bandwidth", FT_STRING, BASE_NONE, NULL, 0x0,
1122 "Bandwidth Information", HFILL }},
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 }},
1140 { "Time Description, active time (t)",
1141 "sdp.time", FT_STRING, BASE_NONE, NULL, 0x0,
1142 "Time Description, active time", HFILL }},
1144 { "Repeat Time (r)",
1145 "sdp.repeat_time", FT_STRING, BASE_NONE, NULL, 0x0,
1146 "Repeat Time", HFILL }},
1148 { "Media Description, name and address (m)",
1149 "sdp.media", FT_STRING, BASE_NONE, NULL, 0x0,
1150 "Media Description, name and address", HFILL }},
1152 { "Media Title (i)",
1153 "sdp.media_title",FT_STRING, BASE_NONE, NULL, 0x0,
1154 "Media Title", HFILL }},
1157 "sdp.unknown",FT_STRING, BASE_NONE, NULL, 0x0,
1158 "Unknown", HFILL }},
1161 "sdp.invalid",FT_STRING, BASE_NONE, NULL, 0x0,
1162 "Invalid line", HFILL }},
1163 { &hf_owner_username,
1165 "sdp.owner.username",FT_STRING, BASE_NONE, NULL, 0x0,
1166 "Owner Username", HFILL }},
1167 { &hf_owner_sessionid,
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,
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,
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 }},
1216 { "Session Start Time",
1217 "sdp.time.start",FT_STRING, BASE_NONE, NULL, 0x0,
1218 "Session Start Time", HFILL }},
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,
1233 "sdp.repeat_time.offset",FT_STRING, BASE_NONE, NULL, 0x0,
1234 "Repeat Offset", HFILL }},
1235 { &hf_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,
1245 "sdp.encryption_key.type",FT_STRING, BASE_NONE, NULL, 0x0,
1247 { &hf_encryption_key_data,
1249 "sdp.encryption_key.data",FT_STRING, BASE_NONE, NULL, 0x0,
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 }},
1261 "sdp.media.media",FT_STRING, BASE_NONE, NULL, 0x0,
1262 "Media Type", HFILL }},
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 }},
1273 "sdp.media.proto",FT_STRING, BASE_NONE, NULL, 0x0,
1274 "Media Protocol", HFILL }},
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 }},
1292 { "IPBCP Command Type",
1293 "ipbcp.command",FT_STRING, BASE_NONE, NULL, 0x0,
1294 "IPBCP Command Type", HFILL }},
1296 static gint *ett[] = {
1299 &ett_sdp_connection_info,
1302 &ett_sdp_repeat_time,
1304 &ett_sdp_encryption_key,
1305 &ett_sdp_session_attribute,
1307 &ett_sdp_media_attribute,
1310 proto_sdp = proto_register_protocol("Session Description Protocol",
1312 proto_register_field_array(proto_sdp, hf, array_length(hf));
1313 proto_register_subtree_array(ett, array_length(ett));
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).
1321 register_dissector("sdp", dissect_sdp, proto_sdp);
1323 /* Register for tapping */
1324 sdp_tap = register_tap("sdp");
1328 proto_reg_handoff_sdp(void)
1330 dissector_handle_t sdp_handle;
1332 rtp_handle = find_dissector("rtp");
1333 rtcp_handle = find_dissector("rtcp");
1335 t38_handle = find_dissector("t38");
1337 sdp_handle = find_dissector("sdp");
1338 dissector_add_string("media_type", "application/sdp", sdp_handle);