]> Git Repo - qemu.git/blobdiff - slirp/socket.c
linux-user: add getrandom() syscall
[qemu.git] / slirp / socket.c
index 1673e3afce2febd5a0bd9faa31b238a792b0beca..2b5453e0205d886e837ec12ee5ac996e57b8039a 100644 (file)
@@ -5,6 +5,7 @@
  * terms and conditions of the copyright.
  */
 
+#include "qemu/osdep.h"
 #include "qemu-common.h"
 #include <slirp.h>
 #include "ip_icmp.h"
 static void sofcantrcvmore(struct socket *so);
 static void sofcantsendmore(struct socket *so);
 
-struct socket *
-solookup(struct socket *head, struct in_addr laddr, u_int lport,
-         struct in_addr faddr, u_int fport)
+struct socket *solookup(struct socket **last, struct socket *head,
+        struct sockaddr_storage *lhost, struct sockaddr_storage *fhost)
 {
-       struct socket *so;
-
-       for (so = head->so_next; so != head; so = so->so_next) {
-               if (so->so_lport == lport &&
-                   so->so_laddr.s_addr == laddr.s_addr &&
-                   so->so_faddr.s_addr == faddr.s_addr &&
-                   so->so_fport == fport)
-                  break;
-       }
-
-       if (so == head)
-          return (struct socket *)NULL;
-       return so;
+    struct socket *so = *last;
+
+    /* Optimisation */
+    if (so != head && sockaddr_equal(&(so->lhost.ss), lhost)
+            && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
+        return so;
+    }
+
+    for (so = head->so_next; so != head; so = so->so_next) {
+        if (sockaddr_equal(&(so->lhost.ss), lhost)
+                && (!fhost || sockaddr_equal(&so->fhost.ss, fhost))) {
+            *last = so;
+            return so;
+        }
+    }
 
+    return (struct socket *)NULL;
 }
 
 /*
@@ -437,8 +440,9 @@ sowrite(struct socket *so)
 void
 sorecvfrom(struct socket *so)
 {
-       struct sockaddr_in addr;
-       socklen_t addrlen = sizeof(struct sockaddr_in);
+       struct sockaddr_storage addr;
+       struct sockaddr_storage saddr, daddr;
+       socklen_t addrlen = sizeof(struct sockaddr_storage);
 
        DEBUG_CALL("sorecvfrom");
        DEBUG_ARG("so = %p", so);
@@ -525,9 +529,21 @@ sorecvfrom(struct socket *so)
 
            /*
             * If this packet was destined for CTL_ADDR,
-            * make it look like that's where it came from, done by udp_output
+            * make it look like that's where it came from
             */
-           udp_output(so, m, &addr);
+           saddr = addr;
+           sotranslate_in(so, &saddr);
+           daddr = so->lhost.ss;
+
+           switch (so->so_ffamily) {
+           case AF_INET:
+               udp_output(so, m, (struct sockaddr_in *) &saddr,
+                          (struct sockaddr_in *) &daddr,
+                          so->so_iptos);
+               break;
+           default:
+               break;
+           }
          } /* rx error */
        } /* if ping packet */
 }
@@ -538,33 +554,20 @@ sorecvfrom(struct socket *so)
 int
 sosendto(struct socket *so, struct mbuf *m)
 {
-       Slirp *slirp = so->slirp;
        int ret;
-       struct sockaddr_in addr;
+       struct sockaddr_storage addr;
 
        DEBUG_CALL("sosendto");
        DEBUG_ARG("so = %p", so);
        DEBUG_ARG("m = %p", m);
 
-        addr.sin_family = AF_INET;
-       if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
-           slirp->vnetwork_addr.s_addr) {
-         /* It's an alias */
-         if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
-           if (get_dns_addr(&addr.sin_addr) < 0)
-             addr.sin_addr = loopback_addr;
-         } else {
-           addr.sin_addr = loopback_addr;
-         }
-       } else
-         addr.sin_addr = so->so_faddr;
-       addr.sin_port = so->so_fport;
-
-       DEBUG_MISC((dfd, " sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n", ntohs(addr.sin_port), inet_ntoa(addr.sin_addr)));
+       addr = so->fhost.ss;
+       DEBUG_CALL(" sendto()ing)");
+       sotranslate_out(so, &addr);
 
        /* Don't care what port we get */
        ret = sendto(so->s, m->m_data, m->m_len, 0,
-                    (struct sockaddr *)&addr, sizeof (struct sockaddr));
+                    (struct sockaddr *)&addr, sizeof(addr));
        if (ret < 0)
                return -1;
 
@@ -619,6 +622,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
 
        so->so_state &= SS_PERSISTENT_MASK;
        so->so_state |= (SS_FACCEPTCONN | flags);
+       so->so_lfamily = AF_INET;
        so->so_lport = lport; /* Kept in network format */
        so->so_laddr.s_addr = laddr; /* Ditto */
 
@@ -645,6 +649,7 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr,
        qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int));
 
        getsockname(s,(struct sockaddr *)&addr,&addrlen);
+       so->so_ffamily = AF_INET;
        so->so_fport = addr.sin_port;
        if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
           so->so_faddr = slirp->vhost_addr;
@@ -718,3 +723,81 @@ sofwdrain(struct socket *so)
        else
                sofcantsendmore(so);
 }
+
+/*
+ * Translate addr in host addr when it is a virtual address
+ */
+void sotranslate_out(struct socket *so, struct sockaddr_storage *addr)
+{
+    Slirp *slirp = so->slirp;
+    struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+    switch (addr->ss_family) {
+    case AF_INET:
+        if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+                slirp->vnetwork_addr.s_addr) {
+            /* It's an alias */
+            if (so->so_faddr.s_addr == slirp->vnameserver_addr.s_addr) {
+                if (get_dns_addr(&sin->sin_addr) < 0) {
+                    sin->sin_addr = loopback_addr;
+                }
+            } else {
+                sin->sin_addr = loopback_addr;
+            }
+        }
+
+        DEBUG_MISC((dfd, " addr.sin_port=%d, "
+            "addr.sin_addr.s_addr=%.16s\n",
+            ntohs(sin->sin_port), inet_ntoa(sin->sin_addr)));
+        break;
+
+    default:
+        break;
+    }
+}
+
+void sotranslate_in(struct socket *so, struct sockaddr_storage *addr)
+{
+    Slirp *slirp = so->slirp;
+    struct sockaddr_in *sin = (struct sockaddr_in *)addr;
+
+    switch (addr->ss_family) {
+    case AF_INET:
+        if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) ==
+            slirp->vnetwork_addr.s_addr) {
+            uint32_t inv_mask = ~slirp->vnetwork_mask.s_addr;
+
+            if ((so->so_faddr.s_addr & inv_mask) == inv_mask) {
+                sin->sin_addr = slirp->vhost_addr;
+            } else if (sin->sin_addr.s_addr == loopback_addr.s_addr ||
+                       so->so_faddr.s_addr != slirp->vhost_addr.s_addr) {
+                sin->sin_addr = so->so_faddr;
+            }
+        }
+        break;
+
+    default:
+        break;
+    }
+}
+
+/*
+ * Translate connections from localhost to the real hostname
+ */
+void sotranslate_accept(struct socket *so)
+{
+    Slirp *slirp = so->slirp;
+
+    switch (so->so_ffamily) {
+    case AF_INET:
+        if (so->so_faddr.s_addr == INADDR_ANY ||
+            (so->so_faddr.s_addr & loopback_mask) ==
+            (loopback_addr.s_addr & loopback_mask)) {
+           so->so_faddr = slirp->vhost_addr;
+        }
+        break;
+
+    default:
+        break;
+    }
+}
This page took 0.03042 seconds and 4 git commands to generate.