aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-sstp.c
blob: 3c1e27a89e9f82c22f5f02441179d4fb6131cd06 (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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
/* packet-sstp.c
 * routines for sstp packet dissasembly
 * - MS-SSTP:
 *
 *    https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sstp
 *
 * Created as part of a semester project at the University of Applied Sciences Hagenberg
 * (https://www.fh-ooe.at/en/hagenberg-campus/)
 *
 * Copyright (c) 2013:
 *   Hofer Manuel (manuel@mnlhfr.at)
 *   Nemeth Franz
 *   Scheipner Alexander
 *   Stiftinger Thomas
 *   Werner Sebastian
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */


#include "config.h"

#include <epan/packet.h>
#include "packet-tcp.h"

void proto_register_sstp(void);
void proto_reg_handoff_sstp(void);

#define SSTP_BITMASK_MAJORVERSION 0xF0
#define SSTP_BITMASK_MINORVERSION 0x0F
#define SSTP_BITMASK_CONTROLFLAG 0x01
#define SSTP_BITMASK_LENGTH_RESERVED 0xF000
#define SSTP_BITMASK_LENGTH_LENGTH 0x0FFF
#define SSTP_CERT_HASH_PROTOCOL_SHA1 0x01
#define SSTP_CERT_HASH_PROTOCOL_SHA256 0x02
#define SSTP_ENCAPSULATED_PPP 0x0001

/* bytewise offsets inside the packet buffer */
#define SSTP_OFFSET_ATTRIBUTES    8
#define SSTP_OFFSET_DATA          4
#define SSTP_OFFSET_RESERVED      1
#define SSTP_OFFSET_ISCONTROL     1
#define SSTP_OFFSET_LENGTH        2
#define SSTP_OFFSET_MAJORVERSION  0
#define SSTP_OFFSET_MINORVERSION  0
#define SSTP_OFFSET_MSGTYPE       4
#define SSTP_OFFSET_NUMATTRIB     6

/* fieldsize in byte */
#define SSTP_FSIZE_ATTRIBUTE              4
#define SSTP_FSIZE_ATTRIB_ID              1
#define SSTP_FSIZE_ATTRIB_LENGTH          2
#define SSTP_FSIZE_ATTRIB_RESERVED        1
#define SSTP_FSIZE_CERT_HASH_SHA1         20
#define SSTP_FSIZE_CERT_HASH_SHA256       32
#define SSTP_FSIZE_COMPOUND_MAC_SHA1      20
#define SSTP_FSIZE_COMPOUND_MAC_SHA256    32
#define SSTP_FSIZE_ENCAPSULATED_PROTOCOL  2
#define SSTP_FSIZE_HASH_PROTOCOL          1
#define SSTP_FSIZE_HASH_PROTOCOL_BITMASK  1
#define SSTP_FSIZE_ISCONTROL              1
#define SSTP_FSIZE_LENGTH                 2
#define SSTP_FSIZE_MAJORVERSION           1
#define SSTP_FSIZE_MINORVERSION           1
#define SSTP_FSIZE_MSGTYPE                2
#define SSTP_FSIZE_NONCE                  32
#define SSTP_FSIZE_NUMATTRIB              2
#define SSTP_FSIZE_PADDING_SHA1           12
#define SSTP_FSIZE_RESERVED               1
#define SSTP_FSIZE_RESERVED2              3
#define SSTP_FSIZE_STATUS                 4

/* Message types */
#define SSTP_MSG_CALL_ABORT 0x005
#define SSTP_MSG_CALL_CONNECTED 0x004
#define SSTP_MSG_CALL_CONNECT_ACK 0x002
#define SSTP_MSG_CALL_CONNECT_NAK 0x003
#define SSTP_MSG_CALL_CONNECT_REQUEST 0x001
#define SSTP_MSG_CALL_DISCONNECT 0x006
#define SSTP_MSG_CALL_DISCONNECT_ACK 0x007
#define SSTP_MSG_ECHO_REQUEST 0x008
#define SSTP_MSG_ECHO_RESPONSE 0x009

/* Attribute Types */
#define SSTP_ATTRIB_CRYPTO_BINDING 3
#define SSTP_ATTRIB_CRYPTO_BINDING_REQ 4
#define SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID 1
#define SSTP_ATTRIB_NO_ERROR 0
#define SSTP_ATTRIB_STATUS_INFO 2

/* Status Types */
#define SSTP_ATTRIB_STATUS_ATTRIB_NOT_SUPPORTED_IN_MSG 0x000009
#define SSTP_ATTRIB_STATUS_DUPLICATE_ATTRIBUTE 0x000001
#define SSTP_ATTRIB_STATUS_INVALID_ATTRIB_VALUE_LENGTH 0x000003
#define SSTP_ATTRIB_STATUS_INVALID_FRAME_RECEIVED 0x000007
#define SSTP_ATTRIB_STATUS_NEGOTIATION_TIMEOUT 0x000008
#define SSTP_ATTRIB_STATUS_NO_ERROR 0x000000
#define SSTP_ATTRIB_STATUS_REQUIRED_ATTRIBUTE_MISSING 0x00000a
#define SSTP_ATTRIB_STATUS_RETRY_COUNT_EXCEEDED 0x000006
#define SSTP_ATTRIB_STATUS_STATUS_INFO_NOT_SUPPORTED_IN_MSG 0x00000b
#define SSTP_ATTRIB_STATUS_UNACCEPTED_FRAME_RECEIVED 0x000005
#define SSTP_ATTRIB_STATUS_UNRECOGNIZED_ATTRIBUTE 0x000002
#define SSTP_ATTRIB_STATUS_VALUE_NOT_SUPPORTED 0x000004

static dissector_handle_t ppp_hdlc_handle = NULL;
static gint ett_sstp;
static gint ett_sstp_attribute;
static gint ett_sstp_version;
static gint hf_sstp_attrib_id;
static gint hf_sstp_attrib_length;
static gint hf_sstp_attrib_length_reserved;
static gint hf_sstp_attrib_reserved;
static gint hf_sstp_attrib_value;
static gint hf_sstp_cert_hash;
static gint hf_sstp_compound_mac;
static gint hf_sstp_control_flag;
static gint hf_sstp_data_unknown;
static gint hf_sstp_ecapsulated_protocol;
static gint hf_sstp_hash_protocol;
static gint hf_sstp_length;
static gint hf_sstp_major;
static gint hf_sstp_messagetype;
static gint hf_sstp_minor;
static gint hf_sstp_nonce;
static gint hf_sstp_numattrib;
static gint hf_sstp_padding;
static gint hf_sstp_reserved;
static gint hf_sstp_status;
static gint proto_sstp;

static const value_string sstp_messagetypes[] = {
  {SSTP_MSG_CALL_CONNECT_REQUEST, "SSTP_MSG_CALL_CONNECT_REQUEST"},
  {SSTP_MSG_CALL_CONNECT_ACK, "SSTP_MSG_CALL_CONNECT_ACK"},
  {SSTP_MSG_CALL_CONNECT_NAK, "SSTP_MSG_CALL_CONNECT_NAK"},
  {SSTP_MSG_CALL_CONNECTED, "SSTP_MSG_CALL_CONNECTED"},
  {SSTP_MSG_CALL_ABORT, "SSTP_MSG_CALL_ABORT"},
  {SSTP_MSG_CALL_DISCONNECT, "SSTP_MSG_CALL_DISCONNECT"},
  {SSTP_MSG_CALL_DISCONNECT_ACK, "SSTP_MSG_CALL_DISCONNECT_ACK"},
  {SSTP_MSG_ECHO_REQUEST, "SSTP_MSG_ECHO_REQUEST"},
  {SSTP_MSG_ECHO_RESPONSE, "SSTP_MSG_ECHO_RESPONSE"},
  {0, NULL}
};

static const value_string sstp_attributes[] = {
  {SSTP_ATTRIB_NO_ERROR, "SSTP_ATTRIB_NO_ERROR"},
  {SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID, "SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID"},
  {SSTP_ATTRIB_STATUS_INFO, "SSTP_ATTRIB_STATUS_INFO"},
  {SSTP_ATTRIB_CRYPTO_BINDING, "SSTP_ATTRIB_CRYPTO_BINDING"},
  {SSTP_ATTRIB_CRYPTO_BINDING_REQ, "SSTP_ATTRIB_CRYPTO_BINDING_REQ"},
  {0, NULL}
};

static const value_string encapsulated_protocols[] = {
  {SSTP_ENCAPSULATED_PPP, "PPP"},
  {0, NULL}
};

static const value_string hash_protocols[] = {
  {SSTP_CERT_HASH_PROTOCOL_SHA1, "SHA1"},
  {SSTP_CERT_HASH_PROTOCOL_SHA256, "SHA256"},
  {0, NULL}
};

static const value_string attrib_status[] = {
  {SSTP_ATTRIB_STATUS_NO_ERROR, "SSTP_ATTRIB_STATUS_NO_ERROR"},
  {SSTP_ATTRIB_STATUS_DUPLICATE_ATTRIBUTE, "SSTP_ATTRIB_STATUS_DUPLICATE_ATTRIBUTE"},
  {SSTP_ATTRIB_STATUS_UNRECOGNIZED_ATTRIBUTE, "SSTP_ATTRIB_STATUS_UNRECOGNIZED_ATTRIBUTE"},
  {SSTP_ATTRIB_STATUS_INVALID_ATTRIB_VALUE_LENGTH , "SSTP_ATTRIB_STATUS_INVALID_ATTRIB_VALUE_LENGTH"},
  {SSTP_ATTRIB_STATUS_VALUE_NOT_SUPPORTED, "SSTP_ATTRIB_STATUS_VALUE_NOT_SUPPORTED"},
  {SSTP_ATTRIB_STATUS_UNACCEPTED_FRAME_RECEIVED, "SSTP_ATTRIB_STATUS_UNACCEPTED_FRAME_RECEIVED"},
  {SSTP_ATTRIB_STATUS_RETRY_COUNT_EXCEEDED, "SSTP_ATTRIB_STATUS_RETRY_COUNT_EXCEEDED"},
  {SSTP_ATTRIB_STATUS_INVALID_FRAME_RECEIVED, "SSTP_ATTRIB_STATUS_INVALID_FRAME_RECEIVED"},
  {SSTP_ATTRIB_STATUS_NEGOTIATION_TIMEOUT, "SSTP_ATTRIB_STATUS_NEGOTIATION_TIMEOUT"},
  {SSTP_ATTRIB_STATUS_ATTRIB_NOT_SUPPORTED_IN_MSG, "SSTP_ATTRIB_STATUS_ATTRIB_NOT_SUPPORTED_IN_MSG"},
  {SSTP_ATTRIB_STATUS_REQUIRED_ATTRIBUTE_MISSING, "SSTP_ATTRIB_STATUS_REQUIRED_ATTRIBUTE_MISSING"},
  {SSTP_ATTRIB_STATUS_STATUS_INFO_NOT_SUPPORTED_IN_MSG, "SSTP_ATTRIB_STATUS_STATUS_INFO_NOT_SUPPORTED_IN_MSG"},
  {0, NULL}
};

static int
dissect_sstp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
  guint16 sstp_control_flag;
  guint32 offset = 0;
  guint8 sstp_major;
  guint8 sstp_minor;
  proto_item *ti;
  proto_tree *sstp_tree;
  proto_tree *sstp_tree_attribute;
  proto_tree *sstp_tree_version;
  guint16 sstp_numattrib;
  tvbuff_t *tvb_next;

  col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSTP");
  /* Clear out stuff in the info column */
  col_clear(pinfo->cinfo, COL_INFO);

  ti = proto_tree_add_item(tree, proto_sstp, tvb, 0, -1, ENC_NA);
  sstp_tree = proto_item_add_subtree(ti, ett_sstp);

  sstp_control_flag = tvb_get_guint8(tvb, SSTP_OFFSET_ISCONTROL) & SSTP_BITMASK_CONTROLFLAG;
  sstp_minor = (tvb_get_guint8(tvb, SSTP_OFFSET_MINORVERSION) & SSTP_BITMASK_MINORVERSION); /* leftmost 4 bit */
  sstp_major = (tvb_get_guint8(tvb, SSTP_OFFSET_MAJORVERSION) >> 4); /* rightmost 4 bit */
  col_append_fstr(pinfo->cinfo, COL_INFO, "SSTP-%u.%u ", sstp_major, sstp_minor);

  sstp_tree_version = proto_tree_add_subtree_format(sstp_tree, tvb, offset, SSTP_FSIZE_MAJORVERSION, ett_sstp_version,
      NULL, "Version %d.%d", sstp_major, sstp_minor);
  proto_tree_add_item(sstp_tree_version, hf_sstp_major, tvb, SSTP_OFFSET_MAJORVERSION, SSTP_FSIZE_MAJORVERSION, ENC_BIG_ENDIAN);
  proto_tree_add_item(sstp_tree_version, hf_sstp_minor, tvb, SSTP_OFFSET_MINORVERSION, SSTP_FSIZE_MINORVERSION, ENC_BIG_ENDIAN);
  proto_tree_add_item(sstp_tree, hf_sstp_reserved, tvb,      SSTP_OFFSET_RESERVED, SSTP_FSIZE_RESERVED, ENC_NA);
  proto_tree_add_item(sstp_tree, hf_sstp_control_flag, tvb,  SSTP_OFFSET_ISCONTROL, SSTP_FSIZE_ISCONTROL, ENC_BIG_ENDIAN);
  proto_tree_add_item(sstp_tree, hf_sstp_length, tvb,        SSTP_OFFSET_LENGTH, SSTP_FSIZE_LENGTH, ENC_BIG_ENDIAN);

  /* check wether we got a control or data packet */
  if (sstp_control_flag) {
    guint16 sstp_messagetype = tvb_get_guint16(tvb, SSTP_OFFSET_MSGTYPE, ENC_BIG_ENDIAN);

    col_append_fstr(pinfo->cinfo, COL_INFO, "Type: CONTROL, %s; ", val_to_str_const(sstp_messagetype, sstp_messagetypes, "Unknown Messagetype"));
    proto_tree_add_item(sstp_tree, hf_sstp_messagetype, tvb,  SSTP_OFFSET_MSGTYPE, SSTP_FSIZE_MSGTYPE, ENC_BIG_ENDIAN);
    proto_tree_add_item(sstp_tree, hf_sstp_numattrib, tvb,    SSTP_OFFSET_NUMATTRIB, SSTP_FSIZE_NUMATTRIB, ENC_BIG_ENDIAN);
    sstp_numattrib = tvb_get_ntohs(tvb, SSTP_OFFSET_NUMATTRIB);

    /* display attributes */
    if (sstp_numattrib > 0) {
      guint16 attrib_length = 0;
      guint8 attrib_id = 0;
      guint8 hashproto = 0;
      offset = SSTP_OFFSET_ATTRIBUTES;

      for(;sstp_numattrib > 0; sstp_numattrib--) {
        /* read attribute id and create subtree for attribute */
        attrib_id = tvb_get_guint8(tvb, offset+1);
        sstp_tree_attribute = proto_tree_add_subtree_format(sstp_tree, tvb, offset, SSTP_FSIZE_ATTRIB_RESERVED, ett_sstp_attribute,
            NULL, "Attribute %s", val_to_str_const(attrib_id, sstp_attributes, "Unknown Attribute"));
        proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_reserved, tvb, offset, SSTP_FSIZE_ATTRIB_RESERVED, ENC_BIG_ENDIAN);
        offset++;
        proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_id, tvb, offset, SSTP_FSIZE_ATTRIB_ID, ENC_BIG_ENDIAN);
        offset++;
        proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_length_reserved, tvb, offset, SSTP_FSIZE_ATTRIB_LENGTH, ENC_BIG_ENDIAN);
        proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_length, tvb, offset, SSTP_FSIZE_ATTRIB_LENGTH, ENC_BIG_ENDIAN);

        /* get length of attribute value */
        attrib_length = (tvb_get_ntohs(tvb, offset) & SSTP_BITMASK_LENGTH_LENGTH);

        /* if this attribute follows the specification, length should at least be 4 */
        if (attrib_length >= 4) {
          /* length field also contains the previously processed 4 bytes */
          attrib_length -= 4;
        }
        offset += 2;

        /* attributes that need special treatment... */
        switch(attrib_id) {

          case SSTP_ATTRIB_ENCAPSULATED_PROTOCOL_ID:
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_ecapsulated_protocol, tvb, offset, SSTP_FSIZE_ENCAPSULATED_PROTOCOL, ENC_BIG_ENDIAN);
            offset += SSTP_FSIZE_ENCAPSULATED_PROTOCOL;
          break;

          case SSTP_ATTRIB_STATUS_INFO:
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_reserved, tvb, offset, SSTP_FSIZE_RESERVED2, ENC_NA);
            offset += SSTP_FSIZE_RESERVED2;
            attrib_length -= SSTP_FSIZE_RESERVED2;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_id, tvb, offset, SSTP_FSIZE_ATTRIB_ID, ENC_BIG_ENDIAN);
            offset += SSTP_FSIZE_ATTRIB_ID;
            attrib_length -= SSTP_FSIZE_ATTRIB_ID;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_status, tvb, offset, SSTP_FSIZE_STATUS, ENC_BIG_ENDIAN);
            offset += SSTP_FSIZE_STATUS;
            attrib_length -= SSTP_FSIZE_STATUS;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_attrib_value, tvb, offset, attrib_length, ENC_NA);
            offset += attrib_length;
          break;

          case SSTP_ATTRIB_CRYPTO_BINDING:
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_reserved, tvb, offset, SSTP_FSIZE_RESERVED2, ENC_NA);
            offset += SSTP_FSIZE_RESERVED2;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_hash_protocol, tvb, offset, SSTP_FSIZE_HASH_PROTOCOL, ENC_BIG_ENDIAN);
            hashproto = tvb_get_guint8(tvb, offset);
            offset += SSTP_FSIZE_HASH_PROTOCOL;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_nonce, tvb, offset, SSTP_FSIZE_NONCE, ENC_NA);
            offset += SSTP_FSIZE_NONCE;

            if (hashproto == SSTP_CERT_HASH_PROTOCOL_SHA1) {
              proto_tree_add_item(sstp_tree_attribute, hf_sstp_cert_hash, tvb, offset, SSTP_FSIZE_CERT_HASH_SHA1, ENC_NA);
              offset += SSTP_FSIZE_CERT_HASH_SHA1;
              proto_tree_add_item(sstp_tree_attribute, hf_sstp_padding, tvb, offset, SSTP_FSIZE_PADDING_SHA1, ENC_NA);
              offset += SSTP_FSIZE_PADDING_SHA1;
              proto_tree_add_item(sstp_tree_attribute, hf_sstp_compound_mac, tvb, offset, SSTP_FSIZE_COMPOUND_MAC_SHA1, ENC_NA);
              offset += SSTP_FSIZE_COMPOUND_MAC_SHA1;
              proto_tree_add_item(sstp_tree_attribute, hf_sstp_padding, tvb, offset, SSTP_FSIZE_PADDING_SHA1, ENC_NA);
              offset += SSTP_FSIZE_PADDING_SHA1;
            }

            if (hashproto == SSTP_CERT_HASH_PROTOCOL_SHA256) {
              proto_tree_add_item(sstp_tree_attribute, hf_sstp_cert_hash, tvb, offset, SSTP_FSIZE_CERT_HASH_SHA256, ENC_NA);
              offset += SSTP_FSIZE_CERT_HASH_SHA256;
            }
          break;

          case SSTP_ATTRIB_CRYPTO_BINDING_REQ:
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_reserved, tvb, offset, SSTP_FSIZE_RESERVED2, ENC_NA);
            offset += SSTP_FSIZE_RESERVED2;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_hash_protocol, tvb, offset, SSTP_FSIZE_HASH_PROTOCOL, ENC_BIG_ENDIAN);
            offset += SSTP_FSIZE_HASH_PROTOCOL;
            proto_tree_add_item(sstp_tree_attribute, hf_sstp_nonce, tvb, offset, SSTP_FSIZE_NONCE, ENC_NA);
            offset += SSTP_FSIZE_NONCE;
          break;
        }
      }
    }

    /* While testing with different dumps, i noticed data in the buffer i couldnt find any documentation about */
    if (tvb_reported_length_remaining(tvb, offset) > 0) {
      proto_tree_add_item(sstp_tree, hf_sstp_data_unknown, tvb, offset, -1, ENC_NA);
    }

  } else {
    col_append_fstr(pinfo->cinfo, COL_INFO, "Type: DATA; ");
    /* our work here is done, since sstp encapsulates ppp, we hand the remaining buffer
       over to the ppp dissector for further analysis */
    tvb_next = tvb_new_subset_remaining(tvb, SSTP_OFFSET_DATA);
    call_dissector(ppp_hdlc_handle, tvb_next, pinfo, tree);
  }

  return tvb_captured_length(tvb);
}

static guint
get_sstp_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
  return tvb_get_ntohs(tvb, offset+SSTP_OFFSET_LENGTH);
}

static int
dissect_sstp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
  tcp_dissect_pdus(tvb, pinfo, tree, TRUE, SSTP_OFFSET_LENGTH+SSTP_FSIZE_LENGTH, get_sstp_pdu_len, dissect_sstp_pdu, data);
  return tvb_captured_length(tvb);
}

void
proto_register_sstp(void)
{
  /* Setting up header data structure */
  static hf_register_info hf[] = {
    /* sstp minor version (4 Bit) */
    { &hf_sstp_major,
      { "Major Version", "sstp.majorversion",
      FT_UINT8, BASE_DEC,
      NULL, SSTP_BITMASK_MAJORVERSION,
      NULL, HFILL }
    },
    /* sstp major version (4 Bit) */
    { &hf_sstp_minor,
      { "Minor Version", "sstp.minorversion",
      FT_UINT8, BASE_DEC,
      NULL, SSTP_BITMASK_MINORVERSION,
      NULL, HFILL }
    },
    /* Several Reserved Fields with different size */
    { &hf_sstp_reserved,
      { "Reserved", "sstp.reserved",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* C (1 Bit, set to 1 if control packet, 0 means data packet) */
    { &hf_sstp_control_flag,
      { "Control Packet", "sstp.iscontrol",
      FT_BOOLEAN, 8,
      NULL, SSTP_BITMASK_CONTROLFLAG,
      NULL, HFILL }
    },
    /* Length Packet (16 Bit) */
    { &hf_sstp_length,
      { "Length-Packet", "sstp.length",
      FT_UINT16, BASE_DEC,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Message Type (16 Bit) */
    { &hf_sstp_messagetype,
      { "Message Type", "sstp.messagetype",
      FT_UINT16, BASE_HEX,
      VALS(sstp_messagetypes), 0x0,
      NULL, HFILL }
    },
    /* Number of Attributes (16 Bit) */
    { &hf_sstp_numattrib,
      { "Number of Attributes", "sstp.numattrib",
      FT_UINT16, BASE_DEC,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Fields for Attributes */
    /* Attribute Reserved Field (8 Bit) */
    { &hf_sstp_attrib_reserved,
      { "Reserved", "sstp.attribreserved",
      FT_UINT8, BASE_HEX,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Attribute ID (8 Bit) */
    { &hf_sstp_attrib_id,
      { "ID", "sstp.attribid",
      FT_UINT8, BASE_DEC,
      VALS(sstp_attributes), 0x0,
      NULL, HFILL }
    },
    /* Attribute Length Reserved (4 Bit reserved for future use inside the 16 bit length field) */
    { &hf_sstp_attrib_length_reserved,
      { "Reserved", "sstp.attriblengthreserved",
      FT_UINT16, BASE_HEX,
      NULL, SSTP_BITMASK_LENGTH_RESERVED,
      NULL, HFILL }
    },
    /* Attribute Length Actual Length (12 Bit) */
    { &hf_sstp_attrib_length,
      { "Length", "sstp.attriblength",
      FT_UINT16, BASE_DEC,
      NULL, SSTP_BITMASK_LENGTH_LENGTH,
      NULL, HFILL }
    },
    /* Undocumented Data in SSTP_MSG_CALL_CONNECT_REQUEST
       see also MS-SSTP section 2.2.9 "Call Connect Request Message
       (SSTP_MSG_CALL_CONNECT_REQUEST)":

           https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-sstp/e73ced14-7bef-407b-a85b-a6f624324dd1
     */
    { &hf_sstp_data_unknown,
      { "Unknown Data", "sstp.dataunknown",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Hash Protocol (8 Bit) */
    { &hf_sstp_hash_protocol,
      { "Hash Protocol", "sstp.hash",
      FT_UINT8, BASE_HEX,
      VALS(hash_protocols), 0x0,
      NULL, HFILL }
    },
    /* Nonce (256 Bit) */
    { &hf_sstp_nonce,
      { "Nonce", "sstp.nonce",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Cert Hash (20 Bytes if SHA1 is used, 32 Bytes with SHA256) */
    { &hf_sstp_cert_hash,
      { "Cert Hash", "sstp.cert_hash",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Cert Padding (0 Bytes if SHA256 is used, 12 Bytes with SHA1) */
    { &hf_sstp_padding,
      { "Padding", "sstp.padding",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Compound MAC (20 Bytes if SHA1 is used, 32 Bytes with SHA1) */
    { &hf_sstp_compound_mac,
      { "Compound Mac", "sstp.compoundmac",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    },
    /* Encapsulated Protocol (2 Bytes) */
    { &hf_sstp_ecapsulated_protocol,
      { "Encapsulated Protocol", "sstp.encapsulatedprotocol",
      FT_UINT16, BASE_HEX,
      VALS(encapsulated_protocols), 0x0,
      NULL, HFILL }
    },
    /* Attribute Status (4 Bytes) */
    { &hf_sstp_status,
      { "Status", "sstp.status",
      FT_UINT32, BASE_HEX,
      VALS(attrib_status), 0x0,
      NULL, HFILL }
    },
    /* Attribute Value (Variable Length) */
    { &hf_sstp_attrib_value,
      { "Attribute Value", "sstp.attribvalue",
      FT_BYTES, BASE_NONE,
      NULL, 0x0,
      NULL, HFILL }
    }
  };

  /* Setup protocol subtree array */
  static gint *ett[] = {
    &ett_sstp,
    &ett_sstp_attribute,
    &ett_sstp_version
  };

  proto_sstp = proto_register_protocol("Secure Socket Tunneling Protocol", "SSTP", "sstp");

  register_dissector("sstp", dissect_sstp, proto_sstp);
  proto_register_field_array(proto_sstp, hf, array_length(hf));
  proto_register_subtree_array(ett, array_length(ett));
}

void
proto_reg_handoff_sstp(void)
{
  ppp_hdlc_handle = find_dissector_add_dependency("ppp_hdlc", proto_sstp);
}

/*
* Editor modelines  -  https://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 2
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=2 tabstop=8 expandtab:
* :indentSize=2:tabSize=8:noTabs=true:
*/