⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 uipc_socket.c

📁 eCos/RedBoot for勤研ARM AnywhereII(4510) 含全部源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
//==========================================================================
//
//      src/sys/kern/uipc_socket.c
//
//==========================================================================
//####BSDCOPYRIGHTBEGIN####
//
// -------------------------------------------
//
// Portions of this software may have been derived from OpenBSD, 
// FreeBSD or other sources, and are covered by the appropriate
// copyright disclaimers included herein.
//
// Portions created by Red Hat are
// Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
//
// -------------------------------------------
//
//####BSDCOPYRIGHTEND####
//==========================================================================

/*
 * Copyright (c) 1982, 1986, 1988, 1990, 1993
 *	The Regents of the University of California.  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 the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 THE REGENTS 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.
 *
 *	@(#)uipc_socket.c	8.3 (Berkeley) 4/15/94
 * $FreeBSD: src/sys/kern/uipc_socket.c,v 1.68.2.16 2001/06/14 20:46:06 ume Exp $
 */

#include <sys/param.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/domain.h>
#include <sys/malloc.h>
#include <sys/protosw.h>
#include <sys/socket.h>
#include <sys/socketvar.h>

#include <cyg/io/file.h>

#ifdef INET
static int	 do_setopt_accept_filter(struct socket *so, struct sockopt *sopt);
#endif /* INET */

#if 0  // FILT
static void 	filt_sordetach(struct knote *kn);
static int 	filt_soread(struct knote *kn, long hint);
static void 	filt_sowdetach(struct knote *kn);
static int	filt_sowrite(struct knote *kn, long hint);
static int	filt_solisten(struct knote *kn, long hint);

static struct filterops solisten_filtops = 
	{ 1, NULL, filt_sordetach, filt_solisten };
static struct filterops soread_filtops =
	{ 1, NULL, filt_sordetach, filt_soread };
static struct filterops sowrite_filtops = 
	{ 1, NULL, filt_sowdetach, filt_sowrite };
#endif

struct	vm_zone *socket_zone;
so_gen_t	so_gencnt;	/* generation count for sockets */

static int somaxconn = SOMAXCONN;

/*
 * Socket operation routines.
 * These routines are called by the routines in
 * sys_socket.c or from a system process, and
 * implement the semantics of socket operations by
 * switching out to the protocol specific routines.
 */

/*
 * Get a socket structure from our zone, and initialize it.
 * 'waitok' has been implemented for eCos, with [currently] some
 * rather fixed strategy - it will retry some number of times (10)
 * after at most 2 minutes.  This seems sufficient for sockets which
 * are tied up in the TCP close process.
 */
struct socket *
soalloc(int waitok)
{
    struct socket *so = NULL;
    int maxtries = waitok ? 10 : 1;

    while (maxtries-- > 0) {
	so = zalloci(socket_zone);
	if (so) {
            /* XXX race condition for reentrant kernel */
            bzero(so, sizeof *so);
            so->so_gencnt = ++so_gencnt;
            so->so_zone = socket_zone;
            TAILQ_INIT(&so->so_aiojobq);
            return so;
	}
        if (waitok) {
            diag_printf("DEBUG: Out of sockets - waiting\n");
            tsleep(socket_zone, PVM|PCATCH, "soalloc", 120*100);
            diag_printf("DEBUG: ... retry sockets\n");
        }
    }
    return so;
}

int
socreate(dom, aso, type, proto, p)
	int dom;
	struct socket **aso;
	register int type;
	int proto;
	struct proc *p;
{
	register struct protosw *prp;
	register struct socket *so;
	register int error;

	if (proto)
		prp = pffindproto(dom, proto, type);
	else
		prp = pffindtype(dom, type);

	if (prp == 0 || prp->pr_usrreqs->pru_attach == 0)
		return (EPROTONOSUPPORT);

	if (prp->pr_type != type)
		return (EPROTOTYPE);
	so = soalloc(p != 0);
	if (so == 0) {
		return (ENOBUFS);
        }

	TAILQ_INIT(&so->so_incomp);
	TAILQ_INIT(&so->so_comp);
	so->so_type = type;
	so->so_proto = prp;
	error = (*prp->pr_usrreqs->pru_attach)(so, proto, p);
	if (error) {
		so->so_state |= SS_NOFDREF;
		sofree(so);
		return (error);
	}
	*aso = so;
	return (0);
}

int
sobind(so, nam, p)
	struct socket *so;
	struct sockaddr *nam;
	struct proc *p;
{
	int s = splnet();
	int error;

	error = (*so->so_proto->pr_usrreqs->pru_bind)(so, nam, p);
	splx(s);
	return (error);
}

void
sodealloc(so)
	struct socket *so;
{

	so->so_gencnt = ++so_gencnt;
#ifdef INET
	if (so->so_accf != NULL) {
		if (so->so_accf->so_accept_filter != NULL && 
			so->so_accf->so_accept_filter->accf_destroy != NULL) {
			so->so_accf->so_accept_filter->accf_destroy(so);
		}
		if (so->so_accf->so_accept_filter_str != NULL)
			FREE(so->so_accf->so_accept_filter_str, M_ACCF);
		FREE(so->so_accf, M_ACCF);
	}
#endif /* INET */
	zfreei(so->so_zone, so);
        wakeup(so->so_zone);
}

int
solisten(so, backlog, p)
	register struct socket *so;
	int backlog;
	struct proc *p;
{
	int s, error;

	s = splnet();
	error = (*so->so_proto->pr_usrreqs->pru_listen)(so, p);
	if (error) {
		splx(s);
		return (error);
	}
	if (TAILQ_EMPTY(&so->so_comp))
		so->so_options |= SO_ACCEPTCONN;
	if (backlog < 0 || backlog > somaxconn)
		backlog = somaxconn;
	so->so_qlimit = backlog;
	splx(s);
	return (0);
}

void
sofree(so)
	register struct socket *so;
{
	struct socket *head = so->so_head;

	if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
		return;
	if (head != NULL) {
		if (so->so_state & SS_INCOMP) {
			TAILQ_REMOVE(&head->so_incomp, so, so_list);
			head->so_incqlen--;
		} else if (so->so_state & SS_COMP) {
			/*
			 * We must not decommission a socket that's
			 * on the accept(2) queue.  If we do, then
			 * accept(2) may hang after select(2) indicated
			 * that the listening socket was ready.
			 */
			return;
		} else {
			panic("sofree: not queued");
		}
		head->so_qlen--;
		so->so_state &= ~SS_INCOMP;
		so->so_head = NULL;
	}
	sbrelease(&so->so_snd, so);
	sorflush(so);
	sodealloc(so);
}

/*
 * Close a socket on last file table reference removal.
 * Initiate disconnect if connected.
 * Free socket when disconnect complete.
 */
int
soclose(so)
	register struct socket *so;
{
	int s = splnet();		/* conservative */
	int error = 0;

	if (so->so_options & SO_ACCEPTCONN) {
		struct socket *sp, *sonext;

		sp = TAILQ_FIRST(&so->so_incomp);
		for (; sp != NULL; sp = sonext) {
			sonext = TAILQ_NEXT(sp, so_list);
			(void) soabort(sp);
		}
		for (sp = TAILQ_FIRST(&so->so_comp); sp != NULL; sp = sonext) {
			sonext = TAILQ_NEXT(sp, so_list);
			/* Dequeue from so_comp since sofree() won't do it */
			TAILQ_REMOVE(&so->so_comp, sp, so_list);
			so->so_qlen--;
			sp->so_state &= ~SS_COMP;
			sp->so_head = NULL;
			(void) soabort(sp);
		}
	}
	if (so->so_pcb == 0)
		goto discard;
	if (so->so_state & SS_ISCONNECTED) {
		if ((so->so_state & SS_ISDISCONNECTING) == 0) {
			error = sodisconnect(so);
			if (error)
				goto drop;
		}
		if (so->so_options & SO_LINGER) {
			if ((so->so_state & SS_ISDISCONNECTING) &&
			    (so->so_state & SS_NBIO))
				goto drop;
			while (so->so_state & SS_ISCONNECTED) {
				error = tsleep((caddr_t)&so->so_timeo,
				    PSOCK | PCATCH, "soclos", so->so_linger * hz);
				if (error)
					break;
			}
		}
	}
drop:
	if (so->so_pcb) {
		int error2 = (*so->so_proto->pr_usrreqs->pru_detach)(so);
		if (error == 0)
			error = error2;
	}
discard:
	if (so->so_state & SS_NOFDREF)
		panic("soclose: NOFDREF");
	so->so_state |= SS_NOFDREF;
	sofree(so);
	splx(s);
	return (error);
}

/*
 * Must be called at splnet...
 */
int
soabort(so)
	struct socket *so;
{
	int error;

	error = (*so->so_proto->pr_usrreqs->pru_abort)(so);
	if (error) {
		sofree(so);
		return error;
	}
	return (0);
}

int
soaccept(so, nam)
	register struct socket *so;
	struct sockaddr **nam;
{
	int s = splnet();
	int error;

	if ((so->so_state & SS_NOFDREF) == 0)
		panic("soaccept: !NOFDREF");
	so->so_state &= ~SS_NOFDREF;
	error = (*so->so_proto->pr_usrreqs->pru_accept)(so, nam);
	splx(s);
	return (error);
}

int
soconnect(so, nam, p)
	register struct socket *so;
	struct sockaddr *nam;
	struct proc *p;
{
	int s;
	int error;

	if (so->so_options & SO_ACCEPTCONN)
		return (EOPNOTSUPP);
	s = splnet();
	/*
	 * If protocol is connection-based, can only connect once.
	 * Otherwise, if connected, try to disconnect first.
	 * This allows user to disconnect by connecting to, e.g.,
	 * a null address.
	 */
	if (so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING) &&
	    ((so->so_proto->pr_flags & PR_CONNREQUIRED) ||
	    (error = sodisconnect(so))))
		error = EISCONN;
	else
		error = (*so->so_proto->pr_usrreqs->pru_connect)(so, nam, p);
	splx(s);
	return (error);
}

int
soconnect2(so1, so2)
	register struct socket *so1;
	struct socket *so2;
{
	int s = splnet();
	int error;

	error = (*so1->so_proto->pr_usrreqs->pru_connect2)(so1, so2);
	splx(s);
	return (error);
}

int
sodisconnect(so)
	register struct socket *so;
{
	int s = splnet();
	int error;

	if ((so->so_state & SS_ISCONNECTED) == 0) {
		error = ENOTCONN;
		goto bad;
	}
	if (so->so_state & SS_ISDISCONNECTING) {
		error = EALREADY;
		goto bad;
	}
	error = (*so->so_proto->pr_usrreqs->pru_disconnect)(so);
bad:
	splx(s);
	return (error);
}

#define	SBLOCKWAIT(f)	(((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
/*
 * Send on a socket.
 * If send must go all at once and message is larger than
 * send buffering, then hard error.
 * Lock against other senders.
 * If must go all at once and not enough room now, then
 * inform user that this would block and do nothing.
 * Otherwise, if nonblocking, send as much as possible.
 * The data to be sent is described by "uio" if nonzero,
 * otherwise by the mbuf chain "top" (which must be null
 * if uio is not).  Data provided in mbuf chain must be small
 * enough to send all at once.
 *
 * Returns nonzero on error, timeout or signal; callers
 * must check for short counts if EINTR/ERESTART are returned.
 * Data and control buffers are freed on return.
 */
int
sosend(so, addr, uio, top, control, flags, p)
	register struct socket *so;
	struct sockaddr *addr;
	struct uio *uio;
	struct mbuf *top;
	struct mbuf *control;
	int flags;
	struct proc *p;
{
	struct mbuf **mp;
	register struct mbuf *m;
	register long space, len, resid;
	int clen = 0, error, s, dontroute, mlen;
	int atomic = sosendallatonce(so) || top;

	if (uio)
		resid = uio->uio_resid;
	else
		resid = top->m_pkthdr.len;
	/*
	 * In theory resid should be unsigned.
	 * However, space must be signed, as it might be less than 0
	 * if we over-committed, and we must use a signed comparison
	 * of space and resid.  On the other hand, a negative resid
	 * causes us to loop sending 0-length segments to the protocol.
	 *
	 * Also check to make sure that MSG_EOR isn't used on SOCK_STREAM
	 * type sockets since that's an error.
	 */
	if (resid < 0 || (so->so_type == SOCK_STREAM && (flags & MSG_EOR))) {
		error = EINVAL;
		goto out;
	}

	dontroute =
	    (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
	    (so->so_proto->pr_flags & PR_ATOMIC);
	if (control)
		clen = control->m_len;
#define	snderr(errno)	{ error = errno; splx(s); goto release; }

restart:
	error = sblock(&so->so_snd, SBLOCKWAIT(flags));
	if (error)
		goto out;
	do {
		s = splnet();
		if (so->so_state & SS_CANTSENDMORE)
			snderr(EPIPE);
		if (so->so_error) {
			error = so->so_error;
			so->so_error = 0;
			splx(s);
			goto release;
		}
		if ((so->so_state & SS_ISCONNECTED) == 0) {
			/*
			 * `sendto' and `sendmsg' is allowed on a connection-
			 * based socket if it supports implied connect.
			 * Return ENOTCONN if not connected and no address is
			 * supplied.
			 */
			if ((so->so_proto->pr_flags & PR_CONNREQUIRED) &&
			    (so->so_proto->pr_flags & PR_IMPLOPCL) == 0) {
				if ((so->so_state & SS_ISCONFIRMING) == 0 &&
				    !(resid == 0 && clen != 0))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -