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