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