Skip to content

Commit d596e90

Browse files
authored
fix(dgw)!: clean up /jet/net/config route (#1387)
The JSON output of the `/jet/net/config` route is changed to be more convenient in the consumer code. Previous output example: ``` [ { "name": "vboxnet0", "addresses": [ { "V4": { "ip": "192.168.56.1", "broadcast": "192.168.56.255", "netmask": "255.255.255.0" } }, { "V6": { "ip": "fe80::800:27ff:fe00:0", "netmask": "ffff:ffff:ffff:ffff::" } } ], "mac_addr": "0a:00:27:00:00:00", "index": 4 } ] ``` New output example: ``` { "vboxnet0": [ { "family": "IPv4", "address": "192.168.56.1", "broadcast": "192.168.56.255", "netmask": "255.255.255.0", "mac": "0a:00:27:00:00:00" }, { "family": "IPv6", "address": "fe80::800:27ff:fe00:0", "netmask": "ffff:ffff:ffff:ffff::", "mac": "0a:00:27:00:00:00" } ] } ``` Issue: DGW-287
1 parent 185bae3 commit d596e90

File tree

2 files changed

+89
-104
lines changed

2 files changed

+89
-104
lines changed

devolutions-gateway/src/api/net.rs

Lines changed: 64 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::extract::RepeatQuery;
2-
use crate::http::HttpError;
3-
use crate::token::Protocol;
4-
use crate::DgwState;
1+
use std::collections::HashMap;
2+
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
3+
use std::time::Duration;
4+
55
use axum::extract::ws::{Message, Utf8Bytes};
66
use axum::extract::WebSocketUpgrade;
77
use axum::response::Response;
@@ -16,9 +16,11 @@ use network_scanner::ping::PingEvent;
1616
use network_scanner::port_discovery::TcpKnockEvent;
1717
use network_scanner::scanner::{self, DnsEvent, NetworkScannerParams, ScannerConfig, TcpKnockWithHost};
1818
use serde::{Deserialize, Serialize};
19-
use std::fmt;
20-
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
21-
use std::time::Duration;
19+
20+
use crate::extract::RepeatQuery;
21+
use crate::http::HttpError;
22+
use crate::token::Protocol;
23+
use crate::DgwState;
2224

2325
pub fn make_router<S>(state: DgwState) -> Router<S> {
2426
let router = Router::new()
@@ -469,109 +471,77 @@ impl EventFilter {
469471
tag = "Net",
470472
path = "/jet/net/config",
471473
responses(
472-
(status = 200, description = "Network interfaces", body = [Vec<NetworkInterface>]),
474+
(status = 200, description = "Network interfaces", body = [HashMap<String, Vec<InterfaceInfo>>]),
473475
(status = 400, description = "Bad request"),
474476
(status = 401, description = "Invalid or missing authorization token"),
475477
(status = 403, description = "Insufficient permissions"),
476478
(status = 500, description = "Unexpected server error"),
477479
),
478480
security(("netscan_token" = [])),
479481
))]
480-
pub async fn get_net_config(_token: crate::extract::NetScanToken) -> Result<Json<Vec<NetworkInterface>>, HttpError> {
481-
let interfaces = interfaces::get_network_interfaces()
482-
.map_err(HttpError::internal().with_msg("failed to get network interfaces").err())?
483-
.into_iter()
484-
.map(NetworkInterface::from)
485-
.collect();
486-
487-
Ok(Json(interfaces))
488-
}
482+
pub(crate) async fn get_net_config(
483+
_token: crate::extract::NetScanToken,
484+
) -> Result<Json<HashMap<String, Vec<InterfaceInfo>>>, HttpError> {
485+
let net_ifaces = interfaces::get_network_interfaces()
486+
.map_err(HttpError::internal().with_msg("failed to get network interfaces").err())?;
489487

490-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
491-
#[derive(Debug, Clone, Serialize)]
492-
pub struct NetworkInterface {
493-
pub name: String,
494-
#[serde(rename = "addresses")]
495-
pub addrs: Vec<Addr>,
496-
#[serde(skip_serializing_if = "Option::is_none")]
497-
pub mac_addr: Option<String>,
498-
pub index: u32,
499-
}
488+
let mut interface_map = HashMap::new();
500489

501-
/// Network interface address
502-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
503-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
504-
pub enum Addr {
505-
V4(V4IfAddr),
506-
V6(V6IfAddr),
507-
}
508-
509-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
510-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
511-
pub struct Netmask<T>(pub T);
490+
for iface in net_ifaces {
491+
let addresses: Vec<InterfaceInfo> = iface
492+
.addr
493+
.into_iter()
494+
.map(|addr| match addr {
495+
interfaces::Addr::V4(addr) => InterfaceInfo {
496+
address: IfAddress::V4 {
497+
address: addr.ip,
498+
broadcast: addr.broadcast,
499+
netmask: addr.netmask,
500+
},
501+
mac: iface.mac_addr.clone(),
502+
},
503+
interfaces::Addr::V6(addr) => InterfaceInfo {
504+
address: IfAddress::V6 {
505+
address: addr.ip,
506+
broadcast: addr.broadcast,
507+
netmask: addr.netmask,
508+
},
509+
mac: iface.mac_addr.clone(),
510+
},
511+
})
512+
.collect();
512513

513-
impl<T: fmt::Display> fmt::Display for Netmask<T> {
514-
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
515-
write!(f, "{}", self.0)
514+
interface_map.insert(iface.name, addresses);
516515
}
517-
}
518516

519-
impl<T> Serialize for Netmask<T>
520-
where
521-
T: Serialize,
522-
{
523-
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
524-
where
525-
S: serde::Serializer,
526-
{
527-
self.0.serialize(serializer)
528-
}
517+
Ok(Json(interface_map))
529518
}
530519

531-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
532-
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
533-
pub struct V4IfAddr {
534-
pub ip: Ipv4Addr,
535-
#[serde(skip_serializing_if = "Option::is_none")]
536-
pub broadcast: Option<Ipv4Addr>,
520+
#[derive(Debug, Clone, Serialize)]
521+
pub(crate) struct InterfaceInfo {
522+
#[serde(flatten)]
523+
address: IfAddress,
537524
#[serde(skip_serializing_if = "Option::is_none")]
538-
pub netmask: Option<Netmask<Ipv4Addr>>,
525+
mac: Option<String>,
539526
}
540527

541-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
542528
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
543-
pub struct V6IfAddr {
544-
pub ip: Ipv6Addr,
545-
#[serde(skip_serializing_if = "Option::is_none")]
546-
pub broadcast: Option<Ipv6Addr>,
547-
#[serde(skip_serializing_if = "Option::is_none")]
548-
pub netmask: Option<Netmask<Ipv6Addr>>,
549-
}
550-
551-
impl From<interfaces::NetworkInterface> for NetworkInterface {
552-
fn from(iface: interfaces::NetworkInterface) -> Self {
553-
let addr = iface
554-
.addr
555-
.into_iter()
556-
.map(|addr| match addr {
557-
interfaces::Addr::V4(v4) => Addr::V4(V4IfAddr {
558-
ip: v4.ip,
559-
broadcast: v4.broadcast,
560-
netmask: v4.netmask.map(Netmask),
561-
}),
562-
interfaces::Addr::V6(v6) => Addr::V6(V6IfAddr {
563-
ip: v6.ip,
564-
broadcast: v6.broadcast,
565-
netmask: v6.netmask.map(Netmask),
566-
}),
567-
})
568-
.collect();
569-
570-
NetworkInterface {
571-
name: iface.name,
572-
mac_addr: iface.mac_addr,
573-
addrs: addr,
574-
index: iface.index,
575-
}
576-
}
529+
#[serde(tag = "family")]
530+
enum IfAddress {
531+
#[serde(rename = "IPv4")]
532+
V4 {
533+
address: Ipv4Addr,
534+
#[serde(skip_serializing_if = "Option::is_none")]
535+
broadcast: Option<Ipv4Addr>,
536+
#[serde(skip_serializing_if = "Option::is_none")]
537+
netmask: Option<Ipv4Addr>,
538+
},
539+
#[serde(rename = "IPv6")]
540+
V6 {
541+
address: Ipv6Addr,
542+
#[serde(skip_serializing_if = "Option::is_none")]
543+
broadcast: Option<Ipv6Addr>,
544+
#[serde(skip_serializing_if = "Option::is_none")]
545+
netmask: Option<Ipv6Addr>,
546+
},
577547
}

devolutions-gateway/src/openapi.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::api::preflight::PreflightAlertStatus;
2626
crate::api::webapp::sign_session_token,
2727
crate::api::update::trigger_update_check,
2828
crate::api::preflight::post_preflight,
29-
// crate::api::net::get_net_config,
29+
crate::api::net::get_net_config,
3030
),
3131
components(schemas(
3232
crate::api::health::Identity,
@@ -54,9 +54,10 @@ use crate::api::preflight::PreflightAlertStatus;
5454
PreflightOutput,
5555
PreflightOutputKind,
5656
PreflightAlertStatus,
57-
// crate::api::net::NetworkInterface,
5857
SessionTokenContentType,
5958
SessionTokenSignRequest,
59+
InterfaceInfo,
60+
AddressFamily,
6061
)),
6162
modifiers(&SecurityAddon),
6263
)]
@@ -347,8 +348,7 @@ enum PreflightOperationKind {
347348
}
348349

349350
#[allow(unused)]
350-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
351-
#[derive(Deserialize)]
351+
#[derive(Deserialize, utoipa::ToSchema)]
352352
struct AppCredential {
353353
/// The kind of credentials.
354354
kind: AppCredentialKind,
@@ -362,15 +362,13 @@ struct AppCredential {
362362
password: Option<String>,
363363
}
364364

365-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
366-
#[derive(Deserialize)]
365+
#[derive(Deserialize, utoipa::ToSchema)]
367366
enum AppCredentialKind {
368367
#[serde(rename = "username-password")]
369368
UsernamePassword,
370369
}
371370

372-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
373-
#[derive(Serialize)]
371+
#[derive(Serialize, utoipa::ToSchema)]
374372
pub(crate) struct PreflightOutput {
375373
/// The ID of the preflight operation associated to this result.
376374
operation_id: Uuid,
@@ -419,8 +417,7 @@ pub(crate) struct PreflightOutput {
419417
}
420418

421419
#[allow(unused)]
422-
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
423-
#[derive(Serialize)]
420+
#[derive(Serialize, utoipa::ToSchema)]
424421
pub(crate) enum PreflightOutputKind {
425422
#[serde(rename = "version")]
426423
Version,
@@ -435,3 +432,21 @@ pub(crate) enum PreflightOutputKind {
435432
#[serde(rename = "alert")]
436433
Alert,
437434
}
435+
436+
#[derive(Serialize, utoipa::ToSchema)]
437+
struct InterfaceInfo {
438+
family: AddressFamily,
439+
address: String,
440+
broadcast: Option<String>,
441+
netmask: Option<String>,
442+
mac: Option<String>,
443+
}
444+
445+
#[allow(unused)]
446+
#[derive(Serialize, utoipa::ToSchema)]
447+
enum AddressFamily {
448+
#[serde(rename = "IPv4")]
449+
V4,
450+
#[serde(rename = "IPv6")]
451+
V6,
452+
}

0 commit comments

Comments
 (0)