16static uint16_t rd16(
const uint8_t *p)
18 return (uint16_t)(p[0] | (p[1] << 8));
20static uint32_t rd32(
const uint8_t *p)
22 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24);
24static uint64_t rd64(
const uint8_t *p)
26 return (uint64_t)rd32(p) | ((uint64_t)rd32(p + 4) << 32);
28static void wr16(uint8_t *p, uint16_t v)
31 p[1] = (uint8_t)(v >> 8);
33static void wr32(uint8_t *p, uint32_t v)
36 p[1] = (uint8_t)(v >> 8);
37 p[2] = (uint8_t)(v >> 16);
38 p[3] = (uint8_t)(v >> 24);
40static void wr64(uint8_t *p, uint64_t v)
43 wr32(p + 4, (uint32_t)(v >> 32));
46static const uint8_t SMB2_PROTOCOL_ID[4] = {0xFE,
'S',
'M',
'B'};
48size_t smb2_transport_frame(uint8_t *out,
size_t cap,
const uint8_t *msg,
size_t msg_len)
50 if (!out || !msg || msg_len > 0x00FFFFFF || 4 + msg_len > cap)
53 out[1] = (uint8_t)(msg_len >> 16);
54 out[2] = (uint8_t)(msg_len >> 8);
55 out[3] = (uint8_t)(msg_len);
56 memcpy(out + 4, msg, msg_len);
60uint32_t smb2_transport_len(
const uint8_t *buf,
size_t len)
62 if (!buf || len < 4 || buf[0] != 0x00)
64 return ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3];
67size_t smb2_build_header(uint8_t *buf,
size_t cap, Smb2Command command, uint16_t credit_request, uint64_t message_id,
68 uint32_t tree_id, uint64_t session_id)
70 if (!buf || cap < SMB2_HEADER_SIZE)
72 memset(buf, 0, SMB2_HEADER_SIZE);
73 memcpy(buf + 0, SMB2_PROTOCOL_ID, 4);
76 wr16(buf + 12, (uint16_t)command);
77 wr16(buf + 14, credit_request);
79 wr64(buf + 24, message_id);
81 wr32(buf + 36, tree_id);
82 wr64(buf + 40, session_id);
84 return SMB2_HEADER_SIZE;
87bool smb2_parse_header(
const uint8_t *buf,
size_t len, Smb2Header *out)
89 if (!buf || !out || len < SMB2_HEADER_SIZE)
91 if (memcmp(buf, SMB2_PROTOCOL_ID, 4) != 0 || rd16(buf + 4) != 64)
93 out->status = rd32(buf + 8);
94 out->command = (Smb2Command)rd16(buf + 12);
95 out->credit_response = rd16(buf + 14);
96 out->flags = rd32(buf + 16);
97 out->message_id = rd64(buf + 24);
98 out->tree_id = rd32(buf + 36);
99 out->session_id = rd64(buf + 40);
103size_t smb2_build_negotiate(uint8_t *buf,
size_t cap,
const uint8_t client_guid[16], uint16_t security_mode)
105 static const Smb2Dialect dialects[] = {Smb2Dialect::SMB2_DIALECT_0202, Smb2Dialect::SMB2_DIALECT_0210,
106 Smb2Dialect::SMB2_DIALECT_0300, Smb2Dialect::SMB2_DIALECT_0302};
107 const uint16_t ndialects = (uint16_t)(
sizeof(dialects) /
sizeof(dialects[0]));
108 const size_t total = SMB2_HEADER_SIZE + 36 + (size_t)ndialects * 2;
109 if (!buf || !client_guid || cap < total)
112 if (smb2_build_header(buf, cap, Smb2Command::SMB2_NEGOTIATE, 1, 0, 0, 0) == 0)
115 uint8_t *b = buf + SMB2_HEADER_SIZE;
118 wr16(b + 2, ndialects);
119 wr16(b + 4, security_mode);
121 memcpy(b + 12, client_guid, 16);
123 for (uint16_t i = 0; i < ndialects; i++)
124 wr16(b + 36 + i * 2, (uint16_t)dialects[i]);
128bool smb2_parse_negotiate_response(
const uint8_t *msg,
size_t len, Smb2NegotiateResp *out)
133 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_NEGOTIATE)
136 if (len < SMB2_HEADER_SIZE + 64)
138 const uint8_t *b = msg + SMB2_HEADER_SIZE;
139 if (rd16(b + 0) != 65)
142 out->security_mode = rd16(b + 2);
143 out->dialect = rd16(b + 4);
144 memcpy(out->server_guid, b + 8, 16);
145 out->capabilities = rd32(b + 24);
146 out->max_transact = rd32(b + 28);
147 out->max_read = rd32(b + 32);
148 out->max_write = rd32(b + 36);
150 uint16_t sec_off = rd16(b + 56);
151 uint16_t sec_len = rd16(b + 58);
154 out->sec_buf =
nullptr;
155 out->sec_buf_len = 0;
158 if ((
size_t)sec_off + sec_len > len || sec_off < SMB2_HEADER_SIZE)
160 out->sec_buf = msg + sec_off;
161 out->sec_buf_len = sec_len;
165size_t smb2_build_session_setup(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id,
166 uint8_t security_mode,
const uint8_t *sec_buf,
size_t sec_len)
168 const size_t body = 24;
169 const size_t total = SMB2_HEADER_SIZE + body + sec_len;
170 if (!buf || !sec_buf || sec_len == 0 || sec_len > 0xFFFF || cap < total)
172 if (smb2_build_header(buf, cap, Smb2Command::SMB2_SESSION_SETUP, 1, message_id, 0, session_id) == 0)
175 uint8_t *b = buf + SMB2_HEADER_SIZE;
179 b[3] = security_mode;
181 wr16(b + 12, (uint16_t)(SMB2_HEADER_SIZE + body));
182 wr16(b + 14, (uint16_t)sec_len);
184 memcpy(b + body, sec_buf, sec_len);
188bool smb2_parse_session_setup_response(
const uint8_t *msg,
size_t len, Smb2SessionSetupResp *out)
193 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_SESSION_SETUP)
196 if (len < SMB2_HEADER_SIZE + 8)
198 const uint8_t *b = msg + SMB2_HEADER_SIZE;
199 if (rd16(b + 0) != 9)
202 out->session_flags = rd16(b + 2);
203 uint16_t sec_off = rd16(b + 4);
204 uint16_t sec_len = rd16(b + 6);
207 out->sec_buf =
nullptr;
208 out->sec_buf_len = 0;
211 if ((
size_t)sec_off + sec_len > len || sec_off < SMB2_HEADER_SIZE)
213 out->sec_buf = msg + sec_off;
214 out->sec_buf_len = sec_len;
218size_t smb2_build_tree_connect(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id,
219 const uint8_t *path_utf16,
size_t path_len)
221 const size_t body = 8;
222 const size_t total = SMB2_HEADER_SIZE + body + path_len;
223 if (!buf || !path_utf16 || path_len == 0 || path_len > 0xFFFF || cap < total)
225 if (smb2_build_header(buf, cap, Smb2Command::SMB2_TREE_CONNECT, 1, message_id, 0, session_id) == 0)
228 uint8_t *b = buf + SMB2_HEADER_SIZE;
232 wr16(b + 4, (uint16_t)(SMB2_HEADER_SIZE + body));
233 wr16(b + 6, (uint16_t)path_len);
234 memcpy(b + body, path_utf16, path_len);
238bool smb2_parse_tree_connect_response(
const uint8_t *msg,
size_t len, Smb2TreeConnectResp *out)
243 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_TREE_CONNECT)
245 if (len < SMB2_HEADER_SIZE + 16)
247 const uint8_t *b = msg + SMB2_HEADER_SIZE;
248 if (rd16(b + 0) != 16)
250 out->share_type = b[2];
251 out->share_flags = rd32(b + 4);
252 out->capabilities = rd32(b + 8);
253 out->maximal_access = rd32(b + 12);
257size_t smb2_build_create(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
258 uint32_t desired_access, uint32_t share_access, uint32_t create_disposition,
259 uint32_t create_options,
const uint8_t *name_utf16,
size_t name_len)
261 const size_t body = 56;
262 const size_t total = SMB2_HEADER_SIZE + body + name_len;
263 if (!buf || !name_utf16 || name_len == 0 || name_len > 0xFFFF || cap < total)
265 if (smb2_build_header(buf, cap, Smb2Command::SMB2_CREATE, 1, message_id, tree_id, session_id) == 0)
268 uint8_t *b = buf + SMB2_HEADER_SIZE;
274 wr32(b + 24, desired_access);
276 wr32(b + 32, share_access);
277 wr32(b + 36, create_disposition);
278 wr32(b + 40, create_options);
279 wr16(b + 44, (uint16_t)(SMB2_HEADER_SIZE + body));
280 wr16(b + 46, (uint16_t)name_len);
282 memcpy(b + body, name_utf16, name_len);
286bool smb2_parse_create_response(
const uint8_t *msg,
size_t len, Smb2CreateResp *out)
291 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_CREATE)
293 if (len < SMB2_HEADER_SIZE + 88)
295 const uint8_t *b = msg + SMB2_HEADER_SIZE;
296 if (rd16(b + 0) != 89)
298 out->create_action = rd32(b + 4);
299 out->end_of_file = rd64(b + 48);
300 out->file_attributes = rd32(b + 56);
301 memcpy(out->file_id, b + 64, 16);
305size_t smb2_build_close(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
306 const uint8_t file_id[16])
308 const size_t body = 24;
309 const size_t total = SMB2_HEADER_SIZE + body;
310 if (!buf || !file_id || cap < total)
312 if (smb2_build_header(buf, cap, Smb2Command::SMB2_CLOSE, 1, message_id, tree_id, session_id) == 0)
315 uint8_t *b = buf + SMB2_HEADER_SIZE;
319 memcpy(b + 8, file_id, 16);
323bool smb2_parse_close_response(
const uint8_t *msg,
size_t len, Smb2CloseResp *out)
328 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_CLOSE)
330 if (len < SMB2_HEADER_SIZE + 60)
332 const uint8_t *b = msg + SMB2_HEADER_SIZE;
333 if (rd16(b + 0) != 60)
335 out->end_of_file = rd64(b + 48);
336 out->file_attributes = rd32(b + 56);
340size_t smb2_build_read(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
341 const uint8_t file_id[16], uint32_t length, uint64_t offset)
343 const size_t body = 48;
344 const size_t total = SMB2_HEADER_SIZE + body + 1;
345 if (!buf || !file_id || cap < total)
347 if (smb2_build_header(buf, cap, Smb2Command::SMB2_READ, 1, message_id, tree_id, session_id) == 0)
350 uint8_t *b = buf + SMB2_HEADER_SIZE;
351 memset(b, 0, body + 1);
353 b[2] = (uint8_t)(SMB2_HEADER_SIZE + 16);
357 memcpy(b + 16, file_id, 16);
364bool smb2_parse_read_response(
const uint8_t *msg,
size_t len, Smb2ReadResp *out)
369 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_READ)
371 if (len < SMB2_HEADER_SIZE + 16)
373 const uint8_t *b = msg + SMB2_HEADER_SIZE;
374 if (rd16(b + 0) != 17)
377 uint8_t data_off = b[2];
378 uint32_t data_len = rd32(b + 4);
385 if (data_off < SMB2_HEADER_SIZE || (
size_t)data_off + data_len > len)
387 out->data = msg + data_off;
388 out->data_len = data_len;
392size_t smb2_build_write(uint8_t *buf,
size_t cap, uint64_t message_id, uint64_t session_id, uint32_t tree_id,
393 const uint8_t file_id[16],
const uint8_t *data,
size_t data_len, uint64_t offset)
395 const size_t body = 48;
396 const size_t total = SMB2_HEADER_SIZE + body + data_len;
397 if (!buf || !file_id || !data || data_len == 0 || data_len > 0xFFFFFFFF || cap < total)
399 if (smb2_build_header(buf, cap, Smb2Command::SMB2_WRITE, 1, message_id, tree_id, session_id) == 0)
402 uint8_t *b = buf + SMB2_HEADER_SIZE;
405 wr16(b + 2, (uint16_t)(SMB2_HEADER_SIZE + body));
406 wr32(b + 4, (uint32_t)data_len);
408 memcpy(b + 16, file_id, 16);
410 memcpy(b + body, data, data_len);
414bool smb2_parse_write_response(
const uint8_t *msg,
size_t len, Smb2WriteResp *out)
419 if (!smb2_parse_header(msg, len, &h) || h.command != Smb2Command::SMB2_WRITE)
421 if (len < SMB2_HEADER_SIZE + 16)
423 const uint8_t *b = msg + SMB2_HEADER_SIZE;
424 if (rd16(b + 0) != 17)
426 out->count = rd32(b + 4);
SMB2 client wire codec (MS-SMB2), DETWS_ENABLE_SMB - increment 1: the transport frame,...