2.11BSD/sys/sys/uipc_syscalls.c
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*
* @(#)uipc_syscalls.c 7.1.3 (2.11BSD) 1999/9/13
*/
#include "param.h"
#include "../machine/seg.h"
#include "../machine/psl.h"
#include "systm.h"
#include "user.h"
#include "proc.h"
#include "file.h"
#include "inode.h"
#include "buf.h"
#include "mbuf.h"
#include "protosw.h"
#include "socket.h"
#include "socketvar.h"
#include "uio.h"
#include "domain.h"
#include "pdpif/if_uba.h"
#include "netinet/in.h"
#include "netinet/in_systm.h"
static void
MBZAP(m, len, type)
register struct mbuf *m;
int len, type;
{
m->m_next = 0;
m->m_off = MMINOFF;
m->m_len = len;
m->m_type = type;
m->m_act = 0;
}
/*
* System call interface to the socket abstraction.
*/
extern int netoff;
struct file *gtsockf();
socket()
{
register struct a {
int domain;
int type;
int protocol;
} *uap = (struct a *)u.u_ap;
struct socket *so;
register struct file *fp;
if (netoff)
return(u.u_error = ENETDOWN);
if ((fp = falloc()) == NULL)
return;
fp->f_flag = FREAD|FWRITE;
fp->f_type = DTYPE_SOCKET;
u.u_error = SOCREATE(uap->domain, &so, uap->type, uap->protocol);
if (u.u_error)
goto bad;
fp->f_socket = so;
return;
bad:
u.u_ofile[u.u_r.r_val1] = 0;
fp->f_count = 0;
}
bind()
{
register struct a {
int s;
caddr_t name;
u_int namelen;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
register struct mbuf *nam;
char sabuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
nam = (struct mbuf *)sabuf;
MBZAP(nam, uap->namelen, MT_SONAME);
if (uap->namelen > MLEN)
return (u.u_error = EINVAL);
u.u_error = copyin(uap->name, mtod(nam, caddr_t), uap->namelen);
if (u.u_error)
return;
u.u_error = SOBIND(fp->f_socket, nam);
}
listen()
{
register struct a {
int s;
int backlog;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
u.u_error = SOLISTEN(fp->f_socket, uap->backlog);
}
accept()
{
register struct a {
int s;
caddr_t name;
int *anamelen;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
struct mbuf *nam;
int namelen;
int s;
register struct socket *so;
char sabuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
if (uap->name == 0)
goto noname;
u.u_error = copyin((caddr_t)uap->anamelen, (caddr_t)&namelen,
sizeof (namelen));
if (u.u_error)
return;
#ifndef pdp11
if (useracc((caddr_t)uap->name, (u_int)namelen, B_WRITE) == 0) {
u.u_error = EFAULT;
return;
}
#endif
noname:
fp = gtsockf(uap->s);
if (fp == 0)
return;
s = splnet();
so = fp->f_socket;
if (SOACC1(so)) {
splx(s);
return;
}
if (ufalloc(0) < 0) {
splx(s);
return;
}
fp = falloc();
if (fp == 0) {
u.u_ofile[u.u_r.r_val1] = 0;
splx(s);
return;
}
if (!(so = (struct socket *)ASOQREMQUE(so, 1))) /* deQ in super */
panic("accept");
fp->f_type = DTYPE_SOCKET;
fp->f_flag = FREAD|FWRITE;
fp->f_socket = so;
nam = (struct mbuf *)sabuf;
MBZAP(nam, 0, MT_SONAME);
u.u_error = SOACCEPT(so, nam);
if (uap->name) {
if (namelen > nam->m_len)
namelen = nam->m_len;
/* SHOULD COPY OUT A CHAIN HERE */
(void) copyout(mtod(nam, caddr_t), (caddr_t)uap->name,
(u_int)namelen);
(void) copyout((caddr_t)&namelen, (caddr_t)uap->anamelen,
sizeof (*uap->anamelen));
}
splx(s);
}
connect()
{
register struct a {
int s;
caddr_t name;
u_int namelen;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
register struct socket *so;
struct mbuf *nam;
int s;
char sabuf[MSIZE];
struct socket kcopy;
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
if (uap->namelen > MLEN)
return (u.u_error = EINVAL);
nam = (struct mbuf *)sabuf;
MBZAP(nam, uap->namelen, MT_SONAME);
u.u_error = copyin(uap->name, mtod(nam, caddr_t), uap->namelen);
if (u.u_error)
return;
so = fp->f_socket;
/*
* soconnect was modified to clear the isconnecting bit on errors.
* also, it was changed to return the EINPROGRESS error if
* nonblocking, etc.
*/
u.u_error = SOCON1(so, nam);
if (u.u_error)
return;
/*
* i don't think the setjmp stuff works too hot in supervisor mode,
* so what is done instead is do the setjmp here and then go back
* to supervisor mode to do the "while (isconnecting && !error)
* sleep()" loop.
*/
s = splnet();
if (setjmp(&u.u_qsave))
{
u.u_error = EINTR;
goto bad2;
}
u.u_error = CONNWHILE(so);
bad2:
splx(s);
}
socketpair()
{
register struct a {
int domain;
int type;
int protocol;
int *rsv;
} *uap = (struct a *)u.u_ap;
register struct file *fp1, *fp2;
struct socket *so1, *so2;
int sv[2];
if (netoff)
return(u.u_error = ENETDOWN);
u.u_error = SOCREATE(uap->domain, &so1, uap->type, uap->protocol);
if (u.u_error)
return;
u.u_error = SOCREATE(uap->domain, &so2, uap->type, uap->protocol);
if (u.u_error)
goto free;
fp1 = falloc();
if (fp1 == NULL)
goto free2;
sv[0] = u.u_r.r_val1;
fp1->f_flag = FREAD|FWRITE;
fp1->f_type = DTYPE_SOCKET;
fp1->f_socket = so1;
fp2 = falloc();
if (fp2 == NULL)
goto free3;
fp2->f_flag = FREAD|FWRITE;
fp2->f_type = DTYPE_SOCKET;
fp2->f_socket = so2;
sv[1] = u.u_r.r_val1;
u.u_error = SOCON2(so1, so2);
if (u.u_error)
goto free4;
if (uap->type == SOCK_DGRAM) {
/*
* Datagram socket connection is asymmetric.
*/
u.u_error = SOCON2(so2, so1);
if (u.u_error)
goto free4;
}
u.u_r.r_val1 = 0;
(void) copyout((caddr_t)sv, (caddr_t)uap->rsv, 2 * sizeof (int));
return;
free4:
fp2->f_count = 0;
u.u_ofile[sv[1]] = 0;
free3:
fp1->f_count = 0;
u.u_ofile[sv[0]] = 0;
free2:
(void)SOCLOSE(so2);
free:
(void)SOCLOSE(so1);
}
sendto()
{
register struct a {
int s;
caddr_t buf;
int len;
int flags;
caddr_t to;
int tolen;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov;
msg.msg_name = uap->to;
msg.msg_namelen = uap->tolen;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_accrights = 0;
msg.msg_accrightslen = 0;
sendit(uap->s, &msg, uap->flags);
}
send()
{
register struct a {
int s;
caddr_t buf;
int len;
int flags;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_accrights = 0;
msg.msg_accrightslen = 0;
sendit(uap->s, &msg, uap->flags);
}
sendmsg()
{
register struct a {
int s;
caddr_t msg;
int flags;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov[MSG_MAXIOVLEN];
u.u_error = copyin(uap->msg, (caddr_t)&msg, sizeof (msg));
if (u.u_error)
return;
if ((u_int)msg.msg_iovlen >= sizeof (aiov) / sizeof (aiov[0])) {
u.u_error = EMSGSIZE;
return;
}
u.u_error =
copyin((caddr_t)msg.msg_iov, (caddr_t)aiov,
(unsigned)(msg.msg_iovlen * sizeof (aiov[0])));
if (u.u_error)
return;
msg.msg_iov = aiov;
sendit(uap->s, &msg, uap->flags);
}
sendit(s, mp, flags)
int s;
register struct msghdr *mp;
int flags;
{
register struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
struct mbuf *to, *rights;
int len;
char sabuf[MSIZE], ribuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(s);
if (fp == 0)
return;
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
auio.uio_rw = UIO_WRITE;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++)
{
if (iov->iov_len == 0)
continue;
auio.uio_resid += iov->iov_len;
}
if (mp->msg_name) {
to = (struct mbuf *)sabuf;
MBZAP(to, mp->msg_namelen, MT_SONAME);
u.u_error =
copyin(mp->msg_name, mtod(to, caddr_t), mp->msg_namelen);
if (u.u_error)
return;
} else
to = 0;
if (mp->msg_accrights) {
rights = (struct mbuf *)ribuf;
MBZAP(rights, mp->msg_accrightslen, MT_RIGHTS);
if (mp->msg_accrightslen > MLEN)
return(u.u_error = EINVAL);
u.u_error = copyin(mp->msg_accrights, mtod(rights, caddr_t),
mp->msg_accrightslen);
if (u.u_error)
return;
} else
rights = 0;
len = auio.uio_resid;
if (setjmp(&u.u_qsave))
{
if (auio.uio_resid == len)
return;
else
u.u_error = 0;
}
else
u.u_error = SOSEND(fp->f_socket, to, &auio, flags, rights);
u.u_r.r_val1 = len - auio.uio_resid;
}
recvfrom()
{
register struct a {
int s;
caddr_t buf;
int len;
int flags;
caddr_t from;
int *fromlenaddr;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov;
int len;
u.u_error = copyin((caddr_t)uap->fromlenaddr, (caddr_t)&len,
sizeof (len));
if (u.u_error)
return;
msg.msg_name = uap->from;
msg.msg_namelen = len;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_accrights = 0;
msg.msg_accrightslen = 0;
recvit(uap->s, &msg, uap->flags, (caddr_t)uap->fromlenaddr, (caddr_t)0);
}
recv()
{
register struct a {
int s;
caddr_t buf;
int len;
int flags;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &aiov;
msg.msg_iovlen = 1;
aiov.iov_base = uap->buf;
aiov.iov_len = uap->len;
msg.msg_accrights = 0;
msg.msg_accrightslen = 0;
recvit(uap->s, &msg, uap->flags, (caddr_t)0, (caddr_t)0);
}
recvmsg()
{
register struct a {
int s;
struct msghdr *msg;
int flags;
} *uap = (struct a *)u.u_ap;
struct msghdr msg;
struct iovec aiov[MSG_MAXIOVLEN];
u.u_error = copyin((caddr_t)uap->msg, (caddr_t)&msg, sizeof (msg));
if (u.u_error)
return;
if ((u_int)msg.msg_iovlen >= sizeof (aiov) / sizeof (aiov[0])) {
u.u_error = EMSGSIZE;
return;
}
u.u_error =
copyin((caddr_t)msg.msg_iov, (caddr_t)aiov,
(unsigned)(msg.msg_iovlen * sizeof(aiov[0])));
if (u.u_error)
return;
recvit(uap->s, &msg, uap->flags,
(caddr_t)&uap->msg->msg_namelen,
(caddr_t)&uap->msg->msg_accrightslen);
}
recvit(s, mp, flags, namelenp, rightslenp)
int s;
register struct msghdr *mp;
int flags;
caddr_t namelenp, rightslenp;
{
register struct file *fp;
struct uio auio;
register struct iovec *iov;
register int i;
struct mbuf *from, *rights;
int len, m_freem();
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(s);
if (fp == 0)
return;
auio.uio_iov = mp->msg_iov;
auio.uio_iovcnt = mp->msg_iovlen;
auio.uio_segflg = UIO_USERSPACE;
auio.uio_offset = 0; /* XXX */
auio.uio_resid = 0;
auio.uio_rw = UIO_READ;
iov = mp->msg_iov;
for (i = 0; i < mp->msg_iovlen; i++, iov++)
{
if (iov->iov_len == 0)
continue;
auio.uio_resid += iov->iov_len;
}
len = auio.uio_resid;
if (setjmp(&u.u_qsave))
{
if (auio.uio_resid == len)
return;
else
u.u_error = 0;
}
else
u.u_error = SORECEIVE((struct socket *)fp->f_data,
&from, &auio,flags, &rights);
if (u.u_error)
return;
u.u_r.r_val1 = len - auio.uio_resid;
if (mp->msg_name) {
len = mp->msg_namelen;
if (len <= 0 || from == 0)
len = 0;
else
(void) NETCOPYOUT(from, mp->msg_name, &len);
(void) copyout((caddr_t)&len, namelenp, sizeof(int));
}
if (mp->msg_accrights) {
len = mp->msg_accrightslen;
if (len <= 0 || rights == 0)
len = 0;
else
(void) NETCOPYOUT(rights, mp->msg_accrights, &len);
(void) copyout((caddr_t)&len, rightslenp, sizeof(int));
}
if (rights)
M_FREEM(rights);
if (from)
M_FREEM(from);
}
shutdown()
{
register struct a {
int s;
int how;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
u.u_error = SOSHUTDOWN(fp->f_socket, uap->how);
}
setsockopt()
{
register struct a {
int s;
int level;
int name;
caddr_t val;
u_int valsize;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
register struct mbuf *m = NULL;
char optbuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
if (uap->valsize > MLEN) {
u.u_error = EINVAL;
return;
}
if (uap->val) {
m = (struct mbuf *)optbuf;
MBZAP(m, uap->valsize, MT_SOOPTS);
u.u_error =
copyin(uap->val, mtod(m, caddr_t), (u_int)uap->valsize);
if (u.u_error)
return;
}
u.u_error = SOSETOPT(fp->f_socket, uap->level, uap->name, m);
}
getsockopt()
{
register struct a {
int s;
int level;
int name;
caddr_t val;
int *avalsize;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
struct mbuf *m = NULL, *m_free();
int valsize;
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->s);
if (fp == 0)
return;
if (uap->val) {
u.u_error = copyin((caddr_t)uap->avalsize, (caddr_t)&valsize,
sizeof (valsize));
if (u.u_error)
return;
} else
valsize = 0;
u.u_error =
SOGETOPT(fp->f_socket, uap->level, uap->name, &m);
if (u.u_error)
goto bad;
if (uap->val && valsize && m != NULL) {
u.u_error = NETCOPYOUT(m, uap->val, &valsize);
if (u.u_error)
goto bad;
u.u_error = copyout((caddr_t)&valsize, (caddr_t)uap->avalsize,
sizeof (valsize));
}
bad:
if (m != NULL)
M_FREE(m);
}
/*
* Get socket name.
*/
getsockname()
{
register struct a {
int fdes;
caddr_t asa;
int *alen;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
struct mbuf *m;
int len;
char sabuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->fdes);
if (fp == 0)
return;
u.u_error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len));
if (u.u_error)
return;
m = (struct mbuf *)sabuf;
MBZAP(m, 0, MT_SONAME);
u.u_error = SOGETNAM(fp->f_socket, m);
if (u.u_error)
return;
if (len > m->m_len)
len = m->m_len;
u.u_error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len);
if (u.u_error)
return;
u.u_error = copyout((caddr_t)&len, (caddr_t)uap->alen, sizeof (len));
}
/*
* Get name of peer for connected socket.
*/
getpeername()
{
register struct a {
int fdes;
caddr_t asa;
int *alen;
} *uap = (struct a *)u.u_ap;
register struct file *fp;
struct mbuf *m;
u_int len;
char sabuf[MSIZE];
if (netoff)
return(u.u_error = ENETDOWN);
fp = gtsockf(uap->fdes);
if (fp == 0)
return;
m = (struct mbuf *)sabuf;
MBZAP(m, 0, MT_SONAME);
u.u_error = copyin((caddr_t)uap->alen, (caddr_t)&len, sizeof (len));
if (u.u_error)
return;
u.u_error = SOGETPEER(fp->f_socket, m);
if (u.u_error)
return;
if (len > m->m_len)
len = m->m_len;
u.u_error = copyout(mtod(m, caddr_t), (caddr_t)uap->asa, (u_int)len);
if (u.u_error)
return;
u.u_error = copyout((caddr_t)&len, (caddr_t)uap->alen, sizeof (len));
}
#ifndef pdp11
sockargs(aname, name, namelen, type)
struct mbuf **aname;
caddr_t name;
int namelen, type;
{
register struct mbuf *m;
int error;
struct mbuf *m_free();
if (namelen > MLEN)
return (EINVAL);
m = m_get(M_WAIT, type);
if (m == NULL)
return (ENOBUFS);
m->m_len = namelen;
error = copyin(name, mtod(m, caddr_t), (u_int)namelen);
if (error)
(void) m_free(m);
else
*aname = m;
return (error);
}
#endif
struct file *
gtsockf(fdes)
int fdes;
{
register struct file *fp;
fp = getf(fdes);
if (fp == NULL)
return (0);
if (fp->f_type != DTYPE_SOCKET) {
u.u_error = ENOTSOCK;
return (0);
}
return (fp);
}