Florian Lohoff's changes for RADIUS tunnel attributes and for the
[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.8 2000/02/15 21:03:04 gram Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@zing.org>
11  * Copyright 1998 Gerald Combs
12  *
13  * 
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  * 
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  * 
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  *
28  *
29  */
30
31 #include "config.h"
32
33 #ifdef HAVE_SYS_TYPES_H
34 #include <sys/types.h>
35 #endif
36
37 #include <string.h>
38 #include <ctype.h>
39
40 #include <glib.h>
41 #include "packet.h"
42 #include "packet-sdp.h"
43
44 static int proto_rtsp = -1;
45 static gint ett_rtsp = -1;
46
47 static int hf_rtsp_method = -1;
48 static int hf_rtsp_url = -1;
49 static int hf_rtsp_status = -1;
50
51 static int process_rtsp_request_or_reply(const u_char *data, int offset,
52         int linelen, proto_tree *tree);
53
54 static int
55 is_content_sdp(const u_char *line, int linelen)
56 {
57         const char      *hdr = "Content-Type:";
58         size_t          hdrlen = strlen(hdr);
59         const char      *type = "application/sdp";
60         size_t          typelen = strlen(type);
61
62         if (linelen < hdrlen || strncasecmp(hdr, line, hdrlen))
63                 return 0;
64
65         line += hdrlen;
66         linelen -= hdrlen;
67         while (linelen > 0 && (*line == ' ' || *line == '\t')) {
68                 line++;
69                 linelen--;
70         }
71
72         if (linelen < typelen || strncasecmp(type, line, typelen))
73                 return 0;
74
75         line += typelen;
76         linelen -= typelen;
77         if (linelen > 0 && !isspace(*line))
78                 return 0;
79
80         return 1;
81 }
82
83 void dissect_rtsp(const u_char *pd, int offset, frame_data *fd,
84         proto_tree *tree)
85 {
86         proto_tree      *rtsp_tree;
87         proto_item      *ti;
88         const u_char    *data, *dataend;
89         const u_char    *linep, *lineend, *eol;
90         int             linelen;
91         u_char          c;
92         int             is_sdp = 0;
93         int             end_offset;
94
95         data = &pd[offset];
96         dataend = data + END_OF_FRAME;
97         end_offset = offset + END_OF_FRAME;
98
99         rtsp_tree = NULL;
100         if (tree) {
101                 ti = proto_tree_add_item(tree, proto_rtsp, offset, 
102                         END_OF_FRAME, NULL);
103                 rtsp_tree = proto_item_add_subtree(ti, ett_rtsp);
104         }
105
106         if (check_col(fd, COL_PROTOCOL))
107                 col_add_str(fd, COL_PROTOCOL, "RTSP");
108         if (check_col(fd, COL_INFO)) {
109                 /*
110                  * Put the first line from the buffer into the summary
111                  * if it's an RTSP request or reply. Otherwise, just call 
112                  * it a continuation.
113                  */
114                 lineend = find_line_end(data, dataend, &eol);
115                 linelen = lineend - data;
116                 if (process_rtsp_request_or_reply(data, offset, linelen,
117                                 rtsp_tree))
118                         col_add_str(fd, COL_INFO, format_text(data, linelen));
119                 else
120                         col_add_str(fd, COL_INFO, "Continuation");
121         }
122
123         if (!rtsp_tree)
124                 return;
125
126         if (offset >= end_offset)
127                 goto bad_len;
128         while (data < dataend) {
129                 /*
130                  * Find the end of the line.
131                  */
132                 lineend = find_line_end(data, dataend, &eol);
133                 linelen = lineend - data;
134
135                 /*
136                  * OK, does it look like an RTSP request or
137                  * response?
138                  */
139                 if (process_rtsp_request_or_reply(data, offset, linelen,
140                                 rtsp_tree))
141                         goto is_rtsp;
142
143                 /*
144                  * No.  Does it look like a blank line (as would
145                  * appear at the end of an RTSP request)?
146                  */
147                 if (linelen == 1) {
148                         if (*data == '\n')
149                                 goto is_rtsp;
150                 }
151                 if (linelen == 2) {
152                         if (strncmp(data, "\r\n", 2) == 0 ||
153                             strncmp(data, "\n\r", 2) == 0)
154                                 goto is_rtsp;
155                 }
156
157                 /*
158                  * No.  Does it look like a MIME header?
159                  */
160                 linep = data;
161                 while (linep < lineend) {
162                         c = *linep++;
163                         if (!isprint(c))
164                                 break;  /* not printable, not a MIME header */
165                         switch (c) {
166
167                         case '(':
168                         case ')':
169                         case '<':
170                         case '>':
171                         case '@':
172                         case ',':
173                         case ';':
174                         case '\\':
175                         case '"':
176                         case '/':
177                         case '[':
178                         case ']':
179                         case '?':
180                         case '=':
181                         case '{':
182                         case '}':
183                                 /*
184                                  * It's a tspecial, so it's not
185                                  * part of a token, so it's not
186                                  * a field name for the beginning
187                                  * of a MIME header.
188                                  */
189                                 goto not_rtsp;
190
191                         case ':':
192                                 /*
193                                  * This ends the token; we consider
194                                  * this to be a MIME header.
195                                  */
196                                 if (is_content_sdp(data, linelen))
197                                         is_sdp = 1;
198                                 goto is_rtsp;
199                         }
200                 }
201
202         not_rtsp:
203                 /*
204                  * We don't consider this part of an RTSP request or
205                  * reply, so we don't display it.
206                  */
207                 break;
208
209         is_rtsp:
210                 /*
211                  * Put this line.
212                  */
213                 proto_tree_add_text(rtsp_tree, offset, linelen, "%s",
214                         format_text(data, linelen));
215                 offset += linelen;
216                 data = lineend;
217         }
218
219         if (is_sdp) {
220                 dissect_sdp(pd, offset, fd, tree);
221                 if (check_col(fd, COL_PROTOCOL))
222                         col_add_str(fd, COL_PROTOCOL, "RTSP/SDP");
223         }
224         else if (data < dataend) {
225                 proto_tree_add_text(rtsp_tree, offset, END_OF_FRAME,
226                     "Data (%d bytes)", END_OF_FRAME);
227         }
228         return;
229
230 bad_len:
231         proto_tree_add_text(rtsp_tree, end_offset, 0,
232                 "Unexpected end of packet");
233 }
234
235 const char *rtsp_methods[] = {
236         "DESCRIBE", "ANNOUNCE", "GET_PARAMETER", "OPTIONS",
237         "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP",
238         "SET_PARAMETER", "TEARDOWN"
239 };
240 const int rtsp_nmethods = sizeof(rtsp_methods) / sizeof(*rtsp_methods);
241
242 static int
243 process_rtsp_request_or_reply(const u_char *data, int offset, int linelen,
244         proto_tree *tree)
245 {
246         int             ii;
247         const u_char    *lineend = data + linelen;
248
249         /* Reply */
250         if (linelen >= 5 && !strncasecmp("RTSP/", data, 5)) {
251                 if (tree) {
252                         /* status code */
253                         const u_char *status = data;
254                         const u_char *status_start;
255                         unsigned int status_i = 0;
256                         while (status < lineend && !isspace(*status))
257                                 status++;
258                         while (status < lineend && isspace(*status))
259                                 status++;
260                         status_start = status;
261                         while (status < lineend && isdigit(*status))
262                                 status_i = status_i * 10 + *status++ - '0';
263                         proto_tree_add_item_hidden(tree, hf_rtsp_status,
264                                 offset + (status_start - data),
265                                 status - status_start, status_i);
266                 }
267                 return TRUE;
268         }
269
270         /* Request Methods */
271         for (ii = 0; ii < rtsp_nmethods; ii++) {
272                 size_t len = strlen(rtsp_methods[ii]);
273                 if (linelen >= len && !strncasecmp(rtsp_methods[ii], data, len))
274                         break;
275         }
276         if (ii == rtsp_nmethods)
277                 return FALSE;
278
279         if (tree) {
280                 const u_char *url;
281                 const u_char *url_start;
282                 u_char *tmp_url;
283
284                 /* method name */
285                 proto_tree_add_item_hidden(tree, hf_rtsp_method, offset,
286                         strlen(rtsp_methods[ii]), rtsp_methods[ii]);
287
288                 /* URL */
289                 url = data;
290                 while (url < lineend && !isspace(*url))
291                         url++;
292                 while (url < lineend && isspace(*url))
293                         url++;
294                 url_start = url;
295                 while (url < lineend && !isspace(*url))
296                         url++;
297                 tmp_url = g_malloc(url - url_start + 1);
298                 memcpy(tmp_url, url_start, url - url_start);
299                 tmp_url[url - url_start] = 0;
300                 proto_tree_add_item_hidden(tree, hf_rtsp_url,
301                         offset + (url_start - data), url - url_start, tmp_url);
302                 g_free(tmp_url);
303         }
304         return TRUE;
305 }
306
307 void
308 proto_register_rtsp(void)
309 {
310         static gint *ett[] = {
311                 &ett_rtsp,
312         };
313         static hf_register_info hf[] = {
314         { &hf_rtsp_method,
315         { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0 }},
316         { &hf_rtsp_url,
317         { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0 }},
318         { &hf_rtsp_status,
319         { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0 }},
320         };
321
322         proto_rtsp = proto_register_protocol("Real Time Streaming Protocol",
323                 "rtsp");
324         proto_register_field_array(proto_rtsp, hf, array_length(hf));
325         proto_register_subtree_array(ett, array_length(ett));
326 }