move to new cspot

This commit is contained in:
philippe44
2023-03-25 16:48:41 -07:00
parent c712b78931
commit 008c36facf
2983 changed files with 465270 additions and 13569 deletions

View File

@@ -0,0 +1,17 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

View File

@@ -0,0 +1,62 @@
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm
*.msp
# Windows shortcuts
*.lnk
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
__history/
*.cb*
*.o
*.tds
*.res
*.map
*.il*
*.bak
build/
lib/
.vs/
*.idb
*.user
*.pdb

View File

@@ -0,0 +1,25 @@
Copyright (c) 2011, Darell Tan
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,12 @@
# Introduction
This is a fork from https://bitbucket.org/geekman/tinysvcmdns. It works under
Windows, MacOS, and Linux (x86, x86_64, arm and aarch64), FreeBSD and Solaris.
I've added a function to stop a registered service, send bye-bye packet and
properly release all allocated memory.
It also uses unicast when cient is asking for it which is essential as more and more routers do IGMP spoofing
I've also added a small real responder
Please see [here](https://github.com/philippe44/cross-compiling/blob/master/README.md#organizing-submodules--packages) to know how to rebuild my apps in general

Binary file not shown.

View File

@@ -0,0 +1,72 @@
#!/bin/bash
list="x86_64-linux-gnu-gcc x86-linux-gnu-gcc arm-linux-gnueabi-gcc aarch64-linux-gnu-gcc \
sparc64-linux-gnu-gcc mips-linux-gnu-gcc powerpc-linux-gnu-gcc x86_64-macos-darwin-gcc \
arm64-macos-darwin-cc x86_64-freebsd-gnu-gcc x86_64-solaris-gnu-gcc armv6-linux-gnueabi-gcc \
armv5-linux-gnueabi-gcc"
declare -A alias=( [x86-linux-gnu-gcc]=i686-stretch-linux-gnu-gcc \
[x86_64-linux-gnu-gcc]=x86_64-stretch-linux-gnu-gcc \
[arm-linux-gnueabi-gcc]=armv7-stretch-linux-gnueabi-gcc \
[armv5-linux-gnueabi-gcc]=armv6-stretch-linux-gnueabi-gcc \
[armv6-linux-gnueabi-gcc]=armv6-stretch-linux-gnueabi-gcc \
[aarch64-linux-gnu-gcc]=aarch64-stretch-linux-gnu-gcc \
[sparc64-linux-gnu-gcc]=sparc64-stretch-linux-gnu-gcc \
[mips-linux-gnu-gcc]=mips64-stretch-linux-gnu-gcc \
[powerpc-linux-gnu-gcc]=powerpc64-stretch-linux-gnu-gcc \
[x86_64-macos-darwin-gcc]=x86_64-apple-darwin19-gcc \
[arm64-macos-darwin-cc]=arm64-apple-darwin20.4-cc \
[x86_64-freebsd-gnu-gcc]=x86_64-cross-freebsd12.3-gcc \
[x86_64-solaris-gnu-gcc]=x86_64-cross-solaris2.x-gcc )
declare -A cflags=( [sparc64-linux-gnu-gcc]="-mcpu=v7" \
[mips-linux-gnu-gcc]="-march=mips32" \
[armv5-linux-gnueabi-gcc]="-march=armv5t -mfloat-abi=soft" \
[powerpc-linux-gnu-gcc]="-m32" )
declare -a compilers
IFS= read -ra candidates <<< "$list"
# do we have "clean" somewhere in parameters (assuming no compiler has "clean" in it...
if [[ $@[*]} =~ clean ]]; then
clean="clean"
fi
# first select platforms/compilers
for cc in ${candidates[@]}; do
# check compiler first
if ! command -v ${alias[$cc]:-$cc} &> /dev/null; then
if command -v $cc &> /dev/null; then
unset alias[$cc]
else
continue
fi
fi
if [[ $# == 0 || ($# == 1 && -n $clean) ]]; then
compilers+=($cc)
continue
fi
for arg in $@
do
if [[ $cc =~ $arg ]]; then
compilers+=($cc)
fi
done
done
# then do the work
for cc in ${compilers[@]}
do
IFS=- read -r platform host package dummy <<< $cc
export CFLAGS=${cflags[$cc]}
CC=${alias[$cc]:-$cc}
make AR=${CC%-*}-ar CC=$CC PLATFORM=$platform HOST=$host $clean -j8
if [[ -n $clean ]]; then
continue
fi
done

View File

@@ -0,0 +1,275 @@
/*
* tinysvcmdns - a tiny MDNS implementation for publishing services
* Copyright (C) 2011 Darell Tan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _WIN32
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <stdint.h>
#ifdef _WIN32
#include <winsock2.h>
#include <iphlpapi.h>
#pragma comment(lib, "IPHLPAPI.lib")
typedef uint32_t in_addr_t;
#define strcasecmp stricmp
#elif defined (__linux__) || defined (__FreeBSD__) || defined (sun)
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <strings.h>
#include <ifaddrs.h>
#if defined (__FreeBSD__) || defined (sun)
#include <net/if_dl.h>
#include <net/if_types.h>
#endif
#if defined (sun)
#include <sys/sockio.h>
#endif
#elif defined (__APPLE__)
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <ifaddrs.h>
#endif
#include "mdnssvc.h"
struct mdns_service *svc;
struct mdnsd *svr;
/*---------------------------------------------------------------------------*/
#ifdef _WIN32
static void winsock_init(void) {
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD(2, 2);
int WSerr = WSAStartup(wVersionRequested, &wsaData);
if (WSerr != 0) exit(1);
}
/*---------------------------------------------------------------------------*/
static void winsock_close(void) {
WSACleanup();
}
#endif
/*---------------------------------------------------------------------------*/
struct in_addr get_interface(char* iface) {
struct in_addr addr;
// try to get the address from the parameter
addr.s_addr = iface && *iface ? inet_addr(iface) : INADDR_NONE;
// if we already are given an address; just use it
if (addr.s_addr != INADDR_NONE) return addr;
#ifdef _WIN32
struct sockaddr_in* host = NULL;
ULONG size = sizeof(IP_ADAPTER_ADDRESSES) * 32;
// otherwise we need to loop and find somethign that works
IP_ADAPTER_ADDRESSES* adapters = (IP_ADAPTER_ADDRESSES*)malloc(size);
int ret = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_GATEWAYS | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_ANYCAST, 0, adapters, &size);
for (PIP_ADAPTER_ADDRESSES adapter = adapters; adapter; adapter = adapter->Next) {
if (adapter->TunnelType == TUNNEL_TYPE_TEREDO ||
adapter->OperStatus != IfOperStatusUp || 0)
continue;
char name[256];
wcstombs(name, adapter->FriendlyName, sizeof(name));
if (iface && *iface && strcasecmp(iface, name)) continue;
for (IP_ADAPTER_UNICAST_ADDRESS* unicast = adapter->FirstUnicastAddress; unicast;
unicast = unicast->Next) {
if (adapter->FirstGatewayAddress && unicast->Address.lpSockaddr->sa_family == AF_INET) {
addr = ((struct sockaddr_in*)unicast->Address.lpSockaddr)->sin_addr;
return addr;
}
}
}
return addr;
#else
struct ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) == -1) return addr;
for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL || ifa->ifa_addr->sa_family != AF_INET ||
!(ifa->ifa_flags & IFF_UP) || !(ifa->ifa_flags & IFF_MULTICAST) ||
ifa->ifa_flags & IFF_LOOPBACK ||
(iface && *iface && strcasecmp(iface, ifa->ifa_name)))
continue;
addr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
break;
}
freeifaddrs(ifaddr);
return addr;
#endif
}
#ifdef _WIN32
/*----------------------------------------------------------------------------*/
int asprintf(char** strp, const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
*strp = malloc(len + 1);
if (*strp) len = vsprintf(*strp, fmt, args);
else len = 0;
va_end(args);
return len;
}
#endif
/*---------------------------------------------------------------------------*/
static void print_usage(void) {
printf("[-v] [-o <ip|ifname>] -i <identity> -t <type> -p <port> [<txt>] ...[<txt>]\n");
}
/*---------------------------------------------------------------------------*/
static void sighandler(int signum) {
mdnsd_stop(svr);
#ifdef _WIN32
winsock_close();
#endif
exit(0);
}
/*---------------------------------------------------------------------------*/
/* */
/*---------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
const char** txt = NULL;
struct in_addr host;
char hostname[256],* arg, * identity = NULL, * type = NULL, * addr = NULL;
int port = 0;
bool verbose = false;
if (argc <= 2) {
print_usage();
exit(0);
}
#ifdef _WIN32
winsock_init();
#endif
signal(SIGINT, sighandler);
signal(SIGTERM, sighandler);
#if defined(SIGPIPE)
signal(SIGPIPE, SIG_IGN);
#endif
#if defined(SIGQUIT)
signal(SIGQUIT, sighandler);
#endif
#if defined(SIGHUP)
signal(SIGHUP, sighandler);
#endif
while ((arg = *++argv) != NULL) {
if (!strcasecmp(arg, "-o") || !strcasecmp(arg, "host")) {
addr = *++argv;
argc -= 2;
} else if (!strcasecmp(arg, "-p")) {
port = atoi(*++argv);
} else if (!strcasecmp(arg, "-v")) {
verbose = true;
} else if (!strcasecmp(arg, "-t")) {
(void)! asprintf(&type, "%s.local", *++argv);
} else if (!strcasecmp(arg, "-i")) {
identity = *++argv;
} else {
// nothing let's try to be smart and handle legacy crappy
if (!identity) identity = *argv;
else if (!type) (void) !asprintf(&type, "%s.local", *argv);
else if (!port) port = atoi(*argv);
else {
txt = (const char**) malloc((argc + 1) * sizeof(char**));
memcpy(txt, argv, argc * sizeof(char**));
txt[argc] = NULL;
}
argc--;
}
}
gethostname(hostname, sizeof(hostname));
strcat(hostname, ".local");
host = get_interface(addr);
svr = mdnsd_start(host, verbose);
if (svr) {
printf("host: %s\nidentity: %s\ntype: %s\nip: %s\nport: %u\n", hostname, identity, type, inet_ntoa(host), port);
mdnsd_set_hostname(svr, hostname, host);
svc = mdnsd_register_svc(svr, identity, type, port, NULL, txt);
mdns_service_destroy(svc);
#ifdef _WIN32
Sleep(INFINITE);
#else
pause();
#endif
mdnsd_stop(svr);
} else {
printf("Can't start server");
print_usage();
}
free(type);
free(txt);
#ifdef _WIN32
winsock_close();
#endif
return 0;
}

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Sample|Win32">
<Configuration>Sample</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="climdnssvc.c" />
</ItemGroup>
<ItemGroup>
<Library Include="lib\win32\x86\libmdnssvc-$(Configuration).lib" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{8F0D9E3E-E6EC-4086-8334-8F6436852561}</ProjectGuid>
<RootNamespace>tinysvcmdns</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup>
<OutDir>bin\</OutDir>
<IntDir>$(SolutionDir)\build\Win32-$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
</PropertyGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Sample|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4267;4244</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>wsock32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Sample|Win32'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>false</GenerateDebugInformation>
<AdditionalDependencies>wsock32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="mdns.c" />
<ClCompile Include="mdnsd.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{4CB44756-1759-4CC2-B979-BE4E045AB140}</ProjectGuid>
<RootNamespace>tinysvcmdns</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<PropertyGroup Label="Configuration">
<OutDir>$(SolutionDir)\lib\win32\$(PlatformTarget)\</OutDir>
<IntDir>$(SolutionDir)\build\Win32-$(ProjectName)\$(Platform)\$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Configuration)</TargetName>
</PropertyGroup>
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup>
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<DisableSpecificWarnings>4267;4244</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,216 @@
/*
* tinysvcmdns - a tiny MDNS implementation for publishing services
* Copyright (C) 2011 Darell Tan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __MDNS_H__
#define __MDNS_H__
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifdef _WIN32
#include <winsock.h>
#else
#include <arpa/inet.h>
#endif
#define MALLOC_ZERO_STRUCT(x, type) \
x = malloc(sizeof(struct type)); \
memset(x, 0, sizeof(struct type));
#define DECL_MALLOC_ZERO_STRUCT(x, type) \
struct type * MALLOC_ZERO_STRUCT(x, type)
#define DECL_STRUCT(x, type) \
struct type * x;
#define DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME 120
#define DEFAULT_TTL 4500
#ifndef NDEBUG
#define DEBUG_PRINTF(fmt, ...) mdnsd_log(false, fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINTF(...) ((void) 0)
#endif
struct rr_data_srv {
uint16_t priority;
uint16_t weight;
uint16_t port;
uint8_t *target; // host
};
struct rr_data_txt {
struct rr_data_txt *next;
uint8_t *txt;
};
struct rr_data_nsec {
//uint8_t *name; // same as record
// NSEC occupies the 47th bit, 5 bytes
//uint8_t bitmap_len; // = 5
uint8_t bitmap[5]; // network order: first byte contains LSB
};
struct rr_data_ptr {
uint8_t *name; // NULL if entry is to be used
struct rr_entry *entry;
};
struct rr_data_a {
uint32_t addr;
};
struct rr_data_aaaa {
struct in6_addr *addr;
};
typedef enum rr_type {
RR_A = 0x01,
RR_PTR = 0x0C,
RR_TXT = 0x10,
RR_AAAA = 0x1C,
RR_SRV = 0x21,
RR_NSEC = 0x2F,
RR_ANY = 0xFF,
} type;
struct rr_entry {
uint8_t *name;
enum rr_type type;
uint32_t ttl;
char unicast_query;
// for use in Answers only
char cache_flush;
uint16_t rr_class;
// RR data
union {
struct rr_data_nsec NSEC;
struct rr_data_srv SRV;
struct rr_data_txt TXT;
struct rr_data_ptr PTR;
struct rr_data_a A;
struct rr_data_aaaa AAAA;
} data;
};
struct rr_list {
struct rr_entry *e;
struct rr_list *next;
};
struct rr_group {
uint8_t *name;
struct rr_list *rr;
struct rr_group *next;
};
#define MDNS_FLAG_RESP (1 << 15) // Query=0 / Response=1
#define MDNS_FLAG_AA (1 << 10) // Authoritative
#define MDNS_FLAG_TC (1 << 9) // TrunCation
#define MDNS_FLAG_RD (1 << 8) // Recursion Desired
#define MDNS_FLAG_RA (1 << 7) // Recursion Available
#define MDNS_FLAG_Z (1 << 6) // Reserved (zero)
#define MDNS_FLAG_GET_RCODE(x) (x & 0x0F)
#define MDNS_FLAG_GET_OPCODE(x) ((x >> 11) & 0x0F)
// gets the PTR target name, either from "name" member or "entry" member
#define MDNS_RR_GET_PTR_NAME(rr) (rr->data.PTR.name != NULL ? rr->data.PTR.name : rr->data.PTR.entry->name)
struct mdns_pkt {
uint16_t id; // transaction ID
uint16_t flags;
uint16_t num_qn;
uint16_t num_ans_rr;
uint16_t num_auth_rr;
uint16_t num_add_rr;
char unicast;
struct rr_list *rr_qn; // questions
struct rr_list *rr_ans; // answer RRs
struct rr_list *rr_auth; // authority RRs
struct rr_list *rr_add; // additional RRs
};
void mdnsd_log(bool force, char* fmt, ...);
struct mdns_pkt *mdns_parse_pkt(uint8_t *pkt_buf, size_t pkt_len);
void mdns_init_reply(struct mdns_pkt *pkt, uint16_t id);
size_t mdns_encode_pkt(struct mdns_pkt *answer, uint8_t *pkt_buf, size_t pkt_len);
void mdns_pkt_destroy(struct mdns_pkt *p);
void rr_group_destroy(struct rr_group *group);
struct rr_group *rr_group_find(struct rr_group *g, uint8_t *name);
struct rr_entry *rr_entry_find(struct rr_list *rr_list, uint8_t *name, uint16_t type);
struct rr_entry *rr_entry_match(struct rr_list *rr_list, struct rr_entry *entry);
void rr_entry_destroy(struct rr_entry *rr);
struct rr_entry *rr_entry_remove(struct rr_group *group, struct rr_entry *entry, enum rr_type type);
void rr_group_add(struct rr_group **group, struct rr_entry *rr);
void rr_group_clean(struct rr_group **head);
int rr_list_count(struct rr_list *rr);
int rr_list_append(struct rr_list **rr_head, struct rr_entry *rr);
struct rr_entry *rr_list_remove(struct rr_list **rr_head, struct rr_entry *rr);
void rr_list_destroy(struct rr_list *rr, char destroy_items);
struct rr_entry *rr_create_ptr(uint8_t *name, struct rr_entry *d_rr);
struct rr_entry *rr_create_srv(uint8_t *name, uint16_t port, uint8_t *target);
struct rr_entry *rr_create_aaaa(uint8_t *name, struct in6_addr *addr);
struct rr_entry *rr_create_a(uint8_t *name, struct in_addr addr);
struct rr_entry *rr_create(uint8_t *name, enum rr_type type);
void rr_set_nsec(struct rr_entry *rr_nsec, enum rr_type type);
void rr_add_txt(struct rr_entry *rr_txt, const char *txt);
const char *rr_get_type_name(enum rr_type type);
uint8_t *create_label(const char *txt);
uint8_t *create_nlabel(const char *name);
char *nlabel_to_str(const uint8_t *name);
uint8_t *dup_label(const uint8_t *label);
uint8_t *dup_nlabel(const uint8_t *n);
uint8_t *join_nlabel(const uint8_t *n1, const uint8_t *n2);
// compares 2 names
static inline int cmp_nlabel(const uint8_t *L1, const uint8_t *L2) {
return strcmp((char *) L1, (char *) L2);
}
#endif /*!__MDNS_H__*/

View File

@@ -0,0 +1,866 @@
/*
* tinysvcmdns - a tiny MDNS implementation for publishing services
* Copyright (C) 2011 Darell Tan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#define LOG_ERR 3
#else
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <syslog.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <assert.h>
#if __has_include(<pthread.h>)
#include <pthread.h>
#define mutex_lock(m) pthread_mutex_lock(&m);
#define mutex_unlock(m) pthread_mutex_unlock(&m);
#elif _WIN32
#define USE_WIN32_THREAD
#define mutex_lock(m) WaitForSingleObject(m, INFINITE);
#define mutex_unlock(m) ReleaseMutex(m);
#else
#error missing pthread
#endif
#include "mdns.h"
#include "mdnssvc.h"
#if _MSC_VER
#define ssize_t SSIZE_T
#endif
#define MDNS_ADDR "224.0.0.251"
#define MDNS_PORT 5353
#define PACKET_SIZE 65536
#define SERVICES_DNS_SD_NLABEL \
((uint8_t *) "\x09_services\x07_dns-sd\x04_udp\x05local")
#define log_message(l,f,...) mdnsd_log(true, f, ##__VA_ARGS__)
struct mdnsd {
#ifdef USE_WIN32_THREAD
HANDLE data_lock;
#else
pthread_mutex_t data_lock;
#endif
int sockfd;
int notify_pipe[2];
int stop_flag;
struct rr_group *group;
struct rr_list *announce;
struct rr_list *services;
struct rr_list *leave;
uint8_t *hostname;
};
struct mdns_service {
struct rr_list *entries;
};
static bool log_verbose;
/////////////////////////////////
void mdnsd_log(bool force, char* fmt, ...) {
if (force || log_verbose) {
va_list ap;
va_start(ap, fmt);
int size = vsnprintf(NULL, 0, fmt, ap);
if (size > 0) {
char* buf = malloc(size + 1);
vsprintf(buf, fmt, ap);
fprintf(stderr, "%s", buf);
free(buf);
}
va_end(ap);
}
}
static int create_recv_sock(uint32_t host) {
int sd = socket(AF_INET, SOCK_DGRAM, 0);
int r = -1;
int on = 1;
char onChar = 1;
struct sockaddr_in serveraddr;
struct ip_mreq mreq;
unsigned char ttl = 255;
if (sd < 0) {
log_message(LOG_ERR, "recv socket(): %m\n");
return sd;
}
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) {
log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %m\n");
return r;
}
#if !defined(_WIN32)
on = sizeof(on);
socklen_t len;
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, &len)) {
on = 1;
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT,(char*) &on, sizeof(on))) < 0) {
log_message(LOG_ERR, "recv setsockopt(SO_REUSEPORT): %m\n", r);
}
}
#endif
/* bind to an address */
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MDNS_PORT);
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
log_message(LOG_ERR, "recv bind(): %m\n");
return r;
}
memset(&mreq, 0, sizeof(struct ip_mreq));
mreq.imr_interface.s_addr = host;
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (char*) &mreq.imr_interface.s_addr, sizeof(mreq.imr_interface.s_addr))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_PROTO_IP): %m\n");
return r;
}
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, (void*) &ttl, sizeof(ttl))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_IP): %m\n");
return r;
}
// add membership to receiving socket
mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %m\n");
return r;
}
// enable loopback in case someone else needs the data
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &onChar, sizeof(onChar))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %m\n");
return r;
}
#ifdef IP_PKTINFO
on = 1;
if ((r = setsockopt(sd, IPPROTO_IP, IP_PKTINFO, (char *) &on, sizeof(on))) < 0) {
log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %m\n");
return r;
}
#endif
return sd;
}
static ssize_t send_packet(int fd, const void *data, size_t len) {
static struct sockaddr_in toaddr;
if (toaddr.sin_family != AF_INET) {
memset(&toaddr, 0, sizeof(struct sockaddr_in));
toaddr.sin_family = AF_INET;
toaddr.sin_port = htons(MDNS_PORT);
toaddr.sin_addr.s_addr = inet_addr(MDNS_ADDR);
}
return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in));
}
// populate the specified list which matches the RR name and type
// type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) {
int num_ans = 0;
struct rr_group *ans_grp;
struct rr_list *n;
// check if we have the records
mutex_lock(svr->data_lock);
ans_grp = rr_group_find(svr->group, name);
if (ans_grp == NULL) {
mutex_unlock(svr->data_lock);
return num_ans;
}
// decide which records should go into answers
n = ans_grp->rr;
for (; n; n = n->next) {
// exclude NSEC for RR_ANY
if (type == RR_ANY && n->e->type == RR_NSEC)
continue;
if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
num_ans += rr_list_append(rr_head, n->e);
}
}
mutex_unlock(svr->data_lock);
return num_ans;
}
// given a list of RRs, look up related records and add them
static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_pkt *reply) {
for (; list; list = list->next) {
struct rr_entry *ans = list->e;
switch (ans->type) {
case RR_PTR:
// target host A, AAAA records
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
break;
case RR_SRV:
// target host A, AAAA records
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
ans->data.SRV.target, RR_ANY);
// perhaps TXT records of the same name?
// if we use RR_ANY, we risk pulling in the same RR_SRV
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
ans->name, RR_TXT);
break;
case RR_A:
case RR_AAAA:
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
ans->name, RR_NSEC);
break;
default:
// nothing to add
break;
}
}
}
// creates an announce packet given the type name PTR
static void announce_srv(struct mdnsd *svr, struct mdns_pkt *reply, uint8_t *name) {
mdns_init_reply(reply, 0);
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR);
// remember to add the services dns-sd PTR too
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans,
SERVICES_DNS_SD_NLABEL, RR_PTR);
// see if we can match additional records for answers
add_related_rr(svr, reply->rr_ans, reply);
// additional records for additional records
add_related_rr(svr, reply->rr_add, reply);
}
// processes the incoming MDNS packet
// returns >0 if processed, 0 otherwise
static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns_pkt *reply) {
int i;
struct rr_list *qnl;
struct rr_list *ans, *prev_ans;
assert(pkt != NULL);
// is it standard query?
if ((pkt->flags & MDNS_FLAG_RESP) == 0 &&
MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) {
mdns_init_reply(reply, pkt->id);
DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n",
pkt->flags,
pkt->num_qn,
pkt->num_ans_rr,
pkt->num_add_rr);
// loop through questions
qnl = pkt->rr_qn;
for (i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
struct rr_entry *qn = qnl->e;
int num_ans_added = 0;
char *namestr = nlabel_to_str(qn->name);
DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr);
free(namestr);
// mark that a unicast response is desired
reply->unicast |= qn->unicast_query;
num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
reply->num_ans_rr += num_ans_added;
DEBUG_PRINTF("added %d answers\n", num_ans_added);
}
// remove our replies if they were already in their answers
ans = NULL; prev_ans = NULL;
for (ans = reply->rr_ans; ans; ) {
struct rr_list *next_ans = ans->next;
struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
// discard answers that have at least half of the actual TTL
if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
char *namestr = nlabel_to_str(ans->e->name);
DEBUG_PRINTF("removing answer for %s\n", namestr);
free(namestr);
// check if list item is head
if (prev_ans == NULL)
reply->rr_ans = ans->next;
else
prev_ans->next = ans->next;
free(ans);
ans = prev_ans;
// adjust answer count
reply->num_ans_rr--;
}
prev_ans = ans;
ans = next_ans;
}
// see if we can match additional records for answers
add_related_rr(svr, reply->rr_ans, reply);
// additional records for additional records
add_related_rr(svr, reply->rr_add, reply);
DEBUG_PRINTF("\n");
return reply->num_ans_rr;
}
return 0;
}
int create_pipe(int handles[2]) {
#ifdef _WIN32
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serv_addr;
int len;
if (sock == INVALID_SOCKET) {
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(0);
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (bind(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if (listen(sock, 1) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
len = sizeof(serv_addr);
if (getsockname(sock, (SOCKADDR*)&serv_addr, &len) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if ((handles[1] = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
closesocket(sock);
return -1;
}
if (connect(handles[1], (struct sockaddr*)&serv_addr, len) == SOCKET_ERROR) {
closesocket(sock);
return -1;
}
if ((handles[0] = accept(sock, (struct sockaddr*)&serv_addr, &len)) == INVALID_SOCKET) {
closesocket((SOCKET)handles[1]);
handles[1] = INVALID_SOCKET;
closesocket(sock);
return -1;
}
closesocket(sock);
return 0;
#else
return pipe(handles);
#endif
}
int read_pipe(int s, char* buf, int len) {
#ifdef _WIN32
int ret = recv(s, buf, len, 0);
if (ret < 0 && WSAGetLastError() == WSAECONNRESET) {
ret = 0;
}
return ret;
#else
return read(s, buf, len);
#endif
}
int write_pipe(int s, char* buf, int len) {
#ifdef _WIN32
return send(s, buf, len, 0);
#else
return write(s, buf, len);
#endif
}
int close_pipe(int s) {
#ifdef _WIN32
return closesocket(s);
#else
return close(s);
#endif
}
// main loop to receive, process and send out MDNS replies
// also handles MDNS service announces
static void main_loop(struct mdnsd *svr) {
fd_set sockfd_set;
int max_fd = svr->sockfd;
char notify_buf[2]; // buffer for reading of notify_pipe
struct mdns_pkt *mdns_reply;
struct mdns_pkt *mdns;
struct rr_list *svc_le;
void *pkt_buffer = malloc(PACKET_SIZE);
if (svr->notify_pipe[0] > max_fd)
max_fd = svr->notify_pipe[0];
mdns_reply = malloc(sizeof(struct mdns_pkt));
memset(mdns_reply, 0, sizeof(struct mdns_pkt));
while (! svr->stop_flag) {
FD_ZERO(&sockfd_set);
FD_SET(svr->sockfd, &sockfd_set);
FD_SET(svr->notify_pipe[0], &sockfd_set);
select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
// flush the notify_pipe
read_pipe(svr->notify_pipe[0], (char*)&notify_buf, 1);
} else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
struct sockaddr_in fromaddr;
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0,
(struct sockaddr *) &fromaddr, &sockaddr_size);
if (recvsize < 0) {
log_message(LOG_ERR, "recv(): %m\n");
}
DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize);
mdns = mdns_parse_pkt(pkt_buffer, recvsize);
if (mdns != NULL) {
if (process_mdns_pkt(svr, mdns, mdns_reply)) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
if (mdns_reply->unicast) {
int sock = socket(fromaddr.sin_family, SOCK_DGRAM, 0);
sendto(sock, pkt_buffer, replylen, 0, (void*) &fromaddr, sizeof(struct sockaddr_in));
DEBUG_PRINTF("unicast answer\n");
#ifdef _WIN32
closesocket(sock);
#else
close(sock);
#endif
} else {
send_packet(svr->sockfd, pkt_buffer, replylen);
}
} else if (mdns->num_qn == 0) {
DEBUG_PRINTF("(no questions in packet)\n\n");
}
mdns_pkt_destroy(mdns);
}
}
// send out announces
while (1) {
struct rr_entry *ann_e = NULL;
char *namestr;
// extract from head of list
mutex_lock(svr->data_lock);
if (svr->announce)
ann_e = rr_list_remove(&svr->announce, svr->announce->e);
mutex_unlock(svr->data_lock);
if (! ann_e)
break;
namestr = nlabel_to_str(ann_e->name);
DEBUG_PRINTF("sending announce for %s\n", namestr);
free(namestr);
announce_srv(svr, mdns_reply, ann_e->name);
if (mdns_reply->num_ans_rr > 0) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
send_packet(svr->sockfd, pkt_buffer, replylen);
}
}
// send out bye-bye for terminating services
while (1) {
struct rr_entry *leave_e = NULL;
char *namestr;
mutex_lock(svr->data_lock);
if (svr->leave)
leave_e = rr_list_remove(&svr->leave, svr->leave->e);
mutex_unlock(svr->data_lock);
if (!leave_e)
break;
mdns_init_reply(mdns_reply, 0);
namestr = nlabel_to_str(leave_e->name);
DEBUG_PRINTF("sending bye-bye for %s\n", namestr);
free(namestr);
leave_e->ttl = 0;
mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, leave_e);
// send out packet
if (mdns_reply->num_ans_rr > 0) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
send_packet(svr->sockfd, pkt_buffer, replylen);
}
rr_entry_destroy(leave_e->data.PTR.entry);
rr_entry_destroy(leave_e);
}
}
// main thread terminating. send out "goodbye packets" for services
mdns_init_reply(mdns_reply, 0);
mutex_lock(svr->data_lock);
svc_le = svr->services;
for (; svc_le; svc_le = svc_le->next) {
// set TTL to zero
svc_le->e->ttl = 0;
mdns_reply->num_ans_rr += rr_list_append(&mdns_reply->rr_ans, svc_le->e);
}
mutex_unlock(svr->data_lock);
// send out packet
if (mdns_reply->num_ans_rr > 0) {
size_t replylen = mdns_encode_pkt(mdns_reply, pkt_buffer, PACKET_SIZE);
send_packet(svr->sockfd, pkt_buffer, replylen);
}
// destroy packet
mdns_init_reply(mdns_reply, 0);
free(mdns_reply);
free(pkt_buffer);
close_pipe(svr->sockfd);
svr->stop_flag = 2;
}
/////////////////////////////////////////////////////
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, struct in_addr addr) {
struct rr_entry *a_e = NULL,
*nsec_e = NULL;
// currently can't be called twice
// dont ask me what happens if the IP changes
assert(svr->hostname == NULL);
a_e = rr_create_a(create_nlabel(hostname), addr);
nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME;
rr_set_nsec(nsec_e, RR_A);
mutex_lock(svr->data_lock);
svr->hostname = create_nlabel(hostname);
rr_group_add(&svr->group, a_e);
rr_group_add(&svr->group, nsec_e);
mutex_unlock(svr->data_lock);
}
void mdnsd_set_hostname_v6(struct mdnsd *svr, const char *hostname, struct in6_addr *addr) {
struct rr_entry *aaaa_e = NULL, *nsec_e = NULL;
// currently can't be called twice
// dont ask me what happens if the IP changes
assert(svr->hostname == NULL);
aaaa_e = rr_create_aaaa(create_nlabel(hostname), addr); // 120 seconds automatically
nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
nsec_e->ttl = DEFAULT_TTL_FOR_RECORD_WITH_HOSTNAME; // set to 120 seconds (default is 4500)
rr_set_nsec(nsec_e, RR_AAAA);
mutex_lock(svr->data_lock);
svr->hostname = create_nlabel(hostname);
rr_group_add(&svr->group, aaaa_e);
rr_group_add(&svr->group, nsec_e);
mutex_unlock(svr->data_lock);
}
void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
mutex_lock(svr->data_lock);
rr_group_add(&svr->group, rr);
mutex_unlock(svr->data_lock);
}
struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
const char *type, uint16_t port, const char *hostname, const char *txt[]) {
struct rr_entry *txt_e = NULL,
*srv_e = NULL,
*ptr_e = NULL,
*bptr_e = NULL;
uint8_t *target;
uint8_t *inst_nlabel, *type_nlabel, *nlabel;
struct mdns_service *service = malloc(sizeof(struct mdns_service));
memset(service, 0, sizeof(struct mdns_service));
// combine service name
type_nlabel = create_nlabel(type);
inst_nlabel = create_label(instance_name);
nlabel = join_nlabel(inst_nlabel, type_nlabel);
// create TXT record
if (txt && *txt) {
txt_e = rr_create(dup_nlabel(nlabel), RR_TXT);
rr_list_append(&service->entries, txt_e);
// add TXTs
for (; *txt; txt++)
rr_add_txt(txt_e, *txt);
}
// create SRV record
assert(hostname || svr->hostname); // either one as target
target = hostname ?
create_nlabel(hostname) :
dup_nlabel(svr->hostname);
srv_e = rr_create_srv(dup_nlabel(nlabel), port, target);
rr_list_append(&service->entries, srv_e);
// create PTR record for type
ptr_e = rr_create_ptr(type_nlabel, srv_e);
// create services PTR record for type
// this enables the type to show up as a "service"
bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e);
// modify lists here
mutex_lock(svr->data_lock);
if (txt_e)
rr_group_add(&svr->group, txt_e);
rr_group_add(&svr->group, srv_e);
rr_group_add(&svr->group, ptr_e);
rr_group_add(&svr->group, bptr_e);
// append PTR entry to announce list
rr_list_append(&svr->announce, ptr_e);
rr_list_append(&svr->services, ptr_e);
mutex_unlock(svr->data_lock);
// don't free type_nlabel - it's with the PTR record
free(nlabel);
free(inst_nlabel);
// notify server
write_pipe(svr->notify_pipe[1], ".", 1);
return service;
}
void mdns_service_remove(struct mdnsd *svr, struct mdns_service *svc) {
struct rr_list *rr;
assert(svr != NULL && svc != NULL);
// modify lists here
mutex_lock(svr->data_lock);
for (rr = svc->entries; rr; rr = rr->next) {
struct rr_group *g;
struct rr_entry *ptr_e;
// remove entry from groups and destroy entries that are not PTR
if ((g = rr_group_find(svr->group, rr->e->name)) != NULL) {
rr_list_remove(&g->rr, rr->e);
}
// remove PTR and BPTR related to this SVC
if ((ptr_e = rr_entry_remove(svr->group, rr->e, RR_PTR)) != NULL) {
struct rr_entry *bptr_e;
// remove PTR from announce and services
rr_list_remove(&svr->announce, ptr_e);
rr_list_remove(&svr->services, ptr_e);
// find BPTR and remove it from groups
bptr_e = rr_entry_remove(svr->group, ptr_e, RR_PTR);
rr_entry_destroy(bptr_e);
// add PTR to list of announces for leaving
rr_list_append(&svr->leave, ptr_e);
} else {
// destroy entries not needed for sending "leave" packet
rr_entry_destroy(rr->e);
}
}
// remove all empty groups
rr_group_clean(&svr->group);
// destroy this service entries
rr_list_destroy(svc->entries, 0);
free(svc);
mutex_unlock(svr->data_lock);
}
void mdns_service_destroy(struct mdns_service *srv) {
assert(srv != NULL);
rr_list_destroy(srv->entries, 0);
free(srv);
}
struct mdnsd *mdnsd_start(struct in_addr host, bool verbose) {
#ifndef USE_WIN32_THREAD
pthread_t tid;
pthread_attr_t attr;
#endif
log_verbose = verbose;
struct mdnsd *server = malloc(sizeof(struct mdnsd));
memset(server, 0, sizeof(struct mdnsd));
if (create_pipe(server->notify_pipe) != 0) {
log_message(LOG_ERR, "pipe(): %m\n");
free(server);
return NULL;
}
server->sockfd = create_recv_sock(host.s_addr);
if (server->sockfd < 0) {
log_message(LOG_ERR, "unable to create recv socket\n");
free(server);
return NULL;
}
#ifdef USE_WIN32_THREAD
server->data_lock = CreateMutex(NULL, FALSE, NULL);
#else
pthread_mutex_init(&server->data_lock, NULL);
#endif
#ifdef USE_WIN32_THREAD
if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) main_loop, (void*) server, 0, NULL) == NULL) {
CloseHandle(server->data_lock);
#else
// init thread
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (pthread_create(&tid, &attr, (void *(*)(void *)) main_loop, (void *) server) != 0) {
pthread_mutex_destroy(&server->data_lock);
#endif
free(server);
return NULL;
}
return server;
}
void mdnsd_stop(struct mdnsd *s) {
struct timeval tv;
if (!s) return;
tv.tv_sec = 0;
tv.tv_usec = 500*1000;
assert(s != NULL);
s->stop_flag = 1;
write_pipe(s->notify_pipe[1], ".", 1);
while (s->stop_flag != 2)
select(0, NULL, NULL, NULL, &tv);
close_pipe(s->notify_pipe[0]);
close_pipe(s->notify_pipe[1]);
#ifdef USE_WIN32_THREAD
CloseHandle(s->data_lock);
#else
pthread_mutex_destroy(&s->data_lock);
#endif
rr_group_destroy(s->group);
rr_list_destroy(s->announce, 0);
rr_list_destroy(s->services, 0);
rr_list_destroy(s->leave, 0);
if (s->hostname)
free(s->hostname);
free(s);
}

View File

@@ -0,0 +1,73 @@
/*
* tinysvcmdns - a tiny MDNS implementation for publishing services
* Copyright (C) 2011 Darell Tan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __TINYSVCMDNS_H__
#define __TINYSVCMDNS_H__
#include <stdint.h>
#include <stdbool.h>
#ifdef _WIN32
#include <inaddr.h>
#else
#include <netinet/in.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct mdnsd;
struct mdns_service;
// starts a MDNS responder instance
// returns NULL if unsuccessful
struct mdnsd *mdnsd_start(struct in_addr host, bool verbose);
// stops the given MDNS responder instance
void mdnsd_stop(struct mdnsd *s);
// sets the hostname for the given MDNS responder instance
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, struct in_addr addr);
// registers a service with the MDNS responder instance
struct mdns_service *mdnsd_register_svc(struct mdnsd *svr, const char *instance_name,
const char *type, uint16_t port, const char *hostname, const char *txt[]);
// destroys the mdns_service struct returned by mdnsd_register_svc()
void mdns_service_destroy(struct mdns_service *srv);
// remove AND destroys the mdns_service struct returned by mdnsd_register_svc()
void mdns_service_remove(struct mdnsd *svr, struct mdns_service *svc);
#ifdef __cplusplus
}
#endif
#endif/*!__MDNSD_H__*/

View File

@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmdnssvc", "libmdnssvc.vcxproj", "{4CB44756-1759-4CC2-B979-BE4E045AB140}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "climdnssvc", "climdnssvc.vcxproj", "{8F0D9E3E-E6EC-4086-8334-8F6436852561}"
ProjectSection(ProjectDependencies) = postProject
{4CB44756-1759-4CC2-B979-BE4E045AB140} = {4CB44756-1759-4CC2-B979-BE4E045AB140}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4CB44756-1759-4CC2-B979-BE4E045AB140}.Debug|x86.ActiveCfg = Debug|Win32
{4CB44756-1759-4CC2-B979-BE4E045AB140}.Debug|x86.Build.0 = Debug|Win32
{4CB44756-1759-4CC2-B979-BE4E045AB140}.Release|x86.ActiveCfg = Release|Win32
{4CB44756-1759-4CC2-B979-BE4E045AB140}.Release|x86.Build.0 = Release|Win32
{8F0D9E3E-E6EC-4086-8334-8F6436852561}.Debug|x86.ActiveCfg = Debug|Win32
{8F0D9E3E-E6EC-4086-8334-8F6436852561}.Debug|x86.Build.0 = Debug|Win32
{8F0D9E3E-E6EC-4086-8334-8F6436852561}.Release|x86.ActiveCfg = Release|Win32
{8F0D9E3E-E6EC-4086-8334-8F6436852561}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C7906313-D2F2-419B-B3D7-798E938180CC}
EndGlobalSection
EndGlobal