Skip to content

Commit

Permalink
Implement stub for inspect hostcall
Browse files Browse the repository at this point in the history
  • Loading branch information
ulyssa committed Aug 16, 2024
1 parent a945b34 commit 626fd99
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 2 deletions.
21 changes: 21 additions & 0 deletions crates/adapter/src/fastly/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,6 +1689,27 @@ pub mod fastly_http_req {
encodings.into(),
))
}

#[export_name = "fastly_http_req#inspect"]
pub fn inspect(
ds_req: RequestHandle,
ds_body: BodyHandle,
info_mask: crate::bindings::fastly::api::types::InspectInfoMask,
info: *mut crate::bindings::fastly::api::types::InspectInfo,
buf: *mut u8,
buf_len: usize,
nwritten_out: *mut usize,
) -> FastlyStatus {
alloc_result!(buf, buf_len, nwritten_out, {
fastly::api::http_req::inspect(
ds_req,
ds_body,
info_mask,
info,
u64::try_from(buf_len).trapping_unwrap(),
)
})
}
}

pub mod fastly_http_resp {
Expand Down
12 changes: 12 additions & 0 deletions lib/compute-at-edge-abi/compute-at-edge.witx
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,18 @@
(param $backend_configuration (@witx pointer $dynamic_backend_config))
(result $err (expected (error $fastly_status)))
)

;;; Hostcall for Fastly Compute guests to inspect request HTTP traffic
;;; using the NGWAF lookaside service.
(@interface func (export "inspect")
(param $req $request_handle)
(param $body $body_handle)
(param $insp_info_mask $inspect_info_mask)
(param $insp_info (@witx pointer $inspect_info))
(param $buf (@witx pointer (@witx char8)))
(param $buf_len (@witx usize))
(result $err (expected $num_bytes (error $fastly_status)))
)
)

(module $fastly_http_resp
Expand Down
17 changes: 17 additions & 0 deletions lib/compute-at-edge-abi/typenames.witx
Original file line number Diff line number Diff line change
Expand Up @@ -358,3 +358,20 @@
(typename $has u32)

(typename $body_length u64)

(typename $inspect_info_mask
(flags (@witx repr u32)
$reserved
$corp
$workspace
)
)

(typename $inspect_info
(record
(field $corp (@witx pointer (@witx char8)))
(field $corp_len u32)
(field $workspace (@witx pointer (@witx char8)))
(field $workspace_len u32)
)
)
35 changes: 35 additions & 0 deletions lib/src/component/http_req.rs
Original file line number Diff line number Diff line change
Expand Up @@ -850,4 +850,39 @@ impl http_req::Host for Session {
.try_into()
.expect("More than u32::MAX headers"))
}

async fn inspect(
&mut self,
ds_req: http_types::RequestHandle,
ds_body: http_types::BodyHandle,
info_mask: http_req::InspectConfigOptions,
info: http_req::InspectConfig,
buf_max_len: u64,
) -> Result<String, types::Error> {
use http_req::InspectConfigOptions as Flags;

// Make sure we're given valid handles, even though we won't use them.
let _ = self.request_parts(ds_req.into())?;
let _ = self.body(ds_body.into())?;

// For now, corp and workspace arguments are required to actually generate the hostname,
// but in the future the lookaside service will be generated using the customer ID, and
// it will be okay for them to be unspecified or empty.
if !info_mask.contains(Flags::CORP | Flags::WORKSPACE) {
return Err(Error::InvalidArgument.into());
}

if info.corp.is_empty() || info.workspace.is_empty() {
return Err(Error::InvalidArgument.into());
}

// Return the mock NGWAF response.
let ngwaf_resp = self.ngwaf_response();
let ngwaf_resp_len = ngwaf_resp.len();

match u64::try_from(ngwaf_resp_len) {
Ok(ngwaf_resp_len) if ngwaf_resp_len <= buf_max_len => Ok(ngwaf_resp),
too_large => Err(types::Error::BufferLen(too_large.unwrap_or(0))),
}
}
}
14 changes: 14 additions & 0 deletions lib/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use {
tokio::sync::oneshot::Sender,
};

const NGWAF_ALLOW_VERDICT: &str = "allow";
const REGION_NONE: &[u8] = b"none";

/// Data specific to an individual request, including any host-side
Expand Down Expand Up @@ -95,6 +96,8 @@ pub struct Session {
///
/// Populated prior to guest execution, and never modified.
device_detection: Arc<DeviceDetection>,
/// The NGWAF verdict to return when using the `inspect` hostcall.
ngwaf_verdict: String,
/// The Geolocations configured for this execution.
///
/// Populated prior to guest execution, and never modified.
Expand Down Expand Up @@ -186,6 +189,7 @@ impl Session {
backends,
device_detection,
geolocation,
ngwaf_verdict: NGWAF_ALLOW_VERDICT.to_string(),
dynamic_backends: Backends::default(),
tls_config,
dictionaries,
Expand Down Expand Up @@ -659,6 +663,16 @@ impl Session {
self.geolocation.lookup(addr).map(|data| data.to_string())
}

// ----- NGWAF Inspect API -----

/// Retrieve the compliance region that received the request for this session.
pub fn ngwaf_response(&self) -> String {
format!(
r#"{{"waf_response":200,"redirect_url":"","tags":[],"verdict":"{}","decision_ms":0}}"#,
self.ngwaf_verdict
)
}

// ----- Object Store API -----
pub fn obj_store_handle(&mut self, key: &str) -> Result<ObjectStoreHandle, Error> {
let obj_key = ObjectStoreKey::new(key);
Expand Down
62 changes: 60 additions & 2 deletions lib/src/wiggle_abi/req_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use {
types::{
BackendConfigOptions, BodyHandle, CacheOverrideTag, ClientCertVerifyResult,
ContentEncodings, DynamicBackendConfig, FramingHeadersMode, HttpVersion,
MultiValueCursor, MultiValueCursorResult, PendingRequestHandle, RequestHandle,
ResponseHandle,
InspectInfo, InspectInfoMask, MultiValueCursor, MultiValueCursorResult,
PendingRequestHandle, RequestHandle, ResponseHandle,
},
},
},
Expand Down Expand Up @@ -1008,4 +1008,62 @@ impl FastlyHttpReq for Session {

Ok(())
}

fn inspect(
&mut self,
memory: &mut GuestMemory<'_>,
ds_req: RequestHandle,
ds_body: BodyHandle,
info_mask: InspectInfoMask,
info: GuestPtr<InspectInfo>,
buf: GuestPtr<u8>,
buf_len: u32,
) -> Result<u32, Error> {
// Make sure we're given valid handles, even though we won't use them.
let _ = self.request_parts(ds_req)?;
let _ = self.body(ds_body)?;

// Make sure the InspectInfo looks good, even though we won't use it.
let info = memory.read(info)?;
let info_string_or_err = |flag, str_field: GuestPtr<u8>, len_field| {
if info_mask.contains(flag) {
if len_field == 0 {
return Err(Error::InvalidArgument);
}

let byte_vec = memory.to_vec(str_field.as_array(len_field))?;
let s = String::from_utf8(byte_vec).map_err(|_| Error::InvalidArgument)?;

Ok(s)
} else {
// For now, corp and workspace arguments are required to actually generate the hostname,
// but in the future the lookaside service will be generated using the customer ID, and
// it will be okay for them to be unspecified or empty.
Err(Error::InvalidArgument)
}
};

let _ = info_string_or_err(InspectInfoMask::CORP, info.corp, info.corp_len)?;
let _ = info_string_or_err(
InspectInfoMask::WORKSPACE,
info.workspace,
info.workspace_len,
)?;

// Return the mock NGWAF response.
let ngwaf_resp = self.ngwaf_response();
let ngwaf_resp_len = ngwaf_resp.len();

match u32::try_from(ngwaf_resp_len) {
Ok(ngwaf_resp_len) if ngwaf_resp_len <= buf_len => {
memory.copy_from_slice(ngwaf_resp.as_bytes(), buf.as_array(buf_len))?;

Ok(ngwaf_resp_len)
}
_ => Err(Error::BufferLengthError {
buf: "buf",
len: "buf_len",
}),
}
}
}
18 changes: 18 additions & 0 deletions lib/wit/deps/fastly/compute.wit
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,17 @@ interface http-req {
error: error,
}

flags inspect-config-options {
reserved,
corp,
workspace,
}

record inspect-config {
corp: string,
workspace: string,
}

cache-override-set: func(
h: request-handle,
tag: cache-override-tag,
Expand Down Expand Up @@ -463,6 +474,13 @@ result<pending-request-handle, error>;
config: dynamic-backend-config,
) -> result<_, error>;

inspect: func(
h: request-handle,
b: body-handle,
options: inspect-config-options,
info: inspect-config,
max-len: u64
) -> result<string, error>;
}

/*
Expand Down

0 comments on commit 626fd99

Please sign in to comment.