/* packet-sdp.c * Routines for SDP packet disassembly (RFC 2327) * * Jason Lango * Liberally copied from packet-http.c, by Guy Harris * * $Id: packet-sdp.c,v 1.45 2004/01/16 19:51:55 guy Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include #include #include #ifdef HAVE_SYS_SOCKET_H #include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef NEED_INET_ATON_H # include #endif #include #include #include #include static dissector_handle_t rtp_handle=NULL; static dissector_handle_t rtcp_handle=NULL; static int proto_sdp = -1; /* Top level fields */ static int hf_protocol_version = -1; static int hf_owner = -1; static int hf_session_name = -1; static int hf_session_info = -1; static int hf_uri = -1; static int hf_email = -1; static int hf_phone = -1; static int hf_connection_info = -1; static int hf_bandwidth = -1; static int hf_timezone = -1; static int hf_encryption_key = -1; static int hf_session_attribute = -1; static int hf_media_attribute = -1; static int hf_time = -1; static int hf_repeat_time = -1; static int hf_media = -1; static int hf_media_title = -1; static int hf_unknown = -1; static int hf_invalid = -1; /* hf_owner subfields*/ static int hf_owner_username = -1; static int hf_owner_sessionid = -1; static int hf_owner_version = -1; static int hf_owner_network_type = -1; static int hf_owner_address_type = -1; static int hf_owner_address = -1; /* hf_connection_info subfields */ static int hf_connection_info_network_type = -1; static int hf_connection_info_address_type = -1; static int hf_connection_info_connection_address = -1; static int hf_connection_info_ttl = -1; static int hf_connection_info_num_addr = -1; /* hf_bandwidth subfields */ static int hf_bandwidth_modifier = -1; static int hf_bandwidth_value = -1; /* hf_time subfields */ static int hf_time_start = -1; static int hf_time_stop = -1; /* hf_repeat_time subfield */ static int hf_repeat_time_interval = -1; static int hf_repeat_time_duration = -1; static int hf_repeat_time_offset = -1; /* hf_timezone subfields */ static int hf_timezone_time = -1; static int hf_timezone_offset = -1; /* hf_encryption_key subfields */ static int hf_encryption_key_type = -1; static int hf_encryption_key_data = -1; /* hf_session_attribute subfields */ static int hf_session_attribute_field = -1; static int hf_session_attribute_value = -1; /* hf_media subfields */ static int hf_media_media = -1; static int hf_media_port = -1; static int hf_media_portcount = -1; static int hf_media_proto = -1; static int hf_media_format = -1; /* hf_session_attribute subfields */ static int hf_media_attribute_field = -1; static int hf_media_attribute_value = -1; /* trees */ static int ett_sdp = -1; static int ett_sdp_owner = -1; static int ett_sdp_connection_info = -1; static int ett_sdp_bandwidth = -1; static int ett_sdp_time = -1; static int ett_sdp_repeat_time = -1; static int ett_sdp_timezone = -1; static int ett_sdp_encryption_key = -1; static int ett_sdp_session_attribute = -1; static int ett_sdp_media = -1; static int ett_sdp_media_attribute = -1; #define SDP_MAX_RTP_CHANNELS 4 typedef struct { char *connection_address; char *connection_type; char *media_port[SDP_MAX_RTP_CHANNELS]; char *media_proto[SDP_MAX_RTP_CHANNELS]; gint8 media_count; } transport_info_t; /* static functions */ static void call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti, transport_info_t *transport_info); /* Subdissector functions */ static void dissect_sdp_owner(tvbuff_t *tvb, proto_item* ti); static void dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti, transport_info_t *transport_info); static void dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti); static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti); static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti); static void dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti); static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti); static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item *ti); static void dissect_sdp_media(tvbuff_t *tvb, proto_item *ti, transport_info_t *transport_info); static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item *ti); static void dissect_sdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *sdp_tree; proto_item *ti, *sub_ti; gint offset = 0; gint next_offset; int linelen; gboolean in_media_description; guchar type; guchar delim; int datalen; int tokenoffset; int hf = -1; char *string; address src_addr; conversation_t *conv=NULL; transport_info_t transport_info; guint32 ipv4_address=0; guint32 ipv4_port=0; gboolean is_rtp=FALSE; gboolean is_ipv4_addr=FALSE; struct in_addr ipaddr; gint n; /* Initialise RTP channel info */ transport_info.connection_address=NULL; transport_info.connection_type=NULL; for (n=0; n < SDP_MAX_RTP_CHANNELS; n++) { transport_info.media_port[n]=NULL; transport_info.media_proto[n]=NULL; } transport_info.media_count = 0; /* * As RFC 2327 says, "SDP is purely a format for session * description - it does not incorporate a transport protocol, * and is intended to use different transport protocols as * appropriate including the Session Announcement Protocol, * Session Initiation Protocol, Real-Time Streaming Protocol, * electronic mail using the MIME extensions, and the * Hypertext Transport Protocol." * * We therefore don't set the protocol or info columns; * instead, we append to them, so that we don't erase * what the protocol inside which the SDP stuff resides * put there. */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) col_append_str(pinfo->cinfo, COL_PROTOCOL, "/SDP"); if (check_col(pinfo->cinfo, COL_INFO)) { /* XXX: Needs description. */ col_append_str(pinfo->cinfo, COL_INFO, ", with session description"); } ti = proto_tree_add_item(tree, proto_sdp, tvb, offset, -1, FALSE); sdp_tree = proto_item_add_subtree(ti, ett_sdp); /* * Show the SDP message a line at a time. */ in_media_description = FALSE; while (tvb_reported_length_remaining(tvb, offset) > 0) { /* * Find the end of the line. */ linelen = tvb_find_line_end_unquoted(tvb, offset, -1, &next_offset); /* * Line must contain at least e.g. "v=". */ if (linelen < 2) break; type = tvb_get_guint8(tvb,offset); delim = tvb_get_guint8(tvb,offset + 1); if (delim != '=') { proto_tree_add_item(sdp_tree,hf_invalid,tvb, offset, linelen, FALSE); offset = next_offset; continue; } /* * Attributes. */ switch (type) { case 'v': hf = hf_protocol_version; break; case 'o': hf = hf_owner; break; case 's': hf = hf_session_name; break; case 'i': if (in_media_description) { hf = hf_media_title; } else{ hf = hf_session_info; } break; case 'u': hf = hf_uri; break; case 'e': hf = hf_email; break; case 'p': hf = hf_phone; break; case 'c': hf = hf_connection_info; break; case 'b': hf = hf_bandwidth; break; case 't': hf = hf_time; break; case 'r': hf = hf_repeat_time; break; case 'm': hf = hf_media; in_media_description = TRUE; break; case 'k': hf = hf_encryption_key; break; case 'a': if (in_media_description) { hf = hf_media_attribute; } else{ hf = hf_session_attribute; } break; case 'z': hf = hf_timezone; break; default: hf = hf_unknown; break; } tokenoffset = 2; if (hf == hf_unknown) tokenoffset = 0; string = tvb_get_string(tvb, offset + tokenoffset, linelen - tokenoffset); sub_ti = proto_tree_add_string_format(sdp_tree,hf,tvb, offset, linelen, string, "%s: %s", proto_registrar_get_name(hf), format_text(string, linelen - tokenoffset)); g_free(string); call_sdp_subdissector(tvb_new_subset(tvb,offset+tokenoffset, linelen-tokenoffset, linelen-tokenoffset), hf,sub_ti,&transport_info), offset = next_offset; } /* Now look, if we have strings collected. * Try to convert ipv4 addresses and ports into binary format, * so we can use them to detect rtp and rtcp streams. * Don't forget to free the strings! */ for (n = 0; n < transport_info.media_count; n++) { if(transport_info.media_port[n]!=NULL) { ipv4_port = atol(transport_info.media_port[n]); g_free(transport_info.media_port[n]); } if(transport_info.media_proto[n]!=NULL) { /* Check if media protocol is RTP */ is_rtp = (strcmp(transport_info.media_proto[n],"RTP/AVP")==0); g_free(transport_info.media_proto[n]); } if(transport_info.connection_address!=NULL) { if(transport_info.connection_type!=NULL && strcmp(transport_info.connection_type,"IP4")==0) { if(inet_aton(transport_info.connection_address, &ipaddr) !=0 ) { /* connection_address could be converted to a valid ipv4 address*/ is_ipv4_addr=TRUE; ipv4_address = ipaddr.s_addr; } } } /* Add rtp and rtcp conversation, if available */ if((!pinfo->fd->flags.visited) && ipv4_address!=0 && ipv4_port!=0 && is_rtp && is_ipv4_addr){ src_addr.type=AT_IPv4; src_addr.len=4; src_addr.data=(char *)&ipv4_address; if(rtp_handle){ conv=find_conversation(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR_B|NO_PORT_B); if(!conv){ conv=conversation_new(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR2|NO_PORT2); conversation_set_dissector(conv, rtp_handle); } } if(rtcp_handle){ ipv4_port++; conv=find_conversation(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR_B|NO_PORT_B); if(!conv){ conv=conversation_new(&src_addr, &src_addr, PT_UDP, ipv4_port, ipv4_port, NO_ADDR2|NO_PORT2); conversation_set_dissector(conv, rtcp_handle); } } } } /* Free up 'connection info' strings */ if(transport_info.connection_address) { g_free(transport_info.connection_address); } if(transport_info.connection_type!=NULL) { g_free(transport_info.connection_type); } datalen = tvb_length_remaining(tvb, offset); if (datalen > 0) { proto_tree_add_text(sdp_tree, tvb, offset, datalen, "Data (%d bytes)", datalen); } } static void call_sdp_subdissector(tvbuff_t *tvb, int hf, proto_tree* ti, transport_info_t *transport_info){ if(hf == hf_owner){ dissect_sdp_owner(tvb,ti); } else if ( hf == hf_connection_info) { dissect_sdp_connection_info(tvb,ti,transport_info); } else if ( hf == hf_bandwidth) { dissect_sdp_bandwidth(tvb,ti); } else if ( hf == hf_time) { dissect_sdp_time(tvb,ti); } else if ( hf == hf_repeat_time ){ dissect_sdp_repeat_time(tvb,ti); } else if ( hf == hf_timezone ) { dissect_sdp_timezone(tvb,ti); } else if ( hf == hf_encryption_key ) { dissect_sdp_encryption_key(tvb,ti); } else if ( hf == hf_session_attribute ){ dissect_sdp_session_attribute(tvb,ti); } else if ( hf == hf_media ) { dissect_sdp_media(tvb,ti,transport_info); } else if ( hf == hf_media_attribute ){ dissect_sdp_media_attribute(tvb,ti); } } static void dissect_sdp_owner(tvbuff_t *tvb, proto_item *ti){ proto_tree *sdp_owner_tree; gint offset,next_offset,tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_owner_tree = proto_item_add_subtree(ti,ett_sdp_owner); /* Find the username */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_owner_tree,hf_owner_username,tvb, offset,tokenlen, FALSE); offset = next_offset + 1; /* Find the session id */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_owner_tree,hf_owner_sessionid, tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the version */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_owner_tree,hf_owner_version, tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the network type */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_owner_tree,hf_owner_network_type, tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the address type */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_owner_tree,hf_owner_address_type, tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the address */ proto_tree_add_item(sdp_owner_tree,hf_owner_address, tvb, offset, -1, FALSE); } /* * XXX - this can leak memory if an exception is thrown after we've fetched * a string. */ static void dissect_sdp_connection_info(tvbuff_t *tvb, proto_item* ti, transport_info_t *transport_info){ proto_tree *sdp_connection_info_tree; gint offset,next_offset,tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_connection_info_tree = proto_item_add_subtree(ti, ett_sdp_connection_info); /* Find the network type */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_connection_info_tree, hf_connection_info_network_type,tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the address type */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; /* Save connection address type */ transport_info->connection_type = tvb_get_string(tvb, offset, tokenlen); proto_tree_add_item(sdp_connection_info_tree, hf_connection_info_address_type,tvb, offset,tokenlen,FALSE); offset = next_offset + 1; /* Find the connection address */ /* XXX - what if there's a value? */ next_offset = tvb_find_guint8(tvb,offset,-1,'/'); if( next_offset == -1){ tokenlen = -1; /* end of tvbuff */ /* Save connection address */ transport_info->connection_address = tvb_get_string(tvb, offset, tvb_length_remaining(tvb, offset)); } else { tokenlen = next_offset - offset; /* Save connection address */ transport_info->connection_address = tvb_get_string(tvb, offset, tokenlen); } proto_tree_add_item(sdp_connection_info_tree, hf_connection_info_connection_address, tvb, offset,tokenlen,FALSE); if(next_offset != -1){ offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset,-1,'/'); if( next_offset == -1){ tokenlen = -1; /* end of tvbuff */ } else { tokenlen = next_offset - offset; } proto_tree_add_item(sdp_connection_info_tree, hf_connection_info_ttl,tvb,offset,tokenlen,FALSE); if(next_offset != -1){ offset = next_offset + 1; proto_tree_add_item(sdp_connection_info_tree, hf_connection_info_num_addr, tvb, offset, -1, FALSE); } } } static void dissect_sdp_bandwidth(tvbuff_t *tvb, proto_item *ti){ proto_tree * sdp_bandwidth_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_bandwidth_tree = proto_item_add_subtree(ti,ett_sdp_bandwidth); /* find the modifier */ next_offset = tvb_find_guint8(tvb,offset,-1,':'); if( next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_modifier, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; proto_tree_add_item(sdp_bandwidth_tree, hf_bandwidth_value, tvb, offset, -1, FALSE); } static void dissect_sdp_time(tvbuff_t *tvb, proto_item* ti){ proto_tree *sdp_time_tree; gint offset,next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_time_tree = proto_item_add_subtree(ti,ett_sdp_time); /* get start time */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_time_tree, hf_time_start, tvb, offset, tokenlen, FALSE); /* get stop time */ offset = next_offset + 1; proto_tree_add_item(sdp_time_tree, hf_time_stop, tvb, offset, -1, FALSE); } static void dissect_sdp_repeat_time(tvbuff_t *tvb, proto_item* ti){ proto_tree *sdp_repeat_time_tree; gint offset,next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_repeat_time_tree = proto_item_add_subtree(ti,ett_sdp_time); /* get interval */ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_interval, tvb, offset, tokenlen, FALSE); /* get duration */ offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1 ) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_repeat_time_tree,hf_repeat_time_duration, tvb, offset, tokenlen, FALSE); /* get offsets */ do{ offset = next_offset +1; next_offset = tvb_find_guint8(tvb,offset,-1,' '); if(next_offset != -1){ tokenlen = next_offset - offset; } else { tokenlen = -1; /* end of tvbuff */ } proto_tree_add_item(sdp_repeat_time_tree, hf_repeat_time_offset, tvb, offset, tokenlen, FALSE); } while( next_offset != -1 ); } static void dissect_sdp_timezone(tvbuff_t *tvb, proto_item* ti){ proto_tree* sdp_timezone_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_timezone_tree = proto_item_add_subtree(ti,ett_sdp_timezone); do{ next_offset = tvb_find_guint8(tvb,offset,-1,' '); if(next_offset == -1) break; tokenlen = next_offset - offset; proto_tree_add_item(sdp_timezone_tree,hf_timezone_time,tvb, offset, tokenlen, FALSE); offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset,-1,' '); if(next_offset != -1){ tokenlen = next_offset - offset; } else { tokenlen = -1; /* end of tvbuff */ } proto_tree_add_item(sdp_timezone_tree,hf_timezone_offset,tvb, offset, tokenlen, FALSE); offset = next_offset + 1; } while (next_offset != -1); } static void dissect_sdp_encryption_key(tvbuff_t *tvb, proto_item * ti){ proto_tree *sdp_encryption_key_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_encryption_key_tree = proto_item_add_subtree(ti,ett_sdp_encryption_key); next_offset = tvb_find_guint8(tvb,offset,-1,':'); if(next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_type, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; proto_tree_add_item(sdp_encryption_key_tree,hf_encryption_key_data, tvb, offset, -1, FALSE); } static void dissect_sdp_session_attribute(tvbuff_t *tvb, proto_item * ti){ proto_tree *sdp_session_attribute_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_session_attribute_tree = proto_item_add_subtree(ti, ett_sdp_session_attribute); next_offset = tvb_find_guint8(tvb,offset,-1,':'); if(next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_session_attribute_tree, hf_session_attribute_field, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; proto_tree_add_item(sdp_session_attribute_tree, hf_session_attribute_value, tvb, offset, -1, FALSE); } static void dissect_sdp_media(tvbuff_t *tvb, proto_item *ti, transport_info_t *transport_info){ proto_tree *sdp_media_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_media_tree = proto_item_add_subtree(ti,ett_sdp_media); next_offset = tvb_find_guint8(tvb,offset, -1, ' '); if(next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_media_tree, hf_media_media, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset, -1, ' '); if(next_offset == -1) return; tokenlen = next_offset - offset; next_offset = tvb_find_guint8(tvb,offset, tokenlen, '/'); if(next_offset != -1){ tokenlen = next_offset - offset; /* Save port info */ transport_info->media_port[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen); proto_tree_add_item(sdp_media_tree, hf_media_port, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset, -1, ' '); if(next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_media_tree, hf_media_portcount, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; } else { next_offset = tvb_find_guint8(tvb,offset, -1, ' '); if(next_offset == -1) return; tokenlen = next_offset - offset; /* Save port info */ transport_info->media_port[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen); /* XXX Remember Port */ proto_tree_add_item(sdp_media_tree, hf_media_port, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; } next_offset = tvb_find_guint8(tvb,offset,-1,' '); if( next_offset == -1) return; tokenlen = next_offset - offset; /* Save port protocol */ transport_info->media_proto[transport_info->media_count] = tvb_get_string(tvb, offset, tokenlen); /* XXX Remember Protocol */ proto_tree_add_item(sdp_media_tree, hf_media_proto, tvb, offset, tokenlen, FALSE); do{ offset = next_offset + 1; next_offset = tvb_find_guint8(tvb,offset,-1,' '); if(next_offset == -1){ tokenlen = tvb_length_remaining(tvb, offset); /* End of tvbuff */ if (tokenlen == 0) break; /* Nothing more left */ } else { tokenlen = next_offset - offset; } proto_tree_add_item(sdp_media_tree, hf_media_format, tvb, offset, tokenlen, FALSE); } while (next_offset != -1); /* Increase the count of media channels, but don't walk off the end of the arrays. */ if (transport_info->media_count < (SDP_MAX_RTP_CHANNELS-1)){ transport_info->media_count++; } /* XXX Dissect traffic to "Port" as "Protocol" * Remember this Port/Protocol pair so we can tear it down again later * Actually, it's harder than that: * We need to find out the address of the other side first and it * looks like that info can be found in SIP headers only. */ } static void dissect_sdp_media_attribute(tvbuff_t *tvb, proto_item * ti){ proto_tree *sdp_media_attribute_tree; gint offset, next_offset, tokenlen; offset = 0; next_offset = 0; tokenlen = 0; sdp_media_attribute_tree = proto_item_add_subtree(ti, ett_sdp_media_attribute); next_offset = tvb_find_guint8(tvb,offset,-1,':'); if(next_offset == -1) return; tokenlen = next_offset - offset; proto_tree_add_item(sdp_media_attribute_tree, hf_media_attribute_field, tvb, offset, tokenlen, FALSE); offset = next_offset + 1; proto_tree_add_item(sdp_media_attribute_tree, hf_media_attribute_value, tvb, offset, -1, FALSE); } void proto_register_sdp(void) { static hf_register_info hf[] = { { &hf_protocol_version, { "Session Description Protocol Version (v)", "sdp.version", FT_STRING, BASE_NONE,NULL,0x0, "Session Description Protocol Version", HFILL }}, { &hf_owner, { "Owner/Creator, Session Id (o)", "sdp.owner", FT_STRING, BASE_NONE, NULL, 0x0, "Owner/Creator, Session Id", HFILL}}, { &hf_session_name, { "Session Name (s)", "sdp.session_name", FT_STRING, BASE_NONE,NULL, 0x0, "Session Name", HFILL }}, { &hf_session_info, { "Session Information (i)", "sdp.session_info", FT_STRING, BASE_NONE, NULL, 0x0, "Session Information", HFILL }}, { &hf_uri, { "URI of Description (u)", "sdp.uri", FT_STRING, BASE_NONE,NULL, 0x0, "URI of Description", HFILL }}, { &hf_email, { "E-mail Address (e)", "sdp.email", FT_STRING, BASE_NONE, NULL, 0x0, "E-mail Address", HFILL }}, { &hf_phone, { "Phone Number (p)", "sdp.phone", FT_STRING, BASE_NONE, NULL, 0x0, "Phone Number", HFILL }}, { &hf_connection_info, { "Connection Information (c)", "sdp.connection_info", FT_STRING, BASE_NONE, NULL, 0x0, "Connection Information", HFILL }}, { &hf_bandwidth, { "Bandwidth Information (b)", "sdp.bandwidth", FT_STRING, BASE_NONE, NULL, 0x0, "Bandwidth Information", HFILL }}, { &hf_timezone, { "Time Zone Adjustments (z)", "sdp.timezone", FT_STRING, BASE_NONE, NULL, 0x0, "Time Zone Adjustments", HFILL }}, { &hf_encryption_key, { "Encryption Key (k)", "sdp.encryption_key", FT_STRING, BASE_NONE, NULL, 0x0, "Encryption Key", HFILL }}, { &hf_session_attribute, { "Session Attribute (a)", "sdp.session_attr", FT_STRING, BASE_NONE, NULL, 0x0, "Session Attribute", HFILL }}, { &hf_media_attribute, { "Media Attribute (a)", "sdp.media_attr", FT_STRING, BASE_NONE, NULL, 0x0, "Media Attribute", HFILL }}, { &hf_time, { "Time Description, active time (t)", "sdp.time", FT_STRING, BASE_NONE, NULL, 0x0, "Time Description, active time", HFILL }}, { &hf_repeat_time, { "Repeat Time (r)", "sdp.repeat_time", FT_STRING, BASE_NONE, NULL, 0x0, "Repeat Time", HFILL }}, { &hf_media, { "Media Description, name and address (m)", "sdp.media", FT_STRING, BASE_NONE, NULL, 0x0, "Media Description, name and address", HFILL }}, { &hf_media_title, { "Media Title (i)", "sdp.media_title",FT_STRING, BASE_NONE, NULL, 0x0, "Media Title", HFILL }}, { &hf_unknown, { "Unknown", "sdp.unknown",FT_STRING, BASE_NONE, NULL, 0x0, "Unknown", HFILL }}, { &hf_invalid, { "Invalid line", "sdp.invalid",FT_STRING, BASE_NONE, NULL, 0x0, "Invalid line", HFILL }}, { &hf_owner_username, { "Owner Username", "sdp.owner.username",FT_STRING, BASE_NONE, NULL, 0x0, "Owner Username", HFILL }}, { &hf_owner_sessionid, { "Session ID", "sdp.owner.sessionid",FT_STRING, BASE_NONE, NULL, 0x0, "Session ID", HFILL }}, { &hf_owner_version, { "Session Version", "sdp.owner.version",FT_STRING, BASE_NONE, NULL, 0x0, "Session Version", HFILL }}, { &hf_owner_network_type, { "Owner Network Type", "sdp.owner.network_type",FT_STRING, BASE_NONE, NULL, 0x0, "Owner Network Type", HFILL }}, { &hf_owner_address_type, { "Owner Address Type", "sdp.owner.address_type",FT_STRING, BASE_NONE, NULL, 0x0, "Owner Address Type", HFILL }}, { &hf_owner_address, { "Owner Address", "sdp.owner.address",FT_STRING, BASE_NONE, NULL, 0x0, "Owner Address", HFILL }}, { &hf_connection_info_network_type, { "Connection Network Type", "sdp.connection_info.network_type",FT_STRING, BASE_NONE, NULL, 0x0, "Connection Network Type", HFILL }}, { &hf_connection_info_address_type, { "Connection Address Type", "sdp.connection_info.address_type",FT_STRING, BASE_NONE, NULL, 0x0, "Connection Address Type", HFILL }}, { &hf_connection_info_connection_address, { "Connection Address", "sdp.connection_info.address",FT_STRING, BASE_NONE, NULL, 0x0, "Connection Address", HFILL }}, { &hf_connection_info_ttl, { "Connection TTL", "sdp.connection_info.ttl",FT_STRING, BASE_NONE, NULL, 0x0, "Connection TTL", HFILL }}, { &hf_connection_info_num_addr, { "Connection Number of Addresses", "sdp.connection_info.num_addr",FT_STRING, BASE_NONE, NULL, 0x0, "Connection Number of Addresses", HFILL }}, { &hf_bandwidth_modifier, { "Bandwidth Modifier", "sdp.bandwidth.modifier",FT_STRING, BASE_NONE, NULL, 0x0, "Bandwidth Modifier", HFILL }}, { &hf_bandwidth_value, { "Bandwidth Value", "sdp.bandwidth.value",FT_STRING, BASE_NONE, NULL, 0x0, "Bandwidth Value", HFILL }}, { &hf_time_start, { "Session Start Time", "sdp.time.start",FT_STRING, BASE_NONE, NULL, 0x0, "Session Start Time", HFILL }}, { &hf_time_stop, { "Session Stop Time", "sdp.time.stop",FT_STRING, BASE_NONE, NULL, 0x0, "Session Stop Time", HFILL }}, { &hf_repeat_time_interval, { "Repeat Interval", "sdp.repeat_time.interval",FT_STRING, BASE_NONE, NULL, 0x0, "Repeat Interval", HFILL }}, { &hf_repeat_time_duration, { "Repeat Duration", "sdp.repeat_time.duration",FT_STRING, BASE_NONE, NULL, 0x0, "Repeat Duration", HFILL }}, { &hf_repeat_time_offset, { "Repeat Offset", "sdp.repeat_time.offset",FT_STRING, BASE_NONE, NULL, 0x0, "Repeat Offset", HFILL }}, { &hf_timezone_time, { "Timezone Time", "sdp.timezone.time",FT_STRING, BASE_NONE, NULL, 0x0, "Timezone Time", HFILL }}, { &hf_timezone_offset, { "Timezone Offset", "sdp.timezone.offset",FT_STRING, BASE_NONE, NULL, 0x0, "Timezone Offset", HFILL }}, { &hf_encryption_key_type, { "Key Type", "sdp.encryption_key.type",FT_STRING, BASE_NONE, NULL, 0x0, "Type", HFILL }}, { &hf_encryption_key_data, { "Key Data", "sdp.encryption_key.data",FT_STRING, BASE_NONE, NULL, 0x0, "Data", HFILL }}, { &hf_session_attribute_field, { "Session Attribute Fieldname", "sdp.session_attr.field",FT_STRING, BASE_NONE, NULL, 0x0, "Session Attribute Fieldname", HFILL }}, { &hf_session_attribute_value, { "Session Attribute Value", "sdp.session_attr.value",FT_STRING, BASE_NONE, NULL, 0x0, "Session Attribute Value", HFILL }}, { &hf_media_media, { "Media Type", "sdp.media.media",FT_STRING, BASE_NONE, NULL, 0x0, "Media Type", HFILL }}, { &hf_media_port, { "Media Port", "sdp.media.port",FT_STRING, BASE_NONE, NULL, 0x0, "Media Port", HFILL }}, { &hf_media_portcount, { "Media Port Count", "sdp.media.portcount",FT_STRING, BASE_NONE, NULL, 0x0, "Media Port Count", HFILL }}, { &hf_media_proto, { "Media Proto", "sdp.media.proto",FT_STRING, BASE_NONE, NULL, 0x0, "Media Proto", HFILL }}, { &hf_media_format, { "Media Format", "sdp.media.format",FT_STRING, BASE_NONE, NULL, 0x0, "Media Format", HFILL }}, { &hf_media_attribute_field, { "Media Attribute Fieldname", "sdp.media_attribute.field",FT_STRING, BASE_NONE, NULL, 0x0, "Media Attribute Fieldname", HFILL }}, { &hf_media_attribute_value, { "Media Attribute Value", "sdp.media_attribute.value",FT_STRING, BASE_NONE, NULL, 0x0, "Media Attribute Value", HFILL }}, }; static gint *ett[] = { &ett_sdp, &ett_sdp_owner, &ett_sdp_connection_info, &ett_sdp_bandwidth, &ett_sdp_time, &ett_sdp_repeat_time, &ett_sdp_timezone, &ett_sdp_encryption_key, &ett_sdp_session_attribute, &ett_sdp_media, &ett_sdp_media_attribute, }; proto_sdp = proto_register_protocol("Session Description Protocol", "SDP", "sdp"); proto_register_field_array(proto_sdp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); /* * Register the dissector by name, so other dissectors can * grab it by name rather than just referring to it directly * (you can't refer to it directly from a plugin dissector * on Windows without stuffing it into the Big Transfer Vector). */ register_dissector("sdp", dissect_sdp, proto_sdp); } void proto_reg_handoff_sdp(void) { dissector_handle_t sdp_handle; rtp_handle = find_dissector("rtp"); rtcp_handle = find_dissector("rtcp"); sdp_handle = find_dissector("sdp"); dissector_add_string("media_type", "application/sdp", sdp_handle); }