mosquitto_plugin/
dynlib.rs

1// This generates the dynamic c bindings functions that are exported and usable by mosquitto, as
2// well as allocating memory for the structure and handles recreating this from raw memory, to
3// allow the generated plugin to use member functions, and thus have mutable state
4
5// if segfaulting, compiling in debug mode, will enable asserts of most ptrs
6
7#[macro_export]
8macro_rules! create_dynamic_library {
9    ($t:ty) => {
10        macro_rules! debug_assert_null_or_str {
11            ($ptr:expr, $l:literal) => {
12                unsafe {
13                    debug_assert!(!$ptr.is_null(), "ptr is null!");
14                    let c_str = std::ffi::CStr::from_ptr($ptr);
15                    c_str.to_str().expect($l)
16                }
17            };
18        }
19
20        macro_rules! debug_assert_null_or_slice {
21            ($data:expr, $datalen:expr, $l:literal) => {
22                unsafe {
23                    let event_data = $data as *const u8;
24                    if event_data.is_null() {
25                        PAYLOAD_NULL
26                    } else {
27                        debug_assert!(!event_data.is_null(), $l);
28                        std::slice::from_raw_parts(event_data, $datalen as usize)
29                    }
30                }
31            };
32        }
33
34        use mosquitto_dev::*;
35        use std::ffi::CString;
36        use std::os::raw::c_int;
37        use std::os::raw::c_void;
38
39        const PAYLOAD_NULL: &[u8] = &[];
40
41        fn __assert_sync()
42        where
43            $t: MosquittoPlugin,
44        {
45        }
46
47        /// Structure internal to the plugin binder.
48        /// identifier is the plugin identifier recievied in mosquitto_plugin_init
49        /// external_user_data is the struct defined by the library user.
50        struct InternalUserData {
51            identifier: *mut c_void,
52            external_user_data: $t,
53        }
54
55        #[no_mangle]
56        pub extern "C" fn mosquitto_plugin_version() -> isize {
57            mosquitto_dev::MOSQ_PLUGIN_VERSION as isize
58        }
59
60        // Trampoline functions that are used as callback for the mosquitto_callback_register
61        // These function satisfy the types of the C bindings and then call their corresponding safer rust calls.
62
63        #[no_mangle]
64        extern "C" fn on_reload_trampoline(
65            _event: c_int,
66            event_data: *mut c_void,
67            user_data: *mut c_void,
68        ) -> c_int {
69            debug_assert!(
70                !event_data.is_null(),
71                "on_reload_trampoline event_data is null"
72            );
73            debug_assert!(
74                !user_data.is_null(),
75                "on_reload_trampoline user_data is null"
76            );
77            let user_data: &mut InternalUserData =
78                unsafe { &mut *(user_data as *mut InternalUserData) };
79            let event_data: &mut mosquitto_evt_reload =
80                unsafe { &mut *(event_data as *mut mosquitto_evt_reload) };
81            let opts = __from_ptr_and_size(event_data.options, event_data.option_count as _);
82            user_data.external_user_data.on_reload(opts);
83            0
84        }
85
86        #[no_mangle]
87        extern "C" fn on_acl_check_trampoline(
88            _event: c_int,
89            event_data: *mut c_void,
90            user_data: *mut c_void,
91        ) -> c_int {
92            debug_assert!(
93                !event_data.is_null(),
94                "on_acl_check_trampoline event_data is null"
95            );
96            debug_assert!(
97                !user_data.is_null(),
98                "on_acl_check_trampoline user_data is null"
99            );
100            let user_data: &mut InternalUserData =
101                unsafe { &mut *(user_data as *mut InternalUserData) };
102            let event_data: &mut mosquitto_evt_acl_check =
103                unsafe { &mut *(event_data as *mut mosquitto_evt_acl_check) };
104            let access_level: AccessLevel = event_data.access.into();
105            let access_level = if let Some(level) = access_level.into() {
106                level
107            } else {
108                mosquitto_warn!("Unexpected access level for acl check. {:?}", access_level);
109                return Error::Unknown.into();
110            };
111
112            let topic: &str = debug_assert_null_or_str!(
113                event_data.topic,
114                "failed to create topic str on acl check trampoline"
115            );
116
117            let payload: &[u8] = debug_assert_null_or_slice!(
118                event_data.payload,
119                event_data.payloadlen,
120                "acl_check_trampoline payload is null"
121            );
122
123            let msg = MosquittoMessage {
124                topic,
125                payload,
126                qos: event_data.qos.into(),
127                retain: event_data.retain,
128            };
129            match user_data.external_user_data.acl_check(
130                &MosquittoClient {
131                    client: event_data.client,
132                },
133                access_level,
134                msg,
135            ) {
136                Ok(s) => s.into(),
137                Err(e) => e.into(),
138            }
139        }
140
141        #[no_mangle]
142        extern "C" fn on_basic_auth_trampoline(
143            _event: c_int,
144            event_data: *mut c_void,
145            user_data: *mut c_void,
146        ) -> c_int {
147            debug_assert!(
148                !event_data.is_null(),
149                "on_basic_auth_trampoline event_data is null"
150            );
151            debug_assert!(
152                !user_data.is_null(),
153                "on_basic_auth_trampoline user_data is null"
154            );
155            let user_data: &mut InternalUserData =
156                unsafe { &mut *(user_data as *mut InternalUserData) };
157            let event_data: &mut mosquitto_evt_basic_auth =
158                unsafe { &mut *(event_data as *mut mosquitto_evt_basic_auth) };
159
160            let username: Option<&str> = unsafe {
161                if event_data.username.is_null() {
162                    None
163                } else {
164                    let c_str = std::ffi::CStr::from_ptr(event_data.username);
165                    Some(c_str.to_str().expect(
166                        "basic auth trampoline failed to create username &str from CStr pointer",
167                    ))
168                }
169            };
170            let password: Option<&str> = unsafe {
171                if event_data.password.is_null() {
172                    None
173                } else {
174                    let c_str = std::ffi::CStr::from_ptr(event_data.password);
175                    Some(c_str.to_str().expect(
176                        "basic auth trampoline failed to create password &str from CStr pointer",
177                    ))
178                }
179            };
180
181            debug_assert!(
182                !event_data.client.is_null(),
183                "no client in basic auth trampoline"
184            );
185
186            match user_data.external_user_data.username_password(
187                &MosquittoClient {
188                    client: event_data.client,
189                },
190                username,
191                password,
192            ) {
193                Ok(r) => r.into(),
194                Err(e) => e.into(),
195            }
196        }
197
198        #[no_mangle]
199        extern "C" fn on_auth(
200            event: c_int,
201            event_data: *mut c_void,
202            user_data: *mut c_void,
203        ) -> c_int {
204            debug_assert!(!event_data.is_null(), "on_auth event_data is null");
205            debug_assert!(!user_data.is_null(), "on_auth user_data is null");
206            let user_data: &mut InternalUserData =
207                unsafe { &mut *(user_data as *mut InternalUserData) };
208            let event_data: &mut mosquitto_evt_extended_auth =
209                unsafe { &mut *(event_data as *mut mosquitto_evt_extended_auth) };
210
211            let method = (!event_data.auth_method.is_null()).then(|| unsafe {
212                let c_str = std::ffi::CStr::from_ptr(event_data.auth_method);
213                c_str.to_str().expect(
214                    "auth start trampoline failed to create auth method &str from CStr pointer",
215                )
216            });
217
218            let data_in = (!event_data.data_in.is_null()).then(|| unsafe {
219                std::slice::from_raw_parts(
220                    event_data.data_in as *const u8,
221                    event_data.data_in_len as usize,
222                )
223            });
224
225            let result = if event == MosquittoPluginEvent::MosqEvtExtAuthStart as c_int {
226                user_data.external_user_data.on_auth_start(
227                    &MosquittoClient {
228                        client: event_data.client,
229                    },
230                    method,
231                    data_in,
232                )
233            } else if event == MosquittoPluginEvent::MosqEvtExtAuthContinue as c_int {
234                user_data.external_user_data.on_auth_continue(
235                    &MosquittoClient {
236                        client: event_data.client,
237                    },
238                    method,
239                    data_in,
240                )
241            } else {
242                unreachable!("invalid event type");
243            };
244
245            match result {
246                Ok(r) => r.into(),
247                Err(Error::AuthContinue(data_out)) => {
248                    debug_assert!(data_out.len() <= u16::MAX as usize);
249                    event_data.data_out_len = data_out.len() as u16;
250                    event_data.data_out = data_out.as_ptr() as _;
251                    std::mem::forget(data_out);
252                    Error::AuthContinue(Vec::with_capacity(0)).into()
253                }
254                Err(e) => e.into(),
255            }
256        }
257
258        #[no_mangle]
259        extern "C" fn on_control_trampoline(
260            _event: c_int,
261            event_data: *mut c_void,
262            user_data: *mut c_void,
263        ) -> c_int {
264            debug_assert!(
265                !event_data.is_null(),
266                "on_control_trampoline event_data is null"
267            );
268            debug_assert!(
269                !user_data.is_null(),
270                "on_control_trampoline user_data is null"
271            );
272            let user_data: &mut InternalUserData =
273                unsafe { &mut *(user_data as *mut InternalUserData) };
274            let event_data: &mut mosquitto_evt_control =
275                unsafe { &mut *(event_data as *mut mosquitto_evt_control) };
276
277            let topic: &str = debug_assert_null_or_str!(
278                event_data.topic,
279                "control trampoline failed to create topic &str from CStr pointer"
280            );
281
282            let payload: &[u8] = debug_assert_null_or_slice!(
283                event_data.payload,
284                event_data.payloadlen,
285                "on_control_trampoline payload is null"
286            );
287
288            let msg = MosquittoMessage {
289                topic,
290                payload,
291                qos: event_data.qos.into(),
292                retain: event_data.retain,
293            };
294
295            user_data.external_user_data.on_control(
296                &MosquittoClient {
297                    client: event_data.client,
298                },
299                msg,
300            );
301            0
302        }
303
304        #[no_mangle]
305        extern "C" fn on_message_trampoline(
306            _event: c_int,
307            event_data: *mut c_void,
308            user_data: *mut c_void,
309        ) -> c_int {
310            debug_assert!(
311                !event_data.is_null(),
312                "on_message_trampoline event_data is null"
313            );
314            debug_assert!(
315                !user_data.is_null(),
316                "on_message_trampoline user_data is null"
317            );
318            let user_data: &mut InternalUserData =
319                unsafe { &mut *(user_data as *mut InternalUserData) };
320            let event_data: &mut mosquitto_evt_message =
321                unsafe { &mut *(event_data as *mut mosquitto_evt_message) };
322
323            let topic: &str = debug_assert_null_or_str!(
324                event_data.topic,
325                "message trampoline failed to create topic &str from CStr pointer"
326            );
327
328            let payload: &[u8] = debug_assert_null_or_slice!(
329                event_data.payload,
330                event_data.payloadlen,
331                "on_message_trampoline_is_null"
332            );
333
334            let msg = MosquittoMessage {
335                topic,
336                payload,
337                qos: event_data.qos.into(),
338                retain: event_data.retain,
339            };
340
341            user_data.external_user_data.on_message(
342                &MosquittoClient {
343                    client: event_data.client,
344                },
345                msg,
346            );
347            0
348        }
349
350        #[no_mangle]
351        extern "C" fn on_psk_key_trampoline(
352            _event: c_int,
353            event_data: *mut c_void,
354            user_data: *mut c_void,
355        ) -> c_int {
356            debug_assert!(
357                !event_data.is_null(),
358                "on_psk_key_trampoline event_data is null"
359            );
360            debug_assert!(
361                !user_data.is_null(),
362                "on_psk_key_trampoline user_data is null"
363            );
364            let user_data: &mut InternalUserData =
365                unsafe { &mut *(user_data as *mut InternalUserData) };
366            let event_data: &mut mosquitto_evt_psk_key =
367                unsafe { &mut *(event_data as *mut mosquitto_evt_psk_key) };
368
369            let hint: &str = debug_assert_null_or_str!(
370                event_data.hint,
371                "psk key trampoline failed to create hint &str from CStr pointer"
372            );
373
374            let identity: &str = debug_assert_null_or_str!(
375                event_data.identity,
376                "psk key trampoline failed to create identity &str from CStr pointer"
377            );
378
379            let key: &str = debug_assert_null_or_str!(
380                event_data.key,
381                "psk key trampoline failed to create key &str from CStr pointer"
382            );
383
384            user_data.external_user_data.on_psk(
385                &MosquittoClient {
386                    client: event_data.client,
387                },
388                hint,
389                identity,
390                key,
391                event_data.max_key_len as i32,
392            )
393        }
394
395        #[no_mangle]
396        extern "C" fn on_tick_trampoline(
397            _event: c_int,
398            event_data: *mut c_void,
399            user_data: *mut c_void,
400        ) -> c_int {
401            debug_assert!(
402                !event_data.is_null(),
403                "on_tick_trampoline event_data is null"
404            );
405            debug_assert!(!user_data.is_null(), "on_tick_trampoline user_data is null");
406            let user_data: &mut InternalUserData =
407                unsafe { &mut *(user_data as *mut InternalUserData) };
408            let event_data: &mut mosquitto_evt_tick =
409                unsafe { &mut *(event_data as *mut mosquitto_evt_tick) };
410
411            user_data.external_user_data.on_tick(
412                event_data.now_ns as i64,
413                event_data.next_ns as i64,
414                event_data.now_s as i32,
415                event_data.next_s as i32,
416            );
417            0
418        }
419
420        #[no_mangle]
421        extern "C" fn on_disconnect_trampoline(
422            _event: c_int,
423            event_data: *mut c_void,
424            user_data: *mut c_void,
425        ) -> c_int {
426            debug_assert!(
427                !event_data.is_null(),
428                "on disconnect_trampoline event_data is null"
429            );
430            debug_assert!(
431                !user_data.is_null(),
432                "on disconnect_trampoline user_data is null"
433            );
434            let user_data: &mut InternalUserData = unsafe {
435                // mosquitto_debug!("Got user data: {:?}", user_data);
436                &mut *(user_data as *mut InternalUserData)
437            };
438
439            let event_data: &mut mosquitto_evt_disconnect =
440                unsafe { &mut *(event_data as *mut mosquitto_evt_disconnect) };
441
442            let client = MosquittoClient {
443                client: event_data.client,
444            };
445
446            user_data
447                .external_user_data
448                .on_disconnect(&client, event_data.reason);
449
450            0
451        }
452
453        #[no_mangle]
454        pub extern "C" fn mosquitto_plugin_init(
455            identifier: *mut c_void,
456            user_data: *mut *mut c_void, // When this pointer is set, every other call will get this pointer as well. Only for v4 plugins?
457            opts: *mut mosquitto_opt,
458            opt_count: c_int,
459        ) -> c_int {
460            let opts = __from_ptr_and_size(opts, opt_count as _);
461            mosquitto_debug!("mosquitto_plugin_init {:?}", opts);
462
463            let instance: $t = <$t>::init(opts);
464            let instance = instance;
465            mosquitto_debug!("external_user_data addr {:?}", instance);
466            let internal_user_data = InternalUserData {
467                identifier,
468                external_user_data: instance,
469            };
470            let internal_user_data = Box::new(internal_user_data);
471            let instance_rawptr: *mut InternalUserData = Box::into_raw(internal_user_data);
472
473            unsafe {
474                *user_data = instance_rawptr as _;
475            }
476
477            unsafe {
478                mosquitto_callback_register(
479                    identifier as _,
480                    MosquittoPluginEvent::MosqEvtReload as _,
481                    Some(on_reload_trampoline),
482                    std::ptr::null(),
483                    instance_rawptr as _,
484                );
485
486                mosquitto_callback_register(
487                    identifier as _,
488                    MosquittoPluginEvent::MosqEvtAclCheck as _,
489                    Some(on_acl_check_trampoline),
490                    std::ptr::null(),
491                    instance_rawptr as _,
492                );
493
494                mosquitto_callback_register(
495                    identifier as _,
496                    MosquittoPluginEvent::MosqEvtBasicAuth as _,
497                    Some(on_basic_auth_trampoline),
498                    std::ptr::null(),
499                    instance_rawptr as _,
500                );
501
502                mosquitto_callback_register(
503                    identifier as _,
504                    MosquittoPluginEvent::MosqEvtExtAuthStart as _,
505                    Some(on_auth),
506                    std::ptr::null(),
507                    instance_rawptr as _,
508                );
509
510                mosquitto_callback_register(
511                    identifier as _,
512                    MosquittoPluginEvent::MosqEvtExtAuthContinue as _,
513                    Some(on_auth),
514                    std::ptr::null(),
515                    instance_rawptr as _,
516                );
517
518                let event_data = "$CONTROL";
519                let cstr = &std::ffi::CString::new(event_data).unwrap();
520                let bytes = cstr.as_bytes_with_nul();
521                let topic = bytes.as_ptr() as *const c_void;
522                // TODO the event_data parameter (4th param) has a meaning for the MOSQ_EVT_CONTROL callback
523                // Something to do with the topic the control events are triggered on?
524                //https://github.com/eclipse/mosquitto/blob/master/plugins/dynamic-security/plugin.c#L494
525                mosquitto_callback_register(
526                    identifier as _,
527                    MosquittoPluginEvent::MosqEvtControl as _,
528                    Some(on_control_trampoline),
529                    topic,
530                    instance_rawptr as _,
531                );
532
533                let res = mosquitto_callback_register(
534                    identifier as _,
535                    MosquittoPluginEvent::MosqEvtMessage as _,
536                    Some(on_message_trampoline),
537                    std::ptr::null(),
538                    instance_rawptr as _,
539                );
540
541                mosquitto_callback_register(
542                    identifier as _,
543                    MosquittoPluginEvent::MosqEvtPskKey as _,
544                    Some(on_psk_key_trampoline),
545                    std::ptr::null(),
546                    instance_rawptr as _,
547                );
548
549                mosquitto_callback_register(
550                    identifier as _,
551                    MosquittoPluginEvent::MosqEvtTick as _,
552                    Some(on_tick_trampoline),
553                    std::ptr::null(),
554                    instance_rawptr as _,
555                );
556
557                let res = mosquitto_callback_register(
558                    identifier as _,
559                    MosquittoPluginEvent::MosqEvtDisconnect as _,
560                    Some(on_disconnect_trampoline),
561                    std::ptr::null(),
562                    instance_rawptr as _,
563                );
564            }
565
566            Success.into()
567        }
568
569        #[no_mangle]
570        extern "C" fn mosquitto_plugin_cleanup(
571            user_data: *mut c_void,
572            opts: *mut mosquitto_opt,
573            opt_count: c_int,
574        ) -> c_int {
575            let opts = __from_ptr_and_size(opts, opt_count as _);
576            let user_data: &mut InternalUserData =
577                unsafe { &mut *(user_data as *mut InternalUserData) };
578
579            unsafe {
580                mosquitto_callback_unregister(
581                    user_data.identifier as _,
582                    MosquittoPluginEvent::MosqEvtDisconnect as _,
583                    Some(on_disconnect_trampoline),
584                    std::ptr::null(),
585                );
586            }
587            if !user_data.identifier.is_null() {
588                let identifier = unsafe {
589                    let c_str = std::ffi::CStr::from_ptr(user_data.identifier as _);
590                    c_str
591                        .to_str()
592                        .expect("plugin identifier is null at cleanup")
593                };
594                mosquitto_debug!("cleaning up plugin: {}", identifier);
595            }
596            drop(unsafe { Box::from_raw(user_data as *mut InternalUserData) });
597
598            Success.into()
599        }
600    };
601}