Squelch a complaint from Visual C++ 6.0 (the code was OK beforehand, at
[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.7 2000/01/22 06:22:17 guy 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
43 static int proto_rtsp = -1;
44 static gint ett_rtsp = -1;
45
46 static int hf_rtsp_method = -1;
47 static int hf_rtsp_url = -1;
48 static int hf_rtsp_status = -1;
49
50 static int process_rtsp_request_or_reply(const u_char *data, int offset,
51         int linelen, proto_tree *tree);
52
53 static int
54 is_content_sdp(const u_char *line, int linelen)
55 {
56         const char      *hdr = "Content-Type:";
57         size_t          hdrlen = strlen(hdr);
58         const char      *type = "application/sdp";
59         size_t          typelen = strlen(type);
60
61         if (linelen < hdrlen || strncasecmp(hdr, line, hdrlen))
62                 return 0;
63
64         line += hdrlen;
65         linelen -= hdrlen;
66         while (linelen > 0 && (*line == ' ' || *line == '\t')) {
67                 line++;
68                 linelen--;
69         }
70
71         if (linelen < typelen || strncasecmp(type, line, typelen))
72                 return 0;
73
74         line += typelen;
75         linelen -= typelen;
76         if (linelen > 0 && !isspace(*line))
77                 return 0;
78
79         return 1;
80 }
81
82 void dissect_rtsp(const u_char *pd, int offset, frame_data *fd,
83         proto_tree *tree)
84 {
85         proto_tree      *rtsp_tree;
86         proto_item      *ti;
87         const u_char    *data, *dataend;
88         const u_char    *linep, *lineend, *eol;
89         int             linelen;
90         u_char          c;
91         int             is_sdp = 0;
92         int             end_offset;
93
94         data = &pd[offset];
95         dataend = data + END_OF_FRAME;
96         end_offset = offset + END_OF_FRAME;
97
98         rtsp_tree = NULL;
99         if (tree) {
100                 ti = proto_tree_add_item(tree, proto_rtsp, offset, 
101                         END_OF_FRAME, NULL);
102                 rtsp_tree = proto_item_add_subtree(ti, ett_rtsp);
103         }
104
105         if (check_col(fd, COL_PROTOCOL))
106                 col_add_str(fd, COL_PROTOCOL, "RTSP");
107         if (check_col(fd, COL_INFO)) {
108                 /*
109                  * Put the first line from the buffer into the summary
110                  * if it's an RTSP request or reply. Otherwise, just call 
111                  * it a continuation.
112                  */
113                 lineend = find_line_end(data, dataend, &eol);
114                 linelen = lineend - data;
115                 if (process_rtsp_request_or_reply(data, offset, linelen,
116                                 rtsp_tree))
117                         col_add_str(fd, COL_INFO, format_text(data, linelen));
118                 else
119                         col_add_str(fd, COL_INFO, "Continuation");
120         }
121
122         if (!rtsp_tree)
123                 return;
124
125         if (offset >= end_offset)
126                 goto bad_len;
127         while (data < dataend) {
128                 /*
129                  * Find the end of the line.
130                  */
131                 lineend = find_line_end(data, dataend, &eol);
132                 linelen = lineend - data;
133
134                 /*
135                  * OK, does it look like an RTSP request or
136                  * response?
137                  */
138                 if (process_rtsp_request_or_reply(data, offset, linelen,
139                                 rtsp_tree))
140                         goto is_rtsp;
141
142                 /*
143                  * No.  Does it look like a blank line (as would
144                  * appear at the end of an RTSP request)?
145                  */
146                 if (linelen == 1) {
147                         if (*data == '\n')
148                                 goto is_rtsp;
149                 }
150                 if (linelen == 2) {
151                         if (strncmp(data, "\r\n", 2) == 0 ||
152                             strncmp(data, "\n\r", 2) == 0)
153                                 goto is_rtsp;
154                 }
155
156                 /*
157                  * No.  Does it look like a MIME header?
158                  */
159                 linep = data;
160                 while (linep < lineend) {
161                         c = *linep++;
162                         if (!isprint(c))
163                                 break;  /* not printable, not a MIME header */
164                         switch (c) {
165
166                         case '(':
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                                 /*
183                                  * It's a tspecial, so it's not
184                                  * part of a token, so it's not
185                                  * a field name for the beginning
186                                  * of a MIME header.
187                                  */
188                                 goto not_rtsp;
189
190                         case ':':
191                                 /*
192                                  * This ends the token; we consider
193                                  * this to be a MIME header.
194                                  */
195                                 if (is_content_sdp(data, linelen))
196                                         is_sdp = 1;
197                                 goto is_rtsp;
198                         }
199                 }
200
201         not_rtsp:
202                 /*
203                  * We don't consider this part of an RTSP request or
204                  * reply, so we don't display it.
205                  */
206                 break;
207
208         is_rtsp:
209                 /*
210                  * Put this line.
211                  */
212                 proto_tree_add_text(rtsp_tree, offset, linelen, "%s",
213                         format_text(data, linelen));
214                 offset += linelen;
215                 data = lineend;
216         }
217
218         if (is_sdp) {
219                 dissect_sdp(pd, offset, fd, tree);
220                 if (check_col(fd, COL_PROTOCOL))
221                         col_add_str(fd, COL_PROTOCOL, "RTSP/SDP");
222         }
223         else if (data < dataend) {
224                 proto_tree_add_text(rtsp_tree, offset, END_OF_FRAME,
225                     "Data (%d bytes)", END_OF_FRAME);
226         }
227         return;
228
229 bad_len:
230         proto_tree_add_text(rtsp_tree, end_offset, 0,
231                 "Unexpected end of packet");
232 }
233
234 const char *rtsp_methods[] = {
235         "DESCRIBE", "ANNOUNCE", "GET_PARAMETER", "OPTIONS",
236         "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP",
237         "SET_PARAMETER", "TEARDOWN"
238 };
239 const int rtsp_nmethods = sizeof(rtsp_methods) / sizeof(*rtsp_methods);
240
241 static int
242 process_rtsp_request_or_reply(const u_char *data, int offset, int linelen,
243         proto_tree *tree)
244 {
245         int             ii;
246         const u_char    *lineend = data + linelen;
247
248         /* Reply */
249         if (linelen >= 5 && !strncasecmp("RTSP/", data, 5)) {
250                 if (tree) {
251                         /* status code */
252                         const u_char *status = data;
253                         const u_char *status_start;
254                         unsigned int status_i = 0;
255                         while (status < lineend && !isspace(*status))
256                                 status++;
257                         while (status < lineend && isspace(*status))
258                                 status++;
259                         status_start = status;
260                         while (status < lineend && isdigit(*status))
261                                 status_i = status_i * 10 + *status++ - '0';
262                         proto_tree_add_item_hidden(tree, hf_rtsp_status,
263                                 offset + (status_start - data),
264                                 status - status_start, status_i);
265                 }
266                 return TRUE;
267         }
268
269         /* Request Methods */
270         for (ii = 0; ii < rtsp_nmethods; ii++) {
271                 size_t len = strlen(rtsp_methods[ii]);
272                 if (linelen >= len && !strncasecmp(rtsp_methods[ii], data, len))
273                         break;
274         }
275         if (ii == rtsp_nmethods)
276                 return FALSE;
277
278         if (tree) {
279                 const u_char *url;
280                 const u_char *url_start;
281                 u_char *tmp_url;
282
283                 /* method name */
284                 proto_tree_add_item_hidden(tree, hf_rtsp_method, offset,
285                         strlen(rtsp_methods[ii]), rtsp_methods[ii]);
286
287                 /* URL */
288                 url = data;
289                 while (url < lineend && !isspace(*url))
290                         url++;
291                 while (url < lineend && isspace(*url))
292                         url++;
293                 url_start = url;
294                 while (url < lineend && !isspace(*url))
295                         url++;
296                 tmp_url = g_malloc(url - url_start + 1);
297                 memcpy(tmp_url, url_start, url - url_start);
298                 tmp_url[url - url_start] = 0;
299                 proto_tree_add_item_hidden(tree, hf_rtsp_url,
300                         offset + (url_start - data), url - url_start, tmp_url);
301                 g_free(tmp_url);
302         }
303         return TRUE;
304 }
305
306 void
307 proto_register_rtsp(void)
308 {
309         static gint *ett[] = {
310                 &ett_rtsp,
311         };
312         static hf_register_info hf[] = {
313         { &hf_rtsp_method,
314         { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0 }},
315         { &hf_rtsp_url,
316         { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0 }},
317         { &hf_rtsp_status,
318         { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0 }},
319         };
320
321         proto_rtsp = proto_register_protocol("Real Time Streaming Protocol",
322                 "rtsp");
323         proto_register_field_array(proto_rtsp, hf, array_length(hf));
324         proto_register_subtree_array(ett, array_length(ett));
325 }