diff options
author | Mark Newton <newton@FreeBSD.org> | 1999-01-30 06:29:48 +0000 |
---|---|---|
committer | Mark Newton <newton@FreeBSD.org> | 1999-01-30 06:29:48 +0000 |
commit | f22dd58acc33489c11e8b6c3ad4038c9393e4980 (patch) | |
tree | 0e997d66b2730a71d886672954027bf3ab6bcb87 /sys/compat/svr4/svr4_stream.c | |
parent | bd56b99ac3360a6e54ac2c03905426d78f25c745 (diff) | |
download | src-f22dd58acc33489c11e8b6c3ad4038c9393e4980.tar.gz src-f22dd58acc33489c11e8b6c3ad4038c9393e4980.zip |
Emulator KLD for SysVR4 executables grabbed from NetBSD.
See http://www.freebsd.org/~newton/freebsd-svr4 for limitations,
capabilities, history and TO-DO list.
Notes
Notes:
svn path=/head/; revision=43412
Diffstat (limited to 'sys/compat/svr4/svr4_stream.c')
-rw-r--r-- | sys/compat/svr4/svr4_stream.c | 2264 |
1 files changed, 2264 insertions, 0 deletions
diff --git a/sys/compat/svr4/svr4_stream.c b/sys/compat/svr4/svr4_stream.c new file mode 100644 index 000000000000..a656d33aa70e --- /dev/null +++ b/sys/compat/svr4/svr4_stream.c @@ -0,0 +1,2264 @@ +/* + * Copyright (c) 1998 Mark Newton. All rights reserved. + * Copyright (c) 1994, 1996 Christos Zoulas. All rights reserved. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + */ + +/* + * Pretend that we have streams... + * Yes, this is gross. + * + * ToDo: The state machine for getmsg needs re-thinking + */ + +#define COMPAT_43 1 + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/file.h> +#include <sys/filedesc.h> +#include <sys/unistd.h> +#include <sys/fcntl.h> +#include <sys/filio.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/socketvar.h> +#include <sys/un.h> +#include <net/if.h> +#include <netinet/in.h> +#include <sys/mount.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/signal.h> +#include <sys/signalvar.h> +#include <sys/uio.h> +#include <sys/ktrace.h> +#include <sys/proc.h> +#include <sys/vnode.h> +#include <sys/device.h> +#include <sys/stat.h> + +#include <sys/sysproto.h> + +#include <svr4/svr4.h> +#include <svr4/svr4_types.h> +#include <svr4/svr4_util.h> +#include <svr4/svr4_signal.h> +#include <svr4/svr4_proto.h> +#include <svr4/svr4_stropts.h> +#include <svr4/svr4_timod.h> +#include <svr4/svr4_sockmod.h> +#include <svr4/svr4_ioctl.h> +#include <svr4/svr4_socket.h> + +/* Utils */ +static int clean_pipe __P((struct proc *, const char *)); +static void getparm __P((struct file *, struct svr4_si_sockparms *)); + +/* Address Conversions */ +static void sockaddr_to_netaddr_in __P((struct svr4_strmcmd *, + const struct sockaddr_in *)); +static void sockaddr_to_netaddr_un __P((struct svr4_strmcmd *, + const struct sockaddr_un *)); +static void netaddr_to_sockaddr_in __P((struct sockaddr_in *, + const struct svr4_strmcmd *)); +static void netaddr_to_sockaddr_un __P((struct sockaddr_un *, + const struct svr4_strmcmd *)); + +/* stream ioctls */ +static int i_nread __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int i_fdinsert __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int i_str __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int i_setsig __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int i_getsig __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int _i_bind_rsvd __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); +static int _i_rele_rsvd __P((struct file *, struct proc *, register_t *, int, + u_long, caddr_t)); + +/* i_str sockmod calls */ +static int sockmod __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int si_listen __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int si_ogetudata __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int si_sockparams __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int si_shutdown __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int si_getudata __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); + +/* i_str timod calls */ +static int timod __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int ti_getinfo __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); +static int ti_bind __P((struct file *, int, struct svr4_strioctl *, + struct proc *)); + +/* infrastructure */ +static int svr4_sendit __P((struct proc *p, int s, struct msghdr *mp, + int flags)); + +static int svr4_recvit __P((struct proc *p, int s, struct msghdr *mp, + caddr_t namelenp)); + +/* <sigh> Ok, so we shouldn't use sendit() in uipc_syscalls.c because + * it isn't part of a "public" interface; We're supposed to use + * pru_sosend instead. Same goes for recvit()/pru_soreceive() for + * that matter. Solution: Suck sendit()/recvit() into here where we + * can do what we like. + * + * I hate code duplication. + * + * I will take out all the #ifdef COMPAT_OLDSOCK gumph, though. + */ +static int +svr4_sendit(p, s, mp, flags) + register struct proc *p; + int s; + register struct msghdr *mp; + int flags; +{ + struct file *fp; + struct uio auio; + register struct iovec *iov; + register int i; + struct mbuf *control; + struct sockaddr *to; + int len, error; + struct socket *so; +#ifdef KTRACE + struct iovec *ktriov = NULL; +#endif + + error = getsock(p->p_fd, s, &fp); + if (error) + return (error); + auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_procp = p; + auio.uio_offset = 0; /* XXX */ + auio.uio_resid = 0; + iov = mp->msg_iov; + for (i = 0; i < mp->msg_iovlen; i++, iov++) { + if ((auio.uio_resid += iov->iov_len) < 0) + return (EINVAL); + } + if (mp->msg_name) { + error = getsockaddr(&to, mp->msg_name, mp->msg_namelen); + if (error) + return (error); + } else + to = 0; + if (mp->msg_control) { + if (mp->msg_controllen < sizeof(struct cmsghdr)) { + error = EINVAL; + goto bad; + } + error = sockargs(&control, mp->msg_control, + mp->msg_controllen, MT_CONTROL); + if (error) + goto bad; + } else + control = 0; +#ifdef KTRACE + if (KTRPOINT(p, KTR_GENIO)) { + int iovlen = auio.uio_iovcnt * sizeof (struct iovec); + + MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); + bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen); + } +#endif + len = auio.uio_resid; + so = (struct socket *)fp->f_data; + error = so->so_proto->pr_usrreqs->pru_sosend(so, to, &auio, 0, control, + flags, p); + if (error) { + if (auio.uio_resid != len && (error == ERESTART || + error == EINTR || error == EWOULDBLOCK)) + error = 0; + if (error == EPIPE) + psignal(p, SIGPIPE); + } + if (error == 0) + p->p_retval[0] = len - auio.uio_resid; +#ifdef KTRACE + if (ktriov != NULL) { + if (error == 0) + ktrgenio(p->p_tracep, s, UIO_WRITE, + ktriov, p->p_retval[0], error); + FREE(ktriov, M_TEMP); + } +#endif +bad: + if (to) + FREE(to, M_SONAME); + return (error); +} + +static int +svr4_recvit(p, s, mp, namelenp) + register struct proc *p; + int s; + register struct msghdr *mp; + caddr_t namelenp; +{ + struct file *fp; + struct uio auio; + register struct iovec *iov; + register int i; + int len, error; + struct mbuf *m, *control = 0; + caddr_t ctlbuf; + struct socket *so; + struct sockaddr *fromsa = 0; +#ifdef KTRACE + struct iovec *ktriov = NULL; +#endif + + error = getsock(p->p_fd, s, &fp); + if (error) + return (error); + auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_READ; + auio.uio_procp = p; + auio.uio_offset = 0; /* XXX */ + auio.uio_resid = 0; + iov = mp->msg_iov; + for (i = 0; i < mp->msg_iovlen; i++, iov++) { + if ((auio.uio_resid += iov->iov_len) < 0) + return (EINVAL); + } +#ifdef KTRACE + if (KTRPOINT(p, KTR_GENIO)) { + int iovlen = auio.uio_iovcnt * sizeof (struct iovec); + + MALLOC(ktriov, struct iovec *, iovlen, M_TEMP, M_WAITOK); + bcopy((caddr_t)auio.uio_iov, (caddr_t)ktriov, iovlen); + } +#endif + len = auio.uio_resid; + so = (struct socket *)fp->f_data; + error = so->so_proto->pr_usrreqs->pru_soreceive(so, &fromsa, &auio, + (struct mbuf **)0, mp->msg_control ? &control : (struct mbuf **)0, + &mp->msg_flags); + if (error) { + if (auio.uio_resid != len && (error == ERESTART || + error == EINTR || error == EWOULDBLOCK)) + error = 0; + } +#ifdef KTRACE + if (ktriov != NULL) { + if (error == 0) + ktrgenio(p->p_tracep, s, UIO_READ, + ktriov, len - auio.uio_resid, error); + FREE(ktriov, M_TEMP); + } +#endif + if (error) + goto out; + p->p_retval[0] = len - auio.uio_resid; + if (mp->msg_name) { + len = mp->msg_namelen; + if (len <= 0 || fromsa == 0) + len = 0; + else { +#ifndef MIN +#define MIN(a,b) ((a)>(b)?(b):(a)) +#endif + /* save sa_len before it is destroyed by MSG_COMPAT */ + len = MIN(len, fromsa->sa_len); + error = copyout(fromsa, + (caddr_t)mp->msg_name, (unsigned)len); + if (error) + goto out; + } + mp->msg_namelen = len; + if (namelenp && + (error = copyout((caddr_t)&len, namelenp, sizeof (int)))) { + goto out; + } + } + if (mp->msg_control) { + len = mp->msg_controllen; + m = control; + mp->msg_controllen = 0; + ctlbuf = (caddr_t) mp->msg_control; + + while (m && len > 0) { + unsigned int tocopy; + + if (len >= m->m_len) + tocopy = m->m_len; + else { + mp->msg_flags |= MSG_CTRUNC; + tocopy = len; + } + + if (error = copyout((caddr_t)mtod(m, caddr_t), + ctlbuf, tocopy)) + goto out; + + ctlbuf += tocopy; + len -= tocopy; + m = m->m_next; + } + mp->msg_controllen = ctlbuf - mp->msg_control; + } +out: + if (fromsa) + FREE(fromsa, M_SONAME); + if (control) + m_freem(control); + return (error); +} + +#ifdef DEBUG_SVR4 +static void bufprint __P((u_char *, size_t)); +static int show_ioc __P((const char *, struct svr4_strioctl *)); +static int show_strbuf __P((struct svr4_strbuf *)); +static void show_msg __P((const char *, int, struct svr4_strbuf *, + struct svr4_strbuf *, int)); + +static void +bufprint(buf, len) + u_char *buf; + size_t len; +{ + size_t i; + + uprintf("\n\t"); + for (i = 0; i < len; i++) { + uprintf("%x ", buf[i]); + if (i && (i % 16) == 0) + uprintf("\n\t"); + } +} + +static int +show_ioc(str, ioc) + const char *str; + struct svr4_strioctl *ioc; +{ + u_char *ptr = (u_char *) malloc(ioc->len, M_TEMP, M_WAITOK); + int error; + + uprintf("%s cmd = %ld, timeout = %d, len = %d, buf = %p { ", + str, ioc->cmd, ioc->timeout, ioc->len, ioc->buf); + + if ((error = copyin(ioc->buf, ptr, ioc->len)) != 0) { + free((char *) ptr, M_TEMP); + return error; + } + + bufprint(ptr, ioc->len); + + uprintf("}\n"); + + free((char *) ptr, M_TEMP); + return 0; +} + + +static int +show_strbuf(str) + struct svr4_strbuf *str; +{ + int error; + u_char *ptr = NULL; + int maxlen = str->maxlen; + int len = str->len; + + if (maxlen < 0) + maxlen = 0; + + if (len >= maxlen) + len = maxlen; + + if (len > 0) { + ptr = (u_char *) malloc(len, M_TEMP, M_WAITOK); + + if ((error = copyin(str->buf, ptr, len)) != 0) { + free((char *) ptr, M_TEMP); + return error; + } + } + + uprintf(", { %d, %d, %p=[ ", str->maxlen, str->len, str->buf); + + if (ptr) + bufprint(ptr, len); + + uprintf("]}"); + + if (ptr) + free((char *) ptr, M_TEMP); + + return 0; +} + + +static void +show_msg(str, fd, ctl, dat, flags) + const char *str; + int fd; + struct svr4_strbuf *ctl; + struct svr4_strbuf *dat; + int flags; +{ + struct svr4_strbuf buf; + int error; + + uprintf("%s(%d", str, fd); + if (ctl != NULL) { + if ((error = copyin(ctl, &buf, sizeof(buf))) != 0) + return; + show_strbuf(&buf); + } + else + uprintf(", NULL"); + + if (dat != NULL) { + if ((error = copyin(dat, &buf, sizeof(buf))) != 0) + return; + show_strbuf(&buf); + } + else + uprintf(", NULL"); + + uprintf(", %x);\n", flags); +} + +#endif /* DEBUG_SVR4 */ + +/* + * We are faced with an interesting situation. On svr4 unix sockets + * are really pipes. But we really have sockets, and we might as + * well use them. At the point where svr4 calls TI_BIND, it has + * already created a named pipe for the socket using mknod(2). + * We need to create a socket with the same name when we bind, + * so we need to remove the pipe before, otherwise we'll get address + * already in use. So we *carefully* remove the pipe, to avoid + * using this as a random file removal tool. We use system calls + * to avoid code duplication. + */ +static int +clean_pipe(p, path) + struct proc *p; + const char *path; +{ + struct lstat_args la; + struct unlink_args ua; + struct stat st; + int error; + caddr_t sg = stackgap_init(); + size_t l = strlen(path) + 1; + void *tpath; + + tpath = stackgap_alloc(&sg, l); + SCARG(&la, ub) = stackgap_alloc(&sg, sizeof(struct stat)); + + if ((error = copyout(path, tpath, l)) != 0) + return error; + + SCARG(&la, path) = tpath; + + if ((error = lstat(p, &la)) != 0) + return 0; + + if ((error = copyin(SCARG(&la, ub), &st, sizeof(st))) != 0) + return 0; + + /* + * Make sure we are dealing with a mode 0 named pipe. + */ + if ((st.st_mode & S_IFMT) != S_IFIFO) + return 0; + + if ((st.st_mode & ALLPERMS) != 0) + return 0; + + SCARG(&ua, path) = SCARG(&la, path); + + if ((error = unlink(p, &ua)) != 0) { + DPRINTF(("clean_pipe: unlink failed %d\n", error)); + return error; + } + + return 0; +} + + +static void +sockaddr_to_netaddr_in(sc, sain) + struct svr4_strmcmd *sc; + const struct sockaddr_in *sain; +{ + struct svr4_netaddr_in *na; + na = SVR4_ADDROF(sc); + + na->family = sain->sin_family; + na->port = sain->sin_port; + na->addr = sain->sin_addr.s_addr; + DPRINTF(("sockaddr_in -> netaddr %d %d %lx\n", na->family, na->port, + na->addr)); +} + + +static void +sockaddr_to_netaddr_un(sc, saun) + struct svr4_strmcmd *sc; + const struct sockaddr_un *saun; +{ + struct svr4_netaddr_un *na; + char *dst, *edst = ((char *) sc) + sc->offs + sizeof(na->family) + 1 - + sizeof(*sc); + const char *src; + + na = SVR4_ADDROF(sc); + na->family = saun->sun_family; + for (src = saun->sun_path, dst = na->path; (*dst++ = *src++) != '\0'; ) + if (dst == edst) + break; + DPRINTF(("sockaddr_un -> netaddr %d %s\n", na->family, na->path)); +} + + +static void +netaddr_to_sockaddr_in(sain, sc) + struct sockaddr_in *sain; + const struct svr4_strmcmd *sc; +{ + const struct svr4_netaddr_in *na; + + + na = SVR4_C_ADDROF(sc); + memset(sain, 0, sizeof(*sain)); + sain->sin_len = sizeof(*sain); + sain->sin_family = na->family; + sain->sin_port = na->port; + sain->sin_addr.s_addr = na->addr; + DPRINTF(("netaddr -> sockaddr_in %d %d %x\n", sain->sin_family, + sain->sin_port, sain->sin_addr.s_addr)); +} + + +static void +netaddr_to_sockaddr_un(saun, sc) + struct sockaddr_un *saun; + const struct svr4_strmcmd *sc; +{ + const struct svr4_netaddr_un *na; + char *dst, *edst = &saun->sun_path[sizeof(saun->sun_path) - 1]; + const char *src; + + na = SVR4_C_ADDROF(sc); + memset(saun, 0, sizeof(*saun)); + saun->sun_family = na->family; + for (src = na->path, dst = saun->sun_path; (*dst++ = *src++) != '\0'; ) + if (dst == edst) + break; + saun->sun_len = dst - saun->sun_path; + DPRINTF(("netaddr -> sockaddr_un %d %s\n", saun->sun_family, + saun->sun_path)); +} + + +static void +getparm(fp, pa) + struct file *fp; + struct svr4_si_sockparms *pa; +{ + struct svr4_strm *st = svr4_stream_get(fp); + struct socket *so = (struct socket *) fp->f_data; + + if (st == NULL) + return; + + pa->family = st->s_family; + + switch (so->so_type) { + case SOCK_DGRAM: + pa->type = SVR4_T_CLTS; + pa->protocol = IPPROTO_UDP; + DPRINTF(("getparm(dgram)\n")); + return; + + case SOCK_STREAM: + pa->type = SVR4_T_COTS; /* What about T_COTS_ORD? XXX */ + pa->protocol = IPPROTO_IP; + DPRINTF(("getparm(stream)\n")); + return; + + case SOCK_RAW: + pa->type = SVR4_T_CLTS; + pa->protocol = IPPROTO_RAW; + DPRINTF(("getparm(raw)\n")); + return; + + default: + pa->type = 0; + pa->protocol = 0; + DPRINTF(("getparm(type %d?)\n", so->so_type)); + return; + } +} + + +static int +si_ogetudata(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct svr4_si_oudata ud; + struct svr4_si_sockparms pa; + + if (ioc->len != sizeof(ud) && ioc->len != sizeof(ud) - sizeof(int)) { + DPRINTF(("SI_OGETUDATA: Wrong size %d != %d\n", + sizeof(ud), ioc->len)); + return EINVAL; + } + + if ((error = copyin(ioc->buf, &ud, sizeof(ud))) != 0) + return error; + + getparm(fp, &pa); + + switch (pa.family) { + case AF_INET: + ud.tidusize = 16384; + ud.addrsize = sizeof(struct svr4_sockaddr_in); + if (pa.type == SVR4_SOCK_STREAM) + ud.etsdusize = 1; + else + ud.etsdusize = 0; + break; + + case AF_LOCAL: + ud.tidusize = 65536; + ud.addrsize = 128; + ud.etsdusize = 128; + break; + + default: + DPRINTF(("SI_OGETUDATA: Unsupported address family %d\n", + pa.family)); + return ENOSYS; + } + + /* I have no idea what these should be! */ + ud.optsize = 128; + ud.tsdusize = 128; + + ud.servtype = pa.type; + + /* XXX: Fixme */ + ud.so_state = 0; + ud.so_options = 0; + return copyout(&ud, ioc->buf, ioc->len); +} + + +static int +si_sockparams(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + struct svr4_si_sockparms pa; + + getparm(fp, &pa); + return copyout(&pa, ioc->buf, sizeof(pa)); +} + + +static int +si_listen(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct svr4_strm *st = svr4_stream_get(fp); + struct svr4_strmcmd lst; + struct listen_args la; + + if (st == NULL) + return EINVAL; + + if ((error = copyin(ioc->buf, &lst, ioc->len)) != 0) + return error; + + if (lst.cmd != SVR4_TI_OLD_BIND_REQUEST) { + DPRINTF(("si_listen: bad request %ld\n", lst.cmd)); + return EINVAL; + } + + /* + * We are making assumptions again... + */ + SCARG(&la, s) = fd; + DPRINTF(("SI_LISTEN: fileno %d backlog = %d\n", fd, 5)); + SCARG(&la, backlog) = 5; + + if ((error = listen(p, &la)) != 0) { + DPRINTF(("SI_LISTEN: listen failed %d\n", error)); + return error; + } + + st->s_cmd = SVR4_TI__ACCEPT_WAIT; + lst.cmd = SVR4_TI_BIND_REPLY; + + switch (st->s_family) { + case AF_INET: + /* XXX: Fill the length here */ + break; + + case AF_LOCAL: + lst.len = 140; + lst.pad[28] = 0x00000000; /* magic again */ + lst.pad[29] = 0x00000800; /* magic again */ + lst.pad[30] = 0x80001400; /* magic again */ + break; + + default: + DPRINTF(("SI_LISTEN: Unsupported address family %d\n", + st->s_family)); + return ENOSYS; + } + + + if ((error = copyout(&lst, ioc->buf, ioc->len)) != 0) + return error; + + return 0; +} + + +static int +si_getudata(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct svr4_si_udata ud; + + if (sizeof(ud) != ioc->len) { + DPRINTF(("SI_GETUDATA: Wrong size %d != %d\n", + sizeof(ud), ioc->len)); + return EINVAL; + } + + if ((error = copyin(ioc->buf, &ud, sizeof(ud))) != 0) + return error; + + getparm(fp, &ud.sockparms); + + switch (ud.sockparms.family) { + case AF_INET: + DPRINTF(("getudata_inet\n")); + ud.tidusize = 16384; + ud.tsdusize = 16384; + ud.addrsize = sizeof(struct svr4_sockaddr_in); + if (ud.sockparms.type == SVR4_SOCK_STREAM) + ud.etsdusize = 1; + else + ud.etsdusize = 0; + ud.optsize = 0; + break; + + case AF_LOCAL: + DPRINTF(("getudata_local\n")); + ud.tidusize = 65536; + ud.tsdusize = 128; + ud.addrsize = 128; + ud.etsdusize = 128; + ud.optsize = 128; + break; + + default: + DPRINTF(("SI_GETUDATA: Unsupported address family %d\n", + ud.sockparms.family)); + return ENOSYS; + } + + + ud.servtype = ud.sockparms.type; + DPRINTF(("ud.servtype = %d\n", ud.servtype)); + /* XXX: Fixme */ + ud.so_state = 0; + ud.so_options = 0; + return copyout(&ud, ioc->buf, sizeof(ud)); +} + + +static int +si_shutdown(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct shutdown_args ap; + + if (ioc->len != sizeof(SCARG(&ap, how))) { + DPRINTF(("SI_SHUTDOWN: Wrong size %d != %d\n", + sizeof(SCARG(&ap, how)), ioc->len)); + return EINVAL; + } + + if ((error = copyin(ioc->buf, &SCARG(&ap, how), ioc->len)) != 0) + return error; + + SCARG(&ap, s) = fd; + + return shutdown(p, &ap); +} + + +static int +sockmod(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + switch (ioc->cmd) { + case SVR4_SI_OGETUDATA: + DPRINTF(("SI_OGETUDATA\n")); + return si_ogetudata(fp, fd, ioc, p); + + case SVR4_SI_SHUTDOWN: + DPRINTF(("SI_SHUTDOWN\n")); + return si_shutdown(fp, fd, ioc, p); + + case SVR4_SI_LISTEN: + DPRINTF(("SI_LISTEN\n")); + return si_listen(fp, fd, ioc, p); + + case SVR4_SI_SETMYNAME: + DPRINTF(("SI_SETMYNAME\n")); + return 0; + + case SVR4_SI_SETPEERNAME: + DPRINTF(("SI_SETPEERNAME\n")); + return 0; + + case SVR4_SI_GETINTRANSIT: + DPRINTF(("SI_GETINTRANSIT\n")); + return 0; + + case SVR4_SI_TCL_LINK: + DPRINTF(("SI_TCL_LINK\n")); + return 0; + + case SVR4_SI_TCL_UNLINK: + DPRINTF(("SI_TCL_UNLINK\n")); + return 0; + + case SVR4_SI_SOCKPARAMS: + DPRINTF(("SI_SOCKPARAMS\n")); + return si_sockparams(fp, fd, ioc, p); + + case SVR4_SI_GETUDATA: + DPRINTF(("SI_GETUDATA\n")); + return si_getudata(fp, fd, ioc, p); + + default: + DPRINTF(("Unknown sockmod ioctl %lx\n", ioc->cmd)); + return 0; + + } +} + + +static int +ti_getinfo(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct svr4_infocmd info; + + memset(&info, 0, sizeof(info)); + + if ((error = copyin(ioc->buf, &info, ioc->len)) != 0) + return error; + + if (info.cmd != SVR4_TI_INFO_REQUEST) + return EINVAL; + + info.cmd = SVR4_TI_INFO_REPLY; + info.tsdu = 0; + info.etsdu = 1; + info.cdata = -2; + info.ddata = -2; + info.addr = 16; + info.opt = -1; + info.tidu = 16384; + info.serv = 2; + info.current = 0; + info.provider = 2; + + ioc->len = sizeof(info); + if ((error = copyout(&info, ioc->buf, ioc->len)) != 0) + return error; + + return 0; +} + + +static int +ti_bind(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + int error; + struct svr4_strm *st = svr4_stream_get(fp); + struct sockaddr_in sain; + struct sockaddr_un saun; + caddr_t sg; + void *skp, *sup = NULL; + int sasize; + struct svr4_strmcmd bnd; + struct bind_args ba; + + if (st == NULL) { + DPRINTF(("ti_bind: bad file descriptor\n")); + return EINVAL; + } + + if ((error = copyin(ioc->buf, &bnd, ioc->len)) != 0) + return error; + + if (bnd.cmd != SVR4_TI_OLD_BIND_REQUEST) { + DPRINTF(("ti_bind: bad request %ld\n", bnd.cmd)); + return EINVAL; + } + + switch (st->s_family) { + case AF_INET: + skp = &sain; + sasize = sizeof(sain); + + if (bnd.offs == 0) + goto reply; + + netaddr_to_sockaddr_in(&sain, &bnd); + + DPRINTF(("TI_BIND: fam %d, port %d, addr %x\n", + sain.sin_family, sain.sin_port, + sain.sin_addr.s_addr)); + break; + + case AF_LOCAL: + skp = &saun; + sasize = sizeof(saun); + if (bnd.offs == 0) + goto reply; + + netaddr_to_sockaddr_un(&saun, &bnd); + + if (saun.sun_path[0] == '\0') + goto reply; + + DPRINTF(("TI_BIND: fam %d, path %s\n", + saun.sun_family, saun.sun_path)); + + if ((error = clean_pipe(p, saun.sun_path)) != 0) + return error; + + bnd.pad[28] = 0x00001000; /* magic again */ + break; + + default: + DPRINTF(("TI_BIND: Unsupported address family %d\n", + st->s_family)); + return ENOSYS; + } + + sg = stackgap_init(); + sup = stackgap_alloc(&sg, sasize); + + if ((error = copyout(skp, sup, sasize)) != 0) + return error; + + SCARG(&ba, s) = fd; + DPRINTF(("TI_BIND: fileno %d\n", fd)); + SCARG(&ba, name) = (void *) sup; + SCARG(&ba, namelen) = sasize; + + if ((error = bind(p, &ba)) != 0) { + DPRINTF(("TI_BIND: bind failed %d\n", error)); + return error; + } + +reply: + if (sup == NULL) { + memset(&bnd, 0, sizeof(bnd)); + bnd.len = sasize + 4; + bnd.offs = 0x10; /* XXX */ + } + + bnd.cmd = SVR4_TI_BIND_REPLY; + + if ((error = copyout(&bnd, ioc->buf, ioc->len)) != 0) + return error; + + return 0; +} + + +static int +timod(fp, fd, ioc, p) + struct file *fp; + int fd; + struct svr4_strioctl *ioc; + struct proc *p; +{ + switch (ioc->cmd) { + case SVR4_TI_GETINFO: + DPRINTF(("TI_GETINFO\n")); + return ti_getinfo(fp, fd, ioc, p); + + case SVR4_TI_OPTMGMT: + DPRINTF(("TI_OPTMGMT\n")); + return 0; + + case SVR4_TI_BIND: + DPRINTF(("TI_BIND\n")); + return ti_bind(fp, fd, ioc, p); + + case SVR4_TI_UNBIND: + DPRINTF(("TI_UNBIND\n")); + return 0; + + default: + DPRINTF(("Unknown timod ioctl %lx\n", ioc->cmd)); + return 0; + } +} + + +int +svr4_stream_ti_ioctl(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + struct svr4_strbuf skb, *sub = (struct svr4_strbuf *) dat; + struct svr4_strm *st = svr4_stream_get(fp); + int error; + void *skp, *sup; + struct sockaddr_in sain; + struct sockaddr_un saun; + struct svr4_strmcmd sc; + int sasize; + caddr_t sg; + int *lenp; + + DPRINTF(("svr4_stream_ti_ioctl\n")); + + if (st == NULL) + return EINVAL; + + sc.offs = 0x10; + + if ((error = copyin(sub, &skb, sizeof(skb))) != 0) { + DPRINTF(("ti_ioctl: error copying in strbuf\n")); + return error; + } + + switch (st->s_family) { + case AF_INET: + skp = &sain; + sasize = sizeof(sain); + break; + + case AF_LOCAL: + skp = &saun; + sasize = sizeof(saun); + break; + + default: + DPRINTF(("ti_ioctl: Unsupported address family %d\n", + st->s_family)); + return ENOSYS; + } + + sg = stackgap_init(); + sup = stackgap_alloc(&sg, sasize); + lenp = stackgap_alloc(&sg, sizeof(*lenp)); + + if ((error = copyout(&sasize, lenp, sizeof(*lenp))) != 0) { + DPRINTF(("ti_ioctl: error copying out lenp\n")); + return error; + } + + switch (cmd) { + case SVR4_TI_GETMYNAME: + DPRINTF(("TI_GETMYNAME\n")); + { + struct getsockname_args ap; + SCARG(&ap, fdes) = fd; + SCARG(&ap, asa) = sup; + SCARG(&ap, alen) = lenp; + if ((error = getsockname(p, &ap)) != 0) { + DPRINTF(("ti_ioctl: getsockname error\n")); + return error; + } + } + break; + + case SVR4_TI_GETPEERNAME: + DPRINTF(("TI_GETPEERNAME\n")); + { + struct getpeername_args ap; + SCARG(&ap, fdes) = fd; + SCARG(&ap, asa) = sup; + SCARG(&ap, alen) = lenp; + if ((error = getpeername(p, &ap)) != 0) { + DPRINTF(("ti_ioctl: getpeername error\n")); + return error; + } + } + break; + + case SVR4_TI_SETMYNAME: + DPRINTF(("TI_SETMYNAME\n")); + return 0; + + case SVR4_TI_SETPEERNAME: + DPRINTF(("TI_SETPEERNAME\n")); + return 0; + default: + DPRINTF(("ti_ioctl: Unknown ioctl %lx\n", cmd)); + return ENOSYS; + } + + if ((error = copyin(sup, skp, sasize)) != 0) { + DPRINTF(("ti_ioctl: error copying in socket data\n")); + return error; + } + + if ((error = copyin(lenp, &sasize, sizeof(*lenp))) != 0) { + DPRINTF(("ti_ioctl: error copying in socket size\n")); + return error; + } + + switch (st->s_family) { + case AF_INET: + sockaddr_to_netaddr_in(&sc, &sain); + skb.len = sasize; + break; + + case AF_LOCAL: + sockaddr_to_netaddr_un(&sc, &saun); + skb.len = sasize + 4; + break; + + default: + return ENOSYS; + } + + + if ((error = copyout(SVR4_ADDROF(&sc), skb.buf, sasize)) != 0) { + DPRINTF(("ti_ioctl: error copying out socket data\n")); + return error; + } + + + if ((error = copyout(&skb, sub, sizeof(skb))) != 0) { + DPRINTF(("ti_ioctl: error copying out strbuf\n")); + return error; + } + + return error; +} + + + + +static int +i_nread(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + int error; + int nread = 0; + + /* + * We are supposed to return the message length in nread, and the + * number of messages in retval. We don't have the notion of number + * of stream messages, so we just find out if we have any bytes waiting + * for us, and if we do, then we assume that we have at least one + * message waiting for us. + */ + if ((error = (*fp->f_ops->fo_ioctl)(fp, FIONREAD, + (caddr_t) &nread, p)) != 0) + return error; + + if (nread != 0) + *retval = 1; + else + *retval = 0; + + return copyout(&nread, dat, sizeof(nread)); +} + +static int +i_fdinsert(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + /* + * Major hack again here. We assume that we are using this to + * implement accept(2). If that is the case, we have already + * called accept, and we have stored the file descriptor in + * afd. We find the file descriptor that the code wants to use + * in fd insert, and then we dup2() our accepted file descriptor + * to it. + */ + int error; + struct svr4_strm *st = svr4_stream_get(fp); + struct svr4_strfdinsert fdi; + struct dup2_args d2p; + struct close_args clp; + + if (st == NULL) { + DPRINTF(("fdinsert: bad file type\n")); + return EINVAL; + } + + if (st->s_afd == -1) { + DPRINTF(("fdinsert: accept fd not found\n")); + return ENOENT; + } + + if ((error = copyin(dat, &fdi, sizeof(fdi))) != 0) { + DPRINTF(("fdinsert: copyin failed %d\n", error)); + return error; + } + + SCARG(&d2p, from) = st->s_afd; + SCARG(&d2p, to) = fdi.fd; + + if ((error = dup2(p, &d2p)) != 0) { + DPRINTF(("fdinsert: dup2(%d, %d) failed %d\n", + st->s_afd, fdi.fd, error)); + return error; + } + + SCARG(&clp, fd) = st->s_afd; + + if ((error = close(p, &clp)) != 0) { + DPRINTF(("fdinsert: close(%d) failed %d\n", + st->s_afd, error)); + return error; + } + + st->s_afd = -1; + + *retval = 0; + return 0; +} + + +static int +_i_bind_rsvd(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + struct mkfifo_args ap; + + /* + * This is a supposed to be a kernel and library only ioctl. + * It gets called before ti_bind, when we have a unix + * socket, to physically create the socket transport and + * ``reserve'' it. I don't know how this get reserved inside + * the kernel, but we are going to create it nevertheless. + */ + SCARG(&ap, path) = dat; + SCARG(&ap, mode) = S_IFIFO; + + return mkfifo(p, &ap); +} + +static int +_i_rele_rsvd(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + struct unlink_args ap; + + /* + * This is a supposed to be a kernel and library only ioctl. + * I guess it is supposed to release the socket. + */ + SCARG(&ap, path) = dat; + + return unlink(p, &ap); +} + +static int +i_str(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + int error; + struct svr4_strioctl ioc; + + if ((error = copyin(dat, &ioc, sizeof(ioc))) != 0) + return error; + +#ifdef DEBUG_SVR4 + if ((error = show_ioc(">", &ioc)) != 0) + return error; +#endif /* DEBUG_SVR4 */ + + switch (ioc.cmd & 0xff00) { + case SVR4_SIMOD: + if ((error = sockmod(fp, fd, &ioc, p)) != 0) + return error; + break; + + case SVR4_TIMOD: + if ((error = timod(fp, fd, &ioc, p)) != 0) + return error; + break; + + default: + DPRINTF(("Unimplemented module %c %ld\n", + (char) (cmd >> 8), cmd & 0xff)); + return 0; + } + +#ifdef DEBUG_SVR4 + if ((error = show_ioc("<", &ioc)) != 0) + return error; +#endif /* DEBUG_SVR4 */ + return copyout(&ioc, dat, sizeof(ioc)); +} + +static int +i_setsig(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + /* + * This is the best we can do for now; we cannot generate + * signals only for specific events so the signal mask gets + * ignored; we save it just to pass it to a possible I_GETSIG... + * + * We alse have to fix the O_ASYNC fcntl bit, so the + * process will get SIGPOLLs. + */ + struct fcntl_args fa; + int error; + register_t oflags, flags; + struct svr4_strm *st = svr4_stream_get(fp); + + if (st == NULL) { + DPRINTF(("i_setsig: bad file descriptor\n")); + return EINVAL; + } + /* get old status flags */ + SCARG(&fa, fd) = fd; + SCARG(&fa, cmd) = F_GETFL; + if ((error = fcntl(p, &fa)) != 0) + return error; + + oflags = p->p_retval[0]; + + /* update the flags */ + if (dat != NULL) { + int mask; + + flags = oflags | O_ASYNC; + if ((error = copyin(dat, &mask, sizeof(mask))) != 0) { + DPRINTF(("i_setsig: bad eventmask pointer\n")); + return error; + } + if (mask & SVR4_S_ALLMASK) { + DPRINTF(("i_setsig: bad eventmask data %x\n", mask)); + return EINVAL; + } + st->s_eventmask = mask; + } + else { + flags = oflags & ~O_ASYNC; + st->s_eventmask = 0; + } + + /* set the new flags, if changed */ + if (flags != oflags) { + SCARG(&fa, cmd) = F_SETFL; + SCARG(&fa, arg) = (long) flags; + if ((error = fcntl(p, &fa)) != 0) + return error; + flags = p->p_retval[0]; + } + + /* set up SIGIO receiver if needed */ + if (dat != NULL) { + SCARG(&fa, cmd) = F_SETOWN; + SCARG(&fa, arg) = (long) p->p_pid; + return fcntl(p, &fa); + } + return 0; +} + +static int +i_getsig(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + int error; + + if (dat != NULL) { + struct svr4_strm *st = svr4_stream_get(fp); + + if (st == NULL) { + DPRINTF(("i_getsig: bad file descriptor\n")); + return EINVAL; + } + if ((error = copyout(&st->s_eventmask, dat, + sizeof(st->s_eventmask))) != 0) { + DPRINTF(("i_getsig: bad eventmask pointer\n")); + return error; + } + } + return 0; +} + +int +svr4_stream_ioctl(fp, p, retval, fd, cmd, dat) + struct file *fp; + struct proc *p; + register_t *retval; + int fd; + u_long cmd; + caddr_t dat; +{ + *retval = 0; + + /* + * All the following stuff assumes "sockmod" is pushed... + */ + switch (cmd) { + case SVR4_I_NREAD: + DPRINTF(("I_NREAD\n")); + return i_nread(fp, p, retval, fd, cmd, dat); + + case SVR4_I_PUSH: + DPRINTF(("I_PUSH %x\n", dat)); +#if defined(DEBUG_SVR4) + show_strbuf(dat); +#endif + return 0; + + case SVR4_I_POP: + DPRINTF(("I_POP\n")); + return 0; + + case SVR4_I_LOOK: + DPRINTF(("I_LOOK\n")); + return 0; + + case SVR4_I_FLUSH: + DPRINTF(("I_FLUSH\n")); + return 0; + + case SVR4_I_SRDOPT: + DPRINTF(("I_SRDOPT\n")); + return 0; + + case SVR4_I_GRDOPT: + DPRINTF(("I_GRDOPT\n")); + return 0; + + case SVR4_I_STR: + DPRINTF(("I_STR\n")); + return i_str(fp, p, retval, fd, cmd, dat); + + case SVR4_I_SETSIG: + DPRINTF(("I_SETSIG\n")); + return i_setsig(fp, p, retval, fd, cmd, dat); + + case SVR4_I_GETSIG: + DPRINTF(("I_GETSIG\n")); + return i_getsig(fp, p, retval, fd, cmd, dat); + + case SVR4_I_FIND: + DPRINTF(("I_FIND\n")); + /* + * Here we are not pushing modules really, we just + * pretend all are present + */ + *retval = 0; + return 0; + + case SVR4_I_LINK: + DPRINTF(("I_LINK\n")); + return 0; + + case SVR4_I_UNLINK: + DPRINTF(("I_UNLINK\n")); + return 0; + + case SVR4_I_ERECVFD: + DPRINTF(("I_ERECVFD\n")); + return 0; + + case SVR4_I_PEEK: + DPRINTF(("I_PEEK\n")); + return 0; + + case SVR4_I_FDINSERT: + DPRINTF(("I_FDINSERT\n")); + return i_fdinsert(fp, p, retval, fd, cmd, dat); + + case SVR4_I_SENDFD: + DPRINTF(("I_SENDFD\n")); + return 0; + + case SVR4_I_RECVFD: + DPRINTF(("I_RECVFD\n")); + return 0; + + case SVR4_I_SWROPT: + DPRINTF(("I_SWROPT\n")); + return 0; + + case SVR4_I_GWROPT: + DPRINTF(("I_GWROPT\n")); + return 0; + + case SVR4_I_LIST: + DPRINTF(("I_LIST\n")); + return 0; + + case SVR4_I_PLINK: + DPRINTF(("I_PLINK\n")); + return 0; + + case SVR4_I_PUNLINK: + DPRINTF(("I_PUNLINK\n")); + return 0; + + case SVR4_I_SETEV: + DPRINTF(("I_SETEV\n")); + return 0; + + case SVR4_I_GETEV: + DPRINTF(("I_GETEV\n")); + return 0; + + case SVR4_I_STREV: + DPRINTF(("I_STREV\n")); + return 0; + + case SVR4_I_UNSTREV: + DPRINTF(("I_UNSTREV\n")); + return 0; + + case SVR4_I_FLUSHBAND: + DPRINTF(("I_FLUSHBAND\n")); + return 0; + + case SVR4_I_CKBAND: + DPRINTF(("I_CKBAND\n")); + return 0; + + case SVR4_I_GETBAND: + DPRINTF(("I_GETBANK\n")); + return 0; + + case SVR4_I_ATMARK: + DPRINTF(("I_ATMARK\n")); + return 0; + + case SVR4_I_SETCLTIME: + DPRINTF(("I_SETCLTIME\n")); + return 0; + + case SVR4_I_GETCLTIME: + DPRINTF(("I_GETCLTIME\n")); + return 0; + + case SVR4_I_CANPUT: + DPRINTF(("I_CANPUT\n")); + return 0; + + case SVR4__I_BIND_RSVD: + DPRINTF(("_I_BIND_RSVD\n")); + return _i_bind_rsvd(fp, p, retval, fd, cmd, dat); + + case SVR4__I_RELE_RSVD: + DPRINTF(("_I_RELE_RSVD\n")); + return _i_rele_rsvd(fp, p, retval, fd, cmd, dat); + + default: + DPRINTF(("unimpl cmd = %lx\n", cmd)); + break; + } + + return 0; +} + + + +int +svr4_sys_putmsg(p, uap) + register struct proc *p; + struct svr4_sys_putmsg_args *uap; +{ + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct svr4_strbuf dat, ctl; + struct svr4_strmcmd sc; + struct sockaddr_in sain; + struct sockaddr_un saun; + void *skp, *sup; + int sasize, *retval; + struct svr4_strm *st; + int error; + caddr_t sg; + + retval = p->p_retval; + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) { +#ifdef DEBUG_SVR4 + uprintf("putmsg: bad fp\n"); +#endif + return EBADF; + } + +#ifdef DEBUG_SVR4 + show_msg(">putmsg", SCARG(uap, fd), SCARG(uap, ctl), + SCARG(uap, dat), SCARG(uap, flags)); +#endif /* DEBUG_SVR4 */ + + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) { +#ifdef DEBUG_SVR4 + uprintf("putmsg: bad fp(2)\n"); +#endif + return EBADF; + } + + if (SCARG(uap, ctl) != NULL) { + if ((error = copyin(SCARG(uap, ctl), &ctl, sizeof(ctl))) != 0) { +#ifdef DEBUG_SVR4 + uprintf("putmsg: copyin(): %d\n", error); +#endif + return error; + } + } + else + ctl.len = -1; + + if (SCARG(uap, dat) != NULL) { + if ((error = copyin(SCARG(uap, dat), &dat, sizeof(dat))) != 0) { +#ifdef DEBUG_SVR4 + uprintf("putmsg: copyin(): %d (2)\n", error); +#endif + return error; + } + } + else + dat.len = -1; + + /* + * Only for sockets for now. + */ + if ((st = svr4_stream_get(fp)) == NULL) { + DPRINTF(("putmsg: bad file type\n")); + return EINVAL; + } + + if (ctl.len > sizeof(sc)) { + DPRINTF(("putmsg: Bad control size %d != %d\n", ctl.len, + sizeof(struct svr4_strmcmd))); + return EINVAL; + } + + if ((error = copyin(ctl.buf, &sc, ctl.len)) != 0) + return error; + + switch (st->s_family) { + case AF_INET: + if (sc.len != sizeof(sain)) { + if (sc.cmd == SVR4_TI_DATA_REQUEST) { + struct write_args wa; + + /* Solaris seems to use sc.cmd = 3 to + * send "expedited" data. telnet uses + * this for options processing, sending EOF, + * etc. I'm sure other things use it too. + * I don't have any documentation + * on it, so I'm making a guess that this + * is how it works. newton@atdot.dotat.org XXX + */ + DPRINTF(("sending expedited data (???)\n")); + SCARG(&wa, fd) = SCARG(uap, fd); + SCARG(&wa, buf) = dat.buf; + SCARG(&wa, nbyte) = dat.len; + return write(p, &wa); + } + DPRINTF(("putmsg: Invalid inet length %ld\n", sc.len)); + return EINVAL; + } + netaddr_to_sockaddr_in(&sain, &sc); + skp = &sain; + sasize = sizeof(sain); + error = sain.sin_family != st->s_family; + break; + + case AF_LOCAL: + if (ctl.len == 8) { + /* We are doing an accept; succeed */ + DPRINTF(("putmsg: Do nothing\n")); + *retval = 0; + return 0; + } + else { + /* Maybe we've been given a device/inode pair */ + dev_t *dev = SVR4_ADDROF(&sc); + ino_t *ino = (ino_t *) &dev[1]; + skp = svr4_find_socket(p, fp, *dev, *ino); + if (skp == NULL) { + skp = &saun; + /* I guess we have it by name */ + netaddr_to_sockaddr_un(skp, &sc); + } + sasize = sizeof(saun); + } + break; + + default: + DPRINTF(("putmsg: Unsupported address family %d\n", + st->s_family)); + return ENOSYS; + } + + sg = stackgap_init(); + sup = stackgap_alloc(&sg, sasize); + + if ((error = copyout(skp, sup, sasize)) != 0) + return error; + + switch (st->s_cmd = sc.cmd) { + case SVR4_TI_CONNECT_REQUEST: /* connect */ + { + struct connect_args co; + + SCARG(&co, s) = SCARG(uap, fd); + SCARG(&co, name) = (void *) sup; + SCARG(&co, namelen) = (int) sasize; + + return connect(p, &co); + } + + case SVR4_TI_SENDTO_REQUEST: /* sendto */ + { + struct msghdr msg; + struct iovec aiov; + + msg.msg_name = (caddr_t) sup; + msg.msg_namelen = sasize; + msg.msg_iov = &aiov; + msg.msg_iovlen = 1; + msg.msg_control = 0; + msg.msg_flags = 0; + aiov.iov_base = dat.buf; + aiov.iov_len = dat.len; +#if 0 + error = so->so_proto->pr_usrreqs->pru_sosend(so, 0, + uio, 0, 0, 0, uio->uio_procp); +#endif + error = svr4_sendit(p, SCARG(uap, fd), &msg, + SCARG(uap, flags)); + DPRINTF(("sendto_request error: %d\n", error)); + *retval = 0; + return error; + } + + default: + DPRINTF(("putmsg: Unimplemented command %lx\n", sc.cmd)); + return ENOSYS; + } +} + +int +svr4_sys_getmsg(p, uap) + register struct proc *p; + struct svr4_sys_getmsg_args *uap; +{ + struct filedesc *fdp = p->p_fd; + struct file *fp; + struct getpeername_args ga; + struct accept_args aa; + struct svr4_strbuf dat, ctl; + struct svr4_strmcmd sc; + int error, *retval; + struct msghdr msg; + struct iovec aiov; + struct sockaddr_in sain; + struct sockaddr_un saun; + void *skp, *sup; + int sasize; + struct svr4_strm *st; + int *flen; + int fl; + caddr_t sg; + + retval = p->p_retval; + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) + return EBADF; + + memset(&sc, 0, sizeof(sc)); + +#ifdef DEBUG_SVR4 + show_msg(">getmsg", SCARG(uap, fd), SCARG(uap, ctl), + SCARG(uap, dat), 0); +#endif /* DEBUG_SVR4 */ + + if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles || + (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL) + return EBADF; + + if (SCARG(uap, ctl) != NULL) { + if ((error = copyin(SCARG(uap, ctl), &ctl, sizeof(ctl))) != 0) + return error; + } + else { + ctl.len = -1; + ctl.maxlen = 0; + } + + if (SCARG(uap, dat) != NULL) { + if ((error = copyin(SCARG(uap, dat), &dat, sizeof(dat))) != 0) + return error; + } + else { + dat.len = -1; + dat.maxlen = 0; + } + + /* + * Only for sockets for now. + */ + if ((st = svr4_stream_get(fp)) == NULL) { + DPRINTF(("getmsg: bad file type\n")); + return EINVAL; + } + + if (ctl.maxlen == -1 || dat.maxlen == -1) { + DPRINTF(("getmsg: Cannot handle -1 maxlen (yet)\n")); + return ENOSYS; + } + + switch (st->s_family) { + case AF_INET: + skp = &sain; + sasize = sizeof(sain); + break; + + case AF_LOCAL: + skp = &saun; + sasize = sizeof(saun); + break; + + default: + DPRINTF(("getmsg: Unsupported address family %d\n", + st->s_family)); + return ENOSYS; + } + + sg = stackgap_init(); + sup = stackgap_alloc(&sg, sasize); + flen = (int *) stackgap_alloc(&sg, sizeof(*flen)); + + fl = sasize; + if ((error = copyout(&fl, flen, sizeof(fl))) != 0) + return error; + + switch (st->s_cmd) { + case SVR4_TI_CONNECT_REQUEST: + DPRINTF(("getmsg: TI_CONNECT_REQUEST\n")); + /* + * We do the connect in one step, so the putmsg should + * have gotten the error. + */ + sc.cmd = SVR4_TI_OK_REPLY; + sc.len = 0; + + ctl.len = 8; + dat.len = -1; + fl = 1; + st->s_cmd = sc.cmd; + break; + + case SVR4_TI_OK_REPLY: + DPRINTF(("getmsg: TI_OK_REPLY\n")); + /* + * We are immediately after a connect reply, so we send + * a connect verification. + */ + + SCARG(&ga, fdes) = SCARG(uap, fd); + SCARG(&ga, asa) = (void *) sup; + SCARG(&ga, alen) = flen; + + if ((error = getpeername(p, &ga)) != 0) { + DPRINTF(("getmsg: getpeername failed %d\n", error)); + return error; + } + + if ((error = copyin(sup, skp, sasize)) != 0) + return error; + + sc.cmd = SVR4_TI_CONNECT_REPLY; + sc.pad[0] = 0x4; + sc.offs = 0x18; + sc.pad[1] = 0x14; + sc.pad[2] = 0x04000402; + + switch (st->s_family) { + case AF_INET: + sc.len = sasize; + sockaddr_to_netaddr_in(&sc, &sain); + break; + + case AF_LOCAL: + sc.len = sasize + 4; + sockaddr_to_netaddr_un(&sc, &saun); + break; + + default: + return ENOSYS; + } + + ctl.len = 40; + dat.len = -1; + fl = 0; + st->s_cmd = sc.cmd; + break; + + case SVR4_TI__ACCEPT_OK: + DPRINTF(("getmsg: TI__ACCEPT_OK\n")); + /* + * We do the connect in one step, so the putmsg should + * have gotten the error. + */ + sc.cmd = SVR4_TI_OK_REPLY; + sc.len = 1; + + ctl.len = 8; + dat.len = -1; + fl = 1; + st->s_cmd = SVR4_TI__ACCEPT_WAIT; + break; + + case SVR4_TI__ACCEPT_WAIT: + DPRINTF(("getmsg: TI__ACCEPT_WAIT\n")); + /* + * We are after a listen, so we try to accept... + */ + SCARG(&aa, s) = SCARG(uap, fd); + SCARG(&aa, name) = (void *) sup; + SCARG(&aa, anamelen) = flen; + + if ((error = accept(p, &aa)) != 0) { + DPRINTF(("getmsg: accept failed %d\n", error)); + return error; + } + + st->s_afd = *retval; + + DPRINTF(("getmsg: Accept fd = %d\n", st->s_afd)); + + if ((error = copyin(sup, skp, sasize)) != 0) + return error; + + sc.cmd = SVR4_TI_ACCEPT_REPLY; + sc.offs = 0x18; + sc.pad[0] = 0x0; + + switch (st->s_family) { + case AF_INET: + sc.pad[1] = 0x28; + sockaddr_to_netaddr_in(&sc, &sain); + ctl.len = 40; + sc.len = sasize; + break; + + case AF_LOCAL: + sc.pad[1] = 0x00010000; + sc.pad[2] = 0xf6bcdaa0; /* I don't know what that is */ + sc.pad[3] = 0x00010000; + ctl.len = 134; + sc.len = sasize + 4; + break; + + default: + return ENOSYS; + } + + dat.len = -1; + fl = 0; + st->s_cmd = SVR4_TI__ACCEPT_OK; + break; + + case SVR4_TI_SENDTO_REQUEST: + DPRINTF(("getmsg: TI_SENDTO_REQUEST\n")); + if (ctl.maxlen > 36 && ctl.len < 36) + ctl.len = 36; + + if ((error = copyin(ctl.buf, &sc, ctl.len)) != 0) + return error; + + switch (st->s_family) { + case AF_INET: + sockaddr_to_netaddr_in(&sc, &sain); + break; + + case AF_LOCAL: + sockaddr_to_netaddr_un(&sc, &saun); + break; + + default: + return ENOSYS; + } + + msg.msg_name = (caddr_t) sup; + msg.msg_namelen = sasize; + msg.msg_iov = &aiov; + msg.msg_iovlen = 1; + msg.msg_control = 0; + aiov.iov_base = dat.buf; + aiov.iov_len = dat.maxlen; + msg.msg_flags = 0; + + error = svr4_recvit(p, SCARG(uap, fd), &msg, (caddr_t) flen); + + if (error) { + DPRINTF(("getmsg: recvit failed %d\n", error)); + return error; + } + + if ((error = copyin(msg.msg_name, skp, sasize)) != 0) + return error; + + sc.cmd = SVR4_TI_RECVFROM_IND; + + switch (st->s_family) { + case AF_INET: + sc.len = sasize; + sockaddr_to_netaddr_in(&sc, &sain); + break; + + case AF_LOCAL: + sc.len = sasize + 4; + sockaddr_to_netaddr_un(&sc, &saun); + break; + + default: + return ENOSYS; + } + + dat.len = *retval; + fl = 0; + st->s_cmd = sc.cmd; + break; + + default: + st->s_cmd = sc.cmd; + if (st->s_cmd == SVR4_TI_CONNECT_REQUEST) { + struct read_args ra; + + /* More wierdness: Again, I can't find documentation + * to back this up, but when a process does a generic + * "getmsg()" call it seems that the command field is + * zero and the length of the data area is zero. I + * think processes expect getmsg() to fill in dat.len + * after reading at most dat.maxlen octets from the + * stream. Since we're using sockets I can let + * read() look after it and frob return values + * appropriately (or inappropriately :-) + * -- newton@atdot.dotat.org XXX + */ + SCARG(&ra, fd) = SCARG(uap, fd); + SCARG(&ra, buf) = dat.buf; + SCARG(&ra, nbyte) = dat.maxlen; + if ((error = read(p, &ra)) != 0) { + return error; + } + dat.len = *retval; + *retval = 0; + st->s_cmd = SVR4_TI_SENDTO_REQUEST; + break; + } + DPRINTF(("getmsg: Unknown state %x\n", st->s_cmd)); + return EINVAL; + } + + if (SCARG(uap, ctl)) { + if (ctl.len != -1) + if ((error = copyout(&sc, ctl.buf, ctl.len)) != 0) + return error; + + if ((error = copyout(&ctl, SCARG(uap, ctl), sizeof(ctl))) != 0) + return error; + } + + if (SCARG(uap, dat)) { + if ((error = copyout(&dat, SCARG(uap, dat), sizeof(dat))) != 0) + return error; + } + + if (SCARG(uap, flags)) { /* XXX: Need translation */ + if ((error = copyout(&fl, SCARG(uap, flags), sizeof(fl))) != 0) + return error; + } + + *retval = 0; + +#ifdef DEBUG_SVR4 + show_msg("<getmsg", SCARG(uap, fd), SCARG(uap, ctl), + SCARG(uap, dat), fl); +#endif /* DEBUG_SVR4 */ + return error; +} + +int svr4_sys_send(p, uap) + struct proc *p; + struct svr4_sys_send_args *uap; +{ + struct osend_args osa; + SCARG(&osa, s) = SCARG(uap, s); + SCARG(&osa, buf) = SCARG(uap, buf); + SCARG(&osa, len) = SCARG(uap, len); + SCARG(&osa, flags) = SCARG(uap, flags); + return osend(p, &osa); +} + +int svr4_sys_recv(p, uap) + struct proc *p; + struct svr4_sys_recv_args *uap; +{ + struct orecv_args ora; + SCARG(&ora, s) = SCARG(uap, s); + SCARG(&ora, buf) = SCARG(uap, buf); + SCARG(&ora, len) = SCARG(uap, len); + SCARG(&ora, flags) = SCARG(uap, flags); + return orecv(p, &ora); +} + +/* + * XXX This isn't necessary, but it's handy for inserting debug code into + * sendto(). Let's leave it here for now... + */ +int +svr4_sys_sendto(p, uap) + struct proc *p; + struct svr4_sys_sendto_args *uap; +{ + struct sendto_args sa; + + SCARG(&sa, s) = SCARG(uap, s); + SCARG(&sa, buf) = SCARG(uap, buf); + SCARG(&sa, len) = SCARG(uap, len); + SCARG(&sa, flags) = SCARG(uap, flags); + SCARG(&sa, to) = SCARG(uap, to); + SCARG(&sa, tolen) = SCARG(uap, tolen); + + DPRINTF(("calling sendto()\n")); + return sendto(p, &sa); +} + |