/*- * Copyright (c)1996-2005 by Hartmut Brandt * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: libbegemot/frame.c,v 1.13 2005/06/01 07:50:40 brandt_h Exp $ */ # include # include # include # include # include # include # include # ifdef HAVE_STREAMS # include # endif # include # include "begemot.h" # define MAXIOVEC 100 # ifndef HAVE_OLDMSGHDR struct passfd { struct cmsghdr cmsg; int fd; }; # endif struct hdr { u_int bytes; int fd; }; int frame_write(int fd, void *hdr, u_int hdr_len, void *arg, ...) { struct iovec iov[MAXIOVEC]; struct hdr *h; u_int iovlen; va_list ap; u_int len; int ret; iovlen = 0; h = (struct hdr *)hdr; iov[iovlen].iov_base = (caddr_t)hdr; iov[iovlen++].iov_len = hdr_len; h->bytes = 0; va_start(ap, arg); while(arg != NULL) { len = va_arg(ap, u_int); if(len != 0) { iov[iovlen].iov_base = (caddr_t)arg; iov[iovlen++].iov_len = len; h->bytes += len; } arg = va_arg(ap, void *); } va_end(ap); h->bytes = htonl(h->bytes); ret = writev(fd, iov, iovlen); h->bytes = ntohl(h->bytes); return ret; } int frame_writev(int fd, void *hdr, u_int hdr_len, struct iovec *v, u_int vlen) { struct iovec iov[MAXIOVEC]; struct hdr *h; u_int i, iovlen; int ret; iovlen = 0; h = (struct hdr *)hdr; iov[iovlen].iov_base = (caddr_t)hdr; iov[iovlen++].iov_len = hdr_len; h->bytes = 0; for(i = 0; i < vlen; i++) { if(v[i].iov_len != 0) { h->bytes += v[i].iov_len; iov[iovlen++] = v[i]; } } h->bytes = htonl(h->bytes); ret = writev(fd, iov, iovlen); h->bytes = ntohl(h->bytes); return ret; } # ifdef SEND_FD_BUG /* * The Solaris 2.5 (and maybe earlier) socket emulation has a bug: * If you send one piece of data and a file descriptor. You can't receive * the data: the receiver must expect the data. So we send first the header * so that the receiver knows whether to wait for a file descriptor or not * and then the extensions together with the file descriptor. */ int framefd_write(int fd, void *hdr, u_int hdr_len, void *arg, ...) { struct msghdr msg; struct iovec iov[MAXIOVEC+1]; va_list ap; u_int len; struct hdr *h; int sendfd, ret, ret1; u_int iovlen; h = (struct hdr *)hdr; sendfd = h->fd; iovlen = 0; iov[iovlen].iov_base = (caddr_t)hdr; iov[iovlen++].iov_len = sizeof(h->bytes); iov[iovlen].iov_base = (caddr_t)((char *)hdr + sizeof(h->bytes)); iov[iovlen++].iov_len = hdr_len - sizeof(h->bytes); h->bytes = 0; h->fd = (sendfd >= 0); va_start(ap, arg); while(arg != NULL) { len = va_arg(ap, u_int); if(len != 0) { iov[iovlen].iov_base = (caddr_t)arg; iov[iovlen++].iov_len = len; h->bytes += len; } arg = va_arg(ap, void *); } va_end(ap); h->bytes = htonl(h->bytes); /* send the 4 byte length only */ msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_accrights = NULL; msg.msg_accrightslen = 0; ret = sendmsg(fd, &msg, 0); h->bytes = ntohl(h->bytes); if(ret != iov[0].iov_len) return ret; if(h->fd >= 0) { sendfd = h->fd; msg.msg_accrights = (caddr_t)&sendfd; msg.msg_accrightslen = sizeof(sendfd); } else { msg.msg_accrights = NULL; msg.msg_accrightslen = 0; } msg.msg_iov = iov + 1; msg.msg_iovlen = iovlen - 1; if((ret1 = sendmsg(fd, &msg, 0)) < 0) return ret1; return ret + ret1; } int framefd_writev(int fd, void *hdr, u_int hdr_len, struct iovec *v, u_int vlen) { struct msghdr msg; struct iovec iov[MAXIOVEC+1]; struct hdr *h; int sendfd, ret, ret1; u_int iovlen, i; h = (struct hdr *)hdr; sendfd = h->fd; iovlen = 0; iov[iovlen].iov_base = (caddr_t)hdr; iov[iovlen++].iov_len = sizeof(h->bytes); iov[iovlen].iov_base = (caddr_t)((char *)hdr + sizeof(h->bytes)); iov[iovlen++].iov_len = hdr_len - sizeof(h->bytes); h->bytes = 0; h->fd = (sendfd >= 0); for(i = 0; i < vlen; i++) { if(v[i].iov_len != 0) { h->bytes += v[i].iov_len; iov[iovlen++] = v[i]; } } h->bytes = htonl(h->bytes); /* send the 4 byte length only */ msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_accrights = NULL; msg.msg_accrightslen = 0; ret = sendmsg(fd, &msg, 0); h->bytes = ntohl(h->bytes); if(ret != iov[0].iov_len) return ret; if(h->fd >= 0) { sendfd = h->fd; msg.msg_accrights = (caddr_t)&sendfd; msg.msg_accrightslen = sizeof(sendfd); } else { msg.msg_accrights = NULL; msg.msg_accrightslen = 0; } msg.msg_iov = iov + 1; msg.msg_iovlen = iovlen - 1; if((ret1 = sendmsg(fd, &msg, 0)) < 0) return ret1; return ret + ret1; } # else int framefd_write(int fd, void *hdr, u_int hdr_len, void *arg, ...) { struct msghdr msg; struct iovec iov[MAXIOVEC]; va_list ap; u_int len; int ret; struct hdr *h; # ifndef HAVE_OLDMSGHDR struct passfd passfd; # else int sendfd; # endif h = (struct hdr *)hdr; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 0; if(h->fd >= 0) { # ifdef HAVE_OLDMSGHDR sendfd = h->fd; msg.msg_accrights = (caddr_t)&sendfd; msg.msg_accrightslen = sizeof(sendfd); # else passfd.fd = h->fd; passfd.cmsg.cmsg_len = sizeof(passfd); passfd.cmsg.cmsg_level = SOL_SOCKET; passfd.cmsg.cmsg_type = SCM_RIGHTS; msg.msg_control = (caddr_t)&passfd; msg.msg_controllen = sizeof(passfd); # endif } else { # ifdef HAVE_OLDMSGHDR msg.msg_accrights = NULL; msg.msg_accrightslen = 0; # else msg.msg_control = NULL; msg.msg_controllen = 0; # endif } iov[msg.msg_iovlen].iov_base = (caddr_t)hdr; iov[msg.msg_iovlen++].iov_len = hdr_len; h->bytes = 0; # ifdef HAVE_OLDMSGHDR h->fd = (sendfd >= 0); # else h->fd = (passfd.fd >= 0); # endif va_start(ap, arg); while(arg != NULL) { len = va_arg(ap, u_int); if(len != 0) { iov[msg.msg_iovlen].iov_base = (caddr_t)arg; iov[msg.msg_iovlen++].iov_len = len; h->bytes += len; } arg = va_arg(ap, void *); } va_end(ap); h->bytes = htonl(h->bytes); ret = sendmsg(fd, &msg, 0); h->bytes = ntohl(h->bytes); return ret; } int framefd_writev(int fd, void *hdr, u_int hdr_len, struct iovec *v, u_int vlen) { struct msghdr msg; struct iovec iov[MAXIOVEC]; u_int i; int ret; struct hdr *h; # ifndef HAVE_OLDMSGHDR struct passfd passfd; # else int sendfd; # endif h = (struct hdr *)hdr; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 0; if(h->fd >= 0) { # ifdef HAVE_OLDMSGHDR sendfd = h->fd; msg.msg_accrights = (caddr_t)&sendfd; msg.msg_accrightslen = sizeof(sendfd); # else passfd.fd = h->fd; passfd.cmsg.cmsg_len = sizeof(passfd); passfd.cmsg.cmsg_level = SOL_SOCKET; passfd.cmsg.cmsg_type = SCM_RIGHTS; msg.msg_control = (caddr_t)&passfd; msg.msg_controllen = sizeof(passfd); # endif } else { # ifdef HAVE_OLDMSGHDR msg.msg_accrights = NULL; msg.msg_accrightslen = 0; # else msg.msg_control = NULL; msg.msg_controllen = 0; # endif } iov[msg.msg_iovlen].iov_base = (caddr_t)hdr; iov[msg.msg_iovlen++].iov_len = hdr_len; h->bytes = 0; # ifdef HAVE_OLDMSGHDR h->fd = (sendfd >= 0); # else h->fd = (passfd.fd >= 0); # endif for(i = 0; i < vlen; i++) { if(v[i].iov_len != 0) { h->bytes += v[i].iov_len; iov[msg.msg_iovlen++] = v[i]; } } h->bytes = htonl(h->bytes); ret = sendmsg(fd, &msg, 0); h->bytes = ntohl(h->bytes); return ret; } # endif int frame_read(int fd, void *hdr, u_int hdr_len, void **parg, u_int *plen) { struct iovec iov[1]; struct hdr *h; u_int iovlen; int n, n1; iovlen = 0; h = (struct hdr *)hdr; iov[iovlen].iov_base = (caddr_t)hdr; iov[iovlen++].iov_len = hdr_len; if((n = readv(fd, iov, iovlen)) < 0 || (u_int)n < hdr_len) return n; h->bytes = ntohl(h->bytes); if(h->bytes == 0) return n; if(h->bytes > *plen) { *parg = xrealloc(*parg, h->bytes); *plen = h->bytes; } iovlen = 0; iov[iovlen].iov_base = (caddr_t)*parg; iov[iovlen++].iov_len = h->bytes; if((n1 = readv(fd, iov, iovlen)) <= 0) return n1; *plen = n1; return n + n1; } # ifdef SEND_FD_BUG int framefd_read(int fd, void *hdr, u_int hdr_len, void **parg, u_int *plen) { struct msghdr msg; struct iovec iov[2]; struct hdr *h; int n, n1; int recvfd; h = (struct hdr *)hdr; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_accrights = NULL; msg.msg_accrightslen = 0; iov[0].iov_base = (caddr_t)hdr; iov[0].iov_len = sizeof(h->bytes); if((n = recvmsg(fd, &msg, 0)) != sizeof(h->bytes)) return n; h->bytes = ntohl(h->bytes); iov[0].iov_base = (caddr_t)((char *)hdr + sizeof(h->bytes)); iov[0].iov_len = hdr_len - sizeof(h->bytes); if(h->bytes > *plen) { *parg = xrealloc(*parg, h->bytes); *plen = h->bytes; } if(h->bytes) { iov[1].iov_base = (caddr_t)*parg; iov[1].iov_len = h->bytes; } msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = (h->bytes ? 2 : 1); msg.msg_accrights = (caddr_t)&recvfd; msg.msg_accrightslen = sizeof(recvfd); recvfd = -1; if((n1 = recvmsg(fd, &msg, 0)) <= 0) return n1; if((u_int)n >= sizeof(struct hdr) - sizeof(h->bytes)) { if(h->fd) { # ifdef HAVE_OLDMSGHDR if(recvfd == -1) h->fd = -2; else h->fd = recvfd; # else if(passfd.fd == -1) h->fd = -2; else h->fd = passfd.fd; # endif } else { # ifdef HAVE_OLDMSGHDR if(recvfd >= 0) (void)close(recvfd); # else if(passfd.fd >= 0) (void)close(passfd.fd); # endif h->fd = -1; } } return n + n1; } # else int framefd_read(int fd, void *hdr, u_int hdr_len, void **parg, u_int *plen) { struct msghdr msg; struct iovec iov[1]; struct hdr *h; int n, n1; # ifndef HAVE_OLDMSGHDR struct passfd passfd; # else int recvfd; # endif h = (struct hdr *)hdr; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; # ifdef HAVE_OLDMSGHDR msg.msg_accrights = (caddr_t)&recvfd; msg.msg_accrightslen = sizeof(recvfd); recvfd = -1; # else msg.msg_control = (caddr_t)&passfd; msg.msg_controllen = sizeof(passfd); passfd.cmsg.cmsg_len = sizeof(passfd); passfd.cmsg.cmsg_level = SOL_SOCKET; passfd.cmsg.cmsg_type = SCM_RIGHTS; passfd.fd = -1; # endif iov[0].iov_base = (caddr_t)hdr; iov[0].iov_len = hdr_len; if((n = recvmsg(fd, &msg, 0)) < 0 || (u_int)n < sizeof(struct hdr)) return n; h->bytes = ntohl(h->bytes); if((u_int)n >= sizeof(struct hdr)) { if(h->fd) { # ifdef HAVE_OLDMSGHDR if(recvfd == -1) h->fd = -2; else h->fd = recvfd; # else if(passfd.fd == -1) h->fd = -2; else h->fd = passfd.fd; # endif } else { # ifdef HAVE_OLDMSGHDR if(recvfd >= 0) (void)close(recvfd); # else if(passfd.fd >= 0) (void)close(passfd.fd); # endif h->fd = -1; } } if((u_int)n < hdr_len) return n; if(h->bytes == 0) return n; if(h->bytes > *plen) { *parg = xrealloc(*parg, h->bytes); *plen = h->bytes; } msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; # ifdef HAVE_OLDMSGHDR msg.msg_accrights = NULL; msg.msg_accrightslen = 0; # else msg.msg_control = NULL; msg.msg_controllen = 0; # endif iov[0].iov_base = (caddr_t)*parg; iov[0].iov_len = h->bytes; if((n1 = recvmsg(fd, &msg, 0)) <= 0) return n1; *plen = n1; return n + n1; } #endif # ifdef TEST # include # include # include # include # include int process(int argc, char *argv[]); int main(int argc, char *argv[]) { int i, p[2]; pid_t pid; if(strcmp(argv[0], "frame") == 0) { /* sender */ for(i = 1; i < argc; i++) if(strcmp(argv[i], "-") == 0) break; if(i == argc) panic("missing arg delimiter"); if(socketpair(PF_UNIX, SOCK_STREAM, 0, p) == -1) panic("socketpair: %s", strerror(errno)); # ifdef HAVE_STREAMS ioctl(p[0], I_SRDOPT, RNORM); ioctl(p[1], I_SRDOPT, RNORM); # endif if((pid = fork()) < 0) panic("fork: %s", strerror(errno)); if(pid == 0) { /* child */ (void)close(p[1]); if(dup2(p[0], 0) == -1) panic("dup2: %s", strerror(errno)); (void)close(p[0]); argv[i] = "-frame"; argc -= i; argv += i; execv("frame", argv); _exit(127); } (void)close(p[0]); if(dup2(p[1], 1) == -1) panic("dup2: %s", strerror(errno)); argv[i] = NULL; argc = i; return process(argc-1, argv+1); } else { /* receiver */ return process(argc-1, argv+1); } } int process(int argc, char *argv[]) { struct { struct hdr h; u_int spare[3]; } hdr; u_int data[1000]; int n, i; u_int extlen; void *ext; struct stat statb; for(i = 0; i < argc; i++) { hdr.h.fd = 0; ext = data; extlen = sizeof(data); if(strcmp(argv[i], "s") == 0) { n = frame_write(1, &hdr, sizeof(hdr), data, sizeof(data), 0); if(n <= 0) panic("write error: %s", strerror(errno)); if((u_int)n != hdr.h.bytes + sizeof(hdr)) panic("bad write: %d, %d", n, hdr.h.bytes + sizeof(hdr)); } else if(strcmp(argv[i], "sf") == 0) { hdr.h.fd = open("/dev/null", O_RDONLY); n = framefd_write(1, &hdr, sizeof(hdr), data, sizeof(data), 0); if(n <= 0) panic("write error: %s", strerror(errno)); if((u_int)n != hdr.h.bytes + sizeof(hdr)) panic("bad write: %d, %d", n, hdr.h.bytes + sizeof(hdr)); } else if(strcmp(argv[i], "s0") == 0) { hdr.h.fd = -1; n = framefd_write(1, &hdr, sizeof(hdr), data, sizeof(data), 0); if(n <= 0) panic("write error: %s", strerror(errno)); if((u_int)n != hdr.h.bytes + sizeof(hdr)) panic("bad write: %d, %d", n, hdr.h.bytes + sizeof(hdr)); } else if(strcmp(argv[i], "r") == 0) { n = frame_read(0, &hdr, sizeof(hdr), &ext, &extlen); if(n <= 0) panic("read error: %s", strerror(errno)); if((u_int)n < sizeof(hdr)) panic("bad read: %d, %d", n, sizeof(hdr)); if((u_int)n != hdr.h.bytes + sizeof(hdr)) panic("bad read: %d, %d", n, sizeof(hdr) + hdr.h.bytes); printf("read: %u bytes\n", hdr.h.bytes); } else if(strcmp(argv[i], "rf") == 0) { n = framefd_read(0, &hdr, sizeof(hdr), &ext, &extlen); if(n <= 0) panic("read error: %s", strerror(errno)); if((u_int)n < sizeof(hdr)) panic("bad read: %d, %d", n, sizeof(hdr)); if((u_int)n != hdr.h.bytes + sizeof(hdr)) panic("bad read: %d, %d", n, sizeof(hdr) + hdr.h.bytes); printf("read: %u bytes\n", hdr.h.bytes); printf("fd: %d\n", hdr.h.fd); if(hdr.h.fd >= 0) { if(fstat(hdr.h.fd, &statb)) panic("fstat: %s", strerror(errno)); prstat(stdout, &statb); } } else panic("bad key '%s'", argv[i]); } sleep(2); return 0; } # endif