From 2c343fddf2b287ee1a07203791352768cfb39105 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Mon, 24 Sep 2018 19:06:14 +0200 Subject: [PATCH] modem: Transparent IP proxy. Removes the need for a TAP/TUN device --- core/deps/picotcp/include/pico_defines.h | 1 - core/deps/picotcp/include/pico_device.h | 1 + core/deps/picotcp/include/pico_frame.h | 3 + core/deps/picotcp/include/pico_socket.h | 2 + core/deps/picotcp/modules/pico_dev_tap.c | 231 -- core/deps/picotcp/modules/pico_dev_tap.h | 16 - .../picotcp/modules/pico_dev_tap_windows.c | 1111 ----- .../picotcp/modules/pico_dev_tap_windows.h | 20 - .../modules/pico_dev_tap_windows_private.h | 89 - core/deps/picotcp/modules/pico_dev_tun.c | 218 - core/deps/picotcp/modules/pico_dev_tun.h | 17 - core/deps/picotcp/modules/pico_dhcp_client.c | 1074 ----- core/deps/picotcp/modules/pico_dhcp_common.c | 190 - core/deps/picotcp/modules/pico_icmp4.c | 434 -- core/deps/picotcp/modules/pico_ipv4.c | 10 +- core/deps/picotcp/modules/pico_mdns.c | 3687 ----------------- core/deps/picotcp/modules/pico_mdns.h | 206 - core/deps/picotcp/modules/pico_tcp.c | 23 +- core/deps/picotcp/modules/pico_tcp.h | 1 + core/deps/picotcp/modules/pico_udp.c | 7 +- core/deps/picotcp/stack/pico_frame.c | 27 +- core/deps/picotcp/stack/pico_socket.c | 147 +- core/hw/modem/dns.cpp | 151 + core/hw/modem/modem.cpp | 5 +- core/hw/modem/picoppp.cpp | 819 +++- core/hw/modem/picoppp.h | 21 + core/hw/modem/pppd.cpp | 257 -- core/hw/modem/pppd.h | 34 - 28 files changed, 936 insertions(+), 7866 deletions(-) delete mode 100644 core/deps/picotcp/modules/pico_dev_tap.c delete mode 100644 core/deps/picotcp/modules/pico_dev_tap.h delete mode 100644 core/deps/picotcp/modules/pico_dev_tap_windows.c delete mode 100644 core/deps/picotcp/modules/pico_dev_tap_windows.h delete mode 100644 core/deps/picotcp/modules/pico_dev_tap_windows_private.h delete mode 100644 core/deps/picotcp/modules/pico_dev_tun.c delete mode 100644 core/deps/picotcp/modules/pico_dev_tun.h delete mode 100644 core/deps/picotcp/modules/pico_dhcp_client.c delete mode 100644 core/deps/picotcp/modules/pico_dhcp_common.c delete mode 100644 core/deps/picotcp/modules/pico_icmp4.c delete mode 100644 core/deps/picotcp/modules/pico_mdns.c delete mode 100644 core/deps/picotcp/modules/pico_mdns.h create mode 100644 core/hw/modem/dns.cpp delete mode 100644 core/hw/modem/pppd.cpp delete mode 100644 core/hw/modem/pppd.h diff --git a/core/deps/picotcp/include/pico_defines.h b/core/deps/picotcp/include/pico_defines.h index 2d2e740fe..d27d83002 100644 --- a/core/deps/picotcp/include/pico_defines.h +++ b/core/deps/picotcp/include/pico_defines.h @@ -4,7 +4,6 @@ #define PICO_SUPPORT_ETH #define PICO_SUPPORT_IPV4 #define PICO_SUPPORT_IPV4FRAG -#define PICO_SUPPORT_ICMP4 #define PICO_SUPPORT_TCP #define PICO_SUPPORT_UDP #define PICO_SUPPORT_DNS_CLIENT diff --git a/core/deps/picotcp/include/pico_device.h b/core/deps/picotcp/include/pico_device.h index 4c6d9a7e0..2b1305323 100644 --- a/core/deps/picotcp/include/pico_device.h +++ b/core/deps/picotcp/include/pico_device.h @@ -38,6 +38,7 @@ struct pico_device { #ifdef PICO_SUPPORT_IPV6 struct pico_nd_hostvars hostvars; #endif + int proxied; }; diff --git a/core/deps/picotcp/include/pico_frame.h b/core/deps/picotcp/include/pico_frame.h index 125785a6b..aa159f031 100644 --- a/core/deps/picotcp/include/pico_frame.h +++ b/core/deps/picotcp/include/pico_frame.h @@ -92,6 +92,9 @@ struct pico_frame { uint8_t send_ttl; /* Special TTL/HOPS value, 0 = auto assign */ uint8_t send_tos; /* Type of service */ + + struct pico_ip4 local_ip; /* address to masquerade */ + uint16_t local_port; /* port to masquerade */ }; /** frame alloc/dealloc/copy **/ diff --git a/core/deps/picotcp/include/pico_socket.h b/core/deps/picotcp/include/pico_socket.h index f5f50e8db..9162d6cd1 100644 --- a/core/deps/picotcp/include/pico_socket.h +++ b/core/deps/picotcp/include/pico_socket.h @@ -175,6 +175,8 @@ struct pico_msginfo { struct pico_device *dev; uint8_t ttl; uint8_t tos; + union pico_address local_addr; + uint16_t local_port; }; struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s)); diff --git a/core/deps/picotcp/modules/pico_dev_tap.c b/core/deps/picotcp/modules/pico_dev_tap.c deleted file mode 100644 index 42f835002..000000000 --- a/core/deps/picotcp/modules/pico_dev_tap.c +++ /dev/null @@ -1,231 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - Authors: Daniele Lacamera - *********************************************************************/ - -#ifndef _WIN32 -#include -#include -#include -#include -#include "pico_device.h" -#include "pico_dev_tap.h" -#include "pico_stack.h" - -#ifndef __FreeBSD__ -#include -#endif - -#include - -struct pico_device_tap { - struct pico_device dev; - int fd; -}; - -#define TUN_MTU 2048 - -/* We only support one global link state - we only have two USR signals, we */ -/* can't spread these out over an arbitrary amount of devices. When you unplug */ -/* one tap, you unplug all of them. */ - -static int tapdev_link_state = 0; - -static void sig_handler(int signo) -{ - if (signo == SIGUSR1) { - tapdev_link_state = 0; - } - - if (signo == SIGUSR2) { - tapdev_link_state = 1; - } -} - -static int tap_link_state(__attribute__((unused)) struct pico_device *self) -{ - return tapdev_link_state; -} - - -static int pico_tap_send(struct pico_device *dev, void *buf, int len) -{ - struct pico_device_tap *tap = (struct pico_device_tap *) dev; - return (int)write(tap->fd, buf, (uint32_t)len); -} - -static int pico_tap_poll(struct pico_device *dev, int loop_score) -{ - struct pico_device_tap *tap = (struct pico_device_tap *) dev; - struct pollfd pfd; - unsigned char buf[TUN_MTU]; - int len; - pfd.fd = tap->fd; - pfd.events = POLLIN; - do { - if (poll(&pfd, 1, 0) <= 0) { - return loop_score; - } - - len = (int)read(tap->fd, buf, TUN_MTU); - if (len > 0) { - loop_score--; - pico_stack_recv(dev, buf, (uint32_t)len); - } - } while(loop_score > 0); - return 0; -} - -/* Public interface: create/destroy. */ - -void pico_tap_destroy(struct pico_device *dev) -{ - struct pico_device_tap *tap = (struct pico_device_tap *) dev; - if(tap->fd > 0) { - close(tap->fd); - } -} - -#ifndef __FreeBSD__ -static int tap_open(const char *name) -{ - struct ifreq ifr; - int tap_fd; - if((tap_fd = open("/dev/net/tun", O_RDWR)) < 0) { - return -1; - } - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; - strncpy(ifr.ifr_name, name, IFNAMSIZ); - if(ioctl(tap_fd, TUNSETIFF, &ifr) < 0) { - return -1; - } - - return tap_fd; -} -#else -static int tap_open(const char *name) -{ - int tap_fd; - (void)name; - tap_fd = open("/dev/tap0", O_RDWR); - return tap_fd; -} -#endif - - -#ifndef __FreeBSD__ -static int tap_get_mac(const char *name, uint8_t *mac) -{ - int sck; - struct ifreq eth; - int retval = -1; - - sck = socket(AF_INET, SOCK_DGRAM, 0); - if(sck < 0) { - return retval; - } - - memset(ð, 0, sizeof(struct ifreq)); - strcpy(eth.ifr_name, name); - /* call the IOCTL */ - if (ioctl(sck, SIOCGIFHWADDR, ð) < 0) { - perror("ioctl(SIOCGIFHWADDR)"); - return -1; - } - - memcpy (mac, ð.ifr_hwaddr.sa_data, 6); - close(sck); - return 0; - -} -#else -#include -#include -#include -static int tap_get_mac(const char *name, uint8_t *mac) -{ - struct sockaddr_dl *sdl; - struct ifaddrs *ifap, *root; - if (getifaddrs(&ifap) != 0) - return -1; - - root = ifap; - while(ifap) { - if (strcmp(name, ifap->ifa_name) == 0) { - sdl = (struct sockaddr_dl *) ifap->ifa_addr; - } - - if (sdl->sdl_type == IFT_ETHER) { - memcpy(mac, LLADDR(sdl), 6); - freeifaddrs(root); - return 0; - } - - ifap = ifap->ifa_next; - } - freeifaddrs(root); - return 0; -} -#endif - -struct pico_device *pico_tap_create(const char *name) -{ - struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap)); - uint8_t mac[6] = {}; - struct sigaction sa; - - if (!tap) { - return NULL; - } - - sa.sa_flags = 0; - sigemptyset(&sa.sa_mask); - sa.sa_handler = sig_handler; - - if ((sigaction(SIGUSR1, &sa, NULL) == 0) && - (sigaction(SIGUSR2, &sa, NULL) == 0)) { - tap->dev.link_state = &tap_link_state; - } - - tap->dev.overhead = 0; - tap->fd = tap_open(name); - if (tap->fd < 0) { - dbg("Tap creation failed.\n"); - pico_tap_destroy((struct pico_device *)tap); - return NULL; - } - - /* Host's mac address is generated * by the host kernel and is - * retrieved via tap_get_mac(). - */ - if (tap_get_mac(name, mac) < 0) { - dbg("Tap mac query failed.\n"); - pico_tap_destroy((struct pico_device *)tap); - return NULL; - } - - /* To act as a second endpoint in the same subnet, the picoTCP - * app using the tap device must have a different mac address. - * For simplicity, we just add 1 to the last byte of the linux - * endpoint so the two addresses are consecutive. - */ - mac[5]++; - - if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) { - dbg("Tap init failed.\n"); - pico_tap_destroy((struct pico_device *)tap); - return NULL; - } - - tap->dev.send = pico_tap_send; - tap->dev.poll = pico_tap_poll; - tap->dev.destroy = pico_tap_destroy; - dbg("Device %s created.\n", tap->dev.name); - return (struct pico_device *)tap; -} - -#endif diff --git a/core/deps/picotcp/modules/pico_dev_tap.h b/core/deps/picotcp/modules/pico_dev_tap.h deleted file mode 100644 index 01c41f344..000000000 --- a/core/deps/picotcp/modules/pico_dev_tap.h +++ /dev/null @@ -1,16 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - *********************************************************************/ -#ifndef _WIN32 -#ifndef INCLUDE_PICO_TAP -#define INCLUDE_PICO_TAP -#include "pico_config.h" -#include "pico_device.h" - -void pico_tap_destroy(struct pico_device *tap); -struct pico_device *pico_tap_create(const char *name); - -#endif -#endif diff --git a/core/deps/picotcp/modules/pico_dev_tap_windows.c b/core/deps/picotcp/modules/pico_dev_tap_windows.c deleted file mode 100644 index e9cec50c3..000000000 --- a/core/deps/picotcp/modules/pico_dev_tap_windows.c +++ /dev/null @@ -1,1111 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - Authors: Maxime Vincent - Based on the OpenVPN tun.c driver, under GPL - - NOTES: This is the Windows-only driver, a Linux-equivalent is available, too - You need to have an OpenVPN TUN/TAP network adapter installed, first - This driver is barely working: - * Only TAP-mode is supported (TUN is not) - * it will simply open the first TAP device it can find - * there is memory being allocated that's never freed - * there is no destroy function, yet - * it has only been tested on a Windows 7 machine - *********************************************************************/ -#ifdef _WIN32 -#include "pico_device.h" -#include "pico_dev_null.h" -#include "pico_stack.h" -#include "pico_dev_tap_windows.h" - -#include -#include -#include -#include "pico_dev_tap_windows_private.h" - -#define DEBUG_TAP_INFO - -/* - * Debugging info - */ -#ifdef DEBUG_TAP_GENERAL -#define dbg_tap dbg /* first level debug */ -#else -#define dbg_tap(...) do{} while(0) -#endif - -#ifdef DEBUG_TAP_INFO -#define dbg_tap_info dbg /* tap info messages */ -#else -#define dbg_tap_info(...) do{} while(0) -#endif - -#ifdef DEBUG_TAP_WIN -#define dbg_tap_win32 dbg /* second level detailed win32 debug */ -#else -#define dbg_tap_win32(...) do{} while(0) -#endif - -#ifdef DEBUG_TAP_REG -#define dbg_tap_reg dbg /* third level: registry debug */ -#else -#define dbg_tap_reg(...) do{} while(0) -#endif - -/* - * Tunnel types - */ -#define DEV_TYPE_UNDEF 0 -#define DEV_TYPE_NULL 1 -#define DEV_TYPE_TUN 2 /* point-to-point IP tunnel */ -#define DEV_TYPE_TAP 3 /* ethernet (802.3) tunnel */ - - -/* - * We try to do all Win32 I/O using overlapped - * (i.e. asynchronous) I/O for a performance win. - */ -struct overlapped_io { -# define IOSTATE_INITIAL 0 -# define IOSTATE_QUEUED 1 /* overlapped I/O has been queued */ -# define IOSTATE_IMMEDIATE_RETURN 2 /* I/O function returned immediately without queueing */ - int iostate; - OVERLAPPED overlapped; - DWORD size; - DWORD flags; - int status; - int addr_defined; - uint8_t *buf_init; - uint32_t buf_init_len; - uint8_t *buf; - uint32_t buf_len; -}; - -struct rw_handle { - HANDLE read; - HANDLE write; -}; - -struct tuntap -{ - int type; /* DEV_TYPE_x as defined in proto.h */ - int ipv6; - int persistent_if; /* if existed before, keep on program end */ - char *actual_name; /* actual name of TUN/TAP dev, usually including unit number */ - int post_open_mtu; - uint8_t mac[6]; - - /* Windows stuff */ - //DWORD adapter_index; /*adapter index for TAP-Windows adapter, ~0 if undefined */ - const char *guid; - HANDLE hand; - struct overlapped_io reads; /* for overlapped IO */ - struct overlapped_io writes; - struct rw_handle rw_handle; - -}; - - -struct pico_device_tap { - struct pico_device dev; - int statistics_frames_out; - struct tuntap *tt; -}; - - -/* - * Private function prototypes - */ -const struct tap_reg *get_tap_reg (void); -const struct panel_reg *get_panel_reg (void); - - -/* - * Private functions - */ - -/* Get TAP info from Windows registry */ -const struct tap_reg *get_tap_reg (void) -{ - HKEY adapter_key; - LONG status; - DWORD len; - struct tap_reg *first = NULL; - struct tap_reg *last = NULL; - int i = 0; - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - ADAPTER_KEY, - 0, - KEY_READ, - &adapter_key); - - if (status != ERROR_SUCCESS) - { - dbg_tap_reg("Error opening registry key: %s\n", ADAPTER_KEY); - return NULL; - } - - while (1) - { - char enum_name[256]; - char unit_string[256]; - HKEY unit_key; - char component_id_string[] = "ComponentId"; - char component_id[256]; - char net_cfg_instance_id_string[] = "NetCfgInstanceId"; - char net_cfg_instance_id[256]; - DWORD data_type; - - len = sizeof (enum_name); - status = RegEnumKeyEx( - adapter_key, - i, - enum_name, - &len, - NULL, - NULL, - NULL, - NULL); - if (status == ERROR_NO_MORE_ITEMS) - break; - else if (status != ERROR_SUCCESS) - dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", ADAPTER_KEY); - - snprintf (unit_string, sizeof(unit_string), "%s\\%s", - ADAPTER_KEY, enum_name); - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - unit_string, - 0, - KEY_READ, - &unit_key); - - if (status != ERROR_SUCCESS) - { - dbg_tap_reg("Error opening registry key: %s\n", unit_string); - } - else - { - len = sizeof (component_id); - status = RegQueryValueEx( - unit_key, - component_id_string, - NULL, - &data_type, - (LPBYTE)component_id, - &len); - - if (status != ERROR_SUCCESS || data_type != REG_SZ) - { - dbg_tap_reg("Error opening registry key: %s\\%s\n", unit_string, component_id_string); - } - else - { - len = sizeof (net_cfg_instance_id); - status = RegQueryValueEx( - unit_key, - net_cfg_instance_id_string, - NULL, - &data_type, - (LPBYTE)net_cfg_instance_id, - &len); - - if (status == ERROR_SUCCESS && data_type == REG_SZ) - { - if (!strcmp (component_id, TAP_WIN_COMPONENT_ID)) - { - struct tap_reg *reg; - reg = PICO_ZALLOC(sizeof(struct tap_reg)); - /* ALLOC_OBJ_CLEAR_GC (reg, struct tap_reg, gc); */ - if (!reg) - return NULL; - - /* reg->guid = string_alloc (net_cfg_instance_id, gc); */ - reg->guid = PICO_ZALLOC (strlen(net_cfg_instance_id) + 1); - if (!(reg->guid)) - { - PICO_FREE(reg); - return NULL; - } - - strcpy((char *)reg->guid, net_cfg_instance_id); - /* link into return list */ - if (!first) - first = reg; - - if (last) - last->next = reg; - - last = reg; - } - } - } - - RegCloseKey (unit_key); - } - - ++i; - } - RegCloseKey (adapter_key); - return first; -} - -/* Get Panel info from Windows registry */ -const struct panel_reg *get_panel_reg (void) -{ - LONG status; - HKEY network_connections_key; - DWORD len; - struct panel_reg *first = NULL; - struct panel_reg *last = NULL; - int i = 0; - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - NETWORK_CONNECTIONS_KEY, - 0, - KEY_READ, - &network_connections_key); - - if (status != ERROR_SUCCESS) - { - dbg_tap_reg("Error opening registry key: %s\n", NETWORK_CONNECTIONS_KEY); - return NULL; - } - - while (1) - { - char enum_name[256]; - char connection_string[256]; - HKEY connection_key; - WCHAR name_data[256]; - DWORD name_type; - const WCHAR name_string[] = L"Name"; - - len = sizeof (enum_name); - status = RegEnumKeyEx( - network_connections_key, - i, - enum_name, - &len, - NULL, - NULL, - NULL, - NULL); - if (status == ERROR_NO_MORE_ITEMS) - break; - else if (status != ERROR_SUCCESS) - dbg_tap_reg("Error enumerating registry subkeys of key: %s.\n", NETWORK_CONNECTIONS_KEY); - - snprintf (connection_string, sizeof(connection_string), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, enum_name); - - status = RegOpenKeyEx( - HKEY_LOCAL_MACHINE, - connection_string, - 0, - KEY_READ, - &connection_key); - if (status != ERROR_SUCCESS) - dbg_tap_reg("Error opening registry key: %s\n", connection_string); - else - { - len = sizeof (name_data); - status = RegQueryValueExW( - connection_key, - name_string, - NULL, - &name_type, - (LPBYTE) name_data, - &len); - - if (status != ERROR_SUCCESS || name_type != REG_SZ) - dbg_tap_reg("Error opening registry key: %s\\%s\\%S\n", NETWORK_CONNECTIONS_KEY, connection_string, name_string); - else - { - int n; - LPSTR name; - struct panel_reg *reg; - - /* ALLOC_OBJ_CLEAR_GC (reg, struct panel_reg, gc); */ - reg = PICO_ZALLOC(sizeof(struct panel_reg)); - if (!reg) - return NULL; - - n = WideCharToMultiByte (CP_UTF8, 0, name_data, -1, NULL, 0, NULL, NULL); - /* name = gc_malloc (n, false, gc); */ - name = PICO_ZALLOC(n); - if (!name) - { - PICO_FREE(reg); - return NULL; - } - - WideCharToMultiByte (CP_UTF8, 0, name_data, -1, name, n, NULL, NULL); - reg->name = name; - /* reg->guid = string_alloc (enum_name, gc); */ - reg->guid = PICO_ZALLOC(strlen(enum_name) + 1); - if (!reg->guid) - { - PICO_FREE((void *)reg->name); - PICO_FREE((void *)reg); - return NULL; - } - - strcpy((char *)reg->guid, enum_name); - - /* link into return list */ - if (!first) - first = reg; - - if (last) - last->next = reg; - - last = reg; - } - - RegCloseKey (connection_key); - } - - ++i; - } - RegCloseKey (network_connections_key); - - return first; -} - - -void show_tap_win_adapters (void) -{ - int warn_panel_null = 0; - int warn_panel_dup = 0; - int warn_tap_dup = 0; - - int links; - - const struct tap_reg *tr; - const struct tap_reg *tr1; - const struct panel_reg *pr; - - const struct tap_reg *tap_reg = get_tap_reg (); - const struct panel_reg *panel_reg = get_panel_reg (); - - if (!(tap_reg && panel_reg)) - return; - - dbg_tap_info("Available TAP-WIN32 adapters [name, GUID]:\n"); - - /* loop through each TAP-Windows adapter registry entry */ - for (tr = tap_reg; tr != NULL; tr = tr->next) - { - links = 0; - - /* loop through each network connections entry in the control panel */ - for (pr = panel_reg; pr != NULL; pr = pr->next) - { - if (!strcmp (tr->guid, pr->guid)) - { - dbg_tap_info("\t>> '%s' %s\n", pr->name, tr->guid); - ++links; - } - } - if (links > 1) - { - warn_panel_dup = 1; - } - else if (links == 0) - { - /* a TAP adapter exists without a link from the network - connections control panel */ - warn_panel_null = 1; - dbg_tap_info("\t>> [NULL] %s\n", tr->guid); - } - } - /* check for TAP-Windows adapter duplicated GUIDs */ - for (tr = tap_reg; tr != NULL; tr = tr->next) - { - for (tr1 = tap_reg; tr1 != NULL; tr1 = tr1->next) - { - if (tr != tr1 && !strcmp (tr->guid, tr1->guid)) - warn_tap_dup = 1; - } - } - /* warn on registry inconsistencies */ - if (warn_tap_dup) - dbg_tap_info("WARNING: Some TAP-Windows adapters have duplicate GUIDs\n"); - - if (warn_panel_dup) - dbg_tap_info("WARNING: Some TAP-Windows adapters have duplicate links from the Network Connections control panel\n"); - - if (warn_panel_null) - dbg_tap_info("WARNING: Some TAP-Windows adapters have no link from the Network Connections control panel\n"); -} - - -/* Get the GUID of the first TAP device found */ -const char *get_first_device_guid(const struct tap_reg *tap_reg, const struct panel_reg *panel_reg, char *name) -{ - const struct tap_reg *tr; - const struct panel_reg *pr; - /* loop through each TAP-Windows adapter registry entry */ - for (tr = tap_reg; tr != NULL; tr = tr->next) - { - /* loop through each network connections entry in the control panel */ - for (pr = panel_reg; pr != NULL; pr = pr->next) - { - if (!strcmp (tr->guid, pr->guid)) - { - dbg_tap_info("Using first TAP device: '%s' %s\n", pr->name, tr->guid); - if (name) - strcpy(name, pr->name); - - return tr->guid; - } - } - } - return NULL; -} - - - -int open_tun (const char *dev, const char *dev_type, const char *dev_node, struct tuntap *tt) -{ - char device_path[256]; - const char *device_guid = NULL; - DWORD len; - - dbg_tap_info("open_tun, tt->ipv6=%d\n", tt->ipv6 ); - - if (!(tt->type == DEV_TYPE_TAP || tt->type == DEV_TYPE_TUN)) - { - dbg_tap_info("Unknown virtual device type: '%s'\n", dev); - return -1; - } - - /* - * Lookup the device name in the registry, using the --dev-node high level name. - */ - { - const struct tap_reg *tap_reg = get_tap_reg(); - const struct panel_reg *panel_reg = get_panel_reg(); - char name[256]; - - if (!(tap_reg && panel_reg)) - { - dbg_tap_info("No TUN/TAP devices found\n"); - return -1; - } - - /* Get the device GUID for the device specified with --dev-node. */ - device_guid = get_first_device_guid (tap_reg, panel_reg, name); - - if (!device_guid) - dbg_tap_info("TAP-Windows adapter '%s' not found\n", dev_node); - - /* Open Windows TAP-Windows adapter */ - snprintf (device_path, sizeof(device_path), "%s%s%s", - USERMODEDEVICEDIR, - device_guid, - TAP_WIN_SUFFIX); - - tt->hand = CreateFile ( - device_path, - GENERIC_READ | GENERIC_WRITE, - 0, /* was: FILE_SHARE_READ */ - 0, - OPEN_EXISTING, - FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, - 0 - ); - - if (tt->hand == INVALID_HANDLE_VALUE) - dbg_tap_info("CreateFile failed on TAP device: %s error %d\n", device_path, GetLastError()); - - /* translate high-level device name into a device instance - GUID using the registry */ - tt->actual_name = PICO_ZALLOC(strlen(name) + 1); - if (tt->actual_name) - strcpy(tt->actual_name, name); - tt->guid = device_guid; - } - - dbg_tap_info("TAP-WIN32 device [%s] opened: %s\n", tt->actual_name, device_path); - /* TODO TODO TODO */ - /* tt->adapter_index = get_adapter_index (device_guid); */ - - /* get driver version info */ - { - ULONG info[3]; - /* TODO TODO TODO */ - /* CLEAR (info); */ - if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_VERSION, &info, sizeof (info), &info, sizeof (info), &len, NULL)) - { - dbg_tap_info ("TAP-Windows Driver Version %d.%d %s\n", - (int) info[0], - (int) info[1], - (info[2] ? "(DEBUG)" : "")); - - } - - if (!(info[0] == TAP_WIN_MIN_MAJOR && info[1] >= TAP_WIN_MIN_MINOR)) - dbg_tap_info ("ERROR: This version of " PACKAGE_NAME " requires a TAP-Windows driver that is at least version %d.%d \ - -- If you recently upgraded your " PACKAGE_NAME " distribution, \ - a reboot is probably required at this point to get Windows to see the new driver.\n", - TAP_WIN_MIN_MAJOR, - TAP_WIN_MIN_MINOR); - - /* usage of numeric constants is ugly, but this is really tied to - * *this* version of the driver - */ - if ( tt->ipv6 && tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] < 8) - { - dbg_tap_info("WARNING: Tap-Win32 driver version %d.%d does not support IPv6 in TUN mode. IPv6 will be disabled. \ - Upgrade to Tap-Win32 9.8 (2.2-beta3 release or later) or use TAP mode to get IPv6\n", (int) info[0], (int) info[1] ); - tt->ipv6 = 0; - } - - /* tap driver 9.8 (2.2.0 and 2.2.1 release) is buggy - */ - if ( tt->type == DEV_TYPE_TUN && info[0] == 9 && info[1] == 8) - { - dbg_tap_info("ERROR: Tap-Win32 driver version %d.%d is buggy regarding small IPv4 packets in TUN mode. Upgrade to Tap-Win32 9.9 (2.2.2 release or later) or use TAP mode\n", (int) info[0], (int) info[1] ); - } - } - - /* get driver MTU */ - { - ULONG mtu; - if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_MTU, - &mtu, sizeof (mtu), - &mtu, sizeof (mtu), &len, NULL)) - { - tt->post_open_mtu = (int) mtu; - dbg_tap_info("TAP-Windows MTU=%d\n", (int) mtu); - } - } - - - /* get driver MAC */ - { - uint8_t mac[6] = { - 0, 0, 0, 0, 0, 0 - }; - if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_MAC, - mac, sizeof (mac), - mac, sizeof (mac), &len, NULL)) - { - dbg_tap_info("TAP-Windows MAC=[%x,%x,%x,%x,%x,%x]\n", mac[0], mac[1], mac[2], - mac[2], mac[4], mac[5]); - memcpy(tt->mac, mac, sizeof(mac)); - } - } - - /* set point-to-point mode if TUN device */ - - if (tt->type == DEV_TYPE_TUN) - { - dbg_tap_info("TUN type not supported for now...\n"); - // TODO: Set Point-to-point through DeviceIoControl TAP_WIN_IOCTL_CONFIG_TUN - return -1; - } - else if (tt->type == DEV_TYPE_TAP) - { /* TAP DEVICE */ - } - - /* set driver media status to 'connected' */ - { - ULONG status = TRUE; - if (!DeviceIoControl (tt->hand, TAP_WIN_IOCTL_SET_MEDIA_STATUS, - &status, sizeof (status), - &status, sizeof (status), &len, NULL)) - dbg_tap_info("WARNING: The TAP-Windows driver rejected a TAP_WIN_IOCTL_SET_MEDIA_STATUS DeviceIoControl call."); - } - - return 0; -} - - - - -/* TODO: Closing a TUN device is currently not implemented */ -/* - void close_tun (struct tuntap *tt) - { - (void)tt; - } - */ - - -int tap_win_getinfo (const struct tuntap *tt, char *buf, int bufsize) -{ - if (tt && tt->hand != NULL && buf != NULL) - { - DWORD len; - if (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_INFO, - buf, bufsize, - buf, bufsize, - &len, NULL)) - { - return 0; - } - } - - return -1; -} - -void tun_show_debug (struct tuntap *tt, char *buf, int bufsize) -{ - if (tt && tt->hand != NULL && buf != NULL) - { - DWORD len; - while (DeviceIoControl (tt->hand, TAP_WIN_IOCTL_GET_LOG_LINE, - buf, bufsize, - buf, bufsize, - &len, NULL)) - { - dbg_tap_info("TAP-Windows: %s\n", buf); - } - } -} - - -/* returns the state */ -int tun_read_queue (struct tuntap *tt, uint8_t *buffer, int maxsize) -{ - if (tt->reads.iostate == IOSTATE_INITIAL) - { - DWORD len = 1500; - BOOL status; - int err; - - /* reset buf to its initial state */ - tt->reads.buf = tt->reads.buf_init; - tt->reads.buf_len = tt->reads.buf_init_len; - - len = maxsize ? maxsize : (tt->reads.buf_len); - if (len > (tt->reads.buf_len)) /* clip to buffer len */ - len = tt->reads.buf_len; - - /* the overlapped read will signal this event on I/O completion */ - if (!ResetEvent (tt->reads.overlapped.hEvent)) - dbg_tap_info("ResetEvent failed\n"); - - status = ReadFile( - tt->hand, - buffer, - len, - &tt->reads.size, - &tt->reads.overlapped - ); - - if (status) /* operation completed immediately? */ - { - /* since we got an immediate return, we must signal the event object ourselves */ - /* ASSERT (SetEvent (tt->reads.overlapped.hEvent)); */ - if (!SetEvent (tt->reads.overlapped.hEvent)) - dbg_tap_info("SetEvent failed\n"); - - tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN; - tt->reads.status = 0; - - dbg_tap_win32 ("WIN32 I/O: TAP Read immediate return [%d,%d]\n", - (int) len, - (int) tt->reads.size); - } - else - { - err = GetLastError (); - if (err == ERROR_IO_PENDING) /* operation queued? */ - { - tt->reads.iostate = IOSTATE_QUEUED; - tt->reads.status = err; - dbg_tap_win32 ("WIN32 I/O: TAP Read queued [%d]\n", (int) len); - } - else /* error occurred */ - { - if (!SetEvent (tt->reads.overlapped.hEvent)) - dbg_tap_info("SetEvent failed\n"); - - tt->reads.iostate = IOSTATE_IMMEDIATE_RETURN; - tt->reads.status = err; - dbg_tap_info("WIN32 I/O: TAP Read error [%d] : %d\n", (int) len, (int) err); - } - } - } - - return tt->reads.iostate; -} - -/* Finalize any pending overlapped IO's */ -int tun_finalize(HANDLE h, struct overlapped_io *io, uint8_t **buf, uint32_t *buf_len) -{ - int ret = -1; - BOOL status; - - switch (io->iostate) - { - case IOSTATE_QUEUED: - status = GetOverlappedResult( - h, - &io->overlapped, - &io->size, - 0u - ); - if (status) - { - /* successful return for a queued operation */ - if (buf) - { - *buf = io->buf; - *buf_len = io->buf_len; - } - - ret = io->size; - io->iostate = IOSTATE_INITIAL; - - if (!ResetEvent (io->overlapped.hEvent)) - dbg_tap_info("ResetEvent in finalize failed!\n"); - - dbg_tap_win32 ("WIN32 I/O: TAP Completion success: QUEUED! [%d]\n", ret); - } - else - { - /* error during a queued operation */ - /* error, or just not completed? */ - ret = 0; - int last_error = GetLastError(); - if (last_error != ERROR_IO_INCOMPLETE) - { - /* if no error (i.e. just not finished yet), - then DON'T execute this code */ - io->iostate = IOSTATE_INITIAL; - if (!ResetEvent (io->overlapped.hEvent)) - dbg_tap_info("ResetEvent in finalize failed!\n"); - - dbg_tap_info("WIN32 I/O: TAP Completion error %d\n", last_error); - ret = -1; /* There actually was an error */ - } - } - - break; - - case IOSTATE_IMMEDIATE_RETURN: - io->iostate = IOSTATE_INITIAL; - if (!ResetEvent (io->overlapped.hEvent)) - dbg_tap_info("ResetEvent in finalize failed!\n"); - - if (io->status) - { - /* error return for a non-queued operation */ - SetLastError (io->status); - ret = -1; - dbg_tap_info("WIN32 I/O: TAP Completion non-queued error\n"); - } - else - { - /* successful return for a non-queued operation */ - if (buf) - *buf = io->buf; - - ret = io->size; - dbg_tap_win32 ("WIN32 I/O: TAP Completion non-queued success [%d]\n", ret); - } - - break; - - case IOSTATE_INITIAL: /* were we called without proper queueing? */ - SetLastError (ERROR_INVALID_FUNCTION); - ret = -1; - dbg_tap_info("WIN32 I/O: TAP Completion BAD STATE\n"); - break; - - default: - dbg_tap_info("Some weird case happened..\n"); - } - - if (buf) - *buf_len = ret; - - return ret; -} - - - -/* returns the amount of bytes written */ -int tun_write_queue (struct tuntap *tt, uint8_t *buf, uint32_t buf_len) -{ - if (tt->writes.iostate == IOSTATE_INITIAL) - { - BOOL status; - int err; - - /* make a private copy of buf */ - tt->writes.buf = tt->writes.buf_init; - tt->writes.buf_len = buf_len; - memcpy(tt->writes.buf, buf, buf_len); - - /* the overlapped write will signal this event on I/O completion */ - if (!ResetEvent (tt->writes.overlapped.hEvent)) - dbg_tap_info("ResetEvent in write_queue failed!\n"); - - status = WriteFile( - tt->hand, - tt->writes.buf, - tt->writes.buf_len, - &tt->writes.size, - &tt->writes.overlapped - ); - - if (status) /* operation completed immediately? */ - { - tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN; - - /* since we got an immediate return, we must signal the event object ourselves */ - if (!SetEvent (tt->writes.overlapped.hEvent)) - dbg_tap_info("SetEvent in write_queue failed!\n"); - - tt->writes.status = 0; - - dbg_tap_win32 ("WIN32 I/O: TAP Write immediate return [%d,%d]\n", - (int)(tt->writes.buf_len), - (int)tt->writes.size); - } - else - { - err = GetLastError (); - if (err == ERROR_IO_PENDING) /* operation queued? */ - { - tt->writes.iostate = IOSTATE_QUEUED; - tt->writes.status = err; - dbg_tap_win32("WIN32 I/O: TAP Write queued [%d]\n", - (tt->writes.buf_len)); - } - else /* error occurred */ - { - if (!SetEvent (tt->writes.overlapped.hEvent)) - dbg_tap_info("SetEvent in write_queue failed!\n"); - - tt->writes.iostate = IOSTATE_IMMEDIATE_RETURN; - tt->writes.status = err; - dbg_tap ("WIN32 I/O: TAP Write error [%d] : %d\n", (int) &tt->writes.buf_len, (int) err); - } - } - } - - return tt->writes.iostate; -} - -static inline int overlapped_io_active (struct overlapped_io *o) -{ - return o->iostate == IOSTATE_QUEUED || o->iostate == IOSTATE_IMMEDIATE_RETURN; -} - -/* if >= 0: returns the amount of bytes read, otherwise error! */ -static int tun_write_win32 (struct tuntap *tt, uint8_t *buf, uint32_t buf_len) -{ - int err = 0; - int status = 0; - if (overlapped_io_active (&tt->writes)) - { - status = tun_finalize (tt->hand, &tt->writes, NULL, 0); - if (status == 0) - { - /* busy, just wait, do not schedule a new write */ - return 0; - } - - if (status < 0) - err = GetLastError (); - } - - /* the overlapped IO is done, now we can schedule a new write */ - tun_write_queue (tt, buf, buf_len); - if (status < 0) - { - SetLastError (err); - return status; - } - else - return buf_len; -} - - -/* if >= 0: returns the amount of bytes read, otherwise error! */ -static int tun_read_win32 (struct tuntap *tt, uint8_t *buf, uint32_t buf_len) -{ - int err = 0; - int status = 0; - - - /* First, finish possible pending IOs */ - if (overlapped_io_active (&tt->reads)) - { - status = tun_finalize (tt->hand, &tt->reads, &buf, &buf_len); - if (status == 0) - { - /* busy, just wait, do not schedule a new read */ - return 0; - } - - if (status < 0) - { - dbg_tap ("tun_finalize status < 0: %d\n", status); - err = GetLastError (); - } - - if (status > 0) - { - return buf_len; - } - } - - /* If no pending IOs, schedule a new read */ - /* queue, or immediate return */ - if (IOSTATE_IMMEDIATE_RETURN == tun_read_queue(tt, buf, buf_len)) - { - return tt->reads.size; - } - - /* If the pending IOs gave an error, report it */ - if (status < 0) - { - SetLastError (err); - return status; - } - else - { - /* no errors, but the newly scheduled read is now pending */ - return 0; - } -} - - -static int read_tun_buffered(struct tuntap *tt, uint8_t *buf, uint32_t buf_len) -{ - return tun_read_win32 (tt, buf, buf_len); -} - -static int write_tun_buffered(struct tuntap *tt, uint8_t *buf, uint32_t buf_len) -{ - return tun_write_win32 (tt, buf, buf_len); -} - - -static int pico_tap_send(struct pico_device *dev, void *buf, int len) -{ - uint32_t bytes_sent = 0; - struct pico_device_tap *tap = (struct pico_device_tap *) dev; - - /* Increase the statistic count */ - tap->statistics_frames_out++; - - bytes_sent = write_tun_buffered (tap->tt, buf, len); - dbg_tap("TX> sent %d bytes\n", bytes_sent); - - /* Discard the frame content silently. */ - return bytes_sent; -} - -uint8_t recv_buffer[1500]; - -static int pico_tap_poll(struct pico_device *dev, int loop_score) -{ - struct pico_device_tap *tap = (struct pico_device_tap *) dev; - while (loop_score) - { - int bytes_read = read_tun_buffered(tap->tt, recv_buffer, 1500); - loop_score--; - if (bytes_read > 0) - { - dbg_tap("RX< recvd: %d bytes\n", bytes_read); - pico_stack_recv(dev, recv_buffer, bytes_read); - /* break; */ - } - else - break; - } - return loop_score; -} - -#define CLEAR(x) memset(&(x), 0, sizeof(x)) - -void overlapped_io_init (struct overlapped_io *o, int event_state) -{ - CLEAR (*o); - - /* manual reset event, initially set according to event_state */ - o->overlapped.hEvent = CreateEvent (NULL, TRUE, event_state, NULL); - if (o->overlapped.hEvent == NULL) - dbg_tap ("Error: overlapped_io_init: CreateEvent failed\n"); - - /* allocate buffer for overlapped I/O */ - o->buf_init = PICO_ZALLOC(1500); /* XXX: MTU */ - o->buf_init_len = 1500; /* XXX: MTU */ - if (!(o->buf_init)) - dbg_tap("buffer alloc failed!\n"); /* XXX: return -1 or so? */ - else - dbg_tap("overlapped_io_init buffer allocated!\n"); -} - -void init_tun_post (struct tuntap *tt) -{ - dbg_tap("TUN post init (for overlapped io)\n"); - overlapped_io_init (&tt->reads, FALSE); - overlapped_io_init (&tt->writes, TRUE); - tt->rw_handle.read = tt->reads.overlapped.hEvent; - tt->rw_handle.write = tt->writes.overlapped.hEvent; -} - - -/* - * Public interface: pico_tap_create - * TODO: pico_tap_destroy - */ - -struct pico_device *pico_tap_create(char *name, uint8_t *mac) -{ - struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap)); - struct tuntap *tt = PICO_ZALLOC(sizeof(struct tuntap)); - - if (!(tap) || !(tt)) - return NULL; - - tap->dev.overhead = 0; - tap->statistics_frames_out = 0; - tap->dev.send = pico_tap_send; - tap->dev.poll = pico_tap_poll; - - show_tap_win_adapters(); - - tt->type = DEV_TYPE_TAP; - if (open_tun(NULL, NULL, "tap0", tt)) - { - dbg_tap("Failed to create TAP device!\n"); - PICO_FREE(tt); - PICO_FREE(tap); - return NULL; - } - - tap->tt = tt; - - if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) { - return NULL; - } - - init_tun_post(tt); /* init overlapped io */ - - dbg_tap("Device %s created.\n", tap->dev.name); - - return (struct pico_device *)tap; -} - -const char *pico_tap_get_guid(struct pico_device *dev) -{ - return ((struct pico_device_tap *)dev)->tt->guid; -} -#endif - diff --git a/core/deps/picotcp/modules/pico_dev_tap_windows.h b/core/deps/picotcp/modules/pico_dev_tap_windows.h deleted file mode 100644 index d8f62071b..000000000 --- a/core/deps/picotcp/modules/pico_dev_tap_windows.h +++ /dev/null @@ -1,20 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - *********************************************************************/ -#ifdef _WIN32 -#ifndef INCLUDE_PICO_TAP -#define INCLUDE_PICO_TAP -#include "pico_config.h" -#include "pico_device.h" - -/* will look for the first TAP device available, and use it */ -struct pico_device *pico_tap_create(char *name, uint8_t *mac); -/* TODO: not implemented yet */ -/* void pico_tap_destroy(struct pico_device *null); */ -const char *pico_tap_get_guid(struct pico_device *dev); - -#endif -#endif - diff --git a/core/deps/picotcp/modules/pico_dev_tap_windows_private.h b/core/deps/picotcp/modules/pico_dev_tap_windows_private.h deleted file mode 100644 index ef52354a4..000000000 --- a/core/deps/picotcp/modules/pico_dev_tap_windows_private.h +++ /dev/null @@ -1,89 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - Authors: Maxime Vincent - Based on the OpenVPN tun.c driver, under GPL - - NOTES: This is the Windows-only driver, a Linux-equivalent is available, too - You need to have an OpenVPN TUN/TAP network adapter installed, first - This driver is barely working: - * Only TAP-mode is supported (TUN is not) - * it will simply open the first TAP device it can find - * there is memory being allocated that's never freed - * there is no destroy function, yet - * it has only been tested on a Windows 7 machine - *********************************************************************/ - -#ifndef __PICO_DEV_TAP_WINDOWS_PRIVATE_H -#define __PICO_DEV_TAP_WINDOWS_PRIVATE_H - -/* Extra defines (vnz) */ -#define TAP_WIN_COMPONENT_ID "tap0901" -#define TAP_WIN_MIN_MAJOR 9 -#define TAP_WIN_MIN_MINOR 9 -#define PACKAGE_NAME "PicoTCP WinTAP" - -/* Extra structs */ -struct tap_reg -{ - const char *guid; - struct tap_reg *next; -}; - -struct panel_reg -{ - const char *name; - const char *guid; - struct panel_reg *next; -}; - - -/* - * ============= - * TAP IOCTLs - * ============= - */ - -#define TAP_WIN_CONTROL_CODE(request, method) \ - CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS) - -/* Present in 8.1 */ - -#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED) -#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED) - -/* Added in 8.2 */ - -/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */ -#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED) - -/* - * ================= - * Registry keys - * ================= - */ - -#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}" - -#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}" - -/* - * ====================== - * Filesystem prefixes - * ====================== - */ - -#define USERMODEDEVICEDIR "\\\\.\\Global\\" -#define SYSDEVICEDIR "\\Device\\" -#define USERDEVICEDIR "\\DosDevices\\Global\\" -#define TAP_WIN_SUFFIX ".tap" - -#endif diff --git a/core/deps/picotcp/modules/pico_dev_tun.c b/core/deps/picotcp/modules/pico_dev_tun.c deleted file mode 100644 index c980e77ec..000000000 --- a/core/deps/picotcp/modules/pico_dev_tun.c +++ /dev/null @@ -1,218 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - Authors: Daniele Lacamera - *********************************************************************/ - -#ifndef _WIN32 -#include -#include -#include -#ifdef __APPLE__ -#include -#include // UTUN_CONTROL_NAME -#include // struct socketaddr_ctl -#else -#include -#endif -#include "pico_device.h" -#include "pico_dev_tun.h" -#include "pico_stack.h" - -#include - -struct pico_device_tun { - struct pico_device dev; - int fd; -}; - -#define TUN_MTU 2048 - -static int pico_tun_send(struct pico_device *dev, void *buf, int len) -{ - struct pico_device_tun *tun = (struct pico_device_tun *) dev; -#ifdef __APPLE__ - // Add the protocol (IP) before the packet (4 bytes) - uint8_t *p = (uint8_t *)malloc(len + 4); - *(uint32_t *)p = 2; - memcpy(p + 4, buf, len); - int rc = (int)write(tun->fd, p, len + 4); - free(p); - return rc; -#endif - return (int)write(tun->fd, buf, (uint32_t)len); -} - -static int pico_tun_poll(struct pico_device *dev, int loop_score) -{ - struct pico_device_tun *tun = (struct pico_device_tun *) dev; - struct pollfd pfd; - unsigned char buf[TUN_MTU]; - int len; - pfd.fd = tun->fd; - pfd.events = POLLIN; - do { - if (poll(&pfd, 1, 0) <= 0) - return loop_score; - - len = (int)read(tun->fd, buf, TUN_MTU); - if (len > 0) { - loop_score--; -#ifdef __APPLE__ - pico_stack_recv(dev, buf + 4, (uint32_t)len - 4); -#else - pico_stack_recv(dev, buf, (uint32_t)len); -#endif - } - } while(loop_score > 0); - return 0; -} - -/* Public interface: create/destroy. */ - -void pico_tun_destroy(struct pico_device *dev) -{ - struct pico_device_tun *tun = (struct pico_device_tun *) dev; - if(tun->fd > 0) - close(tun->fd); -} - -#ifdef IFF_TUN // Linux - -static int tun_open(const char *name) -{ - struct ifreq ifr; - int tun_fd; - if((tun_fd = open("/dev/net/tun", O_RDWR)) < 0) { - return(-1); - } - - memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TUN | IFF_NO_PI; - strncpy(ifr.ifr_name, name, IFNAMSIZ); - if(ioctl(tun_fd, TUNSETIFF, &ifr) < 0) { - return(-1); - } - - return tun_fd; -} - -#else // BSD, OS X, ... - -#ifdef __APPLE__ -static int tun_open(const char *name) -{ - struct ctl_info ctlInfo; - strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name)); - int fd = -1; - - for (int unit = 0; unit < 256 && fd == -1; unit++) - { - fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL); - if (fd < 0) { - perror("socket"); - continue; - } - - struct sockaddr_ctl sc; - - if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) { - close(fd); - perror("ioctl"); - fd = -1; - continue; - } - printf("ctl_info: {ctl_id: %ud, ctl_name: %s}\n", - ctlInfo.ctl_id, ctlInfo.ctl_name); - - sc.sc_id = ctlInfo.ctl_id; - sc.sc_len = sizeof(sc); - sc.sc_family = AF_SYSTEM; - sc.ss_sysaddr = AF_SYS_CONTROL; - sc.sc_unit = unit; - - if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) { - perror("connect"); - close(fd); - fd = -1; - continue; - } - printf("Opened tunnel utun%d\n", unit); - - // set_nonblock (fd); - fcntl (fd, F_SETFL, O_NONBLOCK); - - // set_cloexec (fd); - /* - int s = socket(PF_ROUTE, SOCK_RAW, 0); - af = AF_INET; - aflen = sizeof(struct sockaddr_in); - flags |= RTF_UP; - flags |= RTF_HOST; - if ((ret = rtmsg(*cmd, flags)) == 0) - break; - */ - } - - return fd; -} - -#elif defined(SIOCIFCREATE) -static int tun_open(const char *name) -{ - int fd; - int s; - struct ifreq ifr; - - fd = open(name, O_RDWR); - if (fd == -1) - { - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s < 0) - return -1; - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, name + 5, sizeof(ifr.ifr_name) - 1); - if (!ioctl(s, SIOCIFCREATE, &ifr)) - fd = open(name, O_RDWR); - - close(s); - } - - return fd; -} -#else -#define tun_open(tun_name) open(tun_name, O_RDWR) -#endif -#endif - -struct pico_device *pico_tun_create(const char *name) -{ - struct pico_device_tun *tun = PICO_ZALLOC(sizeof(struct pico_device_tun)); - - if (!tun) - return NULL; - - if( 0 != pico_device_init((struct pico_device *)tun, name, NULL)) { - printf("Tun init failed.\n"); - pico_tun_destroy((struct pico_device *)tun); - return NULL; - } - - tun->dev.overhead = 0; - tun->fd = tun_open(name); - if (tun->fd < 0) { - printf("Tun creation failed.\n"); - pico_tun_destroy((struct pico_device *)tun); - return NULL; - } - - tun->dev.send = pico_tun_send; - tun->dev.poll = pico_tun_poll; - tun->dev.destroy = pico_tun_destroy; - dbg("Device %s created.\n", tun->dev.name); - return (struct pico_device *)tun; -} - -#endif diff --git a/core/deps/picotcp/modules/pico_dev_tun.h b/core/deps/picotcp/modules/pico_dev_tun.h deleted file mode 100644 index 0931de046..000000000 --- a/core/deps/picotcp/modules/pico_dev_tun.h +++ /dev/null @@ -1,17 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - *********************************************************************/ -#ifndef _WIN32 -#ifndef INCLUDE_PICO_TUN -#define INCLUDE_PICO_TUN -#include "pico_config.h" -#include "pico_device.h" - -void pico_tun_destroy(struct pico_device *tun); -struct pico_device *pico_tun_create(const char *name); - -#endif -#endif - diff --git a/core/deps/picotcp/modules/pico_dhcp_client.c b/core/deps/picotcp/modules/pico_dhcp_client.c deleted file mode 100644 index 5b56962ee..000000000 --- a/core/deps/picotcp/modules/pico_dhcp_client.c +++ /dev/null @@ -1,1074 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - Authors: Kristof Roelants, Frederik Van Slycken, Maxime Vincent - *********************************************************************/ - - -#include "pico_dhcp_client.h" -#include "pico_stack.h" -#include "pico_config.h" -#include "pico_device.h" -#include "pico_ipv4.h" -#include "pico_socket.h" -#include "pico_eth.h" - -#if (defined PICO_SUPPORT_DHCPC && defined PICO_SUPPORT_UDP) - -#ifdef DEBUG_DHCP_CLIENT - #define dhcpc_dbg dbg -#else - #define dhcpc_dbg(...) do {} while(0) -#endif - -/* timer values */ -#define DHCP_CLIENT_REINIT 6000 /* msec */ -#define DHCP_CLIENT_RETRANS 4 /* sec */ -#define DHCP_CLIENT_RETRIES 3 - -#define DHCP_CLIENT_TIMER_STOPPED 0 -#define DHCP_CLIENT_TIMER_STARTED 1 - -/* maximum size of a DHCP message */ -#define DHCP_CLIENT_MAXMSGZISE (PICO_IP_MRU - PICO_SIZE_IP4HDR) -#define PICO_DHCP_HOSTNAME_MAXLEN 64U - -/* Mockables */ -#if defined UNIT_TEST -# define MOCKABLE __attribute__((weak)) -#else -# define MOCKABLE -#endif - -static char dhcpc_host_name[PICO_DHCP_HOSTNAME_MAXLEN] = ""; -static char dhcpc_domain_name[PICO_DHCP_HOSTNAME_MAXLEN] = ""; - - -enum dhcp_client_state { - DHCP_CLIENT_STATE_INIT_REBOOT = 0, - DHCP_CLIENT_STATE_REBOOTING, - DHCP_CLIENT_STATE_INIT, - DHCP_CLIENT_STATE_SELECTING, - DHCP_CLIENT_STATE_REQUESTING, - DHCP_CLIENT_STATE_BOUND, - DHCP_CLIENT_STATE_RENEWING, - DHCP_CLIENT_STATE_REBINDING -}; - - -#define PICO_DHCPC_TIMER_INIT 0 -#define PICO_DHCPC_TIMER_REQUEST 1 -#define PICO_DHCPC_TIMER_RENEW 2 -#define PICO_DHCPC_TIMER_REBIND 3 -#define PICO_DHCPC_TIMER_T1 4 -#define PICO_DHCPC_TIMER_T2 5 -#define PICO_DHCPC_TIMER_LEASE 6 -#define PICO_DHCPC_TIMER_ARRAY_SIZE 7 - -struct dhcp_client_timer -{ - uint8_t state; - unsigned int type; - uint32_t xid; - uint32_t timer_id; -}; - -struct pico_dhcp_client_cookie -{ - uint8_t event; - uint8_t retry; - uint32_t xid; - uint32_t *uid; - enum dhcp_client_state state; - void (*cb)(void*dhcpc, int code); - pico_time init_timestamp; - struct pico_socket *s; - struct pico_ip4 address; - struct pico_ip4 netmask; - struct pico_ip4 gateway; - struct pico_ip4 nameserver[2]; - struct pico_ip4 server_id; - struct pico_device *dev; - struct dhcp_client_timer *timer[PICO_DHCPC_TIMER_ARRAY_SIZE]; - uint32_t t1_time; - uint32_t t2_time; - uint32_t lease_time; - uint32_t renew_time; - uint32_t rebind_time; -}; - -static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc); -static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); -static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type); -static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s); -static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); -static void pico_dhcp_client_callback(struct pico_dhcp_client_cookie *dhcpc, int code); - -static const struct pico_ip4 bcast_netmask = { - .addr = 0xFFFFFFFF -}; - -static struct pico_ip4 inaddr_any = { - 0 -}; - - -static int dhcp_cookies_cmp(void *ka, void *kb) -{ - struct pico_dhcp_client_cookie *a = ka, *b = kb; - if (a->xid == b->xid) - return 0; - - return (a->xid < b->xid) ? (-1) : (1); -} -static PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp); - -static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid) -{ - struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = { - 0 - }; - - test.xid = xid; - found = pico_tree_findKey(&DHCPCookies, &test); - if (found) { - pico_err = PICO_ERR_EAGAIN; - return NULL; - } - - dhcpc = PICO_ZALLOC(sizeof(struct pico_dhcp_client_cookie)); - if (!dhcpc) { - pico_err = PICO_ERR_ENOMEM; - return NULL; - } - - dhcpc->state = DHCP_CLIENT_STATE_INIT; - dhcpc->xid = xid; - dhcpc->uid = uid; - *(dhcpc->uid) = 0; - dhcpc->cb = cb; - dhcpc->dev = dev; - - if (pico_tree_insert(&DHCPCookies, dhcpc)) { - PICO_FREE(dhcpc); - return NULL; - } - - return dhcpc; -} - -static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc); -static int pico_dhcp_client_del_cookie(uint32_t xid) -{ - struct pico_dhcp_client_cookie test = { - 0 - }, *found = NULL; - - test.xid = xid; - found = pico_tree_findKey(&DHCPCookies, &test); - if (!found) - return -1; - - pico_dhcp_client_stop_timers(found); - pico_socket_close(found->s); - found->s = NULL; - pico_ipv4_link_del(found->dev, found->address); - pico_tree_delete(&DHCPCookies, found); - PICO_FREE(found); - return 0; -} - -static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid) -{ - struct pico_dhcp_client_cookie test = { - 0 - }, *found = NULL; - - test.xid = xid; - found = pico_tree_findKey(&DHCPCookies, &test); - if (found) - return found; - else - return NULL; -} - -static void pico_dhcp_client_timer_handler(pico_time now, void *arg); -static void pico_dhcp_client_reinit(pico_time now, void *arg); -static struct dhcp_client_timer *pico_dhcp_timer_add(uint8_t type, uint32_t time, struct pico_dhcp_client_cookie *ck) -{ - struct dhcp_client_timer *t = ck->timer[type]; - - if (t) { - /* Stale timer, mark to be freed in the callback */ - t->state = DHCP_CLIENT_TIMER_STOPPED; - } - - /* allocate a new timer, the old one is still in the timer tree, and will be freed as soon as it expires */ - t = PICO_ZALLOC(sizeof(struct dhcp_client_timer)); - - if (!t) - return NULL; - - t->state = DHCP_CLIENT_TIMER_STARTED; - t->xid = ck->xid; - t->type = type; - t->timer_id = pico_timer_add(time, pico_dhcp_client_timer_handler, t); - if (!t->timer_id) { - dhcpc_dbg("DHCP: Failed to start timer\n"); - PICO_FREE(t); - return NULL; - } - - /* store timer struct reference in cookie */ - ck->timer[type] = t; - return t; -} - -static int dhcp_get_timer_event(struct pico_dhcp_client_cookie *dhcpc, unsigned int type) -{ - const int events[PICO_DHCPC_TIMER_ARRAY_SIZE] = - { - PICO_DHCP_EVENT_RETRANSMIT, - PICO_DHCP_EVENT_RETRANSMIT, - PICO_DHCP_EVENT_RETRANSMIT, - PICO_DHCP_EVENT_RETRANSMIT, - PICO_DHCP_EVENT_T1, - PICO_DHCP_EVENT_T2, - PICO_DHCP_EVENT_LEASE - }; - - if (type == PICO_DHCPC_TIMER_REQUEST) { - if (++dhcpc->retry > DHCP_CLIENT_RETRIES) { - reset(dhcpc, NULL); - return PICO_DHCP_EVENT_NONE; - } - } else if (type < PICO_DHCPC_TIMER_T1) { - dhcpc->retry++; - } - - return events[type]; -} - -static void pico_dhcp_client_timer_handler(pico_time now, void *arg) -{ - struct dhcp_client_timer *t = (struct dhcp_client_timer *)arg; - struct pico_dhcp_client_cookie *dhcpc; - - if (!t) - return; - - (void) now; - if (t->state != DHCP_CLIENT_TIMER_STOPPED) { - dhcpc = pico_dhcp_client_find_cookie(t->xid); - if (dhcpc) { - t->state = DHCP_CLIENT_TIMER_STOPPED; - if ((t->type == PICO_DHCPC_TIMER_INIT) && (dhcpc->state < DHCP_CLIENT_STATE_SELECTING)) { - /* this was an INIT timer */ - pico_dhcp_client_reinit(now, dhcpc); - } else if (t->type != PICO_DHCPC_TIMER_INIT) { - /* this was NOT an INIT timer */ - dhcpc->event = (uint8_t)dhcp_get_timer_event(dhcpc, t->type); - if (dhcpc->event != PICO_DHCP_EVENT_NONE) - pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL); - } - } - } - - /* stale timer, it's associated struct should be freed */ - if (t->state == DHCP_CLIENT_TIMER_STOPPED) - PICO_FREE(t); -} - -static void pico_dhcp_client_reinit(pico_time now, void *arg) -{ - struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg; - (void) now; - - if (dhcpc->s) { - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - } - - if (++dhcpc->retry > DHCP_CLIENT_RETRIES) { - pico_err = PICO_ERR_EAGAIN; - pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR); - - pico_dhcp_client_del_cookie(dhcpc->xid); - return; - } - - pico_dhcp_client_init(dhcpc); - return; -} - - -static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc) -{ - int i; - dhcpc->retry = 0; - for (i = 0; i < PICO_DHCPC_TIMER_ARRAY_SIZE; i++) - { - if (dhcpc->timer[i]) { - /* Do not cancel timer, but rather set it's state to be freed when it expires */ - dhcpc->timer[i]->state = DHCP_CLIENT_TIMER_STOPPED; - dhcpc->timer[i] = NULL; - } - } -} - -static int pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc) -{ - uint32_t time = 0; - /* timer value is doubled with every retry (exponential backoff) */ - time = (uint32_t) (DHCP_CLIENT_RETRANS << dhcpc->retry); - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, time * 1000, dhcpc)) - return -1; - - return 0; -} - -static int pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc) -{ - uint32_t time = 0; - - /* timer value is doubled with every retry (exponential backoff) */ - time = (uint32_t)(DHCP_CLIENT_RETRANS << dhcpc->retry); - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_REQUEST, time * 1000, dhcpc)) - return -1; - - return 0; -} - -static int pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc) -{ - uint32_t halftime = 0; - - /* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */ - /* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */ - pico_dhcp_client_stop_timers(dhcpc); - halftime = dhcpc->renew_time >> (dhcpc->retry + 1); - if (halftime < 60) - halftime = 60; - - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc)) - return -1; - - return 0; -} - -static int pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc) -{ - uint32_t halftime = 0; - - pico_dhcp_client_stop_timers(dhcpc); - halftime = dhcpc->rebind_time >> (dhcpc->retry + 1); - if (halftime < 60) - halftime = 60; - - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc)) - return -1; - - return 0; -} - -static int pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc) -{ - - pico_dhcp_client_stop_timers(dhcpc); - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_T1, dhcpc->t1_time * 1000, dhcpc)) - goto fail; - - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_T2, dhcpc->t2_time * 1000, dhcpc)) - goto fail; - - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_LEASE, dhcpc->lease_time * 1000, dhcpc)) - goto fail; - - return 0; - -fail: - pico_dhcp_client_stop_timers(dhcpc); - return -1; -} - -static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc) -{ - uint16_t port = PICO_DHCP_CLIENT_PORT; - if (!dhcpc) - return -1; - - /* adding a link with address 0.0.0.0 and netmask 0.0.0.0, - * automatically adds a route for a global broadcast */ - pico_ipv4_link_add(dhcpc->dev, inaddr_any, bcast_netmask); - if (!dhcpc->s) - dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup); - - if (!dhcpc->s) { - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc)) - return -1; - - return 0; - } - - dhcpc->s->dev = dhcpc->dev; - if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) { - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc)) - return -1; - - return 0; - } - - if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) { - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - if (!pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc)) - return -1; - - return 0; - } - - dhcpc->retry = 0; - dhcpc->init_timestamp = PICO_TIME_MS(); - if (pico_dhcp_client_start_init_timer(dhcpc) < 0) { - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - return -1; - } - - return 0; -} - -int MOCKABLE pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid) -{ - uint8_t retry = 32; - uint32_t xid = 0; - struct pico_dhcp_client_cookie *dhcpc = NULL; - - if (!dev || !cb || !uid) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - if (!dev->eth) { - pico_err = PICO_ERR_EOPNOTSUPP; - return -1; - } - - /* attempt to generate a correct xid, else fail */ - do { - xid = pico_rand(); - } while (!xid && --retry); - - if (!xid) { - pico_err = PICO_ERR_EAGAIN; - return -1; - } - - dhcpc = pico_dhcp_client_add_cookie(xid, dev, cb, uid); - if (!dhcpc) - return -1; - - dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid); - *uid = xid; - return pico_dhcp_client_init(dhcpc); -} - -static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc, struct pico_dhcp_opt *opt) -{ - do { - switch (opt->code) - { - case PICO_DHCP_OPT_PAD: - break; - - case PICO_DHCP_OPT_END: - break; - - case PICO_DHCP_OPT_MSGTYPE: - dhcpc->event = opt->ext.msg_type.type; - dhcpc_dbg("DHCP client: message type %u\n", dhcpc->event); - break; - - case PICO_DHCP_OPT_LEASETIME: - dhcpc->lease_time = long_be(opt->ext.lease_time.time); - dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_time); - break; - - case PICO_DHCP_OPT_RENEWALTIME: - dhcpc->t1_time = long_be(opt->ext.renewal_time.time); - dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->t1_time); - break; - - case PICO_DHCP_OPT_REBINDINGTIME: - dhcpc->t2_time = long_be(opt->ext.rebinding_time.time); - dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->t2_time); - break; - - case PICO_DHCP_OPT_ROUTER: - dhcpc->gateway = opt->ext.router.ip; - dhcpc_dbg("DHCP client: router %08X\n", dhcpc->gateway.addr); - break; - - case PICO_DHCP_OPT_DNS: - dhcpc->nameserver[0] = opt->ext.dns1.ip; - dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[0].addr); - if (opt->len >= 8) { - dhcpc->nameserver[1] = opt->ext.dns2.ip; - dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[1].addr); - } - - break; - - case PICO_DHCP_OPT_NETMASK: - dhcpc->netmask = opt->ext.netmask.ip; - dhcpc_dbg("DHCP client: netmask %08X\n", dhcpc->netmask.addr); - break; - - case PICO_DHCP_OPT_SERVERID: - dhcpc->server_id = opt->ext.server_id.ip; - dhcpc_dbg("DHCP client: server ID %08X\n", dhcpc->server_id.addr); - break; - - case PICO_DHCP_OPT_OPTOVERLOAD: - dhcpc_dbg("DHCP client: WARNING option overload present (not processed)"); - break; - - case PICO_DHCP_OPT_HOSTNAME: - { - uint32_t maxlen = PICO_DHCP_HOSTNAME_MAXLEN; - if (opt->len < maxlen) - maxlen = opt->len; - - strncpy(dhcpc_host_name, opt->ext.string.txt, maxlen); - } - break; - - case PICO_DHCP_OPT_DOMAINNAME: - { - uint32_t maxlen = PICO_DHCP_HOSTNAME_MAXLEN; - if (opt->len < maxlen) - maxlen = opt->len; - - strncpy(dhcpc_domain_name, opt->ext.string.txt, maxlen); - } - break; - - default: - dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code); - break; - } - } while (pico_dhcp_next_option(&opt)); - - /* default values for T1 and T2 when not provided */ - if (!dhcpc->t1_time) - dhcpc->t1_time = dhcpc->lease_time >> 1; - - if (!dhcpc->t2_time) - dhcpc->t2_time = (dhcpc->lease_time * 875) / 1000; - - return; -} - -static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; - struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0); - - pico_dhcp_client_recv_params(dhcpc, opt); - if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time) - return -1; - - dhcpc->address.addr = hdr->yiaddr; - - /* we skip state SELECTING, process first offer received */ - dhcpc->state = DHCP_CLIENT_STATE_REQUESTING; - dhcpc->retry = 0; - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); - if (pico_dhcp_client_start_requesting_timer(dhcpc) < 0) - return -1; - - return 0; -} - -static void pico_dhcp_client_update_link(struct pico_dhcp_client_cookie *dhcpc) -{ - struct pico_ip4 any_address = { - 0 - }; - struct pico_ip4 address = { - 0 - }; - struct pico_ipv4_link *l; - - dbg("DHCP client: update link\n"); - - pico_ipv4_link_del(dhcpc->dev, address); - l = pico_ipv4_link_by_dev(dhcpc->dev); - while(l) { - pico_ipv4_link_del(dhcpc->dev, l->address); - l = pico_ipv4_link_by_dev_next(dhcpc->dev, l); - } - pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask); - - /* If router option is received, use it as default gateway */ - if (dhcpc->gateway.addr != 0U) { - pico_ipv4_route_add(any_address, any_address, dhcpc->gateway, 1, NULL); - } -} - -static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; - struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0); - struct pico_ipv4_link *l; - - pico_dhcp_client_recv_params(dhcpc, opt); - if ((dhcpc->event != PICO_DHCP_MSG_ACK) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time) - return -1; - - /* Issue #20 the server can transmit on ACK a different IP than the one in OFFER */ - /* RFC2131 ch 4.3.2 ... The client SHOULD use the parameters in the DHCPACK message for configuration */ - if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) - dhcpc->address.addr = hdr->yiaddr; - - - /* close the socket used for address (re)acquisition */ - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - - /* Delete all the links before adding the new ip address - * in case the new address doesn't match the old one */ - l = pico_ipv4_link_by_dev(dhcpc->dev); - if (dhcpc->address.addr != (l->address).addr) { - pico_dhcp_client_update_link(dhcpc); - } - - dbg("DHCP client: renewal time (T1) %u\n", (unsigned int)dhcpc->t1_time); - dbg("DHCP client: rebinding time (T2) %u\n", (unsigned int)dhcpc->t2_time); - dbg("DHCP client: lease time %u\n", (unsigned int)dhcpc->lease_time); - - dhcpc->retry = 0; - dhcpc->renew_time = dhcpc->t2_time - dhcpc->t1_time; - dhcpc->rebind_time = dhcpc->lease_time - dhcpc->t2_time; - if (pico_dhcp_client_start_reacquisition_timers(dhcpc) < 0) { - pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR); - return -1; - } - - - *(dhcpc->uid) = dhcpc->xid; - pico_dhcp_client_callback(dhcpc, PICO_DHCP_SUCCESS); - - dhcpc->state = DHCP_CLIENT_STATE_BOUND; - return 0; -} - -static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - uint16_t port = PICO_DHCP_CLIENT_PORT; - (void) buf; - dhcpc->state = DHCP_CLIENT_STATE_RENEWING; - dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup); - if (!dhcpc->s) { - dhcpc_dbg("DHCP client ERROR: failure opening socket on renew, aborting DHCP! (%s)\n", strerror(pico_err)); - pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR); - - return -1; - } - - if (pico_socket_bind(dhcpc->s, &dhcpc->address, &port) != 0) { - dhcpc_dbg("DHCP client ERROR: failure binding socket on renew, aborting DHCP! (%s)\n", strerror(pico_err)); - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR); - - return -1; - } - - dhcpc->retry = 0; - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); - if (pico_dhcp_client_start_renewing_timer(dhcpc) < 0) { - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - pico_dhcp_client_callback(dhcpc, PICO_DHCP_ERROR); - - return -1; - } - - return 0; -} - -static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - (void) buf; - - dhcpc->state = DHCP_CLIENT_STATE_REBINDING; - dhcpc->retry = 0; - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); - if (pico_dhcp_client_start_rebinding_timer(dhcpc) < 0) - return -1; - - return 0; -} - -static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - struct pico_ip4 address = { - 0 - }; - (void) buf; - - if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) - address.addr = PICO_IP4_ANY; - else - address.addr = dhcpc->address.addr; - - /* close the socket used for address (re)acquisition */ - pico_socket_close(dhcpc->s); - dhcpc->s = NULL; - /* delete the link with the currently in use address */ - pico_ipv4_link_del(dhcpc->dev, address); - - pico_dhcp_client_callback(dhcpc, PICO_DHCP_RESET); - - if (dhcpc->state < DHCP_CLIENT_STATE_BOUND) - { - /* pico_dhcp_client_timer_stop(dhcpc, PICO_DHCPC_TIMER_INIT); */ - } - - - dhcpc->state = DHCP_CLIENT_STATE_INIT; - pico_dhcp_client_stop_timers(dhcpc); - pico_dhcp_client_init(dhcpc); - return 0; -} - -static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - (void) buf; - switch (dhcpc->state) - { - case DHCP_CLIENT_STATE_INIT: - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER); - if (pico_dhcp_client_start_init_timer(dhcpc) < 0) - return -1; - break; - - case DHCP_CLIENT_STATE_REQUESTING: - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); - if (pico_dhcp_client_start_requesting_timer(dhcpc) < 0) - return -1; - break; - - case DHCP_CLIENT_STATE_RENEWING: - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); - if (pico_dhcp_client_start_renewing_timer(dhcpc) < 0) - return -1; - break; - - case DHCP_CLIENT_STATE_REBINDING: - pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER); - if (pico_dhcp_client_start_rebinding_timer(dhcpc) < 0) - return -1; - break; - - default: - dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state); - return -1; - } - - return 0; -} - -struct dhcp_action_entry { - int (*offer)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*ack)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*nak)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*timer1)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*timer2)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*timer_lease)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); - int (*timer_retransmit)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); -}; - -static struct dhcp_action_entry dhcp_fsm[] = -{ /* event |offer |ack |nak |T1 |T2 |lease |retransmit */ -/* state init-reboot */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, -/* state rebooting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, -/* state init */ { recv_offer, NULL, NULL, NULL, NULL, NULL, retransmit }, -/* state selecting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, -/* state requesting */ { NULL, recv_ack, reset, NULL, NULL, NULL, retransmit }, -/* state bound */ { NULL, NULL, NULL, renew, NULL, NULL, NULL }, -/* state renewing */ { NULL, recv_ack, reset, NULL, rebind, NULL, retransmit }, -/* state rebinding */ { NULL, recv_ack, reset, NULL, NULL, reset, retransmit }, -}; - -static void dhcp_action_call( int (*call)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf), struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - if (call) - call(dhcpc, buf); -} - -/* TIMERS REMARK: - * In state bound we have T1, T2 and the lease timer running. If T1 goes off, we attempt to renew. - * If the renew succeeds a new T1, T2 and lease timer is started. The former T2 and lease timer is - * still running though. This poses no concerns as the T2 and lease event in state bound have a NULL - * pointer in the fsm. If the former T2 or lease timer goes off, nothing happens. Same situation - * applies for T2 and a succesfull rebind. */ - -static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) -{ - switch (event) - { - case PICO_DHCP_MSG_OFFER: - dhcpc_dbg("DHCP client: received OFFER\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].offer, dhcpc, buf); - break; - - case PICO_DHCP_MSG_ACK: - dhcpc_dbg("DHCP client: received ACK\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].ack, dhcpc, buf); - break; - - case PICO_DHCP_MSG_NAK: - dhcpc_dbg("DHCP client: received NAK\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].nak, dhcpc, buf); - break; - - case PICO_DHCP_EVENT_T1: - dhcpc_dbg("DHCP client: received T1 timeout\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].timer1, dhcpc, buf); - break; - - case PICO_DHCP_EVENT_T2: - dhcpc_dbg("DHCP client: received T2 timeout\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].timer2, dhcpc, buf); - break; - - case PICO_DHCP_EVENT_LEASE: - dhcpc_dbg("DHCP client: received LEASE timeout\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].timer_lease, dhcpc, buf); - break; - - case PICO_DHCP_EVENT_RETRANSMIT: - dhcpc_dbg("DHCP client: received RETRANSMIT timeout\n"); - dhcp_action_call(dhcp_fsm[dhcpc->state].timer_retransmit, dhcpc, buf); - break; - - default: - dhcpc_dbg("DHCP client WARNING: unrecognized event (%u)!\n", dhcpc->event); - return; - } - return; -} - -static int16_t pico_dhcp_client_opt_parse(void *ptr, uint16_t len) -{ - uint32_t optlen = len - (uint32_t)sizeof(struct pico_dhcp_hdr); - struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)ptr; - struct pico_dhcp_opt *opt = DHCP_OPT(hdr, 0); - - if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE) - return -1; - - if (!pico_dhcp_are_options_valid(opt, (int32_t)optlen)) - return -1; - - do { - if (opt->code == PICO_DHCP_OPT_MSGTYPE) - return opt->ext.msg_type.type; - } while (pico_dhcp_next_option(&opt)); - - return -1; -} - -static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type) -{ - int32_t r = 0; - uint16_t optlen = 0, offset = 0; - struct pico_ip4 destination = { - .addr = 0xFFFFFFFF - }; - struct pico_dhcp_hdr *hdr = NULL; - - - /* RFC 2131 3.1.3: Request is always BROADCAST */ - - /* Set again default route for the bcast request */ - pico_ipv4_route_set_bcast_link(pico_ipv4_link_by_dev(dhcpc->dev)); - - switch (msg_type) - { - case PICO_DHCP_MSG_DISCOVER: - dhcpc_dbg("DHCP client: sent DHCPDISCOVER\n"); - optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_END; - hdr = PICO_ZALLOC((size_t)(sizeof(struct pico_dhcp_hdr) + optlen)); - if (!hdr) { - pico_err = PICO_ERR_ENOMEM; - return -1; - } - - /* specific options */ - offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr, offset), DHCP_CLIENT_MAXMSGZISE)); - break; - - case PICO_DHCP_MSG_REQUEST: - optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_REQIP + PICO_DHCP_OPTLEN_SERVERID - + PICO_DHCP_OPTLEN_END; - hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + optlen); - if (!hdr) { - pico_err = PICO_ERR_ENOMEM; - return -1; - } - - /* specific options */ - offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(DHCP_OPT(hdr, offset), DHCP_CLIENT_MAXMSGZISE)); - if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) { - offset = (uint16_t)(offset + pico_dhcp_opt_reqip(DHCP_OPT(hdr, offset), &dhcpc->address)); - offset = (uint16_t)(offset + pico_dhcp_opt_serverid(DHCP_OPT(hdr, offset), &dhcpc->server_id)); - } - - break; - - default: - return -1; - } - - /* common options */ - offset = (uint16_t)(offset + pico_dhcp_opt_msgtype(DHCP_OPT(hdr, offset), msg_type)); - offset = (uint16_t)(offset + pico_dhcp_opt_paramlist(DHCP_OPT(hdr, offset))); - offset = (uint16_t)(offset + pico_dhcp_opt_end(DHCP_OPT(hdr, offset))); - - switch (dhcpc->state) - { - case DHCP_CLIENT_STATE_BOUND: - destination.addr = dhcpc->server_id.addr; - hdr->ciaddr = dhcpc->address.addr; - break; - - case DHCP_CLIENT_STATE_RENEWING: - destination.addr = dhcpc->server_id.addr; - hdr->ciaddr = dhcpc->address.addr; - break; - - case DHCP_CLIENT_STATE_REBINDING: - hdr->ciaddr = dhcpc->address.addr; - break; - - default: - /* do nothing */ - break; - } - - /* header information */ - hdr->op = PICO_DHCP_OP_REQUEST; - hdr->htype = PICO_DHCP_HTYPE_ETH; - hdr->hlen = PICO_SIZE_ETH; - hdr->xid = dhcpc->xid; - /* hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST); / * Nope: see bug #96! * / */ - hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE; - /* copy client hardware address */ - memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH); - - if (destination.addr == PICO_IP4_BCAST) - pico_ipv4_route_set_bcast_link(pico_ipv4_link_get(&dhcpc->address)); - - r = pico_socket_sendto(dhcpc->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + optlen), &destination, PICO_DHCPD_PORT); - PICO_FREE(hdr); - if (r < 0) - return -1; - - return 0; -} - -static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s) -{ - - uint8_t *buf; - int r = 0; - struct pico_dhcp_hdr *hdr = NULL; - struct pico_dhcp_client_cookie *dhcpc = NULL; - - if ((ev & PICO_SOCK_EV_RD) == 0) - return; - - buf = PICO_ZALLOC(DHCP_CLIENT_MAXMSGZISE); - if (!buf) { - return; - } - - r = pico_socket_recvfrom(s, buf, DHCP_CLIENT_MAXMSGZISE, NULL, NULL); - if (r < 0) - goto out_discard_buf; - - /* If the 'xid' of an arriving message does not match the 'xid' - * of the most recent transmitted message, the message must be - * silently discarded. */ - hdr = (struct pico_dhcp_hdr *)buf; - dhcpc = pico_dhcp_client_find_cookie(hdr->xid); - if (!dhcpc) - goto out_discard_buf; - - dhcpc->event = (uint8_t)pico_dhcp_client_opt_parse(buf, (uint16_t)r); - pico_dhcp_state_machine(dhcpc->event, dhcpc, buf); - -out_discard_buf: - PICO_FREE(buf); -} - -static void pico_dhcp_client_callback(struct pico_dhcp_client_cookie *dhcpc, int code) -{ - if(dhcpc->cb) - dhcpc->cb(dhcpc, code); -} - -void *MOCKABLE pico_dhcp_get_identifier(uint32_t xid) -{ - return (void *)pico_dhcp_client_find_cookie(xid); -} - -struct pico_ip4 MOCKABLE pico_dhcp_get_address(void*dhcpc) -{ - return ((struct pico_dhcp_client_cookie*)dhcpc)->address; -} - -struct pico_ip4 MOCKABLE pico_dhcp_get_gateway(void*dhcpc) -{ - return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway; -} - -struct pico_ip4 pico_dhcp_get_netmask(void *dhcpc) -{ - return ((struct pico_dhcp_client_cookie*)dhcpc)->netmask; -} - -struct pico_ip4 pico_dhcp_get_nameserver(void*dhcpc, int index) -{ - struct pico_ip4 fault = { - .addr = 0xFFFFFFFFU - }; - if ((index != 0) && (index != 1)) - return fault; - - return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver[index]; -} - -int pico_dhcp_client_abort(uint32_t xid) -{ - return pico_dhcp_client_del_cookie(xid); -} - - -char *pico_dhcp_get_hostname(void) -{ - return dhcpc_host_name; -} - -char *pico_dhcp_get_domain(void) -{ - return dhcpc_domain_name; -} - -#endif diff --git a/core/deps/picotcp/modules/pico_dhcp_common.c b/core/deps/picotcp/modules/pico_dhcp_common.c deleted file mode 100644 index ccd15f2e1..000000000 --- a/core/deps/picotcp/modules/pico_dhcp_common.c +++ /dev/null @@ -1,190 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - . - - Authors: Frederik Van Slycken - *********************************************************************/ - -#include "pico_config.h" -#include "pico_stack.h" -#include "pico_dhcp_common.h" - -#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD) -/* pico_dhcp_are_options_valid needs to be called first to prevent illegal memory access */ -/* The argument pointer is moved forward to the next option */ -struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr) -{ - uint8_t **p = (uint8_t **)ptr; - struct pico_dhcp_opt *opt = *ptr; - - if (opt->code == PICO_DHCP_OPT_END) - return NULL; - - if (opt->code == PICO_DHCP_OPT_PAD) { - *p += 1; - return *ptr; - } - - *p += (opt->len + 2); /* (len + 2) to account for code and len octet */ - return *ptr; -} - -uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len) -{ - uint8_t optlen = 0, *p = ptr; - - while (len > 0) { - switch (*p) - { - case PICO_DHCP_OPT_END: - return 1; - - case PICO_DHCP_OPT_PAD: - p++; - len--; - break; - - default: - p++; /* move pointer from code octet to len octet */ - len--; - if ((len <= 0) || (len - (*p + 1) < 0)) /* (*p + 1) to account for len octet */ - return 0; - - optlen = *p; - p += optlen + 1; - len -= optlen; - break; - } - } - return 0; -} - -uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: netmask */ - opt->code = PICO_DHCP_OPT_NETMASK; - opt->len = PICO_DHCP_OPTLEN_NETMASK - PICO_DHCP_OPTLEN_HDR; - opt->ext.netmask.ip = *ip; - return PICO_DHCP_OPTLEN_NETMASK; -} - -uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: router */ - opt->code = PICO_DHCP_OPT_ROUTER; - opt->len = PICO_DHCP_OPTLEN_ROUTER - PICO_DHCP_OPTLEN_HDR; - opt->ext.router.ip = *ip; - return PICO_DHCP_OPTLEN_ROUTER; -} - -uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: dns */ - opt->code = PICO_DHCP_OPT_DNS; - opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR; - opt->ext.dns1.ip = *ip; - return PICO_DHCP_OPTLEN_DNS; -} - -uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: broadcast */ - opt->code = PICO_DHCP_OPT_BROADCAST; - opt->len = PICO_DHCP_OPTLEN_BROADCAST - PICO_DHCP_OPTLEN_HDR; - opt->ext.broadcast.ip = *ip; - return PICO_DHCP_OPTLEN_BROADCAST; -} - -uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: request IP address */ - opt->code = PICO_DHCP_OPT_REQIP; - opt->len = PICO_DHCP_OPTLEN_REQIP - PICO_DHCP_OPTLEN_HDR; - opt->ext.req_ip.ip = *ip; - return PICO_DHCP_OPTLEN_REQIP; -} - -uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: lease time */ - opt->code = PICO_DHCP_OPT_LEASETIME; - opt->len = PICO_DHCP_OPTLEN_LEASETIME - PICO_DHCP_OPTLEN_HDR; - opt->ext.lease_time.time = time; - return PICO_DHCP_OPTLEN_LEASETIME; -} - -uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: message type */ - opt->code = PICO_DHCP_OPT_MSGTYPE; - opt->len = PICO_DHCP_OPTLEN_MSGTYPE - PICO_DHCP_OPTLEN_HDR; - opt->ext.msg_type.type = type; - return PICO_DHCP_OPTLEN_MSGTYPE; -} - -uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: server identifier */ - opt->code = PICO_DHCP_OPT_SERVERID; - opt->len = PICO_DHCP_OPTLEN_SERVERID - PICO_DHCP_OPTLEN_HDR; - opt->ext.server_id.ip = *ip; - return PICO_DHCP_OPTLEN_SERVERID; -} - -uint8_t pico_dhcp_opt_paramlist(void *ptr) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - uint8_t *param_code = &(opt->ext.param_list.code[0]); - - /* option: parameter list */ - opt->code = PICO_DHCP_OPT_PARAMLIST; - opt->len = PICO_DHCP_OPTLEN_PARAMLIST - PICO_DHCP_OPTLEN_HDR; - param_code[0] = PICO_DHCP_OPT_NETMASK; - param_code[1] = PICO_DHCP_OPT_TIME; - param_code[2] = PICO_DHCP_OPT_ROUTER; - param_code[3] = PICO_DHCP_OPT_HOSTNAME; - param_code[4] = PICO_DHCP_OPT_RENEWALTIME; - param_code[5] = PICO_DHCP_OPT_REBINDINGTIME; - param_code[6] = PICO_DHCP_OPT_DNS; - return PICO_DHCP_OPTLEN_PARAMLIST; -} - -uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size) -{ - struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; - - /* option: maximum message size */ - opt->code = PICO_DHCP_OPT_MAXMSGSIZE; - opt->len = PICO_DHCP_OPTLEN_MAXMSGSIZE - PICO_DHCP_OPTLEN_HDR; - opt->ext.max_msg_size.size = short_be(size); - return PICO_DHCP_OPTLEN_MAXMSGSIZE; -} - -uint8_t pico_dhcp_opt_end(void *ptr) -{ - uint8_t *opt = (uint8_t *)ptr; - - /* option: end of options */ - *opt = PICO_DHCP_OPT_END; - return PICO_DHCP_OPTLEN_END; -} - -#endif diff --git a/core/deps/picotcp/modules/pico_icmp4.c b/core/deps/picotcp/modules/pico_icmp4.c deleted file mode 100644 index 3a8ae173c..000000000 --- a/core/deps/picotcp/modules/pico_icmp4.c +++ /dev/null @@ -1,434 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - - . - - Authors: Daniele Lacamera - *********************************************************************/ - - -#include "pico_icmp4.h" -#include "pico_config.h" -#include "pico_ipv4.h" -#include "pico_eth.h" -#include "pico_device.h" -#include "pico_stack.h" -#include "pico_tree.h" - -/* Queues */ -static struct pico_queue icmp_in = { - 0 -}; -static struct pico_queue icmp_out = { - 0 -}; - - -/* Functions */ - -static int pico_icmp4_checksum(struct pico_frame *f) -{ - struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; - if (!hdr) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - hdr->crc = 0; - hdr->crc = short_be(pico_checksum(hdr, f->transport_len)); - return 0; -} - -#ifdef PICO_SUPPORT_PING -static void ping_recv_reply(struct pico_frame *f); -#endif - -static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f) -{ - struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; - static int firstpkt = 1; - static uint16_t last_id = 0; - static uint16_t last_seq = 0; - IGNORE_PARAMETER(self); - - if (hdr->type == PICO_ICMP_ECHO) { - hdr->type = PICO_ICMP_ECHOREPLY; - /* outgoing frames require a f->len without the ethernet header len */ - if (f->dev && f->dev->eth) - f->len -= PICO_SIZE_ETHHDR; - - if (!firstpkt && (hdr->hun.ih_idseq.idseq_id == last_id) && (last_seq == hdr->hun.ih_idseq.idseq_seq)) { - /* The network duplicated the echo. Do not reply. */ - pico_frame_discard(f); - return 0; - } - - firstpkt = 0; - last_id = hdr->hun.ih_idseq.idseq_id; - last_seq = hdr->hun.ih_idseq.idseq_seq; - pico_icmp4_checksum(f); - pico_ipv4_rebound(f); - } else if (hdr->type == PICO_ICMP_UNREACH) { - f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE; - pico_ipv4_unreachable(f, hdr->code); - } else if (hdr->type == PICO_ICMP_ECHOREPLY) { -#ifdef PICO_SUPPORT_PING - ping_recv_reply(f); -#endif - pico_frame_discard(f); - } else { - pico_frame_discard(f); - } - - return 0; -} - -static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f) -{ - IGNORE_PARAMETER(self); - IGNORE_PARAMETER(f); - dbg("Called %s\n", __FUNCTION__); - return 0; -} - -/* Interface: protocol definition */ -struct pico_protocol pico_proto_icmp4 = { - .name = "icmp4", - .proto_number = PICO_PROTO_ICMP4, - .layer = PICO_LAYER_TRANSPORT, - .process_in = pico_icmp4_process_in, - .process_out = pico_icmp4_process_out, - .q_in = &icmp_in, - .q_out = &icmp_out, -}; - -static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code) -{ - struct pico_frame *reply; - struct pico_icmp4_hdr *hdr; - struct pico_ipv4_hdr *info; - uint16_t f_tot_len; - - if (f == NULL) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len); - - if (f_tot_len < (sizeof(struct pico_ipv4_hdr))) - return -1; - - /* Truncate tot len to be at most 8 bytes + iphdr */ - if (f_tot_len > (sizeof(struct pico_ipv4_hdr) + 8u)) { - f_tot_len = (sizeof(struct pico_ipv4_hdr) + 8u); - } - - reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, f->dev, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE)); - info = (struct pico_ipv4_hdr*)(f->net_hdr); - hdr = (struct pico_icmp4_hdr *) reply->transport_hdr; - hdr->type = type; - hdr->code = code; - hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500); - hdr->hun.ih_pmtu.ipm_void = 0; - reply->transport_len = (uint16_t)(f_tot_len + PICO_ICMPHDR_UN_SIZE); - reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE; - memcpy(reply->payload, f->net_hdr, f_tot_len); - pico_icmp4_checksum(reply); - pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4); - return 0; -} - -int pico_icmp4_port_unreachable(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT); -} - -int pico_icmp4_proto_unreachable(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL); -} - -int pico_icmp4_dest_unreachable(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST); -} - -int pico_icmp4_ttl_expired(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS); -} - -MOCKABLE int pico_icmp4_frag_expired(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_REASS); -} - -int pico_icmp4_mtu_exceeded(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_NEEDFRAG); -} - -int pico_icmp4_packet_filtered(struct pico_frame *f) -{ - /*Parameter check executed in pico_icmp4_notify*/ - /*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/ - return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB); -} - -int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code) -{ - return pico_icmp4_notify(f, PICO_ICMP_PARAMPROB, code); -} - -/***********************/ -/* Ping implementation */ -/***********************/ -/***********************/ -/***********************/ -/***********************/ - - -#ifdef PICO_SUPPORT_PING - - -struct pico_icmp4_ping_cookie -{ - struct pico_ip4 dst; - uint16_t err; - uint16_t id; - uint16_t seq; - uint16_t size; - int count; - pico_time timestamp; - int interval; - int timeout; - void (*cb)(struct pico_icmp4_stats*); -}; - -static int cookie_compare(void *ka, void *kb) -{ - struct pico_icmp4_ping_cookie *a = ka, *b = kb; - if (a->id < b->id) - return -1; - - if (a->id > b->id) - return 1; - - return (a->seq - b->seq); -} - -static PICO_TREE_DECLARE(Pings, cookie_compare); - -static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie) -{ - struct pico_frame *echo = NULL; - struct pico_icmp4_hdr *hdr; - struct pico_device *dev = pico_ipv4_source_dev_find(&cookie->dst); - if (!dev) - return -1; - - echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size)); - if (!echo) - return -1; - - hdr = (struct pico_icmp4_hdr *) echo->transport_hdr; - - hdr->type = PICO_ICMP_ECHO; - hdr->code = 0; - hdr->hun.ih_idseq.idseq_id = short_be(cookie->id); - hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq); - echo->transport_len = (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size); - echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE; - echo->payload_len = cookie->size; - /* XXX: Fill payload */ - pico_icmp4_checksum(echo); - pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4); - return 0; -} - - -static void ping_timeout(pico_time now, void *arg) -{ - struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg; - IGNORE_PARAMETER(now); - - if(pico_tree_findKey(&Pings, cookie)) { - if (cookie->err == PICO_PING_ERR_PENDING) { - struct pico_icmp4_stats stats; - stats.dst = cookie->dst; - stats.seq = cookie->seq; - stats.time = 0; - stats.size = cookie->size; - stats.err = PICO_PING_ERR_TIMEOUT; - dbg(" ---- Ping timeout!!!\n"); - cookie->cb(&stats); - } - - pico_tree_delete(&Pings, cookie); - PICO_FREE(cookie); - } -} - -static void next_ping(pico_time now, void *arg); -static int send_ping(struct pico_icmp4_ping_cookie *cookie) -{ - uint32_t timeout_timer = 0; - struct pico_icmp4_stats stats; - pico_icmp4_send_echo(cookie); - cookie->timestamp = pico_tick; - timeout_timer = pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie); - if (!timeout_timer) { - goto fail; - } - if (cookie->seq < (uint16_t)cookie->count) { - if (!pico_timer_add((uint32_t)cookie->interval, next_ping, cookie)) { - pico_timer_cancel(timeout_timer); - goto fail; - } - } - return 0; - -fail: - dbg("ICMP4: Failed to start timer\n"); - cookie->err = PICO_PING_ERR_ABORTED; - stats.err = cookie->err; - cookie->cb(&stats); - pico_tree_delete(&Pings, cookie); - - return -1; -} - -static void next_ping(pico_time now, void *arg) -{ - struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg; - IGNORE_PARAMETER(now); - - if(pico_tree_findKey(&Pings, cookie)) { - if (cookie->err == PICO_PING_ERR_ABORTED) - return; - - if (cookie->seq < (uint16_t)cookie->count) { - newcookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie)); - if (!newcookie) - return; - - memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie)); - newcookie->seq++; - - if (pico_tree_insert(&Pings, newcookie)) { - dbg("ICMP4: Failed to insert new cookie in tree \n"); - PICO_FREE(newcookie); - return; - } - - if (send_ping(newcookie)) { - dbg("ICMP4: Failed to send ping\n"); - PICO_FREE(newcookie); - } - } - } -} - - -static void ping_recv_reply(struct pico_frame *f) -{ - struct pico_icmp4_ping_cookie test, *cookie; - struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; - test.id = short_be(hdr->hun.ih_idseq.idseq_id ); - test.seq = short_be(hdr->hun.ih_idseq.idseq_seq); - - cookie = pico_tree_findKey(&Pings, &test); - if (cookie) { - struct pico_icmp4_stats stats; - if (cookie->err == PICO_PING_ERR_ABORTED) - return; - - cookie->err = PICO_PING_ERR_REPLIED; - stats.dst = ((struct pico_ipv4_hdr *)f->net_hdr)->src; - stats.seq = cookie->seq; - stats.size = cookie->size; - stats.time = pico_tick - cookie->timestamp; - stats.err = cookie->err; - stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl; - if(cookie->cb != NULL) - cookie->cb(&stats); - } else { - dbg("Reply for seq=%d, not found.\n", test.seq); - } -} - -int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *)) -{ - static uint16_t next_id = 0x91c0; - struct pico_icmp4_ping_cookie *cookie; - - if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - cookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie)); - if (!cookie) { - pico_err = PICO_ERR_ENOMEM; - return -1; - } - - if (pico_string_to_ipv4(dst, (uint32_t *)&cookie->dst.addr) < 0) { - pico_err = PICO_ERR_EINVAL; - PICO_FREE(cookie); - return -1; - } - - cookie->seq = 1; - cookie->id = next_id++; - cookie->err = PICO_PING_ERR_PENDING; - cookie->size = (uint16_t)size; - cookie->interval = interval; - cookie->timeout = timeout; - cookie->cb = cb; - cookie->count = count; - - if (pico_tree_insert(&Pings, cookie)) { - dbg("ICMP4: Failed to insert cookie in tree \n"); - PICO_FREE(cookie); - return -1; - } - - if (send_ping(cookie)) { - PICO_FREE(cookie); - return -1; - } - - return cookie->id; - -} - -int pico_icmp4_ping_abort(int id) -{ - struct pico_tree_node *node; - int found = 0; - pico_tree_foreach(node, &Pings) - { - struct pico_icmp4_ping_cookie *ck = - (struct pico_icmp4_ping_cookie *) node->keyValue; - if (ck->id == (uint16_t)id) { - ck->err = PICO_PING_ERR_ABORTED; - found++; - } - } - if (found > 0) - return 0; /* OK if at least one pending ping has been canceled */ - - pico_err = PICO_ERR_ENOENT; - return -1; -} - -#endif diff --git a/core/deps/picotcp/modules/pico_ipv4.c b/core/deps/picotcp/modules/pico_ipv4.c index d0127086b..bdc7f6540 100644 --- a/core/deps/picotcp/modules/pico_ipv4.c +++ b/core/deps/picotcp/modules/pico_ipv4.c @@ -349,6 +349,11 @@ static int pico_ipv4_process_local_unicast_in(struct pico_frame *f) else pico_transport_receive(f, hdr->proto); + return 1; + } else if (f->dev->proxied) { + // Proxied device: deliver all traffic locally + pico_transport_receive(f, hdr->proto); + return 1; } else if (pico_tree_findKey(&Tree_dev_link, &test)) { #ifdef PICO_SUPPORT_UDP @@ -1052,7 +1057,10 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro } hdr->dst.addr = dst->addr; - hdr->src.addr = link->address.addr; + if (f->local_ip.addr) + hdr->src.addr = f->local_ip.addr; // Masqueraded + else + hdr->src.addr = link->address.addr; hdr->ttl = ttl; hdr->tos = f->send_tos; hdr->proto = proto; diff --git a/core/deps/picotcp/modules/pico_mdns.c b/core/deps/picotcp/modules/pico_mdns.c deleted file mode 100644 index 2d6cf0702..000000000 --- a/core/deps/picotcp/modules/pico_mdns.c +++ /dev/null @@ -1,3687 +0,0 @@ -/********************************************************************* - PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved. - See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - . - Author: Toon Stegen, Jelle De Vleeschouwer - *********************************************************************/ -#include "pico_config.h" -#include "pico_stack.h" -#include "pico_addressing.h" -#include "pico_socket.h" -#include "pico_ipv4.h" -#include "pico_ipv6.h" -#include "pico_tree.h" -#include "pico_mdns.h" - -#ifdef PICO_SUPPORT_MDNS - -/* --- Debugging --- */ -#ifdef DEBUG_MDNS -#define mdns_dbg dbg -#else -#define mdns_dbg(...) do {} while(0) -#endif - -#define PICO_MDNS_QUERY_TIMEOUT (10000) /* Ten seconds */ -#define PICO_MDNS_RR_TTL_TICK (1000) /* One second */ - -/* mDNS MTU size */ -#define PICO_MDNS_MAXBUF (1400u) - -/* --- Cookie flags --- */ -#define PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT (0x01u) -#define PICO_MDNS_PACKET_TYPE_ANSWER (0x02u) -#define PICO_MDNS_PACKET_TYPE_QUERY (0x04u) -#define PICO_MDNS_PACKET_TYPE_PROBE (0x08u) -#define PICO_MDNS_PACKET_TYPE_QUERY_ANY (0x00u) -/* --- Cookie status --- */ -#define PICO_MDNS_COOKIE_STATUS_ACTIVE (0xffu) -#define PICO_MDNS_COOKIE_STATUS_INACTIVE (0x00u) -#define PICO_MDNS_COOKIE_STATUS_CANCELLED (0x77u) -#define PICO_MDNS_COOKIE_TIMEOUT (10u) - -#define PICO_MDNS_SECTION_ANSWERS (0) -#define PICO_MDNS_SECTION_AUTHORITIES (1) -#define PICO_MDNS_SETCTIO_ADDITIONALS (2) - -#define PICO_MDNS_CTREE_DESTROY(rtree) \ - pico_tree_destroy((rtree), pico_mdns_cookie_delete); - -/* --- Question flags --- */ -#define PICO_MDNS_QUESTION_FLAG_PROBE (0x01u) -#define PICO_MDNS_QUESTION_FLAG_NO_PROBE (0x00u) -#define PICO_MDNS_QUESTION_FLAG_UNICAST_RES (0x02u) -#define PICO_MDNS_QUESTION_FLAG_MULTICAST_RES (0x00u) - -#define IS_QUESTION_PROBE_FLAG_SET(x) \ - (((x) & PICO_MDNS_QUESTION_FLAG_PROBE) ? (1) : (0)) -#define IS_QUESTION_UNICAST_FLAG_SET(x) \ - (((x) & PICO_MDNS_QUESTION_FLAG_UNICAST_RES) ? (1) : (0)) -#define IS_QUESTION_MULTICAST_FLAG_SET(x) \ - (((x) & PICO_MDNS_QUESTION_FLAG_UNICAST_RES) ? (0) : (1)) - -/* Resource Record flags */ -#define PICO_MDNS_RECORD_ADDITIONAL (0x08u) -#define PICO_MDNS_RECORD_SEND_UNICAST (0x10u) -#define PICO_MDNS_RECORD_CURRENTLY_PROBING (0x20u) -#define PICO_MDNS_RECORD_PROBED (0x40u) -#define PICO_MDNS_RECORD_CLAIMED (0x80u) - -#define IS_SHARED_RECORD(x) \ - ((x)->flags & PICO_MDNS_RECORD_SHARED) -#define IS_UNIQUE_RECORD(x) \ - (!((x)->flags & PICO_MDNS_RECORD_SHARED)) -#define IS_RECORD_PROBING(x) \ - ((x)->flags & PICO_MDNS_RECORD_CURRENTLY_PROBING) -#define IS_UNICAST_REQUESTED(x) \ - ((x)->flags & PICO_MDNS_RECORD_SEND_UNICAST) -#define IS_RECORD_VERIFIED(x) \ - ((x)->flags & PICO_MDNS_RECORD_PROBED) -#define IS_RECORD_CLAIMED(x) \ - ((x)->flags & PICO_MDNS_RECORD_CLAIMED) - -/* Set and clear flags */ -#define PICO_MDNS_SET_FLAG(x, b) (x = ((x) | (uint8_t)(b))) -#define PICO_MDNS_CLR_FLAG(x, b) (x = (uint8_t)(((x) & (~((uint8_t)(b)))))) - -/* Set and clear MSB of BE short */ -#define PICO_MDNS_SET_MSB(x) (x = x | (uint16_t)(0x8000u)) -#define PICO_MDNS_CLR_MSB(x) (x = x & (uint16_t)(0x7fffu)) -#define PICO_MDNS_SET_MSB_BE(x) (x = x | (uint16_t)(short_be(0x8000u))) -#define PICO_MDNS_CLR_MSB_BE(x) (x = x & (uint16_t)(short_be(0x7fffu))) -#define PICO_MDNS_IS_MSB_SET(x) ((x & 0x8000u) ? 1 : 0) - -/* **************************************************************************** - * mDNS cookie - * *****************************************************************************/ -struct pico_mdns_cookie -{ - pico_dns_qtree qtree; /* Question tree */ - pico_mdns_rtree antree; /* Answer tree */ - pico_mdns_rtree artree; /* Additional record tree */ - uint8_t count; /* Times to send the query */ - uint8_t type; /* QUERY/ANNOUNCE/PROBE/ANSWER */ - uint8_t status; /* Active status */ - uint8_t timeout; /* Timeout counter */ - uint32_t send_timer; /* For sending events */ - void (*callback)(pico_mdns_rtree *, - char *, - void *); /* Callback */ - void *arg; /* Argument to pass to callback */ -}; - -/* MARK: TREES & GLOBAL VARIABLES */ - -/* MDNS Communication variables */ -static struct pico_socket *mdns_sock_ipv4 = NULL; -static uint16_t mdns_port = 5353u; -static struct pico_ip4 inaddr_any = { - 0 -}; - -/* **************************************************************************** - * Hostname for this machine, only 1 hostname can be set. - * Following RFC6267: 15.4 Recommendation - * *****************************************************************************/ -static char *_hostname = NULL; - -static void (*init_callback)(pico_mdns_rtree *, char *, void *) = 0; - -/* **************************************************************************** - * Compares 2 mDNS records by name and type only - * - * @param a mDNS record A - * @param b mDNS record B - * @return 0 when name and type of records are equal, returns difference when - * they're not. - * ****************************************************************************/ -static int -pico_mdns_record_cmp_name_type( void *a, void *b ) -{ - struct pico_mdns_record *_a = NULL, *_b = NULL; - - /* Check params */ - if (!(_a = (struct pico_mdns_record *)a) || - !(_b = (struct pico_mdns_record *)b)) { - pico_err = PICO_ERR_EINVAL; - return -1; /* Don't want a wrong result when NULL-pointers are passed */ - } - - return pico_dns_record_cmp_name_type(_a->record, _b->record); -} - -/* **************************************************************************** - * Compares 2 mDNS records by type, name AND rdata for a truly unique result - * - * @param ra mDNS record A - * @param rb mDNS record B - * @return 0 when records are equal, returns difference when they're not. - * ****************************************************************************/ -int -pico_mdns_record_cmp( void *a, void *b ) -{ - /* Check params */ - if (!a || !b) { - if (!a && !b) - return 0; - - pico_err = PICO_ERR_EINVAL; - return -1; /* Don't want a wrong result when NULL-pointers are passed */ - } - - return pico_dns_record_cmp((void*)(((struct pico_mdns_record *)a)->record), - (void*)(((struct pico_mdns_record *)b)->record)); -} - -/* **************************************************************************** - * Compares 2 mDNS cookies again each other. Only compares questions since a - * only a cookie query will be added to the tree. And there shouldn't be 2 - * different cookies with the same questions in the tree. - * - * @param ka mDNS cookie A - * @param kb mDNS cookie B - * @return 0 when cookies are equal, returns difference when they're not. - * ****************************************************************************/ -static int -pico_mdns_cookie_cmp( void *ka, void *kb ) -{ - struct pico_mdns_cookie *a = (struct pico_mdns_cookie *)ka; - struct pico_mdns_cookie *b = (struct pico_mdns_cookie *)kb; - struct pico_dns_question *qa = NULL, *qb = 0; - struct pico_tree_node *na = NULL, *nb = 0; - uint16_t ca = 0, cb = 0; - int ret = 0; - - /* Check params */ - if (!a || !b) { - pico_err = PICO_ERR_EINVAL; - return -1; /* Don't want a wrong result when NULL-pointers are passed */ - } - - /* Start comparing the questions */ - for (na = pico_tree_firstNode(a->qtree.root), - nb = pico_tree_firstNode(b->qtree.root); - (na != &LEAF) && (nb != &LEAF); - na = pico_tree_next(na), - nb = pico_tree_next(nb)) { - qa = na->keyValue; - qb = nb->keyValue; - if ((qa) && (qb) && (ret = pico_dns_question_cmp(qa, qb))) - return ret; - } - /* Check for lengths difference */ - ca = pico_tree_count(&(a->qtree)); - cb = pico_tree_count(&(b->qtree)); - if (ca != cb) - return (int)((int)ca - (int)cb); - - /* Cookies contain same questions, shouldn't happen */ - return 0; -} - -/* - * Hash to identify mDNS timers with - */ -static uint32_t mdns_hash = 0; - -/* - * mDNS specific timer creation, to identify if timers are - * created by mDNS module - */ -static uint32_t -pico_mdns_timer_add(pico_time expire, - void (*timer)(pico_time, void *), - void *arg) -{ - return pico_timer_add_hashed(expire, timer, arg, mdns_hash); -} - -#if PICO_MDNS_ALLOW_CACHING == 1 -/* Cache records from mDNS peers on the network */ -static PICO_TREE_DECLARE(Cache, &pico_mdns_record_cmp); -#endif - -/* My records for which I want to have the authority */ -static PICO_TREE_DECLARE(MyRecords, &pico_mdns_record_cmp_name_type); - -/* Cookie-tree */ -static PICO_TREE_DECLARE(Cookies, &pico_mdns_cookie_cmp); - -/* **************************************************************************** - * MARK: PROTOTYPES */ -static int -pico_mdns_getrecord_generic( const char *url, uint16_t type, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg); - -static void -pico_mdns_send_probe_packet( pico_time now, void *arg ); - -static int -pico_mdns_reclaim( pico_mdns_rtree record_tree, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ); -/* EOF PROTOTYPES - * ****************************************************************************/ - -/* MARK: v MDNS NAMES */ - -#define IS_NUM(c) (((c) >= '0') && ((c) <= '9')) -/* **************************************************************************** - * Tries to convert the characters after '-' to a numeric value. - * - * @param opening Pointer to dash index. - * @param closing Pointer to end of label. - * @return Numeric value of suffix on success - * ****************************************************************************/ -static inline uint16_t -pico_mdns_suffix_to_uint16( char *opening, char *closing) -{ - uint16_t n = 0; - char *i = 0; - - /* Check params */ - if (!opening || !closing || - ((closing - opening) > 5) || - ((closing - opening) < 0)) - return 0; - - for (i = (char *)(opening + 1); i < closing; i++) { - if (!IS_NUM(*i)) - return 0; - - n = (uint16_t)((n * 10) + (*i - '0')); - } - return n; -} - -#define iterate_first_label_name_reverse(iterator, name) \ - for ((iterator) = \ - (*name < (char)63) ? ((char *)(name + *name)) : (name); \ - (iterator) > (name); \ - (iterator)--) - -/* **************************************************************************** - * Checks whether there is already a conflict-suffix already present in the - * first label of a name or not. - * - * @param name Name in DNS name notation you want to check for a suffix. - * @param o_i Pointer-pointer, will get filled with location to '-'-char. - * @param c_i Pointer-pointer, will get filled with end of label. - * @return Returns value of the suffix, when it's present, 0 when no correct - * suffix is present. - * ****************************************************************************/ -static uint16_t -pico_mdns_is_suffix_present( char name[], - char **o_i, - char **c_i ) -{ - char *i = NULL; - uint16_t n = 0; - - *o_i = NULL; /* Clear out indexes */ - *c_i = NULL; - - /* Find the end of label. */ - *c_i = (name + *name + 1); - - iterate_first_label_name_reverse(i, name) { - /* Find the last dash */ - if ((*c_i) && (i < *c_i) && *i == '-') { - *o_i = i; - break; - } - } - - /* Convert the string suffix to a number */ - if (!(n = pico_mdns_suffix_to_uint16(*o_i, *c_i))) { - *o_i = NULL; - *c_i = NULL; - } - - return n; -} - -/* **************************************************************************** - * Manual string to uint16_t conversion. - * - * @param n Numeric value you want to convert. - * @param s String to convert to - * @return void - * ****************************************************************************/ -static void pico_itoa( uint16_t n, char s[] ) -{ - int i = 0, j = 0; - char c = 0; - - /* Get char values */ - do { - s[i++] = (char)(n % 10 + '0'); - } while ((n /= 10) > 0); - - /* Reverse the string */ - for (i = 0, j = (int)(pico_dns_strlen(s) - 1); i < j; i++, j--) { - c = s[i]; - s[i] = s[j]; - s[j] = c; - } -} - -/* **************************************************************************** - * Generates a new name by appending a conflict resolution suffix to the first - * label of an FQDN. - * - * @param rname Name you want to append the suffix to - * @return Newly created FQDN with suffix appended to first label. - * ****************************************************************************/ -static char * -pico_mdns_resolve_name_conflict( char rname[] ) -{ - char *new_rname = NULL; - char suffix[5] = { - 0 - }, nsuffix[5] = { - 0 - }, copy_offset = 0; - char *o_i = NULL, *c_i = NULL; - uint16_t new_len = (uint16_t)(pico_dns_strlen(rname) + 1); - uint8_t nslen = 0, slen = 0, ns = 0; - - /* Check params */ - if (pico_dns_check_namelen(new_len)) { - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - /* Check whether a conflict-suffix is already present in the name */ - if ((ns = (uint8_t)pico_mdns_is_suffix_present(rname, &o_i, &c_i))) { - pico_itoa(ns, suffix); - pico_itoa(++ns, nsuffix); - slen = (uint8_t)pico_dns_strlen(suffix); - nslen = (uint8_t)pico_dns_strlen(nsuffix); - new_len = (uint16_t)(new_len + nslen - slen); - } else { - /* If no suffix is present */ - c_i = (o_i = rname + *rname) + 1; - new_len = (uint16_t)(new_len + 2u); - memcpy((void *)nsuffix, "-2\0", (size_t)3); - } - - /* Provide space for the new name */ - if (!(new_rname = PICO_ZALLOC(new_len))) { - pico_err = PICO_ERR_ENOMEM; - return NULL; - } - - /* Assemble the new name again */ - copy_offset = (char)((o_i - rname + 1)); - memcpy(new_rname, rname, (size_t)(copy_offset)); - strcpy(new_rname + copy_offset, nsuffix); - strcpy(new_rname + copy_offset + pico_dns_strlen(nsuffix), c_i); - /* Set the first length-byte */ - new_rname[0] = (char)(new_rname[0] + new_len - pico_dns_strlen(rname) - 1); - return new_rname; -} - -/* MARK: ^ MDNS NAMES */ -/* MARK: v MDNS QUESTIONS */ - -/* **************************************************************************** - * Creates a standalone mDNS Question with a given name and type. - * - * @param url DNS question name in URL format. Will be converted to DNS - * name notation format. - * @param len Will be filled with the total length of the DNS question. - * @param proto Protocol for which you want to create a question. Can be - * either PICO_PROTO_IPV4 or PICO_PROTO_IPV6. - * @param qtype DNS type of the question to be. - * @param flags With the flags you can specify if the question should be - * a QU-question rather than a QM-question - * @param reverse When this is true, a reverse resolution name will be gene- - * from the URL - * @return Returns pointer to the created mDNS Question on success, NULL on - * failure. - * ****************************************************************************/ -static struct pico_dns_question * -pico_mdns_question_create( const char *url, - uint16_t *len, - uint8_t proto, - uint16_t qtype, - uint8_t flags, - uint8_t reverse ) -{ - uint16_t qclass = PICO_DNS_CLASS_IN; - - /* Set the MSB of the qclass field according to the mDNS format */ - if (IS_QUESTION_UNICAST_FLAG_SET(flags)) - PICO_MDNS_SET_MSB(qclass); - - /* Fill in the question suffix */ - if (IS_QUESTION_PROBE_FLAG_SET(flags)) - qtype = PICO_DNS_TYPE_ANY; - - /* Create a question as you would with plain DNS */ - return pico_dns_question_create(url, len, proto, qtype, qclass, reverse); -} - -/* MARK: ^ MDNS QUESTIONS */ -/* MARK: v MDNS RECORDS */ - -/* **************************************************************************** - * Just makes a hardcopy from a single mDNS resource record. - * - * @param record mDNS record you want to create a copy from - * @return Pointer to copied mDNS resource record - * ****************************************************************************/ -static struct pico_mdns_record * -pico_mdns_record_copy( struct pico_mdns_record *record ) -{ - struct pico_mdns_record *copy = NULL; - - /* Check params */ - if (!record) { - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - /* Provide space for the copy */ - if (!(copy = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) { - pico_err = PICO_ERR_ENOMEM; - return NULL; - } - - /* Copy the DNS record */ - if (!(copy->record = pico_dns_record_copy(record->record))) { - PICO_FREE(copy); - return NULL; - } - - /* Copy the fields */ - copy->current_ttl = record->current_ttl; - copy->flags = record->flags; - copy->claim_id = record->claim_id; - - return copy; -} - -/* **************************************************************************** - * Looks for multiple mDNS records in a tree with the same name. - * - * @param tree Tree in which you want to search. - * @param name Name you want to search for. - * @return Tree with found hits, can possibly be empty - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_rtree_find_name( pico_mdns_rtree *tree, - const char *name, - uint8_t copy ) -{ - PICO_MDNS_RTREE_DECLARE(hits); - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - - /* Check params */ - if (!name || !tree) { - pico_err = PICO_ERR_EINVAL; - return hits; - } - - /* Iterate over tree */ - pico_tree_foreach(node, tree) { - record = node->keyValue; - if (record && strcasecmp(record->record->rname, name) == 0) { - if (copy) - record = pico_mdns_record_copy(record); - - if (record) - if (pico_tree_insert(&hits, record) != NULL) - /* either key was already in there, or couldn't be inserted. */ - /* Only delete record if it was copied */ - if (copy) - pico_mdns_record_delete((void **)&record); - } - } - - return hits; -} - -/* **************************************************************************** - * Looks for (possibly) multiple mDNS records in a tree with the same name and - * type. - * - * @param tree Tree in which you want to search. - * @param name Name you want to search for. - * @param rtype DNS type you want to search for. - * @return Tree with found hits, can possibly be empty. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_rtree_find_name_type( pico_mdns_rtree *tree, - char *name, - uint16_t rtype, - uint8_t copy ) -{ - PICO_MDNS_RTREE_DECLARE(hits); - - struct pico_dns_record_suffix test_dns_suffix = { - 0, 1, 0, 0 - }; - struct pico_dns_record test_dns_record = { - 0 - }; - struct pico_mdns_record test = { - 0 - }; - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - test_dns_record.rsuffix = &test_dns_suffix; - test.record = &test_dns_record; - - /* Check params */ - if (!name || !tree) { - pico_err = PICO_ERR_EINVAL; - return hits; - } - - test.record->rname = name; - test.record->rsuffix->rtype = short_be(rtype); - - /* Iterate over the tree */ - pico_tree_foreach(node, tree) { - record = node->keyValue; - if ((record) && (0 == pico_mdns_record_cmp_name_type(record, &test))) { - if (copy) - record = pico_mdns_record_copy(record); - - if (record){ - if (pico_tree_insert(&hits, record) != NULL) { - /* either key was already in there, or couldn't be inserted. */ - /* Only delete record if it was copied */ - if (copy) - pico_mdns_record_delete((void **)&record); - } - } - } - } - - return hits; -} - -/* **************************************************************************** - * Deletes multiple mDNS records in a tree with the same name. - * - * @param tree Tree from which you want to delete records by name. - * @param name Name of records you want to delete from the tree. - * @return 0 on success, something else on failure. - * ****************************************************************************/ -static int -pico_mdns_rtree_del_name( pico_mdns_rtree *tree, - const char *name ) -{ - struct pico_tree_node *node = NULL, *safe = NULL; - struct pico_mdns_record *record = NULL; - - /* Check params */ - if (!name || !tree) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Iterate over tree */ - pico_tree_foreach_safe(node, tree, safe) { - record = node->keyValue; - if (record && strcasecmp(record->record->rname, name) == 0) { - record = pico_tree_delete(tree, record); - pico_mdns_record_delete((void **)&record); - } - } - - return 0; -} - -/* **************************************************************************** - * Deletes (possibly) multiple mDNS records from a tree with same name and - * type. - * - * @param tree Tree from which you want to delete records by name and type. - * @param name Name of records you want to delete. - * @param type DNS type of records you want to delete. - * @return 0 on success, something else on failure. - * ****************************************************************************/ -#if PICO_MDNS_ALLOW_CACHING == 1 -static int -pico_mdns_rtree_del_name_type( pico_mdns_rtree *tree, - char *name, - uint16_t type ) -{ - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_record *record = NULL; - struct pico_dns_record_suffix test_dns_suffix = { - 0, 1, 0, 0 - }; - struct pico_dns_record test_dns_record = { - 0 - }; - struct pico_mdns_record test = { - 0 - }; - - test_dns_record.rsuffix = &test_dns_suffix; - test.record = &test_dns_record; - - /* Check params */ - if (!name || !tree) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - test.record->rname = name; - test.record->rsuffix->rtype = short_be(type); - - /* Iterate over the tree */ - pico_tree_foreach_safe(node, tree, next) { - record = node->keyValue; - if ((record) && (0 == pico_mdns_record_cmp_name_type(record, &test))) { - record = pico_tree_delete(tree, record); - pico_mdns_record_delete((void **)&record); - } - } - - return 0; -} -#endif - -/* **************************************************************************** - * Makes a hardcopy from a single mDNS resource record, but sets a new name - * for the copy. - * - * @param record mDNS record you want to copy. - * @param new_rname New name you want to set the name of the record to. - * @return Pointer to the copy on success, NULL-pointer on failure. - * ****************************************************************************/ -static struct pico_mdns_record * -pico_mdns_record_copy_with_new_name( struct pico_mdns_record *record, - const char *new_rname ) -{ - struct pico_mdns_record *copy = NULL; - uint16_t slen = (uint16_t)(pico_dns_strlen(new_rname) + 1u); - - /* Check params */ - if (!new_rname || pico_dns_check_namelen(slen)) { - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - /* Copy the record */ - if (!(copy = pico_mdns_record_copy(record))) - return NULL; - - /* Provide a new string */ - PICO_FREE(copy->record->rname); - if (!(copy->record->rname = PICO_ZALLOC(slen))) { - pico_err = PICO_ERR_ENOMEM; - pico_mdns_record_delete((void **)©); - return NULL; - } - - memcpy((void *)(copy->record->rname), new_rname, slen); - copy->record->rname_length = slen; - - return copy; -} - -/* **************************************************************************** - * Generates (copies) new records from conflicting ones with another name. - * deletes - * - * @param conflict_records mDNS record tree that contains conflicting records - * @param conflict_name Name for which the conflict occurred. This is to be - * able to delete the conflicting records from the tree - * @param new_name To generate new records from the conflicting ones, - * with this new name. - * @return A mDNS record tree that contains all the newly generated records. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_generate_new_records( pico_mdns_rtree *conflict_records, - char *conflict_name, - char *new_name ) -{ - PICO_MDNS_RTREE_DECLARE(new_records); - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_record *record = NULL, *new_record = NULL; - - /* Delete all the conflicting records from MyRecords */ - if (pico_mdns_rtree_del_name(&MyRecords, conflict_name)) - return new_records; - - pico_tree_foreach_safe(node, conflict_records, next) { - record = node->keyValue; - if (record && strcasecmp(record->record->rname, conflict_name) == 0) { - /* Create a new record */ - new_record = pico_mdns_record_copy_with_new_name(record, new_name); - if (!new_record) { - mdns_dbg("Could not create new non-conflicting record!\n"); - return new_records; - } - - new_record->flags &= (uint8_t)(~(PICO_MDNS_RECORD_PROBED | - PICO_MDNS_RECORD_SHARED | - PICO_MDNS_RECORD_CURRENTLY_PROBING)); - - /* Add the record to the new tree */ - if (pico_tree_insert(&new_records, new_record)) { - mdns_dbg("Could not add new non-conflicting record to the tree!\n"); - pico_mdns_record_delete((void **)&new_record); - return new_records; - } - - /* Delete the old conflicting record */ - record = pico_tree_delete(conflict_records, record); - if (pico_mdns_record_delete((void **)&record)) { - mdns_dbg("Could not delete old conflict record from tree!\n"); - return new_records; - } - } - } - - return new_records; -} - -/* **************************************************************************** - * When hosts observe an unsolicited record, no cookie is currently active - * for that, so it has to check in MyRecords if no conflict occurred for a - * record it has already registered. When this occurs the conflict should be - * resolved as with a normal cookie, just without the cookie. - * - * @param record mDNS record for which the conflict occurred. - * @param rname DNS name for which the conflict occurred in DNS name notation. - * @return 0 when the resolving is applied successfully, 1 otherwise. - * ****************************************************************************/ -static int -pico_mdns_record_resolve_conflict( struct pico_mdns_record *record, - char *rname ) -{ - int retval; - PICO_MDNS_RTREE_DECLARE(new_records); - struct pico_mdns_record *copy = NULL; - char *new_name = NULL; - - /* Check params */ - if (!record || !rname || IS_SHARED_RECORD(record)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Step 2: Create a new name depending on current name */ - if (!(new_name = pico_mdns_resolve_name_conflict(rname))) - return -1; - - copy = pico_mdns_record_copy_with_new_name(record, new_name); - PICO_FREE(new_name); - if (copy){ - if (pico_tree_insert(&new_records, copy)) { - mdns_dbg("MDNS: Failed to insert copy in tree\n"); - pico_mdns_record_delete((void **)©); - return -1; - } - } - - /* Step 3: delete conflicting record from my records */ - pico_tree_delete(&MyRecords, record); - pico_mdns_record_delete((void **)&record); - - /* Step 4: Try to reclaim the newly created records */ - retval = pico_mdns_reclaim(new_records, init_callback, NULL); - pico_tree_destroy(&new_records, NULL); - return retval; -} - -/* **************************************************************************** - * Determines if my_record is lexicographically later than peer_record, returns - * positive value when this is the case. Check happens by comparing rtype first - * and then rdata as prescribed by RFC6762. - * - * @param my_record Record this hosts want to claim. - * @param peer_record Record the peer host wants to claim (the enemy!) - * @return positive value when my record is lexicographically later - * ****************************************************************************/ -static int -pico_mdns_record_am_i_lexi_later( struct pico_mdns_record *my_record, - struct pico_mdns_record *peer_record) -{ - struct pico_dns_record *my = NULL, *peer = NULL; - uint16_t mclass = 0, pclass = 0, mtype = 0, ptype = 0; - int dif = 0; - - /* Check params */ - if (!my_record || !peer_record || - !(my = my_record->record) || !(peer = peer_record->record)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* - * First compare the record class (excluding cache-flush bit described in - * section 10.2) - * The numerically greater class wins - */ - mclass = PICO_MDNS_CLR_MSB_BE(my->rsuffix->rclass); - pclass = PICO_MDNS_CLR_MSB_BE(peer->rsuffix->rclass); - if ((dif = (int)((int)mclass - (int)pclass))) { - return dif; - } - - /* Second, compare the rrtypes */ - mtype = (my->rsuffix->rtype); - ptype = (peer->rsuffix->rtype); - if ((dif = (int)((int)mtype - (int)ptype))) { - return dif; - } - - /* Third compare binary content of rdata (no regard for meaning or structure) */ - - /* When using name compression, names MUST be uncompressed before comparison. See secion 8.2 in RFC6762 - This is already the case, but we won't check for it here. - The current execution stack to get here is: - > pico_mdns_handle_data_as_answers_generic - > > pico_dns_record_decompress - > > pico_mdns_handle_single_authority - > > > pico_mdns_cookie_apply_spt - > > > > pico_mdns_record_am_i_lexi_later - - Make sure pico_dns_record_decompress is executed before pico_mdns_record_am_i_lexi_later gets called, if problems ever arise with this function. - */ - - /* Then compare rdata */ - return pico_dns_rdata_cmp(my->rdata, peer->rdata, - short_be(my->rsuffix->rdlength), - short_be(peer->rsuffix->rdlength), PICO_DNS_CASE_SENSITIVE); -} - -/* **************************************************************************** - * Deletes a single mDNS resource record. - * - * @param record Void-pointer to mDNS Resource Record. Can be used with pico_- - * tree-destroy. - * @return Returns 0 on success, something else on failure. - * ****************************************************************************/ -int -pico_mdns_record_delete( void **record ) -{ - struct pico_mdns_record **rr = (struct pico_mdns_record **)record; - - /* Check params */ - if (!rr || !(*rr)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Delete DNS record contained */ - if (((*rr)->record)) { - pico_dns_record_delete((void **)&((*rr)->record)); - } - - /* Delete the record itself */ - PICO_FREE(*rr); - *record = NULL; - - return 0; -} - -/* **************************************************************************** - * Creates a single standalone mDNS resource record with given name, type and - * data. - * - * @param url DNS rrecord name in URL format. Will be converted to DNS - * name notation format. - * @param _rdata Memory buffer with data to insert in the resource record. If - * data of record should contain a DNS name, the name in the - * data buffer needs to be in URL-format. - * @param datalen The exact length in bytes of the _rdata-buffer. If data of - * record should contain a DNS name, datalen needs to be - * pico_dns_strlen(_rdata). - * @param len Will be filled with the total length of the DNS rrecord. - * @param rtype DNS type of the resource record to be. - * @param rclass DNS class of the resource record to be. - * @param rttl DNS ttl of the resource record to be. - * @param flags You can specify if the mDNS record should be a shared record - * rather than a unique record. - * @return Pointer to newly created mDNS resource record. - * ****************************************************************************/ -struct pico_mdns_record * -pico_mdns_record_create( const char *url, - void *_rdata, - uint16_t datalen, - uint16_t rtype, - uint32_t rttl, - uint8_t flags ) -{ - struct pico_mdns_record *record = NULL; - uint16_t len = 0; - uint16_t cl = 0; - - /* Check params */ - if (!url || !_rdata) { - pico_err = PICO_ERR_EINVAL; - return NULL; - } /* Block 1, 2 paths */ - - /* Provide space for the new mDNS resource record */ - if (!(record = PICO_ZALLOC(sizeof(struct pico_mdns_record)))) { - pico_err = PICO_ERR_ENOMEM; - return NULL; - } /* Block 2, 1 path */ - else { - /* Try to create the actual DNS record */ - if (!(record->record = pico_dns_record_create(url, _rdata, datalen, - &len, rtype, - PICO_DNS_CLASS_IN, rttl))) { - mdns_dbg("Could not create DNS record for mDNS!\n"); - PICO_FREE(record); - return NULL; - } /* Block 3, 2 paths */ - } /* Block 4, Block 3 = 2 paths */ - /* Block 5, (Block 4 + Block 2) * Block 1 = 6 paths */ - - /* Initialise fields */ - record->current_ttl = rttl; - - /* Set the MSB of the DNS class if it's a unique record */ - if (!((flags) & PICO_MDNS_RECORD_SHARED)) { - cl = record->record->rsuffix->rclass; - record->record->rsuffix->rclass = PICO_MDNS_SET_MSB_BE(cl); - } /* Block 6, 2 paths */ - /* Block 7, Block 6 * Block 5 * Block 1 = 12 paths */ - - record->flags = flags; - record->claim_id = 0; - - return record; -} - -/* MARK: ^ MDNS RECORDS */ -/* MARK: v MDNS COOKIES */ - -/* **************************************************************************** - * Deletes a single mDNS packet cookie and frees memory. - * - * @param cookie Void-pointer to mDNS cookie, allow to be used with pico_tree- - * destroy. - * @return Returns 0 on success, something else on failure. - * ****************************************************************************/ -static int -pico_mdns_cookie_delete( void **ptr ) -{ - struct pico_mdns_cookie **c = (struct pico_mdns_cookie **)ptr; - - /* Check params */ - if (!c || !(*c)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Destroy the vectors contained */ - PICO_DNS_QTREE_DESTROY(&((*c)->qtree)); - PICO_MDNS_RTREE_DESTROY(&((*c)->antree)); - PICO_MDNS_RTREE_DESTROY(&((*c)->artree)); - - /* Delete the cookie itself */ - PICO_FREE(*c); - *c = NULL; - - return 0; -} - -/* **************************************************************************** - * Creates a single standalone mDNS cookie - * - * @param qtree DNS questions you want to insert in the cookie. - * @param antree mDNS answers/authority records you want to add to cookie. - * @param artree mDNS additional records you want to add to cookie. - * @param count Times you want to send the cookie as a packet on the wire. - * @param type Type of packet you want to create from the cookie. - * @param callback Callback when the host receives responses for the cookie. - * @return Pointer to newly create cookie, NULL on failure. - * ****************************************************************************/ -static struct pico_mdns_cookie * -pico_mdns_cookie_create( pico_dns_qtree qtree, - pico_mdns_rtree antree, - pico_mdns_rtree artree, - uint8_t count, - uint8_t type, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - struct pico_mdns_cookie *cookie = NULL; /* Packet cookie to send */ - - /* Provide space for the mDNS packet cookie */ - cookie = PICO_ZALLOC(sizeof(struct pico_mdns_cookie)); - if (!cookie) { - pico_err = PICO_ERR_ENOMEM; - return NULL; - } - - /* Fill in the fields */ - cookie->qtree = qtree; - cookie->antree = antree; - cookie->artree = artree; - cookie->count = count; - cookie->type = type; - cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE; - cookie->timeout = PICO_MDNS_COOKIE_TIMEOUT; - cookie->callback = callback; - cookie->arg = arg; - return cookie; -} - -/* **************************************************************************** - * Apply Simultaneous Probe Tiebreakin (S.P.T.) on a probe-cookie. - * See RFC6762: 8.2. Simultaneous Probe Tiebreaking - * - * @param cookie Cookie which contains the record which is simult. probed. - * @param answer Authority record received from peer which is simult. probed. - * @return 0 when SPT is applied correctly, -1 otherwise. - * ****************************************************************************/ -static int -pico_mdns_cookie_apply_spt( struct pico_mdns_cookie *cookie, - struct pico_dns_record *answer) -{ - struct pico_mdns_record *my_record = NULL; - struct pico_mdns_record peer_record; - - /* Check params */ - if ((!cookie) || !answer || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE; - - /* Implement Simultaneous Probe Tiebreaking */ - peer_record.record = answer; - my_record = pico_tree_findKey(&MyRecords, &peer_record); - if (!my_record || !IS_RECORD_PROBING(my_record)) { - mdns_dbg("This is weird! My record magically removed...\n"); - return -1; - } - - if (pico_mdns_record_am_i_lexi_later(my_record, &peer_record) > 0) { - mdns_dbg("My record is lexicographically later! Yay!\n"); - cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE; - } else { - pico_timer_cancel(cookie->send_timer); - cookie->timeout = PICO_MDNS_COOKIE_TIMEOUT; - cookie->count = PICO_MDNS_PROBE_COUNT; - cookie->send_timer = pico_mdns_timer_add(1000, pico_mdns_send_probe_packet, - cookie); - if (!cookie->send_timer) { - mdns_dbg("cookie_apply_spt: failed to start timer\n"); - return -1; - } - mdns_dbg("Probing postponed by one second because of S.P.T.\n"); - } - - return 0; -} - -static int -pico_mdns_cookie_del_questions( struct pico_mdns_cookie *cookie, - char *rname ) -{ - uint16_t qc = 0; - - /* Step 1: Remove question with that name from cookie */ - pico_dns_qtree_del_name(&(cookie->qtree), rname); - cookie->antree.root = &LEAF; - - /* Check if there are no questions left, cancel events if so and delete */ - if (!(qc = pico_tree_count(&(cookie->qtree)))) { - pico_timer_cancel(cookie->send_timer); - cookie = pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void **)&cookie); - } - - return 0; -} - -/* **************************************************************************** - * Applies conflict resolution mechanism to a cookie, when a conflict occurs - * for a name which is present in the cookie. - * - * @param cookie Cookie on which you want to apply the conflict resolution- - * mechanism. - * @param rname Name for which the conflict occurred. A new non-conflicting - * name will be generated from this string. - * @return Returns 0 on success, something else on failure. - * ****************************************************************************/ -static int -pico_mdns_cookie_resolve_conflict( struct pico_mdns_cookie *cookie, - char *rname ) -{ - struct pico_tree_node *node = NULL; - struct pico_dns_question *question = NULL; - PICO_MDNS_RTREE_DECLARE(new_records); - PICO_MDNS_RTREE_DECLARE(antree); - char *new_name = NULL; - void (*callback)(pico_mdns_rtree *, char *, void *); - void *arg = NULL; - int retval; - - /* Check params */ - if ((!cookie) || !rname || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Convert rname to url */ - mdns_dbg("CONFLICT for probe query with name '%s' occurred!\n", rname); - - /* Store some information about a cookie for later on */ - antree = cookie->antree; - callback = cookie->callback; - arg = cookie->arg; - - /* Find the first question in the cookie with the name for which - * the conflict occured. When found, generate a new name. - * - * DNS conflict is case-insensitive. However, we want to keep the original - * capitalisation for the new probe. */ - pico_tree_foreach(node, &(cookie->qtree)) { - question = (struct pico_dns_question *)node->keyValue; - if ((question) && (strcasecmp(question->qname, rname) == 0)) { - /* Create a new name depending on current name */ - new_name = pico_mdns_resolve_name_conflict(question->qname); - - /* Step 1: Check if the new name succeeded, if not: error. */ - if (!new_name) { - /* Delete questions from cookie even if generating a new name failed */ - pico_mdns_cookie_del_questions(cookie, rname); - return -1; - } - - break; - } - } - - /* Step 2: Remove questions with this name from the cookie */ - pico_mdns_cookie_del_questions(cookie, rname); - - /* Step 3: Create records with new name for the records with that name */ - new_records = pico_mdns_generate_new_records(&antree, rname, new_name); - PICO_FREE(new_name); - - /* Step 4: Try to reclaim the newly created records */ - retval = pico_mdns_reclaim(new_records, callback, arg); - pico_tree_destroy(&new_records, NULL); - return retval; -} - -/* **************************************************************************** - * Find a query cookie that contains a question for a specific name. - * - * @param name Name of question you want to look for. - * @return Pointer to cookie in tree when one is found, NULL on failure. - * ****************************************************************************/ -static struct pico_mdns_cookie * -pico_mdns_ctree_find_cookie( const char *name, uint8_t type ) -{ - struct pico_mdns_cookie *cookie = NULL; - struct pico_tree_node *node = NULL; - - /* Check params */ - if (!name) { - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - /* Find the cookie in the tree wherein the question is present */ - pico_tree_foreach(node, &Cookies) { - if ((cookie = node->keyValue) && - pico_dns_qtree_find_name(&(cookie->qtree), name)) { - if (type == PICO_MDNS_PACKET_TYPE_QUERY_ANY) - return cookie; - else if (cookie->type == type) - return cookie; - } - } - - return NULL; -} - -/* MARK: ^ MDNS COOKIES */ -/* MARK: v MY RECORDS */ - -/* **************************************************************************** - * Adds records contained in records-tree to MyRecords. Suppresses adding of - * duplicates. - * - * @param records Tree with records to add to 'MyRecords'. - * @param reclaim If the records contained in records are claimed again. - * @return 0 on success, something else on failure. - * ****************************************************************************/ -static int -pico_mdns_my_records_add( pico_mdns_rtree *records, uint8_t reclaim ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - static uint8_t claim_id_count = 0; - - if (!reclaim) { - ++claim_id_count; - } - - /* Iterate over record vector */ - pico_tree_foreach(node, records) { - record = node->keyValue; - if (record) { - /* Set probed flag if record is a shared record */ - if (IS_SHARED_RECORD(record)) { - PICO_MDNS_SET_FLAG(record->flags, PICO_MDNS_RECORD_PROBED); - } - - /* If record is not claimed again, set new claim-ID */ - if (!reclaim) { - record->claim_id = claim_id_count; - } - - if (pico_tree_insert(&MyRecords, record) == &LEAF) { - mdns_dbg("MDNS: Failed to insert record in tree\n"); - return -1; - } - } - } - return 0; -} - -/* **************************************************************************** - * Generates a tree of all My Records for which the probe flag has already - * been set, and for which the CLAIMED flag has NOT been set. - * Copies the records from MyRecords into a new tree. - * - * @return Tree with all records in MyRecords with the PROBED-flag set. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_my_records_find_probed( void ) -{ - PICO_MDNS_RTREE_DECLARE(probed); - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL, *copy = NULL; - - /* Iterate over MyRecords */ - pico_tree_foreach(node, &MyRecords) { - record = node->keyValue; - - /* IS_RECORD_VERIFIED() checks the PICO_MDNS_RECORD_PROBED flag */ - if (record && IS_RECORD_VERIFIED(record) && !IS_RECORD_CLAIMED(record)) { - copy = pico_mdns_record_copy(record); - if (copy && pico_tree_insert(&probed, copy)) { - pico_mdns_record_delete((void **)©); - } - } - } - - return probed; -} - -/* **************************************************************************** - * Generates a tree of all My Records for which the PROBED-flag has not yet - * been set. Copies the record from MyRecords into a new tree. - * - * @return Tree with all records in MyRecords with the PROBED-flag not set. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_my_records_find_to_probe( void ) -{ - PICO_MDNS_RTREE_DECLARE(to_probe); - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL, *copy = NULL; - - pico_tree_foreach(node, &MyRecords) { - record = node->keyValue; - /* Check if probed flag is not set of a record */ - if (record && - IS_UNIQUE_RECORD(record) && - !IS_RECORD_VERIFIED(record) && - !IS_RECORD_PROBING(record)) { - /* Set record to currently being probed status */ - record->flags |= PICO_MDNS_RECORD_CURRENTLY_PROBING; - copy = pico_mdns_record_copy(record); - if (copy && pico_tree_insert(&to_probe, copy)) - pico_mdns_record_delete((void **)©); - } - } - return to_probe; -} - -/* **************************************************************************** - * Checks whether all MyRecords with a certain claim ID are claimed or not. - * - * @param claim_id Claim ID of the records to check for already been probed. - * @param reg_records Tree in which all MyRecords with claim ID are inserted. - * @return 1 when all MyRecords with claim ID are probed, 0 when they're not. - * ****************************************************************************/ -static uint8_t -pico_mdns_my_records_claimed_id( uint8_t claim_id, - pico_mdns_rtree *reg_records ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - - /* Initialise the iterator for iterating over my records */ - pico_tree_foreach(node, &MyRecords) { - record = node->keyValue; - if (record && record->claim_id == claim_id) { - if (IS_RECORD_VERIFIED(record)) { - if (pico_tree_insert(reg_records, record) == &LEAF) { - mdns_dbg("MDNS: Failed to insert record in tree\n"); - return 0; - } - } else { - return 0; - } - } - } - - return 1; -} - -/* **************************************************************************** - * Marks mDNS resource records in the tree as registered. Checks MyRecords for - * for other records with the same claim ID. If all records with the same - * claim ID as the records in the tree are claimed, - * the callback will get called. - * - * @param rtree Tree with mDNS records that are registered. - * @param callback Callback will get called when all records are registered. - * @return Returns 0 when everything went smooth, something else otherwise. - * ****************************************************************************/ -static int -pico_mdns_my_records_claimed( pico_mdns_rtree rtree, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - PICO_MDNS_RTREE_DECLARE(claimed_records); - struct pico_mdns_record *record = NULL, *myrecord = NULL; - struct pico_tree_node *node = NULL; - uint8_t claim_id = 0; - - /* Iterate over records and set the PROBED flag */ - pico_tree_foreach(node, &rtree) { - if ((record = node->keyValue)) { - if (!claim_id) { - claim_id = record->claim_id; - } - } - - if ((myrecord = pico_tree_findKey(&MyRecords, record))) { - PICO_MDNS_SET_FLAG(myrecord->flags, PICO_MDNS_RECORD_CLAIMED); - } - } - - /* If all_claimed is still true */ - if (pico_mdns_my_records_claimed_id(claim_id, &claimed_records)) { - callback(&claimed_records, _hostname, arg); - } - - pico_tree_destroy(&claimed_records, NULL); - - mdns_dbg(">>>>>> DONE - CLAIM SESSION: %d\n", claim_id); - - return 0; -} - -/* **************************************************************************** - * Makes sure the cache flush bit is set of the records which are probed, and - * set the corresponding MyRecords from 'being probed' to - * 'has been probed'-state. - * - * @param records mDNS records which are probed. - * ****************************************************************************/ -static void -pico_mdns_my_records_probed( pico_mdns_rtree *records ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL, *found = NULL; - - pico_tree_foreach(node, records) { - if ((record = node->keyValue)) { - /* Set the cache flush bit again */ - PICO_MDNS_SET_MSB_BE(record->record->rsuffix->rclass); - if ((found = pico_tree_findKey(&MyRecords, record))) { - if (IS_HOSTNAME_RECORD(found)) { - if (_hostname) { - PICO_FREE(_hostname); - } - - _hostname = pico_dns_qname_to_url(found->record->rname); - } - - PICO_MDNS_CLR_FLAG(found->flags, PICO_MDNS_RECORD_CURRENTLY_PROBING); - PICO_MDNS_SET_FLAG(found->flags, PICO_MDNS_RECORD_PROBED); - } else{ - mdns_dbg("Could not find my corresponding record...\n"); - } - } - } -} - -/* MARK: ^ MY RECORDS */ -/* MARK: v CACHE COHERENCY */ -#if PICO_MDNS_ALLOW_CACHING == 1 -/* **************************************************************************** - * Updates TTL of a cache entry. - * - * @param record Record of which you want to update the TTL of - * @param ttl TTL you want to update the TTL of the record to. - * @return void - * ****************************************************************************/ -static inline void -pico_mdns_cache_update_ttl( struct pico_mdns_record *record, - uint32_t ttl ) -{ - if(ttl > 0) { - /* Update the TTL's */ - record->record->rsuffix->rttl = long_be(ttl); - record->current_ttl = ttl; - } else { - /* TTL 0 means delete from cache but we need to wait one second */ - record->record->rsuffix->rttl = long_be(1u); - record->current_ttl = 1u; - } -} - -static int -pico_mdns_cache_flush_name( char *name, struct pico_dns_record_suffix *suffix ) -{ - /* Check if cache flush bit is set */ - if (PICO_MDNS_IS_MSB_SET(short_be(suffix->rclass))) { - mdns_dbg("FLUSH - Cache flush bit was set, triggered flush.\n"); - if (pico_mdns_rtree_del_name_type(&Cache, name, short_be(suffix->rtype))) { - mdns_dbg("Could not flush records from cache!\n"); - return -1; - } - } - - return 0; -} - -/* **************************************************************************** - * Adds a mDNS record to the cache. - * - * @param record mDNS record to add to the Cache. - * @return 0 when entry successfully added, something else when it all went ho- - * rribly wrong... - * ****************************************************************************/ -static int -pico_mdns_cache_add( struct pico_mdns_record *record ) -{ - struct pico_dns_record_suffix *suffix = NULL; - char *name = NULL; - uint32_t rttl = 0; - - /* Check params */ - if (!record) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - /* 2 paths */ - - suffix = record->record->rsuffix; - name = record->record->rname; - rttl = long_be(suffix->rttl); - - if (pico_mdns_cache_flush_name(name, suffix)) { - return -1; - } - /* 4 paths */ - - /* Check if the TTL is not 0*/ - if (!rttl) { - return -1; - } else { - /* Set current TTL to the original TTL before inserting */ - record->current_ttl = rttl; - - if (pico_tree_insert(&Cache, record) != NULL) - return -1; - - mdns_dbg("RR cached. TICK TACK TICK TACK...\n"); - - return 0; - } - /* 12 paths */ -} - -/* **************************************************************************** - * Add a copy of an mDNS resource record to the cache tree. Checks whether the - * entry is already present in the Cache or not. - * - * @param record Record to add to the Cache-tree - * @return 0 on grrrreat success, something else on awkward failure. - * ****************************************************************************/ -static int -pico_mdns_cache_add_record( struct pico_mdns_record *record ) -{ - struct pico_mdns_record *found = NULL, *copy = NULL; - uint32_t rttl = 0; - - /* Check params */ - if (!record) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* See if the record is already contained in the cache */ - if ((found = pico_tree_findKey(&Cache, record))) { - rttl = long_be(record->record->rsuffix->rttl); - pico_mdns_cache_update_ttl(found, rttl); - } else if ((copy = pico_mdns_record_copy(record))) { - if (pico_mdns_cache_add(copy)) { - pico_mdns_record_delete((void **)©); - return -1; - } - } else - return -1; - - return 0; -} - -#if PICO_MDNS_CONTINUOUS_REFRESH == 1 -/* **************************************************************************** - * Determine if the current TTL is at a refreshing point. - * - * @param original Original TTL to calculate refreshing points - * @param current Current TTL to check. - * @return 1 when Current TTL is at refresh point. 0 when it's not. - * ****************************************************************************/ -static int -pico_mdns_ttl_at_refresh_time( uint32_t original, - uint32_t current ) -{ - uint32_t rnd = 0; - rnd = pico_rand() % 3; - - if (((original - current == - ((original * (80 + rnd)) / 100)) ? 1 : 0) || - ((original - current == - ((original * (85 + rnd)) / 100)) ? 1 : 0) || - ((original - current == - ((original * (90 + rnd)) / 100)) ? 1 : 0) || - ((original - current == - ((original * (95 + rnd)) / 100)) ? 1 : 0)) - return 1; - else - return 0; -} -#endif - -/* **************************************************************************** - * Utility function to update the TTL of cache entries and check for expired - * ones. When continuous refreshing is enabled the records will be reconfirmed - * @ 80%, 85%, 90% and 95% of their original TTL. - * ****************************************************************************/ -static void -pico_mdns_cache_check_expiries( void ) -{ - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_record *record = NULL; -#if PICO_MDNS_CONTINUOUS_REFRESH == 1 - uint32_t current = 0, original = 0; - uint16_t type 0; - char *url = NULL; -#endif - - /* Check for expired cache records */ - pico_tree_foreach_safe(node, &Cache, next) { - if ((record = node->keyValue)) { - /* Update current ttl and delete when TTL is 0*/ - if ((--(record->current_ttl)) == 0) { - record = pico_tree_delete(&Cache, record); - pico_mdns_record_delete((void **)&record); - } - -#if PICO_MDNS_CONTINUOUS_REFRESH == 1 - /* Determine original and current ttl */ - original = long_be(record->record->rsuffix->rttl); - current = record->current_ttl; - - /* Cache refresh at 80 or 85/90/95% of TTL + 2% rnd */ - if (pico_mdns_ttl_at_refresh_time(original, current)) { - url = pico_dns_qname_to_url(record->record->rname); - type = short_be(record->record->rsuffix->rtype) - pico_mdns_getrecord_generic(url, type, NULL, NULL); - PICO_FREE(url); - } - -#endif - } - } -} -#endif /* PICO_MDNS_ALLOW_CACHING */ - -/* **************************************************************************** - * Utility function to update the TTL of cookies and check for expired - * ones. Deletes the expired ones as well. - * ****************************************************************************/ -static void -pico_mdns_cookies_check_timeouts( void ) -{ - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_cookie *cookie = NULL; - - pico_tree_foreach_safe(node, &Cookies, next) { - if ((cookie = node->keyValue) && --(cookie->timeout) == 0) { - /* Call callback to allow error checking */ - if (cookie->callback) { - cookie->callback(NULL, NULL, cookie->arg); - } - - /* Delete cookie */ - cookie = pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void **)&cookie); - - /* If the request was for a reconfirmation of a record, - flush the corresponding record after the timeout */ - } - } -} - -/* **************************************************************************** - * Global mDNS module tick-function, central point where all the timing is - * handled. - * - * @param now Ignore - * @param _arg Ignore - * ****************************************************************************/ -static void -pico_mdns_tick( pico_time now, void *_arg ) -{ - IGNORE_PARAMETER(now); - IGNORE_PARAMETER(_arg); - -#if PICO_MDNS_ALLOW_CACHING == 1 - /* Update the cache */ - pico_mdns_cache_check_expiries(); -#endif - - /* Update the cookies */ - pico_mdns_cookies_check_timeouts(); - - /* Schedule new tick */ - if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) { - mdns_dbg("MDNS: Failed to start tick timer\n"); - /* TODO Not ticking anymore, what to do? */ - } -} - -/* MARK: v MDNS PACKET UTILITIES */ - -/* **************************************************************************** - * Sends a Multicast packet on the wire to the mDNS destination port. - * - * @param packet Packet buffer in memory - * @param len Size of the packet in bytes - * @return 0 When the packet is passed successfully on to the lower layers of - * picoTCP. Doesn't mean the packet is successfully sent on the wire. - * ****************************************************************************/ -static int -pico_mdns_send_packet( pico_dns_packet *packet, uint16_t len ) -{ - /* TODO: why only ipv4 support? */ - struct pico_ip4 dst4; - - /* Set the destination address to the mDNS multicast-address */ - pico_string_to_ipv4(PICO_MDNS_DEST_ADDR4, &dst4.addr); - - /* Send packet to IPv4 socket */ - return pico_socket_sendto(mdns_sock_ipv4, packet, (int)len, &dst4, - short_be(mdns_port)); -} - -/* **************************************************************************** - * Sends a Unicast packet on the wire to the mDNS destination port of specific - * peer in the network - * - * @param packet Packet buffer in memory - * @param len Size of the packet in bytes - * @param peer Peer in the network you want to send the packet to. - * @return 0 When the packet is passed successfully on to the lower layers of - * picoTCP. Doesn't mean the packet is successfully send on the wire. - * ****************************************************************************/ -static int -pico_mdns_send_packet_unicast( pico_dns_packet *packet, - uint16_t len, - struct pico_ip4 peer ) -{ - /* Send packet to IPv4 socket */ - return pico_socket_sendto(mdns_sock_ipv4, packet, (int)len, &peer, - short_be(mdns_port)); -} - - -/* **************************************************************************** - * Send DNS records as answers to a peer via unicast - * - * @param unicast_tree Tree with DNS records to send as answers. - * @param peer Peer IPv4-address - * @return 0 when the packet is properly send, something else otherwise. - * ****************************************************************************/ -static int -pico_mdns_unicast_reply( pico_dns_rtree *unicast_tree, - pico_dns_rtree *artree, - struct pico_ip4 peer ) -{ - union pico_address *local_addr = NULL; - pico_dns_packet *packet = NULL; - uint16_t len = 0; - - if (pico_tree_count(unicast_tree) > 0) { - /* Create response DNS packet */ - packet = pico_dns_answer_create(unicast_tree, NULL, artree, &len); - if (!packet || !len) { - pico_err = PICO_ERR_ENOMEM; - return -1; - } - - packet->id = 0; - - /* Check if source address is on the local link */ - local_addr = (union pico_address *) pico_ipv4_source_find(&peer); - if (!local_addr) { - mdns_dbg("Peer not on same link!\n"); - /* Forced response via multicast */ - - /* RFC6762: 18.6: In both multicast query and response messages, - the RD bit SHOULD be zero on transmission. In - pico_dns_fill_packet_header, the RD bit is set to - PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */ - packet->rd = PICO_DNS_RD_NO_DESIRE; - - - if (pico_mdns_send_packet(packet, len) != (int)len) { - mdns_dbg("Could not send multicast response!\n"); - return -1; - } - } else { - /* Send the packet via unicast */ - if (pico_mdns_send_packet_unicast(packet, len, peer) != (int)len) { - mdns_dbg("Could not send unicast response!\n"); - return -1; - } - - mdns_dbg("Unicast response sent successfully!\n"); - } - - PICO_FREE(packet); - } - - return 0; -} - -/* **************************************************************************** - * Send DNS records as answers to mDNS peers via multicast - * - * @param multicast_tree Tree with DNS records to send as answers. - * @return 0 when the packet is properly send, something else otherwise. - * ****************************************************************************/ -static int -pico_mdns_multicast_reply( pico_dns_rtree *multicast_tree, - pico_dns_rtree *artree ) -{ - pico_dns_packet *packet = NULL; - uint16_t len = 0; - - /* If there are any multicast records */ - if (pico_tree_count(multicast_tree) > 0) { - /* Create response DNS packet */ - packet = pico_dns_answer_create(multicast_tree, NULL, artree, &len); - if (!packet || len == 0) { - pico_err = PICO_ERR_ENOMEM; - return -1; - } - - packet->id = 0; - - /* RFC6762: 18.6: In both multicast query and response messages, - the RD bit SHOULD be zero on transmission. - In pico_dns_fill_packet_header, the RD bit is set to - PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */ - packet->rd = PICO_DNS_RD_NO_DESIRE; - - /* Send the packet via multicast */ - if (pico_mdns_send_packet(packet, len) != (int)len) { - mdns_dbg("Could not send multicast response!\n"); - return -1; - } - - mdns_dbg("Multicast response sent successfully!\n"); - - PICO_FREE(packet); - } - - return 0; -} - -/* MARK: ^ MDNS PACKET UTILITIES */ -/* MARK: ASYNCHRONOUS MDNS RECEPTION */ - -/* **************************************************************************** - * Merges 2 pico_trees with each other. - * - * @param dest Destination tree to merge the other tree in. - * @param src Source tree to get the node from to insert into the dest-tree. - * @return Returns 0 when properly merged, or not.. - * ****************************************************************************/ -static int -pico_tree_merge( struct pico_tree *dest, struct pico_tree *src ) -{ - struct pico_tree_node *node = NULL; - - /* Check params */ - if (!dest || !src) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Insert source nodes */ - pico_tree_foreach(node, src) { - if (node->keyValue) { - if (pico_tree_insert(dest, node->keyValue) == &LEAF) { - mdns_dbg("MDNS: Failed to insert record in tree\n"); - return -1; - } - } - } - - return 0; -} - -/* **************************************************************************** - * Populates an mDNS record tree with answers from MyRecords depending on name - * , qtype and qclass. - * - * @param name Name of records to look for in MyRecords - * @param qtype Type of records to look for in MyRecords - * @param qclass Whether the answer should be sent via unicast or not. - * @return mDNS record tree with possible answers from MyRecords - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_populate_antree( char *name, uint16_t qtype, uint16_t qclass ) -{ - PICO_MDNS_RTREE_DECLARE(antree); - struct pico_tree_node *node = NULL, *next; - struct pico_mdns_record *record = NULL; - - /* Create an answer record vector */ - if (PICO_DNS_TYPE_ANY == qtype) - antree = pico_mdns_rtree_find_name(&MyRecords, name, 1); - else - antree = pico_mdns_rtree_find_name_type(&MyRecords, name, qtype, 1); - - /* Remove answers which aren't successfully registered yet */ - pico_tree_foreach_safe(node, &antree, next) { - if ((record = node->keyValue) && !IS_RECORD_VERIFIED(record)) { - pico_tree_delete(&antree, record); - } - } - - /* Check if question is a QU-question */ - if (PICO_MDNS_IS_MSB_SET(qclass)) { - /* Set all the flags of the answer accordingly */ - pico_tree_foreach(node, &antree) { - if ((record = node->keyValue)) - PICO_MDNS_SET_FLAG(record->flags, - PICO_MDNS_RECORD_SEND_UNICAST); - } - } - - return antree; -} - -/* **************************************************************************** - * Handles a single received question. - * - * @param question DNS question to parse and handle. - * @param packet Received packet in which the DNS question was present. - * @return mDNS record tree with possible answer to the question. Can possibly - * be empty. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_handle_single_question( struct pico_dns_question *question, - pico_dns_packet *packet ) -{ - struct pico_mdns_cookie *cookie = NULL; - PICO_MDNS_RTREE_DECLARE(antree); - char *qname_original = NULL; - uint16_t qtype = 0, qclass = 0; - - /* Check params */ - if (!question || !packet) { - pico_err = PICO_ERR_EINVAL; - return antree; - } - - /* Decompress single DNS question */ - qname_original = pico_dns_question_decompress(question, packet); - mdns_dbg("Question RCVD for '%s'\n", question->qname); - - /* Find currently active query cookie */ - if ((cookie = pico_mdns_ctree_find_cookie(question->qname, - PICO_MDNS_PACKET_TYPE_QUERY))) { - mdns_dbg("Query cookie found for question, suppress duplicate.\n"); - cookie->status = PICO_MDNS_COOKIE_STATUS_CANCELLED; - } else { - qtype = short_be(question->qsuffix->qtype); - qclass = short_be(question->qsuffix->qclass); - antree = pico_mdns_populate_antree(question->qname, qtype, qclass); - } - - PICO_FREE(question->qname); - question->qname = qname_original; - return antree; -} - -/* **************************************************************************** - * When a query-cookie is found for a RCVD answer, the cookie should be - * handled accordingly. This function does that. - * - * @param cookie Cookie that contains the question for the RCVD answer. - * @param answer RCVD answer to handle cookie with - * @return Returns 0 when handling went OK, something else when it didn't. - * ****************************************************************************/ -static int -pico_mdns_handle_cookie_with_answer( struct pico_mdns_cookie *cookie, - struct pico_mdns_record *answer ) -{ - PICO_MDNS_RTREE_DECLARE(antree); - uint8_t type = 0, status = 0; - - /* Check params */ - if (!cookie || !answer) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - type = cookie->type; - status = cookie->status; - if (PICO_MDNS_COOKIE_STATUS_ACTIVE == status) { - if (PICO_MDNS_PACKET_TYPE_PROBE == type) { - /* Conflict occurred, resolve it! */ - pico_mdns_cookie_resolve_conflict(cookie, answer->record->rname); - } else if (PICO_MDNS_PACKET_TYPE_QUERY == type) { - if (cookie->callback) { - /* RCVD Answer on query, callback with answer. Callback is - * responsible for aggregating all the received answers. */ - if (pico_tree_insert(&antree, answer) == &LEAF) { - mdns_dbg("MDNS: Failed to insert answer in tree\n"); - return -1; - } - cookie->callback(&antree, NULL, cookie->arg); - } - } else { /* Don't handle answer cookies with answer */ - } - } - - return 0; -} - -/* **************************************************************************** - * Handles a single received answer record. - * - * @param answer Answer mDNS record. - * @return 0 when answer is properly handled, something else when it's not. - * ****************************************************************************/ -static int -pico_mdns_handle_single_answer( struct pico_mdns_record *answer ) -{ - struct pico_mdns_cookie *found = NULL; - struct pico_mdns_record *record = NULL; - - mdns_dbg("Answer RCVD for '%s'\n", answer->record->rname); - - /* Find currently active query cookie */ - found = pico_mdns_ctree_find_cookie(answer->record->rname, - PICO_MDNS_PACKET_TYPE_QUERY_ANY); - if (found && pico_mdns_handle_cookie_with_answer(found, answer)) { - mdns_dbg("Could not handle found cookie correctly!\n"); - return -1; - } else { - mdns_dbg("RCVD an unsolicited record!\n"); - if ((record = pico_tree_findKey(&MyRecords, answer)) && - !IS_RECORD_PROBING(record)) - return pico_mdns_record_resolve_conflict(record, - answer->record->rname); - } - - return 0; -} - -/* **************************************************************************** - * Handles a single received authority record. - * - * @param answer Authority mDNS record. - * @return 0 when authority is properly handled. -1 when it's not. - * ****************************************************************************/ -static int -pico_mdns_handle_single_authority( struct pico_mdns_record *answer ) -{ - struct pico_mdns_cookie *found = NULL; - char *name = NULL; - - name = answer->record->rname; - mdns_dbg("Authority RCVD for '%s'\n", name); - - /* Find currently active probe cookie */ - if ((found = pico_mdns_ctree_find_cookie(name, PICO_MDNS_PACKET_TYPE_PROBE)) - && PICO_MDNS_COOKIE_STATUS_ACTIVE == found->status) { - mdns_dbg("Simultaneous Probing occurred, went tiebreaking...\n"); - if (pico_mdns_cookie_apply_spt(found, answer->record) < 0) { - mdns_dbg("Could not apply S.P.T. to cookie!\n"); - return -1; - } - } - - return 0; -} - -/* **************************************************************************** - * Handles a single received additional [Temporarily unused] - * - * @param answer Additional mDNS record. - * @return 0 - * ****************************************************************************/ -static int -pico_mdns_handle_single_additional( struct pico_mdns_record *answer ) -{ - /* Don't need this for now ... */ - IGNORE_PARAMETER(answer); - return 0; -} - -/* **************************************************************************** - * Handles a flat chunk of memory as if it were all questions in it. - * Generates a tree with responses if there are any questions for records for - * which host has the authority to answer. - * - * @param ptr Pointer-Pointer to location of question section of packet. - * Will point to right after the question section on return. - * @param qdcount Amount of questions contained in the packet - * @param packet DNS packet where the questions are present. - * @return Tree with possible responses on the questions. - * ****************************************************************************/ -static pico_mdns_rtree -pico_mdns_handle_data_as_questions ( uint8_t **ptr, - uint16_t qdcount, - pico_dns_packet *packet ) -{ - PICO_MDNS_RTREE_DECLARE(antree); - PICO_MDNS_RTREE_DECLARE(rtree); - struct pico_dns_question question; - uint16_t i = 0; - - /* Check params */ - if ((!ptr) || !packet || !(*ptr)) { - pico_err = PICO_ERR_EINVAL; - return antree; - } - - for (i = 0; i < qdcount; i++) { - /* Set qname of the question to the correct location */ - question.qname = (char *)(*ptr); - - /* Set qsuffix of the question to the correct location */ - question.qsuffix = (struct pico_dns_question_suffix *) - (question.qname + pico_dns_namelen_comp(question.qname) + 1); - - /* Handle a single question and merge the returned tree */ - rtree = pico_mdns_handle_single_question(&question, packet); - pico_tree_merge(&antree, &rtree); - pico_tree_destroy(&rtree, NULL); - - /* Move to next question */ - *ptr = (uint8_t *)question.qsuffix + - sizeof(struct pico_dns_question_suffix); - } - if (pico_tree_count(&antree) == 0) { - mdns_dbg("No 'MyRecords' found that corresponds with this query.\n"); - } - - return antree; -} - -static int -pico_mdns_handle_data_as_answers_generic( uint8_t **ptr, - uint16_t count, - pico_dns_packet *packet, - uint8_t type ) -{ - struct pico_mdns_record mdns_answer = { - .record = NULL, .current_ttl = 0, - .flags = 0, .claim_id = 0 - }; - struct pico_dns_record answer; - char *orname = NULL; - uint16_t i = 0; - - /* Check params */ - if ((!ptr) || !packet || !(*ptr)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* TODO: When receiving multiple authoritative answers, */ - /* they should be sorted in lexicographical order */ - /* (just like in pico_mdns_record_am_i_lexi_later) */ - - for (i = 0; i < count; i++) { - /* Set rname of the record to the correct location */ - answer.rname = (char *)(*ptr); - - /* Set rsuffix of the record to the correct location */ - answer.rsuffix = (struct pico_dns_record_suffix *) - (answer.rname + - pico_dns_namelen_comp(answer.rname) + 1u); - - /* Set rdata of the record to the correct location */ - answer.rdata = (uint8_t *) answer.rsuffix + - sizeof(struct pico_dns_record_suffix); - - /* Make an mDNS record from the DNS answer */ - orname = pico_dns_record_decompress(&answer, packet); - mdns_answer.record = &answer; - mdns_answer.record->rname_length = (uint16_t)(pico_dns_strlen(answer.rname) + 1u); - - /* Handle a single aswer */ - switch (type) { - case 1: - pico_mdns_handle_single_authority(&mdns_answer); - break; - case 2: - pico_mdns_handle_single_additional(&mdns_answer); - break; - default: - pico_mdns_handle_single_answer(&mdns_answer); -#if PICO_MDNS_ALLOW_CACHING == 1 - pico_mdns_cache_add_record(&mdns_answer); -#endif - break; - } - - /* Free decompressed name and mDNS record */ - PICO_FREE(mdns_answer.record->rname); - answer.rname = orname; - - /* Move to next record */ - *ptr = (uint8_t *) answer.rdata + short_be(answer.rsuffix->rdlength); - } - return 0; -} - -/* **************************************************************************** - * Splits an mDNS record tree into two DNS record tree, one to send via - * unicast, one to send via multicast. - * - * @param answers mDNS record tree to split up - * @param unicast_tree DNS record tree with unicast answers. - * @param multicast_tree DNS record tee with multicast answers. - * @return 0 when the tree is properly split up. - * ****************************************************************************/ -static int -pico_mdns_sort_unicast_multicast( pico_mdns_rtree *answers, - pico_dns_rtree *unicast_tree, - pico_dns_rtree *multicast_tree ) -{ - struct pico_mdns_record *record = NULL; - struct pico_tree_node *node = NULL; - - /* Check params */ - if (!answers || !unicast_tree || !multicast_tree) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - pico_tree_foreach(node, answers) { - record = node->keyValue; - if ((record = node->keyValue)) { - if (IS_UNICAST_REQUESTED(record)) { - if (record->record){ - if (pico_tree_insert(unicast_tree, record->record) == &LEAF) { - mdns_dbg("MDNS: Failed to instert unicast record in tree\n"); - return -1; - } - } - } else { - if (record->record){ - if (pico_tree_insert(multicast_tree, record->record) == &LEAF) { - mdns_dbg("MDNS: Failed to instert multicast record in tree\n"); - return -1; - } - } - } - } - } - - return 0; -} - -static uint16_t -pico_mdns_nsec_highest_type( pico_mdns_rtree *rtree ) -{ - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_record *record = NULL; - uint16_t highest_type = 0, type = 0; - - pico_tree_foreach_safe(node, rtree, next) { - if ((record = node->keyValue)) { - if (IS_SHARED_RECORD(record)) - pico_tree_delete(rtree, record); - - type = short_be(record->record->rsuffix->rtype); - highest_type = (type > highest_type) ? (type) : (highest_type); - } - } - - return highest_type; -} - -static void -pico_mdns_nsec_gen_bitmap( uint8_t *ptr, pico_mdns_rtree *rtree ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - uint16_t type = 0; - - pico_tree_foreach(node, rtree) { - if ((record = node->keyValue)) { - type = short_be(record->record->rsuffix->rtype); - *(ptr + 1 + (type / 8)) = (uint8_t)(0x80 >> (type % 8)); - } - } -} - -/* **************************************************************************** - * Generates an NSEC record for a specific name. Looks in MyRecords for unique - * records with given name and generates the NSEC bitmap from them. - * - * @param name Name of the records you want to generate a bitmap for. - * @return Pointer to newly created NSEC record on success, NULL on failure. - * ****************************************************************************/ -static struct pico_mdns_record * -pico_mdns_gen_nsec_record( char *name ) -{ - PICO_MDNS_RTREE_DECLARE(rtree); - struct pico_mdns_record *record = NULL; - uint16_t highest_type = 0, rdlen = 0; - uint8_t bitmap_len = 0, *rdata = NULL, *ptr = NULL; - char *url = NULL; - - if (!name) { /* Check params */ - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - /* Determine the highest type of my unique records with this name */ - rtree = pico_mdns_rtree_find_name(&MyRecords, name, 0); - highest_type = pico_mdns_nsec_highest_type(&rtree); - - /* Determine the bimap_len */ - bitmap_len = (uint8_t)(highest_type / 8); - bitmap_len = (uint8_t)(bitmap_len + ((highest_type % 8) ? (1) : (0))); - - /* Provide rdata */ - rdlen = (uint16_t)(pico_dns_strlen(name) + 3u + bitmap_len); - if (!(rdata = PICO_ZALLOC((size_t)rdlen))) { - pico_err = PICO_ERR_ENOMEM; - pico_tree_destroy(&rtree, NULL); - return NULL; - } - - /* Set the next domain name */ - strcpy((char *)rdata, name); - /* Set the bitmap length */ - *(ptr = (uint8_t *)(rdata + pico_dns_strlen(name) + 2)) = bitmap_len; - /* Generate the bitmap */ - pico_mdns_nsec_gen_bitmap(ptr, &rtree); - pico_tree_destroy(&rtree, NULL); - - /* Generate the actual mDNS NSEC record */ - if (!(url = pico_dns_qname_to_url(name))) { - PICO_FREE(rdata); - return NULL; - } - - record = pico_mdns_record_create(url, (void *)rdata, rdlen, - PICO_DNS_TYPE_NSEC, - PICO_MDNS_SERVICE_TTL, - PICO_MDNS_RECORD_UNIQUE); - PICO_FREE(rdata); - PICO_FREE(url); - return record; -} - -/* **************************************************************************** - * Checks in additionals if there is an NSEC record already present with given - * name. If there's not, a new NSEC records will be generated and added to the - * additional tree. - * - * @param artree mDNS record-tree containing additional records. - * @param name Name to check for. - * @return 0 when NSEC is present in additional, whether it was already present - * or a new one is generated doesn't matter. - * ****************************************************************************/ -static int -pico_mdns_additionals_add_nsec( pico_mdns_rtree *artree, - char *name ) -{ - struct pico_mdns_record *record = NULL, *nsec = NULL; - struct pico_tree_node *node = NULL; - uint16_t type = 0; - - /* Check params */ - if (!artree || !name) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Check if there is a NSEC already for this name */ - pico_tree_foreach(node, artree) { - if (node != &LEAF && (record = node->keyValue)) { - type = short_be(record->record->rsuffix->rtype); - if ((PICO_DNS_TYPE_NSEC == type) && 0 == strcasecmp(record->record->rname, name)) { - return 0; - } - } - } - - /* If there is none present generate one for given name */ - if ((nsec = pico_mdns_gen_nsec_record(name))) { - if (pico_tree_insert(artree, nsec)) { - pico_mdns_record_delete((void **)nsec); - return -1; - } - } - - return 0; -} - -/* **************************************************************************** - * Adds hostname records to the additional records - * - * @param artree mDNS record-tree containing additional records. - * @return 0 when hostname records are added successfully to additionals. Rets - * something else on failure. - * ****************************************************************************/ -static int -pico_mdns_additionals_add_host( pico_mdns_rtree *artree ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL, *copy = NULL; - - pico_tree_foreach(node, &MyRecords) { - record = node->keyValue; - if (record) { - if (IS_HOSTNAME_RECORD(record) && IS_RECORD_VERIFIED(record)) { - copy = pico_mdns_record_copy(record); - if (copy && pico_tree_insert(artree, copy)) - pico_mdns_record_delete((void **)©); - } - } - } - - return 0; -} /* Satic path count: 4 */ - -static void -pico_rtree_add_copy( pico_mdns_rtree *tree, struct pico_mdns_record *record ) -{ - struct pico_mdns_record *copy = NULL; - - if (!tree || !record) { - pico_err = PICO_ERR_EINVAL; - return; - } - - if ((copy = pico_mdns_record_copy(record))) { - if (pico_tree_insert(tree, copy)) - pico_mdns_record_delete((void **)©); - } -} - -/* **************************************************************************** - * When a service is found, additional records should be generated and - * added to either the answer section or the additional sections. - * This happens here - * - * @param antree mDNS record tree with answers to send - * @param artree mDNS record tree with additionals to send - * @param srv_record Found SRV record in the answers - * @return 0 When additional records are properly generated - * ****************************************************************************/ -static int -pico_mdns_gather_service_meta( pico_mdns_rtree *antree, - pico_mdns_rtree *artree, - struct pico_mdns_record *srv_record ) -{ - struct pico_mdns_record *ptr_record = NULL, *meta_record = NULL; - char *sin = NULL, *service = NULL; - uint32_t ttl = 0; - - /* Generate proper service instance name and service */ - sin = pico_dns_qname_to_url(srv_record->record->rname); // May be leaking - - if (!antree || !artree || !sin) { - pico_err = PICO_ERR_EINVAL; - PICO_FREE(sin); - return -1; - } else { - /* Add hostname records */ - pico_mdns_additionals_add_host(artree); - - service = sin + pico_dns_first_label_length(sin) + 1u; - ttl = long_be(srv_record->record->rsuffix->rttl); - - /* Generate PTR records */ - ptr_record = pico_mdns_record_create(service, (void *)sin, - (uint16_t)strlen(sin), - PICO_DNS_TYPE_PTR, - ttl, PICO_MDNS_RECORD_SHARED); - /* Meta DNS-SD record */ - meta_record = pico_mdns_record_create("_services._dns-sd._udp.local", - (void *)service, - (uint16_t)strlen(service), - PICO_DNS_TYPE_PTR, - ttl, PICO_MDNS_RECORD_SHARED); - PICO_FREE(sin); // Free allocated memory - if (!meta_record || !ptr_record) { - mdns_dbg("Could not generate META or PTR records!\n"); - pico_mdns_record_delete((void **)&ptr_record); - pico_mdns_record_delete((void **)&meta_record); - return -1; - } - - ptr_record->flags |= (PICO_MDNS_RECORD_PROBED | - PICO_MDNS_RECORD_CLAIMED); - meta_record->flags |= (PICO_MDNS_RECORD_PROBED | - PICO_MDNS_RECORD_CLAIMED); - - /* Add copies to the answer tree */ - pico_rtree_add_copy(antree, meta_record); - pico_rtree_add_copy(antree, ptr_record); - - /* Insert the created service record in MyRecords, alread in, destroy */ - if (pico_tree_insert(&MyRecords, meta_record)) { - mdns_dbg("MDNS: Failed to insert meta record in tree\n"); - pico_mdns_record_delete((void **)&meta_record); - pico_mdns_record_delete((void **)&ptr_record); - return -1; - } - - if (pico_tree_insert(&MyRecords, ptr_record)) { - mdns_dbg("MDNS: Failed to insert ptr record in tree\n"); - pico_mdns_record_delete((void **)&ptr_record); - pico_tree_delete(&MyRecords, meta_record); - pico_mdns_record_delete((void **)&meta_record); - } - } - return 0; -} /* Static path count: 9 */ - -/* **************************************************************************** - * Gathers additional records for a to send response. Checks for services and - * whether or not there should be NSEC records added to the additional section - * - * @param antree mDNS record tree with answers to send - * @param artree mDNS record tree with additionals to send - * @return Returns 0 when additionals are properly generated and added - * ****************************************************************************/ -static int -pico_mdns_gather_additionals( pico_mdns_rtree *antree, - pico_mdns_rtree *artree ) -{ - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - int ret = 0; - - /* Check params */ - if (!antree || !artree) { - pico_err = PICO_ERR_EINVAL; - return -1; - } else { - /* Look for SRV records in the tree */ - pico_tree_foreach(node, antree) { - if ((record = node->keyValue) && - short_be(record->record->rsuffix->rtype) == PICO_DNS_TYPE_SRV && - (ret = pico_mdns_gather_service_meta(antree, artree, record))) - return ret; - } - - /* Look for unique records in the tree to generate NSEC records */ - pico_tree_foreach(node, antree) { - if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) && - (ret = pico_mdns_additionals_add_nsec(artree, - record->record->rname))) - return ret; - } - - /* Look for unique records in the additionals to generate NSEC records*/ - pico_tree_foreach(node, artree) { - if ((record = node->keyValue) && IS_UNIQUE_RECORD(record) && - (ret = pico_mdns_additionals_add_nsec(artree, - record->record->rname))) - return ret; - } - } - - return 0; -} /* Static path count: 9 */ - -/* **************************************************************************** - * Sends mDNS records to either multicast peer via unicast to a single peer. - * - * @param antree Tree with mDNS records to send as answers - * @param peer IPv4-address of peer who this host has RCVD a packet. - * @return 0 when answers are properly handled, something else otherwise. - * ****************************************************************************/ -static int -pico_mdns_reply( pico_mdns_rtree *antree, struct pico_ip4 peer ) -{ - PICO_DNS_RTREE_DECLARE(antree_m); - PICO_DNS_RTREE_DECLARE(antree_u); - PICO_MDNS_RTREE_DECLARE(artree); - PICO_DNS_RTREE_DECLARE(artree_dummy); - PICO_DNS_RTREE_DECLARE(artree_dns); - - /* Try to gather additionals for the to send response */ - if (pico_mdns_gather_additionals(antree, &artree)) { - mdns_dbg("Could not gather additionals properly!\n"); - return -1; - } - - /* Sort the answers into multicast and unicast answers */ - pico_mdns_sort_unicast_multicast(antree, &antree_u, &antree_m); - - /* Convert the mDNS additional tree to a DNS additional tree to send with - * the the unicast AND the multicast response */ - pico_mdns_sort_unicast_multicast(&artree, &artree_dummy, &artree_dns); - - /* Send response via unicast */ - if (pico_mdns_unicast_reply(&antree_u, &artree_dns, peer)) { - mdns_dbg("Could not sent reply via unicast!\n"); - return -1; - } - - /* Send response via multicast */ - if (pico_mdns_multicast_reply(&antree_m, &artree_dns)) { - mdns_dbg("Could not sent reply via multicast!\n"); - return -1; - } - - pico_tree_destroy(&antree_m, NULL); - pico_tree_destroy(&antree_u, NULL); - pico_tree_destroy(&artree_dummy, NULL); - pico_tree_destroy(&artree_dns, NULL); - PICO_MDNS_RTREE_DESTROY(&artree); - - return 0; -} - -/* **************************************************************************** - * Parses DNS records from a plain chunk of data and looks for them in the - * answer tree. If they're found, they will be removed from the tree. - * - * @param rtree Tree to look in for known answers - * @param packet DNS packet in which to look for known answers - * @param ancount Amount of answers in the DNS packet - * @param data Answer section of the DNS packet as a flat chunk of memory. - * @return 0 K.A.S. could be properly applied, something else when not. - * ****************************************************************************/ -static int -pico_mdns_apply_k_a_s( pico_mdns_rtree *rtree, - pico_dns_packet *packet, - uint16_t ancount, - uint8_t **data ) -{ - struct pico_tree_node *node = NULL, *next = NULL; - struct pico_mdns_record *record = NULL, ka = { - 0 - }; - struct pico_dns_record answer = { - 0 - }; - uint16_t i = 0; - - /* Check params */ - if ((!data) || !rtree || !packet || !(*data)) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - for (i = 0; i < ancount; i++) { - /* Set rname of the record to the correct location */ - answer.rname = (char *)(*data); - - /* Set rsuffix of the record to the correct location */ - answer.rsuffix = (struct pico_dns_record_suffix *) - (answer.rname + pico_dns_namelen_comp(answer.rname) + 1u); - - /* Set rdata of the record to the correct location */ - answer.rdata = (uint8_t *) answer.rsuffix + - sizeof(struct pico_dns_record_suffix); - - pico_dns_record_decompress(&answer, packet); - ka.record = &answer; - - /* If the answer is in the record vector */ - pico_tree_foreach_safe(node, rtree, next) { - if ((record = node->keyValue)) { - if (pico_mdns_record_cmp(record, &ka) == 0) - record = pico_tree_delete(rtree, record); - } - } - PICO_FREE(ka.record->rname); - ka.record = NULL; - - /* Move to next record */ - *data = (uint8_t *) answer.rdata + short_be(answer.rsuffix->rdlength); - } - return 0; -} - -/* **************************************************************************** - * Handles a single incoming query packet. Applies Known Answer Suppression - * after handling as well. - * - * @param packet Received packet - * @param peer IPv4 address of the peer who sent the received packet. - * @return Returns 0 when the query packet is properly handled. - * ****************************************************************************/ -static int -pico_mdns_handle_query_packet( pico_dns_packet *packet, struct pico_ip4 peer ) -{ - PICO_MDNS_RTREE_DECLARE(antree); - uint16_t qdcount = 0, ancount = 0; - uint8_t *data = NULL; - - /* Move to the data section of the packet */ - data = (uint8_t *)packet + sizeof(struct pico_dns_header); - - /* Generate a list of answers */ - qdcount = short_be(packet->qdcount); - antree = pico_mdns_handle_data_as_questions(&data, qdcount, packet); - if (pico_tree_count(&antree) == 0) { - mdns_dbg("No records found that correspond with this query!\n"); - return 0; - } - - /* Apply Known Answer Suppression */ - ancount = short_be(packet->ancount); - if (pico_mdns_apply_k_a_s(&antree, packet, ancount, &data)) { - mdns_dbg("Could not apply known answer suppression!\n"); - return -1; - } - - /* Try to reply with the left-over answers */ - pico_mdns_reply(&antree, peer); - PICO_MDNS_RTREE_DESTROY(&antree); - - return 0; -} - -/* **************************************************************************** - * Handles a single incoming probe packet. Checks for Simultaneous Probe - * Tiebreaking as well. - * - * @param packet Received probe packet. - * @param peer IPv4 address of the peer who sent the probe packet. - * @return Returns 0 when the probe packet is properly handled. - * ****************************************************************************/ -static int -pico_mdns_handle_probe_packet( pico_dns_packet *packet, struct pico_ip4 peer ) -{ - PICO_MDNS_RTREE_DECLARE(antree); - uint16_t qdcount = 0, nscount = 0; - uint8_t *data = NULL; - - /* Move to the data section of the packet */ - data = (uint8_t *)packet + sizeof(struct pico_dns_header); - - /* Generate a list of answers */ - qdcount = short_be(packet->qdcount); - antree = pico_mdns_handle_data_as_questions(&data, qdcount, packet); - - /* Check for Simultaneous Probe Tiebreaking */ - nscount = short_be(packet->nscount); - pico_mdns_handle_data_as_answers_generic(&data, nscount, packet, 1); - - /* Try to reply with the answers */ - if (pico_tree_count(&antree) != 0) { - int retval = pico_mdns_reply(&antree, peer); - PICO_MDNS_RTREE_DESTROY(&antree); - return retval; - } - - return 0; -} - -/* **************************************************************************** - * Handles a single incoming answer packet. - * - * @param packet Received answer packet. - * @return Returns 0 when the response packet is properly handled. - * ****************************************************************************/ -static int -pico_mdns_handle_response_packet( pico_dns_packet *packet ) -{ - uint8_t *data = NULL; - uint16_t ancount = 0; - - /* Move to the data section of the packet */ - data = (uint8_t *)packet + sizeof(struct pico_dns_header); - - /* Generate a list of answers */ - ancount = short_be(packet->ancount); - if (pico_mdns_handle_data_as_answers_generic(&data, ancount, packet, 0)) { - mdns_dbg("Could not handle data as answers\n"); - return -1; - } - - return 0; -} - -/* **************************************************************************** - * Parses an incoming packet and handles it according to the type of the - * packet. Packet type determination happens in this function. - * - * @param buf Memory buffer containing the received packet - * @param buflen Length in bytes of the memory buffer - * @param peer IPv4 address of the peer who sent the received packet. - * @return 0 when the packet is properly handled. Something else when it's not - * ****************************************************************************/ -static int -pico_mdns_recv( void *buf, int buflen, struct pico_ip4 peer ) -{ - pico_dns_packet *packet = (pico_dns_packet *) buf; - uint16_t qdcount = short_be(packet->qdcount); - uint16_t ancount = short_be(packet->ancount); - uint16_t authcount = short_be(packet->nscount); - uint16_t addcount = short_be(packet->arcount); - - /* RFC6762: */ - /* 18.3: Messages received with an opcode other than zero MUST be silently */ - /* ignored. */ - /* 18.11: messages received with non-zero Response Codes MUST be silently */ - /* ignored */ - if(packet->opcode == 0 && packet->rcode == 0) { - mdns_dbg(">>>>>>> QDcount: %u, ANcount: %u, NScount: %u, ARcount: %u\n", - qdcount, ancount, authcount, addcount); - - IGNORE_PARAMETER(buflen); - IGNORE_PARAMETER(addcount); - - /* DNS PACKET TYPE DETERMINATION */ - if ((qdcount > 0)) { - if (authcount > 0) { - mdns_dbg(">>>>>>> RCVD a mDNS probe query:\n"); - /* Packet is probe query */ - if (pico_mdns_handle_probe_packet(packet, peer) < 0) { - mdns_dbg("Could not handle mDNS probe query!\n"); - return -1; - } - } else { - mdns_dbg(">>>>>>> RCVD a plain mDNS query:\n"); - /* Packet is a plain query */ - if (pico_mdns_handle_query_packet(packet, peer) < 0) { - mdns_dbg("Could not handle plain DNS query!\n"); - return -1; - } - } - } else { - if (ancount > 0) { - mdns_dbg(">>>>>>> RCVD a mDNS response:\n"); - /* Packet is a response */ - if (pico_mdns_handle_response_packet(packet) < 0) { - mdns_dbg("Could not handle DNS response!\n"); - return -1; - } - } else { - /* Something went wrong here... */ - mdns_dbg("RCVD Packet contains no questions or answers...\n"); - return -1; - } - } - } - - return 0; -} - -/* **************************************************************************** - * picoTCP callback for UDP IPv4 Socket events - * - * @param ev Determination of the occurred event - * @param s Socket on which the event occurred - * ****************************************************************************/ -static void -pico_mdns_event4( uint16_t ev, struct pico_socket *s ) -{ - char *recvbuf = NULL; - struct pico_ip4 peer = { - 0 - }; - int pico_read = 0; - uint16_t port = 0; - - /* process read event, data available */ - if (ev == PICO_SOCK_EV_RD) { - mdns_dbg("\n>>>>>>> READ EVENT! <<<<<<<\n"); - recvbuf = PICO_ZALLOC(PICO_MDNS_MAXBUF); - if (!recvbuf) { - pico_err = PICO_ERR_ENOMEM; - return; - } - - /* Receive while data is available in socket buffer */ - while((pico_read = pico_socket_recvfrom(s, recvbuf, PICO_MDNS_MAXBUF, - &peer, &port)) > 0) { - /* Handle the MDNS data received */ - pico_mdns_recv(recvbuf, pico_read, peer); - } - PICO_FREE(recvbuf); - mdns_dbg(">>>>>>>>>>>>>><<<<<<<<<<<<<\n\n"); - } else - mdns_dbg("Socket Error received. Bailing out.\n"); -} - -/* MARK: ADDRESS RESOLUTION */ - -/* **************************************************************************** - * Send a mDNS query packet on the wire. This is scheduled with a pico_timer- - * event. - * - * @param now Ignore - * @param arg Void-pointer to query-cookie - * ****************************************************************************/ -static void -pico_mdns_send_query_packet( pico_time now, void *arg ) -{ - struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg; - pico_dns_qtree *questions = NULL; - pico_dns_packet *packet = NULL; - uint16_t len = 0; - - IGNORE_PARAMETER(now); - - /* Parse in the cookie */ - if (!cookie || cookie->type != PICO_MDNS_PACKET_TYPE_QUERY) - return; - - /* Create DNS query packet */ - questions = &(cookie->qtree); - if (!(packet = pico_dns_query_create(questions, NULL, NULL, NULL, &len))) { - mdns_dbg("Could not create query packet!\n"); - return; - } - - packet->id = 0; - - /* RFC6762: 18.6: In both multicast query and response messages, - the RD bit SHOULD be zero on transmission. In pico_dns_fill_packet_header, - the RD bit is set to PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */ - packet->rd = PICO_DNS_RD_NO_DESIRE; - - if (cookie->status != PICO_MDNS_COOKIE_STATUS_CANCELLED) { - cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE; - if(pico_mdns_send_packet(packet, len) != (int)len) { - mdns_dbg("Send error occurred!\n"); - return; - } - - mdns_dbg("DONE - Sent query.\n"); - } else { - mdns_dbg("DONE - Duplicate query suppressed.\n"); - pico_timer_cancel(cookie->send_timer); - /* Remove cookie from Cookies */ - cookie = pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void **)&cookie); - } - - PICO_FREE(packet); -} - -/* **************************************************************************** - * Generates a mDNS query packet and schedules a sending on the wire. - * - * @param url URL for the name of the question contained in the query - * @param type DNS type of the question contained in the query - * @param callback Callback to call when a response on this query is RCVD. - * @return 0 When the query is successfully generated and scheduled for sending - * ****************************************************************************/ -static int -pico_mdns_getrecord_generic( const char *url, uint16_t type, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg) -{ - struct pico_mdns_cookie *cookie = NULL; - PICO_DNS_QTREE_DECLARE(qtree); - PICO_MDNS_RTREE_DECLARE(antree); - PICO_MDNS_RTREE_DECLARE(artree); - struct pico_dns_question *q = NULL; - uint16_t l = 0; - - /* Create a single question and add it to the tree */ - q = pico_mdns_question_create(url, &l, PICO_PROTO_IPV4, type, 0, 0); - if (!q) { - mdns_dbg("question_create returned NULL!\n"); - return -1; - } - - if (pico_tree_insert(&qtree, q)) { - mdns_dbg("inserting query into tree failed!\n"); - pico_dns_question_delete((void **)&q); - return -1; - } - - - /* Create a mDNS cookie to send */ - if (!(cookie = pico_mdns_cookie_create(qtree, antree, artree, 1, - PICO_MDNS_PACKET_TYPE_QUERY, - callback, arg))) { - PICO_DNS_QTREE_DESTROY(&qtree); - mdns_dbg("cookie_create returned NULL!\n"); - return -1; - } - - /* Add cookie to Cookies to be able to find it afterwards */ - if(pico_tree_insert(&Cookies, cookie) ){ - mdns_dbg("inserting cookie into tree failed!\n"); - PICO_DNS_QTREE_DESTROY(&qtree); - pico_mdns_cookie_delete((void **)&cookie); - return -1; - } - - /* Create new pico_timer-event to send packet */ - if (!pico_mdns_timer_add((pico_rand() % 120) + 20, pico_mdns_send_query_packet, - (void *)cookie)) { - mdns_dbg("MDNS: Failed to start send_query_packet timer\n"); - pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void**)&cookie); - pico_dns_question_delete((void**)&q); - return -1; - } - - return 0; -} - -/* **************************************************************************** - * API-call to query a record with a certain URL and type. First checks the - * Cache for this record. If no cache-entry is found, a query will be sent on - * the wire for this record. - * - * @param url URL to query for. - * @param type DNS type to query for. - * @param callback Callback to call when records are found for the query. - * @return 0 when query is correctly parsed, something else on failure. - * ****************************************************************************/ -int -pico_mdns_getrecord( const char *url, uint16_t type, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ -#if PICO_MDNS_ALLOW_CACHING == 1 - PICO_MDNS_RTREE_DECLARE(cache_hits); - char *name = NULL; -#endif - - /* Check params */ - if (!url) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* First, try to find records in the cache */ -#if PICO_MDNS_ALLOW_CACHING == 1 - name = pico_dns_url_to_qname(url); - cache_hits = pico_mdns_rtree_find_name_type(&Cache, name, type, 0); - PICO_FREE(name); - if (pico_tree_count(&cache_hits) > 0) { - mdns_dbg("CACHE HIT! Passed cache records to callback.\n"); - callback(&cache_hits, NULL, arg); - } else { -#endif - mdns_dbg("CACHE MISS! Trying to resolve URL '%s'...\n", url); - return pico_mdns_getrecord_generic(url, type, callback, arg); -#if PICO_MDNS_ALLOW_CACHING == 1 -} -return 0; -#endif -} - -/* MARK: PROBING & ANNOUNCING */ - -/* **************************************************************************** - * Function to create an announcement from an mDNS cookie and send it on the - * wire. - * - * @param now Ignore - * @param arg Void-pointer to mDNS announcement cookie - * ***************************************************************************/ -static void -pico_mdns_send_announcement_packet( pico_time now, void *arg ) -{ - struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg; - - /* Check params */ - IGNORE_PARAMETER(now); - if (!cookie) { - return; - } - - cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE; - if (cookie->count > 0) { - /* Send the announcement on the wire */ - pico_mdns_reply(&(cookie->antree), inaddr_any); - mdns_dbg("DONE - Sent announcement!\n"); - - /* The Multicast DNS responder MUST send at least two unsolicited - responses, one second apart. To provide increased robustness - against packet loss, a responder MAY send up to eight unsolicited - responses, provided that the interval between unsolicited - responses increases by at least a factor of two with - every response sent. - */ - --(cookie->count); - if (cookie->count == 0) { - cookie->status = PICO_MDNS_COOKIE_STATUS_INACTIVE; - - /* Update the states of the records */ - pico_mdns_my_records_claimed(cookie->antree, - cookie->callback, - cookie->arg); - - /* Try to delete the cookie */ - pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void **)&cookie); - } - else{ - /* - A responder MAY send up to eight unsolicited responses, - provided that the interval between unsolicited responses increases - by at least a factor of two with every response sent. - Starting at 1 second. - So we bithsift to get our powers of two and we multiply by 1000 to - get our miliseconds. - */ - if (!pico_mdns_timer_add((pico_time)((1 << (PICO_MDNS_ANNOUNCEMENT_COUNT - cookie->count - 1)) - * 1000), pico_mdns_send_announcement_packet, cookie)) { - mdns_dbg("MDNS: Failed to start send_announcement_packet timer\n"); - /* TODO no idea what the consequences of this are */ - - } - } - } -} - -/* **************************************************************************** - * Announces all 'my records' which passed the probing-step or just shared - * records. - * - * @param callback Gets called when all records in the cookie are announced. - * @return 0 When the host successfully started announcing. - * ****************************************************************************/ -static int -pico_mdns_announce( void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - struct pico_mdns_cookie *announcement_cookie = NULL; - PICO_DNS_QTREE_DECLARE(qtree); - PICO_MDNS_RTREE_DECLARE(antree); - PICO_MDNS_RTREE_DECLARE(artree); - - /* Check params */ - if (!callback) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - - IGNORE_PARAMETER(arg); - - /* Find out which resource records can be announced */ - antree = pico_mdns_my_records_find_probed(); - if (pico_tree_count(&antree) == 0) { - return 0; - } - - /* Create a mDNS packet cookie */ - if (!(announcement_cookie = pico_mdns_cookie_create(qtree, antree, artree, - PICO_MDNS_ANNOUNCEMENT_COUNT, - PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT, - callback, arg))) { - mdns_dbg("cookie_create returned NULL!\n"); - PICO_MDNS_RTREE_DESTROY(&antree); - return -1; - } - - /* Send a first unsolicited announcement */ - pico_mdns_send_announcement_packet(0, announcement_cookie); - mdns_dbg("DONE - Started announcing.\n"); - - return 0; -} - -/* **************************************************************************** - * Makes sure the cache flush bit of the to probe records is cleared, and - * generates a DNS record tree to insert in the Authority Section of the DNS - * packet - * - * @param records mDNS records to probe. - * @return DNS record tree to with actual DNS records to insert in Authority - * Section of probe packet. - * ****************************************************************************/ -static pico_dns_rtree -pico_mdns_gen_probe_auths( pico_mdns_rtree *records ) -{ - PICO_DNS_RTREE_DECLARE(nstree); - struct pico_tree_node *node = NULL; - struct pico_mdns_record *record = NULL; - - pico_tree_foreach(node, records) { - if ((record = node->keyValue) && record->record) { - /* Clear the cache flush bit for authority records in probes */ - PICO_MDNS_CLR_MSB_BE(record->record->rsuffix->rclass); - /* Only the actual DNS records is required */ - if (pico_tree_insert(&nstree, record->record) == &LEAF) { - mdns_dbg("MDNS: Failed to insert record in tree\n"); - break; - } - } - } - - return nstree; -} - -/* **************************************************************************** - * Function to create a probe from an mDNS cookie and send it on the wire. - * - * @param now Ignore - * @param arg Void-pointer to mDNS probe cookie - * ****************************************************************************/ -static void -pico_mdns_send_probe_packet( pico_time now, void *arg ) -{ - struct pico_mdns_cookie *cookie = (struct pico_mdns_cookie *)arg; - pico_dns_packet *packet = NULL; - PICO_DNS_RTREE_DECLARE(nstree); - uint16_t len = 0; - - /* Check params */ - IGNORE_PARAMETER(now); - /* if (!cookie || (cookie->type == PICO_MDNS_COOKIE_STATUS_INACTIVE)) { */ - if (!cookie || (cookie->type != PICO_MDNS_PACKET_TYPE_PROBE)) { - pico_err = PICO_ERR_EINVAL; - return; - } else { - /* Set the cookie to the active state */ - cookie->status = PICO_MDNS_COOKIE_STATUS_ACTIVE; - if (cookie->count > 0) { - --(cookie->count); - - /* Generate authority records */ - nstree = pico_mdns_gen_probe_auths(&(cookie->antree)); - - /* Create an mDNS answer */ - if (!(packet = pico_dns_query_create(&(cookie->qtree), NULL, - &nstree, NULL, &len))) { - PICO_DNS_RTREE_DESTROY(&nstree); - mdns_dbg("Could not create probe packet!\n"); - return; - } - - pico_tree_destroy(&nstree, NULL); - - /* RFC6762: 18.1 */ - packet->id = 0; - - /* RFC6762: 18.6: In both multicast query and response messages, - the RD bit SHOULD be zero on transmission. - In pico_dns_fill_packet_header, the RD bit is set to - PICO_DNS_RD_IS_DESIRED, which is defined to be 1 */ - packet->rd = PICO_DNS_RD_NO_DESIRE; - - /* Send the mDNS answer unsolicited via multicast */ - if(pico_mdns_send_packet(packet, len) != (int)len) { - mdns_dbg("Send error occurred!\n"); - return; - } - - PICO_FREE(packet); - - mdns_dbg("DONE - Sent probe!\n"); - - /* Probes should be sent with a delay in between of 250 ms */ - if (PICO_MDNS_COOKIE_STATUS_ACTIVE == cookie->status ) { - cookie->send_timer = pico_mdns_timer_add(250, - pico_mdns_send_probe_packet, - (void *)cookie); - if (!cookie->send_timer) { - mdns_dbg("MDNS: Failed to start send_probe_packet timer\n"); - /* TODO no idea what the consequences of this are */ - return; - } - } - } else { - mdns_dbg("DONE - Probing.\n"); - - pico_mdns_my_records_probed(&(cookie->antree)); - - /* Start announcing */ - cookie->count = PICO_MDNS_ANNOUNCEMENT_COUNT; - cookie->type = PICO_MDNS_PACKET_TYPE_ANNOUNCEMENT; - pico_mdns_send_announcement_packet(0, (void*) cookie); - } - } -} /* Static path count: 10 */ - -/* **************************************************************************** - * Adds a new probe question to the probe cookie questions, if a probe question - * for a new is already present in the question-tree, it will not be generated - * and inserted again - * - * @param qtree Probe question tree - * @param name Name for which the function has to create a probe question - * @return 0 when the probe question is already present or added successfully. - * ****************************************************************************/ -static int -pico_mdns_add_probe_question( pico_dns_qtree *qtree, - char *name ) -{ - struct pico_dns_question *new = NULL; - char *url = NULL; - uint16_t qlen = 0; - uint8_t flags = PICO_MDNS_QUESTION_FLAG_PROBE; - -#if PICO_MDNS_PROBE_UNICAST == 1 - flags |= PICO_MDNS_QUESTION_FLAG_UNICAST_RES; -#endif - - /* Convert name to URL and try to create a new probe question */ - if (!(url = pico_dns_qname_to_url(name))) - return -1; - - mdns_dbg("Probe question for URL: %s\n", url); - if (!(new = pico_mdns_question_create(url, &qlen, PICO_PROTO_IPV4, - PICO_DNS_TYPE_ANY, flags, 0))) { - PICO_FREE(url); - return -1; - } - - PICO_FREE(url); - - /* Try to find an existing question in the vector */ - if (pico_tree_insert(qtree, new)) - pico_dns_question_delete((void **)&new); - - return 0; -} - -/* **************************************************************************** - * Find any of my record that need to be probed and try to probe them. - * - * @param callback Callback to call when all records are properly registered - * @return When host successfully started probing. - * ****************************************************************************/ -static int pico_mdns_probe( void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - struct pico_mdns_cookie *cookie = NULL; - struct pico_mdns_record *record = NULL; - struct pico_tree_node *node = NULL; - PICO_DNS_QTREE_DECLARE(qtree); - PICO_MDNS_RTREE_DECLARE(antree); - PICO_MDNS_RTREE_DECLARE(artree); - - /* Check params */ - if (!callback) { - pico_err = PICO_ERR_EINVAL; - return -1; - } else { - /* Find my records that need to pass the probing step first - * All records that don't have their PROBED flag set and - * are not being probed at hte moment are added to the tree - */ - antree = pico_mdns_my_records_find_to_probe(); - - /* Create probe questions for the records to be probed */ - pico_tree_foreach(node, &antree) { - if ((record = node->keyValue)) { - pico_mdns_add_probe_question(&qtree, record->record->rname); - } - } - - /* Create a mDNS packet to send */ - cookie = pico_mdns_cookie_create(qtree, antree, artree, - PICO_MDNS_PROBE_COUNT, - PICO_MDNS_PACKET_TYPE_PROBE, - callback, arg); - if (!cookie) { - mdns_dbg("Cookie_create returned NULL @ probe()!\n"); - PICO_DNS_QTREE_DESTROY(&qtree); - PICO_MDNS_RTREE_DESTROY(&antree); - return -1; - } - - /* Add the probe cookie to the cookie tree */ - if (pico_tree_insert(&Cookies, cookie)) { - pico_mdns_cookie_delete((void **)&cookie); - return -1; - } - - /* RFC6762: 8.1. Probing */ - /* When ready to send its Multicast DNS probe packet(s) the host should */ - /* first wait for a short random delay time, uniformly distributed in */ - /* the range 0-250 ms. */ - cookie->send_timer = pico_mdns_timer_add(pico_rand() % 250, - pico_mdns_send_probe_packet, - (void *)cookie); - if (!cookie->send_timer) { - mdns_dbg("MDNS: Failed to start send_probe_packet timer\n"); - pico_tree_delete(&Cookies, cookie); - pico_mdns_cookie_delete((void**)&cookie); - return -1; - } - - mdns_dbg("DONE - Started probing.\n"); - } - return 0; -} /* Static path count: 9 */ - -/* MARK: API functions */ - -/* **************************************************************************** - * Claim or reclaim all the mDNS records contain in a tree in one single call - * - * @param rtree mDNS record tree with records to claim - * @param reclaim Whether or not the records in tree should be reclaimed. - * @param callback Callback to call when all records are properly registered - * @return 0 When claiming didn't horribly fail. - * ****************************************************************************/ -static int -pico_mdns_claim_generic( pico_mdns_rtree rtree, - uint8_t reclaim, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - /* Check if arguments are passed correctly */ - if (!callback) { - mdns_dbg("NULL pointers passed to 'pico_mdns_claim()'!\n"); - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Check if module is initialised */ - if (!mdns_sock_ipv4) { - mdns_dbg("Socket not initialised, did you call 'pico_mdns_init()'?\n"); - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* 1.) Appending records to 'my records' */ - pico_mdns_my_records_add(&rtree, reclaim); - - /* 2a.) Try to probe any records */ - pico_mdns_probe(callback, arg); - - /* 2b.) Try to announce any records */ - pico_mdns_announce(callback, arg); - - return 0; -} - -/* **************************************************************************** - * Claim all different mDNS records in a tree in a single API-call. All records - * in tree are called in a single new claim-session. - * - * @param rtree mDNS record tree with records to claim - * @param callback Callback to call when all record are properly claimed. - * @return 0 When claiming didn't horribly fail. - * ****************************************************************************/ -int -pico_mdns_claim( pico_mdns_rtree rtree, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - return pico_mdns_claim_generic(rtree, PICO_MDNS_NO_RECLAIM, callback, arg); -} - -/* **************************************************************************** - * Reclaim records when a conflict occurred, claim-session will stay the same - * as the session in which the conflict occurred. - * - * @param rtree mDNS record tree with records to claim - * @param callback Callback to call when all record are properly claimed. - * @return 0 When claiming didn't horribly fail. - * ****************************************************************************/ -static int -pico_mdns_reclaim( pico_mdns_rtree rtree, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - return pico_mdns_claim_generic(rtree, PICO_MDNS_RECLAIM, callback, arg); -} - -/* **************************************************************************** - * Tries to claim a hostname for this machine. Claims automatically a - * unique A record with the IPv4-address of this host. - * The hostname won't be set directly when this functions returns, - * but only if the claiming of the unique record succeeded. - * Init-callback will be called when the hostname-record is successfully - * registered. - * - * @param url URL to set the hostname to. - * @param arg Argument to pass to the init-callback. - * @return 0 when the host started registering the hostname-record successfully, - * Returns something else when it didn't succeeded. - * ****************************************************************************/ -int -pico_mdns_tryclaim_hostname( const char *url, void *arg ) -{ - PICO_MDNS_RTREE_DECLARE(rtree); - struct pico_mdns_record *record = NULL; - - /* Check if module is initialised */ - if (!mdns_sock_ipv4) { - mdns_dbg("mDNS socket not initialised, did you call 'pico_mdns_init()'?\n"); - pico_err = PICO_ERR_EINVAL; - return -1; - } else { - /* Create an A record for hostname */ - record = pico_mdns_record_create(url, - &(mdns_sock_ipv4->local_addr.ip4.addr), - PICO_SIZE_IP4, PICO_DNS_TYPE_A, - PICO_MDNS_DEFAULT_TTL, - (PICO_MDNS_RECORD_UNIQUE | - PICO_MDNS_RECORD_HOSTNAME)); - if (!record) { - mdns_dbg("Could not create A record for hostname %s!\n", - strerror(pico_err)); - return -1; - } - - /* TODO: Create IPv6 record */ - /* TODO: Create a reverse resolution record */ - - /* Try to claim the record */ - if (pico_tree_insert(&rtree, record)) { - pico_mdns_record_delete((void **)&record); - return -1; - } - - if (pico_mdns_claim(rtree, init_callback, arg)) { - mdns_dbg("Could not claim record for hostname %s!\n", url); - PICO_MDNS_RTREE_DESTROY(&rtree); - return -1; - } - - pico_tree_destroy(&rtree, NULL); - } - return 0; -} /* Static path count: 9 */ - -/* **************************************************************************** - * Get the hostname for this machine. - * - * @return Returns the hostname for this machine when the module is initialised - * Returns NULL when the module is not initialised. - * ****************************************************************************/ -const char * -pico_mdns_get_hostname( void ) -{ - /* Check if module is initialised */ - if (!mdns_sock_ipv4) { - mdns_dbg("mDNS socket not initialised, did you call 'pico_mdns_init()'?\n"); - pico_err = PICO_ERR_EINVAL; - return NULL; - } - - return (const char *)_hostname; -} - -static void -pico_mdns_cleanup( void ) -{ - /* Delete socket if it was previously opened */ - if (mdns_sock_ipv4) { - pico_socket_del(mdns_sock_ipv4); - } - - /* Clear out every memory structure used by mDNS */ -#if PICO_MDNS_ALLOW_CACHING == 1 - PICO_MDNS_RTREE_DESTROY(&Cache); -#endif /* PICO_MDNS_ALLOW_CACHING */ - PICO_MDNS_RTREE_DESTROY(&MyRecords); - PICO_MDNS_CTREE_DESTROY(&Cookies); - - /* Cancel every timer */ - pico_timer_cancel_hashed(mdns_hash); -} - -/* **************************************************************************** - * Initialises the entire mDNS-module and sets the hostname for this machine. - * Sets up the global mDNS socket properly and calls callback when succeeded. - * Only when the module is properly initialised records can be registered on - * the module. - * - * @param hostname_url URL to set the hostname to. - * @param address IPv4-address of this host to bind to. - * @param callback Callback to call when the hostname is registered and - * also the global mDNS module callback. Gets called when - * Passive conflicts occur, so changes in records can be - * tracked in this callback. - * @param arg Argument to pass to the init-callback. - * @return 0 when the module is properly initialised and the host started regis- - * tering the hostname. Returns something else went the host failed - * initialising the module or registering the hostname. - * ****************************************************************************/ -int -pico_mdns_init( const char *hostname, - struct pico_ip4 address, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ) -{ - struct pico_ip_mreq mreq4; - uint16_t proto4 = PICO_PROTO_IPV4, port = 0, loop = 0, ttl = 255; - - /* Initialise port */ - port = short_be(mdns_port); - - /* Check callback parameter */ - if(!callback || !hostname) { - mdns_dbg("No callback function supplied!\n"); - pico_err = PICO_ERR_EINVAL; - return -1; - } - - /* Clear out all the memory structure's and delete socket if it was - * already opened before */ - pico_mdns_cleanup(); - - /* Create a hash to identify mDNS timers with */ - mdns_hash = pico_hash(hostname, (uint32_t)strlen(hostname)); - - /* Open global IPv4 mDNS socket */ - mdns_sock_ipv4 = pico_socket_open(proto4, PICO_PROTO_UDP, &pico_mdns_event4); - if(!mdns_sock_ipv4) { - mdns_dbg("pico_socket_open returned NULL-ptr...\n"); - return -1; - } - - /* Convert the mDNS IPv4 destination address to struct */ - if(pico_string_to_ipv4(PICO_MDNS_DEST_ADDR4, &mreq4.mcast_group_addr.ip4.addr)) { - mdns_dbg("String to IPv4 error\n"); - return -1; - } - - /* Receive data on any network interface */ - mreq4.mcast_link_addr.ip4 = inaddr_any; - - /* Don't want the multicast data to be looped back to the host */ - if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_MULTICAST_LOOP, &loop)) { - mdns_dbg("socket_setoption PICO_IP_MULTICAST_LOOP failed\n"); - return -1; - } - - /* Tell the stack we're interested in this particular multicast group */ - if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_ADD_MEMBERSHIP, &mreq4)) { - mdns_dbg("socket_setoption PICO_IP_ADD_MEMBERSHIP failed\n"); - return -1; - } - - /* RFC6762: - * 11. Source Address Check - * All Multicast DNS responses (including responses sent via unicast) - * SHOULD be sent with IP TTL set to 255. - */ - if(pico_socket_setoption(mdns_sock_ipv4, PICO_IP_MULTICAST_TTL, &ttl)) { - mdns_dbg("socket_setoption PICO_IP_MULTICAST_TTL failed\n"); - return -1; - } - - /* Bind to mDNS port */ - if (pico_socket_bind(mdns_sock_ipv4, (void *)&address, &port)) { - mdns_dbg("Bind error!\n"); - return -1; - } - - /* Set the global init callback variable */ - init_callback = callback; - if (!pico_mdns_timer_add(PICO_MDNS_RR_TTL_TICK, pico_mdns_tick, NULL)) { - mdns_dbg("MDNS: Failed to start tick timer\n"); - return -1; - } - - /* Set the hostname eventually */ - return pico_mdns_tryclaim_hostname(hostname, arg); -} - -#endif /* PICO_SUPPORT_MDNS */ diff --git a/core/deps/picotcp/modules/pico_mdns.h b/core/deps/picotcp/modules/pico_mdns.h deleted file mode 100644 index 54c8ede7c..000000000 --- a/core/deps/picotcp/modules/pico_mdns.h +++ /dev/null @@ -1,206 +0,0 @@ -/* **************************************************************************** - * PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved. - * See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. - * . - * Author: Toon Stegen, Jelle De Vleeschouwer - * ****************************************************************************/ -#ifndef INCLUDE_PICO_MDNS -#define INCLUDE_PICO_MDNS - -#include "pico_dns_common.h" -#include "pico_tree.h" -#include "pico_ipv4.h" - -/* ********************************* CONFIG ***********************************/ -#define PICO_MDNS_PROBE_UNICAST 1 /* Probe queries as QU-questions */ -#define PICO_MDNS_CONTINUOUS_REFRESH 0 /* Continuously update cache */ -#define PICO_MDNS_ALLOW_CACHING 1 /* Enable caching on this host */ -#define PICO_MDNS_DEFAULT_TTL 120 /* Default TTL of mDNS records */ -#define PICO_MDNS_SERVICE_TTL 120 /* Default TTL of SRV/TXT/PTR/NSEC */ -#define PICO_MDNS_PROBE_COUNT 3 -/* Amount of probes to send: - RFC6762: 8.1. Probing: - 250 ms after the first query, the host should send a second; then, - 250 ms after that, a third. If, by 250 ms after the third probe, no - conflicting Multicast DNS responses have been received, the host may - move to the next step, announcing. - */ - -#define PICO_MDNS_ANNOUNCEMENT_COUNT 3 -/* Amount of announcements to send: (we've opted for 1 extra for robustness) - RFC6762: 8.3. Announcing: - The Multicast DNS responder MUST send at least two unsolicited - responses, one second apart. To provide increased robustness against - packet loss, a responder MAY send up to eight unsolicited responses, - provided that the interval between unsolicited responses increases by - at least a factor of two with every response sent. - */ -/* ****************************************************************************/ - -#define PICO_MDNS_DEST_ADDR4 "224.0.0.251" - -/* To make mDNS records unique or shared records */ -#define PICO_MDNS_RECORD_UNIQUE 0x00u -#define PICO_MDNS_RECORD_SHARED 0x01u - -/* To indicate if we reclaim or not */ -#define PICO_MDNS_RECLAIM 1 -#define PICO_MDNS_NO_RECLAIM 0 - -/* Flag to check for when records are returned, to determine the hostname */ -#define PICO_MDNS_RECORD_HOSTNAME 0x02u -#define IS_HOSTNAME_RECORD(x) \ - (((x)->flags) & PICO_MDNS_RECORD_HOSTNAME) ? (1) : (0) - -/* --- MDNS resource record --- */ -struct pico_mdns_record -{ - struct pico_dns_record *record; /* DNS Resource Record */ - uint32_t current_ttl; /* Current TTL */ - uint8_t flags; /* Resource Record flags */ - uint8_t claim_id; /* Claim ID number */ -}; - -/* **************************************************************************** - * Compares 2 mDNS records by type, name AND rdata for a truly unique result - * - * @param ra mDNS record A - * @param rb mDNS record B - * @return 0 when records are equal, returns difference when they're not. - * ****************************************************************************/ -int -pico_mdns_record_cmp( void *a, void *b ); - -/* **************************************************************************** - * Deletes a single mDNS resource record. - * - * @param record Void-pointer to mDNS Resource Record. Can be used with pico_- - * tree-destroy. - * @return Returns 0 on success, something else on failure. - * ****************************************************************************/ -int -pico_mdns_record_delete( void **record ); - -/* **************************************************************************** - * Creates a single standalone mDNS resource record with given name, type and - * data to register on the network. - * - * @param url DNS rrecord name in URL format. Will be converted to DNS - * name notation format. - * @param _rdata Memory buffer with data to insert in the resource record. If - * data of record should contain a DNS name, the name in the - * databuffer needs to be in URL-format. - * @param datalen The exact length in bytes of the _rdata-buffer. If data of - * record should contain a DNS name, datalen needs to be - * pico_dns_strlen(_rdata). - * @param rtype DNS type of the resource record to be. - * @param rclass DNS class of the resource record to be. - * @param rttl DNS ttl of the resource record to be. - * @param flags You can specify if the mDNS record should be a shared record - * rather than a unique record. - * @return Pointer to newly created mDNS resource record. - * ****************************************************************************/ -struct pico_mdns_record * -pico_mdns_record_create( const char *url, - void *_rdata, - uint16_t datalen, - uint16_t rtype, - uint32_t rttl, - uint8_t flags ); - - - -/* **************************************************************************** - * Definition of DNS record tree - * ****************************************************************************/ -typedef struct pico_tree pico_mdns_rtree; -#define PICO_MDNS_RTREE_DECLARE(name) \ - pico_mdns_rtree (name) = {&LEAF, pico_mdns_record_cmp} -#define PICO_MDNS_RTREE_DESTROY(rtree) \ - pico_tree_destroy((rtree), pico_mdns_record_delete) -#define PICO_MDNS_RTREE_ADD(tree, record) \ - pico_tree_insert((tree), (record)) - -/* **************************************************************************** - * API-call to query a record with a certain URL and type. First checks the - * Cache for this record. If no cache-entry is found, a query will be sent on - * the wire for this record. - * - * @param url URL to query for. - * @param type DNS type top query for. - * @param callback Callback to call when records are found for the query. - * @return 0 when query is correctly parsed, something else on failure. - * ****************************************************************************/ -int -pico_mdns_getrecord( const char *url, uint16_t type, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ); - -/* **************************************************************************** - * Claim all different mDNS records in a tree in a single API-call. All records - * in tree are called in a single new claim-session. - * - * @param rtree mDNS record tree with records to claim - * @param callback Callback to call when all record are properly claimed. - * @return 0 When claiming didn't horribly fail. - * ****************************************************************************/ -int -pico_mdns_claim( pico_mdns_rtree record_tree, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ); - -/* **************************************************************************** - * Tries to claim a hostname for this machine. Claims automatically a - * unique A record with the IPv4-address of this host. - * The hostname won't be set directly when this functions returns, - * but only if the claiming of the unique record succeeded. - * Init-callback will be called when the hostname-record is successfully - * registered. - * - * @param url URL to set the hostname to. - * @param arg Argument to pass to the init-callback. - * @return 0 when the host started registering the hostname-record successfully, - * Returns something else when it didn't succeeded. - * ****************************************************************************/ -int -pico_mdns_tryclaim_hostname( const char *url, void *arg ); - -/* **************************************************************************** - * Get the current hostname for this machine. - * - * @return Returns the hostname for this machine when the module is initialised - * Returns NULL when the module is not initialised. - * ****************************************************************************/ -const char * -pico_mdns_get_hostname( void ); - -/* **************************************************************************** - * Initialises the entire mDNS-module and sets the hostname for this machine. - * Sets up the global mDNS socket properly and calls callback when succeeded. - * Only when the module is properly initialised records can be registered on - * the module. - * - * @param hostname URL to set the hostname to. - * @param address IPv4-address of this host to bind to. - * @param callback Callback to call when the hostname is registered and - * also the global mDNS module callback. Gets called when - * Passive conflicts occur, so changes in records can be - * tracked in this callback. - * @param arg Argument to pass to the init-callback. - * @return 0 when the module is properly initialised and the host started regis- - * tering the hostname. Returns something else went the host failed - * initialising the module or registering the hostname. - * ****************************************************************************/ -int -pico_mdns_init( const char *hostname, - struct pico_ip4 address, - void (*callback)(pico_mdns_rtree *, - char *, - void *), - void *arg ); - -#endif /* _INCLUDE_PICO_MDNS */ diff --git a/core/deps/picotcp/modules/pico_tcp.c b/core/deps/picotcp/modules/pico_tcp.c index dc1c4927e..6dc9ac566 100644 --- a/core/deps/picotcp/modules/pico_tcp.c +++ b/core/deps/picotcp/modules/pico_tcp.c @@ -980,6 +980,8 @@ static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f) hdr->crc = 0; hdr->crc = short_be(pico_tcp_checksum(f)); + f->local_ip.addr = ts->sock.local_addr.ip4.addr; // Masqueraded + return tcp_send_try_enqueue(ts, f); } @@ -1318,6 +1320,7 @@ static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags, int is_kee hdr->crc = short_be(pico_tcp_checksum(f)); /* TCP: ENQUEUE to PROTO */ + f->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded pico_enqueue(&tcp_out, f); } @@ -1363,6 +1366,7 @@ static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq) hdr->crc = short_be(pico_tcp_checksum(f)); /* TCP: ENQUEUE to PROTO */ + f->local_ip.addr = s->local_addr.ip4.addr; // Masqueraded pico_enqueue(&tcp_out, f); tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n"); return 0; @@ -1535,6 +1539,7 @@ static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr) hdr->crc = short_be(pico_tcp_checksum(f)); /* TCP: ENQUEUE to PROTO */ + f->local_ip.addr = s->local_addr.ip4.addr; // Masqueraded pico_enqueue(&tcp_out, f); /***************************************************************************/ @@ -1585,6 +1590,7 @@ static void tcp_send_fin(struct pico_socket_tcp *t) hdr->crc = short_be(pico_tcp_checksum(f)); /* tcp_dbg("SENDING FIN...\n"); */ if (t->linger_timeout > 0) { + f->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded pico_enqueue(&tcp_out, f); t->snd_nxt++; } else { @@ -1853,6 +1859,7 @@ static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f) return -1; } + cpy->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded if (pico_enqueue(&tcp_out, cpy) > 0) { t->snd_last_out = SEQN(cpy); add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME); @@ -2010,6 +2017,7 @@ static int tcp_retrans(struct pico_socket_tcp *t, struct pico_frame *f) return -1; } + cpy->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded if (pico_enqueue(&tcp_out, cpy) > 0) { t->in_flight++; t->snd_last_out = SEQN(cpy); @@ -2386,6 +2394,9 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f) if (!new) return -1; + if (s->local_port == 0) + new->sock.local_port = hdr->trans.dport; // Masqueraded + #ifdef PICO_TCP_SUPPORT_SOCKET_STATS if (!pico_timer_add(2000, sock_stats, s)) { tcp_dbg("TCP: Failed to start socket statistics timer\n"); @@ -3130,7 +3141,10 @@ int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f) struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock; IGNORE_PARAMETER(self); pico_err = PICO_ERR_NOERR; - hdr->trans.sport = t->sock.local_port; + if (f->local_port) + hdr->trans.sport = f->local_port; // Masqueraded + else + hdr->trans.sport = t->sock.local_port; hdr->trans.dport = t->sock.remote_port; hdr->seq = long_be(t->snd_last + 1); hdr->len = (uint8_t)((f->payload - f->transport_hdr) << 2u | (int8_t)t->jumbo); @@ -3276,6 +3290,13 @@ int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value) return 0; } +int pico_tcp_get_bufspace_out(struct pico_socket *s, uint32_t *value) +{ + struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; + *value = t->tcpq_out.max_size - t->tcpq_out.size; + return 0; +} + int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value) { struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; diff --git a/core/deps/picotcp/modules/pico_tcp.h b/core/deps/picotcp/modules/pico_tcp.h index 784a6bd43..e1f90b05e 100644 --- a/core/deps/picotcp/modules/pico_tcp.h +++ b/core/deps/picotcp/modules/pico_tcp.h @@ -95,6 +95,7 @@ void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s); int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value); int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value); int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value); +int pico_tcp_get_bufspace_out(struct pico_socket *s, uint32_t *value); int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value); int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value); int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value); diff --git a/core/deps/picotcp/modules/pico_udp.c b/core/deps/picotcp/modules/pico_udp.c index 6d3ba7270..211dfa825 100644 --- a/core/deps/picotcp/modules/pico_udp.c +++ b/core/deps/picotcp/modules/pico_udp.c @@ -107,7 +107,10 @@ static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f) /* this (fragmented) frame should contain a transport header */ if (f->transport_hdr != f->payload) { - hdr->trans.sport = f->sock->local_port; + if (f->local_port) + hdr->trans.sport = f->local_port; // Masqueraded + else + hdr->trans.sport = f->sock->local_port; if (remote_endpoint) { hdr->trans.dport = remote_endpoint->remote_port; } else { @@ -173,6 +176,8 @@ static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msgi struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr); msginfo->ttl = hdr->ttl; msginfo->tos = hdr->tos; + msginfo->local_addr.ip4 = hdr->dst; + msginfo->local_port = ((struct pico_udp_hdr *)f->transport_hdr)->trans.dport; #endif } else { #ifdef PICO_SUPPORT_IPV6 diff --git a/core/deps/picotcp/stack/pico_frame.c b/core/deps/picotcp/stack/pico_frame.c index 7aad7f3b1..ef2753bab 100644 --- a/core/deps/picotcp/stack/pico_frame.c +++ b/core/deps/picotcp/stack/pico_frame.c @@ -278,24 +278,21 @@ struct pico_frame *pico_frame_deepcopy(struct pico_frame *f) static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len) { - uint16_t *buf = (uint16_t *)data; - uint16_t *stop; - - if (len & 0x01) { - --len; + uint8_t *p = (uint8_t *)data; + uint8_t *stop = p + len; + while (p < stop) + { #ifdef PICO_BIGENDIAN - sum += (((uint8_t *)data)[len]) << 8; + sum += *p++ << 8; + if (p < stop) + sum += *p++; #else - sum += ((uint8_t *)data)[len]; + sum += *p++; + if (p < stop) + sum += *p++ << 8; #endif - } - - stop = (uint16_t *)(((uint8_t *)data) + len); - - while (buf < stop) { - sum += *buf++; - } - return sum; + } + return sum; } static inline uint16_t pico_checksum_finalize(uint32_t sum) diff --git a/core/deps/picotcp/stack/pico_socket.c b/core/deps/picotcp/stack/pico_socket.c index 8f11a3e82..4827ad862 100644 --- a/core/deps/picotcp/stack/pico_socket.c +++ b/core/deps/picotcp/stack/pico_socket.c @@ -582,11 +582,19 @@ static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, ui if (!tr) return -1; + // Try to find a socket using the local port number sp = pico_get_sockport(p->proto_number, localport); - if (!sp) { - dbg("No such port %d\n", short_be(localport)); - return -1; + if (sp) + { + if (pico_socket_transport_deliver(p, sp, f) == 0) + return 0; } + // Try to find a wildcard socket + sp = pico_get_sockport(p->proto_number, 0); + if (!sp) { + dbg("No such port %d\n", short_be(localport)); + return -1; + } return pico_socket_transport_deliver(p, sp, f); } @@ -1118,6 +1126,8 @@ static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const in if (msginfo) { f->send_ttl = (uint8_t)msginfo->ttl; f->send_tos = (uint8_t)msginfo->tos; + f->local_ip = msginfo->local_addr.ip4; + f->local_port = msginfo->local_port; } memcpy(f->payload, (const uint8_t *)buf, f->payload_len); @@ -1377,19 +1387,23 @@ int MOCKABLE pico_socket_sendto_extended(struct pico_socket *s, const void *buf, if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0) return -1; - - src = pico_socket_sendto_get_src(s, dst); - if (!src) { + if (msginfo && msginfo->local_addr.ip4.addr) + src = &msginfo->local_addr.ip4; + else + { + src = pico_socket_sendto_get_src(s, dst); + if (!src) { #ifdef PICO_SUPPORT_IPV6 - if((s->net->proto_number == PICO_PROTO_IPV6) - && msginfo && msginfo->dev - && pico_ipv6_is_multicast(((struct pico_ip6 *)dst)->addr)) - { - src = &(pico_ipv6_linklocal_get(msginfo->dev)->address); - } - else + if((s->net->proto_number == PICO_PROTO_IPV6) + && msginfo && msginfo->dev + && pico_ipv6_is_multicast(((struct pico_ip6 *)dst)->addr)) + { + src = &(pico_ipv6_linklocal_get(msginfo->dev)->address); + } + else #endif - return -1; + return -1; + } } remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port); @@ -1589,13 +1603,14 @@ int MOCKABLE pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t } /* When given port = 0, get a random high port to bind to. */ - if (*port == 0) { - *port = pico_socket_high_port(PROTO(s)); - if (*port == 0) { - pico_err = PICO_ERR_EINVAL; - return -1; - } - } +// Port 0 is used as a wildcard for "all ports" +// if (*port == 0) { +// *port = pico_socket_high_port(PROTO(s)); +// if (*port == 0) { +// pico_err = PICO_ERR_EINVAL; +// return -1; +// } +// } if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) { pico_err = PICO_ERR_EADDRINUSE; @@ -1757,33 +1772,73 @@ struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16 } if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) { - struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); - struct pico_socket *found; - uint32_t socklen = sizeof(struct pico_ip4); - /* If at this point no incoming connection socket is found, - * the accept call is valid, but no connection is established yet. - */ - pico_err = PICO_ERR_EAGAIN; - if (sp) { - struct pico_tree_node *index; - /* RB_FOREACH(found, socket_tree, &sp->socks) { */ - pico_tree_foreach(index, &sp->socks){ - found = index->keyValue; - if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { - found->parent = NULL; - pico_err = PICO_ERR_NOERR; - #ifdef PICO_SUPPORT_IPV6 - if (is_sock_ipv6(s)) - socklen = sizeof(struct pico_ip6); + /* If at this point no incoming connection socket is found, + * the accept call is valid, but no connection is established yet. + */ + struct pico_socket *found; + uint32_t socklen = sizeof(struct pico_ip4); + struct pico_sockport *sp; - #endif - memcpy(orig, &found->remote_addr, socklen); - *port = found->remote_port; - s->number_of_pending_conn--; - return found; - } + pico_err = PICO_ERR_EAGAIN; + + if (s->local_port == 0) + { + // Wildcard socket: do a full scan of the TCP table to find a new child + struct pico_tree_node *index, *indexp; + + pico_tree_foreach(indexp, &TCPTable) + { + sp = indexp->keyValue; + if (sp) + { + pico_tree_foreach(index, &sp->socks) + { + found = index->keyValue; + if (found == NULL) + continue; + + if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { + found->parent = NULL; + pico_err = PICO_ERR_NOERR; + #ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + socklen = sizeof(struct pico_ip6); + + #endif + memcpy(orig, &found->remote_addr, socklen); + *port = found->remote_port; + s->number_of_pending_conn--; + return found; + } + } + } } - } + } + else + { + // Normal search using the local port + sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); + if (sp) { + struct pico_tree_node *index; + /* RB_FOREACH(found, socket_tree, &sp->socks) { */ + pico_tree_foreach(index, &sp->socks){ + found = index->keyValue; + if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { + found->parent = NULL; + pico_err = PICO_ERR_NOERR; + #ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + socklen = sizeof(struct pico_ip6); + + #endif + memcpy(orig, &found->remote_addr, socklen); + *port = found->remote_port; + s->number_of_pending_conn--; + return found; + } + } + } + } } return NULL; diff --git a/core/hw/modem/dns.cpp b/core/hw/modem/dns.cpp new file mode 100644 index 000000000..c7bd563ef --- /dev/null +++ b/core/hw/modem/dns.cpp @@ -0,0 +1,151 @@ +/* + Created on: Sep 24, 2018 + + Copyright 2018 flyinghead + + This file is part of reicast. + + reicast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + reicast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with reicast. If not, see . + */ + +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +extern "C" { +#include +#include +#include +} + +void get_host_by_name(const char *name, struct pico_ip4 dnsaddr); +int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr); +char *read_name(char *reader, char *buffer, int *count); +void set_non_blocking(int fd); + +static int sock_fd = -1; +static unsigned short qid = PICO_TIME_MS(); +static int qname_len; + +void get_host_by_name(const char *host, struct pico_ip4 dnsaddr) +{ + if (sock_fd < 0) + { + sock_fd = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); + set_non_blocking(sock_fd); + } + + struct sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = htons(53); + dest.sin_addr.s_addr = dnsaddr.addr; + + // DNS Packet header + char buf[1024]; + pico_dns_packet *dns = (pico_dns_packet *)&buf; + + dns->id = qid++; + dns->qr = PICO_DNS_QR_QUERY; + dns->opcode = PICO_DNS_OPCODE_QUERY; + dns->aa = PICO_DNS_AA_NO_AUTHORITY; + dns->tc = PICO_DNS_TC_NO_TRUNCATION; + dns->rd = PICO_DNS_RD_IS_DESIRED; + dns->ra = PICO_DNS_RA_NO_SUPPORT; + dns->z = 0; + dns->rcode = PICO_DNS_RCODE_NO_ERROR; + dns->qdcount = htons(1); // One question + dns->ancount = 0; + dns->nscount = 0; + dns->arcount = 0; + + char *qname = &buf[sizeof(pico_dns_packet)]; + + strcpy(qname + 1, host); + pico_dns_name_to_dns_notation(qname, 128); + qname_len = strlen(qname) + 1; + + struct pico_dns_question_suffix *qinfo = (struct pico_dns_question_suffix *) &buf[sizeof(pico_dns_packet) + qname_len]; //fill it + qinfo->qtype = htons(PICO_DNS_TYPE_A); // Address record + qinfo->qclass = htons(PICO_DNS_CLASS_IN); + + if (sendto(sock_fd, buf, sizeof(pico_dns_packet) + qname_len + sizeof(struct pico_dns_question_suffix), 0, (struct sockaddr *)&dest, sizeof(dest)) < 0) + perror("DNS sendto failed"); +} + +int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr) +{ + struct sockaddr_in peer; + socklen_t peer_len = sizeof(peer); + char buf[1024]; + + int r = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&peer , &peer_len); + if (r < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + perror("DNS recvfrom failed"); + return -1; + } + if (peer.sin_addr.s_addr != dnsaddr.addr) + return -1; + + pico_dns_packet *dns = (pico_dns_packet*) buf; + + // move to the first answer + char *reader = &buf[sizeof(pico_dns_packet) + qname_len + sizeof(struct pico_dns_question_suffix)]; + + int stop = 0; + + for (int i = 0; i < ntohs(dns->ancount); i++) + { + // FIXME Check name? + free(read_name(reader, buf, &stop)); + reader = reader + stop; + + struct pico_dns_record_suffix *record = (struct pico_dns_record_suffix *)reader; + reader = reader + sizeof(struct pico_dns_record_suffix); + + if (ntohs(record->rtype) == PICO_DNS_TYPE_A) // Address record + { + memcpy(&address->addr, reader, 4); + + return 0; + } + reader = reader + ntohs(record->rdlength); + } + return -1; +} + +char *read_name(char *reader, char *buffer, int *count) +{ + char *name = (char *)malloc(128); + if ((uint8_t)reader[0] & 0xC0) + { + int offset = (((uint8_t)reader[0] & ~0xC0) << 8) + (uint8_t)reader[1]; + reader = &buffer[offset]; + *count = 2; + } + else + { + *count = strlen(reader) + 1; + } + pico_dns_notation_to_name(reader, 128); + strcpy(name, reader + 1); + + return name; +} diff --git a/core/hw/modem/modem.cpp b/core/hw/modem/modem.cpp index 9022e20de..690a76d42 100644 --- a/core/hw/modem/modem.cpp +++ b/core/hw/modem/modem.cpp @@ -142,7 +142,9 @@ static int modem_sched_func(int tag, int c, int j) { if (last_comm_stats != 0) { - printf("Stats sent %d received %d TDBE %d RDBF %d\n", sent_bytes, recvd_bytes, modem_regs.reg1e.TDBE, modem_regs.reg1e.RDBF); + printf("Stats sent %d (%.2f kB/s) received %d (%.2f kB/s) TDBE %d RDBF %d\n", sent_bytes, sent_bytes / 2000.0, + recvd_bytes, recvd_bytes / 2000.0, + modem_regs.reg1e.TDBE, modem_regs.reg1e.RDBF); sent_bytes = 0; recvd_bytes = 0; } @@ -315,6 +317,7 @@ static int modem_sched_func(int tag, int c, int j) static void schedule_callback(int ms) { if (modem_sched == 0) + // FIXME would break save state -> relies on all schedule to be registered at init modem_sched = sh4_sched_register(0, &modem_sched_func); sh4_sched_request(modem_sched, SH4_MAIN_CLOCK / 1000 * ms); } diff --git a/core/hw/modem/picoppp.cpp b/core/hw/modem/picoppp.cpp index db835b613..36e8a5c4d 100644 --- a/core/hw/modem/picoppp.cpp +++ b/core/hw/modem/picoppp.cpp @@ -1,51 +1,131 @@ +/* + Created on: Sep 15, 2018 + + Copyright 2018 flyinghead + + This file is part of reicast. + + reicast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + reicast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with reicast. If not, see . + */ + #ifndef _MSC_VER #include +#include extern "C" { #include #include -#ifdef _WIN32 -#include -#else -#include -#endif -#include -#include -#ifdef DHCP -#include -#endif +#include +#include +#include +#include } -#ifdef _WIN32 -#include -#define NETWORK_TAP -#else -#define NETWORK_TUN +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#include +#include +#include #endif #include "types.h" #include "cfg/cfg.h" #include "picoppp.h" +#ifndef _WIN32 +#define closesocket close +#endif + +#define RESOLVER1_OPENDNS_COM "208.67.222.222" +#define AFO_ORIG_IP 0x83f2fb3f // 63.251.242.131 in network order + static struct pico_device *ppp; -struct pico_device* tap; -struct pico_device* tun; -u8 virtual_mac[] = { 0x76, 0x6D, 0x61, 0x63, 0x30, 0x31 }; static std::queue in_buffer; static std::queue out_buffer; +static cMutex in_buffer_lock; +static cMutex out_buffer_lock; + +struct pico_ip4 dcaddr; +struct pico_ip4 dnsaddr; +static struct pico_socket *pico_tcp_socket, *pico_udp_socket; + +struct pico_ip4 public_ip; +struct pico_ip4 afo_ip; + +// src socket -> socket fd +static map tcp_sockets; +// src port -> socket fd +static map udp_sockets; + +static const uint16_t games_udp_ports[] = { + 7980, // Alien Front Online + 9789, // ChuChu Rocket + // NBA/NFL/NCAA 2K Series + 5502, + 5503, + 5656, + 3512, // The Next Tetris + 6001, // Ooga Booga + // PBA Tour Bowling 2001, Starlancer + // 2300-2400, ? + 6500, + 13139, + // Planet Ring + 7648, + 1285, + 1028, + // World Series Baseball 2K2 + 37171, + 13713, +}; +static const uint16_t games_tcp_ports[] = { + // NBA/NFL/NCAA 2K Series + 5011, + 6666, + 3512, // The Next Tetris + // PBA Tour Bowling 2001, Starlancer + // 2300-2400, ? + 47624, + 17219, // Worms World Party +}; +// listening port -> socket fd +static map tcp_listening_sockets; + +static void read_native_sockets(); +void get_host_by_name(const char *name, struct pico_ip4 dnsaddr); +int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr); + static int modem_read(struct pico_device *dev, void *data, int len) { u8 *p = (u8 *)data; int count = 0; + out_buffer_lock.Lock(); while (!out_buffer.empty() && count < len) { *p++ = out_buffer.front(); out_buffer.pop(); count++; } + out_buffer_lock.Unlock(); return count; } @@ -54,73 +134,469 @@ static int modem_write(struct pico_device *dev, const void *data, int len) { u8 *p = (u8 *)data; + in_buffer_lock.Lock(); while (len > 0) { in_buffer.push(*p++); len--; } + in_buffer_lock.Unlock(); return len; } void write_pico(u8 b) { + out_buffer_lock.Lock(); out_buffer.push(b); + out_buffer_lock.Unlock(); } int read_pico() { - pico_stack_tick(); - + in_buffer_lock.Lock(); if (in_buffer.empty()) + { + in_buffer_lock.Unlock(); return -1; + } else { u32 b = in_buffer.front(); in_buffer.pop(); + in_buffer_lock.Unlock(); return b; } } +void set_non_blocking(int fd) +{ +#ifndef _WIN32 + fcntl(fd, F_SETFL, O_NONBLOCK); +#else + uint32_t optl = 1; + ioctlsocket(fd, FIONBIO, &optl); +#endif +} + +static void tcp_callback(uint16_t ev, struct pico_socket *s) +{ + int r = 0; + + if (ev & PICO_SOCK_EV_RD) + { + auto it = tcp_sockets.find(s); + if (it == tcp_sockets.end()) + { + printf("Unknown socket: remote port %d\n", s->remote_port); + } + else + { + char buf[1510]; + + r = pico_socket_read(it->first, buf, sizeof(buf)); + if (r > 0) { + if (write(it->second, buf, r) < r) + perror("tcp_callback write"); + } + } + } + + if (ev & PICO_SOCK_EV_CONN) + { + uint32_t ka_val = 0; + struct pico_ip4 orig; + uint16_t port; + char peer[30]; + int yes = 1; + + struct pico_socket *sock_a = pico_socket_accept(s, &orig, &port); + if (sock_a == NULL) + { + printf("pico_socket_accept: %s\n", strerror(pico_err)); + } + else + { + pico_ipv4_to_string(peer, orig.addr); + printf("Connection established with %s:%d.\n", peer, short_be(port)); + pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes); + /* Set keepalive options */ + // ka_val = 5; + // pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPCNT, &ka_val); + // ka_val = 30000; + // pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPIDLE, &ka_val); + // ka_val = 5000; + // pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPINTVL, &ka_val); + + int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sockfd < 0) + { + perror("socket"); + } + else + { + struct sockaddr_in serveraddr; + memset(&serveraddr, 0, sizeof(serveraddr)); + serveraddr.sin_family = AF_INET; + serveraddr.sin_addr.s_addr = sock_a->local_addr.ip4.addr; + if (serveraddr.sin_addr.s_addr == AFO_ORIG_IP) // Alien Front Online + serveraddr.sin_addr.s_addr = afo_ip.addr; + + serveraddr.sin_port = sock_a->local_port; + if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) + { + pico_ipv4_to_string(peer, sock_a->local_addr.ip4.addr); + printf("TCP connection to %s:%d failed: ", peer, sock_a->local_port); + perror(NULL); + closesocket(sockfd); + } + else + { + set_non_blocking(sockfd); + + int optval = 1; + socklen_t optlen = sizeof(optval); +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) + setsockopt(sockfd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen); +#else + struct protoent *tcp_proto = getprotobyname("TCP"); + setsockopt(sockfd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen); +#endif + tcp_sockets[sock_a] = sockfd; + } + } + } + } + + if (ev & PICO_SOCK_EV_FIN) { + //printf("Socket closed. Exit normally. \n"); + auto it = tcp_sockets.find(s); + if (it == tcp_sockets.end()) + { + printf("PICO_SOCK_EV_FIN: Unknown socket: remote port %d\n", s->remote_port); + } + else + { + closesocket(it->second); + pico_socket_close(s); + tcp_sockets.erase(s); + } + } + + if (ev & PICO_SOCK_EV_ERR) { + printf("Socket error received: %s. Bailing out.\n", strerror(pico_err)); + auto it = tcp_sockets.find(s); + if (it == tcp_sockets.end()) + { + printf("PICO_SOCK_EV_ERR: Unknown socket: remote port %d\n", s->remote_port); + } + else + { + closesocket(it->second); + pico_socket_close(s); + tcp_sockets.erase(s); + } + } + +// if (ev & PICO_SOCK_EV_CLOSE) +// { +// } + +// if (ev & PICO_SOCK_EV_WR) +// { +// } +} + +static int find_udp_socket(uint16_t src_port) +{ + auto it = udp_sockets.find(src_port); + if (it != udp_sockets.end()) + return it->second; + + int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sockfd < 0) + { + perror("socket"); + return -1; + } +#ifndef _WIN32 + fcntl(sockfd, F_SETFL, O_NONBLOCK); +#else + uint32_t optl = 1; + ioctlsocket(sockfd, FIONBIO, &optl); +#endif + + // FIXME Need to clean up at some point? + udp_sockets[src_port] = sockfd; + + return sockfd; +} + +static void udp_callback(uint16_t ev, struct pico_socket *s) +{ + if (ev & PICO_SOCK_EV_RD) + { + char buf[1510]; + struct pico_ip4 src_addr; + uint16_t src_port; + pico_msginfo msginfo; + int r = 0; + while (true) + { + r = pico_socket_recvfrom_extended(s, buf, sizeof(buf), &src_addr.addr, &src_port, &msginfo); + + if (r <= 0) + { + if (r < 0) + printf("%s: error UDP recv: %s\n", __FUNCTION__, strerror(pico_err)); + break; + } + + int sockfd = find_udp_socket(src_port); + if (sockfd >= 0) + { + struct sockaddr_in dst_addr; + socklen_t addr_len = sizeof(dst_addr); + memset(&dst_addr, 0, sizeof(dst_addr)); + dst_addr.sin_family = AF_INET; + dst_addr.sin_addr.s_addr = msginfo.local_addr.ip4.addr; + dst_addr.sin_port = msginfo.local_port; + if (sendto(sockfd, buf, r, 0, (const struct sockaddr *)&dst_addr, addr_len) < 0) + perror("sendto udp socket"); + } + } + } + + if (ev & PICO_SOCK_EV_ERR) { + printf("UDP Callback error received\n"); + } +} + +static void read_native_sockets() +{ + int r; + struct sockaddr_in src_addr; + socklen_t addr_len; + + // Accept incoming TCP connections + for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++) + { + addr_len = sizeof(src_addr); + memset(&src_addr, 0, addr_len); + int sockfd = accept(it->second, (struct sockaddr *)&src_addr, &addr_len); + if (sockfd < 0) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + perror("accept"); + continue; + } + printf("Incoming TCP connection from %08x to port %d\n", src_addr.sin_addr.s_addr, short_be(it->first)); + struct pico_socket *ps = pico_socket_tcp_open(PICO_PROTO_IPV4); + if (ps == NULL) + { + printf("pico_socket_tcp_open failed: error %d\n", pico_err); + closesocket(sockfd); + continue; + } + ps->local_addr.ip4.addr = src_addr.sin_addr.s_addr; + ps->local_port = src_addr.sin_port; + if (pico_socket_connect(ps, &dcaddr.addr, it->first) != 0) + { + printf("pico_socket_connect failed: error %d\n", pico_err); + closesocket(sockfd); + pico_socket_del(ps); + continue; + } + tcp_sockets[ps] = sockfd; + } + + char buf[1500]; // FIXME MTU ? + struct pico_msginfo msginfo; + + // If modem buffer is full, wait + in_buffer_lock.Lock(); + size_t in_buffer_size = in_buffer.size(); + in_buffer_lock.Unlock(); + if (in_buffer_size >= 256) + return; + + // Read UDP sockets + for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++) + { + addr_len = sizeof(src_addr); + memset(&src_addr, 0, addr_len); + r = recvfrom(it->second, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &addr_len); + if (r > 0) + { + msginfo.dev = ppp; + msginfo.tos = 0; + msginfo.ttl = 0; + msginfo.local_addr.ip4.addr = src_addr.sin_addr.s_addr; + msginfo.local_port = src_addr.sin_port; +// printf("read_native_sockets UCP received %d bytes from %08x:%d\n", r, long_be(msginfo.local_addr.ip4.addr), short_be(msginfo.local_port)); + int r2 = pico_socket_sendto_extended(pico_udp_socket, buf, r, &dcaddr, it->first, &msginfo); + if (r2 < r) + printf("%s: error UDP sending to %d: %s\n", __FUNCTION__, short_be(it->first), strerror(pico_err)); + } + else if (r < 0 && errno != EAGAIN) + { + perror("read udp socket"); + continue; + } + } + + // Read TCP sockets + for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++) + { + uint32_t space; + pico_tcp_get_bufspace_out(it->first, &space); + if (space < sizeof(buf)) + { + // Wait for the out buffer to empty a bit + continue; + } + r = read(it->second, buf, sizeof(buf)); + if (r > 0) + { +// printf("read_native_sockets TCP received %d bytes\n", r); + int r2 = pico_socket_send(it->first, buf, r); + if (r2 < 0) + printf("%s: error TCP sending: %s\n", __FUNCTION__, strerror(pico_err)); + else if (r2 < r) + // FIXME EAGAIN errors. Need to buffer data or wait for call back. + printf("%s: truncated send: %d -> %d\n", __FUNCTION__, r, r2); + } + else if (r < 0 && errno != EAGAIN) + { + perror("read tcp socket"); + closesocket(it->second); + pico_socket_close(it->first); + tcp_sockets.erase(it); + continue; + } + } +} + +void close_native_sockets() +{ + for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++) + closesocket(it->second); + udp_sockets.clear(); + for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++) + { + pico_socket_close(it->first); + closesocket(it->second); + } + tcp_sockets.clear(); +} + static int modem_set_speed(struct pico_device *dev, uint32_t speed) { return 0; } -#ifdef DHCP -static uint32_t dhcpclient_xid; -static struct pico_ip4 dchp_address = { 0 }; - -void callback_dhcpclient(void *arg, int code) +#ifdef _WIN32 +static void usleep(unsigned int usec) { - char s_address[16] = { }, s_gateway[16] = { }; + HANDLE timer; + LARGE_INTEGER ft; - printf("DHCP client: callback happened with code %d!\n", code); - if (code == PICO_DHCP_SUCCESS) - { - dchp_address = pico_dhcp_get_address(arg); - //gateway = pico_dhcp_get_gateway(arg); - pico_ipv4_to_string(s_address, dchp_address.addr); - //pico_ipv4_to_string(s_gateway, gateway.addr); - printf("DHCP client: got IP %s assigned with cli %p\n", s_address, arg); - } + ft.QuadPart = -(10 * (__int64)usec); + + timer = CreateWaitableTimer(NULL, TRUE, NULL); + SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); + CloseHandle(timer); } #endif -static bool pico_stack_inited; - -bool start_pico() +static void check_dns_entries() { - struct pico_ip4 ipaddr, dcaddr, dnsaddr, netmask, zero = { + static uint32_t dns_query_start = 0; + static uint32_t dns_query_attempts = 0; + + + if (public_ip.addr == 0) + { + if (!dns_query_start) + { + dns_query_start = PICO_TIME_MS(); + struct pico_ip4 tmpdns; + pico_string_to_ipv4(RESOLVER1_OPENDNS_COM, &tmpdns.addr); + get_host_by_name("myip.opendns.com", tmpdns); + } + else + { + struct pico_ip4 tmpdns; + pico_string_to_ipv4(RESOLVER1_OPENDNS_COM, &tmpdns.addr); + if (get_dns_answer(&public_ip, tmpdns) == 0) + { + dns_query_attempts = 0; + dns_query_start = 0; + char myip[16]; + pico_ipv4_to_string(myip, public_ip.addr); + printf("My IP is %s\n", myip); + } + else + { + if (PICO_TIME_MS() - dns_query_start > 1000) + { + if (++dns_query_attempts >= 5) + { + public_ip.addr = 0xffffffff; // Bogus but not null + dns_query_attempts = 0; + } + else + // Retry + dns_query_start = 0; + } + } + } + } + else if (afo_ip.addr == 0) + { + if (!dns_query_start) + { + dns_query_start = PICO_TIME_MS(); + get_host_by_name("auriga.segasoft.com", dnsaddr); // Alien Front Online server + } + else + { + if (get_dns_answer(&afo_ip, dnsaddr) == 0) + { + dns_query_attempts = 0; + dns_query_start = 0; + char afoip[16]; + pico_ipv4_to_string(afoip, afo_ip.addr); + printf("AFO server IP is %s\n", afoip); + } + else + { + if (PICO_TIME_MS() - dns_query_start > 1000) + { + if (++dns_query_attempts >= 5) + { + pico_string_to_ipv4("146.185.135.179", &afo_ip.addr); // Default address + dns_query_attempts = 0; + } + else + // Retry + dns_query_start = 0; + } + } + } + } +} + +static bool pico_stack_inited; +static bool pico_thread_running = false; + +static void *pico_thread_func(void *) +{ + struct pico_ip4 ipaddr, netmask, zero = { 0 }; -#ifdef _WIN32 - // No de-init on Windows yet - if (pico_stack_inited) - return; -#endif - if (!pico_stack_inited) { pico_stack_init(); @@ -130,210 +606,121 @@ bool start_pico() // PPP ppp = pico_ppp_create(); if (!ppp) - return false; - string dc_ip = cfgLoadStr("network", "IP", ""); - if (dc_ip.length() == 0) - { - printf("No IP address set for Netplay. Set IP= in the [network] section\n"); - return false; - } - pico_string_to_ipv4(dc_ip.c_str(), &dcaddr.addr); + return NULL; + pico_string_to_ipv4("192.168.167.2", &dcaddr.addr); pico_ppp_set_peer_ip(ppp, dcaddr); pico_string_to_ipv4("192.168.167.1", &ipaddr.addr); pico_ppp_set_ip(ppp, ipaddr); - string dns_ip = cfgLoadStr("network", "DNS", "46.101.91.123"); + string dns_ip = cfgLoadStr("network", "DNS", "46.101.91.123"); // Dreamcast Live DNS pico_string_to_ipv4(dns_ip.c_str(), &dnsaddr.addr); pico_ppp_set_dns1(ppp, dnsaddr); -#ifdef NETWORK_TAP - // TAP - // tap config: - // # ip tuntap add mode tap user joe - // # ifconfig tap0 192.168.5.5 - // # ip link set tap0 up - // # ip route add /32 dev tap0 # where is the value of network:IP in emu.cfg./. This also allows proxy arp - // # echo '1' >/proc/sys/net/ipv4/conf/all/proxy_arp - // (or ...conf/tap0/proxy_arp and ...conf/eth0/proxy_arp only) -#ifdef _WIN32 - tap = pico_tap_create("tap0", virtual_mac); -#else - tap = pico_tap_create("tap0"); -#endif - if (!tap) - { - stop_pico(); - return false; + pico_udp_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &udp_callback); + if (pico_udp_socket == NULL) { + printf("%s: error opening UDP socket: %s\n", __FUNCTION__, strerror(pico_err)); } + int yes = 1; + struct pico_ip4 inaddr_any = {0}; + uint16_t listen_port = 0; + int ret = pico_socket_bind(pico_udp_socket, &inaddr_any, &listen_port); + if (ret < 0) + printf("%s: error binding UDP socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err)); - pico_string_to_ipv4("192.168.166.2", &ipaddr.addr); - pico_string_to_ipv4("255.255.255.0", &netmask.addr); - pico_ipv4_link_add(tap, ipaddr, netmask); - // Proxy ARP - pico_arp_create_entry(tap->eth->mac.addr, ipaddr, ppp); - -#ifdef _WIN32 - int err; - - // Enable routing - OVERLAPPED overlapped; - memset(&overlapped, 0, sizeof(overlapped)); - overlapped.hEvent = overlapped.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); - if (overlapped.hEvent == NULL) - printf("CreateEvent failed with error %d\n", GetLastError()); + pico_tcp_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &tcp_callback); + if (pico_tcp_socket == NULL) { + printf("%s: error opening TCP socket: %s\n", __FUNCTION__, strerror(pico_err)); + } + pico_socket_setoption(pico_tcp_socket, PICO_TCP_NODELAY, &yes); + ret = pico_socket_bind(pico_tcp_socket, &inaddr_any, &listen_port); + if (ret < 0) { + printf("%s: error binding TCP socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err)); + } else { - HANDLE handle; - err = EnableRouter(&handle, &overlapped); - if (err != ERROR_IO_PENDING) - printf("EnableRouter failed with error %d\n", err); - else - printf("Windows router enabled\n"); - CloseHandle(overlapped.hEvent); + if (pico_socket_listen(pico_tcp_socket, 10) != 0) + printf("%s: error listening on port %u\n", __FUNCTION__, short_be(listen_port)); } + ppp->proxied = 1; - // Get the LAN interface index - DWORD idx = -1; - err = GetBestInterface(dnsaddr.addr, &idx); - if (err != NO_ERROR) - printf("GetBestInterface failed error %d\n", err); - - // Create a Proxy ARP entry for the DC on the local LAN - if (idx != -1) + struct sockaddr_in saddr; + socklen_t saddr_len = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = INADDR_ANY; + for (int i = 0; i < sizeof(games_udp_ports) / sizeof(uint16_t); i++) { - err = CreateProxyArpEntry(dcaddr.addr, 0xffffffff, idx); - if (err == ERROR_OBJECT_ALREADY_EXISTS) - printf("Proxy ARP entry already exists\n"); - else if (err != NO_ERROR) - printf("CreateProxyArpEntry failed error %d\n", err); + uint16_t port = short_be(games_udp_ports[i]); + int sockfd = find_udp_socket(port); + saddr.sin_port = port; + + bind(sockfd, (struct sockaddr *)&saddr, saddr_len); } - // Get the TAP interface index - unsigned long size = sizeof(IP_INTERFACE_INFO); - IP_INTERFACE_INFO *infos = (IP_INTERFACE_INFO *)malloc(size); - err = GetInterfaceInfo(infos, &size); - if (err == ERROR_INSUFFICIENT_BUFFER) - { - free(infos); - infos = (IP_INTERFACE_INFO *)malloc(size); - err = GetInterfaceInfo(infos, &size); - if (err != NO_ERROR) - { - printf("GetInterfaceInfo failed error %d\n", err); - infos->NumAdapters = 0; - } - } - - const char *tap_guid = pico_tap_get_guid(tap); - wchar_t wtap_guid[40]; - MultiByteToWideChar(CP_UTF8, 0, tap_guid, strlen(tap_guid), &wtap_guid[0], 40); - DWORD tap_idx = -1; // 11; - for (int i = 0; i < infos->NumAdapters; i++) - { - printf("Found interface %ls index %d\n", infos->Adapter[i].Name, infos->Adapter[i].Index); - if (wcsstr(infos->Adapter[i].Name, wtap_guid) != NULL) - { - tap_idx = infos->Adapter[i].Index; - break; - } - } - free(infos); - - // Set the TAP interface IP address - pico_string_to_ipv4("192.168.166.1", &ipaddr.addr); - pico_string_to_ipv4("255.255.255.0", &netmask.addr); - unsigned long nte_context, nte_instance; - err = AddIPAddress(ipaddr.addr, netmask.addr, tap_idx, &nte_context, &nte_instance); - if (err == ERROR_OBJECT_ALREADY_EXISTS) - printf("TAP IP address already set\n"); - else if (err != NO_ERROR) - printf("AddIpAddress failed with error %d\n", err); - else - printf("TAP IP address set\n"); - - // Create a route to the DC through the TAP interface - if (tap_idx != -1) - { - MIB_IPFORWARDROW fwd; - memset(&fwd, 0, sizeof(fwd)); - fwd.dwForwardDest = dcaddr.addr; - fwd.dwForwardMask = 0xffffffff; - fwd.dwForwardIfIndex = tap_idx; - fwd.dwForwardProto = MIB_IPPROTO_NETMGMT; - fwd.dwForwardAge = INFINITE; - fwd.dwForwardMetric1 = 500; - err = CreateIpForwardEntry(&fwd); - if (err == ERROR_OBJECT_ALREADY_EXISTS) - printf("IP forward entry already exists\n"); - else if (err != NO_ERROR) - printf("CreateIpForwardEntry failed with error %d\n", err); - else - printf("IP forward entry created\n"); - } - -#endif -#endif - -#ifdef NETWORK_TUN - // TUN - // tun config: - // # ip tuntap add mode tun user joe - // # ip link set tun0 up - // # ip route add /32 dev tun0 # where is the value of network:IP in emu.cfg./. This also allows proxy arp - // # echo '1' >/proc/sys/net/ipv4/conf/all/proxy_arp - // (or ...conf/tun0/proxy_arp and ...conf/eth0/proxy_arp only) - tun = pico_tun_create("tun0"); - if (!tun) + for (int i = 0; i < sizeof(games_tcp_ports) / sizeof(uint16_t); i++) { - stop_pico(); - return false; + uint16_t port = short_be(games_tcp_ports[i]); + saddr.sin_port = port; + int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (::bind(sockfd, (struct sockaddr *)&saddr, saddr_len) < 0) + { + perror("bind"); + close(sockfd); + continue; + } + if (listen(sockfd, 5) < 0) + { + perror("listen"); + close(sockfd); + continue; + } + set_non_blocking(sockfd); + tcp_listening_sockets[port] = sockfd; } - pico_string_to_ipv4("192.168.166.1", &ipaddr.addr); - pico_string_to_ipv4("255.255.255.255", &netmask.addr); - pico_ipv4_link_add(tun, ipaddr, netmask); -#endif - - // Default route - pico_string_to_ipv4("192.168.166.1", &ipaddr.addr); - pico_ipv4_route_add(zero, zero, ipaddr, 1, NULL); - pico_ppp_set_serial_read(ppp, modem_read); pico_ppp_set_serial_write(ppp, modem_write); pico_ppp_set_serial_set_speed(ppp, modem_set_speed); pico_ppp_connect(ppp); -#ifdef DHCP - if (pico_dhcp_initiate_negotiation(tap, &callback_dhcpclient, &dhcpclient_xid) < 0) + while (pico_thread_running) { - printf("%s: error initiating negotiation: %s\n", __FUNCTION__, strerror(pico_err)); - return false; + read_native_sockets(); + pico_stack_tick(); + check_dns_entries(); + usleep(1000); } -#endif + + for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++) + closesocket(it->second); + close_native_sockets(); + pico_socket_close(pico_tcp_socket); + pico_socket_close(pico_udp_socket); + + if (ppp) + { + pico_ppp_destroy(ppp); + ppp = NULL; + } + + return NULL; +} + +static cThread pico_thread(pico_thread_func, NULL); + +bool start_pico() +{ + pico_thread_running = true; + pico_thread.Start(); return true; } void stop_pico() { -#ifndef _WIN32 - if (ppp) - { - pico_ppp_destroy(ppp); - ppp = NULL; - } - if (tap) - { - pico_device_destroy(tap); - tap = NULL; - } - if (tun) - { - pico_device_destroy(tun); - tun = NULL; - } -#endif + pico_thread_running = false; + pico_thread.WaitToEnd(); } #else diff --git a/core/hw/modem/picoppp.h b/core/hw/modem/picoppp.h index b2d6ac5d2..43220fa3f 100644 --- a/core/hw/modem/picoppp.h +++ b/core/hw/modem/picoppp.h @@ -1,3 +1,24 @@ +/* + Created on: Sep 15, 2018 + + Copyright 2018 flyinghead + + This file is part of reicast. + + reicast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + reicast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with reicast. If not, see . + */ + bool start_pico(); void stop_pico(); void write_pico(u8 b); diff --git a/core/hw/modem/pppd.cpp b/core/hw/modem/pppd.cpp deleted file mode 100644 index 31afb279f..000000000 --- a/core/hw/modem/pppd.cpp +++ /dev/null @@ -1,257 +0,0 @@ -/* - pppd.cpp - - Created on: Sep 10, 2018 - - Copyright 2018 flyinghead - - This file is part of reicast. - - reicast is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - reicast is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with reicast. If not, see . - */ -#include "types.h" - -#if HOST_OS == OS_LINUX - -#include -#include -#include -#include -#include -#include -#include -#include "oslib/oslib.h" -#include "pppd.h" - -static int pppd_pid; -static int inpipe = -1; -static int outpipe = -1; - -static u8 in_buffer[128]; -static int in_bufsize; -static int in_bufindex; -static bool v42_negotiate = false; -static u32 v42_odp_idx; -static u32 v42_odp_count; -const u8 v42_odp[] { 0x45, 0xfc, 0x17, 0xf9, 0x5f, 0xc4, 0x7f, 0x91, 0xff }; -double last_adp_sent; - -void start_pppd() -{ - int inpipefd[2]; - int outpipefd[2]; - - if (pipe(inpipefd) || pipe(outpipefd)) - { - perror("pipe"); - return; - } - pppd_pid = fork(); - if (pppd_pid < 0) - { - perror("fork"); - close(inpipefd[0]); - close(inpipefd[1]); - close(outpipefd[0]); - close(outpipefd[1]); - return; - } - - if (pppd_pid == 0) - { - // Child - close(inpipefd[0]); - dup2(inpipefd[1], 1); - close(outpipefd[1]); - dup2(outpipefd[0], 0); - execl("/usr/sbin/pppd", "pppd", "notty", NULL); - perror("execl"); - exit(1); - } - // Parent - inpipe = inpipefd[0]; - outpipe = outpipefd[1]; - fcntl(inpipe, F_SETFL, fcntl(inpipe, F_GETFL) | O_NONBLOCK); - in_bufindex = 0; - in_bufsize = 1; - in_buffer[0] = 0; -} - -void stop_pppd() -{ - if (pppd_pid > 0) - { - kill(SIGTERM, pppd_pid); - // FIXME wait / waitpid - pppd_pid = 0; - } - if (inpipe >= 0) - { - close(inpipe); - inpipe = -1; - } - if (outpipe >= 0) - { - close(outpipe); - outpipe = -1; - } -} - -static u8 reverse_bits(u8 x) -{ - x = ((x * 0x0802 & 0x22110) | (x * 0x8020 & 0x88440)) * 0x10101 >> 16; - - return x & 0xFF; -} - -static void send_v42_adp() -{ - in_bufsize = 0; - in_bufindex = 0; - - // V.42 disabled E, 8-16 ones, NULL, 8-16 ones // FIXME leading one is a test - const std::string adp_nov42("1" "0101000101" "11111111" "0000000001" "11111111"); - const std::string adp_v42("1" "0101000101" "11111111" "0110000101" "11111111"); - const std::string& adp = adp_nov42; - const std::string ones("1111111111111111"); - - int adp_idx = 0; - for (int i = 0; i < 10; ) - { - int end = min(adp_idx + 8, (int)adp.length()); - std::string binary = adp.substr(adp_idx, end - adp_idx); - adp_idx += 8; - - if (adp_idx > end) - { - adp_idx -= adp.length(); - if (i == 9) - binary += ones.substr(0, adp_idx); - else - binary += adp.substr(0, adp_idx); - i++; - } - else - { - if (adp_idx >= adp.length()) - { - adp_idx = 0; - i++; - } - } - std::bitset<8> bs(binary); - in_buffer[in_bufsize++] = reverse_bits(bs.to_ulong()); - if (in_bufsize == sizeof(in_buffer)) - { - printf("PPPD in buffer overflow\n"); - return; - } - } - v42_negotiate = false; -} - -void write_pppd(u8 b) -{ - if (v42_negotiate) - { - if (b == v42_odp[v42_odp_idx]) - { - v42_odp_idx++; - if (v42_odp_idx >= sizeof(v42_odp)) - { - v42_odp_count++; - if (v42_odp_count >= 2) - { - if (last_adp_sent == 0 || os_GetSeconds() - last_adp_sent > 0.75) // T400 - { - printf("PPPD V42 2 ODP received. Sending ADP\n"); - last_adp_sent = os_GetSeconds(); - send_v42_adp(); - } - v42_odp_count = 0; - } - v42_odp_idx = 0; - } - } - else - { - v42_odp_idx = 0; - printf("PPPD ignored byte %02x\n", b); - } - return; - } - int rc = write(outpipe, &b, 1); - if (rc < 0) - perror("write outpipe"); - else if (rc == 0) - printf("pppd EOF on outpipe\n"); -} - -int read_pppd() -{ - if (in_bufindex == in_bufsize) - { - in_bufindex = 0; - in_bufsize = 0; - if (v42_negotiate) - return -1; - - int rc = read(inpipe, in_buffer, sizeof(in_buffer)); - if (rc < 0) - { - if (errno != EWOULDBLOCK) - perror("read inpipe"); - return rc; - } - else if (rc == 0) - { - printf("pppd EOF on inpipe\n"); - return -1; - } - in_bufsize = rc; - in_bufindex = 0; - } - return in_buffer[in_bufindex++]; -} - -int avail_pppd() -{ - return in_bufsize - in_bufindex; -} - -#else // not Linux - -void start_pppd() -{ -} - -void stop_pppd() -{ -} - -void write_pppd(u8 b) -{ -} - -int read_pppd() -{ - return -1; -} - -int avail_pppd() { - return 0; -} - -#endif - diff --git a/core/hw/modem/pppd.h b/core/hw/modem/pppd.h deleted file mode 100644 index 83f49dc51..000000000 --- a/core/hw/modem/pppd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - pppd.h - - Created on: Sep 10, 2018 - - Copyright 2018 flyinghead - - This file is part of reicast. - - reicast is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - reicast is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with reicast. If not, see . - */ - -#ifndef CORE_HW_MODEM_PPPD_H_ -#define CORE_HW_MODEM_PPPD_H_ -#include "types.h" - -void start_pppd(); -void stop_pppd(); -void write_pppd(u8 b); -int read_pppd(); -int avail_pppd(); - -#endif /* CORE_HW_MODEM_PPPD_H_ */