RCS_ID_C="$Id: amiga_syscalls.c,v 1.25 1993/06/04 11:16:15 jraja Exp $";
/*
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 * 
 * Created: Sun Feb 14 18:35:48 1993 too
 * Last modified: Fri Jun  4 00:30:56 1993 jraja
 * 
 * HISTORY
 * $Log: amiga_syscalls.c,v $
 * Revision 1.25  1993/06/04  11:16:15  jraja
 * Fixes for first public release.
 *
 * Revision 1.24  1993/05/17  01:07:47  ppessi
 * Changed RCS version.
 *
 * Revision 1.23  1993/05/15  10:09:15  too
 * Uses API_ENTRY() and API_EXIT() no more. Uses CHECK_TASK(),
 * ObtainSyscallSemaphore() -- ReleaseSyscallSemaphore(), API_STD_RETURN()
 * instead.
 *
 * Revision 1.22  93/05/04  14:06:35  14:06:35  too (Tomi Ollila)
 * Changed local api function names to the same as the names given
 * to API programmer, but w/ starting _
 * 
 * Revision 1.21  93/04/28  13:43:59  13:43:59  too (Tomi Ollila)
 * Changed some LONGs to ULONGs so negative arguments can't get through
 * as super big when casted to unsigned longs
 * 
 * Revision 1.20  93/04/26  11:52:39  11:52:39  too (Tomi Ollila)
 * Changed include paths of amiga_api.h, amiga_libcallentry.h and amiga_raf.h
 * from kern to api
 * 
 * Revision 1.19  93/04/06  18:05:00  18:05:00  too (Tomi Ollila)
 * Pitik| olla Hoppu, nyt tuli leikin loppu...ei vaineskaan...
 * ..I meant to say that I forgot to give libPtr as argument to
 * Obtain- and ReleaseSyscallSemaphore
 * 
 * Revision 1.18  93/04/06  17:10:41  17:10:41  too (Tomi Ollila)
 * Minor Changes since LONGs weren't changed to WORDs
 * 
 * Revision 1.17  93/04/06  15:15:42  15:15:42  jraja (Jarno Tapio Rajahalme)
 * Changed spl function return value storage to spl_t,
 * changed bcopys and bzeros to aligned and/or const when possible,
 * added inclusion of conf.h to every .c file.
 * 
 * Revision 1.16  93/03/30  14:28:15  14:28:15  too (Tomi Ollila)
 * Moved fdAlloc() to amiga_libcallentry.h. Changed API_ENTRY() and API_EXIT()
 * out of the code. Now uses CHECK_TASK() and API_STD_RETURN()
 * 
 * Revision 1.15  93/03/19  14:14:46  14:14:46  too (Tomi Ollila)
 * Code changes at night 17-18 March 1993
 * 
 * Revision 1.14  93/03/13  17:12:55  17:12:55  ppessi (Pekka Pessi)
 * Fixed bugs with variable initializations. Works with UDP.
 * 
 * Revision 1.13  93/03/12  19:07:12  19:07:12  too (Tomi Ollila)
 * Code triplechecked w/ pessi and jraja
 * 
 * Revision 1.12  93/03/12  15:21:41  15:21:41  too (Tomi Ollila)
 * Added usage of nextDToSearch in fdAlloc: starts searching free socket
 * descriptor from it and changes it's value to one higher than found 
 * free descriptor.
 * 
 * Revision 1.11  93/03/11  22:28:03  22:28:03  too (Tomi Ollila)
 * Fixed bugs in Accept, socket now put in socket descriptor table
 * and so_refcnt now initialized in accept too
 * 
 * Revision 1.10  93/03/07  12:07:45  12:07:45  too (Tomi Ollila)
 * Added initialazion of so_refcnt (to 1)
 * 
 * Revision 1.9  93/03/07  00:53:49  00:53:49  jraja (Jarno Tapio Rajahalme)
 * Fixed cast on soreceive call.
 * Added SAVEDS keywords to api function definitions.
 * Added API_ENTRY() where it was missing.
 * Changed one return to goto Return.
 * 
 * Revision 1.8  93/03/05  21:11:11  21:11:11  jraja (Jarno Tapio Rajahalme)
 * Fixed includes (again).
 * 
 * Revision 1.7  93/03/05  12:30:13  12:30:13  jraja (Jarno Tapio Rajahalme)
 * Removed <kern/amiga_syscalls_protos.h>.
 * Moved <sys/param.h> upper.
 * 
 * Revision 1.6  93/03/05  03:26:09  03:26:09  ppessi (Pekka Pessi)
 * Compiles with SASC. Initial test version.
 * 
 * Revision 1.5  93/03/02  15:23:28  15:23:28  ppessi (Pekka Pessi)
 * 	fixed slpnet();
 * 
 * Revision 1.4  93/02/25  18:48:44  18:48:44  too (Tomi Ollila)
 * Well, references to too's code fixed.
 * 
 * Revision 1.3  93/02/25  12:41:17  12:41:17  too (Tomi Ollila)
 * blob
 * 
 * Revision 1.2  93/02/23  11:41:15  11:41:15  too (Tomi Ollila)
 * Added uio.uio_procp so sosend() and soreceive() can use it.
 * 
 * Revision 1.1  93/02/22  15:09:58  15:09:58  too (Tomi Ollila)
 * Initial revision
 * 
 * 
 */

#include <conf.h>

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/synch.h>
#include <sys/errno.h>

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/semaphores.h>

#include <api/amiga_api.h>
#include <api/amiga_libcallentry.h>

#include <kern/uipc_socket_protos.h>
#include <kern/uipc_socket2_protos.h>
  
static LONG sendit(struct SocketBase *	p,
		   LONG	s,
		   struct msghdr * mp,
		   LONG flags,
		   LONG * retsize);

static LONG recvit(struct SocketBase * p,
		   LONG s,
		   struct msghdr * mp,
		   LONG *namelen_p,
		   LONG	flags,
		   LONG * retsize);

static LONG sockArgs(struct mbuf **mp,
		     caddr_t buf,
		     LONG buflen,
		     LONG type);


LONG SAVEDS RAF4(_socket,
		 struct SocketBase *, 	libPtr,		a6,
		 LONG,			domain,		d0,
		 LONG,			type,		d1,
		 LONG,			protocol,	d2)

#if 0
{
#endif
  
  struct socket *so;
  LONG fd, error;

  CHECK_TASK();

  if (error = fdAlloc(libPtr, &fd))
      goto Return;
  
  ObtainSyscallSemaphore(libPtr);
  error = socreate(domain, &so, type, protocol);
  ReleaseSyscallSemaphore(libPtr);

  if (! error) {
    so->so_refcnt = 1;		/* reference count is pure AmiTCP addition */
    libPtr->dTable[fd] = so;
  }

 Return: API_STD_RETURN(error, fd);
}


LONG SAVEDS RAF4(_bind,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 caddr_t,		name,		a0,
		 LONG,			namelen,	d1)
#if 0
{
#endif  

  struct socket *so;
  struct mbuf *nam;
  LONG error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);
  
  if (error = getSock(libPtr, s, &so))
    goto Return;
  if (error = sockArgs(&nam, name, namelen, MT_SONAME))
    goto Return;
  error = sobind(so, nam);
  m_freem(nam);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}

LONG SAVEDS RAF3(_listen,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 LONG,			backlog,	d1)
#if 0
{
#endif

  struct socket *so;
  LONG error;
  
  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);
  
  if (error = getSock(libPtr, s, &so))
    goto Return;
  error = solisten(so, backlog);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);

}
    
LONG SAVEDS RAF4(_accept,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 caddr_t,		name,		a0,
		 ULONG *,		anamelen,	a1)
#if 0
{
#endif

  struct socket *so;
  struct mbuf *nam;
  spl_t old_spl;
  LONG error, fd;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, s, &so))
    goto Return;

  old_spl = splnet();
  if ((so->so_options & SO_ACCEPTCONN) == 0) {
    error = EINVAL;
    goto Return_spl;
  }
  if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
    error = EWOULDBLOCK;
    goto Return_spl;
  }
  while (so->so_qlen == 0 && so->so_error == 0) {
    if (so->so_state & SS_CANTRCVMORE) {
      so->so_error = ECONNABORTED;
      break;
    }
    if (error = tsleep(libPtr, (caddr_t)&so->so_timeo, netcon, NULL)) {
      goto Return_spl;
    }
  }
  if (so->so_error) {
    error = so->so_error;
    so->so_error = 0;
    goto Return_spl;
  }
   
  if (error = fdAlloc(libPtr, &fd)) {
    goto Return_spl;
  }

  {
    struct socket *aso = so->so_q;
    if (soqremque(aso, 1) == 0)
      panic("accept");
    so = aso;
  }	

  libPtr->dTable[fd] = so;
  so->so_refcnt = 1;  /* pure AmiTCP addition */

  nam = m_get(M_WAIT, MT_SONAME);
  (void)soaccept(so, nam);  /* is this always successful */
  if (name) {
    if (*anamelen > nam->m_len)
      *anamelen = nam->m_len;
    /* SHOULD COPY OUT A CHAIN HERE */
    aligned_bcopy(mtod(nam, caddr_t), (caddr_t)name, (u_int)*anamelen);
  }
  m_freem(nam);

 Return_spl:
  splx(old_spl);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, fd);
}

LONG SAVEDS RAF4(_connect,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 caddr_t,		name,		a0,
		 LONG,			namelen,	d1)
#if 0
{
#endif

  /*register*/ struct socket *so;
  struct mbuf *nam;
  LONG error;
  spl_t old_spl;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, s, &so))
    goto Return;
  if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
    error = EALREADY;
    goto Return;
  }
  if (error = sockArgs(&nam, name, namelen, MT_SONAME))
    goto Return;
  error = soconnect(so, nam);
  if (error)
    goto bad;
  if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) {
    m_freem(nam);
    error = EINPROGRESS;
    goto Return;
  }	
  old_spl = splnet();
  while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0)
    if (error = tsleep(libPtr,(caddr_t)&so->so_timeo, netcon, NULL))
      break;
  if (error == 0) {
    error = so->so_error;
    so->so_error = 0;
  }
  splx(old_spl);
 bad:
  so->so_state &= ~SS_ISCONNECTING;
  m_freem(nam);
  if (error == ERESTART)
    error = EINTR;

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);

}

LONG SAVEDS RAF7(_sendto,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			s,	d0,
		 caddr_t,		buf,	a0,
		 LONG,			len,	d1,
		 LONG,			flags,	d2,
		 caddr_t,		to,	a1,
		 LONG,			tolen,	d3)
#if 0
{
#endif

  struct msghdr msg;
  struct iovec aiov;
  LONG error, retval;
  
  CHECK_TASK();

  msg.msg_name = to;
  msg.msg_namelen = tolen;
  msg.msg_iov = &aiov;
  msg.msg_iovlen = 1;
  msg.msg_control = 0;
  aiov.iov_base = buf;
  aiov.iov_len = len;
  
  ObtainSyscallSemaphore(libPtr);
  error = sendit(libPtr, s, &msg, flags, &retval);
  ReleaseSyscallSemaphore(libPtr);
  
  API_STD_RETURN(error, retval);
}

LONG SAVEDS RAF5(_send,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			s,	d0,
		 caddr_t,		buf,	a0,
		 LONG,			len,	d1,
		 LONG,			flags,	d2)
#if 0     
{
#endif

  struct msghdr msg;
  struct iovec aiov;
  LONG error, retval;
  
  CHECK_TASK();

  msg.msg_name = 0;
  msg.msg_namelen = 0;
  msg.msg_iov = &aiov;
  msg.msg_iovlen = 1;
  msg.msg_control = 0;
  aiov.iov_base = buf;
  aiov.iov_len = len;
  
  ObtainSyscallSemaphore(libPtr);
  error = sendit(libPtr, s, &msg, flags, &retval);
  ReleaseSyscallSemaphore(libPtr);
  
  API_STD_RETURN(error, retval);
}

/* LONG sendmsg(); */

static LONG sendit(struct SocketBase * p,
		   LONG	s,
		   struct msghdr * mp,
		   LONG flags,
		   LONG * retsize)
{
  struct socket *so;
  struct uio auio;
  register int i;
  register struct iovec *iov;
  struct mbuf *to, *control;
  LONG len, error;

  if (error = getSock(p, s, &so))
    return (error);

  auio.uio_iov = mp->msg_iov;
  auio.uio_iovcnt = mp->msg_iovlen;
  auio.uio_procp = p;
  auio.uio_resid = 0;
  iov = mp->msg_iov;

  for(i = 0; i < mp->msg_iovlen; i++, iov++) {
    if (iov->iov_len < 0)
      return (EINVAL);
    if ((auio.uio_resid += iov->iov_len) < 0)
      return (EINVAL);
  }
  
  if (mp->msg_name) {
    if (error = sockArgs(&to, mp->msg_name, mp->msg_namelen, MT_SONAME))
      return (error);
  }
  else
    to = 0;
  
  if (mp->msg_control) {
    if (mp->msg_controllen < sizeof (struct cmsghdr)) {
      error = EINVAL;
      goto bad;
    }
    if (error = sockArgs(&control, mp->msg_control,
			 mp->msg_controllen, MT_CONTROL))
      goto bad;
  }
  else
    control = 0;

  len = auio.uio_resid;
  if (error = sosend(so, to, &auio, (struct mbuf *)0, control, flags)) {
    if (auio.uio_resid != len && (error == ERESTART || error == EINTR ||
				  error == EWOULDBLOCK))
      error = 0;
  }
  if (error == 0)
    *retsize = len - auio.uio_resid;

  /* sosend() frees control if allocated */
 bad:
  if (to)
    m_freem(to);

  return (error);
}

LONG SAVEDS RAF7(_recvfrom,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 caddr_t,		buf,		a0,
		 LONG,			len,		d1,
		 LONG,			flags,		d2,
		 caddr_t,		from,		a1,
		 LONG *,		fromlenaddr,	a2)
#if 0
{
#endif

  struct msghdr	msg;
  struct iovec aiov;
  LONG error, retval;

  CHECK_TASK();

  if (fromlenaddr)
    msg.msg_namelen = *fromlenaddr;
  else
    msg.msg_namelen = 0;

  msg.msg_name = from;
  msg.msg_iov = &aiov;
  msg.msg_iovlen = 1;
  msg.msg_control = 0;
  aiov.iov_base = buf;
  aiov.iov_len = len;

  ObtainSyscallSemaphore(libPtr);
  error = recvit(libPtr, s, &msg, fromlenaddr, flags, &retval);
  ReleaseSyscallSemaphore(libPtr);

  API_STD_RETURN(error, retval);
}

LONG SAVEDS RAF5(_recv,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			s,	d0,
		 caddr_t,		buf,	a0,
		 LONG,			len,	d1,
		 LONG,			flags,	d2)
#if 0     
{
#endif

  struct msghdr msg;
  struct iovec aiov;
  LONG error, retval;

  CHECK_TASK();

  msg.msg_name = 0;
  msg.msg_namelen = 0;
  msg.msg_iov = &aiov;
  msg.msg_iovlen = 1;
  msg.msg_control = 0;
  aiov.iov_base = buf;
  aiov.iov_len = len;

  ObtainSyscallSemaphore(libPtr);
  error = recvit(libPtr, s, &msg, NULL, flags, &retval);
  ReleaseSyscallSemaphore(libPtr);

  API_STD_RETURN(error, retval);
}

/* LONG recvmsg() */

static LONG recvit(struct SocketBase * p,
		   LONG s,
		   struct msghdr * mp,
		   LONG * namelenp,
		   LONG flags,
		   LONG * retsize)
{
  struct socket * so;
  struct uio auio;
  struct iovec *iov;
  register int i;
  ULONG len;
  LONG error;
  struct mbuf *from = 0, *control = 0;

  if (error = getSock(p, s, &so))
    return (error);

  auio.uio_iov = mp->msg_iov;
  auio.uio_iovcnt = mp->msg_iovlen;
  auio.uio_procp = p;
  auio.uio_resid = 0;

  iov = mp->msg_iov;
  for(i = 0; i < mp->msg_iovlen; i++, iov++) {
    if (iov->iov_len < 0)
      return (EINVAL);
    if ((auio.uio_resid += iov->iov_len) < 0)
      return (EINVAL);
  }
  len = auio.uio_resid;
  if (error = soreceive(so, &from, &auio,
			(struct mbuf **)0, &control, (int *)&flags))
    if (auio.uio_resid != len && (error == ERESTART || error == EINTR ||
				  error == EWOULDBLOCK))
      error = 0;
  
  if (error)
    goto out;
  
  *retsize = len - auio.uio_resid;
  
  if (mp->msg_name) {
    len = mp->msg_namelen;
    if (len <= 0 || from == 0)
      len = 0;
    else {
      if (len > from->m_len)
	len = from->m_len;
      aligned_bcopy(mtod(from, caddr_t), (caddr_t)mp->msg_name, (unsigned)len);
    }
    mp->msg_namelen = len;
    *namelenp = len;
  }
  if (mp->msg_control) {
    len = mp->msg_controllen;
    if (len <= 0 || control == 0)
      len = 0;
    else {
      if (len >= control->m_len)
	len = control->m_len;
/*      else
	flags |= MSG_CTRUNC;  no syscall ever does something w/ mp->flags */
      
      aligned_bcopy(mtod(control, caddr_t),
		    (caddr_t)mp->msg_control, (unsigned)len);
    }
    mp->msg_controllen = len;
  }
 out:
  if (from)
    m_freem(from);
  if (control)
    m_freem(control);
  
  return (error);
}

LONG SAVEDS RAF3(_shutdown,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			s,	d0,
		 LONG,			how,	d1)
#if 0
{
#endif

  struct socket *so;
  LONG error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, s, &so))
    goto Return;

  error = soshutdown(so, how);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}

LONG SAVEDS RAF6(_setsockopt,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 LONG,			level,		d1,
		 LONG,			name,		d2,
		 caddr_t,		val,		a0,
		 ULONG,			valsize,	d3)
#if 0
{
#endif

  struct socket *so;
  struct mbuf *m = NULL;
  LONG error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, s, &so))
    goto Return;
  if (valsize > MLEN) { /* unsigned catches negative values */
    error = EINVAL;
    goto Return;
  }
  if (val) {
    m = m_get(M_WAIT, MT_SOOPTS);
    if (m == NULL) {
      error = ENOBUFS;
      goto Return;
    }
    bcopy(val, mtod(m, caddr_t), valsize); /* aligned ? */
    m->m_len = (int)valsize;
  }
  error = sosetopt(so, level, name, m);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}


LONG SAVEDS RAF6(_getsockopt,
		 struct SocketBase *,	libPtr,		a6,
		 LONG,			s,		d0,
		 LONG,			level,		d1,
		 LONG,			name,		d2,
		 caddr_t,		val,		a0,
		 ULONG *,		avalsize,	a1)
#if 0
{
#endif

  struct socket *so;
  struct mbuf *m = NULL;
  ULONG valsize, error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, s, &so))
    goto Return;
  
  if (val)
    valsize = *avalsize;
  else
    valsize = 0;
  
  if ((error = sogetopt(so, level, name, &m)) == 0
      && val && valsize && m != NULL) {
    if (valsize > m->m_len)  /* valsize is ULONG */
      valsize = m->m_len;
    bcopy(mtod(m, caddr_t), val, (u_int)valsize); /* aligned ? */
    *avalsize = valsize;
  }
  if (m != NULL)
    (void) m_free(m);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}

LONG SAVEDS RAF4(_getsockname,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			fdes,	d0,
		 caddr_t,		asa,	a0,
		 ULONG *,		alen,	a1)
#if 0
{
#endif

  /*register*/
  struct socket *so;
  struct mbuf *m;
  LONG error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, fdes, &so))
    goto Return;

  m = m_getclr(M_WAIT, MT_SONAME);    
  if (m == NULL) {
    error = ENOBUFS;
    goto Return;
  }
  if (error = (*so->so_proto->pr_usrreq)(so, PRU_SOCKADDR, 0, m, 0))
    goto bad;
  if (*alen > m->m_len)
    *alen = m->m_len;
  aligned_bcopy(mtod(m, caddr_t), (caddr_t)asa, (u_int)*alen);

 bad:
  m_freem(m);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}

LONG SAVEDS RAF4(_getpeername,
		 struct SocketBase *,	libPtr,	a6,
		 LONG,			fdes,	d0,
		 caddr_t,		asa,	a0,
		 ULONG *,		alen,	a1)
#if 0
{
#endif

  /*register*/
  struct socket *so;
  struct mbuf *m;
  LONG error;

  CHECK_TASK();
  ObtainSyscallSemaphore(libPtr);

  if (error = getSock(libPtr, fdes, &so))
    goto Return;

  if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) {
    error = ENOTCONN;
    goto Return;
  }

  m = m_getclr(M_WAIT, MT_SONAME);
  if (m == NULL) {
    error = ENOBUFS;
    goto Return;
  }

  if (error = (*so->so_proto->pr_usrreq)(so, PRU_PEERADDR, 0, m, 0))
    goto bad;
  if (*alen > m->m_len)
    *alen = m->m_len;
  aligned_bcopy(mtod(m, caddr_t), (caddr_t)asa, (u_int)*alen);

 bad:
  m_freem(m);

 Return:
  ReleaseSyscallSemaphore(libPtr);
  API_STD_RETURN(error, 0);
}

static LONG sockArgs(struct mbuf **mp,
		     caddr_t buf,	/* aligned */
		     LONG buflen,
		     LONG type)
{
  register struct mbuf *m;
  LONG error = 0;

  if ((u_int)buflen > MLEN)
    return (EINVAL);

  m = m_get(M_WAIT, type);
  if (m == NULL)
    return (ENOBUFS);
  m->m_len = buflen;

  aligned_bcopy(buf, mtod(m, caddr_t), (u_int)buflen);
  *mp = m;
  if (type == MT_SONAME)
    mtod(m, struct sockaddr *)->sa_len = buflen;

  return (error);
}
