1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Obtain a [Uuid] uniquely representing the build of the current binary.
//!
//! This is intended to be used to check that different processes are indeed
//! invocations of identically laid out binaries.
//!
//! As such:
//! * It is guaranteed to be identical within multiple invocations of the same
//! binary.
//! * It is guaranteed to be different across binaries with different code or
//! data segments or layout.
//! * Equality is unspecified if the binaries have identical code and data
//! segments and layout but differ immaterially (e.g. if a timestamp is included
//! in the binary at compile time).
//!
//! # Examples
//!
//! ```
//! # let remote_build_id = build_id::get();
//! let local_build_id = build_id::get();
//! if local_build_id == remote_build_id {
//! 	println!("We're running the same binary as remote!");
//! } else {
//! 	println!("We're running a different binary to remote");
//! }
//! ```
//!
//! # Note
//!
//! This looks first for linker-inserted build ID / binary UUIDs (i.e.
//! `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to
//! hashing the whole binary.

#![doc(html_root_url = "https://docs.rs/build_id/0.1.0")]
#![deny(missing_docs, warnings, deprecated)]
#![allow(intra_doc_link_resolution_failure)]

extern crate byteorder;
extern crate twox_hash;
extern crate uuid;

use std::{env, fs, hash::Hasher, io, sync};
use uuid::Uuid;

static mut BUILD_ID: Uuid = Uuid::nil();
static INIT: sync::Once = sync::ONCE_INIT;

/// Returns a [Uuid] uniquely representing the build of the current binary.
///
/// This is intended to be used to check that different processes are indeed
/// invocations of identically laid out binaries.
///
/// As such:
/// * It is guaranteed to be identical within multiple invocations of the same
/// binary.
/// * It is guaranteed to be different across binaries with different code or
/// data segments or layout.
/// * Equality is unspecified if the binaries have identical code and data
/// segments and layout but differ immaterially (e.g. if a timestamp is included
/// in the binary at compile time).
///
/// # Examples
///
/// ```
/// # let remote_build_id = build_id::get();
/// let local_build_id = build_id::get();
/// if local_build_id == remote_build_id {
/// 	println!("We're running the same binary as remote!");
/// } else {
/// 	println!("We're running a different binary to remote");
/// }
/// ```
///
/// # Note
///
/// This looks first for linker-inserted build ID / binary UUIDs (i.e.
/// `.note.gnu.build-id` on Linux; `LC_UUID` in Mach-O; etc), falling back to
/// hashing the whole binary.
#[inline]
pub fn get() -> Uuid {
	unsafe {
		INIT.call_once(|| {
			BUILD_ID = calculate();
		});
		BUILD_ID
	}
}
fn calculate() -> Uuid {
	// LC_UUID https://opensource.apple.com/source/libsecurity_codesigning/libsecurity_codesigning-55037.6/lib/machorep.cpp https://stackoverflow.com/questions/10119700/how-to-get-mach-o-uuid-of-a-running-process
	// .note.gnu.build-id https://github.com/golang/go/issues/21564 https://github.com/golang/go/blob/178307c3a72a9da3d731fecf354630761d6b246c/src/cmd/go/internal/buildid/buildid.go
	let file = fs::File::open(env::current_exe().unwrap()).unwrap();
	let mut hasher = twox_hash::XxHash::with_seed(0);
	io::copy(&mut &file, &mut HashWriter(&mut hasher)).unwrap();
	let mut bytes = [0; 16];
	<byteorder::LittleEndian as byteorder::ByteOrder>::write_u64(&mut bytes, hasher.finish());
	Uuid::from_random_bytes(bytes)
}

struct HashWriter<T: Hasher>(T);
impl<T: Hasher> io::Write for HashWriter<T> {
	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
		self.0.write(buf);
		Ok(buf.len())
	}
	fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
		self.write(buf).map(|_| ())
	}
	fn flush(&mut self) -> io::Result<()> {
		Ok(())
	}
}

#[cfg(test)]
mod test {
	#[test]
	fn brute() {
		let x = super::calculate();
		for _ in 0..1000 {
			assert_eq!(x, super::calculate());
		}
	}
	#[test]
	fn get() {
		let x = super::calculate();
		assert_eq!(x, super::get());
		assert_eq!(x, super::get());
		assert_eq!(x, super::get());
	}
}