LUKU 15 – Unix Domain Socket

Post on 19-Jan-2016

60 views 0 download

description

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. - PowerPoint PPT Presentation

Transcript of LUKU 15 – Unix Domain Socket

TEKNIIKAN JA TALOUDEN YLIOPISTO

www.lut.fi

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.cintmain(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.cint 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() */

elseerr_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

mycat openfile

descriptori

fork

exit( exit status )

unixdomain/mycat.c

int my_open(const char *, int);

intmain(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_CONTROLunion { 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);

#elseint 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);

#ifdefHAVE_MSGHDR_MSG_CONTROLif ( (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_tRead_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.cintmain(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_twrite_fd(int fd, void *ptr, size_t nbytes, int sendfd){

struct msghdr msg;struct iovec iov[1];

#ifdefHAVE_MSGHDR_MSG_CONTROLunion { 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;

#elsemsg.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_tread_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 *);

voidstr_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");

}