reserve a port range for unprivileged servers
[unix-history] / usr / src / sys / netinet / in_pcb.c
index df3962d..a901bb2 100644 (file)
@@ -1,18 +1,26 @@
-/*     in_pcb.c        4.40    83/01/22        */
-
-#include "../h/param.h"
-#include "../h/systm.h"
-#include "../h/dir.h"
-#include "../h/user.h"
-#include "../h/mbuf.h"
-#include "../h/socket.h"
-#include "../h/socketvar.h"
-#include "../netinet/in.h"
-#include "../netinet/in_systm.h"
+/*
+ * Copyright (c) 1982 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ *
+ *     @(#)in_pcb.c    6.11 (Berkeley) %G%
+ */
+
+#include "param.h"
+#include "systm.h"
+#include "dir.h"
+#include "user.h"
+#include "mbuf.h"
+#include "socket.h"
+#include "socketvar.h"
+#include "ioctl.h"
+#include "in.h"
+#include "in_systm.h"
 #include "../net/if.h"
 #include "../net/route.h"
 #include "../net/if.h"
 #include "../net/route.h"
-#include "../netinet/in_pcb.h"
-#include "../h/protosw.h"
+#include "in_pcb.h"
+#include "in_var.h"
+#include "protosw.h"
 
 struct in_addr zeroin_addr;
 
 
 struct in_addr zeroin_addr;
 
@@ -43,7 +51,7 @@ in_pcbbind(inp, nam)
        register struct sockaddr_in *sin;
        u_short lport = 0;
 
        register struct sockaddr_in *sin;
        u_short lport = 0;
 
-       if (ifnet == 0)
+       if (in_ifaddr == 0)
                return (EADDRNOTAVAIL);
        if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
                return (EINVAL);
                return (EADDRNOTAVAIL);
        if (inp->inp_lport || inp->inp_laddr.s_addr != INADDR_ANY)
                return (EINVAL);
@@ -56,20 +64,22 @@ in_pcbbind(inp, nam)
                int tport = sin->sin_port;
 
                sin->sin_port = 0;              /* yech... */
                int tport = sin->sin_port;
 
                sin->sin_port = 0;              /* yech... */
-               if (if_ifwithaddr((struct sockaddr *)sin) == 0)
+               if (ifa_ifwithaddr((struct sockaddr *)sin) == 0)
                        return (EADDRNOTAVAIL);
                sin->sin_port = tport;
        }
        lport = sin->sin_port;
        if (lport) {
                        return (EADDRNOTAVAIL);
                sin->sin_port = tport;
        }
        lport = sin->sin_port;
        if (lport) {
-               u_short aport = htons(lport);
+               u_short aport = ntohs(lport);
                int wild = 0;
 
                /* GROSS */
                if (aport < IPPORT_RESERVED && u.u_uid != 0)
                        return (EACCES);
                int wild = 0;
 
                /* GROSS */
                if (aport < IPPORT_RESERVED && u.u_uid != 0)
                        return (EACCES);
-               if ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
-                   (so->so_options & SO_ACCEPTCONN) == 0)
+               /* even GROSSER, but this is the Internet */
+               if ((so->so_options & SO_REUSEADDR) == 0 &&
+                   ((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
+                    (so->so_options & SO_ACCEPTCONN) == 0))
                        wild = INPLOOKUP_WILDCARD;
                if (in_pcblookup(head,
                    zeroin_addr, 0, sin->sin_addr, lport, wild))
                        wild = INPLOOKUP_WILDCARD;
                if (in_pcblookup(head,
                    zeroin_addr, 0, sin->sin_addr, lport, wild))
@@ -79,7 +89,8 @@ in_pcbbind(inp, nam)
 noname:
        if (lport == 0)
                do {
 noname:
        if (lport == 0)
                do {
-                       if (head->inp_lport++ < IPPORT_RESERVED)
+                       if (head->inp_lport++ < IPPORT_RESERVED ||
+                           head->inp_lport > IPPORT_USERRESERVED)
                                head->inp_lport = IPPORT_RESERVED;
                        lport = htons(head->inp_lport);
                } while (in_pcblookup(head,
                                head->inp_lport = IPPORT_RESERVED;
                        lport = htons(head->inp_lport);
                } while (in_pcblookup(head,
@@ -98,7 +109,7 @@ in_pcbconnect(inp, nam)
        struct inpcb *inp;
        struct mbuf *nam;
 {
        struct inpcb *inp;
        struct mbuf *nam;
 {
-       struct ifnet *ifp;
+       struct in_ifaddr *ia;
        struct sockaddr_in *ifaddr;
        register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
 
        struct sockaddr_in *ifaddr;
        register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
 
@@ -106,22 +117,64 @@ in_pcbconnect(inp, nam)
                return (EINVAL);
        if (sin->sin_family != AF_INET)
                return (EAFNOSUPPORT);
                return (EINVAL);
        if (sin->sin_family != AF_INET)
                return (EAFNOSUPPORT);
-       if (sin->sin_addr.s_addr == INADDR_ANY || sin->sin_port == 0)
+       if (sin->sin_port == 0)
                return (EADDRNOTAVAIL);
                return (EADDRNOTAVAIL);
+       if (in_ifaddr) {
+               /*
+                * If the destination address is INADDR_ANY,
+                * use the primary local address.
+                * If the supplied address is INADDR_BROADCAST,
+                * and the primary interface supports broadcast,
+                * choose the broadcast address for that interface.
+                */
+#define        satosin(sa)     ((struct sockaddr_in *)(sa))
+               if (sin->sin_addr.s_addr == INADDR_ANY)
+                   sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
+               else if (sin->sin_addr.s_addr == (u_long)INADDR_BROADCAST &&
+                 (in_ifaddr->ia_ifp->if_flags & IFF_BROADCAST))
+                   sin->sin_addr = satosin(&in_ifaddr->ia_broadaddr)->sin_addr;
+       }
        if (inp->inp_laddr.s_addr == INADDR_ANY) {
        if (inp->inp_laddr.s_addr == INADDR_ANY) {
-               ifp = if_ifonnetof(in_netof(sin->sin_addr));
-               if (ifp == 0) {
-                       /*
-                        * We should select the interface based on
-                        * the route to be used, but for udp this would
-                        * result in two calls to rtalloc for each packet
-                        * sent; hardly worthwhile...
+               ia = (struct in_ifaddr *)ifa_ifwithnet((struct sockaddr *)sin);
+               if (ia == (struct in_ifaddr *)0) {
+                       register struct route *ro;
+                       struct ifnet *ifp;
+
+                       /* 
+                        * If route is known or can be allocated now,
+                        * our src addr is taken from the i/f, else punt.
                         */
                         */
-                       ifp = if_ifwithaf(AF_INET);
-                       if (ifp == 0)
+                       ro = &inp->inp_route;
+                       if (ro->ro_rt &&
+                           satosin(&ro->ro_dst)->sin_addr.s_addr !=
+                           sin->sin_addr.s_addr) {
+                               RTFREE(ro->ro_rt);
+                               ro->ro_rt = (struct rtentry *)0;
+                       }
+                       if ((ro->ro_rt == (struct rtentry *)0) ||
+                           (ifp = ro->ro_rt->rt_ifp) == (struct ifnet *)0) {
+                               /* No route yet, so try to acquire one */
+                               ro->ro_dst.sa_family = AF_INET;
+                               ((struct sockaddr_in *) &ro->ro_dst)->sin_addr =
+                                       sin->sin_addr;
+                               rtalloc(ro);
+                               if (ro->ro_rt == 0)
+                                       ifp = (struct ifnet *)0;
+                               else
+                                       ifp = ro->ro_rt->rt_ifp;
+                       }
+                       if (ifp) {
+                               for (ia = in_ifaddr; ia; ia = ia->ia_next)
+                                       if (ia->ia_ifp == ifp)
+                                               break;
+                       } else
+                               ia = (struct in_ifaddr *)0;
+                       if (ia == 0)
+                               ia = in_ifaddr;
+                       if (ia == 0)
                                return (EADDRNOTAVAIL);
                }
                                return (EADDRNOTAVAIL);
                }
-               ifaddr = (struct sockaddr_in *)&ifp->if_addr;
+               ifaddr = (struct sockaddr_in *)&ia->ia_addr;
        }
        if (in_pcblookup(inp->inp_head,
            sin->sin_addr,
        }
        if (in_pcblookup(inp->inp_head,
            sin->sin_addr,
@@ -130,8 +183,11 @@ in_pcbconnect(inp, nam)
            inp->inp_lport,
            0))
                return (EADDRINUSE);
            inp->inp_lport,
            0))
                return (EADDRINUSE);
-       if (inp->inp_laddr.s_addr == INADDR_ANY)
+       if (inp->inp_laddr.s_addr == INADDR_ANY) {
+               if (inp->inp_lport == 0)
+                       in_pcbbind(inp, (struct mbuf *)0);
                inp->inp_laddr = ifaddr->sin_addr;
                inp->inp_laddr = ifaddr->sin_addr;
+       }
        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);
@@ -154,6 +210,8 @@ in_pcbdetach(inp)
 
        so->so_pcb = 0;
        sofree(so);
 
        so->so_pcb = 0;
        sofree(so);
+       if (inp->inp_options)
+               m_free(inp->inp_options);
        if (inp->inp_route.ro_rt)
                rtfree(inp->inp_route.ro_rt);
        remque(inp);
        if (inp->inp_route.ro_rt)
                rtfree(inp->inp_route.ro_rt);
        remque(inp);
@@ -174,36 +232,89 @@ in_setsockaddr(inp, nam)
        sin->sin_addr = inp->inp_laddr;
 }
 
        sin->sin_addr = inp->inp_laddr;
 }
 
+in_setpeeraddr(inp, nam)
+       register struct inpcb *inp;
+       struct mbuf *nam;
+{
+       register struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
+       
+       nam->m_len = sizeof (*sin);
+       sin = mtod(nam, struct sockaddr_in *);
+       bzero((caddr_t)sin, sizeof (*sin));
+       sin->sin_family = AF_INET;
+       sin->sin_port = inp->inp_fport;
+       sin->sin_addr = inp->inp_faddr;
+}
+
 /*
 /*
- * Pass an error to all internet connections
- * associated with address sin.  Call the
- * protocol specific routine to clean up the
- * mess afterwards.
+ * Pass some notification to all connections of a protocol
+ * associated with address dst.  Call the protocol specific
+ * routine (if any) to handle each connection.
  */
  */
-in_pcbnotify(head, dst, errno, abort)
+in_pcbnotify(head, dst, errno, notify)
        struct inpcb *head;
        register struct in_addr *dst;
        struct inpcb *head;
        register struct in_addr *dst;
-       int errno, (*abort)();
+       int errno, (*notify)();
 {
        register struct inpcb *inp, *oinp;
        int s = splimp();
 
        for (inp = head->inp_next; inp != head;) {
 {
        register struct inpcb *inp, *oinp;
        int s = splimp();
 
        for (inp = head->inp_next; inp != head;) {
-               if (inp->inp_faddr.s_addr != dst->s_addr) {
-       next:
+               if (inp->inp_faddr.s_addr != dst->s_addr ||
+                   inp->inp_socket == 0) {
                        inp = inp->inp_next;
                        continue;
                }
                        inp = inp->inp_next;
                        continue;
                }
-               if (inp->inp_socket == 0)
-                       goto next;
-               inp->inp_socket->so_error = errno;
+               if (errno) 
+                       inp->inp_socket->so_error = errno;
                oinp = inp;
                inp = inp->inp_next;
                oinp = inp;
                inp = inp->inp_next;
-               (*abort)(oinp);
+               if (notify)
+                       (*notify)(oinp);
        }
        splx(s);
 }
 
        }
        splx(s);
 }
 
+/*
+ * Check for alternatives when higher level complains
+ * about service problems.  For now, invalidate cached
+ * routing information.  If the route was created dynamically
+ * (by a redirect), time to try a default gateway again.
+ */
+in_losing(inp)
+       struct inpcb *inp;
+{
+       register struct rtentry *rt;
+
+       if ((rt = inp->inp_route.ro_rt)) {
+               if (rt->rt_flags & RTF_DYNAMIC)
+                       rtrequest(SIOCDELRT, rt);
+               rtfree(rt);
+               inp->inp_route.ro_rt = 0;
+               /*
+                * A new route can be allocated
+                * the next time output is attempted.
+                */
+       }
+}
+
+/*
+ * After a routing change, flush old routing
+ * and allocate a (hopefully) better one.
+ */
+in_rtchange(inp)
+       register struct inpcb *inp;
+{
+       if (inp->inp_route.ro_rt) {
+               rtfree(inp->inp_route.ro_rt);
+               inp->inp_route.ro_rt = 0;
+               /*
+                * A new route can be allocated the next time
+                * output is attempted.
+                */
+       }
+}
+
 struct inpcb *
 in_pcblookup(head, faddr, fport, laddr, lport, flags)
        struct inpcb *head;
 struct inpcb *
 in_pcblookup(head, faddr, fport, laddr, lport, flags)
        struct inpcb *head;