SELECT_RESPONSE_t incoming(void *group, int s, uint8_t *data, size_t length, char *addr, uint16_t port, void *param) { buffer_t *buffer; switch(state) { case RECV_STATE_HEADER: { /* Sanity check -- this should never happen with select_group. */ if(length != 4) DIE("Received incorrect number of bytes for NetBIOS header (likely a problem with select_group)."); /* The header is 24-bytes in network byte order. */ buffer = buffer_create_with_data(BO_NETWORK, data, 4); current_length = (buffer_read_next_int32(buffer) & 0x00FFFFFF); buffer_destroy(buffer); /* Enter body-receiving mode. */ select_group_wait_for_bytes((select_group_t*)group, s, current_length); state = RECV_STATE_BODY; break; } case RECV_STATE_BODY: { SMB_t *smb; /* Sanity check -- this should never happen with select_group. */ if(length != current_length) DIE("Received incorrect number of bytes for SMB (likely a problem with select_group)."); smb = smb_create_from_data(data, length, -1, NULL); /* TODO: Fix. */ switch(smb->header.command) { case SMB_COM_NEGOTIATE: parse_SMB_COM_NEGOTIATE(s, smb); break; case SMB_COM_SESSION_SETUP_ANDX: break; default: fprintf(stderr, "Don't know how to handle 0x%02x yet!\n", smb->header.command); } smb_destroy(smb); /* Return to header state. */ select_group_wait_for_bytes((select_group_t*)group, s, 4); state = RECV_STATE_HEADER; break; } default: DIE("Entered an unknown state."); } return SELECT_OK; }
packet_t *packet_parse(uint8_t *data, size_t length, options_t options) { packet_t *packet = (packet_t*) safe_malloc(sizeof(packet_t)); buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length); /* Validate the size */ if(buffer_get_length(buffer) > MAX_PACKET_SIZE) { LOG_FATAL("Packet is too long: %zu bytes\n", buffer_get_length(buffer)); exit(1); } packet->packet_id = buffer_read_next_int16(buffer); packet->packet_type = (packet_type_t) buffer_read_next_int8(buffer); packet->session_id = buffer_read_next_int16(buffer); switch(packet->packet_type) { case PACKET_TYPE_SYN: packet->body.syn.seq = buffer_read_next_int16(buffer); packet->body.syn.options = buffer_read_next_int16(buffer); break; case PACKET_TYPE_MSG: if(options & OPT_CHUNKED_DOWNLOAD) { packet->body.msg.options.chunked.chunk = buffer_read_next_int32(buffer); } else { packet->body.msg.options.normal.seq = buffer_read_next_int16(buffer); packet->body.msg.options.normal.ack = buffer_read_next_int16(buffer); } packet->body.msg.data = buffer_read_remaining_bytes(buffer, &packet->body.msg.data_length, -1, FALSE); break; case PACKET_TYPE_FIN: packet->body.fin.reason = buffer_alloc_next_ntstring(buffer); break; case PACKET_TYPE_PING: packet->body.ping.data = buffer_alloc_next_ntstring(buffer); break; default: LOG_FATAL("Error: unknown message type (0x%02x)\n", packet->packet_type); exit(0); } buffer_destroy(buffer); return packet; }
command_packet_t *command_packet_read(buffer_t *stream) { size_t remaining_bytes = buffer_get_remaining_bytes(stream); uint32_t needed_bytes = -1; uint8_t *data; command_packet_t *out = NULL; size_t length; /* If we don't have a length, we're done. */ if(remaining_bytes < 4) return NULL; /* Check for an overflow. */ needed_bytes = buffer_peek_next_int32(stream); if(needed_bytes + 4 < needed_bytes) { LOG_FATAL("Overflow in command_packet!"); exit(1); } /* Make sure there are enough bytes present for the length + data. */ if(remaining_bytes < needed_bytes + 4) return NULL; /* Consume the length. */ buffer_read_next_int32(stream); /* Read the data. */ data = buffer_read_remaining_bytes(stream, &length, needed_bytes, TRUE); /* Sanity check. */ if(length != needed_bytes) { LOG_FATAL("Something went very wrong with the buffer class; the wrong number of bytes were read!"); exit(1); } /* Parse the data and free the buffer. */ out = command_packet_parse(data, length); safe_free(data); return out; }
command_packet_t *command_packet_stream_read(command_packet_stream_t *stream) { uint32_t length; uint8_t *data; command_packet_t *command_packet; if(!command_packet_stream_ready(stream)) return NULL; length = buffer_read_next_int32(stream->buffer); data = safe_malloc(length); buffer_read_next_bytes(stream->buffer, data, length); command_packet = command_packet_parse(data, length, stream->is_request); safe_free(data); return command_packet; }
/* Parse a packet from a byte stream. */ static command_packet_t *command_packet_parse(uint8_t *data, uint32_t length) { command_packet_t *p = safe_malloc(sizeof(command_packet_t)); buffer_t *buffer = buffer_create_with_data(BO_BIG_ENDIAN, data, length); uint16_t packed_id = buffer_read_next_int16(buffer); /* The first bit of the request_id represents a response */ p->request_id = (packed_id & 0x7FFF); p->is_request = (packed_id & 0x8000) ? FALSE : TRUE; p->command_id = (command_packet_type_t) buffer_read_next_int16(buffer); switch(p->command_id) { case COMMAND_PING: if(p->is_request) p->r.request.body.ping.data = buffer_alloc_next_ntstring(buffer); else p->r.response.body.ping.data = buffer_alloc_next_ntstring(buffer); break; case COMMAND_SHELL: if(p->is_request) p->r.request.body.shell.name = buffer_alloc_next_ntstring(buffer); else p->r.response.body.shell.session_id = buffer_read_next_int16(buffer); break; case COMMAND_EXEC: if(p->is_request) { p->r.request.body.exec.name = buffer_alloc_next_ntstring(buffer); p->r.request.body.exec.command = buffer_alloc_next_ntstring(buffer); } else { p->r.response.body.exec.session_id = buffer_read_next_int16(buffer); } break; case COMMAND_DOWNLOAD: if(p->is_request) { p->r.request.body.download.filename = buffer_alloc_next_ntstring(buffer); } else { p->r.response.body.download.data = (uint8_t*)buffer_read_remaining_bytes(buffer, (size_t*)&p->r.response.body.download.length, -1, TRUE); } break; case COMMAND_UPLOAD: if(p->is_request) { p->r.request.body.upload.filename = buffer_alloc_next_ntstring(buffer); p->r.request.body.upload.data = buffer_read_remaining_bytes(buffer, (size_t*)&p->r.request.body.upload.length, -1, TRUE); } else { } break; case COMMAND_SHUTDOWN: break; case TUNNEL_CONNECT: if(p->is_request) { p->r.request.body.tunnel_connect.options = buffer_read_next_int32(buffer); p->r.request.body.tunnel_connect.host = buffer_alloc_next_ntstring(buffer); p->r.request.body.tunnel_connect.port = buffer_read_next_int16(buffer); } else { p->r.response.body.tunnel_connect.tunnel_id = buffer_read_next_int32(buffer); } break; case TUNNEL_DATA: if(p->is_request) { p->r.request.body.tunnel_data.tunnel_id = buffer_read_next_int32(buffer); p->r.request.body.tunnel_data.data = buffer_read_remaining_bytes(buffer, (size_t*)&p->r.request.body.tunnel_data.length, -1, TRUE); } else { /* n/a */ } break; case TUNNEL_CLOSE: if(p->is_request) { p->r.request.body.tunnel_close.tunnel_id = buffer_read_next_int32(buffer); p->r.request.body.tunnel_close.reason = buffer_alloc_next_ntstring(buffer); } else { /* n/a */ } break; case COMMAND_ERROR: if(p->is_request) { p->r.request.body.error.status = buffer_read_next_int16(buffer); p->r.request.body.error.reason = buffer_alloc_next_ntstring(buffer); } else { p->r.request.body.error.status = buffer_read_next_int16(buffer); p->r.request.body.error.reason = buffer_alloc_next_ntstring(buffer); } break; default: LOG_FATAL("Unknown command_id: 0x%04x", p->command_id); exit(1); } return p; }