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
|
use hyper::{body::Buf, Client};
use hyperlocal::{UnixClientExt, Uri};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("hyper error: {0}")]
Hyper(#[from] hyper::Error),
#[error("json error: {0}")]
JSON(#[from] serde_json::Error),
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WhoisResponse {
#[serde(rename = "Node")]
pub node: WhoisPeer,
#[serde(rename = "UserProfile")]
pub user_profile: User,
}
pub async fn whois(ip: SocketAddr) -> Result<WhoisResponse, Error> {
let url = Uri::new(
"/var/run/tailscale/tailscaled.sock",
&format!("/localapi/v0/whois?addr={ip}"),
)
.into();
let client = Client::unix();
let resp = client.get(url).await?;
if !resp.status().is_success() {
panic!("TODO(Xe): handle {}", resp.status());
}
let body = hyper::body::aggregate(resp).await?;
Ok(serde_json::from_reader(body.reader())?)
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct User {
#[serde(rename = "ID")]
pub id: u64,
#[serde(rename = "LoginName")]
pub login_name: String,
#[serde(rename = "DisplayName")]
pub display_name: String,
#[serde(rename = "ProfilePicURL")]
pub profile_pic_url: String,
#[serde(rename = "Roles")]
pub roles: Vec<Option<serde_json::Value>>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WhoisPeer {
#[serde(rename = "ID")]
pub id: i64,
#[serde(rename = "StableID")]
pub stable_id: String,
#[serde(rename = "Name")]
pub name: String,
#[serde(rename = "User")]
pub user: i64,
#[serde(rename = "Key")]
pub key: String,
#[serde(rename = "KeyExpiry")]
pub key_expiry: String,
#[serde(rename = "Machine")]
pub machine: String,
#[serde(rename = "DiscoKey")]
pub disco_key: String,
#[serde(rename = "Addresses")]
pub addresses: Vec<String>,
#[serde(rename = "AllowedIPs")]
pub allowed_ips: Vec<String>,
#[serde(rename = "Endpoints")]
pub endpoints: Vec<String>,
#[serde(rename = "Hostinfo")]
pub hostinfo: Hostinfo,
#[serde(rename = "Created")]
pub created: String,
#[serde(rename = "PrimaryRoutes")]
pub primary_routes: Option<Vec<String>>,
#[serde(rename = "MachineAuthorized")]
pub machine_authorized: Option<bool>,
#[serde(rename = "Capabilities")]
pub capabilities: Option<Vec<String>>,
#[serde(rename = "ComputedName")]
pub computed_name: String,
#[serde(rename = "ComputedNameWithHost")]
pub computed_name_with_host: String,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Hostinfo {
#[serde(rename = "OS")]
pub os: String,
#[serde(rename = "Hostname")]
pub hostname: String,
#[serde(rename = "RoutableIPs")]
pub routable_ips: Option<Vec<String>>,
#[serde(rename = "Services")]
pub services: Vec<Service>,
}
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Service {
#[serde(rename = "Proto")]
pub proto: String,
#[serde(rename = "Port")]
pub port: i64,
#[serde(rename = "Description")]
pub description: Option<String>,
}
|