2023-03-20 15:20:39 +00:00
use std ::fs ;
use std ::fs ::{ File , remove_file } ;
use std ::io ::{ Read , Write } ;
use std ::os ::unix ::fs ::PermissionsExt ;
use std ::path ::Path ;
use std ::process ::{ Command , Output } ;
use flate2 ::read ::GzDecoder ;
use reqwest ::blocking ::Response ;
use reqwest ::header ::HeaderMap ;
use tar ::Archive ;
2023-03-20 15:24:49 +00:00
2023-03-20 15:20:39 +00:00
#[ derive(serde::Deserialize, Debug) ]
struct GithubRelease {
name : String ,
assets : Vec < GithubReleaseAsset >
}
#[ derive(serde::Deserialize, Debug) ]
2023-03-20 15:24:49 +00:00
struct GithubUser { }
2023-03-20 15:20:39 +00:00
#[ derive(serde::Deserialize, Debug) ]
struct GithubReleaseAsset {
browser_download_url : String ,
name : String ,
2023-03-20 15:24:49 +00:00
size : i64
2023-03-20 15:20:39 +00:00
}
fn main ( ) {
2023-03-20 15:34:46 +00:00
if Path ::new ( & format! ( " {} / {} " , std ::env ::var ( " OUT_DIR " ) . unwrap ( ) , " noredownload " ) ) . exists ( ) & & std ::env ::var ( " TFBUILD_FORCE_REDOWNLOAD " ) . is_err ( ) {
println! ( " noredownload exists and TFBUILD_FORCE_REDOWNLOAD is not set. Not redoing build process. " ) ;
return ;
}
2023-03-20 15:20:39 +00:00
println! ( " [*] Fetching nebula releaseinfo... " ) ;
let mut headers = HeaderMap ::new ( ) ;
let mut has_api_key = false ;
if let Ok ( api_key ) = std ::env ::var ( " GH_API_KEY " ) {
headers . insert ( " Authorization " , format! ( " Bearer {} " , api_key ) . parse ( ) . unwrap ( ) ) ;
has_api_key = true ;
}
let client = reqwest ::blocking ::Client ::builder ( ) . user_agent ( " curl/7.57.1 " ) . default_headers ( headers ) . build ( ) . unwrap ( ) ;
let resp : Response = client . get ( " https://api.github.com/repos/slackhq/nebula/releases/latest " ) . send ( ) . unwrap ( ) ;
if resp . headers ( ) . get ( " X-Ratelimit-Remaining " ) . unwrap ( ) . to_str ( ) . unwrap ( ) = = " 0 " {
println! ( " You've been ratelimited from the GitHub API. Wait a while (1 hour) " ) ;
if ! has_api_key {
println! ( " You can also set a GitHub API key with the environment variable GH_API_KEY, which will increase your ratelimit ( a lot ) " ) ;
}
panic! ( " Ratelimited " ) ;
}
let release : GithubRelease = resp . json ( ) . unwrap ( ) ;
println! ( " [*] Fetching target triplet... " ) ;
let target = std ::env ::var ( " TARGET " ) . unwrap ( ) ;
println! ( " [*] Compiling for target {} " , target ) ;
let target_file = match target . as_str ( ) {
" x86_64-apple-darwin " | " aarch64-apple-darwin " = > " nebula-darwin " ,
" x86_64-unknown-freebsd " = > " nebula-freebsd-amd64 " ,
2023-03-31 00:23:35 +00:00
" x86_64-unknown-linux-gnu " | " x86_64-unknown-linux-musl " = > " nebula-linux-amd64 " ,
2023-03-20 15:20:39 +00:00
" armv5te-unknown-linux-gnueabi " = > " nebula-linux-arm-5 " ,
" arm-unknown-linux-gnueabi " | " arm-unknown-linux-gnueabihf " = > " nebula-linux-arm-6 " ,
" armv7-unknown-linux-gnueabihf " | " armv7-unknown-linux-gnueabi " = > " nebula-linux-arm-7 " ,
2023-03-31 03:08:55 +00:00
" aarch64-unknown-linux-gnu " | " aarch64-unknown-linux-musl " = > " nebula-linux-arm64 " ,
2023-03-20 15:20:39 +00:00
" x86_64-pc-windows-msvc " = > " nebula-windows-amd64 " ,
" aarch64-pc-windows-msvc " = > " nebula-windows-arm64 " ,
_ = > {
println! ( " This architecture is not supported yet :( " ) ;
println! ( " Nebula has a limited set of architectures it is able to function on. " ) ;
println! ( " tfclient can only be compiled on these architectures. " ) ;
println! ( " See https://github.com/slackhq/nebula/releases for a list of supported architectures " ) ;
println! ( " Is your system supported by Nebula? Shoot a message to the mailing list. Include the target triplet (above) in your response, as well as a link to the functioning Nebula binary. We will happily add your machine to the list! " ) ;
panic! ( " Unsupported architecture " ) ;
}
} ;
println! ( " [*] Embedding {} {} " , target_file , release . name ) ;
let download = release . assets . iter ( ) . find ( | r | r . name = = format! ( " {} .tar.gz " , target_file ) ) . expect ( " That architecture isn't avaliable :( " ) ;
println! ( " [*] Downloading {} .tar.gz ( {} , {} bytes) from {} " , target_file , target , download . size , download . browser_download_url ) ;
let response = reqwest ::blocking ::get ( & download . browser_download_url ) . unwrap ( ) ;
let content = response . bytes ( ) . unwrap ( ) . to_vec ( ) ;
let bytes = content . as_slice ( ) ;
let tar = GzDecoder ::new ( bytes ) ;
let mut archive = Archive ::new ( tar ) ;
let entries = archive . entries ( ) . unwrap ( ) ;
let mut nebula_bin = vec! [ ] ;
let mut nebula_cert_bin = vec! [ ] ;
let mut shasum = vec! [ ] ;
for entry in entries {
let mut entry = entry . unwrap ( ) ;
if entry . path ( ) . unwrap ( ) = = Path ::new ( " nebula " ) | | entry . path ( ) . unwrap ( ) = = Path ::new ( " nebula.exe " ) {
nebula_bin . reserve ( entry . size ( ) as usize ) ;
entry . read_to_end ( & mut nebula_bin ) . unwrap ( ) ;
} else if entry . path ( ) . unwrap ( ) = = Path ::new ( " nebula-cert " ) | | entry . path ( ) . unwrap ( ) = = Path ::new ( " nebula-cert.exe " ) {
nebula_cert_bin . reserve ( entry . size ( ) as usize ) ;
entry . read_to_end ( & mut nebula_cert_bin ) . unwrap ( ) ;
} else if entry . path ( ) . unwrap ( ) = = Path ::new ( " SHASUM256.txt " ) {
shasum . reserve ( entry . size ( ) as usize ) ;
entry . read_to_end ( & mut shasum ) . unwrap ( ) ;
}
}
if nebula_bin . is_empty ( ) {
panic! ( " [x] Release did not contain nebula binary " ) ;
}
if nebula_cert_bin . is_empty ( ) {
panic! ( " [x] Release did not contain nebula_cert binary " ) ;
}
let mut nebula_file = File ::create ( format! ( " {} /nebula.bin " , std ::env ::var ( " OUT_DIR " ) . unwrap ( ) ) ) . unwrap ( ) ;
nebula_file . write_all ( & nebula_bin ) . unwrap ( ) ;
codegen_version ( & nebula_bin , " nebula.bin " , " NEBULA " ) ;
let mut nebula_cert_file = File ::create ( format! ( " {} /nebula_cert.bin " , std ::env ::var ( " OUT_DIR " ) . unwrap ( ) ) ) . unwrap ( ) ;
nebula_cert_file . write_all ( & nebula_cert_bin ) . unwrap ( ) ;
codegen_version ( & nebula_cert_bin , " nebula_cert.bin " , " NEBULA_CERT " ) ;
2023-03-20 15:34:46 +00:00
// Indicate to cargo and ourselves that we have already downloaded and codegenned
File ::create ( format! ( " {} / {} " , std ::env ::var ( " OUT_DIR " ) . unwrap ( ) , " noredownload " ) ) . unwrap ( ) ;
2023-03-20 15:20:39 +00:00
println! ( " cargo:rerun-if-changed=build.rs " ) ;
}
fn codegen_version ( bin : & [ u8 ] , fp : & str , name : & str ) {
// get version
let output = execim ( bin , & vec! [ " -version " ] ) ;
let stdout = output . stdout ;
let stdout_str = String ::from_utf8 ( stdout ) . unwrap ( ) ;
if ! stdout_str . starts_with ( " Version: " ) {
panic! ( " Binary did not have expected version output. Unable to get version info. " ) ;
}
let mut version = stdout_str . split ( ' ' ) . collect ::< Vec < & str > > ( ) [ 1 ] . to_string ( ) ;
version . pop ( ) ;
let code = format! ( " // This code was automatically @generated by build.rs. It should not be modified. \n pub const {} _BIN: &[u8] = include_bytes!(concat!(env!( \" OUT_DIR \" ), \" / {} \" )); \n pub const {} _VERSION: &str = \" {} \" ; " , name , fp , name , version ) ;
let mut file = File ::create ( format! ( " {} / {} .rs " , std ::env ::var ( " OUT_DIR " ) . unwrap ( ) , fp ) ) . unwrap ( ) ;
file . write_all ( code . as_bytes ( ) ) . unwrap ( ) ;
}
#[ cfg(not(unix)) ]
fn execim ( buf : & [ u8 ] , args : & Vec < & str > ) -> Output {
let mut file = File ::create ( " tmpexec.bin " ) . unwrap ( ) ;
file . write_all ( buf ) . unwrap ( ) ;
std ::mem ::drop ( file ) ;
let output = Command ::new ( " ./tmpexec.bin " ) . args ( args ) . output ( ) . unwrap ( ) ;
remove_file ( " ./tmpexec.bin " ) . unwrap ( ) ;
output
}
#[ cfg(unix) ]
fn execim ( buf : & [ u8 ] , args : & Vec < & str > ) -> Output {
let mut file = File ::create ( " tmpexec.bin " ) . unwrap ( ) ;
file . write_all ( buf ) . unwrap ( ) ;
let metadata = file . metadata ( ) . unwrap ( ) ;
let mut permissions = metadata . permissions ( ) ;
permissions . set_mode ( 0o0755 ) ;
fs ::set_permissions ( " ./tmpexec.bin " , permissions ) . unwrap ( ) ;
std ::mem ::drop ( file ) ;
let output = Command ::new ( " ./tmpexec.bin " ) . args ( args ) . output ( ) . unwrap ( ) ;
remove_file ( " ./tmpexec.bin " ) . unwrap ( ) ;
output
2023-03-31 03:08:55 +00:00
}