diff --git a/Config.mk b/Config.mk index 6e4eb2f..b0cca67 100644 --- a/Config.mk +++ b/Config.mk @@ -86,4 +86,6 @@ FLUIDSYNTHLIB=$(FLUIDSYNTHBUILDDIR)/src/libfluidsynth.a INIHHOME=$(realpath external/inih) +MDNSHOME=$(realpath external/mdns) + -include $(CIRCLE_STDLIB_CONFIG) diff --git a/Kernel.mk b/Kernel.mk index 2e09d0b..ea00ab4 100644 --- a/Kernel.mk +++ b/Kernel.mk @@ -24,6 +24,7 @@ OBJS := src/config.o \ src/net/applemidi.o \ src/net/ftpdaemon.o \ src/net/ftpworker.o \ + src/net/mdnsresponder.o \ src/net/udpmidi.o \ src/pisound.o \ src/power.o \ @@ -48,6 +49,11 @@ INCLUDE += -I $(INIHHOME) EXTRACLEAN += $(INIHHOME)/ini.d \ $(INIHHOME)/ini.o +# +# mdns +# +INCLUDE += -I $(MDNSHOME) + include $(CIRCLEHOME)/Rules.mk CFLAGS += -Werror -Wextra -Wno-unused-parameter diff --git a/Makefile b/Makefile index 37ac8e6..4d2224d 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ include Config.mk .DEFAULT_GOAL=all -.PHONY: circle-stdlib mt32emu fluidsynth all clean veryclean +.PHONY: circle-stdlib mdns mt32emu fluidsynth all clean veryclean # # Functions to apply/reverse patches only if not completely applied/reversed already @@ -36,7 +36,7 @@ $(CIRCLE_STDLIB_CONFIG) $(CIRCLE_CONFIG)&: # Apply patches @${APPLY_PATCH} $(CIRCLEHOME) patches/circle-45-minimal-usb-drivers.patch @${APPLY_PATCH} $(CIRCLEHOME) patches/circle-45-cp210x-remove-partnum-check.patch - @${APPLY_PATCH} $(CIRCLEHOME) patches/circle-44-multicast.patch + @${APPLY_PATCH} $(CIRCLEHOME) patches/circle-45-multicast.patch ifeq ($(strip $(GC_SECTIONS)),1) # Enable function/data sections for circle-stdlib @@ -64,6 +64,15 @@ $(CIRCLESTDLIBHOME)/.done: $(CIRCLE_STDLIB_CONFIG) @$(MAKE) -C $(CIRCLESTDLIBHOME) touch $@ +# +# Patch mdns +# +mdns: $(MDNSHOME)/.done + +$(MDNSHOME)/.done: + @${APPLY_PATCH} $(MDNSHOME) patches/mdns-1.4.2-circle.patch + touch $@ + # # Build mt32emu # @@ -127,7 +136,7 @@ $(FLUIDSYNTHBUILDDIR)/.done: $(CIRCLESTDLIBHOME)/.done # # Build kernel itself # -all: circle-stdlib mt32emu fluidsynth +all: circle-stdlib mdns mt32emu fluidsynth @$(MAKE) -f Kernel.mk $(KERNEL).img $(KERNEL).hex # @@ -145,11 +154,15 @@ veryclean: clean @${REVERSE_PATCH} $(CIRCLEHOME) patches/circle-45-cp210x-remove-partnum-check.patch @${REVERSE_PATCH} $(CIRCLEHOME) patches/circle-45-minimal-usb-drivers.patch @${REVERSE_PATCH} $(FLUIDSYNTHHOME) patches/fluidsynth-2.3.1-circle.patch + @${REVERSE_PATCH} $(MDNSHOME) patches/mdns-1.4.2-circle.patch # Clean circle-stdlib @$(MAKE) -C $(CIRCLESTDLIBHOME) mrproper @$(RM) $(CIRCLESTDLIBHOME)/.done +# Clean mdns + @$(RM) $(MDNSHOME)/.done + # Clean mt32emu @$(RM) -r $(MT32EMUBUILDDIR) diff --git a/include/mt32pi.h b/include/mt32pi.h index 86ff68d..1c5d559 100644 --- a/include/mt32pi.h +++ b/include/mt32pi.h @@ -55,6 +55,7 @@ #include "midiparser.h" #include "net/applemidi.h" #include "net/ftpdaemon.h" +#include "net/mdnsresponder.h" #include "net/udpmidi.h" #include "pisound.h" #include "power.h" @@ -161,6 +162,7 @@ private: CBcm4343Device m_WLAN; CWPASupplicant m_WPASupplicant; bool m_bNetworkReady; + CMDNSResponder* m_pMDNSResponder; CAppleMIDIParticipant* m_pAppleMIDIParticipant; CUDPMIDIReceiver* m_pUDPMIDIReceiver; CFTPDaemon* m_pFTPDaemon; diff --git a/include/net/mdnsresponder.h b/include/net/mdnsresponder.h new file mode 100644 index 0000000..7d81da8 --- /dev/null +++ b/include/net/mdnsresponder.h @@ -0,0 +1,59 @@ +// +// mdnsresponder.h +// +// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi +// Copyright (C) 2020-2022 Dale Whinham +// +// This file is part of mt32-pi. +// +// mt32-pi 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. +// +// mt32-pi 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 +// mt32-pi. If not, see . +// + +#ifndef _mdnsresponder_h +#define _mdnsresponder_h + +#include +#include + +class CMDNSResponder : protected CTask +{ +public: + CMDNSResponder(); + virtual ~CMDNSResponder() override; + + bool Initialize(); + + virtual void Run() override; + +private: + void Probe(); + void Announce(); + void Goodbye(); + + static constexpr size_t MaxMDNSNameLength = 256; + + // UDP socket + CSocket* m_pSocket; + + // Socket receive buffer + u8 m_Buffer[FRAME_BUFFER_SIZE]; + char m_DNSName[MaxMDNSNameLength]; + + // static int MDNSServiceCallback(mdns_socket_t sock, const struct sockaddr* from, size_t addrlen, mdns_entry_type_t entry, + // uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, + // size_t size, size_t name_offset, size_t name_length, size_t record_offset, + // size_t record_length, void* user_data); +}; + +#endif diff --git a/include/net/udpmidi.h b/include/net/udpmidi.h index 921297c..c1b9495 100644 --- a/include/net/udpmidi.h +++ b/include/net/udpmidi.h @@ -23,7 +23,6 @@ #ifndef _udpmidi_h #define _udpmidi_h -#include #include #include @@ -44,11 +43,11 @@ public: virtual void Run() override; private: - // UDP sockets - CSocket* m_pMIDISocket; + // UDP socket + CSocket* m_pSocket; // Socket receive buffer - u8 m_MIDIBuffer[FRAME_BUFFER_SIZE]; + u8 m_ReceiveBuffer[FRAME_BUFFER_SIZE]; // Callback handler CUDPMIDIHandler* m_pHandler; diff --git a/include/utility.h b/include/utility.h index ff8cade..645def7 100644 --- a/include/utility.h +++ b/include/utility.h @@ -98,6 +98,11 @@ namespace Utility return nTicks / 1000; } + constexpr u32 IPAddressToInteger(u8 nA, u8 nB, u8 nC, u8 nD) + { + return nA | (nB << 8) | (nC << 16) | (nD << 24); + } + // Computes the Roland checksum constexpr u8 RolandChecksum(const u8* pData, size_t nSize) { diff --git a/patches/circle-44.4-multicast.patch b/patches/circle-45-multicast.patch similarity index 100% rename from patches/circle-44.4-multicast.patch rename to patches/circle-45-multicast.patch diff --git a/patches/mdns-1.4.2-circle.patch b/patches/mdns-1.4.2-circle.patch new file mode 100644 index 0000000..dde32d1 --- /dev/null +++ b/patches/mdns-1.4.2-circle.patch @@ -0,0 +1,553 @@ +diff --git a/mdns.h b/mdns.h +index 4b3b03d..96c76ec 100644 +--- a/mdns.h ++++ b/mdns.h +@@ -24,6 +24,10 @@ + #include + #include + #define strncasecmp _strnicmp ++#elif defined(__circle__) ++#include ++#include ++#include + #else + #include + #include +@@ -74,11 +78,46 @@ enum mdns_entry_type { + + enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 }; + ++#ifdef __circle__ ++#define htons(VALUE) __builtin_bswap16(VALUE) ++#define htonl(VALUE) __builtin_bswap32(VALUE) ++#define ntohs(VALUE) __builtin_bswap16(VALUE) ++#define ntohl(VALUE) __builtin_bswap32(VALUE) ++ ++#define AF_INET 2 ++typedef CSocket* mdns_socket_t; ++ ++typedef u32 in_addr_t; ++typedef u8 sa_family_t; ++typedef u16 in_port_t; ++ ++struct in_addr ++{ ++ in_addr_t s_addr; ++}; ++ ++struct sockaddr_in ++{ ++ sa_family_t sin_family; ++ in_port_t sin_port; ++ struct in_addr sin_addr; ++}; ++ ++struct sockaddr { ++ sa_family_t sa_family; ++ char sa_data[14]; ++}; ++ ++typedef size_t socklen_t; ++#else ++typedef int mdns_socket_t; ++#endif ++ + typedef enum mdns_record_type mdns_record_type_t; + typedef enum mdns_entry_type mdns_entry_type_t; + typedef enum mdns_class mdns_class_t; + +-typedef int (*mdns_record_callback_fn)(int sock, const struct sockaddr* from, size_t addrlen, ++typedef int (*mdns_record_callback_fn)(mdns_socket_t sock, const struct sockaddr* from, size_t addrlen, + mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype, + uint16_t rclass, uint32_t ttl, const void* data, size_t size, + size_t name_offset, size_t name_length, size_t record_offset, +@@ -136,9 +175,11 @@ struct mdns_record_a_t { + struct sockaddr_in addr; + }; + ++#ifndef __circle__ + struct mdns_record_aaaa_t { + struct sockaddr_in6 addr; + }; ++#endif + + struct mdns_record_txt_t { + mdns_string_t key; +@@ -152,7 +193,9 @@ struct mdns_record_t { + mdns_record_ptr_t ptr; + mdns_record_srv_t srv; + mdns_record_a_t a; ++#ifndef __circle__ + mdns_record_aaaa_t aaaa; ++#endif + mdns_record_txt_t txt; + } data; + uint16_t rclass; +@@ -176,12 +219,13 @@ struct mdns_query_t { + + // mDNS/DNS-SD public API + ++#ifndef __circle__ + //! Open and setup a IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass + //! in the appropriate socket address in saddr, otherwise pass a null pointer for INADDR_ANY. To + //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a + //! random user level ephemeral port. To run discovery service listening for incoming discoveries + //! and queries, you must set MDNS_PORT as port. +-static inline int ++static inline mdns_socket_t + mdns_socket_open_ipv4(const struct sockaddr_in* saddr); + + //! Setup an already opened IPv4 socket for mDNS/DNS-SD. To bind the socket to a specific interface, +@@ -190,14 +234,14 @@ mdns_socket_open_ipv4(const struct sockaddr_in* saddr); + //! random user level ephemeral port. To run discovery service listening for incoming discoveries + //! and queries, you must set MDNS_PORT as port. + static inline int +-mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr); ++mdns_socket_setup_ipv4(mdns_socket_t sock, const struct sockaddr_in* saddr); + + //! Open and setup a IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass + //! in the appropriate socket address in saddr, otherwise pass a null pointer for in6addr_any. To + //! send one-shot discovery requests and queries pass a null pointer or set 0 as port to assign a + //! random user level ephemeral port. To run discovery service listening for incoming discoveries + //! and queries, you must set MDNS_PORT as port. +-static inline int ++static inline mdns_socket_t + mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); + + //! Setup an already opened IPv6 socket for mDNS/DNS-SD. To bind the socket to a specific interface, +@@ -206,30 +250,31 @@ mdns_socket_open_ipv6(const struct sockaddr_in6* saddr); + //! random user level ephemeral port. To run discovery service listening for incoming discoveries + //! and queries, you must set MDNS_PORT as port. + static inline int +-mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr); ++mdns_socket_setup_ipv6(mdns_socket_t sock, const struct sockaddr_in6* saddr); + + //! Close a socket opened with mdns_socket_open_ipv4 and mdns_socket_open_ipv6. + static inline void +-mdns_socket_close(int sock); ++mdns_socket_close(mdns_socket_t sock); ++#endif + + //! Listen for incoming multicast DNS-SD and mDNS query requests. The socket should have been opened + //! on port MDNS_PORT using one of the mdns open or setup socket functions. Buffer must be 32 bit + //! aligned. Parsing is stopped when callback function returns non-zero. Returns the number of + //! queries parsed. + static inline size_t +-mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_socket_listen(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data); + + //! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 0 + //! on success, or <0 if error. + static inline int +-mdns_discovery_send(int sock); ++mdns_discovery_send(mdns_socket_t sock); + + //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to + //! the given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback + //! function returns non-zero. Returns the number of responses parsed. + static inline size_t +-mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_discovery_recv(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data); + + //! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer +@@ -239,7 +284,7 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac + //! ephemeral port, or a multicast response if the socket is bound to mDNS port 5353. Returns the + //! used query ID, or <0 if error. + static inline int +-mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, ++mdns_query_send(mdns_socket_t sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, + size_t capacity, uint16_t query_id); + + //! Send a multicast mDNS query on the given socket for the given service names. The supplied buffer +@@ -251,7 +296,7 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng + //! unicast response if the socket is bound to an ephemeral port, or a multicast response if the + //! socket is bound to mDNS port 5353. Returns the used query ID, or <0 if error. + static inline int +-mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, ++mdns_multiquery_send(mdns_socket_t sock, const mdns_query_t* query, size_t count, void* buffer, + size_t capacity, uint16_t query_id); + + //! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering +@@ -260,7 +305,7 @@ mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* bu + //! given callback for parsing. Buffer must be 32 bit aligned. Parsing is stopped when callback + //! function returns non-zero. Returns the number of responses parsed. + static inline size_t +-mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_query_recv(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data, int query_id); + + //! Send a variable unicast mDNS query answer to any question with variable number of records to the +@@ -269,7 +314,7 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn + //! Buffer must be 32 bit aligned. The record type and name should match the data from the query + //! recieved. Returns 0 if success, or <0 if error. + static inline int +-mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, ++mdns_query_answer_unicast(mdns_socket_t sock, const void* address, size_t address_size, void* buffer, + size_t capacity, uint16_t query_id, mdns_record_type_t record_type, + const char* name, size_t name_length, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, +@@ -280,7 +325,7 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo + //! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit + //! aligned. Returns 0 if success, or <0 if error. + static inline int +-mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_query_answer_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +@@ -288,14 +333,14 @@ mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record + //! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service + //! startup to announce your instance to the local network. + static inline int +-mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_announce_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + + //! Send a variable multicast mDNS announcement. Use this on service end for removing the resource + //! from the local network. The records must be identical to the according announcement. + static inline int +-mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_goodbye_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count); + +@@ -316,10 +361,12 @@ static inline struct sockaddr_in* + mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in* addr); + ++#ifndef __circle__ + //! Parse an AAAA record, returns the IPv6 address in the record + static inline struct sockaddr_in6* + mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr); ++#endif + + //! Parse a TXT record, returns the number of key=value records parsed and stores the key-value + //! pairs in the supplied buffer +@@ -383,9 +430,10 @@ mdns_htonl(void* data, uint32_t val) { + return MDNS_POINTER_OFFSET(data, sizeof(uint32_t)); + } + +-static inline int ++#ifndef __circle__ ++static inline mdns_socket_t + mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { +- int sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); ++ mdns_socket_t sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv4(sock, saddr)) { +@@ -396,7 +444,7 @@ mdns_socket_open_ipv4(const struct sockaddr_in* saddr) { + } + + static inline int +-mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { ++mdns_socket_setup_ipv4(mdns_socket_t sock, const struct sockaddr_in* saddr) { + unsigned char ttl = 1; + unsigned char loopback = 1; + unsigned int reuseaddr = 1; +@@ -447,9 +495,9 @@ mdns_socket_setup_ipv4(int sock, const struct sockaddr_in* saddr) { + return 0; + } + +-static inline int ++static inline mdns_socket_t + mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { +- int sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); ++ mdns_socket_t sock = (int)socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (sock < 0) + return -1; + if (mdns_socket_setup_ipv6(sock, saddr)) { +@@ -460,7 +508,7 @@ mdns_socket_open_ipv6(const struct sockaddr_in6* saddr) { + } + + static inline int +-mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { ++mdns_socket_setup_ipv6(mdns_socket_t sock, const struct sockaddr_in6* saddr) { + int hops = 1; + unsigned int loopback = 1; + unsigned int reuseaddr = 1; +@@ -512,13 +560,14 @@ mdns_socket_setup_ipv6(int sock, const struct sockaddr_in6* saddr) { + } + + static inline void +-mdns_socket_close(int sock) { ++mdns_socket_close(mdns_socket_t sock) { + #ifdef _WIN32 + closesocket(sock); + #else + close(sock); + #endif + } ++#endif + + static inline int + mdns_is_string_ref(uint8_t val) { +@@ -773,7 +822,7 @@ mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, si + } + + static inline size_t +-mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const void* buffer, ++mdns_records_parse(mdns_socket_t sock, const struct sockaddr* from, size_t addrlen, const void* buffer, + size_t size, size_t* offset, mdns_entry_type_t type, uint16_t query_id, + size_t records, mdns_record_callback_fn callback, void* user_data) { + size_t parsed = 0; +@@ -807,16 +856,30 @@ mdns_records_parse(int sock, const struct sockaddr* from, size_t addrlen, const + } + + static inline int +-mdns_unicast_send(int sock, const void* address, size_t address_size, const void* buffer, ++mdns_unicast_send(mdns_socket_t sock, const void* address, size_t address_size, const void* buffer, + size_t size) { ++#ifdef __circle__ ++ const sockaddr_in* addr = (const struct sockaddr_in*)address; ++ CIPAddress ipaddr(addr->sin_addr.s_addr); ++ u16 port = addr->sin_port; ++ ++ if (sock->SendTo(buffer, size, 0, ipaddr, port) < 0) ++ return -1; ++#else + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, (const struct sockaddr*)address, + (socklen_t)address_size) < 0) + return -1; ++#endif + return 0; + } + + static inline int +-mdns_multicast_send(int sock, const void* buffer, size_t size) { ++mdns_multicast_send(mdns_socket_t sock, const void* buffer, size_t size) { ++#ifdef __circle__ ++ CIPAddress ipaddr(htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U))); ++ if (sock->SendTo(buffer, size, 0, ipaddr, MDNS_PORT) < 0) ++ return -1; ++#else + struct sockaddr_storage addr_storage; + struct sockaddr_in addr; + struct sockaddr_in6 addr6; +@@ -850,6 +913,7 @@ mdns_multicast_send(int sock, const void* buffer, size_t size) { + + if (sendto(sock, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) + return -1; ++#endif + return 0; + } + +@@ -871,13 +935,28 @@ static const uint8_t mdns_services_query[] = { + 0x80, MDNS_CLASS_IN}; + + static inline int +-mdns_discovery_send(int sock) { ++mdns_discovery_send(mdns_socket_t sock) { + return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); + } + + static inline size_t +-mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_discovery_recv(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data) { ++#ifdef __circle__ ++ struct sockaddr_in addr; ++ struct sockaddr* saddr = (struct sockaddr*)&addr; ++ socklen_t addrlen = sizeof(addr); ++ ++ CIPAddress ipaddr; ++ u16 port; ++ mdns_ssize_t ret = sock->ReceiveFrom(buffer, capacity, 0, &ipaddr, &port); ++ if (ret <= 0) ++ return 0; ++ ++ addr.sin_family = AF_INET; ++ addr.sin_port = port; ++ addr.sin_addr.s_addr = (u32)ipaddr; ++#else + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); +@@ -888,6 +967,7 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; ++#endif + + size_t data_size = (size_t)ret; + size_t records = 0; +@@ -980,8 +1060,23 @@ mdns_discovery_recv(int sock, void* buffer, size_t capacity, mdns_record_callbac + } + + static inline size_t +-mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_socket_listen(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data) { ++#ifdef __circle__ ++ struct sockaddr_in addr; ++ struct sockaddr* saddr = (struct sockaddr*)&addr; ++ socklen_t addrlen = sizeof(addr); ++ ++ CIPAddress ipaddr; ++ u16 port; ++ mdns_ssize_t ret = sock->ReceiveFrom(buffer, capacity, 0, &ipaddr, &port); ++ if (ret <= 0) ++ return 0; ++ ++ addr.sin_family = AF_INET; ++ addr.sin_port = port; ++ addr.sin_addr.s_addr = (u32)ipaddr; ++#else + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); +@@ -992,6 +1087,7 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; ++#endif + + size_t data_size = (size_t)ret; + const uint16_t* data = (const uint16_t*)buffer; +@@ -1061,7 +1157,7 @@ mdns_socket_listen(int sock, void* buffer, size_t capacity, mdns_record_callback + } + + static inline int +-mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, ++mdns_query_send(mdns_socket_t sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, + size_t capacity, uint16_t query_id) { + mdns_query_t query; + query.type = type; +@@ -1071,11 +1167,15 @@ mdns_query_send(int sock, mdns_record_type_t type, const char* name, size_t leng + } + + static inline int +-mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity, ++mdns_multiquery_send(mdns_socket_t sock, const mdns_query_t* query, size_t count, void* buffer, size_t capacity, + uint16_t query_id) { + if (!count || (capacity < (sizeof(struct mdns_header_t) + (6 * count)))) + return -1; + ++#ifdef __circle__ ++ // Only multicast response supported ++ uint16_t rclass = MDNS_CLASS_IN; ++#else + // Ask for a unicast response since it's a one-shot query + uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; + +@@ -1090,6 +1190,7 @@ mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* bu + (ntohs(((struct sockaddr_in6*)saddr)->sin6_port) == MDNS_PORT)) + rclass &= ~MDNS_UNICAST_RESPONSE; + } ++#endif + + struct mdns_header_t* header = (struct mdns_header_t*)buffer; + // Query ID +@@ -1122,8 +1223,23 @@ mdns_multiquery_send(int sock, const mdns_query_t* query, size_t count, void* bu + } + + static inline size_t +-mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, ++mdns_query_recv(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, + void* user_data, int only_query_id) { ++#ifdef __circle__ ++ struct sockaddr_in addr; ++ struct sockaddr* saddr = (struct sockaddr*)&addr; ++ socklen_t addrlen = sizeof(addr); ++ ++ CIPAddress ipaddr; ++ u16 port; ++ mdns_ssize_t ret = sock->ReceiveFrom(buffer, capacity, 0, &ipaddr, &port); ++ if (ret <= 0) ++ return 0; ++ ++ addr.sin_family = AF_INET; ++ addr.sin_port = port; ++ addr.sin_addr.s_addr = (u32)ipaddr; ++#else + struct sockaddr_in6 addr; + struct sockaddr* saddr = (struct sockaddr*)&addr; + socklen_t addrlen = sizeof(addr); +@@ -1134,6 +1250,7 @@ mdns_query_recv(int sock, void* buffer, size_t capacity, mdns_record_callback_fn + mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen); + if (ret <= 0) + return 0; ++#endif + + size_t data_size = (size_t)ret; + const uint16_t* data = (const uint16_t*)buffer; +@@ -1264,12 +1381,14 @@ mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t + data = MDNS_POINTER_OFFSET(data, 4); + break; + ++#ifndef __circle__ + case MDNS_RECORDTYPE_AAAA: + if (remain < 16) + return 0; + memcpy(data, &record.data.aaaa.addr.sin6_addr, 16); // ipv6 address + data = MDNS_POINTER_OFFSET(data, 16); + break; ++#endif + + default: + break; +@@ -1360,7 +1479,7 @@ mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) + } + + static inline int +-mdns_query_answer_unicast(int sock, const void* address, size_t address_size, void* buffer, ++mdns_query_answer_unicast(mdns_socket_t sock, const void* address, size_t address_size, void* buffer, + size_t capacity, uint16_t query_id, mdns_record_type_t record_type, + const char* name, size_t name_length, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, +@@ -1424,7 +1543,7 @@ mdns_query_answer_unicast(int sock, const void* address, size_t address_size, vo + } + + static inline int +-mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_answer_multicast_rclass_ttl(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count, + uint16_t rclass, uint32_t ttl) { +@@ -1473,7 +1592,7 @@ mdns_answer_multicast_rclass_ttl(int sock, void* buffer, size_t capacity, mdns_r + } + + static inline int +-mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_query_answer_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, +@@ -1482,7 +1601,7 @@ mdns_query_answer_multicast(int sock, void* buffer, size_t capacity, mdns_record + } + + static inline int +-mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_announce_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, +@@ -1491,7 +1610,7 @@ mdns_announce_multicast(int sock, void* buffer, size_t capacity, mdns_record_t a + } + + static inline int +-mdns_goodbye_multicast(int sock, void* buffer, size_t capacity, mdns_record_t answer, ++mdns_goodbye_multicast(mdns_socket_t sock, void* buffer, size_t capacity, mdns_record_t answer, + const mdns_record_t* authority, size_t authority_count, + const mdns_record_t* additional, size_t additional_count) { + // Goodbye should have ttl of 0 +@@ -1545,6 +1664,7 @@ mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t lengt + return addr; + } + ++#ifndef __circle__ + static inline struct sockaddr_in6* + mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, + struct sockaddr_in6* addr) { +@@ -1557,6 +1677,7 @@ mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t le + memcpy(&addr->sin6_addr, MDNS_POINTER_OFFSET(buffer, offset), 16); + return addr; + } ++#endif + + static inline size_t + mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, diff --git a/src/mt32pi.cpp b/src/mt32pi.cpp index b15dfd3..a853d5b 100644 --- a/src/mt32pi.cpp +++ b/src/mt32pi.cpp @@ -82,6 +82,7 @@ CMT32Pi::CMT32Pi(CI2CMaster* pI2CMaster, CSPIMaster* pSPIMaster, CInterruptSyste m_WLAN(WLANFirmwarePath), m_WPASupplicant(WLANConfigFile), m_bNetworkReady(false), + m_pMDNSResponder(nullptr), m_pAppleMIDIParticipant(nullptr), m_pUDPMIDIReceiver(nullptr), m_pFTPDaemon(nullptr), @@ -901,6 +902,14 @@ void CMT32Pi::UpdateNetwork() else m_pLogger->Write(MT32PiName, LogNotice, "FTP daemon initialized"); } + + m_pMDNSResponder = new CMDNSResponder(); + if (!m_pMDNSResponder->Initialize()) + { + m_pLogger->Write(MT32PiName, LogError, "Failed to init mDNS responder"); + delete m_pMDNSResponder; + m_pMDNSResponder = nullptr; + } } else if (m_bNetworkReady && !bNetIsRunning) { diff --git a/src/net/mdnsresponder.cpp b/src/net/mdnsresponder.cpp new file mode 100644 index 0000000..5b87ee1 --- /dev/null +++ b/src/net/mdnsresponder.cpp @@ -0,0 +1,246 @@ +// +// mdsnresponder.cpp +// +// mt32-pi - A baremetal MIDI synthesizer for Raspberry Pi +// Copyright (C) 2020-2022 Dale Whinham +// +// This file is part of mt32-pi. +// +// mt32-pi 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. +// +// mt32-pi 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 +// mt32-pi. If not, see . +// + +#include + +#include +#include +#include +#include + +#include + +#include "net/mdnsresponder.h" +#include "utility.h" + +constexpr u32 MDNSAddress = Utility::IPAddressToInteger(224, 0, 0, 251); +constexpr u16 MDNSPort = 5353; + +const char MDNSResponderName[] = "mdns"; + +struct TDNSHeader +{ + u16 nID; + u16 nFlags; +#define DNS_FLAGS_QR 0x8000 +#define DNS_FLAGS_OPCODE 0x7800 + #define DNS_FLAGS_OPCODE_QUERY 0x0000 + #define DNS_FLAGS_OPCODE_IQUERY 0x0800 + #define DNS_FLAGS_OPCODE_STATUS 0x1000 +#define DNS_FLAGS_AA 0x0400 +#define DNS_FLAGS_TC 0x0200 +#define DNS_FLAGS_RD 0x0100 +#define DNS_FLAGS_RA 0x0080 +#define DNS_FLAGS_RCODE 0x000F + #define DNS_RCODE_SUCCESS 0x0000 + #define DNS_RCODE_FORMAT_ERROR 0x0001 + #define DNS_RCODE_SERVER_FAILURE 0x0002 + #define DNS_RCODE_NAME_ERROR 0x0003 + #define DNS_RCODE_NOT_IMPLEMENTED 0x0004 + #define DNS_RCODE_REFUSED 0x0005 + u16 nQDCount; + u16 nANCount; + u16 nNSCount; + u16 nARCount; +} +PACKED; + +struct TDNSResourceRecordTrailerAIN +{ + u16 nType; + u16 nClass; + u32 nTTL; + u16 nRDLength; +#define DNS_RDLENGTH_AIN 4 + unsigned char RData[DNS_RDLENGTH_AIN]; +} +PACKED; + +CMDNSResponder::CMDNSResponder() + : CTask(TASK_STACK_SIZE, true), + m_pSocket(nullptr), + m_Buffer{0} +{ + snprintf(m_DNSName, sizeof(m_DNSName), "_%s", "mt32-pi"); +} + +CMDNSResponder::~CMDNSResponder() +{ + if (m_pSocket) + delete m_pSocket; +} + +bool CMDNSResponder::Initialize() +{ + assert(m_pSocket == nullptr); + + CLogger* const pLogger = CLogger::Get(); + CNetSubSystem* const pNet = CNetSubSystem::Get(); + + if ((m_pSocket = new CSocket(pNet, IPPROTO_UDP)) == nullptr) + return false; + + if (m_pSocket->Bind(MDNSPort) != 0) + { + pLogger->Write(MDNSResponderName, LogError, "Couldn't bind to port %d", MDNSPort); + return false; + } + + if (m_pSocket->SetOptionBroadcast(true) != 0) + { + pLogger->Write(MDNSResponderName, LogError, "Couldn't enable broadcast packet for socket"); + return false; + } + + // We started as a suspended task; run now that initialization is successful + Start(); + + return true; +} + +void CMDNSResponder::Run() +{ + assert(m_pSocket != nullptr); + + //CLogger* const pLogger = CLogger::Get(); + CScheduler* const pScheduler = CScheduler::Get(); + + // CIPAddress MDNSIPAddress(MDNSAddress);//Utility::IPAddressToInteger(192, 168, 175, 30)); + + // char buf[] = "hello world"; + + // while (true) + // { + // m_pSocket->SendTo(buf, sizeof(buf), 0, MDNSIPAddress, MDNSPort); + // pScheduler->Sleep(1); + // } + + // return; + + + // mdns_record_t Record = + // { + // { "_applemidi", 10 }, + // MDNS_RECORDTYPE_PTR, + // { + // .ptr.name = "" + // } + // }; + // mdns_record_t Record; + // Record.name = { MDNS_STRING_CONST("_applemidi._udp.local.") }; + // Record.type = MDNS_RECORDTYPE_PTR; + // Record.data.ptr.name = { MDNS_STRING_CONST("mt32-pi._applemidi._udp.local.") }; + // Record.rclass = 0; + // Record.ttl = 0; + + +return; + + while (true) + { + // char buf[] = "hello world"; + // CIPAddress MDNSIPAddress(MDNSAddress); + // m_pSocket->SendTo(buf, sizeof(buf), 0, MDNSIPAddress, MDNSPort); + // pScheduler->Sleep(1); + + mdns_record_t Record = + { + .name = { MDNS_STRING_CONST("_apple-midi._udp.local.") }, + .type = MDNS_RECORDTYPE_PTR, + .data = { .ptr = { .name = { MDNS_STRING_CONST("mt32-pi._apple-midi._udp.local.") } } }, + .rclass = 0, + .ttl = 0 + }; + + assert(mdns_announce_multicast(m_pSocket, m_Buffer, sizeof(m_Buffer), Record, nullptr, 0, nullptr, 0) == 0); + pScheduler->Sleep(2); + + assert(mdns_announce_multicast(m_pSocket, m_Buffer, sizeof(m_Buffer), Record, nullptr, 0, nullptr, 0) == 0); + pScheduler->Sleep(10); + + assert(mdns_announce_multicast(m_pSocket, m_Buffer, sizeof(m_Buffer), Record, nullptr, 0, nullptr, 0) == 0); + pScheduler->Sleep(20); + + assert(mdns_goodbye_multicast(m_pSocket, m_Buffer, sizeof(m_Buffer), Record, nullptr, 0, nullptr, 0) == 0); + pScheduler->Sleep(2); + + // // Non-blocking call + // const int nResult = m_pSocket->Receive(m_ReceiveBuffer, sizeof(m_ReceiveBuffer), MSG_DONTWAIT); + // m_pSocket->ReceiveFrom() + + // if (nResult < 0) + // pLogger->Write(MDNSResponderName, LogError, "Socket receive error: %d", nResult); + // else if (nResult > 0) + // { + // pLogger->Write(MDNSResponderName, LogDebug, "Received something of size %d", nResult); + // } + + // TDNSHeader* Header = reinterpret_cast(m_ReceiveBuffer); + // Header->nID = htons(0); + // Header->nFlags = htons(DNS_FLAGS_QR | DNS_FLAGS_AA); + // Header->nQDCount = htons(0); + // Header->nANCount = htons(1); + // Header->nNSCount = htons(0); + // Header->nARCount = htons(0); + + // u8* pPtr = m_ReceiveBuffer + sizeof(TDNSHeader); + // *pPtr++ = 0x0B; + // *pPtr++ = '_'; + // *pPtr++ = 'a'; + // *pPtr++ = 'p'; + // *pPtr++ = 'p'; + // *pPtr++ = 'l'; + // *pPtr++ = 'e'; + // *pPtr++ = '-'; + // *pPtr++ = 'm'; + // *pPtr++ = 'i'; + // *pPtr++ = 'd'; + // *pPtr++ = 'i'; + // *pPtr++ = 0x04; + // *pPtr++ = '_'; + // *pPtr++ = 'u'; + // *pPtr++ = 'd'; + // *pPtr++ = 'p'; + // *pPtr++ = 0x05; + // *pPtr++ = 'l'; + // *pPtr++ = 'o'; + // *pPtr++ = 'c'; + // *pPtr++ = 'a'; + // *pPtr++ = 'l'; + // *pPtr++ = '\0'; + + // TDNSResourceRecordTrailerAIN* Record = reinterpret_cast(pPtr); + // Record->nType = 0x0C; + // Record->nClass = 0x01; + // Record->nTTL = 120; + // //Record->nRDLength + + // const u8 nNameLength = strlen(m_DNSName); + // *pPtr++ = nNameLength; + // memcpy(reinterpret_cast(pPtr), m_DNSName, nNameLength);//, sizeof(m_ReceiveBuffer) - (pPtr - m_ReceiveBuffer)); + // pPtr += nNameLength; + + // // Allow other tasks to run + // //pScheduler->Yield(); + // pScheduler->Sleep(10); + } +} diff --git a/src/net/udpmidi.cpp b/src/net/udpmidi.cpp index bb95684..aaab39f 100644 --- a/src/net/udpmidi.cpp +++ b/src/net/udpmidi.cpp @@ -33,29 +33,29 @@ const char UDPMIDIName[] = "udpmidi"; CUDPMIDIReceiver::CUDPMIDIReceiver(CUDPMIDIHandler* pHandler) : CTask(TASK_STACK_SIZE, true), - m_pMIDISocket(nullptr), - m_MIDIBuffer{0}, + m_pSocket(nullptr), + m_ReceiveBuffer{0}, m_pHandler(pHandler) { } CUDPMIDIReceiver::~CUDPMIDIReceiver() { - if (m_pMIDISocket) - delete m_pMIDISocket; + if (m_pSocket) + delete m_pSocket; } bool CUDPMIDIReceiver::Initialize() { - assert(m_pMIDISocket == nullptr); + assert(m_pSocket == nullptr); CLogger* const pLogger = CLogger::Get(); CNetSubSystem* const pNet = CNetSubSystem::Get(); - if ((m_pMIDISocket = new CSocket(pNet, IPPROTO_UDP)) == nullptr) + if ((m_pSocket = new CSocket(pNet, IPPROTO_UDP)) == nullptr) return false; - if (m_pMIDISocket->Bind(MIDIPort) != 0) + if (m_pSocket->Bind(MIDIPort) != 0) { pLogger->Write(UDPMIDIName, LogError, "Couldn't bind to port %d", MIDIPort); return false; @@ -70,7 +70,7 @@ bool CUDPMIDIReceiver::Initialize() void CUDPMIDIReceiver::Run() { assert(m_pHandler != nullptr); - assert(m_pMIDISocket != nullptr); + assert(m_pSocket != nullptr); CLogger* const pLogger = CLogger::Get(); CScheduler* const pScheduler = CScheduler::Get(); @@ -78,12 +78,12 @@ void CUDPMIDIReceiver::Run() while (true) { // Blocking call - const int nMIDIResult = m_pMIDISocket->Receive(m_MIDIBuffer, sizeof(m_MIDIBuffer), 0); + const int nResult = m_pSocket->Receive(m_ReceiveBuffer, sizeof(m_ReceiveBuffer), 0); - if (nMIDIResult < 0) - pLogger->Write(UDPMIDIName, LogError, "MIDI socket receive error: %d", nMIDIResult); - else if (nMIDIResult > 0) - m_pHandler->OnUDPMIDIDataReceived(m_MIDIBuffer, nMIDIResult); + if (nResult < 0) + pLogger->Write(UDPMIDIName, LogError, "Socket receive error: %d", nResult); + else if (nResult > 0) + m_pHandler->OnUDPMIDIDataReceived(m_ReceiveBuffer, nResult); // Allow other tasks to run pScheduler->Yield();