From 9c6b5821ac04799697c163cb296d9734b4354979 Mon Sep 17 00:00:00 2001 From: danielbotros Date: Tue, 28 Apr 2026 13:44:27 -0400 Subject: [PATCH 1/3] [RSDK-13658] Plumb force_relay/force_p2p/turn_uri through C FFI Exposes the DialBuilder methods added in #170 (force_relay, force_p2p, turn_uri) through viam_dial so non-Rust SDKs can drive ICE relay/P2P testing and TURN-server filtering. The deprecated `dial` keeps its 7-arg ABI by trampolining into viam_dial with `false, false, NULL` defaults. BREAKING (C ABI): viam_dial now takes 10 args instead of 7. Any non-Rust consumer linking against viam_dial must rebuild against the new header. Consumers still using the deprecated `dial` are unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/ffi/dial_ffi.rs | 61 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/src/ffi/dial_ffi.rs b/src/ffi/dial_ffi.rs index 762b8e5..13dd2cc 100644 --- a/src/ffi/dial_ffi.rs +++ b/src/ffi/dial_ffi.rs @@ -81,14 +81,16 @@ fn dial_without_cred( uri: String, allow_insec: bool, disable_webrtc: bool, + force_relay: bool, + force_p2p: bool, + turn_uri: Option, ) -> Result> { let c = DialOptions::builder().uri(&uri).without_credentials(); - let c = if disable_webrtc { - c.disable_webrtc() - } else { - c - }; + let c = if disable_webrtc { c.disable_webrtc() } else { c }; let c = if allow_insec { c.allow_downgrade() } else { c }; + let c = if force_relay { c.force_relay() } else { c }; + let c = if force_p2p { c.force_p2p() } else { c }; + let c = if let Some(u) = turn_uri { c.turn_uri(u) } else { c }; Ok(c) } @@ -99,15 +101,17 @@ fn dial_with_cred( payload: &str, allow_insec: bool, disable_webrtc: bool, + force_relay: bool, + force_p2p: bool, + turn_uri: Option, ) -> Result> { let creds = RPCCredentials::new(entity, String::from(r#type), String::from(payload)); let c = DialOptions::builder().uri(&uri).with_credentials(creds); - let c = if disable_webrtc { - c.disable_webrtc() - } else { - c - }; + let c = if disable_webrtc { c.disable_webrtc() } else { c }; let c = if allow_insec { c.allow_downgrade() } else { c }; + let c = if force_relay { c.force_relay() } else { c }; + let c = if force_p2p { c.force_p2p() } else { c }; + let c = if let Some(u) = turn_uri { c.turn_uri(u) } else { c }; Ok(c) } @@ -123,6 +127,10 @@ fn dial_with_cred( /// * `c_payload` a C-style string that is the robot's secret, set to NULL if you don't need authentication /// * `c_allow_insecure` a bool, set to true when allowing insecure connection to your robot /// * `c_timeout` a float, set how many seconds we should try to dial before timing out +/// * `c_force_relay` a bool, set to true to force ICE relay-only policy (TURN candidates only) +/// * `c_force_p2p` a bool, set to true to strip TURN servers and force host/reflexive candidates only +/// * `c_turn_uri` a C-style string with a TURN URI filter (e.g. "turns::443?transport=tcp"); set to +/// NULL to use all TURN servers. An empty host matches any TURN provider. /// * `rt_ptr` a pointer to a rust runtime previously obtained with init_rust_runtime #[no_mangle] pub unsafe extern "C" fn viam_dial( @@ -132,6 +140,9 @@ pub unsafe extern "C" fn viam_dial( c_payload: *const c_char, c_allow_insec: bool, c_timeout: f32, + c_force_relay: bool, + c_force_p2p: bool, + c_turn_uri: *const c_char, rt_ptr: Option<&mut DialFfi>, ) -> *mut c_char { let uri = { @@ -215,6 +226,20 @@ pub unsafe extern "C" fn viam_dial( }; let timeout_duration = Duration::from_secs_f32(c_timeout); + let turn_uri_opt = { + match c_turn_uri.is_null() { + true => None, + false => match CStr::from_ptr(c_turn_uri).to_str() { + Ok(s) if !s.is_empty() => Some(s.to_string()), + Ok(_) => None, + Err(e) => { + log::error!("Error parsing turn_uri string: {:?}", e); + return ptr::null_mut(); + } + }, + } + }; + let (server, channel) = match runtime.block_on(async move { let channel = match (r#type, payload) { (Some(t), Some(p)) => { @@ -227,6 +252,9 @@ pub unsafe extern "C" fn viam_dial( p.to_str()?, allow_insec, disable_webrtc, + c_force_relay, + c_force_p2p, + turn_uri_opt, )? .connect(), ) @@ -235,7 +263,15 @@ pub unsafe extern "C" fn viam_dial( (None, None) => { timeout( timeout_duration, - dial_without_cred(uri_str, allow_insec, disable_webrtc)?.connect(), + dial_without_cred( + uri_str, + allow_insec, + disable_webrtc, + c_force_relay, + c_force_p2p, + turn_uri_opt, + )? + .connect(), ) .await? } @@ -297,6 +333,9 @@ pub unsafe extern "C" fn dial( c_payload, c_allow_insec, c_timeout, + false, + false, + ptr::null(), rt_ptr, ) } From 3107b20a5233bb01e2303e38bc899bd0ce8eee98 Mon Sep 17 00:00:00 2001 From: danielbotros Date: Mon, 4 May 2026 13:33:15 -0400 Subject: [PATCH 2/3] Update comment --- src/ffi/dial_ffi.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ffi/dial_ffi.rs b/src/ffi/dial_ffi.rs index 13dd2cc..6303ce2 100644 --- a/src/ffi/dial_ffi.rs +++ b/src/ffi/dial_ffi.rs @@ -129,8 +129,9 @@ fn dial_with_cred( /// * `c_timeout` a float, set how many seconds we should try to dial before timing out /// * `c_force_relay` a bool, set to true to force ICE relay-only policy (TURN candidates only) /// * `c_force_p2p` a bool, set to true to strip TURN servers and force host/reflexive candidates only -/// * `c_turn_uri` a C-style string with a TURN URI filter (e.g. "turns::443?transport=tcp"); set to -/// NULL to use all TURN servers. An empty host matches any TURN provider. +/// * `c_turn_uri` a C-style string with a TURN URI filter (e.g. "turn:turn.viam.com:443"); set to +/// NULL or empty to use all TURN servers. The filter matches TURN URLs by scheme, host, port, +/// and transport (transport defaults to "udp" if unspecified). /// * `rt_ptr` a pointer to a rust runtime previously obtained with init_rust_runtime #[no_mangle] pub unsafe extern "C" fn viam_dial( From e1fe931b312cd383ffcd8e9d82df1ac4e5f7f560 Mon Sep 17 00:00:00 2001 From: danielbotros Date: Tue, 5 May 2026 14:03:08 -0400 Subject: [PATCH 3/3] Generate backward compat viam_dial header for cpp --- cbindgen.toml | 20 ++++++++++++++++++++ src/ffi/dial_ffi.rs | 10 +++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/cbindgen.toml b/cbindgen.toml index f84afca..ad043e0 100644 --- a/cbindgen.toml +++ b/cbindgen.toml @@ -7,6 +7,26 @@ pragma_once = true autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */" cpp_compat = true +# C++-only re-declaration that adds default arguments to viam_dial's trailing +# params, letting C++ callers (e.g. the C++ SDK) keep using the original 7-arg +# signature. C callers must pass all arguments. Keep this parameter list in +# sync with `viam_dial` in src/ffi/dial_ffi.rs — drift will surface as a C++ +# redeclaration mismatch at the caller's compile time. +trailer = ''' +#ifdef __cplusplus +extern "C" char *viam_dial(const char *c_uri, + const char *c_entity, + const char *c_type, + const char *c_payload, + bool c_allow_insec, + float c_timeout, + struct viam_dial_ffi *rt_ptr, + bool c_force_relay = false, + bool c_force_p2p = false, + const char *c_turn_uri = nullptr); +#endif +''' + ############################ Code Style Options ################################ diff --git a/src/ffi/dial_ffi.rs b/src/ffi/dial_ffi.rs index 6303ce2..513ed07 100644 --- a/src/ffi/dial_ffi.rs +++ b/src/ffi/dial_ffi.rs @@ -127,12 +127,16 @@ fn dial_with_cred( /// * `c_payload` a C-style string that is the robot's secret, set to NULL if you don't need authentication /// * `c_allow_insecure` a bool, set to true when allowing insecure connection to your robot /// * `c_timeout` a float, set how many seconds we should try to dial before timing out +/// * `rt_ptr` a pointer to a rust runtime previously obtained with init_rust_runtime +// +// NOTE: C++ SDK's existing 7-arg call site of viam_dial relies on `rt_ptr` keeping its original trailing position; the following +// args are appended so cbindgen.toml's C++ default-argument trailer can fill them in for 7-arg callers. +// /// * `c_force_relay` a bool, set to true to force ICE relay-only policy (TURN candidates only) /// * `c_force_p2p` a bool, set to true to strip TURN servers and force host/reflexive candidates only /// * `c_turn_uri` a C-style string with a TURN URI filter (e.g. "turn:turn.viam.com:443"); set to /// NULL or empty to use all TURN servers. The filter matches TURN URLs by scheme, host, port, /// and transport (transport defaults to "udp" if unspecified). -/// * `rt_ptr` a pointer to a rust runtime previously obtained with init_rust_runtime #[no_mangle] pub unsafe extern "C" fn viam_dial( c_uri: *const c_char, @@ -141,10 +145,10 @@ pub unsafe extern "C" fn viam_dial( c_payload: *const c_char, c_allow_insec: bool, c_timeout: f32, + rt_ptr: Option<&mut DialFfi>, c_force_relay: bool, c_force_p2p: bool, c_turn_uri: *const c_char, - rt_ptr: Option<&mut DialFfi>, ) -> *mut c_char { let uri = { if c_uri.is_null() { @@ -334,10 +338,10 @@ pub unsafe extern "C" fn dial( c_payload, c_allow_insec, c_timeout, + rt_ptr, false, false, ptr::null(), - rt_ptr, ) }