blob: 97319d52ee981d42d2dddd656d5eeba57670d869 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "smbprovider/netbios_packet_parser.h"
#include <utility>
#include <base/check_op.h>
#include <base/logging.h>
#include <base/strings/string_util.h>
namespace smbprovider {
namespace netbios {
namespace {
// https://tools.ietf.org/html/rfc1002
// Section 4.2.13
constexpr int kMinimumValidPacketSize = 26;
constexpr int kNBNSResourceRecord = 0x20;
constexpr int kNBNSSTATUSResourceRecord = 0x21;
// Packet Offsets
constexpr int kEntriesOffset = 25;
constexpr int kEntryCountOffset = 24;
constexpr int kNameLengthOffset = 12;
constexpr int kPositiveResponseOffset = 14;
// Checks whether a NetBios Name Response packet corresponds to the
// transaction.
bool HasValidTransactionId(const std::vector<uint8_t>& packet,
uint16_t expected_transaction_id) {
DCHECK_GE(packet.size(), 2);
const uint16_t actual_transaction_id = (packet[0] << 8) | packet[1];
return actual_transaction_id == expected_transaction_id;
}
// Checks whether a NetBios Name Response packet is a positive response.
bool IsPositiveNameResponse(const std::vector<uint8_t>& packet,
uint32_t byte_index) {
DCHECK_GT(packet.size(), byte_index + 1);
const uint16_t node_status =
(packet[byte_index] << 8) | packet[byte_index + 1];
return node_status == kNBNSResourceRecord ||
node_status == kNBNSSTATUSResourceRecord;
}
size_t GetExpectedPacketLength(uint8_t name_length, uint8_t entry_count) {
return kEntriesOffset + name_length + entry_count * kServerEntrySize;
}
} // namespace
std::vector<std::string> ParsePacket(const std::vector<uint8_t>& packet,
uint16_t transaction_id) {
if (packet.size() < kMinimumValidPacketSize) {
return std::vector<std::string>();
}
// Check the transaction id.
if (!HasValidTransactionId(packet, transaction_id)) {
// This response is not to our broadcast.
return std::vector<std::string>();
}
// Get name length.
const uint8_t name_length = packet[kNameLengthOffset];
// Check if it's a Positive response.
if (kPositiveResponseOffset + name_length + 1 >= packet.size()) {
return std::vector<std::string>();
}
if (!IsPositiveNameResponse(packet, kPositiveResponseOffset + name_length)) {
// Negative response to the broadcast.
return std::vector<std::string>();
}
// Get Address List entry count.
if (kEntryCountOffset + name_length >= packet.size()) {
return std::vector<std::string>();
}
const uint8_t entry_count = packet[kEntryCountOffset + name_length];
// Check that there are the correct number of bytes remaining in the packet.
if (GetExpectedPacketLength(name_length, entry_count) > packet.size()) {
return std::vector<std::string>();
}
// Get Servers.
uint32_t byte_index = kEntriesOffset + name_length;
std::vector<std::string> servers;
for (int i = 0; i < entry_count; ++i) {
DCHECK_LE(byte_index + kServerEntrySize, packet.size());
// Get Server name.
std::string server(packet.begin() + byte_index,
packet.begin() + byte_index + kServerNameLength);
byte_index += kServerNameLength;
// Get type.
const uint8_t type = packet[byte_index];
byte_index += 1;
if (type == kFileServerNodeType) {
base::TrimWhitespaceASCII(server, base::TRIM_TRAILING, &server);
servers.push_back(std::move(server));
}
// Skip flags.
byte_index += 2;
}
return servers;
}
} // namespace netbios
} // namespace smbprovider