Source to kern/mach_net_tcp.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * "Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.0 (the 'License').  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/* 
 * Mach Operating System
 * Copyright (c) 1989 Carnegie-Mellon University
 * Copyright (c) 1988 Carnegie-Mellon University
 * Copyright (c) 1987 Carnegie-Mellon University
 * All rights reserved.  The CMU software License Agreement specifies
 * the terms and conditions for use and redistribution.
 */
/*
 * File:	mach_net_tcp.c
 * Purpose:
 *	Message interface to TCP. To be used primarily by the network server,
 *	in cooperation with the kernel IPC code.
 */

#import <sys/param.h>
#import <sys/systm.h>
#import <sys/mbuf.h>
#import <sys/socket.h>
#import <sys/socketvar.h>
#import <sys/uio.h>
#import <mach/kern_return.h>
#import <mach/port.h>
#import <kern/parallel.h>
#import <sys/errno.h>
#import <kern/xpr.h>

/*
 * Global variable to hold the current error code (a.k.a. u.u_error).
 */
int	np_error;

kern_return_t
mach_tcp_init(ServPort, tcp_port)
port_t		ServPort;
port_t		*tcp_port;
{
	/*
	 * Nothing yet ...
	 */
	return KERN_SUCCESS;
}


kern_return_t
mach_tcp_socket(ServPort, so)
	port_t		ServPort;
	struct socket	**so;
{
	unix_master();
	np_error = socreate(AF_INET,so,SOCK_STREAM,0);
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_close(ServPort, so)
	port_t		ServPort;
	struct socket	*so;
{
	unix_master();
	np_error = soclose(so);
	XPR(XPR_TCP,("mach_tcp_close: np_error=%d, so=0x%x",np_error,so));
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_bind(ServPort, so, name, namelen)
	port_t		ServPort;
	struct socket	*so;
	caddr_t		name;
	int		namelen;
{
	struct mbuf	*m;

	unix_master();
	m = m_get(M_WAIT, MT_SONAME);
	m->m_next = NULL;
	m->m_off = MMINOFF;
	m->m_len = namelen;
	bcopy(name,mtod(m,caddr_t),namelen);
	np_error = sobind(so,m);
	m_free(m);
	so->so_options |= SO_CANTSIG;
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_listen(ServPort, so, backlog)
	port_t		ServPort;
	struct socket	*so;
	int		backlog;
{
	unix_master();
	np_error = solisten(so,backlog);
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_accept(ServPort, so, name, namelen, newso)
	port_t		ServPort;
	struct socket	*so;
	caddr_t		name;
	int		*namelen;
	struct socket	**newso;
{
	struct mbuf	*nam;
	int		s;

	unix_master();
	s = splnet();
	XPR(XPR_TCP,("mach_tcp_accept entered, so=0x%x, newso=0x%x",so,newso));
	if ((so->so_options & SO_ACCEPTCONN) == 0) {
		np_error = EINVAL;
		XPR(XPR_TCP,("mach_tcp_accept failed, no SO_ACCEPTCONN, so=0x%x",so));
		splx(s);
		unix_release();
		return KERN_FAILURE;
	}
	if ((so->so_state & SS_NBIO) && so->so_qlen == 0) {
		np_error = EWOULDBLOCK;
		XPR(XPR_TCP,("mach_tcp_accept failed, EWOULDBLOCK, so=0x%x",so));
		splx(s);
		unix_release();
		return KERN_FAILURE;
	}
	while (so->so_qlen == 0 && so->so_error == 0) {
		XPR(XPR_TCP,("mach_tcp_accept top of sleep loop, so=0x%x",so));
		if (so->so_state & SS_CANTRCVMORE) {
			XPR(XPR_TCP,("mach_tcp_accept SS_CANTRCVMORE in sleep loop, so=0x%x",so));
			so->so_error = ECONNABORTED;
			break;
		}
		sleep((caddr_t)&so->so_timeo, PZERO+1);
	}
	XPR(XPR_TCP,("mach_tcp_accept out of sleep loop, so=0x%x",so));
	if (so->so_error) {
		np_error = so->so_error;
		so->so_error = 0;
		XPR(XPR_TCP,("mach_tcp_accept so_error=%d, so=0x%x",np_error,so));
		splx(s);
		unix_release();
		return KERN_FAILURE;
	}
	{ struct socket *aso = so->so_q;
	  if (soqremque(aso, 1) == 0)
		panic("accept");
	  *newso = aso;
	}
	nam = m_get(M_WAIT, MT_SONAME);
	(void) soaccept(*newso, nam);
	XPR(XPR_TCP,("mach_tcp_accept out of soaccept, np_error=%d, so=0x%x, *newso=0x%x",np_error,so,*newso));
	if (name) {
		if (*namelen > nam->m_len)
			*namelen = nam->m_len;
		/* SHOULD COPY OUT A CHAIN HERE */
		bcopy(mtod(nam, caddr_t), name, *namelen);
	}
	m_freem(nam);
	splx(s);
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_connect(ServPort, so, name, namelen)
	port_t		ServPort;
	struct socket	*so;
	caddr_t		name;
	int		namelen;
{
	struct mbuf	*nam;
	int		s;

	unix_master();
	if ((so->so_state & SS_NBIO) &&
	    (so->so_state & SS_ISCONNECTING)) {
		np_error = EALREADY;
		XPR(XPR_TCP,("mach_tcp_connect: already connecting, so=0x%x",so));
		unix_release();
		return KERN_FAILURE;
	}
	nam = m_get(M_WAIT, MT_SONAME);
	if (nam == NULL) {
		np_error = ENOBUFS;		
		XPR(XPR_TCP,("mach_tcp_connect: no buffers, so=0x%x",so));
		unix_release();
		return KERN_FAILURE;
	}
	nam->m_len = namelen;
	bcopy(name,mtod(nam,caddr_t),namelen);
	np_error = soconnect(so, nam);
	XPR(XPR_TCP,("mach_tcp_connect: back from soconnect, np_error=%d, so=0x%x",np_error,so));
	if (np_error)
		goto bad;
	if ((so->so_state & SS_NBIO) &&
	    (so->so_state & SS_ISCONNECTING)) {
		np_error = EINPROGRESS;
		XPR(XPR_TCP,("mach_tcp_connect: in progress, so=0x%x",so));
		m_freem(nam);
		unix_release();
		return KERN_FAILURE;
	}
	s = splnet();
/*
	if (setjmp(&u.u_qsave)) {
		if (u.u_error == 0)
			u.u_error = EINTR;
		goto bad2;
	}
*/
	while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0)
		sleep((caddr_t)&so->so_timeo, PZERO+1);
	np_error = so->so_error;
	so->so_error = 0;
/* bad2: */
	splx(s);
	XPR(XPR_TCP,("mach_tcp_connect: woke up, np_error=%d, so=0x%x",np_error,so));
bad:
	so->so_state &= ~SS_ISCONNECTING;
	m_freem(nam);
	unix_release();
	if (np_error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}



kern_return_t
mach_tcp_send(ServPort, so, buf, len, flags)
	port_t		ServPort;
	struct socket	*so;
	caddr_t		buf;
	int		*len;
	int		flags;
	/*
	 * Note: len is an in/out argument, returning the number
	 * of bytes actually sent.
	 */
{
	int		error;
	struct iovec	aiov;
	struct uio	auio;

	unix_master();
	aiov.iov_base = buf;
	aiov.iov_len = *len;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_offset = 0;			/* XXX */
	auio.uio_resid = *len;
	error = sosend(so, 0, &auio, flags, 0);
	*len = *len - auio.uio_resid;
	unix_release();
	if (error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}


kern_return_t
mach_tcp_recv(ServPort, so, buf, len, flags)
	port_t		ServPort;
	struct socket	*so;
	caddr_t		buf;
	int		*len;
	int		flags;
	/*
	 * Note: len is an in/out argument, returning the number
	 * of bytes actually received.
	 */
{
	int		error;
	struct iovec	aiov;
	struct uio	auio;
	struct mbuf	*from, *rights;

	unix_master();
	aiov.iov_base = buf;
	aiov.iov_len = *len;
	auio.uio_iov = &aiov;
	auio.uio_iovcnt = 1;
	auio.uio_segflg = UIO_SYSSPACE;
	auio.uio_offset = 0;			/* XXX */
	auio.uio_resid = *len;
	error = soreceive(so, &from, &auio, flags, &rights);
	*len = *len - auio.uio_resid;
	if (rights)
		m_freem(rights);
	if (from)
		m_freem(from);
	unix_release();
	if (error)
		return KERN_FAILURE;
	else
		return KERN_SUCCESS;
}