📄 uipc_socket.c
字号:
//==========================================================================
//
// 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 + -