Skip to content
This repository has been archived by the owner on May 17, 2018. It is now read-only.

Implement safe sendmsg/recvmsg abstractions #16

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ repository = "https://github.com/sfackler/rust-unix-socket"
documentation = "https://sfackler.github.io/rust-unix-socket/doc/v0.4.5/unix_socket"
readme = "README.md"
keywords = ["posix", "unix", "socket", "domain"]
links = "cmsg_manip"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't necessary - it's only needed when linking to third party stuff.

build = "build.rs"

[dependencies]
libc = "0.1"
debug-builders = "0.1"

[build-dependencies]
gcc = "0.3"

[dev-dependencies]
tempdir = "0.3"

Expand Down
5 changes: 5 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extern crate gcc;

fn main() {
gcc::compile_library("libcmsg_manip.a", &["src/cmsg_manip/cmsg.c"]);
}
76 changes: 76 additions & 0 deletions examples/socket_send.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
extern crate libc;
extern crate unix_socket;

use std::os::unix::io::{AsRawFd, FromRawFd};
use std::path::Path;

use unix_socket::{ControlMsg, UCred, UnixDatagram};

fn handle_parent(sock: UnixDatagram) {
let (parent2, child2) = UnixDatagram::pair().unwrap();

let cmsg = ControlMsg::Rights(vec![child2.as_raw_fd()]);
let cmsg2 = unsafe { ControlMsg::Credentials(UCred{
pid: libc::getpid(),
uid: libc::getuid(),
gid: libc::getgid(),
}) };
println!("cmsg {:?}", cmsg2);
let sent_bytes = sock.sendmsg::<&Path>(None, &[&[]], &[cmsg, cmsg2], 0).unwrap();
assert_eq!(sent_bytes, 0);
drop(child2);
println!("Parent sent child SCM_RIGHTS fd");

let mut buf = &mut [0u8; 4096];
let read = parent2.recv(buf).unwrap();
assert_eq!(&buf[..read], "Hello, world!".as_bytes());
println!("Parent received message from child via SCM_RIGHTS fd");
}

fn handle_child(sock: UnixDatagram) {
sock.set_passcred(true).unwrap();
let mut cmsg_buf = &mut [0u8; 4096];
let result = sock.recvmsg(&[&mut[]], cmsg_buf, 0).unwrap();
assert_eq!(result.control_msgs.len(), 2);

let mut new_sock = None;
let mut creds = None;
for cmsg in result.control_msgs {
match cmsg.clone() {
ControlMsg::Rights(fds) => {
assert!(new_sock.is_none());
assert_eq!(fds.len(), 1);
unsafe {
new_sock = Some(UnixDatagram::from_raw_fd(fds[0]));
}
println!("Child received SCM_RIGHTS fd");
},
ControlMsg::Credentials(ucred) => {
assert!(creds.is_none());
creds = Some(ucred);
println!("Child received SCM_CREDENTIALS");
},
_ => unreachable!(),
}
}

let creds = creds.unwrap();
unsafe {
assert_eq!(creds.uid, libc::getuid());
assert_eq!(creds.gid, libc::getgid());
assert!(creds.pid != 0);
}
let sent = new_sock.unwrap().send("Hello, world!".as_bytes()).unwrap();
println!("Child sent message to parent via SCM_RIGHTS fd");
assert_eq!(sent, 13);
}

fn main() {
let (parent_sock, child_sock) = UnixDatagram::pair().unwrap();
let pid = unsafe { libc::fork() };
if pid == 0 {
handle_child(child_sock);
} else {
handle_parent(parent_sock);
}
}
41 changes: 41 additions & 0 deletions src/cmsg_manip/cmsg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#define _GNU_SOURCE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this specifically necessary?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, nevermind, looks like it's required to get ucred.

#include <sys/socket.h>

size_t cmsghdr_size = sizeof(struct cmsghdr);
size_t iovec_size = sizeof(struct iovec);
size_t msghdr_size = sizeof(struct msghdr);
size_t ucred_size = sizeof(struct ucred);

int scm_credentials = SCM_CREDENTIALS;
int scm_rights = SCM_RIGHTS;
int so_passcred = SO_PASSCRED;

int msg_eor = MSG_EOR;
int msg_trunc = MSG_TRUNC;
int msg_ctrunc = MSG_CTRUNC;
int msg_oob = MSG_OOB;
int msg_errqueue = MSG_ERRQUEUE;

struct cmsghdr * cmsg_firsthdr(struct msghdr *msgh) {
return CMSG_FIRSTHDR(msgh);
}

struct cmsghdr * cmsg_nxthdr(struct msghdr *msgh, struct cmsghdr *cmsg) {
return CMSG_NXTHDR(msgh, cmsg);
}

size_t cmsg_align(size_t length) {
return CMSG_ALIGN(length);
}

size_t cmsg_space(size_t length) {
return CMSG_SPACE(length);
}

size_t cmsg_len(size_t length) {
return CMSG_LEN(length);
}

unsigned char * cmsg_data(struct cmsghdr *cmsg) {
return CMSG_DATA(cmsg);
}
Loading