bug fixes from elz and utcsrgv!thomson
[unix-history] / usr / src / sys / netinet / in_pcb.c
index 81e2b5d..ad13011 100644 (file)
@@ -1,4 +1,4 @@
-/*     in_pcb.c        4.17    82/02/27        */
+/*     in_pcb.c        4.24    82/03/30        */
 
 #include "../h/param.h"
 #include "../h/systm.h"
 
 #include "../h/param.h"
 #include "../h/systm.h"
@@ -10,7 +10,9 @@
 #include "../net/in.h"
 #include "../net/in_systm.h"
 #include "../net/if.h"
 #include "../net/in.h"
 #include "../net/in_systm.h"
 #include "../net/if.h"
+#include "../net/route.h"
 #include "../net/in_pcb.h"
 #include "../net/in_pcb.h"
+#include "../h/protosw.h"
 
 /*
  * Routines to manage internet protocol control blocks.
 
 /*
  * Routines to manage internet protocol control blocks.
@@ -67,20 +69,29 @@ COUNT(IN_PCBATTACH);
        if (sin) {
                if (sin->sin_family != AF_INET)
                        return (EAFNOSUPPORT);
        if (sin) {
                if (sin->sin_family != AF_INET)
                        return (EAFNOSUPPORT);
-               if (sin->sin_addr.s_addr &&
-                   if_ifwithaddr(sin->sin_addr.s_addr) == 0)
-                       return (EADDRNOTAVAIL);
+               if (sin->sin_addr.s_addr) {
+                       int tport = sin->sin_port;
+
+                       sin->sin_port = 0;              /* yech... */
+                       if (if_ifwithaddr((struct sockaddr *)sin) == 0)
+                               return (EADDRNOTAVAIL);
+                       sin->sin_port = tport;
+               }
                lport = sin->sin_port;
                if (lport) {
                        u_short aport = lport;
                lport = sin->sin_port;
                if (lport) {
                        u_short aport = lport;
+                       int wild = 0;
 #if vax
                        aport = htons(aport);
 #endif
                        /* GROSS */
                        if (aport < IPPORT_RESERVED && u.u_uid != 0)
                                return (EPERM);
 #if vax
                        aport = htons(aport);
 #endif
                        /* GROSS */
                        if (aport < IPPORT_RESERVED && u.u_uid != 0)
                                return (EPERM);
+                       if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+                           (so->so_options & SO_ACCEPTCONN) == 0)
+                               wild = INPLOOKUP_WILDCARD;
                        if (in_pcblookup(head,
                        if (in_pcblookup(head,
-                           zeroin_addr, 0, sin->sin_addr, lport, 0))
+                           zeroin_addr, 0, sin->sin_addr, lport, wild))
                                return (EADDRINUSE);
                }
        }
                                return (EADDRINUSE);
                }
        }
@@ -115,11 +126,18 @@ bad:
        return (ENOBUFS);
 }
 
        return (ENOBUFS);
 }
 
+/*
+ * Connect from a socket to a specified address.
+ * Both address and port must be specified in argument sin.
+ * If don't have a local address for this socket yet,
+ * then pick one.
+ */
 in_pcbconnect(inp, sin)
        struct inpcb *inp;
        struct sockaddr_in *sin;
 {
        struct ifnet *ifp;
 in_pcbconnect(inp, sin)
        struct inpcb *inp;
        struct sockaddr_in *sin;
 {
        struct ifnet *ifp;
+       struct sockaddr_in *ifaddr;
 
 COUNT(IN_PCBCONNECT);
        if (sin->sin_family != AF_INET)
 
 COUNT(IN_PCBCONNECT);
        if (sin->sin_family != AF_INET)
@@ -127,14 +145,28 @@ COUNT(IN_PCBCONNECT);
        if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
                return (EADDRNOTAVAIL);
        if (inp->inp_laddr.s_addr == 0) {
        if (sin->sin_addr.s_addr == 0 || sin->sin_port == 0)
                return (EADDRNOTAVAIL);
        if (inp->inp_laddr.s_addr == 0) {
-               ifp = if_ifonnetof(sin->sin_addr.s_addr);
-               if (ifp == 0)
-                       ifp = ifnet;
-               inp->inp_laddr = ifp->if_addr;
+               ifp = if_ifonnetof(sin->sin_addr.s_net);
+               if (ifp == 0) {
+                       ifp = if_ifwithaf(AF_INET);
+                       if (ifp == 0)
+                               return (EADDRNOTAVAIL);         /* XXX */
+               }
+               ifaddr = (struct sockaddr_in *)&ifp->if_addr;
        }
        if (in_pcblookup(inp->inp_head,
        }
        if (in_pcblookup(inp->inp_head,
-           sin->sin_addr, sin->sin_port, inp->inp_laddr, inp->inp_lport, 0))
+           sin->sin_addr,
+           sin->sin_port,
+           inp->inp_laddr.s_addr ? inp->inp_laddr : ifaddr->sin_addr,
+           inp->inp_lport,
+           0))
                return (EADDRINUSE);
                return (EADDRINUSE);
+       if (inp->inp_laddr.s_addr == 0) {
+               struct sockaddr_in *sin2 =
+                   (struct sockaddr_in *)&inp->inp_socket->so_addr;
+
+               inp->inp_laddr = ifaddr->sin_addr;
+               sin2->sin_addr = inp->inp_laddr;
+       }
        inp->inp_faddr = sin->sin_addr;
        inp->inp_fport = sin->sin_port;
        return (0);
        inp->inp_faddr = sin->sin_addr;
        inp->inp_fport = sin->sin_port;
        return (0);
@@ -157,6 +189,7 @@ in_pcbdisconnect(inp)
 
 COUNT(IN_PCBDISCONNECT);
        inp->inp_faddr.s_addr = 0;
 
 COUNT(IN_PCBDISCONNECT);
        inp->inp_faddr.s_addr = 0;
+       inp->inp_fport = 0;
        if (inp->inp_socket->so_state & SS_USERGONE)
                in_pcbdetach(inp);
 }
        if (inp->inp_socket->so_state & SS_USERGONE)
                in_pcbdetach(inp);
 }
@@ -168,24 +201,21 @@ in_pcbdetach(inp)
 
        so->so_pcb = 0;
        sofree(so);
 
        so->so_pcb = 0;
        sofree(so);
+       if (inp->inp_route.ro_rt)
+               rtfree(inp->inp_route.ro_rt);
        remque(inp);
        (void) m_free(dtom(inp));
 }
 
 /*
        remque(inp);
        (void) m_free(dtom(inp));
 }
 
 /*
- * Look for a control block to accept a segment.
- * First choice is an exact address match.
- * Second choice is a match with either the foreign or the local
- * address specified.
- *
  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
  */
 struct inpcb *
  * SHOULD ALLOW MATCH ON MULTI-HOMING ONLY
  */
 struct inpcb *
-in_pcblookup(head, faddr, fport, laddr, lport, enter)
+in_pcblookup(head, faddr, fport, laddr, lport, flags)
        struct inpcb *head;
        struct in_addr faddr, laddr;
        u_short fport, lport;
        struct inpcb *head;
        struct in_addr faddr, laddr;
        u_short fport, lport;
-       int enter;
+       int flags;
 {
        register struct inpcb *inp, *match = 0;
        int matchwild = 3, wildcard;
 {
        register struct inpcb *inp, *match = 0;
        int matchwild = 3, wildcard;
@@ -195,20 +225,25 @@ in_pcblookup(head, faddr, fport, laddr, lport, enter)
                        continue;
                wildcard = 0;
                if (inp->inp_laddr.s_addr != 0) {
                        continue;
                wildcard = 0;
                if (inp->inp_laddr.s_addr != 0) {
-                       if (inp->inp_laddr.s_addr != laddr.s_addr)
+                       if (laddr.s_addr == 0)
+                               wildcard++;
+                       else if (inp->inp_laddr.s_addr != laddr.s_addr)
                                continue;
                } else {
                        if (laddr.s_addr != 0)
                                wildcard++;
                }
                if (inp->inp_faddr.s_addr != 0) {
                                continue;
                } else {
                        if (laddr.s_addr != 0)
                                wildcard++;
                }
                if (inp->inp_faddr.s_addr != 0) {
-                       if (inp->inp_faddr.s_addr != faddr.s_addr)
+                       if (faddr.s_addr == 0)
+                               wildcard++;
+                       else if (inp->inp_faddr.s_addr != faddr.s_addr ||
+                           inp->inp_fport != fport)
                                continue;
                } else {
                        if (faddr.s_addr != 0)
                                wildcard++;
                }
                                continue;
                } else {
                        if (faddr.s_addr != 0)
                                wildcard++;
                }
-               if (enter == 0 && wildcard)
+               if (wildcard && (flags & INPLOOKUP_WILDCARD) == 0)
                        continue;
                if (wildcard < matchwild) {
                        match = inp;
                        continue;
                if (wildcard < matchwild) {
                        match = inp;
@@ -217,9 +252,5 @@ in_pcblookup(head, faddr, fport, laddr, lport, enter)
                                break;
                }
        }
                                break;
                }
        }
-       if (match && enter) {
-               match->inp_laddr = laddr;
-               in_setsockaddr(match);
-       }
        return (match);
 }
        return (match);
 }