网络协议(四)Socket编程之Unix

Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。
1)Unix域套接字往往比通信两端位于同一主机的TCP套接字要快;
2)Unix域套接字可用于在同一主机上的不同进程之间传递描述符;
3)Unix域套接较新的实现把客户端的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。

Unix域中用于标识客户和服务端的协议地址是普通文件系统中的路径名。



struct sockaddr_un {
	sa_family_t sun_family;     
	char        sum_path[104]; /* null-terminated pathname */
}
#include "../myunp.h"


int main(int argc, char **argv)
{
	int sockfd;
	socklen_t len;
	struct sockaddr_un addr1, addr2;

	if (argc != 2) {
		printf("usage: unix bind ");
		return -1;
	}

	if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
		printf("socket error: %s\n", strerror(errno));
		return -1;
	} 

	unlink(argv[1]);  /* OK if this fails */

	memset(&addr1, 0, sizeof(addr1));
	addr1.sun_family = AF_LOCAL;
	strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path) - 1);

	if (bind(sockfd, (struct sockaddr *)&addr1, sizeof(addr1)) < 0) {
		printf("bind error: %s\n" strerror(errno));
		return -1;
	}

	len = sizeof(addr2);
	getsockname(sockfd, (struct sockaddr *)&addr2, &len);
	printf("bound name = %s, returned len = %d\n", addr2.sun_path, len);

	return 0;
}
(1)UDP
#include "../myunp.h"

void UdpClient(int sockfd, const SA *pservaddr, socklen_t servlen)
{
	int n = 0;
	char sendline[MAXLINE] = {0};
	char recvline[MAXLINE + 1] = {0};
	socklen_t len = 0;
	struct sockaddr *preplyaddr = NULL;


	while (fgets(sendline, MAXLINE, stdin) != NULL) {
		if (sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen) < 0) {
			printf("sendto error: %s\n", strerror(errno));
			return;
		}

		len = servlen;
		if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, preplyaddr, &len)) < 0) {
			printf("recvfrom error: %s\n", strerror(errno));
			return;
		}
		
		recvline[n] = 0; /* null terminate */
		fputs(recvline, stdout);
	}
}

int main(int argc, char **argv)
{
	int sockfd;
	struct sockaddr_un servaddr;
	struct sockaddr_un cliaddr;

	if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
		printf("sockfd error: %s\n", strerror(errno));
		return -1;
	}

	memset(&cliaddr, '\0', sizeof(cliaddr));
	cliaddr.sun_family = AF_LOCAL;
	strcpy(cliaddr.sun_path, tmpnam(NULL));
	
	/* bind an address for us */
	if (bind(sockfd, (SA *)&cliaddr, sizeof(cliaddr)) < 0) {
		printf("bind error: %s\n", strerror(errno));
		return -1;
	}

	
	memset(&servaddr, '\0', sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strcpy(servaddr.sun_path, UNIXDG_PATH);	
	
	UdpClient(sockfd, (SA *)&servaddr, sizeof(servaddr));

	return 0;
}
#include "../myunp.h"


void UdpEcho(int sockfd, SA *pcliaddr, socklen_t clilen)
{
	int n = 0;
	socklen_t len = 0;
	char msg[MAXLINE] = {0};

	for ( ; ; ) {
		len = clilen;
		if ((n = recvfrom(sockfd, msg, MAXLINE, 0, pcliaddr, &len)) < 0) {
			printf("recvfrom error: %s\n", strerror(errno));
			return ;
		} 

		if (sendto(sockfd, msg, n, 0, pcliaddr, len) < 0) {
			printf("sendto error: %s\n", strerror(errno));
			return ;
		}
	}
}

int main(int argc, int **argv)
{
	int sockfd;
	struct sockaddr_un servaddr;
	struct sockaddr_un cliaddr;

	if ((sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0)) < 0) {
		printf("socked error: %s\n", strerror(errno));
		return -1;
	}

	unlink(UNIXDG_PATH);
	memset(&servaddr, '\0', sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strcpy(servaddr.sun_path, UNIXDG_PATH);

	if (bind(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0) {
		printf("bind error: %s\n", strerror(errno));
		return -1;
	}

	UdpEcho(sockfd, (SA *)&cliaddr, sizeof(cliaddr));
}
(2)TCP

#include "../myunp.h"

#include 

void StrCli(FILE *fp, int sockfd)
{
	char sendline[MAXLINE] = {0};
	char recvline[MAXLINE] = {0};

	while (fgets(sendline, MAXLINE, fp) != NULL) {
		writen(sockfd, sendline, strlen(sendline));

		if (readline(sockfd, recvline, MAXLINE) == 0) {
			printf("StrCli: server terminated prematurely\n");
			return;
		}
		fputs(recvline, stdout);
	}
}

int main(int argc, char **argv)
{
	int sockfd;
	struct sockaddr_un servaddr;

	if ((sockfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
		printf("socket error\n");
		return -1;
	}

	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strcpy(servaddr.sun_path, UNIXSTR_PATH);

	if ((connect(sockfd, (SA *)&servaddr, sizeof(servaddr))) < 0) {
		printf("connect error: %s\n", strerror(errno));
		return -1;
	}

	StrCli(stdin, sockfd); /* do it all */

	return 0;
}
#include "../myunp.h"

#include 

void sig_child(int signo)
{
	pid_t pid;
	int stat;

	while ((pid = waitpid(-1, &stat, WNOHANG)) > 0) {
		printf("child %d terminated\n", pid);
	}

	return;
}

void StrEcho(int sockfd)
{
	ssize_t n = 0;
	char buf[MAXLINE] = {0};

	for ( ; ; ) {
		while ((n = read(sockfd, buf, MAXLINE)) > 0) {
			if (writen(sockfd, buf, n) != n) {
				printf("writen error: %s\n", strerror(errno));
				return ;
			}
		}

		if (n < 0 && errno == EINTR) {
			continue;
		} else if (n < 0) {
			printf("read error: %s\n", strerror(errno));
			return ;
		}

		break;
	}
}

int main()
{
	int listenfd;
	int connfd;
	int optval = 1;
	pid_t childpid;
	socklen_t clilen;
	struct sockaddr_un cliaddr;
	struct sockaddr_un servaddr;

	if ((listenfd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
		printf("socket error: %s\n", strerror(errno));
		return -1;
	}
	
	/* Eliminates "Address already in use" error from bind */
	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
		(const void *)&optval, sizeof(optval)) < 0) {
		printf("setsockopt error\n");
		return -1;
	}
	
	unlink(UNIXSTR_PATH);
	memset(&servaddr, '\0', sizeof(servaddr));
	servaddr.sun_family = AF_LOCAL;
	strcpy(servaddr.sun_path, UNIXSTR_PATH);

	if (bind(listenfd, (SA *)&servaddr, sizeof(servaddr)) < 0) {
		printf("bind error: %s\n", strerror(errno));
		return -1;
	}

	if (listen(listenfd, LISTENQ) < 0) {
		printf("listen error: %s\n", strerror(errno));
		return -1;
	}

	signal(SIGCHLD, sig_child);

	for ( ; ; ) {
		clilen = sizeof(cliaddr);
		if ((connfd = accept(listenfd, (SA *)&cliaddr, &clilen)) < 0) {
			if (errno == EINTR) {
				continue;      /* back to for */
			} else {
				printf("accept error: %s\n", strerror(errno));
				return -1;
			}
		}
		
		if ((childpid = fork()) == 0) { /* child process */
			close(listenfd); /* close listening socket */
			StrEcho(connfd); /* process the request */
			exit(0);
		}

		close(connfd);       /* parent closes connected socket */
	}
}
可以使用Unix域在进程间传递描述符。
传递一个描述符并不是传递一个描述符号,而是涉及在接受进程中创建一个新的描述符,而这个新的描述符和发送进程中飞行(in flight)前的那个描述符指向内核中相同的文件表项。
通过执行另一个程序来打开文件的优势在于,另一个程序可以是一个setuid到root的程序,能够打开我们通常没有打开权限的文件。该程序能够把通常的Unxi权限概念(用户、用户组和其他用户)扩展到它想要的任何形式的访问检查。
#include "../myunp.h"


int my_open(const char *, int);

ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd);

int main(int argc, char **argv)
{
	int fd, n;
	char buff[BUFFSIZE] = { 0 };

	if (argc != 2) {
		printf("usage: mycat \n");
		return -1;
	}

	if ((fd = my_open(argv[1], O_RDONLY)) < 0) {
		printf("cannot open %s\n", argv[1]);
		return -1;
	}

	while ((n = read(fd, buff, BUFFSIZE)) > 0) {
		write(STDOUT_FILENO, buff, n);
	}

	exit(0);
}

int my_open(const char *pathname, int mode)
{
	int fd, status;
	int sockfd[2];
	pid_t childpid;
	char c;
	char argsockfd[10] = { 0 };
	char argmode[10] = { 0 };

	if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0) {
		printf("socketpair error: %s\n", strerror(errno));
		return -1;
	}

	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);
		printf("execl error: %s\n", strerror(errno));
		return -1;
	}

	/* 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) {
		printf("child did not ternimate");
	}
	
	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;
}

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) {
			printf("control level != SOL_SOCKET\n");
			return -1;
		}	

		if (cmptr->cmsg_type != SCM_RIGHTS) {
			printf("control type != SCM_RIGHTS\n");
			return -1;
		}

		*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 "../myunp.h"


ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd);

int main(int argc, char **argv)
{
	int fd;

	if (argc != 4) {
		printf("openfile   ");
		return -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);
}

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));
}

你可能感兴趣的:(网络)