Unix domain sockets have a unique ability to pass file descriptors.

#include <sys/sockets.h>

int sendmsg(int fd, const struct msghdr *msg, unsigned int flags);

int recvmsg(int fd, struct msghdr *msg, unsigned int flags);

sender:

static int connect_to_internal(void)
{
    int fd, tries;
    struct sockaddr_un addr;

    addr.sun_family = AF_UNIX;
    strcpy( addr.sun_path, "/tmp/shared.sock" );
    for ( tries = 0; tries < 12; ++tries )
    {
        if ( ( fd = socket( PF_UNIX, SOCK_STREAM, 0 ) ) < 0 )
        {
            perror( "socket" );
            return -1;
        }
        if ( connect( fd, (struct sockaddr *)&addr, sizeof( addr ) ) == 0 )
            return fd;

        close( fd );
        usleep( 250000 );
    }
    printf("unable to connect to Spook control socket");
    return -1;
}

/*
 * Pass a file descriptor to another process.
 * If fd<0, then -fd is sent back instead as the error status.
 */
static int send_fd(int fd, int fd_to_send)
{
    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() */
    if (sendmsg(fd, &msg, 0) != 2)
        return(-1);
    return(0);
}

#if TUNNEL_SHARED_SOCKET
    {
        int fd;
        if( (fd = connect_to_internal()) > 0 )
        {
            PDEBUG("pass the socket to spook to do http tunning");
            send_fd(fd, ourTunnel->incomingClientSocket);
            send_fd(fd, ourTunnel->outgoingClientSocket);
            close(fd);
        }
    }
#else

 

receiver:

/* 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 */

static ssize_t func(int fd, const void *buf, size_t size)
{
    return size;
}

/*
 * Receive a file descriptor from a server process.  Also, any data
 * received is passed to (*userfunc)(STDERR_FILENO, buf, nbytes).
 * We have a 2-byte protocol for receiving the fd from send_fd().
 */
static 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 error");
       } else if (nr == 0) {
           printf("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])
                   printf("message format error");
               status = *ptr & 0xFF;  /* prevent sign extension */
               if (status == 0) {
                   if (msg.msg_controllen != CONTROLLEN)
                       printf("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 */
   }
}

int tunnel_listen()
{
    struct listener *listener;
    int fd, addrlen, i;
    struct sockaddr_un addr;

    addr.sun_family = AF_UNIX;
    strcpy( addr.sun_path, "/tmp/shared.sock" );

    unlink( addr.sun_path );
    if ( ( fd = socket( PF_UNIX, SOCK_STREAM, 0 ) ) < 0 )
    {
        printf("error creating control socket: %s\n",
                   strerror( errno ) );
        return -1;
    }
    if ( bind( fd, (struct sockaddr *)&addr, sizeof( addr ) ) < 0 )
    {
        printf("unable to bind control socket: %s\n", strerror( errno ) );
        close( fd );
        return -1;
    }
    if ( listen( fd, 5 ) < 0 )
    {
        printf("error attempting to listen on control socket: %s\n", strerror( errno ) );
        close( fd );
        return -1;
    }

    listener = (struct listener *)malloc( sizeof( struct listener ) );
    listener->domain = PF_UNIX;
    listener->fd = fd;

    add_fd_event( fd, 0, 0, do_accept, listener );

    PDEBUG("unix domain listening for tunneling");
}

 

#if TUNNEL_SHARED_SOCKET
    if( listener->domain == PF_UNIX )
    {
        int control_fd = -1, data_fd = -1;
        control_fd = recv_fd(fd, func);
        PDEBUGG("receive control fd %d", control_fd);
        data_fd = recv_fd(fd, func);
        PDEBUGG("receive data fd %d", data_fd);
        close(fd);
        if( control_fd > 0 && data_fd > 0 )
            conn_new(addr, control_fd, data_fd);
        else
        {
            if( control_fd > 0 ) close(control_fd);
            if( data_fd > 0 )  close(data_fd);
        }
    }

#endif

arrow
arrow
    全站熱搜

    Person 發表在 痞客邦 留言(2) 人氣()