Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-rtcdc.c
Line
Count
Source
1
/*
2
 * packet-rtcdc.c
3
 * Routines for the RTCWeb Data Channel Protocol dissection
4
 * as specified in
5
 * https://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-03
6
 * and specified in
7
 * https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-08
8
 * We might want to remove the support of
9
 * https://tools.ietf.org/html/draft-jesup-rtcweb-data-protocol-03
10
 * in the future, but I'll leave it in for now.
11
 * Copyright 2012 - 2013, Michael Tuexen <tuexen@wireshark.org>
12
 *
13
 * Wireshark - Network traffic analyzer
14
 * By Gerald Combs <gerald@wireshark.org>
15
 * Copyright 1998 Gerald Combs
16
 *
17
 * SPDX-License-Identifier: GPL-2.0-or-later
18
 */
19
20
#include "config.h"
21
22
#include <epan/packet.h>
23
#include <epan/expert.h>
24
#include <epan/prefs.h>
25
#include "packet-sctp.h"
26
27
void proto_register_rtcdc(void);
28
void proto_reg_handoff_rtcdc(void);
29
30
static dissector_handle_t rtcdc_handle;
31
32
/* Initialize the protocol and registered fields */
33
static int proto_rtcdc;
34
static int hf_message_type;
35
static int hf_channel_type;
36
static int hf_flags;
37
static int hf_flags_reserved;
38
static int hf_unordered_allowed;
39
static int hf_reliability;
40
static int hf_priority;
41
static int hf_label;
42
static int hf_error;
43
static int hf_sid;
44
static int hf_new_channel_type;
45
static int hf_new_reliability;
46
static int hf_new_priority;
47
static int hf_new_label_length;
48
static int hf_new_protocol_length;
49
static int hf_new_label;
50
static int hf_new_protocol;
51
52
/* Initialize the subtree pointers */
53
static int ett_rtcdc;
54
static int ett_flags;
55
56
static expert_field ei_rtcdc_new_reliability_non_zero;
57
static expert_field ei_rtcdc_message_type_unknown;
58
static expert_field ei_rtcdc_inconsistent_label_and_parameter_length;
59
static expert_field ei_rtcdc_message_too_long;
60
static expert_field ei_rtcdc_new_channel_type;
61
62
2
#define DATA_CHANNEL_OPEN_REQUEST     0x00
63
0
#define DATA_CHANNEL_OPEN_RESPONSE    0x01
64
1
#define DATA_CHANNEL_ACK              0x02
65
0
#define DATA_CHANNEL_NEW_OPEN_REQUEST 0x03
66
67
static const value_string message_type_values[] = {
68
    { DATA_CHANNEL_OPEN_REQUEST,     "DATA_CHANNEL_OPEN_REQUEST"  },
69
    { DATA_CHANNEL_OPEN_RESPONSE,    "DATA_CHANNEL_OPEN_RESPONSE" },
70
    { DATA_CHANNEL_ACK,              "DATA_CHANNEL_ACK"           },
71
    { DATA_CHANNEL_NEW_OPEN_REQUEST, "DATA_CHANNEL_OPEN_REQUEST"  },
72
    { 0,                             NULL                         }
73
};
74
75
#define DATA_CHANNEL_RELIABLE                0x00
76
#define DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT 0x01
77
#define DATA_CHANNEL_PARTIAL_RELIABLE_TIMED  0x02
78
79
static const value_string channel_type_values[] = {
80
    { DATA_CHANNEL_RELIABLE,                "DATA_CHANNEL_RELIABLE"                },
81
    { DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT, "DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT" },
82
    { DATA_CHANNEL_PARTIAL_RELIABLE_TIMED,  "DATA_CHANNEL_PARTIAL_RELIABLE_TIMED"  },
83
    { 0,                                    NULL                                   }
84
};
85
86
20
#define MESSAGE_TYPE_LENGTH 1
87
14
#define CHANNEL_TYPE_LENGTH 1
88
12
#define FLAGS_LENGTH        2
89
6
#define RELIABILITY_LENGTH  2
90
4
#define PRIORITY_LENGTH     2
91
92
24
#define MESSAGE_TYPE_OFFSET 0
93
14
#define CHANNEL_TYPE_OFFSET (MESSAGE_TYPE_OFFSET + MESSAGE_TYPE_LENGTH)
94
12
#define FLAGS_OFFSET        (CHANNEL_TYPE_OFFSET + CHANNEL_TYPE_LENGTH)
95
6
#define RELIABILITY_OFFSET  (FLAGS_OFFSET + FLAGS_LENGTH)
96
4
#define PRIORITY_OFFSET     (RELIABILITY_OFFSET + RELIABILITY_LENGTH)
97
2
#define LABEL_OFFSET        (PRIORITY_OFFSET + PRIORITY_LENGTH)
98
99
16
#define DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED_MASK 0x0001
100
16
#define DATA_CHANNEL_FLAG_RESERVED_MASK             0xFFFE
101
102
static void
103
dissect_open_request_message(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *rtcdc_tree, proto_item *rtcdc_item _U_)
104
2
{
105
2
    if (rtcdc_tree) {
106
2
        proto_tree *flags_tree;
107
2
        proto_item *flags_item;
108
109
2
        proto_tree_add_item(rtcdc_tree, hf_channel_type, tvb, CHANNEL_TYPE_OFFSET, CHANNEL_TYPE_LENGTH, ENC_BIG_ENDIAN);
110
2
        flags_item = proto_tree_add_item(rtcdc_tree, hf_flags, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
111
2
        flags_tree = proto_item_add_subtree(flags_item, ett_flags);
112
2
        proto_tree_add_item(flags_tree, hf_flags_reserved, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
113
2
        proto_tree_add_item(flags_tree, hf_unordered_allowed, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
114
2
        proto_tree_add_item(rtcdc_tree, hf_reliability, tvb, RELIABILITY_OFFSET, RELIABILITY_LENGTH, ENC_BIG_ENDIAN);
115
2
        proto_tree_add_item(rtcdc_tree, hf_priority, tvb, PRIORITY_OFFSET, PRIORITY_LENGTH, ENC_BIG_ENDIAN);
116
2
        proto_tree_add_item(rtcdc_tree, hf_label, tvb, LABEL_OFFSET, -1, ENC_ASCII);
117
2
    }
118
2
    return;
119
2
}
120
121
0
#define ERROR_LENGTH                 1
122
0
#define SID_LENGTH                   2
123
0
#define DATA_CHANNEL_RESPONSE_LENGTH (MESSAGE_TYPE_LENGTH + ERROR_LENGTH + FLAGS_LENGTH + SID_LENGTH)
124
125
0
#define ERROR_OFFSET                 (MESSAGE_TYPE_OFFSET + MESSAGE_TYPE_LENGTH)
126
0
#define SID_OFFSET                   (FLAGS_OFFSET + FLAGS_LENGTH)
127
128
static void
129
dissect_open_response_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtcdc_tree, proto_item *rtcdc_item)
130
0
{
131
0
    if (tvb_reported_length(tvb) > DATA_CHANNEL_RESPONSE_LENGTH) {
132
0
        expert_add_info(pinfo, rtcdc_item, &ei_rtcdc_message_too_long);
133
0
    }
134
0
    if (rtcdc_tree) {
135
0
        proto_tree_add_item(rtcdc_tree, hf_error, tvb, ERROR_OFFSET, ERROR_LENGTH, ENC_BIG_ENDIAN);
136
0
        proto_tree_add_item(rtcdc_tree, hf_flags, tvb, FLAGS_OFFSET, FLAGS_LENGTH, ENC_BIG_ENDIAN);
137
0
        proto_tree_add_item(rtcdc_tree, hf_sid, tvb, SID_OFFSET, SID_LENGTH, ENC_BIG_ENDIAN);
138
0
    }
139
0
    return;
140
0
}
141
142
1
#define DATA_CHANNEL_ACK_LENGTH MESSAGE_TYPE_LENGTH
143
144
static void
145
dissect_open_ack_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtcdc_tree _U_, proto_item *rtcdc_item)
146
1
{
147
1
    if (tvb_reported_length(tvb) > DATA_CHANNEL_ACK_LENGTH) {
148
1
        expert_add_info(pinfo, rtcdc_item, &ei_rtcdc_message_too_long);
149
1
    }
150
1
    return;
151
1
}
152
153
#define NEW_DATA_CHANNEL_RELIABLE                          0x00
154
#define NEW_DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT           0x01
155
#define NEW_DATA_CHANNEL_PARTIAL_RELIABLE_TIMED            0x02
156
#define NEW_DATA_CHANNEL_RELIABLE_UNORDERED                0x80
157
#define NEW_DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED 0x81
158
#define NEW_DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED  0x82
159
160
static const value_string new_channel_type_values[] = {
161
    { NEW_DATA_CHANNEL_RELIABLE,                          "DATA_CHANNEL_RELIABLE"                          },
162
    { NEW_DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT,           "DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT"           },
163
    { NEW_DATA_CHANNEL_PARTIAL_RELIABLE_TIMED,            "DATA_CHANNEL_PARTIAL_RELIABLE_TIMED"            },
164
    { NEW_DATA_CHANNEL_RELIABLE_UNORDERED,                "DATA_CHANNEL_RELIABLE_UNORDERED"                },
165
    { NEW_DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED, "DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED" },
166
    { NEW_DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED,  "DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED"  },
167
    { 0,                                                  NULL                                             }
168
};
169
170
0
#define NEW_MESSAGE_TYPE_LENGTH    1
171
0
#define NEW_CHANNEL_TYPE_LENGTH    1
172
0
#define NEW_PRIORITY_LENGTH        2
173
0
#define NEW_RELIABILITY_LENGTH     4
174
0
#define NEW_LABEL_LENGTH_LENGTH    2
175
0
#define NEW_PROTOCOL_LENGTH_LENGTH 2
176
0
#define NEW_OPEN_REQUEST_HEADER_LENGTH (unsigned)(NEW_MESSAGE_TYPE_LENGTH + \
177
0
                                               NEW_CHANNEL_TYPE_LENGTH + \
178
0
                                               NEW_PRIORITY_LENGTH +    \
179
0
                                               NEW_RELIABILITY_LENGTH + \
180
0
                                               NEW_LABEL_LENGTH_LENGTH + \
181
0
                                               NEW_PROTOCOL_LENGTH_LENGTH)
182
183
0
#define NEW_MESSAGE_TYPE_OFFSET    0
184
0
#define NEW_CHANNEL_TYPE_OFFSET    (NEW_MESSAGE_TYPE_OFFSET + NEW_MESSAGE_TYPE_LENGTH)
185
0
#define NEW_PRIORITY_OFFSET        (NEW_CHANNEL_TYPE_OFFSET + NEW_CHANNEL_TYPE_LENGTH)
186
0
#define NEW_RELIABILITY_OFFSET     (NEW_PRIORITY_OFFSET + NEW_PRIORITY_LENGTH)
187
0
#define NEW_LABEL_LENGTH_OFFSET    (NEW_RELIABILITY_OFFSET + NEW_RELIABILITY_LENGTH)
188
0
#define NEW_PROTOCOL_LENGTH_OFFSET (NEW_LABEL_LENGTH_OFFSET + NEW_LABEL_LENGTH_LENGTH)
189
0
#define NEW_LABEL_OFFSET           (NEW_PROTOCOL_LENGTH_OFFSET + NEW_PROTOCOL_LENGTH_LENGTH)
190
191
static void
192
dissect_new_open_request_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtcdc_tree, proto_item *rtcdc_item)
193
0
{
194
0
    uint8_t channel_type;
195
0
    uint32_t reliability;
196
0
    uint16_t label_length;
197
0
    uint16_t protocol_length;
198
199
0
    channel_type = tvb_get_uint8(tvb, NEW_CHANNEL_TYPE_OFFSET);
200
0
    if ((channel_type & 0x7f) > 0x02) {
201
0
        expert_add_info(pinfo, rtcdc_item, &ei_rtcdc_new_channel_type);
202
0
    }
203
0
    reliability = tvb_get_ntohl(tvb, NEW_RELIABILITY_OFFSET);
204
0
    if ((reliability > 0) && ((channel_type & 0x7f) == 0x00)) {
205
0
        expert_add_info(pinfo, rtcdc_item, &ei_rtcdc_new_reliability_non_zero);
206
0
    }
207
0
    label_length = tvb_get_ntohs(tvb, NEW_LABEL_LENGTH_OFFSET);
208
0
    protocol_length = tvb_get_ntohs(tvb, NEW_PROTOCOL_LENGTH_OFFSET);
209
0
    if (NEW_OPEN_REQUEST_HEADER_LENGTH + (unsigned)label_length + (unsigned)protocol_length != tvb_reported_length(tvb)) {
210
0
        expert_add_info(pinfo, rtcdc_item, &ei_rtcdc_inconsistent_label_and_parameter_length);
211
0
    }
212
0
    if (rtcdc_tree) {
213
0
        proto_tree_add_item(rtcdc_tree, hf_new_channel_type, tvb, NEW_CHANNEL_TYPE_OFFSET, NEW_CHANNEL_TYPE_LENGTH, ENC_BIG_ENDIAN);
214
0
        proto_tree_add_item(rtcdc_tree, hf_new_priority, tvb, NEW_PRIORITY_OFFSET, NEW_PRIORITY_LENGTH, ENC_BIG_ENDIAN);
215
0
        proto_tree_add_item(rtcdc_tree, hf_new_reliability, tvb, NEW_RELIABILITY_OFFSET, NEW_RELIABILITY_LENGTH, ENC_BIG_ENDIAN);
216
0
        proto_tree_add_item(rtcdc_tree, hf_new_label_length, tvb, NEW_LABEL_LENGTH_OFFSET, NEW_LABEL_LENGTH_LENGTH, ENC_BIG_ENDIAN);
217
0
        proto_tree_add_item(rtcdc_tree, hf_new_protocol_length, tvb, NEW_PROTOCOL_LENGTH_OFFSET, NEW_PROTOCOL_LENGTH_LENGTH, ENC_BIG_ENDIAN);
218
0
        proto_tree_add_item(rtcdc_tree, hf_new_label, tvb, NEW_LABEL_OFFSET, label_length, ENC_ASCII);
219
0
        proto_tree_add_item(rtcdc_tree, hf_new_protocol, tvb, NEW_LABEL_OFFSET + label_length, protocol_length, ENC_ASCII);
220
0
    }
221
0
    return;
222
0
}
223
224
static int
225
dissect_rtcdc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
226
5
{
227
5
    proto_item *rtcdc_item, *msg_item;
228
5
    proto_tree *rtcdc_tree;
229
5
    uint8_t     message_type;
230
231
5
    message_type  = tvb_get_uint8(tvb, MESSAGE_TYPE_OFFSET);
232
233
5
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTCDC");
234
5
    col_add_fstr(pinfo->cinfo, COL_INFO, "%s ", val_to_str_const(message_type, message_type_values, "reserved"));
235
5
    rtcdc_item = proto_tree_add_item(tree, proto_rtcdc, tvb, 0, -1, ENC_NA);
236
5
    rtcdc_tree = proto_item_add_subtree(rtcdc_item, ett_rtcdc);
237
5
    msg_item   = proto_tree_add_item(rtcdc_tree, hf_message_type, tvb, MESSAGE_TYPE_OFFSET, MESSAGE_TYPE_LENGTH, ENC_BIG_ENDIAN);
238
239
5
    switch (message_type) {
240
2
        case DATA_CHANNEL_OPEN_REQUEST:
241
2
            dissect_open_request_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
242
2
            break;
243
0
        case DATA_CHANNEL_OPEN_RESPONSE:
244
0
            dissect_open_response_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
245
0
            break;
246
1
        case DATA_CHANNEL_ACK:
247
1
            dissect_open_ack_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
248
1
            break;
249
0
        case DATA_CHANNEL_NEW_OPEN_REQUEST:
250
0
            dissect_new_open_request_message(tvb, pinfo, rtcdc_tree, rtcdc_item);
251
0
            break;
252
2
        default:
253
2
            expert_add_info(pinfo, msg_item, &ei_rtcdc_message_type_unknown);
254
2
            break;
255
5
    }
256
4
    return tvb_captured_length(tvb);
257
5
}
258
259
void
260
proto_register_rtcdc(void)
261
16
{
262
16
    expert_module_t *expert_rtcdc;
263
264
16
    static hf_register_info hf[] = {
265
16
        { &hf_message_type,
266
16
          { "Message type", "rtcdc.message_type",
267
16
            FT_UINT8, BASE_DEC, VALS(message_type_values), 0x0,
268
16
            NULL, HFILL }
269
16
        },
270
16
        { &hf_channel_type,
271
16
          { "Channel type", "rtcdc.channel_type",
272
16
            FT_UINT8, BASE_DEC, VALS(channel_type_values), 0x0,
273
16
            NULL, HFILL }
274
16
        },
275
16
        { &hf_flags,
276
16
          { "Flags", "rtcdc.flags",
277
16
            FT_UINT16, BASE_HEX, NULL, 0x0,
278
16
            NULL, HFILL }
279
16
        },
280
16
        { &hf_flags_reserved,
281
16
          { "Reserved", "rtcdc.flags_reserved",
282
16
            FT_UINT16, BASE_HEX, NULL, DATA_CHANNEL_FLAG_RESERVED_MASK,
283
16
            NULL, HFILL }
284
16
        },
285
16
        { &hf_unordered_allowed,
286
16
          { "Unordered allowed", "rtcdc.flags_unordered_allowed",
287
16
            FT_BOOLEAN, 16, NULL, DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED_MASK,
288
16
            NULL, HFILL }
289
16
        },
290
16
        { &hf_reliability,
291
16
          { "Reliability parameter", "rtcdc.reliability_parameter",
292
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
293
16
            NULL, HFILL }
294
16
        },
295
16
        { &hf_priority,
296
16
          { "Priority", "rtcdc.priority",
297
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
298
16
            NULL, HFILL }
299
16
        },
300
16
        { &hf_label,
301
16
          { "Label", "rtcdc.label",
302
16
            FT_STRING, BASE_NONE, NULL, 0x0,
303
16
            NULL, HFILL }
304
16
        },
305
16
        { &hf_error,
306
16
          { "Error", "rtcdc.error",
307
16
            FT_UINT8, BASE_DEC, NULL, 0x0,
308
16
            NULL, HFILL }
309
16
        },
310
16
        { &hf_sid,
311
16
          { "Reverse stream identifier", "rtcdc.reverse_stream_id",
312
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
313
16
            NULL, HFILL }
314
16
        },
315
16
        { &hf_new_channel_type,
316
16
          { "Channel type", "rtcdc.channel_type",
317
16
            FT_UINT8, BASE_DEC, VALS(new_channel_type_values), 0x0,
318
16
            NULL, HFILL }
319
16
        },
320
16
        { &hf_new_reliability,
321
16
          { "Reliability parameter", "rtcdc.reliability_parameter",
322
16
            FT_UINT32, BASE_DEC, NULL, 0x0,
323
16
            NULL, HFILL }
324
16
        },
325
16
        { &hf_new_priority,
326
16
          { "Priority", "rtcdc.priority",
327
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
328
16
            NULL, HFILL }
329
16
        },
330
16
        { &hf_new_label_length,
331
16
          { "Label length", "rtcdc.label_length",
332
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
333
16
            NULL, HFILL }
334
16
        },
335
16
        { &hf_new_protocol_length,
336
16
          { "Protocol length", "rtcdc.protocol_length",
337
16
            FT_UINT16, BASE_DEC, NULL, 0x0,
338
16
            NULL, HFILL }
339
16
        },
340
16
        { &hf_new_label,
341
16
          { "Label", "rtcdc.label",
342
16
            FT_STRING, BASE_NONE, NULL, 0x0,
343
16
            NULL, HFILL }
344
16
        },
345
16
        { &hf_new_protocol,
346
16
          { "Protocol", "rtcdc.protocol",
347
16
            FT_STRING, BASE_NONE, NULL, 0x0,
348
16
            NULL, HFILL }
349
16
        }
350
16
    };
351
16
    static int *ett[] = {
352
16
        &ett_rtcdc,
353
16
        &ett_flags
354
16
    };
355
356
16
    static ei_register_info ei[] = {
357
16
        { &ei_rtcdc_message_too_long, { "rtcdc.message_too_long", PI_MALFORMED, PI_ERROR, "Message too long", EXPFILL }},
358
16
        { &ei_rtcdc_new_channel_type, { "rtcdc.channel_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown channel type", EXPFILL }},
359
16
        { &ei_rtcdc_new_reliability_non_zero, { "rtcdc.reliability_parameter.non_zero", PI_PROTOCOL, PI_WARN, "Reliability parameter non zero for reliable channel", EXPFILL }},
360
16
        { &ei_rtcdc_inconsistent_label_and_parameter_length, { "rtcdc.inconsistent_label_and_parameter_length", PI_MALFORMED, PI_ERROR, "Inconsistent label and parameter length", EXPFILL }},
361
16
        { &ei_rtcdc_message_type_unknown, { "rtcdc.message_type.unknown", PI_PROTOCOL, PI_WARN, "Unknown message type", EXPFILL }},
362
16
    };
363
364
16
    proto_rtcdc = proto_register_protocol("WebRTC Datachannel Protocol", "RTCDC", "rtcdc");
365
16
    proto_register_field_array(proto_rtcdc, hf, array_length(hf));
366
16
    proto_register_subtree_array(ett, array_length(ett));
367
16
    expert_rtcdc = expert_register_protocol(proto_rtcdc);
368
16
    expert_register_field_array(expert_rtcdc, ei, array_length(ei));
369
    /* rtcdc_module = prefs_register_protocol(proto_rtcdc, NULL); */
370
371
16
    rtcdc_handle = register_dissector("rtcdc", dissect_rtcdc, proto_rtcdc);
372
16
}
373
374
void
375
proto_reg_handoff_rtcdc(void)
376
16
{
377
16
    dissector_add_uint_with_preference("sctp.ppi", WEBRTC_DCEP_PROTOCOL_ID, rtcdc_handle);
378
16
}
379
380
/*
381
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
382
 *
383
 * Local variables:
384
 * c-basic-offset: 4
385
 * tab-width: 8
386
 * indent-tabs-mode: nil
387
 * End:
388
 *
389
 * vi: set shiftwidth=4 tabstop=8 expandtab:
390
 * :indentSize=4:tabSize=8:noTabs=true:
391
 */