summaryrefslogtreecommitdiff
path: root/slirp/socket.c
diff options
context:
space:
mode:
Diffstat (limited to 'slirp/socket.c')
-rw-r--r--slirp/socket.c112
1 files changed, 92 insertions, 20 deletions
diff --git a/slirp/socket.c b/slirp/socket.c
index bf603c9185..d1034fb536 100644
--- a/slirp/socket.c
+++ b/slirp/socket.c
@@ -438,6 +438,7 @@ void
sorecvfrom(struct socket *so)
{
struct sockaddr_storage addr;
+ struct sockaddr_storage saddr, daddr;
socklen_t addrlen = sizeof(struct sockaddr_storage);
DEBUG_CALL("sorecvfrom");
@@ -525,11 +526,17 @@ 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
*/
+ saddr = addr;
+ sotranslate_in(so, &saddr);
+ daddr = so->lhost.ss;
+
switch (so->so_ffamily) {
case AF_INET:
- udp_output(so, m, (struct sockaddr_in *) &addr);
+ udp_output(so, m, (struct sockaddr_in *) &saddr,
+ (struct sockaddr_in *) &daddr,
+ so->so_iptos);
break;
default:
break;
@@ -544,33 +551,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;
@@ -726,3 +720,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;
+ }
+}