tech-net archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

in_pcbbind() changes



Hey,

Attached is a diff implementing changes to in_pcbbind() similar to the
ones recently done in in6_pcbbind():

  - Add in_pcbbind_{addr,port}() and split functionality
  - Add in_pcbsetport() (almost an exact copy of in6_pcbsetport() that
    was open-coded inside in_pcbbind())
  - All three "static" for now, will be exposed if we decide to
  - Fix a bug where "sin" was passed to kauth(9) without being set to
    anything

Please review. :)

Thanks,

-e.
Index: in_pcb.c
===================================================================
RCS file: /cvsroot/src/sys/netinet/in_pcb.c,v
retrieving revision 1.131
diff -u -p -r1.131 in_pcb.c
--- in_pcb.c    14 Apr 2009 21:25:20 -0000      1.131
+++ in_pcb.c    21 Apr 2009 16:12:17 -0000
@@ -111,6 +111,7 @@ __KERNEL_RCSID(0, "$NetBSD: in_pcb.c,v 1
 #include <sys/proc.h>
 #include <sys/kauth.h>
 #include <sys/uidinfo.h>
+#include <sys/domain.h>
 
 #include <net/if.h>
 #include <net/route.h>
@@ -222,35 +223,92 @@ in_pcballoc(struct socket *so, void *v)
        return (0);
 }
 
-int
-in_pcbbind(void *v, struct mbuf *nam, struct lwp *l)
+static int
+in_pcbsetport(struct in_addr *laddr, struct inpcb *inp,
+    struct sockaddr_in *sin, kauth_cred_t cred)
 {
-       struct in_ifaddr *ia = NULL;
-       struct inpcb *inp = v;
-       struct socket *so = inp->inp_socket;
        struct inpcbtable *table = inp->inp_table;
-       struct sockaddr_in *sin = NULL; /* XXXGCC */
+       struct socket *so = inp->inp_socket;
+       int        cnt;
+       u_int16_t  mymin, mymax;
+       u_int16_t *lastport;
        u_int16_t lport = 0;
-       int wild = 0, reuseport = (so->so_options & SO_REUSEPORT);
-       kauth_cred_t cred = l->l_cred;
 
-       if (inp->inp_af != AF_INET)
-               return (EINVAL);
+       if (inp->inp_flags & INP_LOWPORT) {
+#ifndef IPNOPRIVPORTS
+               if (kauth_authorize_network(cred,
+                   KAUTH_NETWORK_BIND,
+                   KAUTH_REQ_NETWORK_BIND_PRIVPORT, so,
+                   sin, NULL))
+                       return (EACCES);
+#endif
+               mymin = lowportmin;
+               mymax = lowportmax;
+               lastport = &table->inpt_lastlow;
+       } else {
+               mymin = anonportmin;
+               mymax = anonportmax;
+               lastport = &table->inpt_lastport;
+       }
+       if (mymin > mymax) {    /* sanity check */
+               u_int16_t swp;
+
+               swp = mymin;
+               mymin = mymax;
+               mymax = swp;
+       }
+
+       lport = *lastport - 1;
+       for (cnt = mymax - mymin + 1; cnt; cnt--, lport--) {
+               if (lport < mymin || lport > mymax)
+                       lport = mymax;
+               if (!in_pcblookup_port(table, inp->inp_laddr,
+                   htons(lport), 1))
+                       goto found;
+       }
+
+       return (EAGAIN);
+
+ found:
+       inp->inp_flags |= INP_ANONPORT;
+       *lastport = lport;
+       lport = htons(lport);
+       inp->inp_lport = lport;
+       in_pcbstate(inp, INP_BOUND);
 
-       if (TAILQ_FIRST(&in_ifaddrhead) == 0)
-               return (EADDRNOTAVAIL);
-       if (inp->inp_lport || !in_nullhost(inp->inp_laddr))
-               return (EINVAL);
-       if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
-               wild = 1;
-       if (nam == 0)
-               goto noname;
-       sin = mtod(nam, struct sockaddr_in *);
-       if (nam->m_len != sizeof (*sin))
-               return (EINVAL);
+       return (0);
+}
+
+static int
+in_pcbbind_addr(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred)
+{
        if (sin->sin_family != AF_INET)
                return (EAFNOSUPPORT);
-       lport = sin->sin_port;
+
+       if (!in_nullhost(sin->sin_addr)) {
+               struct in_ifaddr *ia = NULL;
+
+               INADDR_TO_IA(sin->sin_addr, ia);
+               /* check for broadcast addresses */
+               if (ia == NULL)
+                       ia = ifatoia(ifa_ifwithaddr(sintosa(sin)));
+               if (ia == NULL)
+                       return (EADDRNOTAVAIL);
+       }
+
+       inp->inp_laddr = sin->sin_addr;
+
+       return (0);
+}
+
+static int
+in_pcbbind_port(struct inpcb *inp, struct sockaddr_in *sin, kauth_cred_t cred)
+{
+       struct inpcbtable *table = inp->inp_table;
+       struct socket *so = inp->inp_socket;
+       int reuseport = (so->so_options & SO_REUSEPORT);
+       int wild = 0;
+
        if (IN_MULTICAST(sin->sin_addr.s_addr)) {
                /*
                 * Treat SO_REUSEADDR as SO_REUSEPORT for multicast;
@@ -261,23 +319,27 @@ in_pcbbind(void *v, struct mbuf *nam, st
                 */
                if (so->so_options & SO_REUSEADDR)
                        reuseport = SO_REUSEADDR|SO_REUSEPORT;
-       } else if (!in_nullhost(sin->sin_addr)) {
-               INADDR_TO_IA(sin->sin_addr, ia);
-               /* check for broadcast addresses */
-               if (ia == NULL)
-                       ia = ifatoia(ifa_ifwithaddr(sintosa(sin)));
-               if (ia == NULL)
-                       return (EADDRNOTAVAIL);
-       }
-       if (lport) {
+       } 
+
+       if (sin->sin_port == 0) {
+               int error;
+
+               error = in_pcbsetport(&inp->inp_laddr, inp, sin, cred);
+               if (error)
+                       return (error);
+       } else {
                struct inpcb *t;
 #ifdef INET6
                struct in6pcb *t6;
                struct in6_addr mapped;
 #endif
+
+               if ((so->so_options & (SO_REUSEADDR|SO_REUSEPORT)) == 0)
+                       wild = 1;
+
 #ifndef IPNOPRIVPORTS
                /* GROSS */
-               if (ntohs(lport) < IPPORT_RESERVED &&
+               if (ntohs(sin->sin_port) < IPPORT_RESERVED &&
                    kauth_authorize_network(cred,
                    KAUTH_NETWORK_BIND,
                    KAUTH_REQ_NETWORK_BIND_PRIVPORT, so, sin,
@@ -289,12 +351,12 @@ in_pcbbind(void *v, struct mbuf *nam, st
                mapped.s6_addr16[5] = 0xffff;
                memcpy(&mapped.s6_addr32[3], &sin->sin_addr,
                    sizeof(mapped.s6_addr32[3]));
-               t6 = in6_pcblookup_port(table, &mapped, lport, wild);
+               t6 = in6_pcblookup_port(table, &mapped, sin->sin_port, wild);
                if (t6 && (reuseport & t6->in6p_socket->so_options) == 0)
                        return (EADDRINUSE);
 #endif
                if (so->so_uidinfo->ui_uid && 
!IN_MULTICAST(sin->sin_addr.s_addr)) {
-                       t = in_pcblookup_port(table, sin->sin_addr, lport, 1);
+                       t = in_pcblookup_port(table, sin->sin_addr, 
sin->sin_port, 1);
                /*
                 * XXX: investigate ramifications of loosening this
                 *      restriction so that as long as both ports have
@@ -308,63 +370,58 @@ in_pcbbind(void *v, struct mbuf *nam, st
                                return (EADDRINUSE);
                        }
                }
-               t = in_pcblookup_port(table, sin->sin_addr, lport, wild);
+               t = in_pcblookup_port(table, sin->sin_addr, sin->sin_port, 
wild);
                if (t && (reuseport & t->inp_socket->so_options) == 0)
                        return (EADDRINUSE);
-       }
-       inp->inp_laddr = sin->sin_addr;
-
-noname:
-       if (lport == 0) {
-               int        cnt;
-               u_int16_t  mymin, mymax;
-               u_int16_t *lastport;
 
-               if (inp->inp_flags & INP_LOWPORT) {
-#ifndef IPNOPRIVPORTS
-                       if (kauth_authorize_network(cred,
-                           KAUTH_NETWORK_BIND,
-                           KAUTH_REQ_NETWORK_BIND_PRIVPORT, so,
-                           sin, NULL))
-                               return (EACCES);
-#endif
-                       mymin = lowportmin;
-                       mymax = lowportmax;
-                       lastport = &table->inpt_lastlow;
-               } else {
-                       mymin = anonportmin;
-                       mymax = anonportmax;
-                       lastport = &table->inpt_lastport;
-               }
-               if (mymin > mymax) {    /* sanity check */
-                       u_int16_t swp;
-
-                       swp = mymin;
-                       mymin = mymax;
-                       mymax = swp;
-               }
-
-               lport = *lastport - 1;
-               for (cnt = mymax - mymin + 1; cnt; cnt--, lport--) {
-                       if (lport < mymin || lport > mymax)
-                               lport = mymax;
-                       if (!in_pcblookup_port(table, inp->inp_laddr,
-                           htons(lport), 1))
-                               goto found;
-               }
-               if (!in_nullhost(inp->inp_laddr))
-                       inp->inp_laddr.s_addr = INADDR_ANY;
-               return (EAGAIN);
-       found:
-               inp->inp_flags |= INP_ANONPORT;
-               *lastport = lport;
-               lport = htons(lport);
+               inp->inp_lport = sin->sin_port;
+               in_pcbstate(inp, INP_BOUND);
        }
-       inp->inp_lport = lport;
+
        LIST_REMOVE(&inp->inp_head, inph_lhash);
        LIST_INSERT_HEAD(INPCBHASH_PORT(table, inp->inp_lport), &inp->inp_head,
            inph_lhash);
-       in_pcbstate(inp, INP_BOUND);
+
+       return (0);
+}
+
+int
+in_pcbbind(void *v, struct mbuf *nam, struct lwp *l)
+{
+       struct inpcb *inp = v;
+       struct sockaddr_in *sin = NULL; /* XXXGCC */
+       int error;
+
+       if (inp->inp_af != AF_INET)
+               return (EINVAL);
+
+       if (TAILQ_FIRST(&in_ifaddrhead) == 0)
+               return (EADDRNOTAVAIL);
+       if (inp->inp_lport || !in_nullhost(inp->inp_laddr))
+               return (EINVAL);
+
+       if (nam != NULL) {
+               sin = mtod(nam, struct sockaddr_in *);
+               if (nam->m_len != sizeof (*sin))
+                       return (EINVAL);
+       } else {
+               sin = (struct sockaddr_in *)
+                   __UNCONST(inp->inp_socket->so_proto->pr_domain->dom_sa_any);
+       }
+
+       /* Bind address. */
+       error = in_pcbbind_addr(inp, sin, l->l_cred);
+       if (error)
+               return (error);
+
+       /* Bind port. */
+       error = in_pcbbind_port(inp, sin, l->l_cred);
+       if (error) {
+               inp->inp_laddr.s_addr = INADDR_ANY;
+
+               return (error);
+       }
+
        return (0);
 }
 


Home | Main Index | Thread Index | Old Index