tfclient enrollment successful
This commit is contained in:
parent
8a607733a3
commit
b291d47459
|
@ -219,9 +219,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.23"
|
version = "0.4.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f"
|
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
|
@ -1856,9 +1856,9 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.14"
|
version = "0.11.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9"
|
checksum = "27b71749df584b7f4cac2c426c127a7c785a5106cc98f7a8feb044115f0fa254"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -2420,6 +2420,8 @@ dependencies = [
|
||||||
name = "tfclient"
|
name = "tfclient"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.21.0",
|
||||||
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"dirs 5.0.0",
|
"dirs 5.0.0",
|
||||||
|
|
|
@ -7,6 +7,9 @@ Connection: close
|
||||||
|
|
||||||
{"code":"xM22QsIzd4F0nLDTbh86RCSYwelfU_Hshqt-7u4yy_Y","dhPubkey":"LS0tLS1CRUdJTiBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQpqZW9aaDZZYUNNSHZKK04zWGRlQ1hCbHo3dm5saTBjL1NlQ1hVR3lYbEIwPQotLS0tLUVORCBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQo=","edPubkey":"LS0tLS1CRUdJTiBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0KWHE0RG9mUGJoQzBubjc4VEhRWUxhNC83V1Ixei9iU1kzSm9pRzNRZ1VMcz0KLS0tLS1FTkQgTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCg==","timestamp":"2023-02-01T13:24:56.380006369-05:00"}
|
{"code":"xM22QsIzd4F0nLDTbh86RCSYwelfU_Hshqt-7u4yy_Y","dhPubkey":"LS0tLS1CRUdJTiBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQpqZW9aaDZZYUNNSHZKK04zWGRlQ1hCbHo3dm5saTBjL1NlQ1hVR3lYbEIwPQotLS0tLUVORCBORUJVTEEgWDI1NTE5IFBVQkxJQyBLRVktLS0tLQo=","edPubkey":"LS0tLS1CRUdJTiBORUJVTEEgRUQyNTUxOSBQVUJMSUMgS0VZLS0tLS0KWHE0RG9mUGJoQzBubjc4VEhRWUxhNC83V1Ixei9iU1kzSm9pRzNRZ1VMcz0KLS0tLS1FTkQgTkVCVUxBIEVEMjU1MTkgUFVCTElDIEtFWS0tLS0tCg==","timestamp":"2023-02-01T13:24:56.380006369-05:00"}
|
||||||
|
|
||||||
|
2023-02-01T13:24:56.380006369-05:00
|
||||||
|
%Y-%m-%dT%H:%M:%S.%f-%:z
|
||||||
|
|
||||||
HTTP/2 200 OK
|
HTTP/2 200 OK
|
||||||
Cache-Control: no-store
|
Cache-Control: no-store
|
||||||
Content-Security-Policy: default-src 'none'
|
Content-Security-Policy: default-src 'none'
|
||||||
|
|
|
@ -19,6 +19,9 @@ toml = "0.7.3"
|
||||||
serde = { version = "1.0.158", features = ["derive"] }
|
serde = { version = "1.0.158", features = ["derive"] }
|
||||||
serde_json = "1.0.94"
|
serde_json = "1.0.94"
|
||||||
ctrlc = "3.2.5"
|
ctrlc = "3.2.5"
|
||||||
|
reqwest = { version = "0.11.16", features = ["blocking"] }
|
||||||
|
base64 = "0.21.0"
|
||||||
|
chrono = "0.4.24"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde = { version = "1.0.157", features = ["derive"] }
|
serde = { version = "1.0.157", features = ["derive"] }
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use log::trace;
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollRequest {
|
||||||
|
pub code: String,
|
||||||
|
#[serde(rename = "dhPubkey")]
|
||||||
|
pub dh_pubkey: String,
|
||||||
|
#[serde(rename = "edPubkey")]
|
||||||
|
pub ed_pubkey: String,
|
||||||
|
pub timestamp: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollResponseMetadata {}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollResponseOrganization {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollResponseData {
|
||||||
|
pub config: String,
|
||||||
|
#[serde(rename = "hostID")]
|
||||||
|
pub host_id: String,
|
||||||
|
pub counter: i64,
|
||||||
|
#[serde(rename = "trustedKeys")]
|
||||||
|
pub trusted_keys: String,
|
||||||
|
pub organization: EnrollResponseOrganization,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollResponse {
|
||||||
|
pub data: EnrollResponseData,
|
||||||
|
pub metadata: EnrollResponseMetadata,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum APIResponse {
|
||||||
|
Error(EnrollError),
|
||||||
|
Success(EnrollResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollError {
|
||||||
|
pub errors: Vec<EnrollErrorSingular>
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct EnrollErrorSingular {
|
||||||
|
pub code: String,
|
||||||
|
pub message: String
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enroll(server: &Url, request: &EnrollRequest) -> Result<APIResponse, Box<dyn Error>> {
|
||||||
|
let endpoint = server.join("/v2/enroll")?;
|
||||||
|
let client = Client::new();
|
||||||
|
|
||||||
|
let text = serde_json::to_string(request)?;
|
||||||
|
|
||||||
|
trace!("sending enroll: {}", text);
|
||||||
|
|
||||||
|
let resp = client.post(endpoint).body(text).send()?;
|
||||||
|
Ok(resp.json()?)
|
||||||
|
}
|
|
@ -1,16 +1,24 @@
|
||||||
use std::sync::mpsc::{Receiver, TryRecvError};
|
use std::sync::mpsc::{Receiver, TryRecvError};
|
||||||
use log::{error, info};
|
use base64::Engine;
|
||||||
|
use chrono::Local;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use url::Url;
|
||||||
|
use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public};
|
||||||
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
||||||
use trifid_pki::rand_core::OsRng;
|
use trifid_pki::rand_core::OsRng;
|
||||||
use trifid_pki::x25519_dalek::StaticSecret;
|
use trifid_pki::x25519_dalek::StaticSecret;
|
||||||
|
use crate::api::{APIResponse, enroll, EnrollRequest};
|
||||||
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
||||||
use crate::daemon::ThreadMessageSender;
|
use crate::daemon::ThreadMessageSender;
|
||||||
|
|
||||||
pub enum APIWorkerMessage {
|
pub enum APIWorkerMessage {
|
||||||
Shutdown
|
Shutdown,
|
||||||
|
Enroll { code: String }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn apiworker_main(config: TFClientConfig, instance: String, _transmitters: ThreadMessageSender, rx: Receiver<APIWorkerMessage>) {
|
pub fn apiworker_main(config: TFClientConfig, instance: String, url: String, _transmitters: ThreadMessageSender, rx: Receiver<APIWorkerMessage>) {
|
||||||
|
let server = Url::parse(&url).unwrap();
|
||||||
|
|
||||||
// Generate dhPubkey and edPubkey if it doesn't exist
|
// Generate dhPubkey and edPubkey if it doesn't exist
|
||||||
// Load vardata
|
// Load vardata
|
||||||
let mut vdata = match load_cdata(&instance) {
|
let mut vdata = match load_cdata(&instance) {
|
||||||
|
@ -58,6 +66,65 @@ pub fn apiworker_main(config: TFClientConfig, instance: String, _transmitters: T
|
||||||
APIWorkerMessage::Shutdown => {
|
APIWorkerMessage::Shutdown => {
|
||||||
info!("recv on command socket: shutdown, stopping");
|
info!("recv on command socket: shutdown, stopping");
|
||||||
break;
|
break;
|
||||||
|
},
|
||||||
|
APIWorkerMessage::Enroll { code } => {
|
||||||
|
info!("recv on command socket: enroll {}", code);
|
||||||
|
let mut cdata = match load_cdata(&instance) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
error!("error in api worker thread: {}", e);
|
||||||
|
error!("APIWorker exiting with error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if cdata.host_id.is_some() {
|
||||||
|
warn!("enrollment failed: already enrolled");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let dh_encoded = base64::engine::general_purpose::STANDARD.encode(serialize_x25519_public(&dh_key.to_bytes()));
|
||||||
|
let ed_encoded = base64::engine::general_purpose::STANDARD.encode(serialize_ed25519_public(&ed_key.to_bytes()));
|
||||||
|
let req = EnrollRequest {
|
||||||
|
code,
|
||||||
|
dh_pubkey: dh_encoded,
|
||||||
|
ed_pubkey: ed_encoded,
|
||||||
|
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
|
||||||
|
};
|
||||||
|
let res = match enroll(&server, &req) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(e) => {
|
||||||
|
error!("error in api worker thread: {}", e);
|
||||||
|
error!("APIWorker exiting with error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let resp = match res {
|
||||||
|
APIResponse::Error(e) => {
|
||||||
|
error!("error with enrollment: {}: {}", e.errors[0].code, e.errors[0].message);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
APIResponse::Success(resp) => resp
|
||||||
|
};
|
||||||
|
|
||||||
|
info!("Enrolled with server. Host-ID {} config count {}", resp.data.host_id, resp.data.counter);
|
||||||
|
info!("NebulaCAPool {}, org {} {}", resp.data.trusted_keys, resp.data.organization.name, resp.data.organization.id);
|
||||||
|
info!("Config: {}", resp.data.config);
|
||||||
|
|
||||||
|
cdata.host_id = Some(resp.data.host_id);
|
||||||
|
cdata.counter = resp.data.counter as i32;
|
||||||
|
cdata.ca_pool = Some(resp.data.trusted_keys);
|
||||||
|
cdata.org_name = Some(resp.data.organization.name);
|
||||||
|
cdata.org_id = Some(resp.data.organization.id);
|
||||||
|
cdata.config = Some(resp.data.config);
|
||||||
|
|
||||||
|
// Save vardata
|
||||||
|
match save_cdata(&instance, cdata) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error saving cdata: {}", e);
|
||||||
|
error!("APIWorker exiting with error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -17,7 +17,12 @@ pub struct TFClientConfig {
|
||||||
pub struct TFClientData {
|
pub struct TFClientData {
|
||||||
pub host_id: Option<String>,
|
pub host_id: Option<String>,
|
||||||
pub ed_privkey: Option<[u8; 32]>,
|
pub ed_privkey: Option<[u8; 32]>,
|
||||||
pub dh_privkey: Option<[u8; 32]>
|
pub dh_privkey: Option<[u8; 32]>,
|
||||||
|
pub counter: i32,
|
||||||
|
pub ca_pool: Option<String>,
|
||||||
|
pub org_id: Option<String>,
|
||||||
|
pub org_name: Option<String>,
|
||||||
|
pub config: Option<String>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_config(instance: &str) -> Result<(), Box<dyn Error>> {
|
pub fn create_config(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
@ -52,7 +57,7 @@ pub fn create_cdata(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||||
info!("Creating data directory...");
|
info!("Creating data directory...");
|
||||||
fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?;
|
fs::create_dir_all(get_cdata_dir(instance).ok_or("Unable to load data dir")?)?;
|
||||||
info!("Copying default data file to config directory...");
|
info!("Copying default data file to config directory...");
|
||||||
let config = TFClientData { host_id: None, ed_privkey: None, dh_privkey: None };
|
let config = TFClientData { host_id: None, ed_privkey: None, dh_privkey: None, counter: 0, ca_pool: None, org_id: None, org_name: None, config: None };
|
||||||
let config_str = toml::to_string(&config)?;
|
let config_str = toml::to_string(&config)?;
|
||||||
fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?;
|
fs::write(get_cdata_file(instance).ok_or("Unable to load data dir")?, config_str)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -74,8 +74,9 @@ pub fn daemon_main(name: String, server: String) {
|
||||||
let config_api = config.clone();
|
let config_api = config.clone();
|
||||||
let transmitter_api = transmitter.clone();
|
let transmitter_api = transmitter.clone();
|
||||||
let name_api = name.clone();
|
let name_api = name.clone();
|
||||||
|
let server_api = server.clone();
|
||||||
let api_thread = thread::spawn(move || {
|
let api_thread = thread::spawn(move || {
|
||||||
apiworker_main(config_api, name_api, transmitter_api, rx_api);
|
apiworker_main(config_api, name_api, server_api,transmitter_api, rx_api);
|
||||||
});
|
});
|
||||||
|
|
||||||
info!("Starting Nebula thread...");
|
info!("Starting Nebula thread...");
|
||||||
|
|
|
@ -23,6 +23,8 @@ pub mod config;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod apiworker;
|
pub mod apiworker;
|
||||||
pub mod socketworker;
|
pub mod socketworker;
|
||||||
|
pub mod api;
|
||||||
|
pub mod socketclient;
|
||||||
|
|
||||||
pub mod nebula_bin {
|
pub mod nebula_bin {
|
||||||
include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs"));
|
include!(concat!(env!("OUT_DIR"), "/nebula.bin.rs"));
|
||||||
|
@ -37,6 +39,7 @@ use std::fs;
|
||||||
use clap::{Parser, ArgAction, Subcommand};
|
use clap::{Parser, ArgAction, Subcommand};
|
||||||
use log::{error, info};
|
use log::{error, info};
|
||||||
use simple_logger::SimpleLogger;
|
use simple_logger::SimpleLogger;
|
||||||
|
use crate::config::load_config;
|
||||||
use crate::dirs::get_data_dir;
|
use crate::dirs::get_data_dir;
|
||||||
use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert};
|
use crate::embedded_nebula::{run_embedded_nebula, run_embedded_nebula_cert};
|
||||||
use crate::service::entry::{cli_install, cli_start, cli_stop, cli_uninstall};
|
use crate::service::entry::{cli_install, cli_start, cli_stop, cli_uninstall};
|
||||||
|
@ -228,7 +231,23 @@ fn main() {
|
||||||
Commands::Run { name, server } => {
|
Commands::Run { name, server } => {
|
||||||
daemon::daemon_main(name, server);
|
daemon::daemon_main(name, server);
|
||||||
}
|
}
|
||||||
Commands::Enroll { .. } => {}
|
Commands::Enroll { name, code } => {
|
||||||
|
info!("Loading config...");
|
||||||
|
let config = match load_config(&name) {
|
||||||
|
Ok(cfg) => cfg,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error loading configuration: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match socketclient::enroll(&code, &config) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error sending enrollment request: {}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
use std::error::Error;
|
||||||
|
use std::io::{BufRead, BufReader, BufWriter, Write};
|
||||||
|
use std::net::{IpAddr, SocketAddr, TcpStream};
|
||||||
|
use log::{error, info};
|
||||||
|
use crate::config::TFClientConfig;
|
||||||
|
use crate::socketworker::{ctob, DisconnectReason, JSON_API_VERSION, JsonMessage};
|
||||||
|
|
||||||
|
pub fn enroll(code: &str, config: &TFClientConfig) -> Result<(), Box<dyn Error>> {
|
||||||
|
info!("Connecting to local command socket...");
|
||||||
|
let mut stream = TcpStream::connect(SocketAddr::new(IpAddr::from([127, 0, 0, 1]), config.listen_port))?;
|
||||||
|
let mut stream2 = stream.try_clone()?;
|
||||||
|
let mut reader = BufReader::new(&stream2);
|
||||||
|
|
||||||
|
info!("Sending Hello...");
|
||||||
|
stream.write_all(&ctob(JsonMessage::Hello {
|
||||||
|
version: JSON_API_VERSION,
|
||||||
|
}))?;
|
||||||
|
info!("Waiting for hello...");
|
||||||
|
let msg = read_msg(&mut reader)?;
|
||||||
|
match msg {
|
||||||
|
JsonMessage::Hello { .. } => {
|
||||||
|
info!("Server sent hello, connection established")
|
||||||
|
}
|
||||||
|
JsonMessage::Goodbye { reason } => {
|
||||||
|
error!("Disconnected by server. Reason: {:?}", reason);
|
||||||
|
return Err("Disconnected by server".into());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
error!("Server returned unexpected message: {:?}", msg);
|
||||||
|
error!("Sending goodbye and exiting");
|
||||||
|
stream.write_all(&ctob(JsonMessage::Goodbye {
|
||||||
|
reason: DisconnectReason::UnexpectedMessageType,
|
||||||
|
}))?;
|
||||||
|
return Err("Unexpected message type by server".into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Sending enroll request...");
|
||||||
|
stream.write_all(&ctob(JsonMessage::Enroll {
|
||||||
|
code: code.to_string(),
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
info!("Sending disconnect...");
|
||||||
|
stream.write_all(&ctob(JsonMessage::Goodbye {
|
||||||
|
reason: DisconnectReason::Done,
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
info!("Sent enroll request to tfclient daemon. Check logs to see if the enrollment was successful.");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_msg(reader: &mut BufReader<&TcpStream>) -> Result<JsonMessage, Box<dyn Error>> {
|
||||||
|
let mut str = String::new();
|
||||||
|
reader.read_line(&mut str)?;
|
||||||
|
let msg: JsonMessage = serde_json::from_str(&str)?;
|
||||||
|
Ok(msg)
|
||||||
|
}
|
|
@ -228,6 +228,12 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
|
||||||
has_id: data.host_id.is_some(),
|
has_id: data.host_id.is_some(),
|
||||||
id: data.host_id
|
id: data.host_id
|
||||||
}))?;
|
}))?;
|
||||||
|
},
|
||||||
|
|
||||||
|
JsonMessage::Enroll { code } => {
|
||||||
|
info!("Client sent enroll with code {}", code);
|
||||||
|
info!("Sending enroll request to apiworker");
|
||||||
|
transmitter.api_thread.send(APIWorkerMessage::Enroll { code }).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -242,7 +248,7 @@ fn senthello_handle(client: &mut Client, transmitter: &ThreadMessageSender, comm
|
||||||
Ok(should_disconnect)
|
Ok(should_disconnect)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ctob(command: JsonMessage) -> Vec<u8> {
|
pub fn ctob(command: JsonMessage) -> Vec<u8> {
|
||||||
let command_str = serde_json::to_string(&command).unwrap() + "\n";
|
let command_str = serde_json::to_string(&command).unwrap() + "\n";
|
||||||
command_str.into_bytes()
|
command_str.into_bytes()
|
||||||
}
|
}
|
||||||
|
@ -256,7 +262,7 @@ pub const JSON_API_VERSION: i32 = 1;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(tag = "method")]
|
#[serde(tag = "method")]
|
||||||
enum JsonMessage {
|
pub enum JsonMessage {
|
||||||
#[serde(rename = "hello")]
|
#[serde(rename = "hello")]
|
||||||
Hello {
|
Hello {
|
||||||
version: i32
|
version: i32
|
||||||
|
@ -273,12 +279,16 @@ enum JsonMessage {
|
||||||
HostID {
|
HostID {
|
||||||
has_id: bool,
|
has_id: bool,
|
||||||
id: Option<String>
|
id: Option<String>
|
||||||
|
},
|
||||||
|
#[serde(rename = "enroll")]
|
||||||
|
Enroll {
|
||||||
|
code: String
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
enum DisconnectReason {
|
pub enum DisconnectReason {
|
||||||
#[serde(rename = "unsupported_version")]
|
#[serde(rename = "unsupported_version")]
|
||||||
UnsupportedVersion { expected: i32, got: i32 },
|
UnsupportedVersion { expected: i32, got: i32 },
|
||||||
#[serde(rename = "unexpected_message_type")]
|
#[serde(rename = "unexpected_message_type")]
|
||||||
|
|
Loading…
Reference in New Issue