Moved dissector for UNICODE_STRING structure from packet-dcerpc-samr.c to packet...
[obnox/wireshark/wip.git] / packet-rtsp.c
1 /* packet-rtsp.c
2  * Routines for RTSP packet disassembly (RFC 2326)
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-rtsp.c,v 1.47 2002/01/21 07:36:41 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 #include <stdlib.h>
37
38 #include <glib.h>
39 #include <epan/packet.h>
40 #include "packet-rtp.h"
41 #include "packet-rtcp.h"
42 #include <epan/conversation.h>
43 #include <epan/strutil.h>
44
45 static int proto_rtsp = -1;
46 static gint ett_rtsp = -1;
47
48 static gint ett_rtspframe = -1;
49
50 static int hf_rtsp_method = -1;
51 static int hf_rtsp_url = -1;
52 static int hf_rtsp_status = -1;
53
54 static dissector_handle_t sdp_handle;
55 static dissector_handle_t rtp_handle;
56 static dissector_handle_t rtcp_handle;
57
58 static GMemChunk *rtsp_vals = NULL;
59 #define rtsp_hash_init_count 20
60
61 #define TCP_PORT_RTSP                   554
62
63 /*
64  * Takes an array of bytes, assumed to contain a null-terminated
65  * string, as an argument, and returns the length of the string -
66  * i.e., the size of the array, minus 1 for the null terminator.
67  */
68 #define STRLEN_CONST(str)       (sizeof (str) - 1)
69
70 #define RTSP_FRAMEHDR   ('$')
71
72 typedef struct {
73         dissector_handle_t              dissector;
74 } rtsp_interleaved_t;
75
76 #define RTSP_MAX_INTERLEAVED            (8)
77
78 /*
79  * Careful about dynamically allocating memory in this structure (say
80  * for dynamically increasing the size of the 'interleaved' array) -
81  * the containing structure is garbage collected and contained
82  * pointers will not be freed.
83  */
84 typedef struct {
85         rtsp_interleaved_t              interleaved[RTSP_MAX_INTERLEAVED];
86 } rtsp_conversation_data_t;
87
88 static int
89 dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
90         proto_tree *tree)
91 {
92         proto_tree      *rtspframe_tree;
93         proto_item      *ti;
94         int             orig_offset;
95         guint8          rf_start;       /* always RTSP_FRAMEHDR */
96         guint8          rf_chan;        /* interleaved channel id */
97         guint16         rf_len;         /* packet length */
98         gint            framelen;
99         tvbuff_t        *next_tvb;
100         conversation_t  *conv;
101         rtsp_conversation_data_t        *data;
102         dissector_handle_t              dissector;
103
104         orig_offset = offset;
105         rf_start = tvb_get_guint8(tvb, offset);
106         rf_chan = tvb_get_guint8(tvb, offset+1);
107         rf_len = tvb_get_ntohs(tvb, offset+2);
108
109         if (check_col(pinfo->cinfo, COL_INFO))
110                 col_add_fstr(pinfo->cinfo, COL_INFO, 
111                         "Interleaved channel 0x%02x, %u bytes",
112                         rf_chan, rf_len);
113
114         if (tree == NULL) {
115                 /*
116                  * We're not building a full protocol tree; all we care
117                  * about is setting the column info.
118                  */
119                 return -1;
120         }
121
122         ti = proto_tree_add_protocol_format(tree, proto_rtsp, tvb, offset, 4,
123                 "RTSP Interleaved Frame, Channel: 0x%02x, %u bytes",
124                 rf_chan, rf_len);
125         rtspframe_tree = proto_item_add_subtree(ti, ett_rtspframe);
126
127         proto_tree_add_text(rtspframe_tree, tvb, offset, 1,
128                 "Magic: 0x%02x",
129                 rf_start);
130         offset += 1;
131
132         proto_tree_add_text(rtspframe_tree, tvb, offset, 1,
133                 "Channel: 0x%02x",
134                 rf_chan);
135         offset += 1;
136
137         proto_tree_add_text(rtspframe_tree, tvb, offset, 2,
138                 "Length: %u bytes",
139                 rf_len);
140         offset += 2;
141
142         /*
143          * We set the actual length of the tvbuff for the interleaved
144          * stuff to the minimum of what's left in the tvbuff and the
145          * length in the header.
146          *
147          * XXX - what if there's nothing left in the tvbuff?
148          * We'd want a BoundsError exception to be thrown, so
149          * that a Short Frame would be reported.
150          */
151         framelen = tvb_length_remaining(tvb, offset);
152         if (framelen > rf_len)
153                 framelen = rf_len;
154         next_tvb = tvb_new_subset(tvb, offset, framelen, rf_len);
155
156         conv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
157                 pinfo->srcport, pinfo->destport, 0);
158
159         if (conv &&
160             (data = conversation_get_proto_data(conv, proto_rtsp)) &&
161             rf_chan < RTSP_MAX_INTERLEAVED &&
162             (dissector = data->interleaved[rf_chan].dissector)) {
163                 call_dissector(dissector, next_tvb, pinfo, tree);
164         } else {
165                 proto_tree_add_text(rtspframe_tree, tvb, offset, rf_len,
166                         "Data (%u bytes)", rf_len);
167         }
168
169         offset += rf_len;
170
171         return offset - orig_offset;
172 }
173
174 static void process_rtsp_request(tvbuff_t *tvb, int offset, const u_char *data,
175         size_t linelen, proto_tree *tree);
176
177 static void process_rtsp_reply(tvbuff_t *tvb, int offset, const u_char *data,
178         size_t linelen, proto_tree *tree);
179
180 typedef enum {
181         RTSP_REQUEST,
182         RTSP_REPLY,
183         NOT_RTSP
184 } rtsp_type_t;
185
186 static const char *rtsp_methods[] = {
187         "DESCRIBE", "ANNOUNCE", "GET_PARAMETER", "OPTIONS",
188         "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP",
189         "SET_PARAMETER", "TEARDOWN"
190 };
191
192 #define RTSP_NMETHODS   (sizeof rtsp_methods / sizeof rtsp_methods[0])
193
194 static rtsp_type_t
195 is_rtsp_request_or_reply(const u_char *line, size_t linelen)
196 {
197         unsigned        ii;
198
199         /* Is this an RTSP reply? */
200         if (linelen >= 5 && strncasecmp("RTSP/", line, 5) == 0) {
201                 /*
202                  * Yes.
203                  */
204                 return RTSP_REPLY;
205         }
206
207         /*
208          * Is this an RTSP request?
209          * Check whether the line begins with one of the RTSP request
210          * methods.
211          */
212         for (ii = 0; ii < RTSP_NMETHODS; ii++) {
213                 size_t len = strlen(rtsp_methods[ii]);
214                 if (linelen >= len &&
215                     strncasecmp(rtsp_methods[ii], line, len) == 0)
216                         return RTSP_REQUEST;
217         }
218         return NOT_RTSP;
219 }
220
221 static const char rtsp_content_type[] = "Content-Type:";
222
223 static int
224 is_content_sdp(const u_char *line, size_t linelen)
225 {
226         static const char type[] = "application/sdp";
227         size_t          typelen = STRLEN_CONST(type);
228
229         line += STRLEN_CONST(rtsp_content_type);
230         linelen -= STRLEN_CONST(rtsp_content_type);
231         while (linelen > 0 && (*line == ' ' || *line == '\t')) {
232                 line++;
233                 linelen--;
234         }
235
236         if (linelen < typelen || strncasecmp(type, line, typelen))
237                 return FALSE;
238
239         line += typelen;
240         linelen -= typelen;
241         if (linelen > 0 && !isspace(*line))
242                 return FALSE;
243
244         return TRUE;
245 }
246
247 static const char rtsp_transport[] = "Transport:";
248 static const char rtsp_sps[] = "server_port=";
249 static const char rtsp_cps[] = "client_port=";
250 static const char rtsp_rtp[] = "rtp/";
251 static const char rtsp_inter[] = "interleaved=";
252
253 static void
254 rtsp_create_conversation(packet_info *pinfo, const u_char *line_begin,
255         size_t line_len)
256 {
257         conversation_t  *conv;
258         u_char          buf[256];
259         u_char          *tmp;
260         u_int           c_data_port, c_mon_port;
261         u_int           s_data_port, s_mon_port;
262         address         null_addr;
263
264         if (line_len > sizeof(buf) - 1) {
265                 /*
266                  * Don't overflow the buffer.
267                  */
268                 line_len = sizeof(buf) - 1;
269         }
270         memcpy(buf, line_begin, line_len);
271         buf[line_len] = '\0';
272
273         tmp = buf + STRLEN_CONST(rtsp_transport);
274         while (*tmp && isspace(*tmp))
275                 tmp++;
276         if (strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) != 0) {
277                 g_warning("Frame %u: rtsp: unknown transport", pinfo->fd->num);
278                 return;
279         }
280
281         c_data_port = c_mon_port = 0;
282         s_data_port = s_mon_port = 0;
283         if ((tmp = strstr(buf, rtsp_sps))) {
284                 tmp += strlen(rtsp_sps);
285                 if (sscanf(tmp, "%u-%u", &s_data_port, &s_mon_port) < 1) {
286                         g_warning("Frame %u: rtsp: bad server_port",
287                                 pinfo->fd->num);
288                         return;
289                 }
290         }
291         if ((tmp = strstr(buf, rtsp_cps))) {
292                 tmp += strlen(rtsp_cps);
293                 if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1) {
294                         g_warning("Frame %u: rtsp: bad client_port",
295                                 pinfo->fd->num);
296                         return;
297                 }
298         }
299         if (!c_data_port) {
300                 rtsp_conversation_data_t        *data;
301                 u_int                           s_data_chan, s_mon_chan;
302                 int                             i;
303
304                 /*
305                  * Deal with RTSP TCP-interleaved conversations.
306                  */
307                 if ((tmp = strstr(buf, rtsp_inter)) == NULL) {
308                         /*
309                          * No interleaved or server_port - probably a
310                          * SETUP request, rather than reply.
311                          */
312                         return;
313                 }
314                 tmp += strlen(rtsp_inter);
315                 i = sscanf(tmp, "%u-%u", &s_data_chan, &s_mon_chan);
316                 if (i < 1) {
317                         g_warning("Frame %u: rtsp: bad interleaved",
318                                 pinfo->fd->num);
319                         return;
320                 }
321                 conv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
322                         pinfo->srcport, pinfo->destport, 0);
323                 if (!conv) {
324                         conv = conversation_new(&pinfo->src, &pinfo->dst,
325                                 pinfo->ptype, pinfo->srcport, pinfo->destport,
326                                 0);
327                 }
328                 data = conversation_get_proto_data(conv, proto_rtsp);
329                 if (!data) {
330                         data = g_mem_chunk_alloc(rtsp_vals);
331                         conversation_add_proto_data(conv, proto_rtsp, data);
332                 }
333                 if (s_data_chan < RTSP_MAX_INTERLEAVED) {
334                         data->interleaved[s_data_chan].dissector =
335                                 rtp_handle;
336                 }
337                 if (i > 1 && s_mon_chan < RTSP_MAX_INTERLEAVED) {
338                         data->interleaved[s_mon_chan].dissector =
339                                 rtcp_handle;
340                 }
341                 return;
342         }
343
344         /*
345          * We only want to match on the destination address, not the
346          * source address, because the server might send back a packet
347          * from an address other than the address to which its client
348          * sent the packet, so we construct a conversation with no
349          * second address.
350          */
351         SET_ADDRESS(&null_addr, pinfo->src.type, 0, NULL);
352
353         conv = conversation_new(&pinfo->dst, &null_addr, PT_UDP, c_data_port,
354                 s_data_port, NO_ADDR2 | (!s_data_port ? NO_PORT2 : 0));
355         conversation_set_dissector(conv, rtp_handle);
356
357         if (!c_mon_port)
358                 return;
359
360         conv = conversation_new(&pinfo->dst, &null_addr, PT_UDP, c_mon_port,
361                 s_mon_port, NO_ADDR2 | (!s_mon_port ? NO_PORT2 : 0));
362         conversation_set_dissector(conv, rtcp_handle);
363 }
364
365 static const char rtsp_content_length[] = "Content-Length:";
366
367 static int
368 rtsp_get_content_length(const u_char *line_begin, size_t line_len)
369 {
370         u_char          buf[256];
371         u_char          *tmp;
372         long            content_length;
373         char            *p;
374         u_char          *up;
375
376         if (line_len > sizeof(buf) - 1) {
377                 /*
378                  * Don't overflow the buffer.
379                  */
380                 line_len = sizeof(buf) - 1;
381         }
382         memcpy(buf, line_begin, line_len);
383         buf[line_len] = '\0';
384
385         tmp = buf + STRLEN_CONST(rtsp_content_length);
386         while (*tmp && isspace(*tmp))
387                 tmp++;
388         content_length = strtol(tmp, &p, 10);
389         up = p;
390         if (up == tmp || (*up != '\0' && !isspace(*up)))
391                 return -1;      /* not a valid number */
392         return content_length;
393 }
394
395 static int
396 dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
397         proto_tree *tree)
398 {
399         proto_tree      *rtsp_tree;
400         proto_item      *ti = NULL;
401         const u_char    *line;
402         gint            next_offset;
403         const u_char    *linep, *lineend;
404         int             orig_offset;
405         size_t          linelen;
406         u_char          c;
407         gboolean        is_mime_header;
408         int             is_sdp = FALSE;
409         int             datalen;
410         int             content_length;
411         int             reported_datalen;
412
413         orig_offset = offset;
414         rtsp_tree = NULL;
415         if (tree) {
416                 ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1,
417                     FALSE);
418                 rtsp_tree = proto_item_add_subtree(ti, ett_rtsp);
419         }
420
421         if (check_col(pinfo->cinfo, COL_INFO)) {
422                 /*
423                  * Put the first line from the buffer into the summary
424                  * if it's an RTSP request or reply (but leave out the
425                  * line terminator).
426                  * Otherwise, just call it a continuation.
427                  */
428                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
429                 line = tvb_get_ptr(tvb, offset, linelen);
430                 switch (is_rtsp_request_or_reply(line, linelen)) {
431
432                 case RTSP_REQUEST:
433                 case RTSP_REPLY:
434                         col_add_str(pinfo->cinfo, COL_INFO,
435                             format_text(line, linelen));
436                         break;
437
438                 default:
439                         col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
440                         break;
441                 }
442         }
443
444         /*
445          * We haven't yet seen a Content-Length header.
446          */
447         content_length = -1;
448
449         /*
450          * Process the packet data, a line at a time.
451          */
452         while (tvb_offset_exists(tvb, offset)) {
453                 /*
454                  * We haven't yet concluded that this is a MIME header.
455                  */
456                 is_mime_header = FALSE;
457
458                 /*
459                  * Find the end of the line.
460                  */
461                 linelen = tvb_find_line_end(tvb, offset, -1, &next_offset);
462
463                 /*
464                  * Get a buffer that refers to the line.
465                  */
466                 line = tvb_get_ptr(tvb, offset, linelen);
467                 lineend = line + linelen;
468
469                 /*
470                  * OK, does it look like an RTSP request or
471                  * response?
472                  */
473                 switch (is_rtsp_request_or_reply(line, linelen)) {
474
475                 case RTSP_REQUEST:
476                         if (rtsp_tree != NULL)
477                                 process_rtsp_request(tvb, offset, line, linelen,
478                                     rtsp_tree);
479                         goto is_rtsp;
480
481                 case RTSP_REPLY:
482                         if (rtsp_tree != NULL)
483                                 process_rtsp_reply(tvb, offset, line, linelen,
484                                     rtsp_tree);
485                         goto is_rtsp;
486
487                 case NOT_RTSP:
488                         break;
489                 }
490
491                 /*
492                  * No.  Does it look like a blank line (as would
493                  * appear at the end of an RTSP request)?
494                  */
495                 if (linelen == 0)
496                         goto is_rtsp;   /* Yes. */
497
498                 /*
499                  * No.  Does it look like a MIME header?
500                  */
501                 linep = line;
502                 while (linep < lineend) {
503                         c = *linep++;
504                         if (!isprint(c))
505                                 break;  /* not printable, not a MIME header */
506                         switch (c) {
507
508                         case '(':
509                         case ')':
510                         case '<':
511                         case '>':
512                         case '@':
513                         case ',':
514                         case ';':
515                         case '\\':
516                         case '"':
517                         case '/':
518                         case '[':
519                         case ']':
520                         case '?':
521                         case '=':
522                         case '{':
523                         case '}':
524                                 /*
525                                  * It's a tspecial, so it's not
526                                  * part of a token, so it's not
527                                  * a field name for the beginning
528                                  * of a MIME header.
529                                  */
530                                 goto not_rtsp;
531
532                         case ':':
533                                 /*
534                                  * This ends the token; we consider
535                                  * this to be a MIME header.
536                                  */
537                                 is_mime_header = TRUE;
538                                 goto is_rtsp;
539
540                         case ' ':
541                         case '\t':
542                                 /*
543                                  * LWS (RFC-2616, 4.2); continue the previous
544                                  * header.
545                                  */
546                                 goto is_rtsp;
547                         }
548                 }
549
550         not_rtsp:
551                 /*
552                  * We don't consider this part of an RTSP request or
553                  * reply, so we don't display it.
554                  */
555                 break;
556
557         is_rtsp:
558                 /*
559                  * Put this line.
560                  */
561                 if (rtsp_tree) {
562                         proto_tree_add_text(rtsp_tree, tvb, offset,
563                             next_offset - offset, "%s",
564                             tvb_format_text(tvb, offset, next_offset - offset));
565                 }
566                 if (is_mime_header) {
567                         /*
568                          * Process some MIME headers specially.
569                          */
570 #define MIME_HDR_MATCHES(header) \
571         (linelen > STRLEN_CONST(header) && \
572          strncasecmp(line, (header), STRLEN_CONST(header)) == 0)
573
574                         if (MIME_HDR_MATCHES(rtsp_transport)) {
575                                 /*
576                                  * Based on the port numbers specified
577                                  * in the Transport: header, set up
578                                  * a conversation that will be dissected
579                                  * with the appropriate dissector.
580                                  */
581                                 rtsp_create_conversation(pinfo, line, linelen);
582                         } else if (MIME_HDR_MATCHES(rtsp_content_type)) {
583                                 /*
584                                  * If the Content-Type: header says this
585                                  * is SDP, dissect the payload as SDP.
586                                  */
587                                 if (is_content_sdp(line, linelen))
588                                         is_sdp = TRUE;
589                         } else if (MIME_HDR_MATCHES(rtsp_content_length)) {
590                                 /*
591                                  * Only the amount specified by the
592                                  * Content-Length: header should be treated
593                                  * as payload.
594                                  */
595                                 content_length = rtsp_get_content_length(line,
596                                     linelen);
597                         }
598                 }
599                 offset = next_offset;
600         }
601
602         /*
603          * If a content length was supplied, the amount of data to be
604          * processed as RTSP payload is the minimum of the content
605          * length and the amount of data remaining in the frame.
606          *
607          * If no content length was supplied, the amount of data to be
608          * processed is the amount of data remaining in the frame.
609          */
610         datalen = tvb_length_remaining(tvb, offset);
611         if (content_length != -1) {
612                 if (datalen > content_length)
613                         datalen = content_length;
614
615                 /*
616                  * XXX - for now, if the content length is greater
617                  * than the amount of data left in this frame (not
618                  * the amount of *captured* data left in the frame
619                  * minus the current offset, but the amount of *actual*
620                  * data that was reported to be in the frame minus
621                  * the current offset), limit it to the amount
622                  * of data left in this frame.
623                  *
624                  * If we ever handle data that crosses frame
625                  * boundaries, we'll need to remember the actual
626                  * content length.
627                  */
628                 reported_datalen = tvb_reported_length_remaining(tvb, offset);
629                 if (content_length > reported_datalen)
630                         content_length = reported_datalen;
631         }
632
633         if (datalen > 0) {
634                 /*
635                  * There's stuff left over; process it.
636                  */
637                 if (is_sdp) {
638                         tvbuff_t *new_tvb;
639
640                         /*
641                          * Fix up the top-level item so that it doesn't
642                          * include the SDP stuff.
643                          */
644                         if (ti != NULL)
645                                 proto_item_set_len(ti, offset);
646
647                         /*
648                          * Now create a tvbuff for the SDP stuff and
649                          * dissect it.
650                          *
651                          * The amount of data to be processed that's
652                          * available in the tvbuff is "datalen", which
653                          * is the minimum of the amount of data left in
654                          * the tvbuff and any specified content length.
655                          *
656                          * The amount of data to be processed that's in
657                          * this frame, regardless of whether it was
658                          * captured or not, is "content_length",
659                          * which, if no content length was specified,
660                          * is -1, i.e. "to the end of the frame.
661                          */
662                         new_tvb = tvb_new_subset(tvb, offset, datalen,
663                             content_length);
664                         call_dissector(sdp_handle, new_tvb, pinfo, tree);
665                 } else {
666                         if (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR) {
667                                 /*
668                                  * This is interleaved stuff; don't
669                                  * treat it as raw data - set "datalen"
670                                  * to 0, so we won't skip the offset
671                                  * past it, which will cause our
672                                  * caller to process that stuff itself.
673                                  */
674                                 datalen = 0;
675                         } else {
676                                 proto_tree_add_text(rtsp_tree, tvb, offset,
677                                     datalen, "Data (%d bytes)", datalen);
678                         }
679                 }
680
681                 /*
682                  * We've processed "datalen" bytes worth of data
683                  * (which may be no data at all); advance the
684                  * offset past whatever data we've processed, so they
685                  * don't process it.
686                  */
687                 offset += datalen;
688         }
689         return offset - orig_offset;
690 }
691
692 static void
693 process_rtsp_request(tvbuff_t *tvb, int offset, const u_char *data,
694         size_t linelen, proto_tree *tree)
695 {
696         const u_char    *lineend = data + linelen;
697         unsigned        ii;
698         const u_char    *url;
699         const u_char    *url_start;
700         u_char          *tmp_url;
701
702         /* Request Methods */
703         for (ii = 0; ii < RTSP_NMETHODS; ii++) {
704                 size_t len = strlen(rtsp_methods[ii]);
705                 if (linelen >= len && !strncasecmp(rtsp_methods[ii], data, len))
706                         break;
707         }
708         if (ii == RTSP_NMETHODS) {
709                 /*
710                  * We got here because "is_rtsp_request_or_reply()" returned
711                  * RTSP_REQUEST, so we know one of the request methods
712                  * matched, so we "can't get here".
713                  */
714                 g_assert_not_reached();
715         }
716
717         /* Method name */
718         proto_tree_add_string_hidden(tree, hf_rtsp_method, tvb, offset,
719                 strlen(rtsp_methods[ii]), rtsp_methods[ii]);
720
721         /* URL */
722         url = data;
723         while (url < lineend && !isspace(*url))
724                 url++;
725         while (url < lineend && isspace(*url))
726                 url++;
727         url_start = url;
728         while (url < lineend && !isspace(*url))
729                 url++;
730         tmp_url = g_malloc(url - url_start + 1);
731         memcpy(tmp_url, url_start, url - url_start);
732         tmp_url[url - url_start] = 0;
733         proto_tree_add_string_hidden(tree, hf_rtsp_url, tvb,
734                 offset + (url_start - data), url - url_start, tmp_url);
735         g_free(tmp_url);
736 }
737
738 static void
739 process_rtsp_reply(tvbuff_t *tvb, int offset, const u_char *data,
740         size_t linelen, proto_tree *tree)
741 {
742         const u_char    *lineend = data + linelen;
743         const u_char    *status = data;
744         const u_char    *status_start;
745         unsigned int    status_i;
746
747         /* status code */
748         while (status < lineend && !isspace(*status))
749                 status++;
750         while (status < lineend && isspace(*status))
751                 status++;
752         status_start = status;
753         status_i = 0;
754         while (status < lineend && isdigit(*status))
755                 status_i = status_i * 10 + *status++ - '0';
756         proto_tree_add_uint_hidden(tree, hf_rtsp_status, tvb,
757                 offset + (status_start - data),
758                 status - status_start, status_i);
759 }
760
761 static void
762 dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
763 {
764         int             offset = 0;
765         int             len;
766
767         if (check_col(pinfo->cinfo, COL_PROTOCOL))
768                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTSP");
769         if (check_col(pinfo->cinfo, COL_INFO))
770                 col_clear(pinfo->cinfo, COL_INFO);
771
772         while (tvb_offset_exists(tvb, offset)) {
773                 len = (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR)
774                         ? dissect_rtspinterleaved(tvb, offset, pinfo, tree)
775                         : dissect_rtspmessage(tvb, offset, pinfo, tree);
776                 if (len == -1)
777                         break;
778                 offset += len;
779
780                 /*
781                  * OK, we've set the Protocol and Info columns for the
782                  * first RTSP message; make the columns non-writable,
783                  * so that we don't change it for subsequent RTSP messages.
784                  */
785                 col_set_writable(pinfo->cinfo, FALSE);
786         }
787 }
788
789 static void
790 rtsp_init(void)
791 {
792 /* Routine to initialize rtsp protocol before each capture or filter pass. */
793 /* Release any memory if needed.  Then setup the memory chunks.         */
794
795         if (rtsp_vals)
796                 g_mem_chunk_destroy(rtsp_vals);
797
798         rtsp_vals = g_mem_chunk_new("rtsp_vals",
799                 sizeof(rtsp_conversation_data_t),
800                 rtsp_hash_init_count * sizeof(rtsp_conversation_data_t),
801                 G_ALLOC_AND_FREE);
802 }
803
804 void
805 proto_register_rtsp(void)
806 {
807         static gint *ett[] = {
808                 &ett_rtspframe,
809                 &ett_rtsp,
810         };
811         static hf_register_info hf[] = {
812         { &hf_rtsp_method,
813         { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0, "", HFILL }},
814         { &hf_rtsp_url,
815         { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0, "", HFILL }},
816         { &hf_rtsp_status,
817         { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0, "", HFILL }},
818         };
819
820         proto_rtsp = proto_register_protocol("Real Time Streaming Protocol",
821                 "RTSP", "rtsp");
822         proto_register_field_array(proto_rtsp, hf, array_length(hf));
823         proto_register_subtree_array(ett, array_length(ett));
824
825         register_init_routine(rtsp_init);       /* register re-init routine */
826 }
827
828 void
829 proto_reg_handoff_rtsp(void)
830 {
831         dissector_handle_t rtsp_handle;
832
833         rtsp_handle = create_dissector_handle(dissect_rtsp, proto_rtsp);
834         dissector_add("tcp.port", TCP_PORT_RTSP, rtsp_handle);
835
836         sdp_handle = find_dissector("sdp");
837         rtp_handle = find_dissector("rtp");
838         rtcp_handle = find_dissector("rtcp");
839 }