Calling sendmsg() from Ruby

Hello

I’m trying to call the sendmsg() system call from ruby in a way that
allocates space for the kernel to set the user credentials in the
appropriate structures (this is in FreeBSD i386).

I’m using the code below, which gives me an EFAULT (bad address) error,
and I cannot see what I’m doing wrong here… any help would be awesome.

The equivalent C code (which works) follows after the ruby attempt.

def pointer(buf)
[buf].pack(“P”).unpack(“L_”).first
end

def sendmsg(fd, buf)
iov = [buf, buf.length].pack(“PI_”)

cmsg_len = 96       # CMSG_LEN(sizeof(struct cmsgcred))
cmsg_space = 96     # CMSG_SPACE(sizeof(struct cmsgcred))
cmsg_level = 0xffff # SOL_SOCKET
cmsg_type = 0x03    # SCM_CREDS
cmsg_data = ["\x00" * cmsg_space].pack("P")

cmsghdr = [
  cmsg_len,
  cmsg_level,
  cmsg_type,
  cmsg_data
].pack("I_i_i_P")

msg_control_ptr = pointer(cmsghdr)
msg_controllen = cmsg_space

msghdr = [
  0,                # msg_name
  0,                # msg_namelen
  pointer(iov),     # msg_iov
  1,                # msg_iovlen
  pointer(cmsghdr), # msg_control
  cmsg_space,       # msg_controllen
  0                 # msg_flags
].pack("L_i_L_i_L_i_i_")

syscall(28, pointer(msghdr), 0) # 28 is sendmsg()

end

Here’s the C snippet:

union {
struct cmsghdr hdr;
char cred[CMSG_SPACE(sizeof(struct cmsgcred))];
} cmsg;
struct iovec iov[1];

struct msghdr msg;
iov[0].iov_base = (void *)buf;
iov[0].iov_len = count;

memset(&msg, 0, sizeof(msg));
msg.msg_iov = iov;
msg.msg_iovlen = 1;

msg.msg_control = (caddr_t)&cmsg;
msg.msg_controllen = CMSG_SPACE(sizeof(struct cmsgcred));
memset(&cmsg, 0, sizeof(cmsg));
cmsg.hdr.cmsg_len = CMSG_LEN(sizeof(struct cmsgcred));
cmsg.hdr.cmsg_level = SOL_SOCKET;
cmsg.hdr.cmsg_type = SCM_CREDS;

sendmsg(fd, &msg, 0);

Thanks in advance,
Andre

On Tue, 2008-08-26 at 04:30 +0900, Andre N. wrote:

I’m trying to call the sendmsg() system call from ruby in a way that
allocates space for the kernel to set the user credentials in the
appropriate structures (this is in FreeBSD i386).

For the record, this is how I got it to work. I was wrong in the way I
was packing cmsg_data, and I also had forgotten passing an argument to
the syscall (duh).

def sendmsg(fd, buf)
iov = [buf, buf.length].pack(“pI_”)

cmsg_len = 96       # CMSG_LEN(sizeof(struct cmsgcred))
cmsg_space = 96     # CMSG_SPACE(sizeof(struct cmsgcred))
cmsg_level = 0xffff # SOL_SOCKET
cmsg_type = 0x03    # SCM_CREDS
cmsg_data_len = 84

cmsghdr = ([
  cmsg_len,
  cmsg_level,
  cmsg_type
] + [0] * cmsg_data_len).pack("I_i_i_C#{cmsg_data_len}")

msg_control_ptr = pointer(cmsghdr)
msg_controllen = cmsg_space

msghdr = [
  0,                # msg_name
  0,                # msg_namelen
  pointer(iov),     # msg_iov
  1,                # msg_iovlen
  pointer(cmsghdr), # msg_control
  cmsg_space,       # msg_controllen
  0                 # msg_flags
].pack("L_i_L_i_L_i_i_")

syscall(28, fd.fileno, pointer(msghdr), 0)

end