Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Armored Dragon 2024-07-21 09:39:31 -05:00
commit da06bcb9b7
No known key found for this signature in database
GPG key ID: C7207ACC3382AD8B
13 changed files with 248 additions and 28 deletions

View file

@ -1,6 +1,6 @@
Copyright (c) 2013-2019, High Fidelity, Inc.
Copyright (c) 2019-2021, Vircadia contributors.
Copyright (c) 2022-2023, Overte e.V.
Copyright (c) 2022-2024, Overte e.V.
All rights reserved.
https://overte.org

View file

@ -97,7 +97,7 @@ int main(int argc, const char* argv[]) {
);
QCommandLineOption protocolVersionOption(
"protocolVersion",
"Writes the protocol version base64 signature to a file?",
"Writes the protocol version base64 signature to a file",
"path"
);
QCommandLineOption noUpdaterOption(
@ -275,6 +275,14 @@ int main(int argc, const char* argv[]) {
"abortAfterInit",
"Debug option. Aborts after initialization, right before the program starts running the event loop."
);
QCommandLineOption getProtocolVersionHashOption(
"getProtocolVersionHash",
"Debug option. Returns the network protocol version MD5 hash."
);
QCommandLineOption getProtocolVersionDataOption(
"getProtocolVersionData",
"Debug option. Returns the network protocol detailed data in JSON."
);
// "--qmljsdebugger", which appears in output from "--help-all".
// Those below don't seem to be optional.
@ -321,6 +329,8 @@ int main(int argc, const char* argv[]) {
parser.addOption(abortAfterStartupOption);
parser.addOption(abortAfterInitOption);
parser.addOption(getPluginsOption);
parser.addOption(getProtocolVersionHashOption);
parser.addOption(getProtocolVersionDataOption);
QString applicationPath;
@ -455,6 +465,34 @@ int main(int argc, const char* argv[]) {
return 1;
}
}
if (parser.isSet(getProtocolVersionHashOption)) {
std::cout << protocolVersionsSignatureHex().toStdString() << std::endl;
return 0;
}
if (parser.isSet(getProtocolVersionDataOption)) {
auto protocolMap = protocolVersionsSignatureMap();
QMetaEnum packetMetaEnum = QMetaEnum::fromType<PacketTypeEnum::Value>();
QJsonArray packetTypesList;
auto keyList = protocolMap.keys();
std::sort(keyList.begin(), keyList.end()); // Sort by numeric value
for(const auto packet : keyList) {
QJsonObject data;
int intValue = static_cast<int>(packet);
QString keyName = packetMetaEnum.valueToKey(intValue);
data["name"] = keyName;
data["value"] = intValue;
data["version"] = versionForPacketType(packet);
packetTypesList.append(data);
}
std::cout << QJsonDocument(packetTypesList).toJson().toStdString() << std::endl;
return 0;
}
static const QString APPLICATION_CONFIG_FILENAME = "config.json";
QDir applicationDir(applicationPath);

View file

@ -130,6 +130,10 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion) {
static QByteArray protocolVersionSignature;
static QString protocolVersionSignatureBase64;
static QString protocolVersionSignatureHex;
static QMap<PacketType, uint8_t> protocolVersionMap;
static void ensureProtocolVersionsSignature() {
static std::once_flag once;
std::call_once(once, [&] {
@ -139,12 +143,14 @@ static void ensureProtocolVersionsSignature() {
stream << numberOfProtocols;
for (uint8_t packetType = 0; packetType < numberOfProtocols; packetType++) {
uint8_t packetTypeVersion = static_cast<uint8_t>(versionForPacketType(static_cast<PacketType>(packetType)));
protocolVersionMap[static_cast<PacketType>(packetType)] = packetTypeVersion;
stream << packetTypeVersion;
}
QCryptographicHash hash(QCryptographicHash::Md5);
hash.addData(buffer);
protocolVersionSignature = hash.result();
protocolVersionSignatureBase64 = protocolVersionSignature.toBase64();
protocolVersionSignatureHex = protocolVersionSignature.toHex(0);
});
}
QByteArray protocolVersionsSignature() {
@ -161,3 +167,13 @@ QString protocolVersionsSignatureBase64() {
ensureProtocolVersionsSignature();
return protocolVersionSignatureBase64;
}
QString protocolVersionsSignatureHex() {
ensureProtocolVersionsSignature();
return protocolVersionSignatureHex;
}
QMap<PacketType, uint8_t> protocolVersionsSignatureMap() {
ensureProtocolVersionsSignature();
return protocolVersionMap;
}

View file

@ -31,8 +31,15 @@ class PacketTypeEnum {
Q_GADGET
Q_ENUMS(Value)
public:
// If adding a new packet packetType, you can replace one marked usable or add at the end.
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
/**
* @brief Packet type identifier
*
* Identifies the type of packet being sent.
*
* @note If adding a new packet packetType, you can replace one marked usable or add at the end.
* @note This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t
*/
enum class Value : uint8_t {
Unknown,
DomainConnectRequestPending,
@ -143,6 +150,8 @@ public:
NUM_PACKET_TYPE
};
Q_ENUM(Value)
const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> getReplicatedPacketMapping() {
const static QHash<PacketTypeEnum::Value, PacketTypeEnum::Value> REPLICATED_PACKET_MAPPING {
{ PacketTypeEnum::Value::MicrophoneAudioNoEcho, PacketTypeEnum::Value::ReplicatedMicrophoneAudioNoEcho },
@ -219,10 +228,60 @@ const int NUM_BYTES_MD5_HASH = 16;
// NOTE: There is a max limit of 255, hopefully we have a better way to manage this by then.
typedef uint8_t PacketVersion;
/**
* @brief Returns the version number of the given packet type
*
* If the implementation of a packet type is modified in an incompatible way, the implementation
* of this function needs to be modified to return an incremented value.
*
* This is used to determine whether the protocol is compatible between client and server.
*
* @note Version is limited to a max of 255.
*
* @param packetType Type of packet
* @return PacketVersion Version
*/
PacketVersion versionForPacketType(PacketType packetType);
QByteArray protocolVersionsSignature(); /// returns a unique signature for all the current protocols
/**
* @brief Returns a unique signature for all the current protocols
*
* This computes a MD5 hash that expresses the state of the protocol's specification. The calculation
* is done in ensureProtocolVersionsSignature and accounts for the following:
*
* * Number of known packet types
* * versionForPacketType(type) for each packet type.
*
* There's no provision for backwards compatibility, anything that changes this calculation is a protocol break.
*
* @return QByteArray MD5 digest as a byte array
*/
QByteArray protocolVersionsSignature();
/***
* @brief Returns a unique signature for all the current protocols
*
* Same as protocolVersionsSignature(), in base64.
*/
QString protocolVersionsSignatureBase64();
/***
* @brief Returns a unique signature for all the current protocols
*
* Same as protocolVersionsSignature(), in hex;
*/
QString protocolVersionsSignatureHex();
/***
* @brief Returns the data used to compute the protocol version
*
* The key is the packet type. The value is the version for that packet type.
*
* Used for aiding in development.
*/
QMap<PacketType, uint8_t> protocolVersionsSignatureMap();
#if (PR_BUILD || DEV_BUILD)
void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debugging version negotiation
#endif
@ -428,4 +487,5 @@ enum class AvatarQueryVersion : PacketVersion {
ConicalFrustums = 22
};
#endif // hifi_PacketHeaders_h

View file

@ -510,7 +510,7 @@ Rectangle {
mess = mess.replace(arrow, "&lt;");
var link = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
mess = mess.replace(link, (match) => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a> <a onclick='Window.openUrl("+match+")'>⮺</a>"});
mess = mess.replace(link, (match) => {return `<a style="color:#4EBAFD" onclick='Window.openUrl("+match+")' href='` + match + `'>` + match + `</a> <a onclick='Window.openUrl(`+match+`)'></a>`});
var newline = /\n/gi;
mess = mess.replace(newline, "<br>");

View file

@ -58,7 +58,7 @@ function runDefaultsTogether() {
function runDefaultsSeparately() {
for (var i in CONTOLLER_SCRIPTS) {
if (CONTOLLER_SCRIPTS.hasOwnProperty(i)) {
print("loading " + CONTOLLER_SCRIPTS[j]);
print("loading " + CONTOLLER_SCRIPTS[i]);
Script.load(CONTOLLER_SCRIPTS[i]);
}
}

View file

@ -31,6 +31,7 @@
// Program ----
function onMouseLookChanged(newMouseLook) {
disableMouseLook();
mouseLookEnabled = newMouseLook;
}
@ -38,8 +39,6 @@
// Toggle using the m key
if (event.text.toLowerCase() === "m") {
if (Camera.captureMouse) {
mouseLookActive = false;
Settings.setValue("mouselook-active", false);
disableMouseLook();
} else {
mouseLookActive = true;
@ -82,12 +81,16 @@
if (hmdActive) return;
if (tablet.tabletShown) return;
if (overlayActive) return;
if (!mouseLookEnabled) return; // Mouse look disabled via setting
if (!mouseLookActive) return; // Mouse look disabled via the hotkey
Camera.captureMouse = true;
}
function disableMouseLook() {
mouseLookActive = false;
Settings.setValue("mouselook-active", false);
Camera.captureMouse = false;
}

View file

@ -1,4 +1,5 @@
print("Loading hfudt")
bit32 = require("bit32")
-- create the HFUDT protocol
p_hfudt = Proto("hfudt", "HFUDT Protocol")
@ -154,19 +155,55 @@ local packet_types = {
[99] = "EntityQueryInitialResultsComplete",
[100] = "BulkAvatarTraits",
[101] = "AudioSoloRequest",
[102] = "BulkAvatarTraitsAck"
[102] = "BulkAvatarTraitsAck",
[103] = "StopInjector",
[104] = "AvatarZonePresence",
[105] = "WebRTCSignaling"
}
-- PacketHeaders.h, getNonSourcedPackets()
local unsourced_packet_types = {
["DomainList"] = true,
["DomainConnectRequestPending"] = true,
["CreateAssignment"] = true,
["RequestAssignment"] = true,
["DomainServerRequireDTLS"] = true,
["DomainConnectRequest"] = true,
["ICEPing"] = true,
["ICEPingReply"] = true,
["DomainList"] = true,
["DomainConnectionDenied"] = true,
["DomainServerPathQuery"] = true,
["DomainServerPathResponse"] = true,
["DomainServerAddedNode"] = true,
["DomainServerConnectionToken"] = true,
["DomainSettingsRequest"] = true,
["ICEServerHeartbeatACK"] = true
["OctreeDataFileRequest"] = true,
["OctreeDataFileReply"] = true,
["OctreeDataPersist"] = true,
["DomainContentReplacementFromUrl"] = true,
["DomainSettings"] = true,
["ICEServerPeerInformation"] = true,
["ICEServerQuery"] = true,
["ICEServerHeartbeat"] = true,
["ICEServerHeartbeatACK"] = true,
["ICEPing"] = true,
["ICEPingReply"] = true,
["ICEServerHeartbeatDenied"] = true,
["AssignmentClientStatus"] = true,
["StopNode"] = true,
["DomainServerRemovedNode"] = true,
["UsernameFromIDReply"] = true,
["OctreeFileReplacement"] = true,
["ReplicatedMicrophoneAudioNoEcho"] = true,
["ReplicatedMicrophoneAudioWithEcho"] = true,
["ReplicatedInjectAudio"] = true,
["ReplicatedSilentAudioFrame"] = true,
["ReplicatedAvatarIdentity"] = true,
["ReplicatedKillAvatar"] = true,
["ReplicatedBulkAvatarData"] = true,
["AvatarZonePresence"] = true,
["WebRTCSignaling"] = true
}
-- PacketHeaders.h, getNonVerifiedPackets()
local nonverified_packet_types = {
["NodeJsonStats"] = true,
["EntityQuery"] = true,
@ -222,6 +259,7 @@ function p_hfudt.dissector(buf, pinfo, tree)
type:append_text(" (".. control_types[shifted_type][1] .. ")")
subtree:add(f_control_type_text, control_types[shifted_type][1])
pinfo.cols.info:append(" [" .. control_types[shifted_type][1] .. "]")
end
if shifted_type == 0 then
@ -257,7 +295,7 @@ function p_hfudt.dissector(buf, pinfo, tree)
-- read the obfuscation level
local obfuscation_bits = bit32.band(0x03, bit32.rshift(first_word, 27))
subtree:add(f_obfuscation_level, obfuscation_bits)
-- read the sequence number
subtree:add(f_sequence_number, bit32.band(first_word, SEQUENCE_NUMBER_MASK))
@ -300,10 +338,12 @@ function p_hfudt.dissector(buf, pinfo, tree)
local packet_type = buf(payload_offset, 1):le_uint()
local ptype = subtree:add_le(f_type, buf(payload_offset, 1))
local packet_type_text = packet_types[packet_type]
if packet_type_text ~= nil then
subtree:add(f_type_text, packet_type_text)
-- if we know this packet type then add the name
ptype:append_text(" (".. packet_type_text .. ")")
pinfo.cols.info:append(" [" .. packet_type_text .. "]")
end
-- read the version
@ -431,12 +471,12 @@ function deobfuscate(message_bit, buf, level)
else
return
end
local start = 4
if message_bit == 1 then
local start = 12
end
local p = 0
for i = start, buf:len() - 1 do
out:set_index(i, bit.bxor(buf(i, 1):le_uint(), key:get_index(7 - (p % 8))) )

View file

@ -1,5 +1,5 @@
print("Loading hf-audio")
bit32 = require("bit32")
-- create the audio protocol
p_hf_audio = Proto("hf-audio", "HF Audio Protocol")

View file

@ -1,4 +1,5 @@
print("Loading hf-avatar")
bit32 = require("bit32")
-- create the avatar protocol
p_hf_avatar = Proto("hf-avatar", "HF Avatar Protocol")

View file

@ -1,4 +1,5 @@
print("Loading hf-entity")
bit32 = require("bit32")
-- create the entity protocol
p_hf_entity = Proto("hf-entity", "HF Entity Protocol")

View file

@ -1,4 +1,6 @@
-- create the domain protocol
print("Loading hf-domain")
bit32 = require("bit32")
p_hf_domain = Proto("hf-domain", "HF Domain Protocol")
-- domain packet fields

View file

@ -1,14 +1,73 @@
High Fidelity Wireshark Plugins
---------------------------------
# High Fidelity Wireshark Plugins
Install wireshark 2.4.6 or higher.
Copy these lua files into c:\Users\username\AppData\Roaming\Wireshark\Plugins
## Installation
After a capture any detected High Fidelity Packets should be easily identifiable by one of the following protocols
* HF-AUDIO - Streaming audio packets
* HF-AVATAR - Streaming avatar mixer packets
* HF-ENTITY - Entity server traffic
* HF-DOMAIN - Domain server traffic
* HFUDT - All other UDP traffic
* Install wireshark 2.4.6 or higher.
* Copy these lua files into `c:\Users\username\AppData\Roaming\Wireshark\Plugins` on Windows, or `$HOME/.local/lib/wireshark/plugins` on Linux.
## Lua version
This is a Lua plugin, which requires the bit32 module to be installed. You can find the Lua version wireshark uses in the About dialog, eg:
Version 4.2.5 (Git commit 798e06a0f7be).
Compiled (64-bit) using GCC 14.1.1 20240507 (Red Hat 14.1.1-1), with GLib
2.80.2, with Qt 6.7.0, with libpcap, with POSIX capabilities (Linux), with libnl
3, with zlib 1.3.0.zlib-ng, with PCRE2, with Lua 5.1.5, with GnuTLS 3.8.5 and
This indicates Lua 5.1 is used (see on the last line)
## Requirements
On Fedora 40:
* wireshark-devel
* lua5.1-bit32
## Usage
After a capture any detected Overte Packets should be easily identifiable by one of the following protocols
* `HF-AUDIO` - Streaming audio packets
* `HF-AVATAR` - Streaming avatar mixer packets
* `HF-ENTITY` - Entity server traffic
* `HF-DOMAIN` - Domain server traffic
* `HFUDT` - All other UDP traffic
## Troubleshooting
### attempt to index global 'bit32' (a nil value)
`[Expert Info (Error/Undecoded): Lua Error: /home/dale/.local/lib/wireshark/plugins/1-hfudt.lua:207: attempt to index global 'bit32' (a nil value)]`
See the installation requirements, you need to install the bit32 Lua module for the right Lua version.
## Development hints
* Symlink files from the development tree to `$HOME/.local/lib/wireshark/plugins`, to have Wireshark work on the latest dissector code.
* Capture packets for later analysis in a PCAPNG file.
* Only save needed packets in the dump
Decode on the commandline with:
tshark -r packets.pcapng.gz -V
Decode only the first packet:
tshark -r packets.pcapng.gz -V -c 1
### Useful tshark arguments
* `-x` hex dump
* `-c N` Only decode first N packets
* `-O hfudt,hf-domain,hf-entity,hf-avatar,hf-audio` Only dump Overte protocol data, skip dumping UDP/etc parts.
* `-V` decode protocols
*