From Shoichi Sakane via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5270 :
[obnox/wireshark/wip.git] / epan / dissectors / packet-coap.c
1 /* packet-coap.c
2  * Routines for COAP packet disassembly
3  * Shoichi Sakane <sakane@tanu.org>
4  *
5  * $Id$
6  * draft-core-coap-02.txt
7  *
8  * Wireshark - Network traffic analyzer
9  * By Gerald Combs <gerald@wireshark.org>
10  * Copyright 1998 Gerald Combs
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <glib.h>
32
33 #include <epan/packet.h>
34 #include <epan/prefs.h>
35
36 static dissector_table_t media_type_dissector_table;
37
38 static int proto_coap = -1;
39
40 static int hf_coap_version              = -1;
41 static int hf_coap_ttype                = -1;
42 static int hf_coap_opt_count            = -1;
43 static int hf_coap_code                 = -1;
44 static int hf_coap_tid                  = -1;
45 static int hf_coap_opt_type             = -1;
46 static int hf_coap_opt_ctype            = -1;
47 static int hf_coap_opt_max_age          = -1;
48 static int hf_coap_opt_etag             = -1;
49 static int hf_coap_opt_uri_authority    = -1;
50 static int hf_coap_opt_location         = -1;
51 static int hf_coap_opt_uri_path         = -1;
52
53 static gint ett_coap                    = -1;
54 static gint ett_coap_noop               = -1;
55 static gint ett_coap_ctype              = -1;
56 static gint ett_coap_max_age            = -1;
57 static gint ett_coap_uri_scheme         = -1;
58 static gint ett_coap_etag               = -1;
59 static gint ett_coap_uri_authority      = -1;
60 static gint ett_coap_location           = -1;
61 static gint ett_coap_uri_path           = -1;
62 static gint ett_coap_payload            = -1;
63
64 /* TODO: COAP port number will be assigned by IANA after the draft become a RFC */
65 #define DEFAULT_COAP_PORT       61616
66
67 static const gchar *coap_content_type = NULL;
68 static guint global_coap_port_number = DEFAULT_COAP_PORT;
69
70 /*
71  * Transaction Type
72  */
73 static const value_string vals_ttype[] = {
74         { 0, "Confirmable" },
75         { 1, "Non-Confirmable" },
76         { 2, "Acknowledgement" },
77         { 3, "Reset" },
78         { 0, NULL },
79 };
80
81 /*
82  * Method Code
83  * Response Code
84  */
85 static const value_string vals_code[] = {
86         /* method code */
87         { 1, "GET" },
88         { 2, "POST" },
89         { 3, "PUT" },
90         { 4, "DELETE" },
91
92         /* response code */
93         { 40,  "100 Continue" },
94         { 80,  "200 OK"},
95         { 81,  "201 Created"},
96         { 124, "304 Not Modified"},
97         { 160, "400 Bad Request"},
98         { 164, "404 Not Found"},
99         { 165, "405 Method Not Allowed"},
100         { 175, "415 Unsupported Media Type"},
101         { 200, "500 Internal Server Error"},
102         { 202, "502 Bad Gateway"},
103         { 203, "503 Service Unavailable"},
104         { 204, "504 Gateway Timeout"},
105         { 0, NULL },
106 };
107
108 /*
109  * Option Headers
110  * No-Option must not be included in this structure, is handled in the function
111  * of the dissector, especially.
112  */
113 #define COAP_OPT_CONTENT_TYPE   1
114 #define COAP_OPT_MAX_AGE        2
115 #define COAP_OPT_ETAG           4
116 #define COAP_OPT_URI_AUTHORITY  5
117 #define COAP_OPT_LOCATION       6
118 #define COAP_OPT_URI_PATH       9
119
120 static const value_string vals_opt_type[] = {
121         { COAP_OPT_CONTENT_TYPE, "Content-Type" },
122         { COAP_OPT_MAX_AGE, "Max-age"},
123         { COAP_OPT_ETAG, "Etag"},
124         { COAP_OPT_URI_AUTHORITY, "Uri-Authority"},
125         { COAP_OPT_LOCATION, "Location"},
126         { COAP_OPT_URI_PATH, "Uri-Path"},
127         { 0, NULL },
128 };
129
130 static const value_string vals_ctype[] = {
131         { 0, "text/plain (UTF-8)" },
132         { 1, "text/xml (UTF-8)" },
133         { 2, "text/csv (UTF-8)" },
134         { 3, "text/html (UTF-8)" },
135         { 21, "image/gif" },
136         { 22, "image/jpeg" },
137         { 23, "image/png" },
138         { 24, "image/tiff" },
139         { 25, "audio/raw" },
140         { 26, "video/raw" },
141         { 40, "application/link-format" },
142         { 41, "application/xml" },
143         { 42, "application/octet-stream" },
144         { 43, "application/rdf+xml" },
145         { 44, "application/soap+xml" },
146         { 45, "application/atom+xml" },
147         { 46, "application/xmpp+xml" },
148         { 47, "application/exi" },
149         { 48, "application/x-bxml" },
150         { 49, "application/fastinfoset" },
151         { 50, "application/soap+fastinfoset" },
152         { 51, "application/json" },
153         { 0, NULL },
154 };
155
156 void proto_reg_handoff_coap(void);
157
158 /*
159  * dissector for each option of COAP.
160  * return the total length of the option including the header (e.g. delta and length).
161  */
162 static int
163 dissect_coap_options(tvbuff_t *tvb, proto_tree *coap_tree, proto_tree *parent_tree _U_, int offset, guint8 *opt_code)
164 {
165         guint8 opt_delta;
166         guint32 opt_ctype = 0;
167         guint opt_max_age = 0;
168         gint opt_length;
169         proto_tree *subtree = NULL;
170         proto_item *item = NULL;
171         int opt_hlen = 0;
172
173         opt_delta = (tvb_get_guint8(tvb, offset) & 0xf0) >> 4;
174         *opt_code += opt_delta;
175         opt_length = (tvb_get_guint8(tvb, offset) & 0x0f);
176         opt_hlen = 1;
177         if (opt_length == 0x0f) {
178                 opt_length += tvb_get_guint8(tvb, offset + 1);
179                 opt_hlen = 2;
180         }
181         item = proto_tree_add_uint_format(coap_tree, hf_coap_opt_type, tvb, offset, 1, *opt_code,
182             "Option (Length: %u) %s",
183             opt_length, val_to_str(*opt_code, vals_opt_type, "Unknown Option Type"));
184         offset += opt_hlen;
185
186         /* if opt_code is a multiple of 14, that means the option is a noop option */
187         if (*opt_code % 14 == 0) {
188                 subtree = proto_item_add_subtree(item, ett_coap_noop);
189                 proto_tree_add_text(subtree, tvb, 0, 0, "No-Op option");
190         } else {
191                 switch (*opt_code) {
192                 case COAP_OPT_CONTENT_TYPE:
193                         subtree = proto_item_add_subtree(item, ett_coap_ctype);
194                         opt_ctype = tvb_get_guint8(tvb, offset);
195                         coap_content_type = val_to_str(opt_ctype, vals_code, "Unknown %d");
196                         proto_tree_add_item(subtree, hf_coap_opt_ctype, tvb, offset, 1, FALSE);
197                         break;
198                 case COAP_OPT_MAX_AGE:
199                         subtree = proto_item_add_subtree(item, ett_coap_max_age);
200                         switch (opt_length) {
201                         case 1:
202                                 opt_max_age = (guint)tvb_get_guint8(tvb, offset);
203                                 break;
204                         case 2:
205                                 opt_max_age = (guint)tvb_get_ntohs(tvb, offset);
206                                 break;
207                         case 3:
208                                 opt_max_age = (guint)tvb_get_ntoh24(tvb, offset);
209                                 break;
210                         case 4:
211                                 opt_max_age = (guint)tvb_get_ntohl(tvb, offset);
212                                 break;
213                         default:
214                                 proto_tree_add_text(subtree, tvb, 0, 0, "Invalid length: %d", opt_length);
215                                 break;
216                         }
217                         if (opt_length >= 1 && opt_length <= 4) {
218                           proto_tree_add_item(subtree, hf_coap_opt_max_age, tvb, offset, opt_length, FALSE);
219                         }
220                         break;
221                 case COAP_OPT_ETAG:
222                         subtree = proto_item_add_subtree(item, ett_coap_etag);
223                         proto_tree_add_item(subtree, hf_coap_opt_etag, tvb, offset, opt_length, FALSE);
224                         break;
225                 case COAP_OPT_URI_AUTHORITY:
226                         subtree = proto_item_add_subtree(item, ett_coap_uri_authority);
227                         proto_tree_add_item(subtree, hf_coap_opt_uri_authority, tvb, offset, opt_length, FALSE);
228                         break;
229                 case COAP_OPT_LOCATION:
230                         subtree = proto_item_add_subtree(item, ett_coap_location);
231                         proto_tree_add_item(subtree, hf_coap_opt_location, tvb, offset, opt_length, FALSE);
232                         break;
233                 case COAP_OPT_URI_PATH:
234                         subtree = proto_item_add_subtree(item, ett_coap_uri_path);
235                         proto_tree_add_item(subtree, hf_coap_opt_uri_path, tvb, offset, opt_length, FALSE);
236                         break;
237                 default:
238                         proto_tree_add_text(subtree, tvb, 0, 0, "Unkown Option Type");
239                 }
240         }
241         return offset + opt_length;
242 }
243
244 static void
245 dissect_coap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree)
246 {
247         guint offset = 0;
248         proto_item *coap_root = NULL;
249         proto_tree *coap_tree = NULL;
250         guint8 ttype = 0;
251         guint8 opt_count = 0;
252         guint8 code = 0;
253         guint16 tid = 0;
254         guint coap_length = pinfo->iplen - pinfo->iphdrlen - 8;
255         guint8 opt_code = 0;
256
257         col_set_str(pinfo->cinfo, COL_PROTOCOL, "COAP");
258         col_clear(pinfo->cinfo, COL_INFO);
259
260         if (!parent_tree)
261                 return;
262
263         coap_root = proto_tree_add_item(parent_tree, proto_coap, tvb, offset, -1, FALSE);
264         coap_tree = proto_item_add_subtree(coap_root, ett_coap);
265
266         proto_tree_add_item(coap_tree, hf_coap_version, tvb, offset, 1, FALSE);
267
268         proto_tree_add_item(coap_tree, hf_coap_ttype, tvb, offset, 1, FALSE);
269         ttype = (tvb_get_guint8(tvb, offset) & 0x30) >> 4;
270         col_add_fstr(pinfo->cinfo, COL_INFO, "%s", val_to_str(ttype, vals_ttype, "Unknown %d"));
271
272         proto_tree_add_item(coap_tree, hf_coap_opt_count, tvb, offset, 1, FALSE);
273         opt_count = tvb_get_guint8(tvb, offset) & 0x0f;
274         offset += 1;
275
276         proto_tree_add_item(coap_tree, hf_coap_code, tvb, offset, 1, FALSE);
277         code = tvb_get_guint8(tvb, offset);
278         col_append_fstr(pinfo->cinfo, COL_INFO, ", %s", val_to_str(code, vals_code, "Unknown %d"));
279         offset += 1;
280
281         proto_tree_add_item(coap_tree, hf_coap_tid, tvb, offset, 2, FALSE);
282         tid = tvb_get_ntohs(tvb, offset);
283         offset += 2;
284
285         /* append the header information */
286         proto_item_append_text(coap_tree, ", TID: %u, Length: %u", tid, coap_length);
287
288         /* dissect the options */
289         while (opt_count--) {
290                 offset = dissect_coap_options(tvb, coap_tree, parent_tree, offset, &opt_code);
291                 if (coap_length < offset) {
292                         /* error */
293                         proto_tree_add_text(coap_tree, tvb, 0, 0, "Invalid length: coap_length(%d) < offset(%d)", coap_length, offset);
294                         return;
295                 }
296         }
297
298         /* dissect the payload */
299         if (coap_length > offset) {
300                 proto_tree *payload_tree = NULL;
301                 proto_item *payload_item = NULL;
302                 tvbuff_t *payload_tvb;
303                 guint payload_length = coap_length - offset;
304                 char *ctype_str_default = "";
305                 gboolean result = TRUE;
306
307                 /*
308                  * TODO:
309                  * currently, coap_content_type is used to distinguish whether
310                  * the content-type was specified.  If we need to properly handle
311                  * the case when the type was unknown, we need another flag.
312                  */
313                 if (coap_content_type == NULL) {
314                         /* default: coap-02 section 3.2.1 */
315                         /* when it's NULL, "text/plain" is set anyway */
316                         coap_content_type = "text/plain";
317                         ctype_str_default = "(as default)";
318                 }
319                 /*
320                  * TODO: should the content type be canonicalized,
321                  * currently assuming it be small ?
322                  */
323
324                 payload_item = proto_tree_add_text(coap_tree, tvb, offset, -1, "Payload Content-Type: %s%s, Length: %u, offset: %u",
325                     coap_content_type, ctype_str_default, payload_length, offset);
326                 payload_tree = proto_item_add_subtree(payload_item, ett_coap_payload);
327                 payload_tvb = tvb_new_subset(tvb, offset, payload_length, payload_length);
328
329                 result = dissector_try_string(media_type_dissector_table, coap_content_type, payload_tvb, pinfo, payload_tree);
330                 if (!result) {
331                         /* TODO: call heuristic dissector */
332                         ;
333                 }
334         }
335 }
336
337 /*
338  * Protocol initialization
339  */
340 void
341 proto_register_coap(void)
342 {
343         static hf_register_info hf[] = {
344             { &hf_coap_version, { "Version", "coap.version", FT_UINT8, BASE_DEC, NULL, 0xc0, "COAP Version", HFILL }},
345             { &hf_coap_ttype, { "Type", "coap.type", FT_UINT8, BASE_DEC, VALS(&vals_ttype), 0x30, "COAP Transaction Type", HFILL }},
346             { &hf_coap_opt_count, { "Option Count", "coap.optcount", FT_UINT8, BASE_DEC, NULL, 0x0f, "COAP Option Count", HFILL }},
347             { &hf_coap_code, { "Code", "coap.code", FT_UINT8, BASE_DEC, VALS(&vals_code), 0x0, "COAP Method or Response Code", HFILL }},
348             { &hf_coap_tid, { "Transaction ID", "coap.tid", FT_UINT16, BASE_DEC, NULL, 0x0, "COAP Transaction ID", HFILL }},
349             { &hf_coap_opt_type, { "Option Type", "coap.opt.opt_type", FT_UINT8, BASE_DEC, VALS(&vals_opt_type), 0x0, "COAP Option Type", HFILL }},
350             { &hf_coap_opt_ctype, { "Content-type", "coap.opt.ctype", FT_UINT8, BASE_DEC, VALS(&vals_ctype), 0x0, "COAP Media Type", HFILL }},
351             { &hf_coap_opt_max_age, { "Max-age", "coap.opt.maxage", FT_UINT32, BASE_DEC, NULL, 0x0, "COAP Max-age", HFILL }},
352             { &hf_coap_opt_etag, { "Etag", "coap.opt.etag", FT_BYTES, BASE_NONE, NULL, 0x0, "COAP Etag", HFILL }},
353             { &hf_coap_opt_uri_authority, { "Uri-Authority", "coap.opt.uri_auth", FT_STRING, BASE_NONE, NULL, 0x0, "COAP Uri-Authority", HFILL }},
354             { &hf_coap_opt_location, { "Location", "coap.opt.location", FT_STRING, BASE_NONE, NULL, 0x0, "COAP Location", HFILL }},
355             { &hf_coap_opt_uri_path, { "Uri-Path", "coap.opt.uri_path", FT_STRING, BASE_NONE, NULL, 0x0, "COAP Uri-Path", HFILL }},
356         };
357
358         static gint *ett[] = {
359                 &ett_coap,
360                 &ett_coap_noop,
361                 &ett_coap_ctype,
362                 &ett_coap_max_age,
363                 &ett_coap_uri_scheme,
364                 &ett_coap_etag,
365                 &ett_coap_uri_authority,
366                 &ett_coap_location,
367                 &ett_coap_uri_path,
368                 &ett_coap_payload,
369         };
370
371         module_t *coap_module;
372
373         proto_coap = proto_register_protocol("Constrained Application Protocol", "COAP", "coap");
374         proto_register_field_array(proto_coap, hf, array_length(hf));
375         proto_register_subtree_array(ett, array_length(ett));
376
377         register_dissector("coap", dissect_coap, proto_coap);
378
379         /* Register our configuration options */
380         coap_module = prefs_register_protocol (proto_coap, proto_reg_handoff_coap);
381
382         prefs_register_uint_preference (coap_module, "udp_port",
383                                         "COAP port number",
384                                         "Port number used for COAP traffic",
385                                         10, &global_coap_port_number);
386 }
387
388 void
389 proto_reg_handoff_coap(void)
390 {
391         static gboolean coap_prefs_initialized = FALSE;
392         static dissector_handle_t coap_handle;
393         static guint    coap_port_number;
394
395         if (!coap_prefs_initialized) {
396                 coap_handle = find_dissector("coap");
397                 media_type_dissector_table = find_dissector_table("media_type");
398                 coap_prefs_initialized = TRUE;
399         } else {
400                 dissector_delete("udp.port", coap_port_number, coap_handle);
401                 dissector_delete("tcp.port", coap_port_number, coap_handle);
402         }
403
404         coap_port_number = global_coap_port_number;
405         dissector_add("udp.port", coap_port_number, coap_handle);
406         dissector_add("tcp.port", coap_port_number, coap_handle);
407 }