270 likes | 432 Vues
LUKU 15 – Unix Domain Socket. Unix Domain Socket Descriptorien välittäminen prosessien välillä Asiakkaan tietojen välittäminen (client credentials). UNIX Domain Socket. Socket rajapintaa käyttävä IPC mekanismi Stream ja Datagram protokollat ovat tuettuja, (RAW) ei. Osoitestruktuuri.
E N D
LUKU 15 – Unix Domain Socket • Unix Domain Socket • Descriptorien välittäminen prosessien välillä • Asiakkaan tietojen välittäminen (client credentials)
UNIX Domain Socket • Socket rajapintaa käyttävä IPC mekanismi • Stream ja Datagram protokollat ovat tuettuja, (RAW) ei
Osoitestruktuuri • #include sys/un.h • struct sockaddr_un { • sa_family_t sun_family; • char sun_path[108]; • }; • sun_path[0] = ”INADDR_ANY”;
unixdomain/unixbind.c • 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 */ • 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); • }
Huomioita • Bind ei onnistu, jos tiedoston nimi on jo olemassa →unlink() • Tarkasta polun mahtuminen osoitekenttään
socketpair • #include <sys/types.h> • #include <sys/socket.h> • int socketpair(int family, int type, int protocol, int sv[2]); • Osoite perheen pitää olla AF_LOCAL • tyyppi voi olla SOCK_STREAM tai SOCK_DGRAM • Vertaa pipe() funktioon!
Huomioita • Annetun polun tulisi olla absoluuttinen. Suhteellisten polkujen käyttäminen voi antaa epäluotettavia tuloksia. • Connect():lle annetun polun tulee olla avoin domain socket ja oikeaa protokolla tyyppiä • Tiedosto-oikeuksien tarkastus connect():in yhteydessä vastaa open() kutsua kirjoitustilaan. • Stream ja datagrammi protokollat • Connect palauttaa ECONNREFUSED, mikäli kuuntelevan socketin jono on täynnä. Ei uudelleen lähetystä! • Jos bind():iä ei ole kutsuttu, datagrammin mukana ei välity lähettäjän tietoja. Vastaanottaja ei voi siis lähettää vastausta.
unixdomain/unixstrserv01.c • 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 ( ; ; ) { • clilen = sizeof(cliaddr); • if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) { • if (errno == EINTR) • continue; /* back to for() */ • else • err_sys("accept error"); • } • if ( (childpid = Fork()) == 0) { /* child process */ • Close(listenfd); /* close listening socket */ • str_echo(connfd); /* process request */ • exit(0); • } • Close(connfd); /* parent closes connected socket */ • } • }
unixdomain/unixstrcli01.c • 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); • }
unixdomain/unixdgserv01.c • 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)); • }
unixdomain/unixdgcli01.c • 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)); • 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); • }
tmpnam • #include <stdio.h> • char *tmpnam(char *s); • Jos s == NULL, palauttaa sisäisen staattisen puskurin osoitteen • - s:n pitää olla L_tempnam pituinen
Descriptorin välittäminen • Luodaan domain socket tai käytetään socketpair-funktiota • forkataan, mikäli on tarkoitus, että lapsi avaa descriptorin, joka välitetään vanhemmalle • Joku prosessi avaa välitettävän descriptorin (open, pipe, mkfifo, socket, accept) • Lähettävä prosessi rakentaa msghdr struktuurin, jossa descriptori lähetetään • Lähettävä prosessi lähettää dataa ja msghdr-struktuurin sendmsg-funktiolla • Vastaanottava prosessi kutsuu recvmsg funktiota • Descriptori ja descriptorin numero prosessissa ovat eri asiat!!!
Esimerkki exit( exit status ) fork mycat openfile descriptori
unixdomain/mycat.c • 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); • }
unixdomain/myopen.c • 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); • }
msghdr • struct msghdr { • void * msg_name; /* optional address */ • socklen_t msg_namelen; /* size of address */ • struct iovec * msg_iov; /* scatter/gather array */ • size_t msg_iovlen; /* # elements in msg_iov */ • void * msg_control; /* ancillary data, see below */ • socklen_t msg_controllen; /* ancillary data buffer len */ • int msg_flags; /* flags on received message */ • };
msgcontrol • struct cmsghdr { • socklen_t cmsg_len; /* data byte count, including hdr */ • int cmsg_level; /* originating protocol */ • int cmsg_type; /* protocol-specific type */ • /* followed by u_char cmsg_data[]; */ • };
Makrot msgcontrol struktuurien käsittelyyn • #include <sys/socket.h> • struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh); • struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg); • size_t CMSG_ALIGN(size_t length); • size_t CMSG_SPACE(size_t length); • size_t CMSG_LEN(size_t length);
lib/read_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) • 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 • /* *INDENT-OFF* */ • if (msg.msg_accrightslen == sizeof(int)) • *recvfd = newfd; • else • *recvfd = -1; /* descriptor was not passed */ • /* *INDENT-ON* */ • #endif • return(n); • } • /* end read_fd */ • ssize_t • Read_fd(int fd, void *ptr, size_t nbytes, int *recvfd) • { • ssize_t n; • if ( (n = read_fd(fd, ptr, nbytes, recvfd)) < 0) • err_sys("read_fd error"); • return(n); • }
unixdomain/openfile.c • int • main(int argc, char **argv) • { • int fd; • if (argc != 4) • err_quit("openfile <sockfd#> <filename> <mode>"); • 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); • }
lib/write_fd.c • 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)); • }
cmsgcred • struct cmscred { • pid_t cmcred_pid; // PID of sender • uid_t cmcred_uid; // Real UID of sender • uid_t cmcred_euid; // Effective UID • gid_t cmcred_gid; // Real GID of process • gid_t cmcred_groups[CMGROUP_MAX]; • };
unixdomain/readcred.c • #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) • err_quit("control length = %d", cmptr->cmsg_len); • if (cmptr->cmsg_level != SOL_SOCKET) • err_quit("control level != SOL_SOCKET"); • if (cmptr->cmsg_type != SCM_CREDS) • err_quit("control type != SCM_CREDS"); • memcpy(cmsgcredptr, CMSG_DATA(cmptr), sizeof(struct cmsgcred)); • } • return(n);
unixdomain/strecho.c • ssize_t read_cred(int, void *, size_t, struct cmsgcred *); • 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"); • } • Writen(sockfd, buf, n); • } • if (n < 0 && errno == EINTR) • goto again; • else if (n < 0) • err_sys("str_echo: read error"); • }