aboutsummaryrefslogtreecommitdiffstats
path: root/packet-sip.c
blob: ee7bb7ed51bef783b3771f3bae0762f06f2381ab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/* packet-sip.c
 * Routines for the Session Initiation Protocol (SIP) dissection.
 * RFC 2543
 *
 * TODO: Pay attention to Content-Type: It might not always be SDP.
 *       Add hf_* fields for filtering support.
 *       Add sip msg body dissection based on Content-Type for:
 *                SDP, MIME, and other types
 *       Align SIP methods with recent Internet Drafts or RFC
 *               (SIP INFO, rfc2976 - done)
 *               (SIP SUBSCRIBE-NOTIFY - done)
 *               (SIP REFER - done)
 *               check for other
 *
 * Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
 * Copyright 2001, Jean-Francois Mule <jfm@clarent.com>
 *
 * $Id: packet-sip.c,v 1.32 2002/08/28 21:00:30 jmayer Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * Copyright 1998 Gerald Combs
 *
 * Copied from packet-cops.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>
#include <epan/packet.h>

#define TCP_PORT_SIP 5060
#define UDP_PORT_SIP 5060

/* Initialize the protocol and registered fields */
static gint proto_sip = -1;
static gint hf_msg_hdr = -1;

/* Initialize the subtree pointers */
static gint ett_sip = -1;
static gint ett_sip_hdr = -1;

static const char *sip_methods[] = {
        "<Invalid method>",      /* Pad so that the real methods start at index 1 */
        "ACK",
        "BYE",
        "CANCEL",
        "DO",
        "INFO",
        "INVITE",
        "MESSAGE",
        "NOTIFY",
        "OPTIONS",
        "PRACK",
        "QAUTH",
        "REFER",
        "REGISTER",
        "SPRACK",
        "SUBSCRIBE"
};

static gboolean sip_is_request(tvbuff_t *tvb, gint eol);
static gboolean sip_is_known_request(tvbuff_t *tvb, guint32 offset);
static gint sip_get_msg_offset(tvbuff_t *tvb, guint32 offset);

static dissector_handle_t sdp_handle;
static dissector_handle_t data_handle;

#define SIP2_HDR "SIP/2.0"
#define SIP2_HDR_LEN (strlen (SIP2_HDR))

/* Code to actually dissect the packets */
static void dissect_sip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
        guint32 offset;
        gint eol, next_offset, msg_offset;
        tvbuff_t *next_tvb;
        gboolean is_request, is_known_request;
        char *req_descr;

        /*
         * Note that "tvb_strneql()" doesn't throw exceptions, so
         * "sip_is_request()" won't throw an exception.
         *
         * Note that "tvb_find_line_end()" will return a value that
         * is not longer than what's in the buffer, so the
         * "tvb_get_ptr()" call s below won't throw exceptions.
         */
        offset = 0;
        eol = tvb_find_line_end(tvb, 0, -1, &next_offset, FALSE);
        /* XXX - Check for a valid status message as well. */
        is_request = sip_is_request(tvb, eol);
        is_known_request = sip_is_known_request(tvb, 0);
        /* XXX - Is this case-sensitive?  RFC 2543 didn't explicitly say. */
        if (tvb_strneql(tvb, 0, SIP2_HDR, SIP2_HDR_LEN) != 0 && ! is_request)
                goto bad;

        if (check_col(pinfo->cinfo, COL_PROTOCOL))
                col_set_str(pinfo->cinfo, COL_PROTOCOL, "SIP");

        req_descr = is_known_request ? "Request" : "Unknown request";
        if (check_col(pinfo->cinfo, COL_INFO))
                col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s",
                             is_request ? req_descr : "Status",
                             is_request ?
                             tvb_format_text(tvb, 0, eol - SIP2_HDR_LEN - 1) :
                             tvb_format_text(tvb, SIP2_HDR_LEN + 1, eol - SIP2_HDR_LEN - 1));


        msg_offset = sip_get_msg_offset(tvb, offset);
        if (msg_offset < 0) {
                /*
                 * XXX - this may just mean that the entire SIP message
                 * didn't fit in this TCP segment.
                 */
                goto bad;
        }

        if (tree) {
                proto_item *ti, *th;
                proto_tree *sip_tree, *hdr_tree;

                ti = proto_tree_add_item(tree, proto_sip, tvb, 0, -1, FALSE);
                sip_tree = proto_item_add_subtree(ti, ett_sip);

                proto_tree_add_text(sip_tree, tvb, 0, next_offset, "%s line: %s",
                                    is_request ? req_descr : "Status",
                                    tvb_format_text(tvb, 0, eol));

                offset = next_offset;
                th = proto_tree_add_item(sip_tree, hf_msg_hdr, tvb, offset, msg_offset - offset, FALSE);
                hdr_tree = proto_item_add_subtree(th, ett_sip_hdr);

                /* - 2 since we have a CRLF separating the message-body */
                while (msg_offset - 2 > (int) offset) {
                        eol = tvb_find_line_end(tvb, offset, -1, &next_offset,
                            FALSE);
                        proto_tree_add_text(hdr_tree, tvb, offset, next_offset - offset, "%s",
                                            tvb_format_text(tvb, offset, eol));
                        offset = next_offset;
                }
                offset += 2;  /* Skip the CRLF mentioned above */
       }

        if (tvb_offset_exists(tvb, msg_offset)) {
                next_tvb = tvb_new_subset(tvb, msg_offset, -1, -1);
                call_dissector(sdp_handle, next_tvb, pinfo, tree);
        }

        return;

  bad:
        next_tvb = tvb_new_subset(tvb, offset, -1, -1);
        call_dissector(data_handle,next_tvb, pinfo, tree);

        return;
}

static gboolean
dissect_sip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
        gint eol, next_offset;

        /*
         * This is a heuristic dissector, which means we get all the
         * UDP and TCP traffic not sent to a known dissector and not
         * claimed by a heuristic dissector called before us!
         * So we first check if the frame is really meant for us.
         */

        /*
         * Check for a response.
         * First, make sure we have enough data to do the check.
         */
        if (!tvb_bytes_exist(tvb, 0, SIP2_HDR_LEN)) {
                /*
                 * We don't.
                 */
                return FALSE;
        }

        /*
         * Now see if we have a response header; they begin with
         * "SIP/2.0".
         */
        if (tvb_strneql(tvb, 0, SIP2_HDR, SIP2_HDR_LEN) != 0)  {
                /*
                 * We don't, so this isn't a response; check for a request.
                 * They *end* with "SIP/2.0".
                 */
                eol = tvb_find_line_end(tvb, 0, -1, &next_offset, FALSE);
                if (eol <= (gint)SIP2_HDR_LEN) {
                        /*
                         * The line isn't long enough to end with "SIP/2.0".
                         */
                        return FALSE;
                }
                if (!tvb_bytes_exist(tvb, eol - SIP2_HDR_LEN, SIP2_HDR_LEN)) {
                        /*
                         * We don't have enough of the data in the line
                         * to check.
                         */
                        return FALSE;
                }

                if (tvb_strneql(tvb, eol - SIP2_HDR_LEN, SIP2_HDR, SIP2_HDR_LEN - 1) != 0) {
                        /*
                         * Not a request, either.
                         */
                        return FALSE;
                }
        }

        /*
         * The message seems to be a valid SIP message!
         */
        dissect_sip(tvb, pinfo, tree);

        return TRUE;
}

/* Returns the offset to the start of the optional message-body, or
 * -1 if not found.
 */
static gint sip_get_msg_offset(tvbuff_t *tvb, guint32 offset)
{
        gint eol;

        while ((eol = tvb_find_guint8(tvb, offset, -1, '\r')) > 0
            && tvb_bytes_exist(tvb, eol, 4)) {
                if (tvb_get_guint8(tvb, eol + 1) == '\n' &&
                    tvb_get_guint8(tvb, eol + 2) == '\r' &&
                    tvb_get_guint8(tvb, eol + 3) == '\n')
                        return eol + 4;
                offset = eol + 2;
        }

        return -1;
}

/* From section 4.1 of RFC 2543:
 *
 * Request-Line  =  Method SP Request-URI SP SIP-Version CRLF
 */

static gboolean sip_is_request(tvbuff_t *tvb, gint eol)
{
        gint meth_len, req_len, req_colon_pos;
        guint8 req_start, ver_start, ver_len;

        meth_len = tvb_find_guint8(tvb, 0, -1, ' ');
        req_start = meth_len + 1;
        req_len = tvb_find_guint8(tvb, req_start, -1, ' ') - meth_len - 1;
        req_colon_pos = tvb_find_guint8(tvb, req_start + 1, -1, ':');
        ver_start = meth_len + req_len + 2;
        ver_len = eol - req_len - meth_len - 2; /*CRLF, plus two spaces */

        /* Do we have:
         *   A method of at least one character?
         *   A URI consisting of at least three characters?
         *   A version string length matching that of SIP2_HDR?
         */
        if (meth_len <= 0 || req_len <= 3 || ver_len != SIP2_HDR_LEN)
                return FALSE;

        /* Does our method have a colon character? */
        if (req_colon_pos < 0 || req_colon_pos > ver_start)
                return FALSE;
        /* XXX - Check for a proper URI prefix? */

        /* Do we have a proper version string? */
        if (tvb_strneql(tvb, ver_start, SIP2_HDR, SIP2_HDR_LEN))
                return TRUE;

        return TRUE;
}

static gboolean sip_is_known_request(tvbuff_t *tvb, guint32 offset)
{
        guint8 i, meth_len;

        meth_len = tvb_find_guint8(tvb, 0, -1, ' ');

        for (i = 1; i < array_length(sip_methods); i++) {
                if ((meth_len == strlen(sip_methods[i])) && tvb_strneql(tvb, offset, sip_methods[i], strlen(sip_methods[i])) == 0)
                        return TRUE;
        }

        return FALSE;
}

/* Register the protocol with Ethereal */
void proto_register_sip(void)
{

        /* Setup list of header fields */
        static hf_register_info hf[] = {

                { &hf_msg_hdr,
                        { "Message Header",           "sip.msg_hdr",
                        FT_NONE, 0, NULL, 0,
                        "Message Header in SIP message", HFILL }
                },
        };

        /* Setup protocol subtree array */
        static gint *ett[] = {
                &ett_sip,
                &ett_sip_hdr,
        };

        /* Register the protocol name and description */
        proto_sip = proto_register_protocol("Session Initiation Protocol",
            "SIP", "sip");

        /* Required function calls to register the header fields and subtrees used */
        proto_register_field_array(proto_sip, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_sip(void)
{
        dissector_handle_t sip_handle;

        sip_handle = create_dissector_handle(dissect_sip, proto_sip);
        dissector_add("tcp.port", TCP_PORT_SIP, sip_handle);
        dissector_add("udp.port", UDP_PORT_SIP, sip_handle);

        heur_dissector_add( "udp", dissect_sip_heur, proto_sip );
        heur_dissector_add( "tcp", dissect_sip_heur, proto_sip );

        /*
         * Get a handle for the SDP dissector.
         */
        sdp_handle = find_dissector("sdp");
        data_handle = find_dissector("data");
}