* Copyright (c) 1982, 1986, 1988 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* @(#)uipc_socket.c 7.14 (Berkeley) %G%
* 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.
* out-of-band is a kludge
socreate(dom
, aso
, type
, proto
)
register struct protosw
*prp
;
register struct socket
*so
;
prp
= pffindproto(dom
, proto
, type
);
prp
= pffindtype(dom
, type
);
return (EPROTONOSUPPORT
);
if (prp
->pr_type
!= type
)
MALLOC(so
, struct socket
*, sizeof(*so
), M_SOCKET
, M_WAIT
);
bzero((caddr_t
)so
, sizeof(*so
));
(*prp
->pr_usrreq
)(so
, PRU_ATTACH
,
(struct mbuf
*)0, (struct mbuf
*)proto
, (struct mbuf
*)0);
so
->so_state
|= SS_NOFDREF
;
(*so
->so_proto
->pr_usrreq
)(so
, PRU_BIND
,
(struct mbuf
*)0, nam
, (struct mbuf
*)0);
register struct socket
*so
;
(*so
->so_proto
->pr_usrreq
)(so
, PRU_LISTEN
,
(struct mbuf
*)0, (struct mbuf
*)0, (struct mbuf
*)0);
so
->so_options
|= SO_ACCEPTCONN
;
so
->so_qlimit
= min(backlog
, SOMAXCONN
);
register struct socket
*so
;
if (so
->so_pcb
|| (so
->so_state
& SS_NOFDREF
) == 0)
if (!soqremque(so
, 0) && !soqremque(so
, 1))
* Close a socket on last file table reference removal.
* Initiate disconnect if connected.
* Free socket when disconnect complete.
register struct socket
*so
;
int s
= splnet(); /* conservative */
if (so
->so_options
& SO_ACCEPTCONN
) {
(void) soabort(so
->so_q0
);
(void) soabort(so
->so_q
);
if (so
->so_state
& SS_ISCONNECTED
) {
if ((so
->so_state
& SS_ISDISCONNECTING
) == 0) {
error
= sodisconnect(so
);
if (so
->so_options
& SO_LINGER
) {
if ((so
->so_state
& SS_ISDISCONNECTING
) &&
(so
->so_state
& SS_NBIO
))
while (so
->so_state
& SS_ISCONNECTED
)
sleep((caddr_t
)&so
->so_timeo
, PZERO
+1);
(*so
->so_proto
->pr_usrreq
)(so
, PRU_DETACH
,
(struct mbuf
*)0, (struct mbuf
*)0, (struct mbuf
*)0);
if (so
->so_state
& SS_NOFDREF
)
panic("soclose: NOFDREF");
so
->so_state
|= SS_NOFDREF
;
* Must be called at splnet...
(*so
->so_proto
->pr_usrreq
)(so
, PRU_ABORT
,
(struct mbuf
*)0, (struct mbuf
*)0, (struct mbuf
*)0));
register struct socket
*so
;
if ((so
->so_state
& SS_NOFDREF
) == 0)
panic("soaccept: !NOFDREF");
so
->so_state
&= ~SS_NOFDREF
;
error
= (*so
->so_proto
->pr_usrreq
)(so
, PRU_ACCEPT
,
(struct mbuf
*)0, nam
, (struct mbuf
*)0);
register struct socket
*so
;
if (so
->so_options
& SO_ACCEPTCONN
)
* 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.,
if (so
->so_state
& (SS_ISCONNECTED
|SS_ISCONNECTING
) &&
((so
->so_proto
->pr_flags
& PR_CONNREQUIRED
) ||
(error
= sodisconnect(so
))))
error
= (*so
->so_proto
->pr_usrreq
)(so
, PRU_CONNECT
,
(struct mbuf
*)0, nam
, (struct mbuf
*)0);
register struct socket
*so1
;
error
= (*so1
->so_proto
->pr_usrreq
)(so1
, PRU_CONNECT2
,
(struct mbuf
*)0, (struct mbuf
*)so2
, (struct mbuf
*)0);
register struct socket
*so
;
if ((so
->so_state
& SS_ISCONNECTED
) == 0) {
if (so
->so_state
& SS_ISDISCONNECTING
) {
error
= (*so
->so_proto
->pr_usrreq
)(so
, PRU_DISCONNECT
,
(struct mbuf
*)0, (struct mbuf
*)0, (struct mbuf
*)0);
* 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.
sosend(so
, nam
, uio
, flags
, rights
, control
)
register struct socket
*so
;
register struct uio
*uio
;
struct mbuf
*rights
, *control
;
struct mbuf
*top
= 0, **mp
;
int rlen
= 0, error
= 0, s
, dontroute
, first
= 1, mlen
;
int atomic
= sosendallatonce(so
);
if (atomic
&& uio
->uio_resid
> so
->so_snd
.sb_hiwat
)
(flags
& MSG_DONTROUTE
) && (so
->so_options
& SO_DONTROUTE
) == 0 &&
(so
->so_proto
->pr_flags
& PR_ATOMIC
);
#define snderr(errno) { error = errno; splx(s); goto release; }
if (so
->so_state
& SS_CANTSENDMORE
)
if ((so
->so_state
& SS_ISCONNECTED
) == 0) {
if (so
->so_proto
->pr_flags
& PR_CONNREQUIRED
) {
if (!uio
->uio_resid
&& !rights
&& control
) {
snderr((*so
->so_proto
->pr_usrreq
)(so
,
(flags
& MSG_OOB
) ? PRU_SENDOOB
: PRU_SEND
,
top
, (caddr_t
)0, rights
, control
));
} else if (so
->so_state
& SS_ISCONFIRMING
)
space
= sbspace(&so
->so_snd
);
(atomic
&& space
< uio
->uio_resid
+ rlen
) ||
(uio
->uio_resid
>= MCLBYTES
&& space
< MCLBYTES
&&
so
->so_snd
.sb_cc
>= MCLBYTES
&&
(so
->so_state
& SS_NBIO
) == 0)) {
if (so
->so_state
& SS_NBIO
) {
MGETHDR(m
, M_WAIT
, MT_DATA
);
m
->m_pkthdr
.rcvif
= (struct ifnet
*)0;
MGET(m
, M_WAIT
, MT_DATA
);
if (uio
->uio_resid
>= MINCLSIZE
&& space
>= MCLBYTES
) {
if ((m
->m_flags
& M_EXT
) == 0)
len
= min(MCLBYTES
, uio
->uio_resid
);
if (len
< mlen
- max_hdr
)
len
= min(MCLBYTES
- max_hdr
, uio
->uio_resid
);
len
= min(min(mlen
, uio
->uio_resid
), space
);
* For datagram protocols, leave room
* for protocol headers in first mbuf.
if (atomic
&& top
== 0 && len
< mlen
)
error
= uiomove(mtod(m
, caddr_t
), len
, uio
);
top
->m_pkthdr
.len
+= len
;
if (uio
->uio_resid
<= 0) {
if ((flags
& MSG_EOR
) && top
)
} while (space
> 0 && atomic
);
so
->so_options
|= SO_DONTROUTE
;
error
= (*so
->so_proto
->pr_usrreq
)(so
,
(flags
& MSG_OOB
) ? PRU_SENDOOB
: PRU_SEND
,
top
, (caddr_t
)nam
, rights
, control
);
so
->so_options
&= ~SO_DONTROUTE
;
} while (uio
->uio_resid
&& space
> 0);
} while (uio
->uio_resid
);
psignal(u
.u_procp
, SIGPIPE
);
* Implement receive operations on a socket.
* We depend on the way that records are added to the sockbuf
* by sbappend*. In particular, each record (mbufs linked through m_next)
* must begin with an address if the protocol so specifies,
* followed by an optional mbuf containing access rights if supported
* by the protocol, and then zero or more mbufs of data.
* In order to avoid blocking network interrupts for the entire time here,
* we splx() while doing the actual copy to user space.
* Although the sockbuf is locked, new data may still be appended,
* and thus we must maintain consistency of the sockbuf during that time.
soreceive(so
, aname
, uio
, flagsp
, rightsp
, controlp
)
register struct socket
*so
;
register struct uio
*uio
;
struct mbuf
**rightsp
, **controlp
;
register int flags
, len
, error
= 0, s
, offset
;
struct protosw
*pr
= so
->so_proto
;
flags
= *flagsp
&~ MSG_EOR
;
m
= m_get(M_WAIT
, MT_DATA
);
error
= (*pr
->pr_usrreq
)(so
, PRU_RCVOOB
,
m
, (struct mbuf
*)(flags
& MSG_PEEK
), (struct mbuf
*)0);
error
= uiomove(mtod(m
, caddr_t
), (int)len
, uio
);
} while (uio
->uio_resid
&& error
== 0 && m
);
if (so
->so_state
& SS_ISCONFIRMING
&& uio
->uio_resid
)
(*pr
->pr_usrreq
)(so
, PRU_RCVD
, (struct mbuf
*)0,
(struct mbuf
*)0, (struct mbuf
*)0);
if (so
->so_state
& SS_CANTRCVMORE
)
if ((so
->so_state
& SS_ISCONNECTED
) == 0 &&
(so
->so_proto
->pr_flags
& PR_CONNREQUIRED
)) {
if (so
->so_state
& SS_NBIO
) {
nextrecord
= m
->m_nextpkt
;
if (pr
->pr_flags
& PR_ADDR
) {
if (m
->m_type
!= MT_SONAME
)
*aname
= m_copy(m
, 0, m
->m_len
);
so
->so_rcv
.sb_mb
= m
->m_next
;
MFREE(m
, so
->so_rcv
.sb_mb
);
if (m
&& m
->m_type
== MT_RIGHTS
) {
if ((pr
->pr_flags
& PR_RIGHTS
) == 0)
*rightsp
= m_copy(m
, 0, m
->m_len
);
so
->so_rcv
.sb_mb
= m
->m_next
;
MFREE(m
, so
->so_rcv
.sb_mb
);
if (m
&& m
->m_type
== MT_CONTROL
) {
*controlp
= m_copy(m
, 0, m
->m_len
);
so
->so_rcv
.sb_mb
= m
->m_next
;
MFREE(m
, so
->so_rcv
.sb_mb
);
m
->m_nextpkt
= nextrecord
;
while (m
&& uio
->uio_resid
> 0 && error
== 0) {
if (m
->m_type
== MT_OOBDATA
)
else if (m
->m_type
!= MT_DATA
&& m
->m_type
!= MT_HEADER
)
so
->so_state
&= ~SS_RCVATMARK
;
if (so
->so_oobmark
&& len
> so
->so_oobmark
- offset
)
len
= so
->so_oobmark
- offset
;
if (len
> m
->m_len
- moff
)
error
= uiomove(mtod(m
, caddr_t
) + moff
, (int)len
, uio
);
if (len
== m
->m_len
- moff
) {
nextrecord
= m
->m_nextpkt
;
MFREE(m
, so
->so_rcv
.sb_mb
);
m
->m_nextpkt
= nextrecord
;
if ((flags
& MSG_PEEK
) == 0) {
if (so
->so_oobmark
== 0) {
so
->so_state
|= SS_RCVATMARK
;
if (m
&& (flags
& MSG_EOR
)) {
if ((flags
& MSG_PEEK
) == 0)
if ((flags
& MSG_PEEK
) == 0) {
so
->so_rcv
.sb_mb
= nextrecord
;
else if (pr
->pr_flags
& PR_ATOMIC
) {
(void) sbdroprecord(&so
->so_rcv
);
if (pr
->pr_flags
& PR_WANTRCVD
&& so
->so_pcb
)
(*pr
->pr_usrreq
)(so
, PRU_RCVD
, (struct mbuf
*)0,
(struct mbuf
*)flags
, (struct mbuf
*)0,
if (error
== 0 && rightsp
&& *rightsp
&&
pr
->pr_domain
->dom_externalize
)
error
= (*pr
->pr_domain
->dom_externalize
)(*rightsp
);
register struct socket
*so
;
register struct protosw
*pr
= so
->so_proto
;
return ((*pr
->pr_usrreq
)(so
, PRU_SHUTDOWN
,
(struct mbuf
*)0, (struct mbuf
*)0, (struct mbuf
*)0));
register struct socket
*so
;
register struct sockbuf
*sb
= &so
->so_rcv
;
register struct protosw
*pr
= so
->so_proto
;
bzero((caddr_t
)sb
, sizeof (*sb
));
if (pr
->pr_flags
& PR_RIGHTS
&& pr
->pr_domain
->dom_dispose
)
(*pr
->pr_domain
->dom_dispose
)(asb
.sb_mb
);
sosetopt(so
, level
, optname
, m0
)
register struct socket
*so
;
register struct mbuf
*m
= m0
;
if (level
!= SOL_SOCKET
) {
if (so
->so_proto
&& so
->so_proto
->pr_ctloutput
)
return ((*so
->so_proto
->pr_ctloutput
)
(PRCO_SETOPT
, so
, level
, optname
, &m0
));
if (m
== NULL
|| m
->m_len
!= sizeof (struct linger
)) {
so
->so_linger
= mtod(m
, struct linger
*)->l_linger
;
if (m
== NULL
|| m
->m_len
< sizeof (int)) {
so
->so_options
|= optname
;
so
->so_options
&= ~optname
;
if (m
== NULL
|| m
->m_len
< sizeof (int)) {
if (sbreserve(optname
== SO_SNDBUF
?
&so
->so_snd
: &so
->so_rcv
,
(u_long
) *mtod(m
, int *)) == 0) {
so
->so_snd
.sb_lowat
= *mtod(m
, int *);
so
->so_rcv
.sb_lowat
= *mtod(m
, int *);
so
->so_snd
.sb_timeo
= *mtod(m
, int *);
so
->so_rcv
.sb_timeo
= *mtod(m
, int *);
sogetopt(so
, level
, optname
, mp
)
register struct socket
*so
;
if (level
!= SOL_SOCKET
) {
if (so
->so_proto
&& so
->so_proto
->pr_ctloutput
) {
return ((*so
->so_proto
->pr_ctloutput
)
(PRCO_GETOPT
, so
, level
, optname
, mp
));
m
= m_get(M_WAIT
, MT_SOOPTS
);
m
->m_len
= sizeof (struct linger
);
mtod(m
, struct linger
*)->l_onoff
=
so
->so_options
& SO_LINGER
;
mtod(m
, struct linger
*)->l_linger
= so
->so_linger
;
*mtod(m
, int *) = so
->so_options
& optname
;
*mtod(m
, int *) = so
->so_type
;
*mtod(m
, int *) = so
->so_error
;
*mtod(m
, int *) = so
->so_snd
.sb_hiwat
;
*mtod(m
, int *) = so
->so_rcv
.sb_hiwat
;
*mtod(m
, int *) = so
->so_snd
.sb_lowat
;
*mtod(m
, int *) = so
->so_rcv
.sb_lowat
;
*mtod(m
, int *) = so
->so_snd
.sb_timeo
;
*mtod(m
, int *) = so
->so_rcv
.sb_timeo
;
register struct socket
*so
;
gsignal(-so
->so_pgid
, SIGURG
);
else if (so
->so_pgid
> 0 && (p
= pfind(so
->so_pgid
)) != 0)
selwakeup(so
->so_rcv
.sb_sel
, so
->so_rcv
.sb_flags
& SB_COLL
);
so
->so_rcv
.sb_flags
&= ~SB_COLL
;