ipcon_sys/
ipcon.rs

1extern crate libc;
2use crate::ipcon_error::IpconError;
3use crate::ipcon_msg::{IpconMsg, LibIpconMsg, IPCON_MAX_NAME_LEN, IPCON_MAX_PAYLOAD_LEN};
4use error_stack::{Report, Result, ResultExt};
5#[allow(unused)]
6use jlogger_tracing::{jdebug, jerror, jinfo, jwarn};
7use libc::{c_void, size_t};
8use nix::errno::Errno;
9use std::ffi::CString;
10use std::os::raw::{c_char, c_uchar};
11
12#[link(name = "ipcon")]
13extern "C" {
14    fn ipcon_create_handler(peer_name: *const c_char, flags: usize) -> *mut c_void;
15    fn ipcon_free_handler(handler: *mut c_void);
16    fn is_peer_present(handler: *mut c_void, peer: *const c_char) -> i32;
17    fn is_group_present(handler: *mut c_void, peer: *const c_char, group: *const c_char) -> i32;
18    fn ipcon_rcv(handler: *mut c_void, msg: &LibIpconMsg) -> i32;
19    fn ipcon_send_unicast(
20        handler: *mut c_void,
21        peer: *const c_char,
22        buf: *const c_uchar,
23        size: size_t,
24    ) -> i32;
25    fn ipcon_register_group(handler: *mut c_void, name: *const c_char) -> i32;
26    fn ipcon_unregister_group(handler: *mut c_void, name: *const c_char) -> i32;
27    fn ipcon_join_group(
28        handler: *mut c_void,
29        srv_name: *const c_char,
30        grp_name: *const c_char,
31    ) -> i32;
32    fn ipcon_leave_group(
33        handler: *mut c_void,
34        srv_name: *const c_char,
35        grp_name: *const c_char,
36    ) -> i32;
37    fn ipcon_send_multicast(
38        handler: *mut c_void,
39        name: *const c_char,
40        buf: *const c_uchar,
41        size: size_t,
42        sync: i32,
43    ) -> i32;
44    fn ipcon_rcv_timeout(
45        handler: *mut c_void,
46        im: &LibIpconMsg,
47        timeout: *const libc::timeval,
48    ) -> i32;
49    fn ipcon_get_read_fd(handler: *mut c_void) -> i32;
50    fn ipcon_get_write_fd(handler: *mut c_void) -> i32;
51    fn ipcon_get_ctrl_fd(handler: *mut c_void) -> i32;
52}
53
54/// IPCON peer.
55pub struct Ipcon {
56    handler: usize,
57    name: Option<String>,
58}
59
60pub type IpconFlag = std::os::raw::c_ulong;
61pub const IPF_DISABLE_KEVENT_FILTER: IpconFlag = 0x1 << 0;
62pub const IPF_RCV_IF: IpconFlag = 0x1 << 1;
63pub const IPF_SND_IF: IpconFlag = 0x1 << 2;
64pub const IPF_DEFAULT: IpconFlag = IPF_RCV_IF | IPF_SND_IF;
65pub const IPCON_KERNEL_NAME: &str = "ipcon";
66pub const IPCON_KERNEL_GROUP_NAME: &str = "ipcon_kevent";
67
68fn errno_to_error(i: i32) -> IpconError {
69    let eno = Errno::from_i32(i.abs());
70    match eno {
71        Errno::ETIMEDOUT => IpconError::SysErrorTimeOut,
72        Errno::EINVAL => IpconError::SysErrorInvalidValue,
73        Errno::EPERM => IpconError::SysErrorPermission,
74        Errno::ENOENT => IpconError::SystemErrorNotExist,
75        _ => IpconError::SystemErrorOther,
76    }
77}
78
79pub fn valid_name(name: &str) -> Result<(), IpconError> {
80    let mut error_str = None;
81
82    if name.is_empty() {
83        error_str = Some("Name is null".to_owned());
84    }
85
86    if name.len() > IPCON_MAX_NAME_LEN {
87        error_str = Some(format!(
88            "Name is too long {} > {}",
89            name.len(),
90            IPCON_MAX_NAME_LEN
91        ));
92    }
93
94    if name.trim() != name {
95        error_str = Some("Name has blank character".to_owned());
96    }
97
98    if let Some(err_str) = error_str {
99        Err(Report::new(IpconError::InvalidName)).attach_printable(err_str)
100    } else {
101        Ok(())
102    }
103}
104
105impl Drop for Ipcon {
106    fn drop(&mut self) {
107        unsafe {
108            ipcon_free_handler(Ipcon::to_handler(self.handler));
109        }
110    }
111}
112
113impl Ipcon {
114    pub fn to_handler(u: usize) -> *mut c_void {
115        u as *mut c_void
116    }
117
118    ///# Safety
119    pub unsafe fn from_handler(h: *mut c_void) -> usize {
120        h as usize
121    }
122
123    /// Create an IPCON peer.
124    /// If the name is omitted, an anonymous will be created.
125    /// Following flags can be specified with bitwise OR (|).
126    /// * IPF_DISABLE_KEVENT_FILTER  
127    ///   By default, IPCON kernel module will only delivery the add/remove notification of
128    ///   peers and groups which are considered to be interested by the peer. If this flag is
129    ///   enabled, all notification will be delivered by IPCON kernel module.
130    /// * IPF_SND_IF  
131    ///   Use message sending interface.
132    /// * IPF_RCV_IF  
133    ///   Use message receiving interface.
134    /// * IPF_DEFAULT  
135    ///   This is same to IPF_RCV_IF | IPF_SND_IF.
136    ///
137    ///   
138    pub fn new(peer_name: Option<&str>, flag: Option<IpconFlag>) -> Result<Ipcon, IpconError> {
139        let handler: *mut c_void;
140        let mut flg = 0_usize;
141        let mut name = None;
142
143        if let Some(a) = flag {
144            flg = a as usize;
145        }
146
147        let pname = match peer_name {
148            Some(a) => {
149                valid_name(a).attach_printable(format!("Invalid peer name: {}", a))?;
150                name = Some(a.to_string());
151                CString::new(a)
152                    .map_err(|_| Report::new(IpconError::InvalidName))?
153                    .into_raw()
154            }
155            None => std::ptr::null(),
156        };
157
158        unsafe {
159            handler = ipcon_create_handler(pname as *const c_char, flg as usize);
160
161            if !pname.is_null() {
162                /* deallocate the pname */
163                let _ = CString::from_raw(pname as *mut c_char);
164            }
165        }
166        if handler.is_null() {
167            Err(Report::new(IpconError::SystemErrorOther)).attach_printable(format!(
168                "Failed to create ipcon handler for {}, peer name already used?",
169                name.as_deref().unwrap_or("Anon")
170            ))
171        } else {
172            Ok(Ipcon {
173                handler: unsafe { Ipcon::from_handler(handler) },
174                name,
175            })
176        }
177    }
178
179    /// Retrieve netlink socket file descriptor of message receiving interface.
180    pub fn get_read_fd(&self) -> Result<i32, IpconError> {
181        unsafe {
182            let fd = ipcon_get_read_fd(Ipcon::to_handler(self.handler));
183            if fd < 0 {
184                Err(Report::new(errno_to_error(fd))).attach_printable(format!(
185                    "ipcon_get_read_fd() {} get read fd failed: {}",
186                    self.name.as_deref().unwrap_or("Anon"),
187                    fd
188                ))
189            } else {
190                Ok(fd)
191            }
192        }
193    }
194
195    /// Retrieve netlink socket file descriptor of message sending interface.
196    pub fn get_write_fd(&self) -> Result<i32, IpconError> {
197        unsafe {
198            let fd = ipcon_get_write_fd(Ipcon::to_handler(self.handler));
199            if fd < 0 {
200                Err(Report::new(errno_to_error(fd))).attach_printable(format!(
201                    "ipcon_get_write_fd() {} get write fd failed: {}",
202                    self.name.as_deref().unwrap_or("Anon"),
203                    fd
204                ))
205            } else {
206                Ok(fd)
207            }
208        }
209    }
210
211    /// Retrieve netlink socket file descriptor of control interface.
212    pub fn get_ctrl_fd(&self) -> Result<i32, IpconError> {
213        unsafe {
214            let fd = ipcon_get_ctrl_fd(Ipcon::to_handler(self.handler));
215            if fd < 0 {
216                Err(Report::new(errno_to_error(fd))).attach_printable(format!(
217                    "ipcon_get_ctrl_fd() {} get ctrl fd failed: {}",
218                    self.name.as_deref().unwrap_or("Anon"),
219                    fd
220                ))
221            } else {
222                Ok(fd)
223            }
224        }
225    }
226
227    /// Inquiry whether a peer is present.
228    pub fn is_peer_present(&self, peer: &str) -> bool {
229        let mut present = false;
230        let p = match CString::new(peer) {
231            Ok(a) => a,
232            Err(_) => return false,
233        };
234
235        unsafe {
236            let ptr = p.into_raw();
237            let ret = is_peer_present(Ipcon::to_handler(self.handler), ptr as *const c_char);
238            if ret != 0 {
239                present = true;
240            }
241
242            let _ = CString::from_raw(ptr);
243        }
244
245        present
246    }
247
248    /// Inquiry whether the group of a peer is present.
249    pub fn is_group_present(&self, peer: &str, group: &str) -> bool {
250        let mut present = false;
251        let p = match CString::new(peer) {
252            Ok(a) => a,
253            Err(_) => return false,
254        };
255
256        let g = match CString::new(group) {
257            Ok(a) => a,
258            Err(_) => return false,
259        };
260
261        unsafe {
262            let ptr = p.into_raw();
263            let pgtr = g.into_raw();
264            let ret = is_group_present(
265                Ipcon::to_handler(self.handler),
266                ptr as *const c_char,
267                pgtr as *const c_char,
268            );
269            let _ = CString::from_raw(ptr);
270            let _ = CString::from_raw(pgtr);
271
272            if ret != 0 {
273                present = true;
274            }
275        }
276
277        present
278    }
279
280    /// Receive IPCON message.
281    /// This function will fail if the peer doesn't enable IPF_RCV_IF.
282    pub fn receive_msg(&self) -> Result<IpconMsg, IpconError> {
283        let lmsg = LibIpconMsg::new();
284
285        unsafe {
286            let ret = ipcon_rcv(Ipcon::to_handler(self.handler), &lmsg);
287            if ret < 0 {
288                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
289                    "ipcon_rcv() {} receive message failed: {}",
290                    self.name.as_deref().unwrap_or("Anon"),
291                    ret
292                ));
293            }
294        }
295
296        lmsg.into()
297    }
298
299    /// Send an unicast IPCON message to a specific peer.
300    /// This function will fail if the peer doesn't enable IPF_SND_IF.
301    pub fn send_unicast_msg(&self, peer: &str, buf: &[u8]) -> Result<(), IpconError> {
302        self.send_unicast_msg_by_ref(peer, buf)
303    }
304
305    /// Send an unicast IPCON message to a specific peer.
306    /// This function will fail if the peer doesn't enable IPF_SND_IF.
307    pub fn send_unicast_msg_by_ref(&self, peer: &str, buf: &[u8]) -> Result<(), IpconError> {
308        valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
309
310        if buf.len() > IPCON_MAX_PAYLOAD_LEN {
311            return Err(Report::new(IpconError::InvalidData)).attach_printable(format!(
312                "Buffer length is to large {} > {}",
313                buf.len(),
314                IPCON_MAX_PAYLOAD_LEN
315            ));
316        }
317
318        let pname = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidData))?;
319
320        unsafe {
321            let ptr = pname.into_raw();
322            let ret = ipcon_send_unicast(
323                Ipcon::to_handler(self.handler),
324                ptr as *const c_char,
325                buf.as_ptr(),
326                buf.len() as size_t,
327            );
328
329            let _ = CString::from_raw(ptr);
330
331            if ret < 0 {
332                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
333                    "send_unicast_msg() {} send message to peer `{}` failed: {}",
334                    self.name.as_deref().unwrap_or("Anon"),
335                    peer,
336                    ret
337                ));
338            }
339        }
340
341        Ok(())
342    }
343
344    /// Register a multicast group.
345    pub fn register_group(&self, group: &str) -> Result<(), IpconError> {
346        valid_name(group).attach_printable("register_group error: invalid group name")?;
347
348        let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
349
350        unsafe {
351            let ptr = g.into_raw();
352            let ret = ipcon_register_group(Ipcon::to_handler(self.handler), ptr as *const c_char);
353            let _ = CString::from_raw(ptr);
354            if ret < 0 {
355                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
356                    "ipcon_register_group() {} register `{}` failed: {}",
357                    self.name.as_deref().unwrap_or("Anon"),
358                    group,
359                    ret
360                ));
361            }
362        }
363
364        Ok(())
365    }
366
367    /// Unregister a multicast group.
368    pub fn unregister_group(&self, group: &str) -> Result<(), IpconError> {
369        valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
370
371        let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
372
373        unsafe {
374            let ptr = g.into_raw();
375            let ret = ipcon_unregister_group(Ipcon::to_handler(self.handler), ptr as *const c_char);
376            let _ = CString::from_raw(ptr);
377            if ret < 0 {
378                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
379                    "ipcon_unregister_group() {} unregister `{}` failed: {}",
380                    self.name.as_deref().unwrap_or("Anon"),
381                    group,
382                    ret
383                ));
384            }
385        }
386
387        Ok(())
388    }
389
390    /// Subscribe a multicast group of a peer.
391    pub fn join_group(&self, peer: &str, group: &str) -> Result<(), IpconError> {
392        valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
393        valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
394
395        let p = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidName))?;
396        let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
397
398        unsafe {
399            let ptr = p.into_raw();
400            let pgtr = g.into_raw();
401            let ret = ipcon_join_group(
402                Ipcon::to_handler(self.handler),
403                ptr as *const c_char,
404                pgtr as *const c_char,
405            );
406            let _ = CString::from_raw(ptr);
407            let _ = CString::from_raw(pgtr);
408            if ret < 0 {
409                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
410                    "ipcon_join_group() {} join `{}@{}` failed: {}",
411                    self.name.as_deref().unwrap_or("Anon"),
412                    group,
413                    peer,
414                    ret
415                ));
416            }
417        }
418
419        Ok(())
420    }
421
422    /// Unsubscribe a multicast group of a peer.
423    pub fn leave_group(&self, peer: &str, group: &str) -> Result<(), IpconError> {
424        valid_name(peer).attach_printable(format!("Invalid peer name: {}", peer))?;
425        valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
426
427        let p = CString::new(peer).map_err(|_| Report::new(IpconError::InvalidName))?;
428        let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
429
430        unsafe {
431            let ptr = p.into_raw();
432            let pgtr = g.into_raw();
433            let ret = ipcon_leave_group(
434                Ipcon::to_handler(self.handler),
435                ptr as *const c_char,
436                pgtr as *const c_char,
437            );
438            let _ = CString::from_raw(ptr);
439            let _ = CString::from_raw(pgtr);
440
441            if ret < 0 {
442                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
443                    "ipcon_leave_group() {} leave `{}@{}` failed: {}",
444                    self.name.as_deref().unwrap_or("Anon"),
445                    group,
446                    peer,
447                    ret
448                ));
449            }
450        }
451
452        Ok(())
453    }
454
455    /// Send multicast messages to an owned group.
456    pub fn send_multicast(&self, group: &str, buf: &[u8], sync: bool) -> Result<(), IpconError> {
457        self.send_multicast_by_ref(group, buf, sync)
458    }
459
460    /// Send multicast messages to an owned group.
461    pub fn send_multicast_by_ref(
462        &self,
463        group: &str,
464        buf: &[u8],
465        sync: bool,
466    ) -> Result<(), IpconError> {
467        valid_name(group).attach_printable(format!("Invalid group name: {}", group))?;
468
469        if buf.len() > IPCON_MAX_PAYLOAD_LEN {
470            return Err(Report::new(IpconError::InvalidData)).attach_printable(format!(
471                "Buffer length is too large {} > {}",
472                buf.len(),
473                IPCON_MAX_PAYLOAD_LEN,
474            ));
475        }
476
477        let g = CString::new(group).map_err(|_| Report::new(IpconError::InvalidName))?;
478
479        let mut s: i32 = 0;
480        if sync {
481            s = 1;
482        }
483
484        unsafe {
485            let pgtr = g.into_raw();
486            let ret = ipcon_send_multicast(
487                Ipcon::to_handler(self.handler),
488                pgtr as *const c_char,
489                buf.as_ptr(),
490                buf.len() as size_t,
491                s,
492            );
493            let _ = CString::from_raw(pgtr);
494
495            if ret < 0 {
496                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
497                    "ipcon_send_multicast() to `{}@{}` failed: {}",
498                    group,
499                    self.name.as_deref().unwrap_or("Anon"),
500                    ret
501                ));
502            }
503        }
504
505        Ok(())
506    }
507
508    /// Receiving message with timeout.
509    /// receive_msg() will block until a message come. receive_msg_timeout() adds a timeout to
510    /// it.The timeout is specified with seconds and microseconds.
511    pub fn receive_msg_timeout(&self, tv_sec: u32, tv_usec: u32) -> Result<IpconMsg, IpconError> {
512        let lmsg = LibIpconMsg::new();
513        let t = libc::timeval {
514            tv_sec: tv_sec as libc::time_t,
515            tv_usec: tv_usec as libc::suseconds_t,
516        };
517
518        unsafe {
519            let ret = ipcon_rcv_timeout(Ipcon::to_handler(self.handler), &lmsg, &t);
520            if ret < 0 {
521                return Err(Report::new(errno_to_error(ret))).attach_printable(format!(
522                    "ipcon_rcv_timeout() {} receive message failed: {}",
523                    self.name.as_deref().unwrap_or("Anon"),
524                    ret
525                ));
526            }
527        }
528
529        lmsg.into()
530    }
531
532    /// Receiving message without block.
533    /// This is same to receive_msg_timeout(0, 0);
534    pub fn receive_msg_nonblock(&self) -> Result<IpconMsg, IpconError> {
535        self.receive_msg_timeout(0, 0)
536    }
537}