diff options
Diffstat (limited to 'sys/kern/uipc_socket.c')
-rw-r--r-- | sys/kern/uipc_socket.c | 245 |
1 files changed, 148 insertions, 97 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 5abd6fe..f429434 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)uipc_socket.c 8.3 (Berkeley) 4/15/94 - * $Id: uipc_socket.c,v 1.41 1998/07/06 19:27:14 fenner Exp $ + * $Id: uipc_socket.c,v 1.42 1998/07/18 18:48:45 fenner Exp $ */ #include <sys/param.h> @@ -898,31 +898,69 @@ sorflush(so) sbrelease(&asb); } +/* + * Perhaps this routine, and sooptcopyout(), below, ought to come in + * an additional variant to handle the case where the option value needs + * to be some kind of integer, but not a specific size. + * In addition to their use here, these functions are also called by the + * protocol-level pr_ctloutput() routines. + */ int -sosetopt(so, level, optname, m0, p) - register struct socket *so; - int level, optname; - struct mbuf *m0; - struct proc *p; +sooptcopyin(sopt, buf, len, minlen) + struct sockopt *sopt; + void *buf; + size_t len; + size_t minlen; { - int error = 0; - register struct mbuf *m = m0; + size_t valsize; + + /* + * If the user gives us more than we wanted, we ignore it, + * but if we don't get the minimum length the caller + * wants, we return EINVAL. On success, sopt->sopt_valsize + * is set to however much we actually retrieved. + */ + if ((valsize = sopt->sopt_valsize) < minlen) + return EINVAL; + if (valsize > len) + sopt->sopt_valsize = valsize = len; + + if (sopt->sopt_p != 0) + return (copyin(sopt->sopt_val, buf, valsize)); - if (level != SOL_SOCKET) { + bcopy(sopt->sopt_val, buf, valsize); + return 0; +} + +int +sosetopt(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int error, optval; + struct linger l; + struct timeval tv; + short val; + + error = 0; + if (sopt->sopt_level != SOL_SOCKET) { if (so->so_proto && so->so_proto->pr_ctloutput) return ((*so->so_proto->pr_ctloutput) - (PRCO_SETOPT, so, level, optname, &m0, p)); + (so, sopt)); error = ENOPROTOOPT; } else { - switch (optname) { - + switch (sopt->sopt_name) { case SO_LINGER: - if (m == NULL || m->m_len != sizeof (struct linger)) { - error = EINVAL; + error = sooptcopyin(sopt, &l, sizeof l, sizeof l); + if (error) goto bad; - } - so->so_linger = mtod(m, struct linger *)->l_linger; - /* fall thru... */ + + so->so_linger = l.l_linger; + if (l.l_onoff) + so->so_options |= SO_LINGER; + else + so->so_options &= ~SO_LINGER; + break; case SO_DEBUG: case SO_KEEPALIVE: @@ -933,45 +971,40 @@ sosetopt(so, level, optname, m0, p) case SO_REUSEPORT: case SO_OOBINLINE: case SO_TIMESTAMP: - if (m == NULL || m->m_len < sizeof (int)) { - error = EINVAL; + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) goto bad; - } - if (*mtod(m, int *)) - so->so_options |= optname; + if (optval) + so->so_options |= sopt->sopt_name; else - so->so_options &= ~optname; + so->so_options &= ~sopt->sopt_name; break; case SO_SNDBUF: case SO_RCVBUF: case SO_SNDLOWAT: case SO_RCVLOWAT: - { - int optval; - - if (m == NULL || m->m_len < sizeof (int)) { - error = EINVAL; + error = sooptcopyin(sopt, &optval, sizeof optval, + sizeof optval); + if (error) goto bad; - } /* * Values < 1 make no sense for any of these * options, so disallow them. */ - optval = *mtod(m, int *); if (optval < 1) { error = EINVAL; goto bad; } - switch (optname) { - + switch (sopt->sopt_name) { case SO_SNDBUF: case SO_RCVBUF: - if (sbreserve(optname == SO_SNDBUF ? - &so->so_snd : &so->so_rcv, - (u_long) optval) == 0) { + if (sbreserve(sopt->sopt_name == SO_SNDBUF ? + &so->so_snd : &so->so_rcv, + (u_long) optval) == 0) { error = ENOBUFS; goto bad; } @@ -993,27 +1026,21 @@ sosetopt(so, level, optname, m0, p) break; } break; - } case SO_SNDTIMEO: case SO_RCVTIMEO: - { - struct timeval *tv; - short val; - - if (m == NULL || m->m_len < sizeof (*tv)) { - error = EINVAL; + error = sooptcopyin(sopt, &tv, sizeof tv, + sizeof tv); + if (error) goto bad; - } - tv = mtod(m, struct timeval *); - if (tv->tv_sec > SHRT_MAX / hz - hz) { + + if (tv.tv_sec > SHRT_MAX / hz - hz) { error = EDOM; goto bad; } - val = tv->tv_sec * hz + tv->tv_usec / tick; - - switch (optname) { + val = tv.tv_sec * hz + tv.tv_usec / tick; + switch (sopt->sopt_name) { case SO_SNDTIMEO: so->so_snd.sb_timeo = val; break; @@ -1022,7 +1049,6 @@ sosetopt(so, level, optname, m0, p) break; } break; - } default: error = ENOPROTOOPT; @@ -1030,42 +1056,69 @@ sosetopt(so, level, optname, m0, p) } if (error == 0 && so->so_proto && so->so_proto->pr_ctloutput) { (void) ((*so->so_proto->pr_ctloutput) - (PRCO_SETOPT, so, level, optname, &m0, p)); - m = NULL; /* freed by protocol */ + (so, sopt)); } } bad: - if (m) - (void) m_free(m); return (error); } +/* Helper routine for getsockopt */ int -sogetopt(so, level, optname, mp, p) - register struct socket *so; - int level, optname; - struct mbuf **mp; - struct proc *p; +sooptcopyout(sopt, buf, len) + struct sockopt *sopt; + void *buf; + size_t len; { - register struct mbuf *m; + int error; + size_t valsize; + + error = 0; + + /* + * Documented get behavior is that we always return a value, + * possibly truncated to fit in the user's buffer. + * We leave the correct length in sopt->sopt_valsize, + * to be copied out in getsockopt(). Note that this + * interface is not idempotent; the entire answer must + * generated ahead of time. + */ + valsize = len; + if (sopt->sopt_valsize < valsize) { + valsize = sopt->sopt_valsize; + sopt->sopt_valsize = len; + } + if (sopt->sopt_val != 0) { + if (sopt->sopt_p != 0) + error = copyout(buf, sopt->sopt_val, valsize); + else + bcopy(buf, sopt->sopt_val, valsize); + } + return error; +} + +int +sogetopt(so, sopt) + struct socket *so; + struct sockopt *sopt; +{ + int error, optval; + struct linger l; + struct timeval tv; - if (level != SOL_SOCKET) { + error = 0; + if (sopt->sopt_level != SOL_SOCKET) { if (so->so_proto && so->so_proto->pr_ctloutput) { return ((*so->so_proto->pr_ctloutput) - (PRCO_GETOPT, so, level, optname, mp, p)); + (so, sopt)); } else return (ENOPROTOOPT); } else { - m = m_get(M_WAIT, MT_SOOPTS); - m->m_len = sizeof (int); - - switch (optname) { - + switch (sopt->sopt_name) { case SO_LINGER: - 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; + l.l_onoff = so->so_options & SO_LINGER; + l.l_linger = so->so_linger; + error = sooptcopyout(sopt, &l, sizeof l); break; case SO_USELOOPBACK: @@ -1077,53 +1130,51 @@ sogetopt(so, level, optname, mp, p) case SO_BROADCAST: case SO_OOBINLINE: case SO_TIMESTAMP: - *mtod(m, int *) = so->so_options & optname; + optval = so->so_options & sopt->sopt_name; +integer: + error = sooptcopyout(sopt, &optval, sizeof optval); break; case SO_TYPE: - *mtod(m, int *) = so->so_type; - break; + optval = so->so_type; + goto integer; case SO_ERROR: - *mtod(m, int *) = so->so_error; + optval = so->so_error; so->so_error = 0; - break; + goto integer; case SO_SNDBUF: - *mtod(m, int *) = so->so_snd.sb_hiwat; - break; + optval = so->so_snd.sb_hiwat; + goto integer; case SO_RCVBUF: - *mtod(m, int *) = so->so_rcv.sb_hiwat; - break; + optval = so->so_rcv.sb_hiwat; + goto integer; case SO_SNDLOWAT: - *mtod(m, int *) = so->so_snd.sb_lowat; - break; + optval = so->so_snd.sb_lowat; + goto integer; case SO_RCVLOWAT: - *mtod(m, int *) = so->so_rcv.sb_lowat; - break; + optval = so->so_rcv.sb_lowat; + goto integer; case SO_SNDTIMEO: case SO_RCVTIMEO: - { - int val = (optname == SO_SNDTIMEO ? - so->so_snd.sb_timeo : so->so_rcv.sb_timeo); - - m->m_len = sizeof(struct timeval); - mtod(m, struct timeval *)->tv_sec = val / hz; - mtod(m, struct timeval *)->tv_usec = - (val % hz) * tick; - break; - } + optval = (sopt->sopt_name == SO_SNDTIMEO ? + so->so_snd.sb_timeo : so->so_rcv.sb_timeo); + + tv.tv_sec = optval / hz; + tv.tv_usec = (optval % hz) * tick; + error = sooptcopyout(sopt, &tv, sizeof tv); + break; default: - (void)m_free(m); - return (ENOPROTOOPT); + error = ENOPROTOOPT; + break; } - *mp = m; - return (0); + return (error); } } |