Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-tplink-smarthome.c
blobf3ea0131b4efa9d3c027289f8222bb6eb9ead077
1 /* packet-tplink-smarthome.c
3 * Routines for TP-Link Smart Home Protocol dissection
5 * Copyright 2020-2021, Fulko Hew <fulko.hew@gmail.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * SPDX-License-Identifier: GPL-2.0-or-later
15 * TP-Link Smart Home Protocol (Port 9999) Wireshark Dissector
16 * For decrypting local network traffic between TP-Link Smart Home Devices (such as a KP400)
17 * and the Kasa Smart Home App (or equivalent)
19 * Protocol Message
21 * +--+--+--+--+--+--+--+--+--+--+
22 * UDP | Autokey XOR'ed message ... |
23 * +--+--+--+--+--+--+--+--+--+--+
25 * +-------+-------+-------+-------+--+--+--+--+--+--+--+--+--+--+
26 * TCP | Big-endian 32-bit byte count + Autokey XOR'ed message ... |
27 * +-------+-------+-------+-------+--+--+--+--+--+--+--+--+--+--+
29 * I.e. They are both the same except TCP is prefixed with a byte count.
32 #include <config.h>
33 #include <epan/packet.h>
34 #include <epan/address.h>
35 #include <epan/conversation.h>
36 #include <epan/wmem_scopes.h>
37 #include "packet-tcp.h"
39 #define TPLINK_SMARTHOME_PORT 9999 /* Not IANA registered */
40 /* TP-Link Smart Home devices use this port on both TCP and UDP */
41 #define FRAME_HEADER_LEN 4 /* 4 bytes of TCP frame length header info */
43 /* Prototypes */
44 /* (Required to prevent [-Wmissing-prototypes] warnings */
46 void proto_reg_handoff_tplink_smarthome(void);
47 void proto_register_tplink_smarthome(void);
49 static dissector_handle_t tplink_smarthome_handle;
50 static dissector_handle_t tplink_smarthome_message_handle;
52 /* Initialize the protocol and registered fields */
54 static int proto_tplink_smarthome;
55 static int ett_tplink_smarthome; /* Initialize the subtree pointers */
57 static int hf_tplink_smarthome_Len;
58 static int hf_tplink_smarthome_Msg;
60 static bool
61 test_tplink_smarthome(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
63 uint8_t key = 171;
64 uint8_t c, d;
65 if (tvb_captured_length_remaining(tvb, offset) < 2) {
66 return false;
69 /* The message is always JSON, so test the first two characters.
70 * They must be {" or {}, as the protocol doesn't appear to
71 * have whitespace.). */
72 c = tvb_get_uint8(tvb, offset);
73 d = c ^ key;
74 if (d != '{') {
75 return false;
77 d = c ^ tvb_get_uint8(tvb, offset+1);
78 if (d != '"' && d != '}') {
79 return false;
82 return true;
85 static int
86 dissect_tplink_smarthome_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
87 void *data _U_)
89 proto_item *ti;
90 proto_tree *tplink_smarthome_tree;
91 int8_t start = 0;
92 uint8_t c, d;
93 uint8_t key = 171;
94 int32_t len = tvb_captured_length(tvb);
96 switch (pinfo->ptype) { /* look at the IP port type */
97 case PT_UDP:
98 start = 0;
99 break;
100 case PT_TCP:
101 start = 4;
102 break;
103 default:
104 return 0;
107 if (!test_tplink_smarthome(pinfo, tvb, start, data)) {
108 return 0;
111 col_set_str(pinfo->cinfo, COL_PROTOCOL, "TPLINK-SMARTHOME"); /* show the protocol name of what we're dissecting */
112 col_clear(pinfo->cinfo, COL_INFO); /* and clear anything that might be in the Info field on the UI */
114 ti = proto_tree_add_item(tree, proto_tplink_smarthome, tvb, 0, -1, ENC_NA); /* create display subtree for this protocol */
115 tplink_smarthome_tree = proto_item_add_subtree(ti, ett_tplink_smarthome); /* and add it to the display tree */
117 if (pinfo->ptype == PT_TCP) {
118 proto_tree_add_item(tplink_smarthome_tree, hf_tplink_smarthome_Len,
119 tvb, 0, FRAME_HEADER_LEN, ENC_BIG_ENDIAN); /* decode the 4 byte message length field pre-pended in a TCP message, */
121 int i_offset = start;
122 int o_offset = 0;
123 int decode_len = len - start;
124 char *ascii_buffer = (char *)wmem_alloc(pinfo->pool, 1 + len - start); /* create a buffer for the decoded (JSON) message */
126 for (; o_offset < decode_len; i_offset++, o_offset++) { /* decrypt 'Autokey XOR' message (into ASCII) */
127 c = tvb_get_uint8(tvb, i_offset);
128 d = c ^ key; /* XOR the byte with the key to get the decoded byte */
129 key = c; /* then use that decoded byte as the value for the next key */
130 *(ascii_buffer + o_offset) = g_ascii_isprint(d) ? d : '.'; /* buffer a printable version (for display and JSON decoding) */
132 *(ascii_buffer + o_offset) = '\0';
134 char *mtype; /* categorize the message's intent: */
135 if (pinfo->destport == TPLINK_SMARTHOME_PORT) { mtype = "Cmd"; } /* 'Cmd' - if it's TO the TP_Link port */
136 else if (pinfo->srcport == TPLINK_SMARTHOME_PORT) { mtype = "Rsp"; } /* 'Rsp' - if it's FROM the TP_Link port */
137 else { mtype = "Msg"; } /* impossible... because we're registered on this port so src or dest must have matched */
139 proto_tree_add_string_format(tplink_smarthome_tree, hf_tplink_smarthome_Msg, tvb,
140 start, -1, ascii_buffer, "%s: %s", mtype, ascii_buffer); /* add the decrypted data to the subtree so you can 'expand' on it */
142 tvbuff_t *next_tvb = tvb_new_child_real_data(tvb, (uint8_t *)ascii_buffer, decode_len, decode_len); /* create a new TVB and insert the decrypted ASCII string, and */
143 add_new_data_source(pinfo, next_tvb, "JSON Message"); /* add it so you can click on this JSON entry and see the decoded buffer */
144 call_dissector(find_dissector("json"), next_tvb, pinfo, ti); /* and decode/dissect it as JSON so you can drill down into it as well */
146 col_add_fstr(pinfo->cinfo, COL_INFO, "%s %s: %s",
147 (pinfo->ptype == PT_UDP) ? "UDP" : "TCP",
148 mtype, ascii_buffer); /* add the decoded string to the INFO column for a quick and easy read */
150 return tvb_captured_length(tvb); /* finally return the amount of data this dissector was able to dissect */
153 static unsigned
154 get_tplink_smarthome_message_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
155 { /* the PDU size is... the value in the length field */
156 return (unsigned)tvb_get_ntohl(tvb, offset) + FRAME_HEADER_LEN; /* plus the 'size of' the length field itself */
159 static int
160 dissect_tplink_smarthome(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
162 conversation_t *conv = find_or_create_conversation(pinfo);
163 if (!conversation_get_proto_data(conv, proto_tplink_smarthome)) {
164 if (!test_tplink_smarthome(pinfo, tvb, FRAME_HEADER_LEN, data)) {
165 return 0;
167 conversation_add_proto_data(conv, proto_tplink_smarthome, GUINT_TO_POINTER(1));
169 tcp_dissect_pdus(tvb, pinfo, tree, true, FRAME_HEADER_LEN,
170 get_tplink_smarthome_message_len, dissect_tplink_smarthome_message, data);
171 return tvb_captured_length(tvb);
174 /* Register the protocol with Wireshark. */
176 void
177 proto_register_tplink_smarthome(void)
179 static hf_register_info hf[] = { /* setup list of header fields */
180 { &hf_tplink_smarthome_Len,
181 { "Len", "tplink_smarthome.len",
182 FT_UINT32, BASE_DEC, NULL, 0,
183 "Message Length", HFILL }
185 { &hf_tplink_smarthome_Msg,
186 { "Msg", "tplink_smarthome.msg",
187 FT_STRING, BASE_NONE, NULL, 0,
188 "Message", HFILL }
192 static int *ett[] = { /* setup protocol subtree array */
193 &ett_tplink_smarthome
196 proto_tplink_smarthome = proto_register_protocol("TP-Link Smart Home Protocol", /* register the protocol name and description */
197 "TPLINK-SMARTHOME", "tplink-smarthome");
198 tplink_smarthome_handle = register_dissector("tplink-smarthome",
199 dissect_tplink_smarthome, proto_tplink_smarthome);
200 tplink_smarthome_message_handle = register_dissector("tplink-smarthome-message",
201 dissect_tplink_smarthome_message, proto_tplink_smarthome);
203 proto_register_field_array(proto_tplink_smarthome, hf, array_length(hf)); /* register the header fields */
204 proto_register_subtree_array(ett, array_length(ett)); /* and subtrees */
207 void
208 proto_reg_handoff_tplink_smarthome(void)
211 dissector_add_uint_with_preference("tcp.port", TPLINK_SMARTHOME_PORT, tplink_smarthome_handle);
212 dissector_add_uint_with_preference("udp.port", TPLINK_SMARTHOME_PORT, tplink_smarthome_message_handle);
216 * Editor modelines - https://www.wireshark.org/tools/modelines.html
218 * Local variables:
219 * c-basic-offset: 4
220 * tab-width: 8
221 * indent-tabs-mode: t
222 * End:
224 * vi: set shiftwidth=4 tabstop=8 noexpandtab:
225 * :indentSize=4:tabSize=8:noTabs=false: