/src/wireshark/epan/dissectors/packet-ssyncp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-ssyncp.c |
2 | | * Routines for dissecting mosh's State Synchronization Protocol |
3 | | * Copyright 2020 Google LLC |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | /* |
13 | | * State Synchronization Protocol is the protocol used by mosh: |
14 | | * <https://mosh.org/mosh-paper-draft.pdf> |
15 | | * |
16 | | * The protocol name is abbreviated as SSyncP to avoid conflict with the |
17 | | * "Scripting Service Protocol". |
18 | | * |
19 | | * The protocol is based on UDP, with a plaintext header followed by an |
20 | | * encrypted payload. For now we just support decrypting a single connection at |
21 | | * a time, using the MOSH_KEY dumped from the environment variables |
22 | | * (`cat /proc/$pid/environ | tr '\0' '\n' | grep MOSH_KEY` on Linux). |
23 | | * Note that to display the embedded protobuf properly, you'll have to add |
24 | | * src/protobufs/ from mosh's source code to the ProtoBuf search path. |
25 | | * For now we stop decoding after reaching the first level of protobufs; in |
26 | | * them, a second layer of protobufs is sometimes embedded (e.g. for |
27 | | * transmitting screen contents and such). Implementing that is left as an |
28 | | * exercise for the reader. |
29 | | */ |
30 | | |
31 | | #include <config.h> |
32 | | |
33 | | #include <epan/packet.h> /* Should be first Wireshark include (other than config.h) */ |
34 | | #include <epan/conversation.h> |
35 | | #include <epan/wmem_scopes.h> |
36 | | #include <epan/proto_data.h> |
37 | | #include <epan/prefs.h> |
38 | | #include <epan/expert.h> |
39 | | #include <epan/tfs.h> |
40 | | #include <wsutil/array.h> |
41 | | #include <wsutil/report_message.h> |
42 | | #include <wsutil/wsgcrypt.h> |
43 | | |
44 | | void proto_reg_handoff_ssyncp(void); |
45 | | void proto_register_ssyncp(void); |
46 | | |
47 | | static dissector_handle_t ssyncp_handle; |
48 | | |
49 | | static int proto_ssyncp; |
50 | | static int hf_ssyncp_direction; |
51 | | static int hf_ssyncp_seq; |
52 | | static int hf_ssyncp_encrypted; |
53 | | static int hf_ssyncp_seq_delta; |
54 | | static int hf_ssyncp_timestamp; |
55 | | static int hf_ssyncp_timestamp_reply; |
56 | | static int hf_ssyncp_frag_seq; |
57 | | static int hf_ssyncp_frag_final; |
58 | | static int hf_ssyncp_frag_idx; |
59 | | static int hf_ssyncp_rtt_to_server; |
60 | | static int hf_ssyncp_rtt_to_client; |
61 | | |
62 | | /* Initialize the subtree pointers */ |
63 | | static int ett_ssyncp; |
64 | | static int ett_ssyncp_decrypted; |
65 | | |
66 | | static expert_field ei_ssyncp_fragmented; |
67 | | static expert_field ei_ssyncp_bad_key; |
68 | | |
69 | | static const char *pref_ssyncp_key; |
70 | | static char ssyncp_raw_aes_key[16]; |
71 | | static bool have_ssyncp_key; |
72 | | |
73 | | static dissector_handle_t dissector_protobuf; |
74 | | |
75 | | typedef struct _ssyncp_conv_info_t { |
76 | | /* last sequence numbers per direction */ |
77 | | uint64_t last_seq[2]; |
78 | | /* for each direction, have we seen any traffic yet? */ |
79 | | bool seen_packet[2]; |
80 | | |
81 | | uint16_t clock_offset[2]; |
82 | | bool clock_seen[2]; |
83 | | } ssyncp_conv_info_t; |
84 | | |
85 | | typedef struct _ssyncp_packet_info_t { |
86 | | bool first_packet; |
87 | | int64_t seq_delta; |
88 | | bool have_rtt_estimate; |
89 | | int16_t rtt_estimate; |
90 | | } ssyncp_packet_info_t; |
91 | | |
92 | 0 | #define SSYNCP_IV_PAD 4 |
93 | 6 | #define SSYNCP_SEQ_LEN 8 |
94 | 6 | #define SSYNCP_DATAGRAM_HEADER_LEN (SSYNCP_SEQ_LEN + 2 + 2) /* 64-bit IV and two 16-bit timestamps */ |
95 | 6 | #define SSYNCP_TRANSPORT_HEADER_LEN (8 + 2) |
96 | 6 | #define SSYNCP_AUTHTAG_LEN 16 /* 128-bit auth tag */ |
97 | | |
98 | | /* |
99 | | * We only match on 60001, which mosh uses for its first connection. |
100 | | * If there are more connections in the range 60002-61000, the user will have to |
101 | | * mark those as ssyncp traffic manually - we'd have too many false positives |
102 | | * otherwise. |
103 | | */ |
104 | 14 | #define SSYNCP_UDP_PORT 60001 |
105 | | |
106 | | static int |
107 | | dissect_ssyncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
108 | | void *data _U_) |
109 | 6 | { |
110 | | /* Check that we have at least a datagram plus an OCB auth tag. */ |
111 | 6 | if (tvb_reported_length(tvb) < SSYNCP_DATAGRAM_HEADER_LEN + SSYNCP_TRANSPORT_HEADER_LEN + SSYNCP_AUTHTAG_LEN) |
112 | 1 | return 0; |
113 | | |
114 | 5 | uint64_t direction_and_seq = tvb_get_uint64(tvb, 0, ENC_BIG_ENDIAN); |
115 | 5 | unsigned direction = direction_and_seq >> 63; |
116 | 5 | uint64_t seq = direction_and_seq & ~(1ULL << 63); |
117 | | |
118 | | /* Heuristic: The 63-bit sequence number starts from zero and increments |
119 | | * from there. Even if you send 1000 packets per second over 10 years, you |
120 | | * won't reach 2^35. So check that the sequence number is not outrageously |
121 | | * high. |
122 | | */ |
123 | 5 | if (seq > (1ULL << 35)) |
124 | 2 | return 0; |
125 | | |
126 | | /* On the first pass, track the previous sequence numbers per direction, |
127 | | * compute deltas between sequence numbers, and save those deltas. |
128 | | * On subsequent passes, use the computed deltas. |
129 | | */ |
130 | 3 | ssyncp_packet_info_t *ssyncp_pinfo; |
131 | 3 | ssyncp_conv_info_t *ssyncp_info = NULL; |
132 | 3 | if (pinfo->fd->visited) { |
133 | 0 | ssyncp_pinfo = (ssyncp_packet_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_ssyncp, 0); |
134 | 3 | } else { |
135 | 3 | conversation_t *conversation = find_or_create_conversation(pinfo); |
136 | 3 | ssyncp_info = (ssyncp_conv_info_t *)conversation_get_proto_data(conversation, proto_ssyncp); |
137 | 3 | if (!ssyncp_info) { |
138 | 2 | ssyncp_info = wmem_new0(wmem_file_scope(), ssyncp_conv_info_t); |
139 | 2 | conversation_add_proto_data(conversation, proto_ssyncp, ssyncp_info); |
140 | 2 | } |
141 | | |
142 | 3 | ssyncp_pinfo = wmem_new(wmem_file_scope(), ssyncp_packet_info_t); |
143 | 3 | ssyncp_pinfo->first_packet = !ssyncp_info->seen_packet[direction]; |
144 | 3 | if (ssyncp_pinfo->first_packet) { |
145 | 2 | ssyncp_info->seen_packet[direction] = true; |
146 | 2 | } else { |
147 | 1 | ssyncp_pinfo->seq_delta = seq - ssyncp_info->last_seq[direction]; |
148 | 1 | } |
149 | 3 | ssyncp_pinfo->have_rtt_estimate = false; |
150 | 3 | p_add_proto_data(wmem_file_scope(), pinfo, proto_ssyncp, 0, ssyncp_pinfo); |
151 | | |
152 | 3 | ssyncp_info->last_seq[direction] = seq; |
153 | 3 | } |
154 | | |
155 | | /*** COLUMN DATA ***/ |
156 | | |
157 | 3 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ssyncp"); |
158 | | |
159 | 3 | col_clear(pinfo->cinfo, COL_INFO); |
160 | | |
161 | 3 | char *direction_str = direction ? "Server->Client" : "Client->Server"; |
162 | 3 | col_set_str(pinfo->cinfo, COL_INFO, direction_str); |
163 | | |
164 | | /*** PROTOCOL TREE ***/ |
165 | | |
166 | | /* create display subtree for the protocol */ |
167 | 3 | proto_item *ti = proto_tree_add_item(tree, proto_ssyncp, tvb, 0, -1, ENC_NA); |
168 | | |
169 | 3 | proto_tree *ssyncp_tree = proto_item_add_subtree(ti, ett_ssyncp); |
170 | | |
171 | | /* Add an item to the subtree, see section 1.5 of README.dissector for more |
172 | | * information. */ |
173 | 3 | proto_tree_add_item(ssyncp_tree, hf_ssyncp_direction, tvb, |
174 | 3 | 0, 1, ENC_BIG_ENDIAN); |
175 | 3 | proto_tree_add_item(ssyncp_tree, hf_ssyncp_seq, tvb, |
176 | 3 | 0, 8, ENC_BIG_ENDIAN); |
177 | 3 | #ifdef GCRY_OCB_BLOCK_LEN |
178 | 3 | proto_item *encrypted_item = |
179 | 3 | #endif |
180 | 3 | proto_tree_add_item(ssyncp_tree, hf_ssyncp_encrypted, |
181 | 3 | tvb, 8, -1, ENC_NA); |
182 | | |
183 | 3 | if (!ssyncp_pinfo->first_packet) { |
184 | 1 | proto_item *delta_item = |
185 | 1 | proto_tree_add_int64(ssyncp_tree, hf_ssyncp_seq_delta, tvb, 0, 0, |
186 | 1 | ssyncp_pinfo->seq_delta); |
187 | 1 | proto_item_set_generated(delta_item); |
188 | 1 | } |
189 | | |
190 | 3 | unsigned char *decrypted = NULL; |
191 | 3 | unsigned decrypted_len = 0; |
192 | | |
193 | | /* avoid build failure on ancient libgcrypt without OCB support */ |
194 | 3 | #ifdef GCRY_OCB_BLOCK_LEN |
195 | 3 | if (have_ssyncp_key) { |
196 | 0 | gcry_error_t gcry_err; |
197 | | |
198 | | /* try to decrypt the rest of the packet */ |
199 | 0 | gcry_cipher_hd_t gcry_hd; |
200 | 0 | gcry_err = gcry_cipher_open(&gcry_hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_OCB, 0); |
201 | 0 | if (gcry_err_code(gcry_err)) { |
202 | | /* this shouldn't happen (even if the packet is garbage) */ |
203 | 0 | report_failure("ssyncp: unable to initialize cipher???"); |
204 | 0 | return tvb_captured_length(tvb); |
205 | 0 | } |
206 | 0 | gcry_err = gcry_cipher_setkey(gcry_hd, ssyncp_raw_aes_key, sizeof(ssyncp_raw_aes_key)); |
207 | 0 | if (gcry_err_code(gcry_err)) { |
208 | | /* this shouldn't happen (even if the packet is garbage) */ |
209 | 0 | report_failure("ssyncp: unable to set key???"); |
210 | 0 | gcry_cipher_close(gcry_hd); |
211 | 0 | return tvb_captured_length(tvb); |
212 | 0 | } |
213 | 0 | char nonce[SSYNCP_IV_PAD + SSYNCP_SEQ_LEN]; |
214 | 0 | memset(nonce, 0, SSYNCP_IV_PAD); |
215 | 0 | tvb_memcpy(tvb, nonce + SSYNCP_IV_PAD, 0, SSYNCP_SEQ_LEN); |
216 | 0 | gcry_err = gcry_cipher_setiv(gcry_hd, nonce, sizeof(nonce)); |
217 | 0 | if (gcry_err_code(gcry_err)) { |
218 | | /* this shouldn't happen (even if the packet is garbage) */ |
219 | 0 | report_failure("ssyncp: unable to set iv???"); |
220 | 0 | gcry_cipher_close(gcry_hd); |
221 | 0 | return tvb_captured_length(tvb); |
222 | 0 | } |
223 | 0 | decrypted_len = tvb_captured_length(tvb) - SSYNCP_SEQ_LEN - SSYNCP_AUTHTAG_LEN; |
224 | 0 | decrypted = (unsigned char *)tvb_memdup(pinfo->pool, tvb, |
225 | 0 | SSYNCP_SEQ_LEN, decrypted_len); |
226 | 0 | gcry_cipher_final(gcry_hd); |
227 | 0 | gcry_err = gcry_cipher_decrypt(gcry_hd, decrypted, decrypted_len, NULL, 0); |
228 | 0 | if (gcry_err_code(gcry_err)) { |
229 | | /* this shouldn't happen (even if the packet is garbage) */ |
230 | 0 | report_failure("ssyncp: unable to decrypt???"); |
231 | 0 | gcry_cipher_close(gcry_hd); |
232 | 0 | return tvb_captured_length(tvb); |
233 | 0 | } |
234 | 0 | gcry_err = gcry_cipher_checktag(gcry_hd, |
235 | 0 | tvb_get_ptr(tvb, SSYNCP_SEQ_LEN+decrypted_len, SSYNCP_AUTHTAG_LEN), |
236 | 0 | SSYNCP_AUTHTAG_LEN); |
237 | 0 | if (gcry_err_code(gcry_err) && gcry_err_code(gcry_err) != GPG_ERR_CHECKSUM) { |
238 | | /* this shouldn't happen (even if the packet is garbage) */ |
239 | 0 | report_failure("ssyncp: unable to check auth tag???"); |
240 | 0 | gcry_cipher_close(gcry_hd); |
241 | 0 | return tvb_captured_length(tvb); |
242 | 0 | } |
243 | 0 | if (gcry_err_code(gcry_err)) { |
244 | | /* if the tag is wrong, the key was wrong and the decrypted data is useless */ |
245 | 0 | decrypted = NULL; |
246 | 0 | expert_add_info(pinfo, encrypted_item, &ei_ssyncp_bad_key); |
247 | 0 | } |
248 | 0 | gcry_cipher_close(gcry_hd); |
249 | 0 | } |
250 | 3 | #endif |
251 | | |
252 | 3 | if (decrypted) { |
253 | 0 | tvbuff_t *decrypted_tvb = tvb_new_child_real_data(tvb, decrypted, decrypted_len, decrypted_len); |
254 | 0 | add_new_data_source(pinfo, decrypted_tvb, "Decrypted data"); |
255 | |
|
256 | 0 | if (!pinfo->fd->visited && ssyncp_info) { |
257 | 0 | uint16_t our_clock16 = ((uint64_t)pinfo->abs_ts.secs * 1000 + pinfo->abs_ts.nsecs / 1000000) & 0xffff; |
258 | 0 | uint16_t sender_ts = tvb_get_uint16(decrypted_tvb, 0, ENC_BIG_ENDIAN); |
259 | 0 | uint16_t reply_ts = tvb_get_uint16(decrypted_tvb, 2, ENC_BIG_ENDIAN); |
260 | 0 | ssyncp_info->clock_offset[direction] = sender_ts - our_clock16; |
261 | 0 | ssyncp_info->clock_seen[direction] = true; |
262 | 0 | if (reply_ts != 0xffff && ssyncp_info->clock_seen[1-direction]) { |
263 | 0 | uint16_t projected_send_time_our_clock = reply_ts - ssyncp_info->clock_offset[1-direction]; |
264 | 0 | ssyncp_pinfo->rtt_estimate = our_clock16 - projected_send_time_our_clock; |
265 | 0 | ssyncp_pinfo->have_rtt_estimate = true; |
266 | 0 | } |
267 | 0 | } |
268 | |
|
269 | 0 | proto_tree *dec_tree = proto_tree_add_subtree(ssyncp_tree, decrypted_tvb, |
270 | 0 | 0, -1, ett_ssyncp_decrypted, NULL, "Decrypted data"); |
271 | |
|
272 | 0 | proto_tree_add_item(dec_tree, hf_ssyncp_timestamp, decrypted_tvb, |
273 | 0 | 0, 2, ENC_BIG_ENDIAN); |
274 | 0 | proto_tree_add_item(dec_tree, hf_ssyncp_timestamp_reply, decrypted_tvb, |
275 | 0 | 2, 2, ENC_BIG_ENDIAN); |
276 | |
|
277 | 0 | if (ssyncp_pinfo->have_rtt_estimate) { |
278 | 0 | int rtt_id = direction ? hf_ssyncp_rtt_to_server : hf_ssyncp_rtt_to_client; |
279 | 0 | proto_item *rtt_item = proto_tree_add_int(dec_tree, rtt_id, decrypted_tvb, 2, 2, ssyncp_pinfo->rtt_estimate); |
280 | 0 | proto_item_set_generated(rtt_item); |
281 | 0 | } |
282 | |
|
283 | 0 | proto_tree_add_item(dec_tree, hf_ssyncp_frag_seq, decrypted_tvb, |
284 | 0 | 4, 8, ENC_BIG_ENDIAN); |
285 | 0 | proto_tree_add_item(dec_tree, hf_ssyncp_frag_final, decrypted_tvb, |
286 | 0 | 12, 2, ENC_BIG_ENDIAN); |
287 | 0 | proto_item *frag_idx_item = proto_tree_add_item(dec_tree, |
288 | 0 | hf_ssyncp_frag_idx, decrypted_tvb, 12, 2, ENC_BIG_ENDIAN); |
289 | | |
290 | | /* TODO actually handle fragmentation; for now just bail out on fragmentation */ |
291 | 0 | if (tvb_get_uint16(decrypted_tvb, 12, ENC_BIG_ENDIAN) != 0x8000) { |
292 | 0 | expert_add_info(pinfo, frag_idx_item, &ei_ssyncp_fragmented); |
293 | 0 | return tvb_captured_length(tvb); |
294 | 0 | } |
295 | | |
296 | 0 | tvbuff_t *inflated_tvb = tvb_child_uncompress_zlib(decrypted_tvb, decrypted_tvb, 14, decrypted_len - 14); |
297 | 0 | if (inflated_tvb == NULL) |
298 | 0 | return tvb_captured_length(tvb); |
299 | 0 | add_new_data_source(pinfo, inflated_tvb, "Inflated data"); |
300 | |
|
301 | 0 | if (dissector_protobuf) { |
302 | 0 | call_dissector_with_data(dissector_protobuf, inflated_tvb, pinfo, |
303 | 0 | dec_tree, "message,TransportBuffers.Instruction"); |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | 3 | return tvb_captured_length(tvb); |
308 | 3 | } |
309 | | |
310 | | /* Register the protocol with Wireshark. |
311 | | * |
312 | | * This format is required because a script is used to build the C function that |
313 | | * calls all the protocol registration. |
314 | | */ |
315 | | void |
316 | | proto_register_ssyncp(void) |
317 | 14 | { |
318 | 14 | static const true_false_string direction_name = { |
319 | 14 | "Server->Client", |
320 | 14 | "Client->Server" |
321 | 14 | }; |
322 | | |
323 | | /* Setup list of header fields See Section 1.5 of README.dissector for |
324 | | * details. */ |
325 | 14 | static hf_register_info hf[] = { |
326 | 14 | { &hf_ssyncp_direction, |
327 | 14 | { "Direction", "ssyncp.direction", |
328 | 14 | FT_BOOLEAN, 8, TFS(&direction_name), 0x80, |
329 | 14 | "Direction of packet", HFILL } |
330 | 14 | }, |
331 | 14 | { &hf_ssyncp_seq, |
332 | 14 | { "Sequence number", "ssyncp.seq", |
333 | 14 | FT_UINT64, BASE_HEX, NULL, 0x7fffffffffffffff, |
334 | 14 | "Monotonically incrementing packet sequence number", HFILL } |
335 | 14 | }, |
336 | 14 | { &hf_ssyncp_encrypted, |
337 | 14 | { "Encrypted data", "ssyncp.enc_data", |
338 | 14 | FT_BYTES, BASE_NONE, NULL, 0, |
339 | 14 | "Encrypted RTT estimation fields and Transport Layer payload, encrypted with AES-128-OCB", |
340 | 14 | HFILL } |
341 | 14 | }, |
342 | 14 | { &hf_ssyncp_seq_delta, |
343 | 14 | { "Sequence number delta", "ssyncp.seq_delta", |
344 | 14 | FT_INT64, BASE_DEC, NULL, 0, |
345 | 14 | "Delta from last sequence number; 1 is normal, 0 is duplicated packet, <0 is reordering, >1 is reordering or packet loss", HFILL } |
346 | 14 | }, |
347 | 14 | { &hf_ssyncp_timestamp, |
348 | 14 | { "Truncated timestamp", "ssyncp.timestamp", |
349 | 14 | FT_UINT16, BASE_HEX, NULL, 0, |
350 | 14 | "Low 16 bits of sender's time in milliseconds", HFILL } |
351 | 14 | }, |
352 | 14 | { &hf_ssyncp_timestamp_reply, |
353 | 14 | { "Last timestamp received", "ssyncp.timestamp_reply", |
354 | 14 | FT_UINT16, BASE_HEX, NULL, 0, |
355 | 14 | "Low 16 bits of timestamp of last received packet plus time since it was received (for RTT estimation)", HFILL } |
356 | 14 | }, |
357 | 14 | { &hf_ssyncp_frag_seq, |
358 | 14 | { "Fragment ID", "ssyncp.frag_seq", |
359 | 14 | FT_UINT64, BASE_HEX, NULL, 0, |
360 | 14 | "Transport-level sequence number, used for fragment reassembly", HFILL } |
361 | 14 | }, |
362 | 14 | { &hf_ssyncp_frag_final, |
363 | 14 | { "Final fragment", "ssyncp.frag_final", |
364 | 14 | FT_BOOLEAN, 16, NULL, 0x8000, |
365 | 14 | "Is this the last fragment?", HFILL } |
366 | 14 | }, |
367 | 14 | { &hf_ssyncp_frag_idx, |
368 | 14 | { "Fragment Index", "ssyncp.frag_idx", |
369 | 14 | FT_UINT16, BASE_HEX, NULL, 0x7fff, |
370 | 14 | "Index of this fragment in the list of fragments of the transport-level message", HFILL } |
371 | 14 | }, |
372 | 14 | { &hf_ssyncp_rtt_to_server, |
373 | 14 | { "RTT estimate to server (in ms)", "ssyncp.rtt_est_to_server", |
374 | 14 | FT_INT16, BASE_DEC, NULL, 0, |
375 | 14 | "Estimated round trip time from point of capture to server", HFILL } |
376 | 14 | }, |
377 | 14 | { &hf_ssyncp_rtt_to_client, |
378 | 14 | { "RTT estimate to client (in ms)", "ssyncp.rtt_est_to_client", |
379 | 14 | FT_INT16, BASE_DEC, NULL, 0, |
380 | 14 | "Estimated round trip time from point of capture to client", HFILL } |
381 | 14 | } |
382 | 14 | }; |
383 | | |
384 | | /* Setup protocol subtree array */ |
385 | 14 | static int *ett[] = { |
386 | 14 | &ett_ssyncp, |
387 | 14 | &ett_ssyncp_decrypted |
388 | 14 | }; |
389 | | |
390 | | /* Setup protocol expert items */ |
391 | 14 | static ei_register_info ei[] = { |
392 | 14 | { &ei_ssyncp_fragmented, |
393 | 14 | { "ssyncp.fragmented", PI_REASSEMBLE, PI_WARN, |
394 | 14 | "SSYNCP-level fragmentation, dissector can't handle that", EXPFILL } |
395 | 14 | }, |
396 | 14 | { &ei_ssyncp_bad_key, |
397 | 14 | { "ssyncp.badkey", PI_DECRYPTION, PI_WARN, |
398 | 14 | "Encrypted data could not be decrypted with the provided key", EXPFILL } |
399 | 14 | } |
400 | 14 | }; |
401 | | |
402 | | /* Register the protocol name and description */ |
403 | 14 | proto_ssyncp = proto_register_protocol("State Synchronization Protocol", "SSyncP", "ssyncp"); |
404 | | |
405 | | /* Register the dissector handle */ |
406 | 14 | ssyncp_handle = register_dissector("ssyncp", dissect_ssyncp, proto_ssyncp); |
407 | | |
408 | | /* Required function calls to register the header fields and subtrees */ |
409 | 14 | proto_register_field_array(proto_ssyncp, hf, array_length(hf)); |
410 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
411 | | |
412 | 14 | expert_module_t *expert_ssyncp = expert_register_protocol(proto_ssyncp); |
413 | 14 | expert_register_field_array(expert_ssyncp, ei, array_length(ei)); |
414 | | |
415 | 14 | module_t *ssyncp_module = prefs_register_protocol(proto_ssyncp, proto_reg_handoff_ssyncp); |
416 | | |
417 | 14 | prefs_register_string_preference(ssyncp_module, "key", |
418 | 14 | "ssyncp MOSH_KEY", |
419 | 14 | "MOSH_KEY AES key (from mosh-{client,server} environment variable)", |
420 | 14 | &pref_ssyncp_key); |
421 | 14 | } |
422 | | |
423 | | void |
424 | | proto_reg_handoff_ssyncp(void) |
425 | 14 | { |
426 | 14 | static bool initialized = false; |
427 | | |
428 | 14 | if (!initialized) { |
429 | 14 | dissector_add_uint("udp.port", SSYNCP_UDP_PORT, ssyncp_handle); |
430 | | |
431 | 14 | dissector_protobuf = find_dissector("protobuf"); |
432 | 14 | if (dissector_protobuf == NULL) { |
433 | 0 | report_failure("unable to find protobuf dissector"); |
434 | 0 | } |
435 | | |
436 | 14 | initialized = true; |
437 | 14 | } |
438 | | |
439 | 14 | have_ssyncp_key = false; |
440 | 14 | if (strlen(pref_ssyncp_key) != 0) { |
441 | 0 | if (strlen(pref_ssyncp_key) != 22) { |
442 | 0 | report_failure("ssyncp: invalid key, must be 22 characters long"); |
443 | 0 | return; |
444 | 0 | } |
445 | 0 | char base64_key[25]; |
446 | 0 | memcpy(base64_key, pref_ssyncp_key, 22); |
447 | 0 | memcpy(base64_key+22, "==\0", 3); |
448 | 0 | size_t out_len; |
449 | 0 | if (g_base64_decode_inplace(base64_key, &out_len) == NULL || out_len != sizeof(ssyncp_raw_aes_key)) { |
450 | 0 | report_failure("ssyncp: invalid key, base64 decoding (with \"==\" appended) failed"); |
451 | 0 | return; |
452 | 0 | } |
453 | 0 | memcpy(ssyncp_raw_aes_key, base64_key, sizeof(ssyncp_raw_aes_key)); |
454 | 0 | have_ssyncp_key = true; |
455 | 0 | } |
456 | 14 | } |
457 | | |
458 | | /* |
459 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
460 | | * |
461 | | * Local variables: |
462 | | * c-basic-offset: 4 |
463 | | * tab-width: 8 |
464 | | * indent-tabs-mode: nil |
465 | | * End: |
466 | | * |
467 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
468 | | * :indentSize=4:tabSize=8:noTabs=true: |
469 | | */ |