socket.c revision 0f252ad257bdf002284cb2a1359814cd9fa3f2a9
/*
* Copyright (c) 1995 Danny Gasparovski.
*
* Please read the file COPYRIGHT for the
* terms and conditions of the copyright.
*/
#define WANT_SYS_IOCTL_H
#include <slirp.h>
#include "ip_icmp.h"
#include "main.h"
#ifdef __sun__
#endif
#if defined (RT_OS_WINDOWS)
#include <iphlpapi.h>
#include <icmpapi.h>
#endif
static void send_icmp_to_guest(PNATState, char *, size_t, struct socket *, const struct sockaddr_in *);
#ifdef RT_OS_WINDOWS
#else /* RT_OS_WINDOWS */
#endif /* !RT_OS_WINDOWS */
void
so_init()
{
}
struct socket *
{
{
return so;
}
}
/*
* Create a new socket, initialise the fields
* It is the responsibility of the caller to
* insque() it into the correct linked-list
*/
struct socket *
socreate()
{
if(so)
{
so->s = -1;
}
return so;
}
/*
* remque and free a socket, clobber cache
* VBOX_WITH_SLIRP_MT: before sofree queue should be locked, because
* in sofree we don't know from which queue item beeing removed.
*/
void
{
if (so == tcp_last_so)
tcp_last_so = &tcb;
else if (so == udp_last_so)
udp_last_so = &udb;
/* check if mbuf haven't been already freed */
{
{
}
}
}
#ifdef VBOX_WITH_SLIRP_MT
void
{
}
#endif
/*
* Read from so's socket into sb_snd, updating all relevant sbuf fields
* NOTE: This will only be called if it is select()ed for reading, so
* a read() of 0 (or less) means it's disconnected
*/
int
{
DEBUG_CALL("soread");
/*
* No need to check if there's enough room to read.
* soread wouldn't have been called if there weren't
*/
{
/* Should never succeed, but... */
n = 1;
}
else
{
/* Should never succeed, but... */
if (len)
{
{
{
n = 2;
}
else
{
n = 1;
}
}
else
n = 2;
}
else
{
n = 1;
}
}
#ifdef HAVE_READV
#else
#endif
if (nn <= 0)
{
#if defined(VBOX_WITH_SIMPLIFIED_SLIRP_SYNC) && defined(RT_OS_WINDOWS)
/*
* Special case for WSAEnumNetworkEvents: If we receive 0 bytes that
* _could_ mean that the connection is closed. But we will receive an
* FD_CLOSE event later if the connection was _really_ closed. With
* www.youtube.com I see this very often. Closing the socket too early
* would be dangerous.
*/
if (nn == 0 && !fCloseIfNothingRead)
{
return 0;
}
#endif
{
return 0;
}
else
{
/* nn == 0 means peer has performed an orderly shutdown */
return -1;
}
}
#ifndef HAVE_READV
/*
* If there was no error, try and read the second time round
* We read again if n = 2 (ie, there's another part of the buffer)
* and we read as much as we could in the first read
* We don't test for <= 0 this time, because there legitimately
* might not be any more data (since the socket is non-blocking),
* a close will be detected on next iteration.
* A return of -1 wont (shouldn't) happen, since it didn't happen above
*/
{
int ret;
if (ret > 0)
}
#endif
/* Update fields */
return nn;
}
/*
* Get urgent data
*
* When the socket is created, we set it SO_OOBINLINE,
* so when OOB data arrives, we soread() it and everything
* in the send buffer is sent as urgent data
*/
void
{
DEBUG_CALL("sorecvoob");
/*
* We take a guess at how much urgent data has arrived.
* In most situations, when urgent data arrives, the next
* read() should get all the urgent data. This guess will
* be wrong however if more data arrives just after the
* urgent data, or the read() doesn't return all the
* urgent data.
*/
}
/*
* Send urgent data
* There's a lot duplicated code here, but...
*/
int
{
int n, len;
DEBUG_CALL("sosendoob");
{
/* We can send it directly */
}
else
{
/*
* Since there's no sendv or sendtov like writev,
* we must copy all data to a linear buffer then
* send it all
*/
{
len += n;
}
#ifdef DEBUG
if (n != len)
#endif
}
return n;
}
/*
* Write data from so_rcv to so's socket,
* updating all sbuf field as necessary
*/
int
{
int n,nn;
DEBUG_CALL("sowrite");
{
{
return 0;
}
}
/*
* No need to check if there's something to write,
* sowrite wouldn't have been called otherwise
*/
{
/* Should never succeed, but... */
n = 1;
}
else
{
if (len)
{
n = 2;
}
else
n = 1;
}
/* Check if there's urgent data to send, and if so, send it */
#ifdef HAVE_READV
#else
#endif
/* This should never happen, but people tell me it does *shrug* */
{
return 0;
}
{
return -1;
}
#ifndef HAVE_READV
{
int ret;
if (ret > 0)
}
#endif
/* Update sbuf */
/*
* If in DRAIN mode, and there's no more data, set
* it CANTSENDMORE
*/
return nn;
}
/*
* recvfrom() a UDP socket
*/
void
{
struct sockaddr_in addr;
DEBUG_CALL("sorecvfrom");
{
/* This is a "ping" reply */
#ifdef RT_OS_WINDOWS
#else /* RT_OS_WINDOWS */
#endif /* !RT_OS_WINDOWS */
}
else
{
/* A "normal" UDP packet */
struct mbuf *m;
u_long n;
{
return;
}
m->m_data += if_maxlinkhdr;
+ sizeof(struct ip); /*XXX: no options atm*/
#endif
/*
* XXX Shouldn't FIONREAD packets destined for port 53,
* but I don't know the max packet size for DNS lookups
*/
len = M_FREEROOM(m);
/* if (so->so_fport != htons(53)) */
{
if (n > len)
{
m_inc(m, n);
len = M_FREEROOM(m);
}
}
Log2((" did recvfrom %d, errno = %d-%s\n",
if(m->m_len < 0)
{
if (errno == EHOSTUNREACH)
else if(errno == ENETUNREACH)
}
else
{
/*
* Hack: domain name lookup will be used the most for UDP,
* and since they'll only be used once there's no need
* for the 4 minute (or whatever) timeout... So we time them
* out much quicker (10 seconds for now...)
*/
{
else
}
#if 0
{
m->m_len = 0;
}
#endif
/*
* If this packet was destined for CTL_ADDR,
* make it look like that's where it came from, done by udp_output
*/
} /* rx error */
} /* if ping packet */
}
/*
* sendto() a socket
*/
int
{
int ret;
struct sockaddr_in addr;
#if 0
struct sockaddr_in host_addr;
#endif
DEBUG_CALL("sosendto");
DEBUG_ARG("m = %lx", (long)m);
{
/* It's an alias */
switch(last_byte)
{
#if 0
/* handle this case at 'default:' */
case CTL_BROADCAST:
/* Send the packet to host to fully emulate broadcast */
/** @todo r=klaus: on Linux host this causes the host to receive
* the packet twice for some reason. And I cannot find any place
* in the man pages which states that sending a broadcast does not
* reach the host itself. */
break;
#endif
case CTL_DNS:
#ifndef VBOX_WITH_MULTI_DNS
else
break;
#endif
case CTL_ALIAS:
default:
else
break;
}
}
else
/* Don't care what port we get */
if (ret < 0)
{
return -1;
}
/*
* Kill the socket if there's no reply in 4 minutes,
* but only if it's an expirable socket
*/
return 0;
}
/*
* XXX This should really be tcp_listen
*/
struct socket *
{
struct sockaddr_in addr;
int s, opt = 1;
DEBUG_CALL("solisten");
{
/* RTMemFree(so); Not sofree() ??? free(NULL) == NOP */
return NULL;
}
/* Don't tcp_attach... we don't need so_snd nor so_rcv */
{
return NULL;
}
/*
* SS_FACCEPTONCE sockets must time out.
*/
if (flags & SS_FACCEPTONCE)
|| (listen(s,1) < 0))
{
#ifdef RT_OS_WINDOWS
closesocket(s);
/* Restore the real errno */
#else
close(s);
/* Restore the real errno */
#endif
return NULL;
}
else
so->s = s;
return so;
}
/*
* Data is available in so_rcv
* Just write() the data to the socket
* XXX not yet...
*/
void
{
#if 0
#endif
}
/*
* Data has been freed in so_snd
* We have room for a read() if we want to
* For now, don't read, it'll be done in the main loop
*/
void
{
}
/*
* Various session state calls
* XXX Should be #define's
* The socket state stuff needs work, these often get call 2 or 3
* times each when only 1 was needed
*/
void
{
}
void
{
}
void
{
{
}
/* XXX close() here as well? */
else
}
void
{
else
}
void
{
#if 0
/*
* XXX Do nothing ... ?
*/
#endif
}
/*
* Set write drain mode
* Set CANTSENDMORE once all data has been write()n
*/
void
{
else
}
static void
send_icmp_to_guest(PNATState pData, char *buff, size_t len, struct socket *so, const struct sockaddr_in *addr)
{
char ip_copy[256];
int old_ip_len;
int hlen, original_hlen = 0;
struct mbuf *m;
int type = 0;
{
return;
}
if ( type == ICMP_TIMXCEED
|| type == ICMP_UNREACH)
{
}
else
{
}
{
Log(("NAT: Can't find the corresponding packet for the received ICMP\n"));
return;
}
/* Now ip is pointing on header we've sent from guest */
{
if (old_ip_len > sizeof(ip_copy))
old_ip_len = sizeof(ip_copy);
}
/* source address from original IP packet*/
/* overide ther tail of old packet */
/* saves original ip header and options */
if ( type == ICMP_TIMXCEED
|| type == ICMP_UNREACH)
{
/* according RFC 793 error messages required copy of initial IP header + 64 bit */
}
icmp_reflect(pData, m);
/* Don't call m_free here*/
if ( type == ICMP_TIMXCEED
|| type == ICMP_UNREACH)
{
switch (proto)
{
case IPPROTO_UDP:
/*XXX: so->so_m already freed so we shouldn't call sofree */
break;
case IPPROTO_TCP:
/*close tcp should be here */
break;
default:
/* do nothing */
break;
}
}
}
#ifdef RT_OS_WINDOWS
static void
{
int len;
int i;
struct mbuf *m;
int hlen = 0;
int data_len = 0;
int nbytes = 0;
#ifndef VBOX_WITH_SIMPLIFIED_SLIRP_SYNC
fIcmp = 0; /* reply processed */
#endif
if (len < 0)
{
return;
}
if (len == 0)
return; /* no error */
for (i = 0; i < len; ++i)
{
{
case IP_DEST_HOST_UNREACHABLE:
case IP_DEST_NET_UNREACHABLE:
case IP_DEST_PROT_UNREACHABLE:
/* UNREACH error inject here */
case IP_DEST_PORT_UNREACHABLE:
break;
case IP_SUCCESS: /* echo replied */
m->m_data += if_maxlinkhdr;
data_len += ICMP_MINLEN;
icmp_reflect(pData, m);
break;
case IP_TTL_EXPIRED_TRANSIT: /* TTL expired */
return;
}
icmp_reflect(pData, m);
break;
default:
Log(("ICMP(default): message with Status: %x was received from %x\n", icr[i].Status, icr[i].Address));
break;
}
}
}
#else /* RT_OS_WINDOWS */
{
struct sockaddr_in addr;
char buff[1500];
int len;
/* XXX Check if reply is "correct"? */
{
if (errno == EHOSTUNREACH)
else if(errno == ENETUNREACH)
}
else
{
}
}
#endif /* !RT_OS_WINDOWS */