第十五章 Unix域协议:
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sun_family; /* AF_LOCAL */
char sun_path[104]; /* null-teminated pathname */
};
sun_path[0] means any-address
#include <sys/socket.h>
int socketpair(int family, int type, int protocol, int sockfd[2]);
family: AF_LOCAL
type: SOCK_STREAM, SOCK_DGRAM
protocol: 0
success return non-zero, error return -1
#include <sys/socket.h>
struct cmsgcred { /* cannot find in Linux */
pid_t cmcred_pid; /* PID of sending process */
uid_t cmcred_uid; /* real UID of sending process */
uid_t cmcred_euid; /* effective UID of sending process */
gid_t cmcred_gid; /* read GID of sending process */
short cmcred_ngroups; /* number of groups */
gid_t cmcred_groups[CMGROUP_MAX]; /* groups */
};
CMGROUP_MAX is 16, cmcred_ngroups >= 1, cmcred_groups[0] is effective gid
示例:
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include "err_exit.h"
#include "my_signal.h"
#define PORT 9999
#define ADDR "127.0.0.1"
#define MAXBACKLOG 100
struct sockaddr_in serv;
pid_t pid;
int pipefd[2];
void do_parent(void);
void do_child(void);
int
main(int argc, char ** argv)
{
if (argc != 1) {
printf("usage: %s\n", argv[0]);
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd) == -1) {
err_exit("socketpair error");
}
bzero(&serv, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(PORT);
if (inet_pton(AF_INET, ADDR, &serv.sin_addr) <= 0) {
err_exit("inet_pton error");
}
if ((pid = fork()) < 0) {
err_exit("fork error");
}
else if (pid == 0) {
do_child();
}
else {
do_parent();
}
exit(0);
}
void
parent_alrm(int signo)
{
return; /* just interrupt blocked connect() */
}
void
do_parent(void)
{
int backlog;
int j;
int k;
int junk;
int fd[MAXBACKLOG + 1];
if (close(pipefd[0]) == -1) {
err_exit("close error");
}
if (my_signal(SIGALRM, parent_alrm) == SIG_ERR) {
err_exit("my_signal error");
}
for (backlog = 0; backlog <= 14; backlog++) {
printf("backlog = %d: ", backlog);
/* tell child value */
if (write(pipefd[1], &backlog, sizeof(int)) != sizeof(int)) {
err_exit("write error");
}
/* wait for child */
if (read(pipefd[1], &junk, sizeof(int)) < 0) {
err_exit("read error");
}
for (j = 1; j <= MAXBACKLOG; j++) {
if ((fd[j] = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
err_exit("socket error");
}
alarm(2);
if (connect(fd[j], (struct sockaddr *)&serv, sizeof(serv)) < 0) {
if (errno != EINTR) {
printf("connect error, j = %d\n", j);
exit(1);
}
printf("timeout, %d connections completed\n", j-1);
for (k = 1; k <= j; k++) {
if (close(fd[k]) == -1) {
err_exit("close error");
}
}
break; /* next value of backlog */
}
alarm(0);
}
if (j > MAXBACKLOG) {
printf("%d connections?\n", MAXBACKLOG);
}
}
backlog = -1; /* tell child we're all done */
if (write(pipefd[1], &backlog, sizeof(int)) != sizeof(int)) {
err_exit("write error");
}
}
void
do_child(void)
{
int listenfd;
int backlog;
int junk;
const int on = 1;
if (close(pipefd[1]) == -1) {
err_exit("close error");
}
/* wait for parent */
if (read(pipefd[0], &backlog, sizeof(int)) != sizeof(int)) {
err_exit("read error");
}
while (backlog >= 0) {
if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
err_exit("socket error");
}
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof(on)) == -1) {
err_exit("setsockopt error");
}
if (bind(listenfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
err_exit("bind error");
}
if (listen(listenfd, backlog) == -1) { /* start the listen */
err_exit("listen error");
}
/* tell parent */
if (write(pipefd[0], &junk, sizeof(int)) != sizeof(int)) {
err_exit("write error");
}
/* just wait for parent */
if (read(pipefd[0], &backlog, sizeof(int)) != sizeof(int)) {
err_exit("read error");
}
/* closes all queued connections, too */
if (close(listenfd) == -1) {
err_exit("close error");
}
}
}
#include <sys/socket.h>
#include <sys/param.h>
#include "err_exit.h"
#define HAVE_MSGHDR_MSG_CONTROL 1
ssize_t
write_fd(int fd, void * ptr, size_t nbytes, int sendfd)
{
struct msghdr msg;
struct iovec iov[1];
#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);
cmptr = CMSG_FIRSTHDR(&msg);
cmptr->cmsg_len = CMSG_LEN(sizeof(int));
cmptr->cmsg_level = SOL_SOCKET;
cmptr->cmsg_type = SCM_RIGHTS;
*((int *)CMSG_DATA(cmptr)) = sendfd;
#else
msg.msg_accrights = (caddr_t) &sendfd;
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;
return(sendmsg(fd, &msg, 0));
}
#include "err_exit.h"
#include "write_fd.h"
int
main(int argc, char ** argv)
{
int fd;
if (argc != 4) {
printf("openfile <sockfd#> <filename> <mode>\n");
exit(1);
}
if ((fd = open(argv[2], atoi(argv[3]))) < 0) {
exit(errno > 0 ? errno : 255);
}
if (write_fd(atoi(argv[1]), "", 1, fd) < 0) {
exit(errno > 0 ? errno : 255);
}
exit(0);
}
#include <sys/socket.h>
#include <sys/param.h>
#include "err_exit.h"
#define HAVE_MSGHDR_MSG_CONTROL 1
ssize_t
read_fd(int fd, void * ptr, size_t nbytes, int * recvfd)
{
struct msghdr msg;
struct iovec iov[1];
ssize_t n;
#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
int newfd;
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);
}
#ifdef HAVE_MSGHDR_MSG_CONTROL
if ((cmptr = CMSG_FIRSTHDR(&msg)) != NULL &&
cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {
if (cmptr->cmsg_level != SOL_SOCKET) {
err_exit("control level != SOL_SOCKET");
}
if (cmptr->cmsg_type != SCM_RIGHTS) {
err_exit("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);
}
#include <sys/socket.h>
#include <wait.h>
#include <unistd.h>
#include "err_exit.h"
int
my_open(const char * pathname, int mode)
{
int fd;
int sockfd[2];
int status;
pid_t childpid;
char c;
char argsockfd[10];
char argmode[10];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) == -1) {
err_exit("socketpair error");
}
if ((childpid = fork()) < 0) {
err_exit("fork error");
}
else if (childpid == 0) {
if (close(sockfd[0]) == -1) {
err_exit("close error");
}
snprintf(argsockfd, sizeof(argsockfd), "%d", sockfd[1]);
snprintf(argmode, sizeof(argmode), "%d", mode);
execl("./openfile", "openfile", argsockfd, pathname, argmode, NULL);
err_exit("execl error");
}
if (close(sockfd[1]) == -1) {
err_exit("close error");
}
if (waitpid(childpid, &status, 0) <= 0) {
err_exit("waitpid error");
}
if (WIFEXITED(status) == 0) {
err_exit("child did not terminate");
}
if ((status = WEXITSTATUS(status)) == 0) {
if (read_fd(sockfd[0], &c, 1, &fd) <= 0) {
err_exit("read_fd error");
}
}
else {
errno = status; /* set errno value from child's status */
fd = -1;
}
if (close(sockfd[0]) == -1) {
err_exit("close error");
}
return(fd);
}
#include <fcntl.h>
#include <unistd.h>
#include "err_exit.h"
#include "myopen.h"
#define BUFFSIZE 4096
int
main(int argc, char ** argv)
{
int fd;
int n;
char buff[BUFFSIZE];
if (argc != 2) {
printf("usage: mycat <pathname>\n");
exit(1);
}
if ((fd = my_open(argv[1], O_RDONLY)) < 0) {
printf("cannot open %s\n", argv[1]);
exit(1);
}
while ((n = read(fd, buff, BUFFSIZE)) > 0) {
if (write(STDOUT_FILENO, buff, n) != n) {
err_exit("write error");
}
}
if (n < 0) {
err_exit("read error");
}
exit(0);
}
#include <sys/socket.h>
#include <sys/un.h>
#include "err_exit.h"
int
main(int argc, char ** argv)
{
int sockfd;
socklen_t len;
struct sockaddr_un addr1;
struct sockaddr_un addr2;
if (argc != 2) {
printf("usage: unixbind <pathname>\n");
exit(1);
}
if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
err_exit("socket error");
}
unlink(argv[1]); /* OK if this fails */
bzero(&addr1, sizeof(addr1));
addr1.sun_family = AF_LOCAL;
strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
if (bind(sockfd, (struct sockaddr *)&addr1, SUN_LEN(&addr1)) == -1) {
err_exit("bind error");
}
len = sizeof(addr2);
if (getsockname(sockfd, (struct sockaddr *)&addr2, &len) == -1) {
err_exit("getsockname error");
}
printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);
exit(0);
}
#include <sys/socket.h>
#include <sys/un.h>
#include "err_exit.h"
#define UNIXDG_PATH "/tmp/uinxdg"
int
main(int argc, char ** argv)
{
int sockfd;
struct sockaddr_un servaddr;
struct sockaddr_un cliaddr;
if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) {
err_exit("socket error");
}
unlink(UNIXDG_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
if (bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
err_exit("bind error");
}
dg_echo(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
}
#include <sys/socket.h>
#include <sys/un.h>
#include "err_exit.h"
#define UNIXDG_PATH "/tmp/uinxdg"
int
main(int argc, char ** argv)
{
int sockfd;
struct sockaddr_un cliaddr;
struct sockaddr_un servaddr;
if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) {
err_exit("socket error");
}
bzero(&cliaddr, sizeof(cliaddr)); /* bind an address for us */
cliaddr.sun_family = AF_LOCAL;
strcpy(cliaddr.sun_path, tmpnam(NULL));
if (bind(sockfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) == -1) {
err_exit("bind error");
}
bzero(&servaddr, sizeof(servaddr)); /* fill in server's address */
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXDG_PATH);
dg_cli(stdin, sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
exit(0);
}
#include <sys/socket.h>
#include <sys/un.h>
#include "err_exit.h"
#define UNIXSTR_PATH "/tmp/unixstr"
int
main(int argc, char ** argv)
{
int sockfd;
struct sockaddr_un servaddr;
if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
err_exit("socket error");
}
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
err_exit("connect error");
}
str_cli(stdin, sockfd);
exit(0);
}
#include <sys/socket.h>
#include <sys/un.h>
#include "err_exit.h"
#include "my_signal.h"
#define LISTENQ 1024
#define UNIXSTR_PATH "/tmp/unixstr"
void
sig_chld(int signo)
{
pid_t pid;
int stat;
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
printf("child %d terminated\n", pid);
}
return;
}
int
main(int argc, char ** argv)
{
int listenfd;
int connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_un cliaddr;
struct sockaddr_un servaddr;
if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1) {
err_exit("socket error");
}
unlink(UNIXSTR_PATH);
bzero(&servaddr, sizeof(servaddr));
servaddr.sun_family = AF_LOCAL;
strcpy(servaddr.sun_path, UNIXSTR_PATH);
if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1) {
err_exit("bind error");
}
if (listen(listenfd, LISTENQ) == -1) {
err_exit("listen error");
}
if (my_signal(SIGCHLD, sig_chld) == SIG_ERR) {
err_exit("my_signal error");
}
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr,
&clilen)) < 0) {
if (errno == EINTR) {
continue; /* back to for() */
}
else {
err_exit("accept error");
}
}
if ((childpid = fork()) < 0) {
err_exit("fork error");
}
else if (childpid == 0) {
if (close(listenfd) == -1) {
err_exit("close error");
}
str_echo(connfd);
exit(0);
}
if (close(connfd) == -1) {
err_exit("close error");
}
}
}
#include <sys/socket.h>
#include "err_exit.h"
#define CONTROL_LEN (sizeof(struct cmsghdr) + sizeof(struct cmsgcred))
ssize_t
read_cred(int fd, void * ptr, size_t nbytes, struct cmsgcred * cmsgcredptr)
{
struct msghdr msg;
struct iovec iov[1];
char control[CONTROL_LEN];
int n;
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;
msg.msg_control = control;
msg.msg_controllen = sizeof(control);
msg.msg_flags = 0;
if ((n = recvmsg(fd, &msg, 0)) < 0) {
return(n);
}
cmsgcredptr->cmcred_ngroups = 0; /* indicates no credentials returned */
if (cmsgcredptr && msg.msg_controllen > 0) {
struct cmsghdr * cmptr = (struct cmsghdr *)control;
if (cmptr->cmsg_len < CONTROL_LEN) {
printf("control length = %d\n", cmptr->cmsg_len);
exit(1);
}
if (cmptr->cmsg_level != SOL_SOCKET) {
err_exit("control level != SOL_SOCKET");
}
if (cmptr->cmsg_type != SCM_CREDS) {
err_exit("control type != SCM_CREDS");
}
memcpy(cmsgcredptr, CMSG_DATA(cmptr), sizeof(struct cmsgcred));
}
return(n);
}
#include <sys/socket.h>
#include "err_exit.h"
#include "writen.h"
#include "readcred.h"
ssize_t read_cred(int, void *, size_t, struct cmsgcred *);
#define MAXLINE 4096
void
str_echo(int sockfd)
{
ssize_t n;
int i;
char buf[MAXLINE];
struct cmsgcred cred;
again:
while ((n = read_cred(sockfd, buf, MAXLINE, &cred)) > 0) {
if (cred.cmcred_ngroups == 0) {
printf("(no credentials returned)\n");
}
else {
printf("PID of sender = %d\n", cred.cmcred_pid);
printf("real user ID = %d\n", cred.cmcred_uid);
printf("real group ID = %d\n", cred.cmcred_gid);
printf("effective user ID = %d\n", cred.cmcred_euid);
printf("%d groups:", cred.cmcred_ngroups - 1);
for (i = 1; i < cred.cmcred_ngroups; i++) {
printf(" %d", cred.cmcred_groups[i]);
}
printf("\n");
}
if (writen(sockfd, buf, n) != n) {
err_exit("write error");
}
}
if (n < 0 && errno == EINTR) {
goto again;
}
else if (n < 0) {
err_exit("str_echo: read error");
}
}