Abhishek Gupta
Abhishek Gupta

Reputation: 6615

sendmsg not working while sending file descriptor

I am using unix domain sockets to send open file descriptor between different processes. Unix domain sockets work fine but when i used sendmsg to send file descriptor something wierd happened. The function returns just after sendmsg and sendmsg not returning anything. And then recvmsg is giving Invalid Argument error. Here is my code.

Client (sending the file descriptor):

#include <stropts.h>
#include "accesories.c"
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdio.h>


#define CONTROLLEN  CMSG_LEN(sizeof(int))
static struct cmsghdr   *cmptr = NULL;  /* malloc'ed first time */

int send_err(int fd, int errcode, const char *msg);
int send_fd(int fd, int fd_to_send);

int main(int argc, char const *argv[])
{
    int fd_to_send;
    if((fd_to_send = open("vi",O_RDONLY)) < 0)
        printf("vi open failed");

    struct sockaddr_un address;
    int  socket_fd, nbytes;
    char buffer[256];

    socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
    if(socket_fd < 0)
    {
        printf("socket() failed\n");
        return 1;
    }

    /* start with a clean address structure */
    memset(&address, 0, sizeof(struct sockaddr_un));

    address.sun_family = AF_UNIX;
    snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");

    if(connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
    {
        printf("connect() failed\n");
        return 1;
    }

    nbytes = snprintf(buffer, 256, "hello from a client");
    write(socket_fd, buffer, nbytes);

    nbytes = read(socket_fd, buffer, 256);
    buffer[nbytes] = 0;

    printf("MESSAGE FROM SERVER: %s\n", buffer);

    //sending the file descriptor    
    printf("From send_fd %d \n",send_fd(socket_fd,fd_to_send));

    close(socket_fd);

    exit(0);

}

int send_err(int fd, int errcode, const char *msg)
{
    int     n;

    if ((n = strlen(msg)) > 0)
        if (write(fd, msg, n) != n)    /* send the error message */
            return(-1);

    if (errcode >= 0)
        errcode = -1;   /* must be negative */

    if (send_fd(fd, errcode) < 0)
        return(-1);

    return(0);
}

int send_fd(int fd, int fd_to_send)
{

    int temp;
    struct iovec    iov[1];
    struct msghdr   msg;
    char            buf[2]; /* send_fd()/recv_fd() 2-byte protocol */

    iov[0].iov_base = buf;
    iov[0].iov_len  = 2;
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;
    if (fd_to_send < 0) {
        msg.msg_control    = NULL;
        msg.msg_controllen = 0;
        buf[1] = -fd_to_send;   /* nonzero status means error */
        if (buf[1] == 0)
            buf[1] = 1; /* -256, etc. would screw up protocol */
    } else {
        if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
            return(-1);
        cmptr->cmsg_level  = SOL_SOCKET;
        cmptr->cmsg_type   = SCM_RIGHTS;
        cmptr->cmsg_len    = CONTROLLEN;
        msg.msg_control    = cmptr;
        msg.msg_controllen = CONTROLLEN;
        *(int *)CMSG_DATA(cmptr) = fd_to_send;     /* the fd to pass */
        buf[1] = 0;          /* zero status means OK */
    }
    buf[0] = 0;              /* null byte flag to recv_fd() */
    printf("before sendmsg \n");
    if (temp = sendmsg(fd, &msg, 0) != 2)
    {
        printf("inside sendmsg condition %d\n",temp);
        return(-1);  
    }
    printf("after sendmsg %d\n",temp);
    return(0);

}

Server (recv the file descriptor):

#include <stropts.h>
#include "accesories.c"
#include <sys/ioctl.h>
#include <sys/un.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>

#define MAXLINE 10
/* size of control buffer to send/recv one file descriptor */
#define CONTROLLEN  CMSG_LEN(sizeof(int))

static struct cmsghdr   *cmptr = NULL;      /* malloc'ed first time */

int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t));
ssize_t errcheckfunc(int a,const void *b, size_t c);
int connection_handler(int connection_fd);

int main(int argc, char const *argv[])
{
  struct sockaddr_un address;
  int socket_fd, connection_fd;
  socklen_t address_length;
  pid_t child;

  socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
  if(socket_fd < 0)
  {
    printf("socket() failed\n");
    return 1;
  } 

  unlink("./demo_socket");

  /* start with a clean address structure */
  memset(&address, 0, sizeof(struct sockaddr_un));

  address.sun_family = AF_UNIX;
  snprintf(address.sun_path, sizeof(address.sun_path)-1, "./demo_socket");

  if(bind(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)) != 0)
  {
    printf("bind() failed\n");
    return 1;
  }

  if(listen(socket_fd, 5) != 0)
  {
    printf("listen() failed\n");
    return 1;
  }

  while((connection_fd = accept(socket_fd, (struct sockaddr *) &address,&address_length)) > -1)
  {
    connection_handler(connection_fd);

    int fd_to_recv;
    fd_to_recv = recv_fd(socket_fd,&errcheckfunc);

    if(read(fd_to_recv,msgbuf,5) < 0)
      printf("message read failed");
    printf("message received:%s\n",msgbuf);

    close(connection_fd);
  }

  close(socket_fd);
  unlink("./demo_socket");

  return 0;
}

int recv_fd(int fd, ssize_t (*userfunc)(int, const void *, size_t))
{
  int             newfd, nr, status;
  char            *ptr;
  char            buf[MAXLINE];
  struct iovec    iov[1];
  struct msghdr   msg;

  status = -1;
  for ( ; ; ) 
  {
    iov[0].iov_base = buf;
    iov[0].iov_len  = sizeof(buf);
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;
    if (cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
      return(-1);
    msg.msg_control    = cmptr;
    msg.msg_controllen = CONTROLLEN;
    if ((nr = recvmsg(fd, &msg, 0)) < 0) 
    {
      printf("recvmsg errrrror %d %d %s\n",nr,errno,strerror(errno));
      //perror("recvmsg errrrror");
    } else if (nr == 0) 
    {
      perror("connection closed by server");
      return(-1);
    }
    /*
    * See if this is the final data with null & status.  Null
    * is next to last byte of buffer; status byte is last byte.
    * Zero status means there is a file descriptor to receive.
    */
    for (ptr = buf; ptr < &buf[nr]; ) 
    {
      if (*ptr++ == 0) 
      {
        if (ptr != &buf[nr-1])
          perror("message format error");
        status = *ptr & 0xFF;  /* prevent sign extension */
        if (status == 0) 
        {
          if (msg.msg_controllen != CONTROLLEN)
          perror("status = 0 but no fd");
          newfd = *(int *)CMSG_DATA(cmptr);
        } else 
        {
          newfd = -status;
        }
        nr -= 2;
      }
    }
    if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
      return(-1);
    if (status >= 0)    /* final data has arrived */
      return(newfd);  /* descriptor, or -status */
  }
}

ssize_t errcheckfunc(int a,const void *b, size_t c)
{
    return 0;
}

int connection_handler(int connection_fd)
{
  int nbytes;
  char buffer[256];

  nbytes = read(connection_fd, buffer, 256);
  buffer[nbytes] = 0;

  printf("MESSAGE FROM CLIENT: %s\n", buffer);
  nbytes = snprintf(buffer, 256, "hello from the server");
  write(connection_fd, buffer, nbytes);


  return 0;
}

output of client:

MESSAGE FROM SERVER: hello from the server
before sendmsg 

Not even inside sendmsg condition and after sendmsg prints?

Upvotes: 1

Views: 3231

Answers (1)

Armali
Armali

Reputation: 19375

Since feature requests to mark a comment as an answer remain declined, I copy the above solution here.

Your call to recvmsg() is trying to receive data from a listening socket rather than a connected socket, because your main loop is passing socket_fd to recv_fd() rather than connection_fd. – Matthew Slattery

Upvotes: 3

Related Questions