Ethereal-dev: [Ethereal-dev] sdp

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: PC Drew <drewpc@xxxxxxxxxxxx>
Date: Wed, 06 Jun 2001 09:51:32 -0600
I've added a bunch of code to the SDP dissector so that it creates an RTP conversation for the addresses and ports listed in the SDP packet. I haven't been able to find a capture that contains multiple connection addresses or multiple ports...if anyone has such captures, I'd really like some feedback.

Attached are the c file that I used (from ethereal 0.8.16) and a diff with the packet-sdp.c file from ethereal 0.8.18.

--
PC Drew

  Be nice, or I'll replace you with a very
  small shell script

Attachment: packet-sdp.diff
Description: Binary data

/* packet-sdp.c
 * Routines for SDP packet disassembly (RFC 2327)
 *
 * Jason Lango <jal@xxxxxxxxxx>
 * Liberally copied from packet-http.c, by Guy Harris <guy@xxxxxxxxxxxx>
 *
 * $Id: packet-sdp.c,v 1.20 2001/01/25 06:14:14 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxx>
 * 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"

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#include <string.h>
#include <ctype.h>

#include <glib.h>
#include "packet.h"
#include "conversation.h"
#include "strutil.h"
#include "packet-rtp.h"
#include "packet-rtcp.h"

static int proto_sdp = -1;

static int ett_sdp = -1;

static GMemChunk *address_chunk = NULL;
static GMemChunk *ipv4_chunk = NULL;
static address fake_addr;

static void
dissect_sdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
	proto_tree	*sdp_tree = NULL;
	proto_item	*ti;
	gint		offset = 0;
	const u_char	*line;
	gint		next_offset;
	int		linelen;
	u_char		section;
	u_char		type;
	const u_char	*value;
	int		valuelen;
	const char	*typename;
	int		datalen;
	char **conn_list;
	char **media_list;
	int conn_i = 0;
	int media_i = 0;
	int max_conn_i = 25; /* this is a bad idea!! */
	int max_media_i = 25; /* this is a bad idea!! */
	address **addrs;
	char *tmp_ptr;
	int i, j, k, m;

	/* the list of connections.  since there can be many connections, we
	 * have to have an array of char*'s
	 */
	conn_list = (char **)malloc(sizeof(char *) * max_conn_i);

	/* the list of media types.  since there can be many ports, we
	 * have to have an array of char*'s
	 */
	media_list = (char **)malloc(sizeof(char *) * max_media_i);

	/* the list of addresses being invited to the call.  since there can be
	 * many addresses, we have to have an array of address*'s
	 */
	addrs = (address **)malloc(sizeof(address *) * max_conn_i);

	/*
	 * 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->fd, COL_PROTOCOL))
		col_append_str(pinfo->fd, COL_PROTOCOL, "/SDP");

	if (check_col(pinfo->fd, COL_INFO)) {
		/* XXX: Needs description. */
		col_append_str(pinfo->fd, COL_INFO, ", with session description");
	}

	if (tree) {
		ti = proto_tree_add_item(tree, proto_sdp, tvb, offset, tvb_length_remaining(tvb, offset), FALSE);
		sdp_tree = proto_item_add_subtree(ti, ett_sdp);
	}

	/*
	 * Show the SDP message a line at a time.
	 */
	section = 0;
	while (tvb_offset_exists(tvb, offset)) {
		/*
		 * 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=".
		 *
		 * This use to be just a break inside the { }, but because most of
		 * the decoding needs to be done regardless of whether or not
		 * if(tree) is true, I changed this a little.
		 */
		if (linelen < 2) {
            offset = next_offset;
			continue;
		}

		line = tvb_get_ptr(tvb, offset, next_offset - offset);
		type = line[0];
		if (line[1] != '=') {
			if (tree) {
			proto_tree_add_text(sdp_tree, tvb, offset, next_offset - offset, "Invalid line: %s", tvb_format_text(tvb, offset, next_offset - offset));
			}
                offset = next_offset;
			continue;
		}
		value = line + 2;
		valuelen = linelen - 2;

		/*
		 * Attributes.
		 */
		switch (type) {
		case 'v':
			section = 'v';
			typename = "Session Description, version";
			break;
		case 'o':
			typename = "Owner/Creator, Session Id";
			break;
		case 's':
			typename = "Session Name";
			break;
		case 'i':
			if (section == 'v')
				typename = "Session Information";
			else if (section == 'm')
				typename = "Media Title";
			else
				typename = "Misplaced";
			break;
		case 'u':
			typename = "URI of Description";
			break;
		case 'e':
			typename = "E-mail Address";
			break;
		case 'p':
			typename = "Phone Number";
			break;
		case 'c':
			typename = "Connection Information";
			/* the first time this packet gets decoded, I need to add this
			 * string to the list of connections
			 */
			if (pinfo->fd->flags.visited == 0 && conn_i < max_conn_i) {
				conn_list[conn_i] = (char *)malloc(sizeof(char) * valuelen);
				conn_list[conn_i] = strncpy(conn_list[conn_i], value, valuelen);
				conn_i++;
			}
			break;
		case 'b':
			typename = "Bandwidth Information";
			break;
		case 't':
			section = 't';
			typename = "Time Description, active time";
			break;
		case 'r':
			typename = "Repeat Time";
			break;
		case 'm':
			section = 'm';
			typename = "Media Description, name and address";
			/* the first time this packet gets decoded, I need to add this
			 * string to the list of media.
			 */
			if (pinfo->fd->flags.visited == 0 && media_i < max_media_i) {
				media_list[media_i] = (char *)malloc(sizeof(char) * valuelen);
				media_list[media_i] = strncpy(media_list[media_i], value, valuelen);
				media_i++;
			}
			break;
		case 'k':
			typename = "Encryption Key";
			break;
		case 'a':
			if (section == 'v')
				typename = "Session Attribute";
			else if (section == 'm')
				typename = "Media Attribute";
			else
				typename = "Misplaced";
			break;
		case 'z':
			typename = "Time Zone Adjustment";
			break;
		default:
			typename = "Unknown";
			break;
		}

		if (tree) {
		proto_tree_add_text(sdp_tree, tvb, offset,
		    next_offset - offset,
		    "%s (%c): %s", typename, type,
		    format_text(value, valuelen));
		}
		offset = next_offset;
	}

	datalen = tvb_length_remaining(tvb, offset);
	if (datalen > 0) {
		if (tree) {
		proto_tree_add_text(sdp_tree, tvb, offset, datalen, "Data (%d bytes)", datalen);
		}
	}

	/* only do this the first time the packet is decoded */
	if (pinfo->fd->flags.visited == 0) {
		/* parse through all of the conn strings */
		for (i = 0; i < conn_i; i++) {
			guint8 *addr_data;
			address *addr;

			/* network type
			 * because I'm decoding this to dissect RTP/RTCP, we only care
			 * about network types of IN
			 */
			tmp_ptr = strtok(conn_list[i], " ");
			if (tmp_ptr == NULL || strcmp(tmp_ptr, "IN") != 0) {
				break;
			}

			/* address type
			 * again, because I'm decoding this to dissect RTP/RTCP, we only care
			 * about network types of IP4.  This could be changed...?
			 */
			tmp_ptr = strtok(NULL, " ");
			if (tmp_ptr == NULL || strcmp(tmp_ptr, "IP4") != 0) {
				break;
			}

			/* address
			 * This can be a multicast or unicast address.
			 */
			tmp_ptr = strtok(NULL, " ");
			if (tmp_ptr == NULL) {
				break;
			}

			/* address
			 * There can actually be stuff after the address, within the
			 * address field, seperated by '/'.  For creating RTP/RTCP
			 * conversations, we only care about the first string after
			 * tokenizing by '/'.
			 */
			tmp_ptr = strtok(tmp_ptr, "/");
			if (tmp_ptr == NULL) {
				break;
			}

			addr_data = g_mem_chunk_alloc(ipv4_chunk);
			addr = g_mem_chunk_alloc(address_chunk);

			/* load the address into an array */
			addr_data[0] = atoi(strtok(tmp_ptr, "."));
			addr_data[1] = atoi(strtok(NULL, "."));
			addr_data[2] = atoi(strtok(NULL, "."));
			addr_data[3] = atoi(strtok(NULL, "."));

			/* set the data in the address* structure */
			SET_ADDRESS(addr, AT_IPv4, 4, addr_data);

			/* add to the list of addresses */
			addrs[i] = addr;
		}

		/* this goes through all of the media strings (which contain port
		 * information) */
		for (j = 0; j < media_i; j++) {
			/* if there's more than 0 addresses... */
			if (i > 0) {
				guint16 port;
				guint8 num_ports;
				char *port_str;
				void *func_ptr = NULL;
				conversation_t  *conv = NULL;

				/* media type */
				tmp_ptr = strtok(media_list[j], " ");
				if (tmp_ptr == NULL) {
					break;
				}

				/* port */
				tmp_ptr = strtok(NULL, " ");
				if (tmp_ptr == NULL) {
					break;
				}

				/* much to our dismay, the port can actually be specified
				 * like 3040/2, which means there will be two ports (or in
				 * the case of RTP/RTCP, 2 pairs of RTP/RTCP ports. This
				 * logic tries to figure all of that out.
				 */
				port_str = strtok(tmp_ptr, "/");
				port = (guint16)atoi(port_str);

				/* fix our char* after strtok */
				tmp_ptr += strlen(port_str) + 1;
				port_str = strtok(NULL, "/");
				if (port_str != NULL) {
					/* there was a / and let's get the number of ports it
					 * wants.
					 */
					num_ports = atoi(port_str);
					tmp_ptr += strlen(port_str) + 1;
				} else {
					/* there was no /, so we're assuming 1 port (or one
					 * RTP/RTCP pair.
					 */
					num_ports = 1;
				}

				/* transport */
				tmp_ptr = strtok(tmp_ptr, " ");
				if (tmp_ptr == NULL) {
					break;
				}

				/* right now we only care about rtp, if the first 3 chars
				 * of the transport string are RTP, then we're in business.
				 * Otherwise, ethereal will handle it and make it dissect
				 * to UDP data.  Other dissectors should be listed in this
				 * if statement.
				 */
				if (strncmp(tmp_ptr, "RTP", 3) == 0) {
					func_ptr = &dissect_rtp;
				}

				/* if we've got anything to report... */
				if (func_ptr != NULL) {
					/* for every address in the address list... */
					for (k = 0, m = 0; k < i; k++) {
						/* if it's already there, we don't need to do any
						 * more work.
						 */
						conv = find_conversation(addrs[k], &fake_addr, PT_UDP, port + m, 0, NO_DST_ADDR | NO_DST_PORT);
						if (conv == NULL) {
							/* create the conversation and set the
							 * dissector to be the func_ptr we set above.
							 */
							conv = conversation_new(addrs[k], &fake_addr, PT_UDP, port + m, 0, 0, NO_DST_ADDR | NO_DST_PORT);
							conversation_set_dissector(conv, func_ptr);
							m++;
							/* if it's RTP, the SDP RFC (RFC 2327) says to
							 * follow the RTP standard and make RTCP the
							 * next port (i.e. the RTP port should be an
							 * even number, and the RTCP should be the next
							 * higher odd number)
							 */
							if (func_ptr == &dissect_rtp) {
								conv = conversation_new(addrs[k], &fake_addr, PT_UDP, port + m, 0, 0, NO_DST_ADDR | NO_DST_PORT);
								conversation_set_dissector(conv, dissect_rtcp);
								m++;
							}
						}
					}
				}
			}
		}
	}
}

void
proto_register_sdp(void)
{
/*        static hf_register_info hf[] = {
                { &variable,
                { "Name",           "sdp.abbreviation", TYPE, VALS_POINTER }},
        };*/
	static gint *ett[] = {
		&ett_sdp,
	};

        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);

	address_chunk = g_mem_chunk_new("sdp address change", sizeof(address), sizeof(address) * 128, G_ALLOC_ONLY);
	ipv4_chunk = g_mem_chunk_new("sdp address change 2", sizeof(guint8), sizeof(guint8) * 4, G_ALLOC_ONLY);
}