Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 102 additions & 42 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions datadog-sidecar-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ pub unsafe extern "C" fn ddog_remote_config_reader_for_endpoint<'a>(
language: language.to_utf8_lossy().into(),
tracer_version: tracer_version.to_utf8_lossy().into(),
endpoint: endpoint.clone(),
agentless: None,
},
&Arc::new(Target {
service: service_name.to_utf8_lossy().into(),
Expand Down
1 change: 1 addition & 0 deletions datadog-sidecar/src/service/sidecar_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ impl SidecarInterface for ConnectionSidecarHandler {
language: config.language,
tracer_version: config.tracer_version,
endpoint: config.endpoint,
agentless: None,
},
products: config.remote_config_products,
capabilities: config.remote_config_capabilities,
Expand Down
1 change: 1 addition & 0 deletions libdd-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ features = [
"Win32_Foundation",
"Win32_System_Diagnostics_ToolHelp",
"Win32_System_Performance",
"Win32_System_Registry",
"Win32_System_Threading",
]

Expand Down
22 changes: 21 additions & 1 deletion libdd-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
#![cfg_attr(not(test), deny(clippy::unimplemented))]

use anyhow::Context;
use http::uri;
use http::uri::PathAndQuery;
use http::{uri, Uri};
use serde::de::Error;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::sync::{Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
Expand All @@ -21,6 +22,7 @@ pub mod connector;
#[cfg(feature = "reqwest")]
pub mod dump_server;
pub mod entity_id;
pub mod machine_id;
pub mod regex_engine;
#[macro_use]
pub mod cstr;
Expand Down Expand Up @@ -332,6 +334,24 @@ impl Endpoint {
/// Default value for the timeout field in milliseconds.
pub const DEFAULT_TIMEOUT: u64 = 3_000;

pub fn agentless(site: &str, api_key: String) -> anyhow::Result<Self> {
Ok(Self {
url: Uri::builder()
.scheme("https")
.authority(
uri::Authority::try_from(site)
.with_context(|| format!("dd_site is an invalid url: {site}"))?,
)
.path_and_query(PathAndQuery::from_static(""))
.build()
.with_context(|| format!("rc url is invalid for site: {site}"))?,
api_key: Some(api_key.into()),
timeout_ms: Self::DEFAULT_TIMEOUT,
test_token: None,
use_system_resolver: true,
})
}

/// Returns an iterator of optional endpoint-specific headers (api-key, test-token)
/// as (header_name, header_value) string tuples for any that are available.
pub fn get_optional_headers(&self) -> impl Iterator<Item = (&'static str, &str)> {
Expand Down
121 changes: 121 additions & 0 deletions libdd-common/src/machine_id/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use std::path::Path;

fn read_trimmed(path: &Path) -> Option<String> {
let s = std::fs::read_to_string(path).ok()?;
let s = s.trim().to_owned();
if s.is_empty() {
None
} else {
Some(s)
}
}

pub fn get_machine_id_impl_paths(dmi_path: &Path, etc_path: &Path, boot_path: &Path) -> String {
if let Some(id) = read_trimmed(dmi_path) {
return id;
}
// agent compatibility:
// gopsutil only accepts /etc/machine-id when it's exactly 32 chars (bare hex)
if let Some(id) = read_trimmed(etc_path) {
if id.len() == 32 {
return id;
}
}
read_trimmed(boot_path).unwrap_or_default()
}

pub fn get_machine_id_impl() -> String {
get_machine_id_impl_paths(
Path::new("/sys/class/dmi/id/product_uuid"),
Path::new("/etc/machine-id"),
Path::new("/proc/sys/kernel/random/boot_id"),
)
}

#[cfg(test)]
mod tests {
use super::*;

fn write(path: &Path, content: &[u8]) {
std::fs::write(path, content).unwrap();
}

fn tmp_paths(
dir: &tempfile::TempDir,
) -> (std::path::PathBuf, std::path::PathBuf, std::path::PathBuf) {
(
dir.path().join("product_uuid"),
dir.path().join("machine_id"),
dir.path().join("boot_id"),
)
}

#[test]
fn level1_dmi_wins_when_present() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
write(&dmi, b"B08FA8A2-B01A-4D2B-BD95-FEC7E30C5AEC\n");
write(&etc, b"aabbccddaabbccddaabbccddaabbccdd\n");
write(&boot, b"cccccccccccccccccccccccccccccccc\n");
assert_eq!(
get_machine_id_impl_paths(&dmi, &etc, &boot),
"B08FA8A2-B01A-4D2B-BD95-FEC7E30C5AEC"
);
}

#[test]
fn level2_etc_used_when_dmi_absent() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
write(&etc, b"aabbccddaabbccddaabbccddaabbccdd\n");
write(&boot, b"cccccccccccccccccccccccccccccccc\n");
assert_eq!(
get_machine_id_impl_paths(&dmi, &etc, &boot),
"aabbccddaabbccddaabbccddaabbccdd"
);
}

#[test]
fn level2_skipped_when_etc_not_32_chars() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
write(&etc, b"aabbccdd-aabb-ccdd-aabb-ccddaabbccdd\n");
write(&boot, b"dddddddddddddddddddddddddddddddd\n");
assert_eq!(
get_machine_id_impl_paths(&dmi, &etc, &boot),
"dddddddddddddddddddddddddddddddd"
);
}

#[test]
fn level3_boot_id_as_last_resort() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
write(&boot, b"cccccccccccccccccccccccccccccccc\n");
assert_eq!(
get_machine_id_impl_paths(&dmi, &etc, &boot),
"cccccccccccccccccccccccccccccccc"
);
}

#[test]
fn all_absent_returns_empty() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
assert_eq!(get_machine_id_impl_paths(&dmi, &etc, &boot), "");
}

#[test]
fn trims_whitespace() {
let dir = tempfile::tempdir().unwrap();
let (dmi, etc, boot) = tmp_paths(&dir);
write(&etc, b" aabbccddaabbccddaabbccddaabbccdd \n");
assert_eq!(
get_machine_id_impl_paths(&dmi, &etc, &boot),
"aabbccddaabbccddaabbccddaabbccdd"
);
}
}
39 changes: 39 additions & 0 deletions libdd-common/src/machine_id/macos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

/// Returns `IOPlatformUUID` via `gethostuuid(3)`, which avoids a fork+exec of `ioreg`.
pub fn get_machine_id_impl() -> String {
let mut uuid = [0u8; 16];
let wait = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
let rc = unsafe { libc::gethostuuid(uuid.as_mut_ptr(), &wait) };
if rc != 0 {
return String::new();
}
format!(
"{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
uuid[0], uuid[1], uuid[2], uuid[3],
uuid[4], uuid[5],
uuid[6], uuid[7],
uuid[8], uuid[9],
uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15],
)
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn returns_nonempty_uuid() {
let id = get_machine_id_impl();
assert!(!id.is_empty());
assert_eq!(id.len(), 36);
assert_eq!(&id[8..9], "-");
assert_eq!(&id[13..14], "-");
assert_eq!(&id[18..19], "-");
assert_eq!(&id[23..24], "-");
}
}
135 changes: 135 additions & 0 deletions libdd-common/src/machine_id/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2021-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

//! Host machine identifier, mirroring `pkg/util/uuid.GetUUID()` in the Go agent.
//!
//! | Platform | Source |
//! |----------|--------|
//! | Linux | `/sys/class/dmi/id/product_uuid` then `/etc/machine-id` → `/proc/sys/kernel/random/boot_id` |
//! | macOS | `gethostuuid(3)` |
//! | Windows | `HKLM\SOFTWARE\Microsoft\Cryptography\MachineGuid` |
//! | Other | `""` |
//!
//! All values are normalised to lowercase `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`.
//! Returns `""` on failure rather than a random UUID — the backend can detect
//! a missing value but not a wrong one.

use std::sync::LazyLock;

#[cfg(target_os = "linux")]
mod linux;

#[cfg(target_os = "macos")]
mod macos;

#[cfg(windows)]
mod windows;

/// Normalise a raw OS machine-id to a lowercase hyphenated UUID string.
/// Strips hyphens, filters to hex digits, lowercases, then re-inserts hyphens.
/// Returns `""` if the result is not exactly 32 hex digits.
pub(crate) fn normalize_uuid(raw: &str) -> String {
let hex: String = raw
.chars()
.filter(|c| c.is_ascii_hexdigit())
.flat_map(char::to_lowercase)
.collect();

if hex.len() != 32 {
return String::new();
}

format!(
"{}-{}-{}-{}-{}",
&hex[0..8],
&hex[8..12],
&hex[12..16],
&hex[16..20],
&hex[20..32],
)
}

static MACHINE_ID: LazyLock<String> = LazyLock::new(|| {
let raw = {
#[cfg(target_os = "linux")]
{
linux::get_machine_id_impl()
}
#[cfg(target_os = "macos")]
{
macos::get_machine_id_impl()
}
#[cfg(windows)]
{
windows::get_machine_id_impl()
}
#[cfg(not(any(target_os = "linux", target_os = "macos", windows)))]
{
String::new()
}
};
normalize_uuid(&raw)
});

/// Returns the host machine ID as a lowercase hyphenated UUID, cached for the process lifetime.
/// Returns `""` on failure or unsupported platforms.
pub fn get_machine_id() -> &'static str {
MACHINE_ID.as_str()
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn cached_value_is_stable() {
assert_eq!(get_machine_id(), get_machine_id());
}

#[test]
fn value_has_uuid_shape_if_nonempty() {
let id = get_machine_id();
if id.is_empty() {
return;
}
assert_eq!(id.len(), 36);
for (i, c) in id.chars().enumerate() {
if [8, 13, 18, 23].contains(&i) {
assert_eq!(c, '-');
} else {
assert!(c.is_ascii_hexdigit() && !c.is_ascii_uppercase());
}
}
}

#[test]
fn normalize_bare_hex_inserts_hyphens() {
assert_eq!(
normalize_uuid("b08fa8a2b01a4d2bbd95fec7e30c5aec"),
"b08fa8a2-b01a-4d2b-bd95-fec7e30c5aec"
);
}

#[test]
fn normalize_uppercase_uuid_lowercased() {
assert_eq!(
normalize_uuid("B08FA8A2-B01A-4D2B-BD95-FEC7E30C5AEC"),
"b08fa8a2-b01a-4d2b-bd95-fec7e30c5aec"
);
}

#[test]
fn normalize_lowercase_uuid_unchanged() {
assert_eq!(
normalize_uuid("b08fa8a2-b01a-4d2b-bd95-fec7e30c5aec"),
"b08fa8a2-b01a-4d2b-bd95-fec7e30c5aec"
);
}

#[test]
fn normalize_invalid_returns_empty() {
assert_eq!(normalize_uuid(""), "");
assert_eq!(normalize_uuid("b08fa8a2"), "");
assert_eq!(normalize_uuid("zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz"), "");
}
}
Loading
Loading