Path: blob/a-new-beginning/SharedDependencies/Sources/libslirp/slirp.c
2 views
/* SPDX-License-Identifier: MIT */1/*2* libslirp glue3*4* Copyright (c) 2004-2008 Fabrice Bellard5*6* Permission is hereby granted, free of charge, to any person obtaining a copy7* of this software and associated documentation files (the "Software"), to deal8* in the Software without restriction, including without limitation the rights9* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell10* copies of the Software, and to permit persons to whom the Software is11* furnished to do so, subject to the following conditions:12*13* The above copyright notice and this permission notice shall be included in14* all copies or substantial portions of the Software.15*16* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR17* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,18* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL19* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER20* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,21* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN22* THE SOFTWARE.23*/24#include "slirp.h"252627#ifndef _WIN3228#include <net/if.h>29#endif3031/* https://gitlab.freedesktop.org/slirp/libslirp/issues/18 */32#if defined(__NetBSD__) && defined(if_mtu)33#undef if_mtu34#endif3536#if defined(_WIN32)3738#define INITIAL_DNS_ADDR_BUF_SIZE 32 * 102439#define REALLOC_RETRIES 54041// Broadcast site local DNS resolvers. We do not use these because they are42// highly unlikely to be valid.43// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt44static const struct in6_addr SITE_LOCAL_DNS_BROADCAST_ADDRS[] = {45{46{{470xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,480x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0149}}50},51{52{{530xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,540x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0255}}56},57{58{{590xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff,600x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,61}}62},63};6465#endif6667int slirp_debug;6869/* Define to 1 if you want KEEPALIVE timers */70bool slirp_do_keepalive;7172/* host loopback address */73struct in_addr loopback_addr;74/* host loopback network mask */75unsigned long loopback_mask;7677/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */78static const uint8_t special_ethaddr[ETH_ALEN] = { 0x52, 0x55, 0x00,790x00, 0x00, 0x00 };8081unsigned curtime;8283static struct in_addr dns_addr;84static struct in6_addr dns6_addr;85static uint32_t dns6_scope_id;86static unsigned dns_addr_time;87static unsigned dns6_addr_time;8889#define TIMEOUT_FAST 2 /* milliseconds */90#define TIMEOUT_SLOW 499 /* milliseconds */91/* for the aging of certain requests like DNS */92#define TIMEOUT_DEFAULT 1000 /* milliseconds */9394#if defined(_WIN32)9596int get_dns_addr(struct in_addr *pdns_addr)97{98FIXED_INFO *FixedInfo = NULL;99ULONG BufLen;100DWORD ret;101IP_ADDR_STRING *pIPAddr;102struct in_addr tmp_addr;103104if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) {105*pdns_addr = dns_addr;106return 0;107}108109FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO));110BufLen = sizeof(FIXED_INFO);111112if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) {113if (FixedInfo) {114GlobalFree(FixedInfo);115FixedInfo = NULL;116}117FixedInfo = GlobalAlloc(GPTR, BufLen);118}119120if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) {121printf("GetNetworkParams failed. ret = %08x\n", (unsigned)ret);122if (FixedInfo) {123GlobalFree(FixedInfo);124FixedInfo = NULL;125}126return -1;127}128129pIPAddr = &(FixedInfo->DnsServerList);130inet_aton(pIPAddr->IpAddress.String, &tmp_addr);131*pdns_addr = tmp_addr;132dns_addr = tmp_addr;133dns_addr_time = curtime;134if (FixedInfo) {135GlobalFree(FixedInfo);136FixedInfo = NULL;137}138return 0;139}140141static int is_site_local_dns_broadcast(struct in6_addr *address)142{143int i;144for (i = 0; i < G_N_ELEMENTS(SITE_LOCAL_DNS_BROADCAST_ADDRS); i++) {145if (in6_equal(address, &SITE_LOCAL_DNS_BROADCAST_ADDRS[i])) {146return 1;147}148}149return 0;150}151152static void print_dns_v6_address(struct in6_addr address)153{154char address_str[INET6_ADDRSTRLEN] = "";155if (inet_ntop(AF_INET6, &address, address_str, INET6_ADDRSTRLEN)156== NULL) {157DEBUG_ERROR("Failed to stringify IPv6 address for logging.");158return;159}160DEBUG_RAW_CALL("IPv6 DNS server found: %s", address_str);161}162163// Gets the first valid DNS resolver with an IPv6 address.164// Ignores any site local broadcast DNS servers, as these165// are on deprecated addresses and not generally expected166// to work. Further details at:167// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt168static int get_ipv6_dns_server(struct in6_addr *dns_server_address, uint32_t *scope_id)169{170PIP_ADAPTER_ADDRESSES addresses = NULL;171PIP_ADAPTER_ADDRESSES address = NULL;172IP_ADAPTER_DNS_SERVER_ADDRESS *dns_server = NULL;173struct sockaddr_in6 *dns_v6_addr = NULL;174175ULONG buf_size = INITIAL_DNS_ADDR_BUF_SIZE;176DWORD res = ERROR_BUFFER_OVERFLOW;177int i;178179for (i = 0; i < REALLOC_RETRIES; i++) {180// If non null, we hit buffer overflow, free it so we can try again.181if (addresses != NULL) {182g_free(addresses);183}184185addresses = g_malloc(buf_size);186res = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL,187addresses, &buf_size);188189if (res != ERROR_BUFFER_OVERFLOW) {190break;191}192}193194if (res != NO_ERROR) {195DEBUG_ERROR("Failed to get IPv6 DNS addresses due to error %lX", res);196goto failure;197}198199address = addresses;200for (address = addresses; address != NULL; address = address->Next) {201for (dns_server = address->FirstDnsServerAddress;202dns_server != NULL;203dns_server = dns_server->Next) {204205if (dns_server->Address.lpSockaddr->sa_family != AF_INET6) {206continue;207}208209dns_v6_addr = (struct sockaddr_in6 *)dns_server->Address.lpSockaddr;210if (is_site_local_dns_broadcast(&dns_v6_addr->sin6_addr) == 0) {211print_dns_v6_address(dns_v6_addr->sin6_addr);212*dns_server_address = dns_v6_addr->sin6_addr;213*scope_id = dns_v6_addr->sin6_scope_id;214215g_free(addresses);216return 0;217}218}219}220221DEBUG_ERROR("No IPv6 DNS servers found.\n");222223failure:224g_free(addresses);225return -1;226}227228int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)229{230if (!in6_zero(&dns6_addr) && (curtime - dns6_addr_time) < TIMEOUT_DEFAULT) {231*pdns6_addr = dns6_addr;232*scope_id = dns6_scope_id;233return 0;234}235236if (get_ipv6_dns_server(pdns6_addr, scope_id) == 0) {237dns6_addr = *pdns6_addr;238dns6_addr_time = curtime;239dns6_scope_id = *scope_id;240return 0;241}242243return -1;244}245246static void winsock_cleanup(void)247{248WSACleanup();249}250251#elif defined(__APPLE__)252253#include <resolv.h>254255static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,256socklen_t addrlen, unsigned *cached_time)257{258if (curtime - *cached_time < TIMEOUT_DEFAULT) {259memcpy(pdns_addr, cached_addr, addrlen);260return 0;261}262return 1;263}264265static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr,266socklen_t addrlen,267uint32_t *scope_id, uint32_t *cached_scope_id,268unsigned *cached_time)269{270struct __res_state state;271union res_sockaddr_union servers[NI_MAXSERV];272int count;273int found;274void *addr;275276// we only support IPv4 and IPv4, we assume it's one or the other277assert(af == AF_INET || af == AF_INET6);278279if (res_ninit(&state) != 0) {280return -1;281}282283count = res_getservers(&state, servers, NI_MAXSERV);284found = 0;285DEBUG_MISC("IP address of your DNS(s):");286for (int i = 0; i < count; i++) {287if (af == servers[i].sin.sin_family) {288found++;289}290if (af == AF_INET) {291addr = &servers[i].sin.sin_addr;292} else { // af == AF_INET6293addr = &servers[i].sin6.sin6_addr;294}295296// we use the first found entry297if (found == 1) {298memcpy(pdns_addr, addr, addrlen);299memcpy(cached_addr, addr, addrlen);300if (scope_id) {301*scope_id = 0;302}303if (cached_scope_id) {304*cached_scope_id = 0;305}306*cached_time = curtime;307}308309if (found > 3) {310DEBUG_MISC(" (more)");311break;312} else if (slirp_debug & DBG_MISC) {313char s[INET6_ADDRSTRLEN];314const char *res = inet_ntop(af, addr, s, sizeof(s));315if (!res) {316res = " (string conversion error)";317}318DEBUG_MISC(" %s", res);319}320}321322res_ndestroy(&state);323if (!found)324return -1;325return 0;326}327328int get_dns_addr(struct in_addr *pdns_addr)329{330if (dns_addr.s_addr != 0) {331int ret;332ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr),333&dns_addr_time);334if (ret <= 0) {335return ret;336}337}338return get_dns_addr_libresolv(AF_INET, pdns_addr, &dns_addr,339sizeof(dns_addr), NULL, NULL, &dns_addr_time);340}341342int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)343{344if (!in6_zero(&dns6_addr)) {345int ret;346ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),347&dns6_addr_time);348if (ret == 0) {349*scope_id = dns6_scope_id;350}351if (ret <= 0) {352return ret;353}354}355return get_dns_addr_libresolv(AF_INET6, pdns6_addr, &dns6_addr,356sizeof(dns6_addr),357scope_id, &dns6_scope_id, &dns6_addr_time);358}359360#else // !defined(_WIN32) && !defined(__APPLE__)361362#if defined(__HAIKU__)363#define RESOLV_CONF_PATH "/boot/system/settings/network/resolv.conf"364#else365#define RESOLV_CONF_PATH "/etc/resolv.conf"366#endif367368static int get_dns_addr_cached(void *pdns_addr, void *cached_addr,369socklen_t addrlen, struct stat *cached_stat,370unsigned *cached_time)371{372struct stat old_stat;373if (curtime - *cached_time < TIMEOUT_DEFAULT) {374memcpy(pdns_addr, cached_addr, addrlen);375return 0;376}377old_stat = *cached_stat;378if (stat(RESOLV_CONF_PATH, cached_stat) != 0) {379return -1;380}381if (cached_stat->st_dev == old_stat.st_dev &&382cached_stat->st_ino == old_stat.st_ino &&383cached_stat->st_size == old_stat.st_size &&384cached_stat->st_mtime == old_stat.st_mtime) {385memcpy(pdns_addr, cached_addr, addrlen);386return 0;387}388return 1;389}390391static bool try_and_setdns_server(int af, unsigned found, unsigned if_index,392const char *buff2, void *pdns_addr, void *cached_addr,393socklen_t addrlen, uint32_t *scope_id, uint32_t *cached_scope_id,394unsigned *cached_time)395{396union {397struct in_addr dns_addr;398struct in6_addr dns6_addr;399} tmp_addr;400401assert(sizeof(tmp_addr) >= addrlen);402403if (!inet_pton(af, buff2, &tmp_addr))404return false;405406/* If it's the first one, set it to dns_addr */407if (!found) {408memcpy(pdns_addr, &tmp_addr, addrlen);409memcpy(cached_addr, &tmp_addr, addrlen);410if (scope_id) {411*scope_id = if_index;412}413if (cached_scope_id) {414*cached_scope_id = if_index;415}416*cached_time = curtime;417}418419if (found > 2) {420DEBUG_MISC(" (more)");421} else if (slirp_debug & DBG_MISC) {422char s[INET6_ADDRSTRLEN];423const char *res = inet_ntop(af, &tmp_addr, s, sizeof(s));424if (!res) {425res = " (string conversion error)";426}427DEBUG_MISC(" %s", res);428}429430return true;431}432433static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr,434socklen_t addrlen,435uint32_t *scope_id, uint32_t *cached_scope_id,436unsigned *cached_time)437{438char buff[512];439char buff2[257];440FILE *f;441int found = 0;442unsigned if_index;443unsigned nameservers = 0;444445f = fopen(RESOLV_CONF_PATH, "r");446if (!f)447return -1;448449DEBUG_MISC("IP address of your DNS(s):");450while (fgets(buff, 512, f) != NULL) {451if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) {452char *c = strchr(buff2, '%');453if (c) {454if_index = if_nametoindex(c + 1);455*c = '\0';456} else {457if_index = 0;458}459460nameservers++;461462if (!try_and_setdns_server(af, found, if_index, buff2, pdns_addr,463cached_addr, addrlen, scope_id,464cached_scope_id, cached_time))465continue;466467if (++found > 3)468break;469}470}471fclose(f);472if (nameservers && !found)473return -1;474if (!nameservers) {475found += try_and_setdns_server(af, found, 0, "127.0.0.1",476pdns_addr, cached_addr, addrlen, scope_id,477cached_scope_id, cached_time);478found += try_and_setdns_server(af, found, 0, "::1",479pdns_addr, cached_addr, addrlen, scope_id,480cached_scope_id, cached_time);481}482483return found ? 0 : -1;484}485486int get_dns_addr(struct in_addr *pdns_addr)487{488static struct stat dns_addr_stat;489490if (dns_addr.s_addr != 0) {491int ret;492ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr),493&dns_addr_stat, &dns_addr_time);494if (ret <= 0) {495return ret;496}497}498return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr,499sizeof(dns_addr),500NULL, NULL, &dns_addr_time);501}502503int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id)504{505static struct stat dns6_addr_stat;506507if (!in6_zero(&dns6_addr)) {508int ret;509ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr),510&dns6_addr_stat, &dns6_addr_time);511if (ret == 0) {512*scope_id = dns6_scope_id;513}514if (ret <= 0) {515return ret;516}517}518return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr,519sizeof(dns6_addr),520scope_id, &dns6_scope_id, &dns6_addr_time);521}522523#endif524525static void slirp_init_once(void)526{527static int initialized;528const char *debug;529#ifdef _WIN32530WSADATA Data;531#endif532533if (initialized) {534return;535}536initialized = 1;537538#ifdef _WIN32539WSAStartup(MAKEWORD(2, 0), &Data);540atexit(winsock_cleanup);541#endif542543loopback_addr.s_addr = htonl(INADDR_LOOPBACK);544loopback_mask = htonl(IN_CLASSA_NET);545546debug = g_getenv("SLIRP_DEBUG");547if (debug) {548const GDebugKey keys[] = {549{ "call", DBG_CALL },550{ "misc", DBG_MISC },551{ "error", DBG_ERROR },552{ "tftp", DBG_TFTP },553{ "verbose_call", DBG_VERBOSE_CALL },554};555slirp_debug = g_parse_debug_string(debug, keys, G_N_ELEMENTS(keys));556}557}558559static void ra_timer_handler_cb(void *opaque)560{561Slirp *slirp = opaque;562563ra_timer_handler(slirp, NULL);564}565566void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque)567{568g_return_if_fail(id >= 0 && id < SLIRP_TIMER_NUM);569570switch (id) {571case SLIRP_TIMER_RA:572ra_timer_handler(slirp, cb_opaque);573return;574default:575abort();576}577}578579void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque)580{581g_return_val_if_fail(id >= 0 && id < SLIRP_TIMER_NUM, NULL);582583if (slirp->cfg_version >= 4 && slirp->cb->timer_new_opaque) {584return slirp->cb->timer_new_opaque(id, cb_opaque, slirp->opaque);585}586587switch (id) {588case SLIRP_TIMER_RA:589g_return_val_if_fail(cb_opaque == NULL, NULL);590return slirp->cb->timer_new(ra_timer_handler_cb, slirp, slirp->opaque);591592default:593abort();594}595}596597Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque)598{599Slirp *slirp;600601g_return_val_if_fail(cfg != NULL, NULL);602g_return_val_if_fail(cfg->version >= SLIRP_CONFIG_VERSION_MIN, NULL);603g_return_val_if_fail(cfg->version <= SLIRP_CONFIG_VERSION_MAX, NULL);604g_return_val_if_fail(cfg->if_mtu >= IF_MTU_MIN || cfg->if_mtu == 0, NULL);605g_return_val_if_fail(cfg->if_mtu <= IF_MTU_MAX, NULL);606g_return_val_if_fail(cfg->if_mru >= IF_MRU_MIN || cfg->if_mru == 0, NULL);607g_return_val_if_fail(cfg->if_mru <= IF_MRU_MAX, NULL);608g_return_val_if_fail(!cfg->bootfile ||609(strlen(cfg->bootfile) <610G_SIZEOF_MEMBER(struct bootp_t, bp_file)), NULL);611612slirp = g_malloc0(sizeof(Slirp));613614slirp_init_once();615616slirp->cfg_version = cfg->version;617slirp->opaque = opaque;618slirp->cb = callbacks;619slirp->grand = g_rand_new();620slirp->restricted = cfg->restricted;621622slirp->in_enabled = cfg->in_enabled;623slirp->in6_enabled = cfg->in6_enabled;624625if_init(slirp);626ip_init(slirp);627628m_init(slirp);629630slirp->vnetwork_addr = cfg->vnetwork;631slirp->vnetwork_mask = cfg->vnetmask;632slirp->vhost_addr = cfg->vhost;633slirp->vprefix_addr6 = cfg->vprefix_addr6;634slirp->vprefix_len = cfg->vprefix_len;635slirp->vhost_addr6 = cfg->vhost6;636if (cfg->vhostname) {637slirp_pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname),638cfg->vhostname);639}640slirp->tftp_prefix = g_strdup(cfg->tftp_path);641slirp->bootp_filename = g_strdup(cfg->bootfile);642slirp->vdomainname = g_strdup(cfg->vdomainname);643slirp->vdhcp_startaddr = cfg->vdhcp_start;644slirp->vnameserver_addr = cfg->vnameserver;645slirp->vnameserver_addr6 = cfg->vnameserver6;646slirp->tftp_server_name = g_strdup(cfg->tftp_server_name);647648if (cfg->vdnssearch) {649translate_dnssearch(slirp, cfg->vdnssearch);650}651slirp->if_mtu = cfg->if_mtu == 0 ? IF_MTU_DEFAULT : cfg->if_mtu;652slirp->if_mru = cfg->if_mru == 0 ? IF_MRU_DEFAULT : cfg->if_mru;653slirp->disable_host_loopback = cfg->disable_host_loopback;654slirp->enable_emu = cfg->enable_emu;655656if (cfg->version >= 2) {657slirp->outbound_addr = cfg->outbound_addr;658slirp->outbound_addr6 = cfg->outbound_addr6;659} else {660slirp->outbound_addr = NULL;661slirp->outbound_addr6 = NULL;662}663664if (cfg->version >= 3) {665slirp->disable_dns = cfg->disable_dns;666} else {667slirp->disable_dns = false;668}669670if (cfg->version >= 4) {671slirp->disable_dhcp = cfg->disable_dhcp;672} else {673slirp->disable_dhcp = false;674}675676if (slirp->cfg_version >= 4 && slirp->cb->init_completed) {677slirp->cb->init_completed(slirp, slirp->opaque);678}679680if (cfg->version >= 5) {681slirp->mfr_id = cfg->mfr_id;682memcpy(slirp->oob_eth_addr, cfg->oob_eth_addr, ETH_ALEN);683} else {684slirp->mfr_id = 0;685memset(slirp->oob_eth_addr, 0, ETH_ALEN);686}687688ip6_post_init(slirp);689return slirp;690}691692Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork,693struct in_addr vnetmask, struct in_addr vhost,694bool in6_enabled, struct in6_addr vprefix_addr6,695uint8_t vprefix_len, struct in6_addr vhost6,696const char *vhostname, const char *tftp_server_name,697const char *tftp_path, const char *bootfile,698struct in_addr vdhcp_start, struct in_addr vnameserver,699struct in6_addr vnameserver6, const char **vdnssearch,700const char *vdomainname, const SlirpCb *callbacks,701void *opaque)702{703SlirpConfig cfg;704memset(&cfg, 0, sizeof(cfg));705cfg.version = 1;706cfg.restricted = restricted;707cfg.in_enabled = in_enabled;708cfg.vnetwork = vnetwork;709cfg.vnetmask = vnetmask;710cfg.vhost = vhost;711cfg.in6_enabled = in6_enabled;712cfg.vprefix_addr6 = vprefix_addr6;713cfg.vprefix_len = vprefix_len;714cfg.vhost6 = vhost6;715cfg.vhostname = vhostname;716cfg.tftp_server_name = tftp_server_name;717cfg.tftp_path = tftp_path;718cfg.bootfile = bootfile;719cfg.vdhcp_start = vdhcp_start;720cfg.vnameserver = vnameserver;721cfg.vnameserver6 = vnameserver6;722cfg.vdnssearch = vdnssearch;723cfg.vdomainname = vdomainname;724return slirp_new(&cfg, callbacks, opaque);725}726727void slirp_cleanup(Slirp *slirp)728{729struct gfwd_list *e, *next;730731for (e = slirp->guestfwd_list; e; e = next) {732next = e->ex_next;733g_free(e->ex_exec);734g_free(e->ex_unix);735g_free(e);736}737738ip_cleanup(slirp);739ip6_cleanup(slirp);740m_cleanup(slirp);741tftp_cleanup(slirp);742743g_rand_free(slirp->grand);744745g_free(slirp->vdnssearch);746g_free(slirp->tftp_prefix);747g_free(slirp->bootp_filename);748g_free(slirp->vdomainname);749g_free(slirp);750}751752#define CONN_CANFSEND(so) \753(((so)->so_state & (SS_FCANTSENDMORE | SS_ISFCONNECTED)) == SS_ISFCONNECTED)754#define CONN_CANFRCV(so) \755(((so)->so_state & (SS_FCANTRCVMORE | SS_ISFCONNECTED)) == SS_ISFCONNECTED)756757static void slirp_update_timeout(Slirp *slirp, uint32_t *timeout)758{759uint32_t t;760761if (*timeout <= TIMEOUT_FAST) {762return;763}764765t = MIN(1000, *timeout);766767/* If we have tcp timeout with slirp, then we will fill @timeout with768* more precise value.769*/770if (slirp->time_fasttimo) {771*timeout = TIMEOUT_FAST;772return;773}774if (slirp->do_slowtimo) {775t = MIN(TIMEOUT_SLOW, t);776}777*timeout = t;778}779780void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout,781SlirpAddPollCb add_poll, void *opaque)782{783struct socket *so, *so_next;784785/*786* First, TCP sockets787*/788789/*790* *_slowtimo needs calling if there are IP fragments791* in the fragment queue, or there are TCP connections active792*/793slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) ||794(&slirp->ipq.ip_link != slirp->ipq.ip_link.next));795796for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) {797int events = 0;798799so_next = so->so_next;800801so->pollfds_idx = -1;802803/*804* See if we need a tcp_fasttimo805*/806if (slirp->time_fasttimo == 0 && so->so_tcpcb->t_flags & TF_DELACK) {807slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */808}809810/*811* NOFDREF can include still connecting to local-host,812* newly socreated() sockets etc. Don't want to select these.813*/814if (so->so_state & SS_NOFDREF || so->s == -1) {815continue;816}817818/*819* Set for reading sockets which are accepting820*/821if (so->so_state & SS_FACCEPTCONN) {822so->pollfds_idx = add_poll(823so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque);824continue;825}826827/*828* Set for writing sockets which are connecting829*/830if (so->so_state & SS_ISFCONNECTING) {831so->pollfds_idx =832add_poll(so->s, SLIRP_POLL_OUT | SLIRP_POLL_ERR, opaque);833continue;834}835836/*837* Set for writing if we are connected, can send more, and838* we have something to send839*/840if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) {841events |= SLIRP_POLL_OUT | SLIRP_POLL_ERR;842}843844/*845* Set for reading (and urgent data) if we are connected, can846* receive more, and we have room for it.847*848* If sb is already half full, we will wait for the guest to consume it,849* and notify again in sbdrop() when the sb becomes less than half full.850*/851if (CONN_CANFRCV(so) &&852(so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) {853events |= SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR |854SLIRP_POLL_PRI;855}856857if (events) {858so->pollfds_idx = add_poll(so->s, events, opaque);859}860}861862/*863* UDP sockets864*/865for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) {866so_next = so->so_next;867868so->pollfds_idx = -1;869870/*871* See if it's timed out872*/873if (so->so_expire) {874if (so->so_expire <= curtime) {875udp_detach(so);876continue;877} else {878slirp->do_slowtimo = true; /* Let socket expire */879}880}881882/*883* When UDP packets are received from over the884* link, they're sendto()'d straight away, so885* no need for setting for writing886* Limit the number of packets queued by this session887* to 4. Note that even though we try and limit this888* to 4 packets, the session could have more queued889* if the packets needed to be fragmented890* (XXX <= 4 ?)891*/892if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) {893so->pollfds_idx = add_poll(894so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque);895}896}897898/*899* ICMP sockets900*/901for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) {902so_next = so->so_next;903904so->pollfds_idx = -1;905906/*907* See if it's timed out908*/909if (so->so_expire) {910if (so->so_expire <= curtime) {911icmp_detach(so);912continue;913} else {914slirp->do_slowtimo = true; /* Let socket expire */915}916}917918if (so->so_state & SS_ISFCONNECTED) {919so->pollfds_idx = add_poll(920so->s, SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque);921}922}923924slirp_update_timeout(slirp, timeout);925}926927void slirp_pollfds_poll(Slirp *slirp, int select_error,928SlirpGetREventsCb get_revents, void *opaque)929{930struct socket *so, *so_next;931int ret;932933curtime = slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS;934935/*936* See if anything has timed out937*/938if (slirp->time_fasttimo &&939((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) {940tcp_fasttimo(slirp);941slirp->time_fasttimo = 0;942}943if (slirp->do_slowtimo &&944((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) {945ip_slowtimo(slirp);946tcp_slowtimo(slirp);947slirp->last_slowtimo = curtime;948}949950/*951* Check sockets952*/953if (!select_error) {954/*955* Check TCP sockets956*/957for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) {958int revents;959960so_next = so->so_next;961962revents = 0;963if (so->pollfds_idx != -1) {964revents = get_revents(so->pollfds_idx, opaque);965}966967if (so->so_state & SS_NOFDREF || so->s == -1) {968continue;969}970971#ifndef __APPLE__972/*973* Check for URG data974* This will soread as well, so no need to975* test for SLIRP_POLL_IN below if this succeeds.976*977* This is however disabled on MacOS, which apparently always978* reports data as PRI when it is the last data of the979* connection. We would then report it out of band, which the guest980* would most probably not be ready for.981*/982if (revents & SLIRP_POLL_PRI) {983ret = sorecvoob(so);984if (ret < 0) {985/* Socket error might have resulted in the socket being986* removed, do not try to do anything more with it. */987continue;988}989}990/*991* Check sockets for reading992*/993else994#endif995if (revents &996(SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR | SLIRP_POLL_PRI)) {997/*998* Check for incoming connections999*/1000if (so->so_state & SS_FACCEPTCONN) {1001tcp_connect(so);1002continue;1003} /* else */1004ret = soread(so);10051006/* Output it if we read something */1007if (ret > 0) {1008tcp_output(sototcpcb(so));1009}1010if (ret < 0) {1011/* Socket error might have resulted in the socket being1012* removed, do not try to do anything more with it. */1013continue;1014}1015}10161017/*1018* Check sockets for writing1019*/1020if (!(so->so_state & SS_NOFDREF) &&1021(revents & (SLIRP_POLL_OUT | SLIRP_POLL_ERR))) {1022/*1023* Check for non-blocking, still-connecting sockets1024*/1025if (so->so_state & SS_ISFCONNECTING) {1026/* Connected */1027so->so_state &= ~SS_ISFCONNECTING;10281029ret = send(so->s, (const void *)&ret, 0, 0);1030if (ret < 0) {1031/* XXXXX Must fix, zero bytes is a NOP */1032if (errno == EAGAIN || errno == EWOULDBLOCK ||1033errno == EINPROGRESS || errno == ENOTCONN) {1034continue;1035}10361037/* else failed */1038so->so_state &= SS_PERSISTENT_MASK;1039so->so_state |= SS_NOFDREF;1040}1041/* else so->so_state &= ~SS_ISFCONNECTING; */10421043/*1044* Continue tcp_input1045*/1046tcp_input((struct mbuf *)NULL, sizeof(struct ip), so,1047so->so_ffamily);1048/* continue; */1049} else {1050ret = sowrite(so);1051if (ret > 0) {1052/* Call tcp_output in case we need to send a window1053* update to the guest, otherwise it will be stuck1054* until it sends a window probe. */1055tcp_output(sototcpcb(so));1056}1057}1058}1059}10601061/*1062* Now UDP sockets.1063* Incoming packets are sent straight away, they're not buffered.1064* Incoming UDP data isn't buffered either.1065*/1066for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) {1067int revents;10681069so_next = so->so_next;10701071revents = 0;1072if (so->pollfds_idx != -1) {1073revents = get_revents(so->pollfds_idx, opaque);1074}10751076if (so->s != -1 &&1077(revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) {1078sorecvfrom(so);1079}1080}10811082/*1083* Check incoming ICMP relies.1084*/1085for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) {1086int revents;10871088so_next = so->so_next;10891090revents = 0;1091if (so->pollfds_idx != -1) {1092revents = get_revents(so->pollfds_idx, opaque);1093}10941095if (so->s != -1 &&1096(revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) {1097if (so->so_type == IPPROTO_IPV6 || so->so_type == IPPROTO_ICMPV6)1098icmp6_receive(so);1099else1100icmp_receive(so);1101}1102}1103}11041105if_start(slirp);1106}11071108static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)1109{1110const struct slirp_arphdr *ah =1111(const struct slirp_arphdr *)(pkt + ETH_HLEN);1112uint8_t arp_reply[MAX(2 + ETH_HLEN + sizeof(struct slirp_arphdr), 2 + 64)];1113struct ethhdr *reh = (struct ethhdr *)(arp_reply + 2);1114struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_reply + 2 + ETH_HLEN);1115int ar_op;1116struct gfwd_list *ex_ptr;11171118if (!slirp->in_enabled) {1119return;1120}11211122if (pkt_len < ETH_HLEN + sizeof(struct slirp_arphdr)) {1123return; /* packet too short */1124}11251126ar_op = ntohs(ah->ar_op);1127switch (ar_op) {1128case ARPOP_REQUEST:1129if (ah->ar_tip == ah->ar_sip) {1130/* Gratuitous ARP */1131arp_table_add(slirp, ah->ar_sip, ah->ar_sha);1132return;1133}11341135if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) ==1136slirp->vnetwork_addr.s_addr) {1137if (ah->ar_tip == slirp->vnameserver_addr.s_addr ||1138ah->ar_tip == slirp->vhost_addr.s_addr)1139goto arp_ok;1140/* TODO: IPv6 */1141for (ex_ptr = slirp->guestfwd_list; ex_ptr;1142ex_ptr = ex_ptr->ex_next) {1143if (ex_ptr->ex_addr.s_addr == ah->ar_tip)1144goto arp_ok;1145}1146return;1147arp_ok:1148memset(arp_reply, 0, sizeof(arp_reply));11491150arp_table_add(slirp, ah->ar_sip, ah->ar_sha);11511152/* ARP request for alias/dns mac address */1153memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN);1154memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);1155memcpy(&reh->h_source[2], &ah->ar_tip, 4);1156reh->h_proto = htons(ETH_P_ARP);11571158rah->ar_hrd = htons(1);1159rah->ar_pro = htons(ETH_P_IP);1160rah->ar_hln = ETH_ALEN;1161rah->ar_pln = 4;1162rah->ar_op = htons(ARPOP_REPLY);1163memcpy(rah->ar_sha, reh->h_source, ETH_ALEN);1164rah->ar_sip = ah->ar_tip;1165memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN);1166rah->ar_tip = ah->ar_sip;1167slirp_send_packet_all(slirp, arp_reply + 2, sizeof(arp_reply) - 2);1168}1169break;1170case ARPOP_REPLY:1171arp_table_add(slirp, ah->ar_sip, ah->ar_sha);1172break;1173default:1174break;1175}1176}11771178void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len)1179{1180struct mbuf *m;1181int proto;11821183if (pkt_len < ETH_HLEN)1184return;11851186proto = (((uint16_t)pkt[12]) << 8) + pkt[13];1187switch (proto) {1188case ETH_P_ARP:1189arp_input(slirp, pkt, pkt_len);1190break;1191case ETH_P_IP:1192case ETH_P_IPV6:1193m = m_get(slirp);1194if (!m)1195return;1196/* Note: we add 2 to align the IP header on 8 bytes despite the ethernet1197* header, and add the margin for the tcpiphdr overhead */1198if (M_FREEROOM(m) < pkt_len + TCPIPHDR_DELTA + 2) {1199m_inc(m, pkt_len + TCPIPHDR_DELTA + 2);1200}1201m->m_len = pkt_len + TCPIPHDR_DELTA + 2;1202memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len);12031204m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN;1205m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN;12061207if (proto == ETH_P_IP) {1208ip_input(m);1209} else if (proto == ETH_P_IPV6) {1210ip6_input(m);1211}1212break;12131214case ETH_P_NCSI:1215ncsi_input(slirp, pkt, pkt_len);1216break;12171218default:1219break;1220}1221}12221223/* Prepare the IPv4 packet to be sent to the ethernet device. Returns 1 if no1224* packet should be sent, 0 if the packet must be re-queued, 2 if the packet1225* is ready to go.1226*/1227static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,1228uint8_t ethaddr[ETH_ALEN])1229{1230const struct ip *iph = (const struct ip *)ifm->m_data;12311232if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) {1233uint8_t arp_req[2 + ETH_HLEN + sizeof(struct slirp_arphdr)];1234struct ethhdr *reh = (struct ethhdr *)(arp_req + 2);1235struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_req + 2 + ETH_HLEN);12361237if (!ifm->resolution_requested) {1238/* If the client addr is not known, send an ARP request */1239memset(reh->h_dest, 0xff, ETH_ALEN);1240memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4);1241memcpy(&reh->h_source[2], &slirp->vhost_addr, 4);1242reh->h_proto = htons(ETH_P_ARP);1243rah->ar_hrd = htons(1);1244rah->ar_pro = htons(ETH_P_IP);1245rah->ar_hln = ETH_ALEN;1246rah->ar_pln = 4;1247rah->ar_op = htons(ARPOP_REQUEST);12481249/* source hw addr */1250memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4);1251memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4);12521253/* source IP */1254rah->ar_sip = slirp->vhost_addr.s_addr;12551256/* target hw addr (none) */1257memset(rah->ar_tha, 0, ETH_ALEN);12581259/* target IP */1260rah->ar_tip = iph->ip_dst.s_addr;1261slirp->client_ipaddr = iph->ip_dst;1262slirp_send_packet_all(slirp, arp_req + 2, sizeof(arp_req) - 2);1263ifm->resolution_requested = true;12641265/* Expire request and drop outgoing packet after 1 second */1266ifm->expiration_date =1267slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL;1268}1269return 0;1270} else {1271memcpy(eh->h_source, special_ethaddr, ETH_ALEN - 4);1272/* XXX: not correct */1273memcpy(&eh->h_source[2], &slirp->vhost_addr, 4);1274eh->h_proto = htons(ETH_P_IP);12751276/* Send this */1277return 2;1278}1279}12801281/* Prepare the IPv6 packet to be sent to the ethernet device. Returns 1 if no1282* packet should be sent, 0 if the packet must be re-queued, 2 if the packet1283* is ready to go.1284*/1285static int if_encap6(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh,1286uint8_t ethaddr[ETH_ALEN])1287{1288const struct ip6 *ip6h = mtod(ifm, const struct ip6 *);1289if (!ndp_table_search(slirp, ip6h->ip_dst, ethaddr)) {1290if (!ifm->resolution_requested) {1291ndp_send_ns(slirp, ip6h->ip_dst);1292ifm->resolution_requested = true;1293ifm->expiration_date =1294slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL;1295}1296return 0;1297} else {1298eh->h_proto = htons(ETH_P_IPV6);1299in6_compute_ethaddr(ip6h->ip_src, eh->h_source);13001301/* Send this */1302return 2;1303}1304}13051306/* Output the IP packet to the ethernet device. Returns 0 if the packet must be1307* re-queued.1308*/1309int if_encap(Slirp *slirp, struct mbuf *ifm)1310{1311uint8_t buf[IF_MTU_MAX + 100];1312struct ethhdr *eh = (struct ethhdr *)(buf + 2);1313uint8_t ethaddr[ETH_ALEN];1314const struct ip *iph = (const struct ip *)ifm->m_data;1315int ret;1316char ethaddr_str[ETH_ADDRSTRLEN];13171318if (ifm->m_len + ETH_HLEN > sizeof(buf) - 2) {1319return 1;1320}13211322switch (iph->ip_v) {1323case IPVERSION:1324ret = if_encap4(slirp, ifm, eh, ethaddr);1325if (ret < 2) {1326return ret;1327}1328break;13291330case IP6VERSION:1331ret = if_encap6(slirp, ifm, eh, ethaddr);1332if (ret < 2) {1333return ret;1334}1335break;13361337default:1338g_assert_not_reached();1339}13401341memcpy(eh->h_dest, ethaddr, ETH_ALEN);1342DEBUG_ARG("src = %s", slirp_ether_ntoa(eh->h_source, ethaddr_str,1343sizeof(ethaddr_str)));1344DEBUG_ARG("dst = %s", slirp_ether_ntoa(eh->h_dest, ethaddr_str,1345sizeof(ethaddr_str)));1346memcpy(buf + 2 + sizeof(struct ethhdr), ifm->m_data, ifm->m_len);1347slirp_send_packet_all(slirp, buf + 2, ifm->m_len + ETH_HLEN);1348return 1;1349}13501351/* Drop host forwarding rule, return 0 if found. */1352int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,1353int host_port)1354{1355struct socket *so;1356struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb);1357struct sockaddr_in addr;1358int port = htons(host_port);1359socklen_t addr_len;13601361for (so = head->so_next; so != head; so = so->so_next) {1362addr_len = sizeof(addr);1363if ((so->so_state & SS_HOSTFWD) &&1364getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&1365addr_len == sizeof(addr) &&1366addr.sin_family == AF_INET &&1367addr.sin_addr.s_addr == host_addr.s_addr &&1368addr.sin_port == port) {1369so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);1370closesocket(so->s);1371sofree(so);1372return 0;1373}1374}13751376return -1;1377}13781379int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr,1380int host_port, struct in_addr guest_addr, int guest_port)1381{1382if (!guest_addr.s_addr) {1383guest_addr = slirp->vdhcp_startaddr;1384}1385if (is_udp) {1386if (!udp_listen(slirp, host_addr.s_addr, htons(host_port),1387guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))1388return -1;1389} else {1390if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port),1391guest_addr.s_addr, htons(guest_port), SS_HOSTFWD))1392return -1;1393}1394return 0;1395}13961397int slirp_remove_hostxfwd(Slirp *slirp,1398const struct sockaddr *haddr, socklen_t haddrlen,1399int flags)1400{1401struct socket *so;1402struct socket *head = (flags & SLIRP_HOSTFWD_UDP ? &slirp->udb : &slirp->tcb);1403struct sockaddr_storage addr;1404socklen_t addr_len;14051406for (so = head->so_next; so != head; so = so->so_next) {1407addr_len = sizeof(addr);1408if ((so->so_state & SS_HOSTFWD) &&1409getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 &&1410sockaddr_equal(&addr, (const struct sockaddr_storage *) haddr)) {1411so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque);1412closesocket(so->s);1413sofree(so);1414return 0;1415}1416}14171418return -1;1419}14201421int slirp_add_hostxfwd(Slirp *slirp,1422const struct sockaddr *haddr, socklen_t haddrlen,1423const struct sockaddr *gaddr, socklen_t gaddrlen,1424int flags)1425{1426struct sockaddr_in gdhcp_addr;1427int fwd_flags = SS_HOSTFWD;14281429if (flags & SLIRP_HOSTFWD_V6ONLY)1430fwd_flags |= SS_HOSTFWD_V6ONLY;14311432if (gaddr->sa_family == AF_INET) {1433const struct sockaddr_in *gaddr_in = (const struct sockaddr_in *) gaddr;14341435if (gaddrlen < sizeof(struct sockaddr_in)) {1436errno = EINVAL;1437return -1;1438}14391440if (!gaddr_in->sin_addr.s_addr) {1441gdhcp_addr = *gaddr_in;1442gdhcp_addr.sin_addr = slirp->vdhcp_startaddr;1443gaddr = (struct sockaddr *) &gdhcp_addr;1444gaddrlen = sizeof(gdhcp_addr);1445}1446} else {1447if (gaddrlen < sizeof(struct sockaddr_in6)) {1448errno = EINVAL;1449return -1;1450}14511452/*1453* Libslirp currently only provides a stateless DHCPv6 server, thus1454* we can't translate "addr-any" to the guest here. Instead, we defer1455* performing the translation to when it's needed. See1456* soassign_guest_addr_if_needed().1457*/1458}14591460if (flags & SLIRP_HOSTFWD_UDP) {1461if (!udpx_listen(slirp, haddr, haddrlen,1462gaddr, gaddrlen,1463fwd_flags))1464return -1;1465} else {1466if (!tcpx_listen(slirp, haddr, haddrlen,1467gaddr, gaddrlen,1468fwd_flags))1469return -1;1470}1471return 0;1472}14731474/* TODO: IPv6 */1475static bool check_guestfwd(Slirp *slirp, struct in_addr *guest_addr,1476int guest_port)1477{1478struct gfwd_list *tmp_ptr;14791480if (!guest_addr->s_addr) {1481guest_addr->s_addr = slirp->vnetwork_addr.s_addr |1482(htonl(0x0204) & ~slirp->vnetwork_mask.s_addr);1483}1484if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) !=1485slirp->vnetwork_addr.s_addr ||1486guest_addr->s_addr == slirp->vhost_addr.s_addr ||1487guest_addr->s_addr == slirp->vnameserver_addr.s_addr) {1488return false;1489}14901491/* check if the port is "bound" */1492for (tmp_ptr = slirp->guestfwd_list; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) {1493if (guest_port == tmp_ptr->ex_fport &&1494guest_addr->s_addr == tmp_ptr->ex_addr.s_addr)1495return false;1496}14971498return true;1499}15001501int slirp_add_exec(Slirp *slirp, const char *cmdline,1502struct in_addr *guest_addr, int guest_port)1503{1504if (!check_guestfwd(slirp, guest_addr, guest_port)) {1505return -1;1506}15071508add_exec(&slirp->guestfwd_list, cmdline, *guest_addr, htons(guest_port));1509return 0;1510}15111512int slirp_add_unix(Slirp *slirp, const char *unixsock,1513struct in_addr *guest_addr, int guest_port)1514{1515#ifdef G_OS_UNIX1516if (!check_guestfwd(slirp, guest_addr, guest_port)) {1517return -1;1518}15191520add_unix(&slirp->guestfwd_list, unixsock, *guest_addr, htons(guest_port));1521return 0;1522#else1523g_warn_if_reached();1524return -1;1525#endif1526}15271528int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque,1529struct in_addr *guest_addr, int guest_port)1530{1531if (!check_guestfwd(slirp, guest_addr, guest_port)) {1532return -1;1533}15341535add_guestfwd(&slirp->guestfwd_list, write_cb, opaque, *guest_addr,1536htons(guest_port));1537return 0;1538}15391540int slirp_remove_guestfwd(Slirp *slirp, struct in_addr guest_addr,1541int guest_port)1542{1543return remove_guestfwd(&slirp->guestfwd_list, guest_addr,1544htons(guest_port));1545}15461547slirp_ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags)1548{1549if (so->s == -1 && so->guestfwd) {1550/* XXX this blocks entire thread. Rewrite to use1551* qemu_chr_fe_write and background I/O callbacks */1552so->guestfwd->write_cb(buf, len, so->guestfwd->opaque);1553return len;1554}15551556if (so->s == -1) {1557/*1558* This should in theory not happen but it is hard to be1559* sure because some code paths will end up with so->s == -11560* on a failure but don't dispose of the struct socket.1561* Check specifically, so we don't pass -1 to send().1562*/1563errno = EBADF;1564return -1;1565}15661567return send(so->s, buf, len, flags);1568}15691570struct socket *slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr,1571int guest_port)1572{1573struct socket *so;15741575/* TODO: IPv6 */1576for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) {1577if (so->so_faddr.s_addr == guest_addr.s_addr &&1578htons(so->so_fport) == guest_port) {1579return so;1580}1581}1582return NULL;1583}15841585size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr,1586int guest_port)1587{1588struct iovec iov[2];1589struct socket *so;15901591so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);15921593if (!so || so->so_state & SS_NOFDREF) {1594return 0;1595}15961597if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) {1598/* If the sb is already half full, we will wait for the guest to consume it,1599* and notify again in sbdrop() when the sb becomes less than half full. */1600return 0;1601}16021603return sopreprbuf(so, iov, NULL);1604}16051606void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port,1607const uint8_t *buf, int size)1608{1609int ret;1610struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port);16111612if (!so)1613return;16141615ret = soreadbuf(so, (const char *)buf, size);16161617if (ret > 0)1618tcp_output(sototcpcb(so));1619}16201621void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len)1622{1623slirp_ssize_t ret;16241625if (len < ETH_MINLEN) {1626char tmp[ETH_MINLEN];1627memcpy(tmp, buf, len);1628memset(tmp + len, 0, ETH_MINLEN - len);16291630ret = slirp->cb->send_packet(tmp, ETH_MINLEN, slirp->opaque);1631} else {1632ret = slirp->cb->send_packet(buf, len, slirp->opaque);1633}16341635if (ret < 0) {1636g_critical("Failed to send packet, ret: %ld", (long)ret);1637} else if (ret < len) {1638DEBUG_ERROR("send_packet() didn't send all data: %ld < %lu", (long)ret,1639(unsigned long)len);1640}1641}164216431644