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
留言列表