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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
use std::{
mem::{self, ManuallyDrop, MaybeUninit},
ptr,
};
use open62541_sys::{
UA_ByteString, UA_CertificateVerification, UA_CertificateVerification_AcceptAll, UA_StatusCode,
UA_String,
};
use crate::{CustomCertificateVerification, DataType, Userdata, ua};
/// Wrapper for [`UA_CertificateVerification`] from [`open62541_sys`].
#[derive(Debug)]
pub struct CertificateVerification(UA_CertificateVerification);
impl CertificateVerification {
/// Creates certificate verification with all checks disabled.
///
/// Note that this disables certificate verification entirely. Use only when the other end can
/// be identified in some other way, or identity is not relevant.
#[must_use]
pub fn accept_all() -> Self {
let mut certificate_verification = Self::init();
// SAFETY: Certificate verification is null, but that is valid.
unsafe {
UA_CertificateVerification_AcceptAll(certificate_verification.as_mut_ptr());
}
certificate_verification
}
/// Creates certificate verification with custom callbacks.
pub fn custom(certificate_verification: impl CustomCertificateVerification + 'static) -> Self {
type Ud = Userdata<Box<dyn CustomCertificateVerification>>;
unsafe extern "C" fn verify_certificate_c(
cv: *const UA_CertificateVerification,
certificate: *const UA_ByteString,
) -> UA_StatusCode {
// SAFETY: Reference is used only for the remainder of this function.
let certificate = ua::ByteString::raw_ref(unsafe {
certificate.as_ref().expect("certificate should be set")
});
// SAFETY: We use the user data only when it is still alive.
let certificate_verification = unsafe { Ud::peek_at((*cv).context) };
let status_code = certificate_verification.verify_certificate(certificate);
status_code.into_raw()
}
unsafe extern "C" fn verify_application_uri_c(
cv: *const UA_CertificateVerification,
certificate: *const UA_ByteString,
application_uri: *const UA_String,
) -> UA_StatusCode {
// SAFETY: References are used only for the remainder of this function.
let certificate = ua::ByteString::raw_ref(unsafe {
certificate.as_ref().expect("certificate should be set")
});
let application_uri = ua::String::raw_ref(unsafe {
application_uri
.as_ref()
.expect("application URI should be set")
});
// SAFETY: We use the user data only when it is still alive.
let certificate_verification = unsafe { Ud::peek_at((*cv).context) };
let status_code =
certificate_verification.verify_application_uri(certificate, application_uri);
status_code.into_raw()
}
unsafe extern "C" fn clear_c(cv: *mut UA_CertificateVerification) {
// Reclaim ownership of certificate verification and drop it.
// SAFETY: We use the user data only when it is still alive.
let _unused = unsafe { Ud::consume((*cv).context) };
}
let inner = UA_CertificateVerification {
context: Ud::prepare(Box::new(certificate_verification)),
verifyCertificate: Some(verify_certificate_c),
verifyApplicationURI: Some(verify_application_uri_c),
getExpirationDate: None,
getSubjectName: None,
clear: Some(clear_c),
logging: ptr::null_mut(),
};
unsafe { Self::from_raw(inner) }
}
/// Creates wrapper by taking ownership of value.
///
/// When `Self` is dropped, allocations held by the inner type are cleaned up.
///
/// # Safety
///
/// Ownership of the value passes to `Self`. This must only be used for values that are not
/// contained within other values that may be dropped.
#[must_use]
pub(crate) const unsafe fn from_raw(src: UA_CertificateVerification) -> Self {
Self(src)
}
/// Gives up ownership and returns value.
///
/// The returned value must be re-wrapped with [`from_raw()`], cleared manually, or copied into
/// an owning value (like [`UA_Client`]) to free internal allocations and not leak memory.
///
/// [`from_raw()`]: Self::from_raw
/// [`UA_Client`]: open62541_sys::UA_Client
#[expect(clippy::allow_attributes, reason = "non-static condition")]
#[allow(clippy::missing_const_for_fn, reason = "unsupported before Rust 1.87")]
#[must_use]
pub(crate) fn into_raw(self) -> UA_CertificateVerification {
// Use `ManuallyDrop` to avoid double-free even when added code might cause panic. See
// documentation of `mem::forget()` for details.
let this = ManuallyDrop::new(self);
// SAFETY: Aliasing memory temporarily is safe because destructor will not be called.
unsafe { ptr::read(&raw const this.0) }
}
/// Creates wrapper initialized with defaults.
///
/// This initializes the value and makes all attributes well-defined. Additional attributes may
/// need to be initialized for the value to be actually useful afterwards.
pub(crate) const fn init() -> Self {
let inner = MaybeUninit::<UA_CertificateVerification>::zeroed();
// SAFETY: Zero-initialized memory is a valid certificate verification.
let inner = unsafe { inner.assume_init() };
// SAFETY: We pass a value without pointers to it into `Self`.
unsafe { Self::from_raw(inner) }
}
/// Moves value into `dst`, giving up ownership.
///
/// Existing data in `dst` is cleared before moving the value; it is safe to use this operation
/// on already initialized target values.
///
/// The logging reference will be transferred from the old to the new certificate verification.
///
/// After this, it is the responsibility of `dst` to eventually clean up the data.
pub(crate) fn move_into_raw(self, dst: &mut UA_CertificateVerification) {
// Move certificate verification into target, transferring ownership.
let orig = mem::replace(dst, self.into_raw());
// Take ownership of previously set certificate verification in order to drop it.
let mut orig = unsafe { Self::from_raw(orig) };
// Before dropping, transfer previously set logging to new certificate verification. We do
// this because certificate verifications do not own the logging reference. For instance,
// after creating a new config, the config's owned logger is copied (!) here. Refer to
// comments in `ClientConfig::new()` for more info.
mem::swap(&mut dst.logging, &mut unsafe { orig.as_mut() }.logging);
}
/// Returns exclusive reference to value.
///
/// # Safety
///
/// The value is owned by `Self`. Ownership must not be given away, in whole or in parts. This
/// may happen when `open62541` functions are called that take ownership of values by pointer.
#[must_use]
#[expect(clippy::allow_attributes, reason = "non-static condition")]
#[allow(clippy::missing_const_for_fn, reason = "unsupported before Rust 1.87")]
pub(crate) unsafe fn as_mut(&mut self) -> &mut UA_CertificateVerification {
&mut self.0
}
/// Returns mutable pointer to value.
///
/// # Safety
///
/// The value is owned by `Self`. Ownership must not be given away, in whole or in parts. This
/// may happen when `open62541` functions are called that take ownership of values by pointer.
#[must_use]
#[expect(clippy::allow_attributes, reason = "non-static condition")]
#[allow(clippy::missing_const_for_fn, reason = "unsupported before Rust 1.87")]
pub(crate) unsafe fn as_mut_ptr(&mut self) -> *mut UA_CertificateVerification {
&raw mut self.0
}
}
impl Drop for CertificateVerification {
fn drop(&mut self) {
if let Some(clear) = self.0.clear {
unsafe {
clear(&raw mut self.0);
}
}
}
}