Unix Domain Sockets - Tamkang University

Download Report

Transcript Unix Domain Sockets - Tamkang University

Unix Domain Sockets
Computer Network Programming
1
Outline
• Motivation
• What are Unix domain sockets
• address structure
• How to use unix domain sockets
• Example client-server apps using UDSs.
• Example use of UDSs.
• Passing descriptors
• Passing credentials
2
Motivation
– We need a way of interprocess communication
between process running in the same host
• faster than TCP/IP
– But we want to use the same socket API
• hence our programs for TCP and UDP sockets will
work with little modifications for processes running
on the same host.
– (TCP and UDP sockets can also be used for
communicatşon between process on the same host but it is
slower
Use Unix Domain Sockets
3
Unix Domain Sockets
• Not an actual protocol suite like TCP/IP
• For Unix only
• A way of performing IPC between
processes running on the same host using
the same socket API.
• Two types of UD sockets
• stream sockets (similar to TCP)
• datagram sockets (similar to UDP)
4
Uses of UD sockets
– They are faster than TCP and UDP sockets
• at least twice
• for example X Window System uses UD sockets of
client is on the same host as the server
» client checks the DISPLAY environment variable
– Used for passing file (socket) descriptors
• any descriptor can be passed
– Used to pass client’s credentials (user ID etc) to
the server (can be used for security check).
5
Protocol Addresses
– Pathnames are used as protocol addresses
• instead of IP address, port number pair
struct sockaddr_un {
uint8_t
sun_len;/* 1 byte */
sa_familiy_t sun_family;/* 1 byte */
char
sun_path[104];
}
– The pathnames must be null-terminated.
– If pathname is null-string, it corresponds to
INADDR_ANY constant.
6
How to bind a protocol address (pathname) to a UD socket
int main(int argc, char **argv)
{
int
sockfd;
socklen_t
len;
struct sockaddr_un addr1, addr2;
if (argc != 2)
err_quit("usage: unixbind <pathname>");
sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
unlink(argv[1]);
/* OK if this fails */
If pathname exists already,
bind will fail. Therefore we
remove the pathname first
using unlink function
When bind() is called it creates
a file(path) corresponding to
the pathname (protocol
address) provided as argument
bzero(&addr1, sizeof(addr1));
addr1.sun_family = AF_LOCAL;
strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
Bind(sockfd, (SA *) &addr1, SUN_LEN(&addr1));
len = sizeof(addr2);
Getsockname(sockfd, (SA *) &addr2, &len);
printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
exit(0);
}
7
Notes about use Socket Function with UD
sockets
– The pathname created should have file
permissions as 0777
– can read, write and execute by user, group or others
– The pathname should be absolute pathname.
– so that client and server does not have to be run in the
same directory
– For a client to be able to issue a connect()
– there has to be pathname in the file system
– there has to be an open socket on that pathname
– the socket and pathname should be of the same type
» stream or datagram
8
Notes about use Socket Function with UD
sockets
– Unix domain stream sockets are similar to TCP
sockets: byte stream oriented, no record
boundaries
– Unix domain datagram sockets are similar to
UDP sockets: unreliable and preserves record
boundaries.
– Unlike UDP, sending a datagram out of a
socket does not bind a pathname to the socket.
» Hence receiver can not send a reply back unless the
sender binds a pathname to its UD socket.
» Similarly issuing connect does not bind a pathname to
the socket
9
Sockpair function
• This function creates two sockets that are then
connected together. (only for Unix domain sockets)
int socketpair(int family, int type, int protocol, int sockfd[2]);
»
»
»
»
family is AF_LOCAL
protocol is 0
type is SOCK_DGRAM or SOCK_STREAM
sockfd[0] and sockfd[1] are two socket descriptors
• created sockets are unnamed
• they are full-duplex (data can go in each direction)
• also called stream pipes if type is SOCK_STREAM
10
Example of Unix Domain Sockets: Stream Server
#define UNIXSTR_PATH "/tmp/unix.str"
int main(int argc, char **argv)
{
int listenfd, connfd; pid_t childpid;
socklen_t clilen; struct sockaddr_un cliaddr, servaddr;
void sig_chld(int);
listenfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
unlink(UNIXSTR_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
Signal(SIGCHLD, sig_chld);
for ( ; ; ) {
/* Rest is same with a TCP server:
accept() called
fork() called
child process serves the requests*/
}
}
11
Example of Unix Domain Sockets: Stream Client
Same with a
TCP client
except we
need to fill
a sockaddr_un
structure with
the information
about the server
protocol address
#define UNIXSTR_PATH "/tmp/unix.str”
int main(int argc, char **argv)
{
int
sockfd;
struct sockaddr_un servaddr;
sockfd = Socket(AF_LOCAL, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
str_cli(stdin, sockfd);
/* do it all */
exit(0);
}
12
Example of Unix Domain Sockets: Datagram Server
Same with a
UDP server
except we
need to fill
a sockaddr_un
structure with
the information
about the server
protocol address
and bind it to the
server socket
#define UNIXDG_PATH
"/tmp/unix.dg"
int
main(int argc, char **argv)
{
int
sockfd;
struct sockaddr_un servaddr, cliaddr;
sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
unlink(UNIXDG_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
}
13
Example of Unix Domain Sockets: Datagram Client
Similar to a
UDP client
except we
need to bind
a protocol address
to the socket
#define UNIXDG_PATH
"/tmp/unix.dg"
int main(int argc, char **argv)
{
int
sockfd;
struct sockaddr_un
cliaddr, servaddr;
sockfd = Socket(AF_LOCAL, SOCK_DGRAM, 0);
bzero(&cliaddr, sizeof(cliaddr)); /* bind an address for us */
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, tmpnam(NULL));
Bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
tmpnam() generates
a temporary
file name.
bzero(&servaddr, sizeof(servaddr)); /* fill in server's address */
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
exit(0);
}
14
Passing Descriptors
• A descriptor is an integer value that corresponds to a
socket or file or terminal device etc.
• We learned how to pass open descriptors from
patent to child.
– Similarly we can pass descriptors as the arguments of exec
while executing an other program from the child process
– Files and sockets corresponding to descriptors that are
passed from parent to child remain open.
• We want also pass descriptors:
– from a child to a parent
– between two unrelated processes
• We can achieve this by using Unix domain sockets
15
Steps involved in passing descriptors between
two processes
• Create a Unix domain socket (stream or datagram)
– if the processes are parent and child then we can use
socketpair function
– if processes are unrelated then one should be server and
bind a well known pathname to the socket and the client
should talk/connect to this socket.
• One process (sending process)
– open a descriptor
» for example open a file (using open, socket, accept,
pipe…)
– builds a msghdr structure containg the descriptor to be
passed and then calls sendmsg
• Receiving process calls recvmsg to obtain the
descriptor and then uses it.
16
Example: mycat program
We want to execute the following scenerio
mycat
program
Unix domain
stream communication
openfile
program
(2)
stdout
(screen)
(4)
parent
fd
fd
fd
(1) Child open a file and
obtains descriptor fd
(2) Child sends the descriptor
to parent
(3) Parent reads from the received
descriptor - namely reads from the file
(4) Parent prints the content of the file
to the screen
child
(3)
(1)
file
By this method, a process may
be executing as root and opening
important files for other clients.
17
mycat program
fist create a socket pair (stream pipe)
mycat
[0]
[1]
then fork and exec a new program
exit (exit status)
mycat
openfile
fork
[0]
exec(pathname, mode, sockfd)
[1]
descriptor
18
Mycat program
int my_open(const char *, int);
int main(int argc, char **argv)
{
int
fd, n;
char buff[BUFFSIZE];
if (argc != 2)
err_quit("usage: mycat <pathname>");
if ( (fd = my_open(argv[1], O_RDONLY)) < 0)
err_sys("cannot open %s", argv[1]);
while ( (n = Read(fd, buff, BUFFSIZE)) > 0)
Write(STDOUT_FILENO, buff, n);
exit(0);
}
19
int my_open(const char *pathname, int mode)
{
int fd, sockfd[2], status; pid_t
childpid; char c, argsockfd[10], argmode[10];
Socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
if ( (childpid = Fork()) == 0) {
/* child process */
Close(sockfd[0]);
snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
snprintf(argmode, sizeof(argmode), "%d", mode);
execl("./openfile", "openfile", argsockfd, pathname, argmode, (char *) NULL);
err_sys("execl error");
}
/* parent process - wait for the child to terminate */
Close(sockfd[1]);
/* close the end we don't use */
Waitpid(childpid, &status, 0);
if (WIFEXITED(status) == 0)
err_quit("child did not terminate");
if ( (status = WEXITSTATUS(status)) == 0)
Read_fd(sockfd[0], &c, 1, &fd);
else {
errno = status;
/* set errno value from child's status */
fd = -1;
}
Close(sockfd[0]); return(fd);
}
20
Definition of msghdr structure in /usr/include/sys/socket.h
/*
* Message header for recvmsg and sendmsg calls.
*/
#if !defined(_XPG4_2)
struct msghdr {
caddr_t msg_name;
/* optional address */
int msg_namelen;
/* size of address */
struct iovec *msg_iov;
/* scatter/gather array */
int msg_iovlen;
/* # elements in msg_iov */
caddr_t msg_accrights;
/* access rights sent/received */
int msg_accrightslen;
};
#else
struct msghdr {
void *msg_name;
/* optional address */
size_t msg_namelen;
/* size of address */
struct iovec *msg_iov;
/* scatter/gather array */
int msg_iovlen;
/* # elements in msg_iov */
void *msg_control;
/* ancillary data */
size_t msg_controllen;
/* ancillary data buffer len */
int msg_flags;
/* flags on received message */
};
#endif /* !defined(_XPG4_2) */
21
read_fd() function
ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd)
{
struct msghdr msg; struct iovec iov[1]; ssize_t n; int newfd;
#ifdef
HAVE_MSGHDR_MSG_CONTROL
union {
struct cmsghdr cm;
char
control[CMSG_SPACE(sizeof(int))];
} control_un;
struct cmsghdr *cmptr;
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
#else
msg.msg_accrights = (caddr_t) &newfd;
msg.msg_accrightslen = sizeof(int);
#endif
msg.msg_name = NULL; msg.msg_namelen = 0;
iov[0].iov_base = ptr;
iov[0].iov_len = nbytes;
msg.msg_iov = iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(fd, &msg, 0)) <= 0)
return(n);
/* continued in the next page */
22
read_fd() function continued
#ifdef
/* conitinued from previous page */
HAVE_MSGHDR_MSG_CONTROL
if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET)
err_quit("control level != SOL_SOCKET");
if (cmptr->cmsg_type != SCM_RIGHTS)
err_quit("control type != SCM_RIGHTS");
*recvfd = *((int *) CMSG_DATA(cmptr));
} else
*recvfd = -1;
/* descriptor was not passed */
#else
if (msg.msg_accrightslen == sizeof(int))
*recvfd = newfd;
else
*recvfd = -1;
/* descriptor was not passed */
#endif
return(n);
}
23
openfile program
int
main(int argc, char **argv)
{
int fd;
ssize_t n;
if (argc != 4)
err_quit("openfile <sockfd#> <filename> <mode>");
if ( (fd = open(argv[2], atoi(argv[3]))) < 0)
exit( (errno > 0) ? errno : 255 );
if ( (n = write_fd(atoi(argv[1]), "", 1, fd)) < 0)
exit( (errno > 0) ? errno : 255 );
exit(0);
}
write_fd similar to read_fd. It prepares a msghdr structure with
control data (descriptor) in it and sends it to the mycat program
using sendmsg() function over Unix Datagram Stream Socket.
24
Sending Credentials
– Similar to sending descriptors, user credentials
can be sent over a Unix datagram socket using
sendmsg() and recvmsg() again as control
(ancillary) data
• credentials include:
–
–
–
–
–
read user ID (from password file for user)
read group ID (from password file for user)
effective user ID
login name
supplementary group id’s etc.
25