├── test ├── all │ ├── tizen │ │ ├── res │ │ │ └── tizenapp.png │ │ └── tizen-manifest.xml │ ├── android │ │ ├── drawable-hdpi │ │ │ └── icon.png │ │ ├── drawable-ldpi │ │ │ └── icon.png │ │ ├── drawable-mdpi │ │ │ └── icon.png │ │ ├── drawable-xhdpi │ │ │ └── icon.png │ │ ├── drawable-xxhdpi │ │ │ └── icon.png │ │ ├── drawable-xxxhdpi │ │ │ └── icon.png │ │ ├── values │ │ │ └── strings.xml │ │ ├── layout │ │ │ └── main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── rampantpixels │ │ │ └── foundation │ │ │ └── test │ │ │ └── TestActivity.java │ ├── ios │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── icon_100.png │ │ │ │ ├── icon_114.png │ │ │ │ ├── icon_120.png │ │ │ │ ├── icon_144.png │ │ │ │ ├── icon_152.png │ │ │ │ ├── icon_180.png │ │ │ │ ├── icon_29.png │ │ │ │ ├── icon_40.png │ │ │ │ ├── icon_50.png │ │ │ │ ├── icon_57.png │ │ │ │ ├── icon_58.png │ │ │ │ ├── icon_72.png │ │ │ │ ├── icon_76.png │ │ │ │ ├── icon_80.png │ │ │ │ ├── icon_29-1.png │ │ │ │ ├── icon_58-1.png │ │ │ │ ├── icon_80-1.png │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.launchimage │ │ │ │ ├── launch_1024_748.png │ │ │ │ ├── launch_1024_768.png │ │ │ │ ├── launch_320_480.png │ │ │ │ ├── launch_640_1136.png │ │ │ │ ├── launch_640_960.png │ │ │ │ ├── launch_768_1004.png │ │ │ │ ├── launch_768_1024.png │ │ │ │ ├── launch_1536_2008.png │ │ │ │ ├── launch_1536_2048.png │ │ │ │ ├── launch_2048_1496.png │ │ │ │ ├── launch_2048_1536.png │ │ │ │ └── Contents.json │ │ ├── viewcontroller.h │ │ ├── viewcontroller.m │ │ ├── test-all.plist │ │ └── test-all.xib │ └── main.c └── dnssd │ └── main.c ├── mdns ├── hashstrings.txt ├── version.c ├── hashstrings.h ├── service.h ├── build.h ├── socket.h ├── discovery.h ├── mdns.h ├── string.h ├── record.h ├── socket.c ├── service.c ├── mdns.c ├── types.h ├── query.h ├── discovery.c ├── record.c ├── string.c └── query.c ├── README.md ├── .gitattributes ├── LICENSE ├── configure.py ├── .gitignore └── tools └── mdns └── main.c /test/all/tizen/res/tizenapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/tizen/res/tizenapp.png -------------------------------------------------------------------------------- /mdns/hashstrings.txt: -------------------------------------------------------------------------------- 1 | 2 | HASH_MDNS mdns 3 | HASH_DNSSD dns-sd 4 | -------------------------------------------------------------------------------- /test/all/android/drawable-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-hdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-ldpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-mdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-xhdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-xxhdpi/icon.png -------------------------------------------------------------------------------- /test/all/android/drawable-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/android/drawable-xxxhdpi/icon.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_100.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_114.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_120.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_144.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_152.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_180.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_40.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_50.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_57.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_72.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_76.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80.png -------------------------------------------------------------------------------- /test/all/android/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Foundation Test Suite 4 | 5 | -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_29-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_58-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/AppIcon.appiconset/icon_80-1.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_748.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_748.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_768.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1024_768.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_320_480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_320_480.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_1136.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_1136.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_960.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_640_960.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1004.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_768_1024.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2008.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2008.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_1536_2048.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1496.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1496.png -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1536.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mjansson/mdns_lib/HEAD/test/all/ios/Images.xcassets/LaunchImage.launchimage/launch_2048_1536.png -------------------------------------------------------------------------------- /mdns/version.c: -------------------------------------------------------------------------------- 1 | /* ****** AUTOMATICALLY GENERATED, DO NOT EDIT ****** 2 | This file is generated from the git describe command. 3 | Run the configure script to regenerate this file */ 4 | 5 | #include 6 | #include 7 | 8 | version_t 9 | mdns_module_version(void) { 10 | return version_make(0, 0, 1, 0, 0x0); 11 | } 12 | -------------------------------------------------------------------------------- /mdns/hashstrings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* ****** AUTOMATICALLY GENERATED, DO NOT EDIT ****** 6 | Edit corresponding definitions file and rerun 7 | the foundation hashify tool to update this file */ 8 | 9 | #define HASH_MDNS static_hash_string("mdns", 4, 0x6965a0217618e2acULL) 10 | #define HASH_DNSSD static_hash_string("dns-sd", 6, 0xf242950108a12389ULL) 11 | -------------------------------------------------------------------------------- /test/all/android/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/all/tizen/tizen-manifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tizenapp.png 6 | 7 | 8 | 9 | http://tizen.org/privilege/systemsettings 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mDNS Library - Public Domain 2 | 3 | This library provides a cross-platform mDNS and DNS-DS library in C based 4 | on our foundation and network libraries. 5 | 6 | The latest source code maintained by Mattias Jansson is always available at 7 | 8 | https://github.com/rampantpixels/mdns_lib 9 | 10 | The foundation and network library source code maintained by Mattias Jansson 11 | is always available at 12 | 13 | https://github.com/rampantpixels/foundation_lib 14 | https://github.com/rampantpixels/network_lib 15 | 16 | This library is put in the public domain; you can redistribute it and/or 17 | modify it without any restrictions. 18 | 19 | -------------------------------------------------------------------------------- /test/all/ios/viewcontroller.h: -------------------------------------------------------------------------------- 1 | /* viewcontroller.h - Foundation test launcher - Public Domain - 2013 Mattias Jansson / Rampant Pixels 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/rampantpixels/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #pragma once 14 | 15 | #include 16 | #include 17 | 18 | #ifdef __OBJC__ 19 | 20 | @interface ViewController : UIViewController { 21 | @public 22 | } 23 | @end 24 | 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Never mangle resource blob files 5 | *.blob -text 6 | 7 | # Custom for Visual Studio 8 | *.cs diff=csharp 9 | *.sln merge=union 10 | *.csproj merge=union 11 | *.vbproj merge=union 12 | *.fsproj merge=union 13 | *.dbproj merge=union 14 | 15 | # Standard to msysgit 16 | *.doc diff=astextplain 17 | *.DOC diff=astextplain 18 | *.docx diff=astextplain 19 | *.DOCX diff=astextplain 20 | *.dot diff=astextplain 21 | *.DOT diff=astextplain 22 | *.pdf diff=astextplain 23 | *.PDF diff=astextplain 24 | *.rtf diff=astextplain 25 | *.RTF diff=astextplain 26 | 27 | # Ignore build scripts in language stats 28 | build/* linguist-vendored 29 | test/* linguist-vendored 30 | *.h linguist-language=C 31 | configure.py linguist-vendored=true 32 | -------------------------------------------------------------------------------- /test/all/android/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mdns/service.h: -------------------------------------------------------------------------------- 1 | /* service.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | //! Service incoming multicast DNS-SD and mDNS query requests. The socket should have been bound to port MDNS_PORT using 30 | //! mdns_socket_bind. Buffer must be 32 bit aligned. Returns the number of queries parsed. 31 | MDNS_API size_t 32 | mdns_service_listen(socket_t* socket, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /mdns/build.h: -------------------------------------------------------------------------------- 1 | /* build.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #if defined(MDNS_COMPILE) && MDNS_COMPILE 27 | #ifdef __cplusplus 28 | #define MDNS_EXTERN extern "C" 29 | #define MDNS_API extern "C" 30 | #else 31 | #define MDNS_EXTERN extern 32 | #define MDNS_API extern 33 | #endif 34 | #else 35 | #ifdef __cplusplus 36 | #define MDNS_EXTERN extern "C" 37 | #define MDNS_API extern "C" 38 | #else 39 | #define MDNS_EXTERN extern 40 | #define MDNS_API extern 41 | #endif 42 | #endif 43 | 44 | #define MDNS_QUERY_SIZE_DEFAULT 512 45 | #define MDNS_DISCOVERY_SIZE_DEFAULT 512 46 | -------------------------------------------------------------------------------- /test/all/ios/viewcontroller.m: -------------------------------------------------------------------------------- 1 | /* viewcontroller.m - Foundation test launcher - Public Domain - 2013 Mattias Jansson / Rampant Pixels 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/rampantpixels/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #include "viewcontroller.h" 14 | 15 | #if FOUNDATION_COMPILER_CLANG 16 | # if __has_warning("-Wpartial-availability") 17 | # pragma clang diagnostic ignored "-Wpartial-availability" 18 | # endif 19 | #endif 20 | 21 | @interface ViewController() 22 | @end 23 | 24 | @implementation ViewController 25 | 26 | - (void)viewDidLoad { 27 | [super viewDidLoad]; 28 | 29 | if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) 30 | [self setNeedsStatusBarAppearanceUpdate]; 31 | //iOS pre-9.0 - else 32 | // [[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationNone]; 33 | } 34 | 35 | - (void)didReceiveMemoryWarning { 36 | [super didReceiveMemoryWarning]; 37 | } 38 | 39 | - (BOOL)prefersStatusBarHidden { 40 | return TRUE; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /mdns/socket.h: -------------------------------------------------------------------------------- 1 | /* socket.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | //! Bind a socket for mDNS/DNS-SD. To bind the socket to a specific interface, pass in the appropriate socket address, 30 | //! otherwise use IPv4 INADDR_ANY or IPv6 in6addr_any. To send one-shot discovery requests and queries set 0 as port to 31 | //! assign a random user level ephemeral port. To run discovery service listening for incoming discoveries and queries, 32 | //! you must set MDNS_PORT as port. 33 | MDNS_API bool 34 | mdns_socket_bind(socket_t* socket, const network_address_t* address); 35 | -------------------------------------------------------------------------------- /mdns/discovery.h: -------------------------------------------------------------------------------- 1 | /* discovery.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | //! Send a multicast DNS-SD reqeuest on the given socket to discover available services. Returns 30 | // 0 on success, or <0 if error. 31 | MDNS_API int 32 | mdns_discovery_send(socket_t* sock); 33 | 34 | //! Recieve unicast responses to a DNS-SD sent with mdns_discovery_send. Any data will be piped to 35 | // the given callback for parsing. Buffer must be 32 bit aligned. Returns the number of 36 | // responses parsed. 37 | MDNS_API size_t 38 | mdns_discovery_recv(socket_t* sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data); 39 | -------------------------------------------------------------------------------- /test/all/ios/test-all.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Foundation 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | TEST 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSMainNibFile 26 | test-all 27 | NSMainNibFile~ipad 28 | test-all 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarHidden 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | UIInterfaceOrientationPortraitUpsideDown 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /mdns/mdns.h: -------------------------------------------------------------------------------- 1 | /* mdns.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | MDNS_API int 37 | mdns_module_initialize(const mdns_config_t config); 38 | 39 | MDNS_API void 40 | mdns_module_finalize(void); 41 | 42 | MDNS_API bool 43 | mdns_module_is_initialized(void); 44 | 45 | MDNS_API version_t 46 | mdns_module_version(void); 47 | 48 | MDNS_API int 49 | mdns_unicast_send(socket_t* sock, const network_address_t* to, const void* buffer, size_t size); 50 | 51 | MDNS_API int 52 | mdns_multicast_send(socket_t* sock, const void* buffer, size_t size); 53 | 54 | MDNS_API uint16_t 55 | mdns_ntohs(const void* data); 56 | 57 | MDNS_API uint32_t 58 | mdns_ntohl(const void* data); 59 | 60 | MDNS_API void* 61 | mdns_htons(void* data, uint16_t val); 62 | 63 | MDNS_API void* 64 | mdns_htonl(void* data, uint32_t val); 65 | -------------------------------------------------------------------------------- /mdns/string.h: -------------------------------------------------------------------------------- 1 | /* string.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include 27 | 28 | MDNS_API string_const_t 29 | mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity); 30 | 31 | MDNS_API int 32 | mdns_string_skip(const void* buffer, size_t size, size_t* offset); 33 | 34 | MDNS_API int 35 | mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, size_t size_rhs, 36 | size_t* ofs_rhs); 37 | 38 | MDNS_API void* 39 | mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, 40 | mdns_string_table_t* string_table); 41 | 42 | MDNS_API void* 43 | mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset); 44 | 45 | MDNS_API void 46 | mdns_string_table_add(mdns_string_table_t* string_table, size_t offset); 47 | 48 | MDNS_API size_t 49 | mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, const char* str, 50 | size_t first_length, size_t total_length); 51 | -------------------------------------------------------------------------------- /mdns/record.h: -------------------------------------------------------------------------------- 1 | /* record.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | MDNS_API string_const_t 30 | mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); 31 | 32 | MDNS_API mdns_record_srv_t 33 | mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity); 34 | 35 | MDNS_API network_address_ipv4_t* 36 | mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, network_address_ipv4_t* addr); 37 | 38 | MDNS_API network_address_ipv6_t* 39 | mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, network_address_ipv6_t* addr); 40 | 41 | MDNS_API size_t 42 | mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, 43 | size_t capacity); 44 | 45 | MDNS_API size_t 46 | mdns_records_parse(socket_t* sock, const network_address_t* from, const void* buffer, size_t size, size_t* offset, 47 | mdns_entry_type_t type, uint16_t query_id, size_t records, mdns_record_callback_fn callback, 48 | void* user_data); 49 | -------------------------------------------------------------------------------- /test/all/android/java/com/rampantpixels/foundation/test/TestActivity.java: -------------------------------------------------------------------------------- 1 | package com.rampantpixels.foundation.test; 2 | 3 | import android.os.Bundle; 4 | import android.app.NativeActivity; 5 | import android.graphics.Color; 6 | import android.graphics.Point; 7 | import android.widget.TextView; 8 | import android.util.Log; 9 | import android.widget.LinearLayout; 10 | import android.widget.PopupWindow; 11 | import android.view.Gravity; 12 | import android.view.Display; 13 | import android.view.ViewGroup; 14 | import android.view.ViewGroup.LayoutParams; 15 | import android.view.ViewGroup.MarginLayoutParams; 16 | 17 | public class TestActivity extends NativeActivity 18 | { 19 | private TextView textView; 20 | private boolean displayedTextView = false; 21 | 22 | @Override 23 | public void onWindowFocusChanged( boolean hasFocus ) 24 | { 25 | super.onWindowFocusChanged( hasFocus ); 26 | 27 | if( !displayedTextView && hasFocus ) 28 | { 29 | displayedTextView = true; 30 | 31 | setContentView( R.layout.main ); 32 | 33 | textView = (TextView)findViewById( R.id.logtext ); 34 | textView.setText( "" ); 35 | ((ViewGroup)textView.getParent()).removeView(textView); 36 | 37 | final TestActivity activity = this; 38 | 39 | runOnUiThread( new Runnable() { 40 | 41 | @Override 42 | public void run() 43 | { 44 | PopupWindow popup = new PopupWindow( activity ); 45 | 46 | Display display = getWindowManager().getDefaultDisplay(); 47 | Point size = new Point(); 48 | display.getSize( size ); 49 | 50 | popup.setWidth( size.x ); 51 | popup.setHeight( size.y ); 52 | popup.setWindowLayoutMode( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ); 53 | popup.setClippingEnabled( false ); 54 | 55 | MarginLayoutParams params = new MarginLayoutParams( LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT ); 56 | params.setMargins(0, 0, 0, 0); 57 | 58 | LinearLayout layout = new LinearLayout( activity ); 59 | layout.setOrientation( LinearLayout.VERTICAL ); 60 | layout.addView( activity.textView, params ); 61 | 62 | popup.setContentView( layout ); 63 | 64 | final ViewGroup viewGroup = (ViewGroup)((ViewGroup)activity.findViewById( android.R.id.content )).getChildAt(0); 65 | 66 | popup.showAtLocation( viewGroup, Gravity.TOP, 0, 0 ); 67 | popup.update(); 68 | } 69 | } ); 70 | } 71 | } 72 | 73 | public void appendLog( final String msg ) 74 | { 75 | runOnUiThread( new Runnable() { 76 | @Override 77 | public void run() 78 | { 79 | if( textView != null ) 80 | textView.append( msg ); 81 | } 82 | } ); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "29x29", 5 | "idiom" : "iphone", 6 | "filename" : "icon_29.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "29x29", 11 | "idiom" : "iphone", 12 | "filename" : "icon_58.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "40x40", 17 | "idiom" : "iphone", 18 | "filename" : "icon_80-1.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "57x57", 23 | "idiom" : "iphone", 24 | "filename" : "icon_57.png", 25 | "scale" : "1x" 26 | }, 27 | { 28 | "size" : "57x57", 29 | "idiom" : "iphone", 30 | "filename" : "icon_114.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "icon_120.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "icon_180.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "29x29", 47 | "idiom" : "ipad", 48 | "filename" : "icon_29-1.png", 49 | "scale" : "1x" 50 | }, 51 | { 52 | "size" : "29x29", 53 | "idiom" : "ipad", 54 | "filename" : "icon_58-1.png", 55 | "scale" : "2x" 56 | }, 57 | { 58 | "size" : "40x40", 59 | "idiom" : "ipad", 60 | "filename" : "icon_40.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "40x40", 65 | "idiom" : "ipad", 66 | "filename" : "icon_80.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "50x50", 71 | "idiom" : "ipad", 72 | "filename" : "icon_50.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "50x50", 77 | "idiom" : "ipad", 78 | "filename" : "icon_100.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "72x72", 83 | "idiom" : "ipad", 84 | "filename" : "icon_72.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "72x72", 89 | "idiom" : "ipad", 90 | "filename" : "icon_144.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "icon_76.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "icon_152.png", 103 | "scale" : "2x" 104 | } 105 | ], 106 | "info" : { 107 | "version" : 1, 108 | "author" : "xcode" 109 | } 110 | } -------------------------------------------------------------------------------- /test/all/ios/test-all.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /mdns/socket.c: -------------------------------------------------------------------------------- 1 | /* socket.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | bool 27 | mdns_socket_bind(socket_t* sock, const network_address_t* address) { 28 | if (socket_type(sock) != NETWORK_SOCKETTYPE_UDP) 29 | return false; 30 | if (sock->state != SOCKETSTATE_NOTCONNECTED) 31 | return false; 32 | if (!address) 33 | return false; 34 | 35 | socket_set_reuse_address(sock, true); 36 | socket_set_reuse_port(sock, true); 37 | socket_set_blocking(sock, false); 38 | 39 | if (sock->fd < 0) 40 | sock->family = address->family; 41 | 42 | if (!socket_create(sock)) 43 | return false; 44 | 45 | network_address_t multicast_addr; 46 | if (sock->family == NETWORK_ADDRESSFAMILY_IPV4) { 47 | network_address_ipv4_initialize((network_address_ipv4_t*)&multicast_addr); 48 | network_address_ipv4_set_ip(&multicast_addr, (((uint32_t)224U) << 24U) | (uint32_t)251U); 49 | if (!socket_set_multicast_group(sock, &multicast_addr, address, true)) { 50 | //log_error(HASH_MDNS, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Failed to set multicast group on mDNS socket")); 51 | return false; 52 | } 53 | if (!address) { 54 | network_address_ipv4_set_ip(&multicast_addr, INADDR_ANY); 55 | address = &multicast_addr; 56 | } 57 | } else if (sock->family == NETWORK_ADDRESSFAMILY_IPV6) { 58 | network_address_ipv6_initialize((network_address_ipv6_t*)&multicast_addr); 59 | struct in6_addr ip = {0}; 60 | ip.s6_addr[0] = 0xFF; 61 | ip.s6_addr[1] = 0x02; 62 | ip.s6_addr[15] = 0xFB; 63 | network_address_ipv6_set_ip(&multicast_addr, ip); 64 | if (!socket_set_multicast_group(sock, &multicast_addr, address, true)) { 65 | //log_error(HASH_MDNS, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Failed to set multicast group on mDNS socket")); 66 | return false; 67 | } 68 | if (!address) { 69 | network_address_ipv6_set_ip(&multicast_addr, in6addr_any); 70 | address = &multicast_addr; 71 | } 72 | } else { 73 | return false; 74 | } 75 | 76 | if (!socket_bind(sock, address)) { 77 | log_error(HASH_MDNS, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Failed to bind mDNS socket")); 78 | return false; 79 | } 80 | 81 | return true; 82 | } 83 | -------------------------------------------------------------------------------- /test/all/ios/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "filename" : "launch_640_960.png", 9 | "scale" : "2x" 10 | }, 11 | { 12 | "extent" : "full-screen", 13 | "idiom" : "iphone", 14 | "subtype" : "retina4", 15 | "filename" : "launch_640_1136.png", 16 | "minimum-system-version" : "7.0", 17 | "orientation" : "portrait", 18 | "scale" : "2x" 19 | }, 20 | { 21 | "orientation" : "portrait", 22 | "idiom" : "ipad", 23 | "extent" : "full-screen", 24 | "minimum-system-version" : "7.0", 25 | "filename" : "launch_768_1024.png", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "orientation" : "landscape", 30 | "idiom" : "ipad", 31 | "extent" : "full-screen", 32 | "minimum-system-version" : "7.0", 33 | "filename" : "launch_1024_768.png", 34 | "scale" : "1x" 35 | }, 36 | { 37 | "orientation" : "portrait", 38 | "idiom" : "ipad", 39 | "extent" : "full-screen", 40 | "minimum-system-version" : "7.0", 41 | "filename" : "launch_1536_2048.png", 42 | "scale" : "2x" 43 | }, 44 | { 45 | "orientation" : "landscape", 46 | "idiom" : "ipad", 47 | "extent" : "full-screen", 48 | "minimum-system-version" : "7.0", 49 | "filename" : "launch_2048_1536.png", 50 | "scale" : "2x" 51 | }, 52 | { 53 | "orientation" : "portrait", 54 | "idiom" : "iphone", 55 | "extent" : "full-screen", 56 | "filename" : "launch_320_480.png", 57 | "scale" : "1x" 58 | }, 59 | { 60 | "orientation" : "portrait", 61 | "idiom" : "iphone", 62 | "extent" : "full-screen", 63 | "filename" : "launch_640_960.png", 64 | "scale" : "2x" 65 | }, 66 | { 67 | "orientation" : "portrait", 68 | "idiom" : "iphone", 69 | "extent" : "full-screen", 70 | "filename" : "launch_640_1136.png", 71 | "subtype" : "retina4", 72 | "scale" : "2x" 73 | }, 74 | { 75 | "orientation" : "portrait", 76 | "idiom" : "ipad", 77 | "extent" : "to-status-bar", 78 | "filename" : "launch_768_1004.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "orientation" : "landscape", 83 | "idiom" : "ipad", 84 | "extent" : "to-status-bar", 85 | "filename" : "launch_1024_748.png", 86 | "scale" : "1x" 87 | }, 88 | { 89 | "orientation" : "portrait", 90 | "idiom" : "ipad", 91 | "extent" : "to-status-bar", 92 | "filename" : "launch_1536_2008.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "orientation" : "landscape", 97 | "idiom" : "ipad", 98 | "extent" : "to-status-bar", 99 | "filename" : "launch_2048_1496.png", 100 | "scale" : "2x" 101 | } 102 | ], 103 | "info" : { 104 | "version" : 1, 105 | "author" : "xcode" 106 | } 107 | } -------------------------------------------------------------------------------- /configure.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """Ninja build configurator for mdns library""" 4 | 5 | import sys 6 | import os 7 | 8 | sys.path.insert( 0, os.path.join( 'build', 'ninja' ) ) 9 | 10 | import generator 11 | 12 | dependlibs = [ 'mdns', 'network', 'foundation' ] 13 | 14 | generator = generator.Generator( project = 'mdns', dependlibs = dependlibs, variables = [ ( 'bundleidentifier', 'com.rampantpixels.mdns.$(binname)' ) ] ) 15 | target = generator.target 16 | writer = generator.writer 17 | toolchain = generator.toolchain 18 | 19 | mdns_lib = generator.lib( module = 'mdns', sources = [ 20 | 'discovery.c', 'mdns.c', 'query.c', 'record.c', 'service.c', 'socket.c', 'string.c', 'version.c' ] ) 21 | 22 | if generator.skip_tests(): 23 | sys.exit() 24 | 25 | includepaths = generator.test_includepaths() 26 | 27 | extralibs = [] 28 | if target.is_windows(): 29 | extralibs += ['iphlpapi', 'ws2_32'] 30 | 31 | if not target.is_ios() and not target.is_android(): 32 | configs = [ config for config in toolchain.configs if config not in [ 'profile', 'deploy' ] ] 33 | if not configs == []: 34 | generator.bin( 'mdns', [ 'main.c' ], 'mdns', basepath = 'tools', implicit_deps = [ mdns_lib ], libs = dependlibs + extralibs, configs = configs ) 35 | 36 | test_cases = [ 37 | 'dnssd' 38 | ] 39 | if toolchain.is_monolithic() or target.is_ios() or target.is_android() or target.is_tizen(): 40 | #Build one fat binary with all test cases 41 | test_resources = [] 42 | test_extrasources = [] 43 | test_cases += ['all'] 44 | if target.is_ios(): 45 | test_resources = [os.path.join('all', 'ios', item) for item in ['test-all.plist', 'Images.xcassets', 'test-all.xib']] 46 | test_extrasources = [os.path.join('all', 'ios', 'viewcontroller.m')] 47 | elif target.is_android(): 48 | test_resources = [os.path.join( 'all', 'android', item) for item in [ 49 | 'AndroidManifest.xml', os.path.join('layout', 'main.xml'), os.path.join('values', 'strings.xml'), 50 | os.path.join('drawable-ldpi', 'icon.png'), os.path.join('drawable-mdpi', 'icon.png'), os.path.join('drawable-hdpi', 'icon.png'), 51 | os.path.join('drawable-xhdpi', 'icon.png'), os.path.join('drawable-xxhdpi', 'icon.png'), os.path.join('drawable-xxxhdpi', 'icon.png') 52 | ]] 53 | test_extrasources = [os.path.join('all', 'android', 'java', 'com', 'maniccoder', 'foundation', 'test', item) for item in [ 54 | 'TestActivity.java' 55 | ]] 56 | dependlibs = ['test'] + dependlibs 57 | if target.is_macos() or target.is_ios() or target.is_android() or target.is_tizen(): 58 | generator.app(module = '', sources = [os.path.join(module, 'main.c') for module in test_cases] + test_extrasources, binname = 'test-mdns', basepath = 'test', implicit_deps = [mdns_lib], libs = dependlibs, dependlibs = dependlibs, resources = test_resources, includepaths = includepaths) 59 | else: 60 | generator.bin(module = '', sources = [os.path.join(module, 'main.c') for module in test_cases] + test_extrasources, binname = 'test-mdns', basepath = 'test', implicit_deps = [mdns_lib], libs = dependlibs, dependlibs = dependlibs, resources = test_resources, includepaths = includepaths) 61 | else: 62 | #Build one binary per test case 63 | if not generator.is_subninja: 64 | generator.bin(module = 'all', sources = ['main.c'], binname = 'test-all', basepath = 'test', implicit_deps = [mdns_lib], libs = dependlibs + extralibs, dependlibs = dependlibs, includepaths = includepaths) 65 | dependlibs = ['test'] + dependlibs 66 | for test in test_cases: 67 | generator.bin(module = test, sources = ['main.c'], binname = 'test-' + test, basepath = 'test', implicit_deps = [mdns_lib], libs = dependlibs + extralibs, dependlibs = dependlibs, includepaths = includepaths) 68 | -------------------------------------------------------------------------------- /mdns/service.c: -------------------------------------------------------------------------------- 1 | /* service.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | 24 | #include 25 | 26 | extern const uint8_t mdns_services_query[46]; 27 | 28 | size_t 29 | mdns_service_listen(socket_t* sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { 30 | const network_address_t* addr = 0; 31 | size_t data_size = udp_socket_recvfrom(sock, buffer, capacity, &addr); 32 | if (!data_size) 33 | return 0; 34 | 35 | const uint16_t* data = (const uint16_t*)buffer; 36 | 37 | uint16_t query_id = mdns_ntohs(data++); 38 | uint16_t flags = mdns_ntohs(data++); 39 | uint16_t questions = mdns_ntohs(data++); 40 | uint16_t answer_rrs = mdns_ntohs(data++); 41 | uint16_t authority_rrs = mdns_ntohs(data++); 42 | uint16_t additional_rrs = mdns_ntohs(data++); 43 | 44 | size_t records; 45 | size_t total_records = 0; 46 | for (int iquestion = 0; iquestion < questions; ++iquestion) { 47 | size_t question_offset = (size_t)pointer_diff(data, buffer); 48 | size_t offset = question_offset; 49 | size_t verify_offset = 12; 50 | int dns_sd = 0; 51 | if (mdns_string_equal(buffer, data_size, &offset, mdns_services_query, sizeof(mdns_services_query), 52 | &verify_offset)) { 53 | dns_sd = 1; 54 | } else { 55 | offset = question_offset; 56 | if (!mdns_string_skip(buffer, data_size, &offset)) 57 | break; 58 | } 59 | size_t length = offset - question_offset; 60 | data = pointer_offset_const(buffer, offset); 61 | 62 | uint16_t rtype = mdns_ntohs(data++); 63 | uint16_t rclass = mdns_ntohs(data++); 64 | uint16_t class_without_flushbit = rclass & ~MDNS_CACHE_FLUSH; 65 | 66 | // Make sure we get a question of class IN 67 | if (!((class_without_flushbit == MDNS_CLASS_IN) || (class_without_flushbit == MDNS_CLASS_ANY))) 68 | return 0; 69 | if (dns_sd && flags) 70 | continue; 71 | 72 | ++total_records; 73 | if (callback && callback(sock, addr, MDNS_ENTRYTYPE_QUESTION, query_id, rtype, rclass, 0, buffer, data_size, 74 | question_offset, length, question_offset, length, user_data)) 75 | return total_records; 76 | } 77 | 78 | size_t offset = (size_t)pointer_diff(data, buffer); 79 | records = mdns_records_parse(sock, addr, buffer, data_size, &offset, 80 | MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, callback, user_data); 81 | total_records += records; 82 | if (records != answer_rrs) 83 | return total_records; 84 | 85 | records = 86 | mdns_records_parse(sock, addr, buffer, data_size, &offset, 87 | MDNS_ENTRYTYPE_AUTHORITY, query_id, authority_rrs, callback, user_data); 88 | total_records += records; 89 | if (records != authority_rrs) 90 | return total_records; 91 | 92 | records = mdns_records_parse(sock, addr, buffer, data_size, &offset, 93 | MDNS_ENTRYTYPE_ADDITIONAL, query_id, additional_rrs, callback, 94 | user_data); 95 | 96 | return total_records; 97 | } 98 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pydevproject 2 | .project 3 | .metadata 4 | .gitconfig 5 | bin/ 6 | tmp/ 7 | *.tmp 8 | *.bak 9 | *.swp 10 | *~.nib 11 | local.properties 12 | .classpath 13 | .settings/ 14 | .loadpath 15 | .ninja* 16 | build.ninja 17 | 18 | # Generated version 19 | version.c 20 | 21 | # External tool builders 22 | .externalToolBuilders/ 23 | 24 | # Locally stored "Eclipse launch configurations" 25 | *.launch 26 | 27 | # CDT-specific 28 | .cproject 29 | 30 | # PDT-specific 31 | .buildpath 32 | 33 | #Android local build files 34 | build/android/assets 35 | build/android/libs 36 | 37 | #Xcode build 38 | build/xcode/foundation/build 39 | 40 | #Doxygen generated 41 | doc/html 42 | 43 | #Coverity scan 44 | cov-int/ 45 | cov-int.* 46 | 47 | ################# 48 | ## Visual Studio 49 | ################# 50 | 51 | ## Ignore Visual Studio temporary files, build results, and 52 | ## files generated by popular Visual Studio add-ons. 53 | 54 | # User-specific files 55 | *.suo 56 | *.user 57 | *.sln.docstates 58 | 59 | # Build results 60 | [Dd]ebug/ 61 | [Rr]elease/ 62 | [Pp]rofile/ 63 | [Dd]eploy/ 64 | *_i.c 65 | *_p.c 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.pch 70 | *.pchi 71 | *.pdb 72 | *.pgc 73 | *.pgd 74 | *.rsp 75 | *.sbr 76 | *.tlb 77 | *.tli 78 | *.tlh 79 | *.tmp 80 | *.vspscc 81 | .builds 82 | *.dotCover 83 | *.lastbuildstate 84 | *.unsuccessfulbuild 85 | *.opendb 86 | *.vc 87 | *.VC.db 88 | *.db-shm 89 | *.db-wal 90 | 91 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 92 | #packages/ 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opensdf 99 | *.sdf 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper* 107 | 108 | # Installshield output folder 109 | [Ee]xpress 110 | 111 | # DocProject is a documentation generator add-in 112 | DocProject/buildhelp/ 113 | DocProject/Help/*.HxT 114 | DocProject/Help/*.HxC 115 | DocProject/Help/*.hhc 116 | DocProject/Help/*.hhk 117 | DocProject/Help/*.hhp 118 | DocProject/Help/Html2 119 | DocProject/Help/html 120 | 121 | # Click-Once directory 122 | publish 123 | 124 | # Others 125 | [Bb]in 126 | [Oo]bj 127 | sql 128 | TestResults 129 | *.Cache 130 | ClientBin 131 | stylecop.* 132 | ~$* 133 | *.dbmdl 134 | Generated_Code #added for RIA/Silverlight projects 135 | 136 | # Backup & report files from converting an old project file to a newer 137 | # Visual Studio version. Backup files are not needed, because we have git ;-) 138 | _UpgradeReport_Files/ 139 | Backup*/ 140 | UpgradeLog*.XML 141 | 142 | 143 | ############ 144 | ## Windows 145 | ############ 146 | 147 | # Windows image file caches 148 | Thumbs.db 149 | 150 | # Folder config file 151 | Desktop.ini 152 | 153 | 154 | ############# 155 | ## Python 156 | ############# 157 | 158 | *.py[co] 159 | 160 | # Packages 161 | *.egg 162 | *.egg-info 163 | dist 164 | eggs 165 | parts 166 | var 167 | sdist 168 | develop-eggs 169 | .installed.cfg 170 | 171 | # Installer logs 172 | pip-log.txt 173 | 174 | # Unit test / coverage reports 175 | .coverage 176 | .tox 177 | 178 | #Translations 179 | *.mo 180 | 181 | #Mr Developer 182 | .mr.developer.cfg 183 | 184 | # Mac crap 185 | .DS_Store 186 | 187 | 188 | ############### 189 | ## Generic 190 | ############### 191 | 192 | #Project builds 193 | lib/** 194 | bin/** 195 | dist/** 196 | libs/** 197 | 198 | #Log files 199 | *.log 200 | *.tlog 201 | 202 | #Scons build 203 | .sconsign.dblite 204 | build/scons/debug* 205 | build/scons/release* 206 | build/scons/profi* 207 | build/scons/deploy* 208 | 209 | #Backups 210 | *~ 211 | 212 | #Object files 213 | *.o 214 | 215 | #XCode 216 | xcuserdata 217 | *.xccheckout 218 | 219 | #SublimeText local workspace 220 | *.sublime-workspace 221 | 222 | #Never store keystores in version control! 223 | *.keystore 224 | 225 | #Do not store local build prefs 226 | build.json 227 | codesign.json 228 | coveralls.json 229 | coverallsreport.json 230 | codecov.json 231 | codecovreport.json 232 | -------------------------------------------------------------------------------- /mdns/mdns.c: -------------------------------------------------------------------------------- 1 | /* mdns.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | 27 | static bool mdns_initialized = false; 28 | 29 | extern const uint8_t mdns_services_query[46]; 30 | 31 | const uint8_t mdns_services_query[46] = { 32 | // Query ID 33 | 0x00, 0x00, 34 | // Flags 35 | 0x00, 0x00, 36 | // 1 question 37 | 0x00, 0x01, 38 | // No answer, authority or additional RRs 39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 40 | // _services._dns-sd._udp.local. 41 | 0x09, '_', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', 0x07, '_', 'd', 'n', 's', '-', 's', 'd', 0x04, '_', 'u', 'd', 42 | 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 43 | // PTR record 44 | 0x00, MDNS_RECORDTYPE_PTR, 45 | // QU (unicast response) and class IN 46 | 0x80, MDNS_CLASS_IN}; 47 | 48 | int 49 | mdns_module_initialize(const mdns_config_t config) { 50 | FOUNDATION_UNUSED(config); 51 | 52 | if (mdns_initialized) 53 | return 0; 54 | 55 | mdns_initialized = true; 56 | 57 | return 0; 58 | } 59 | 60 | void 61 | mdns_module_finalize(void) { 62 | if (!mdns_initialized) 63 | return; 64 | 65 | mdns_initialized = false; 66 | } 67 | 68 | bool 69 | mdns_module_is_initialized(void) { 70 | return mdns_initialized; 71 | } 72 | 73 | uint16_t 74 | mdns_ntohs(const void* data) { 75 | uint16_t aligned; 76 | memcpy(&aligned, data, sizeof(uint16_t)); 77 | return ntohs(aligned); 78 | } 79 | 80 | uint32_t 81 | mdns_ntohl(const void* data) { 82 | uint32_t aligned; 83 | memcpy(&aligned, data, sizeof(uint32_t)); 84 | return ntohl(aligned); 85 | } 86 | 87 | void* 88 | mdns_htons(void* data, uint16_t val) { 89 | val = htons(val); 90 | memcpy(data, &val, sizeof(uint16_t)); 91 | return pointer_offset(data, sizeof(uint16_t)); 92 | } 93 | 94 | void* 95 | mdns_htonl(void* data, uint32_t val) { 96 | val = htonl(val); 97 | memcpy(data, &val, sizeof(uint32_t)); 98 | return pointer_offset(data, sizeof(uint32_t)); 99 | } 100 | 101 | int 102 | mdns_unicast_send(socket_t* sock, const network_address_t* to, const void* buffer, size_t size) { 103 | if (udp_socket_sendto(sock, buffer, size, to) != size) 104 | return -1; 105 | return 0; 106 | } 107 | 108 | int 109 | mdns_multicast_send(socket_t* sock, const void* buffer, size_t size) { 110 | struct sockaddr_storage addr_storage; 111 | struct sockaddr_in addr; 112 | struct sockaddr_in6 addr6; 113 | struct sockaddr* saddr = (struct sockaddr*)&addr_storage; 114 | socklen_t saddrlen = sizeof(struct sockaddr_storage); 115 | if (sock->family == NETWORK_ADDRESSFAMILY_IPV4) { 116 | memset(&addr, 0, sizeof(addr)); 117 | addr.sin_family = AF_INET; 118 | #ifdef __APPLE__ 119 | addr.sin_len = sizeof(addr); 120 | #endif 121 | addr.sin_addr.s_addr = htonl((((uint32_t)224U) << 24U) | ((uint32_t)251U)); 122 | addr.sin_port = htons((unsigned short)MDNS_PORT); 123 | saddr = (struct sockaddr*)&addr; 124 | saddrlen = sizeof(addr); 125 | } else if (sock->family == NETWORK_ADDRESSFAMILY_IPV6) { 126 | memset(&addr6, 0, sizeof(addr6)); 127 | addr6.sin6_family = AF_INET6; 128 | #ifdef __APPLE__ 129 | addr6.sin6_len = sizeof(addr6); 130 | #endif 131 | addr6.sin6_addr.s6_addr[0] = 0xFF; 132 | addr6.sin6_addr.s6_addr[1] = 0x02; 133 | addr6.sin6_addr.s6_addr[15] = 0xFB; 134 | addr6.sin6_port = htons((unsigned short)MDNS_PORT); 135 | saddr = (struct sockaddr*)&addr6; 136 | saddrlen = sizeof(addr6); 137 | } else { 138 | return -1; 139 | } 140 | 141 | if (sendto(sock->fd, (const char*)buffer, (mdns_size_t)size, 0, saddr, saddrlen) < 0) 142 | return -1; 143 | return 0; 144 | } 145 | -------------------------------------------------------------------------------- /mdns/types.h: -------------------------------------------------------------------------------- 1 | /* types.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #define MDNS_PORT 5353 32 | #define MDNS_UNICAST_RESPONSE 0x8000U 33 | #define MDNS_CACHE_FLUSH 0x8000U 34 | #define MDNS_MAX_SUBSTRINGS 64 35 | 36 | enum mdns_record_type { 37 | MDNS_RECORDTYPE_IGNORE = 0, 38 | // Address 39 | MDNS_RECORDTYPE_A = 1, 40 | // Domain Name pointer 41 | MDNS_RECORDTYPE_PTR = 12, 42 | // Arbitrary text string 43 | MDNS_RECORDTYPE_TXT = 16, 44 | // IP6 Address [Thomson] 45 | MDNS_RECORDTYPE_AAAA = 28, 46 | // Server Selection [RFC2782] 47 | MDNS_RECORDTYPE_SRV = 33, 48 | // Any available records 49 | MDNS_RECORDTYPE_ANY = 255 50 | }; 51 | 52 | enum mdns_entry_type { 53 | MDNS_ENTRYTYPE_QUESTION = 0, 54 | MDNS_ENTRYTYPE_ANSWER = 1, 55 | MDNS_ENTRYTYPE_AUTHORITY = 2, 56 | MDNS_ENTRYTYPE_ADDITIONAL = 3, 57 | MDNS_ENTRYTYPE_END = 255 58 | }; 59 | 60 | enum mdns_class { MDNS_CLASS_IN = 1, MDNS_CLASS_ANY = 255 }; 61 | 62 | typedef enum mdns_record_type mdns_record_type_t; 63 | typedef enum mdns_entry_type mdns_entry_type_t; 64 | typedef enum mdns_class mdns_class_t; 65 | 66 | typedef int (*mdns_record_callback_fn)(socket_t* sock, const network_address_t* from, mdns_entry_type_t entry, 67 | uint16_t query_id, uint16_t rtype, uint16_t rclass, uint32_t ttl, 68 | const void* data, size_t size, size_t name_offset, size_t name_length, 69 | size_t record_offset, size_t record_length, void* user_data); 70 | 71 | typedef struct mdns_config_t mdns_config_t; 72 | typedef struct mdns_string_pair_t mdns_string_pair_t; 73 | typedef struct mdns_string_table_t mdns_string_table_t; 74 | typedef struct mdns_record_t mdns_record_t; 75 | typedef struct mdns_record_srv_t mdns_record_srv_t; 76 | typedef struct mdns_record_ptr_t mdns_record_ptr_t; 77 | typedef struct mdns_record_a_t mdns_record_a_t; 78 | typedef struct mdns_record_aaaa_t mdns_record_aaaa_t; 79 | typedef struct mdns_record_txt_t mdns_record_txt_t; 80 | 81 | #ifdef _WIN32 82 | typedef int mdns_size_t; 83 | typedef int mdns_ssize_t; 84 | #else 85 | typedef size_t mdns_size_t; 86 | typedef ssize_t mdns_ssize_t; 87 | #endif 88 | 89 | struct mdns_config_t { 90 | int unused; 91 | }; 92 | 93 | struct mdns_string_pair_t { 94 | size_t offset; 95 | size_t length; 96 | int ref; 97 | }; 98 | 99 | struct mdns_string_table_t { 100 | size_t offset[16]; 101 | size_t count; 102 | size_t next; 103 | }; 104 | 105 | struct mdns_record_srv_t { 106 | uint16_t priority; 107 | uint16_t weight; 108 | uint16_t port; 109 | string_const_t name; 110 | }; 111 | 112 | struct mdns_record_ptr_t { 113 | string_const_t name; 114 | }; 115 | 116 | struct mdns_record_a_t { 117 | struct sockaddr_in addr; 118 | }; 119 | 120 | struct mdns_record_aaaa_t { 121 | struct sockaddr_in6 addr; 122 | }; 123 | 124 | struct mdns_record_txt_t { 125 | string_const_t key; 126 | string_const_t value; 127 | }; 128 | 129 | struct mdns_record_t { 130 | string_const_t name; 131 | mdns_record_type_t type; 132 | union mdns_record_data { 133 | mdns_record_ptr_t ptr; 134 | mdns_record_srv_t srv; 135 | mdns_record_a_t a; 136 | mdns_record_aaaa_t aaaa; 137 | mdns_record_txt_t txt; 138 | } data; 139 | uint16_t rclass; 140 | uint32_t ttl; 141 | }; 142 | 143 | struct mdns_header_t { 144 | uint16_t query_id; 145 | uint16_t flags; 146 | uint16_t questions; 147 | uint16_t answer_rrs; 148 | uint16_t authority_rrs; 149 | uint16_t additional_rrs; 150 | }; 151 | -------------------------------------------------------------------------------- /mdns/query.h: -------------------------------------------------------------------------------- 1 | /* query.h - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #pragma once 23 | 24 | #include 25 | 26 | #include 27 | #include 28 | 29 | //! Send a multicast mDNS query on the given socket for the given service name. The supplied buffer 30 | // will be used to build the query packet and must be 32 bit aligned. The query ID can be set to 31 | // non-zero to filter responses, however the RFC states that the query ID SHOULD be set to 0 for 32 | // multicast queries. The query will request a unicast response if the socket is bound to an 33 | // ephemeral port, or a multicast response if the socket is bound to mDNS port 5353. Returns the 34 | // used query ID, or <0 if error. 35 | MDNS_API int 36 | mdns_query_send(socket_t* sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, size_t capacity, 37 | uint16_t query_id); 38 | 39 | //! Receive unicast responses to a mDNS query sent with mdns_discovery_recv, optionally filtering 40 | // out any responses not matching the given query ID. Set the query ID to 0 to parse 41 | // all responses, even if it is not matching the query ID set in a specific query. Any data will 42 | // be piped to the given callback for parsing. Buffer must be 32 bit aligned. Returns the number 43 | // of responses parsed. 44 | MDNS_API size_t 45 | mdns_query_recv(socket_t* sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, 46 | int query_id); 47 | 48 | //! Send a variable unicast mDNS query answer to any question with variable number of records to the 49 | //! given address. Use the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query 50 | //! recieved to determine if the answer should be sent unicast (bit set) or multicast (bit not set). 51 | //! Buffer must be 32 bit aligned. The record type and name should match the data from the query 52 | //! recieved. Returns 0 if success, or <0 if error. 53 | MDNS_API int 54 | mdns_query_answer_unicast(socket_t* sock, const network_address_t* address, void* buffer, size_t capacity, 55 | uint16_t query_id, mdns_record_type_t record_type, const char* name, size_t name_length, 56 | mdns_record_t answer, const mdns_record_t* authority, size_t authority_count, 57 | const mdns_record_t* additional, size_t additional_count); 58 | 59 | //! Send a variable multicast mDNS query answer to any question with variable number of records. Use 60 | //! the top bit of the query class field (MDNS_UNICAST_RESPONSE) in the query recieved to determine 61 | //! if the answer should be sent unicast (bit set) or multicast (bit not set). Buffer must be 32 bit 62 | //! aligned. Returns 0 if success, or <0 if error. 63 | MDNS_API int 64 | mdns_query_answer_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 65 | const mdns_record_t* authority, size_t authority_count, const mdns_record_t* additional, 66 | size_t additional_count); 67 | 68 | //! Send a variable multicast mDNS announcement (as an unsolicited answer) with variable number of 69 | //! records.Buffer must be 32 bit aligned. Returns 0 if success, or <0 if error. Use this on service 70 | //! startup to announce your instance to the local network. 71 | MDNS_API int 72 | mdns_announce_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 73 | const mdns_record_t* authority, size_t authority_count, 74 | const mdns_record_t* additional, size_t additional_count); 75 | 76 | //! Send a variable multicast mDNS announcement. Use this on service end for removing the resource 77 | //! from the local network. The records must be identical to the according announcement. 78 | MDNS_API int 79 | mdns_goodbye_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 80 | const mdns_record_t* authority, size_t authority_count, 81 | const mdns_record_t* additional, size_t additional_count); 82 | -------------------------------------------------------------------------------- /mdns/discovery.c: -------------------------------------------------------------------------------- 1 | /* discovery.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | extern const uint8_t mdns_services_query[46]; 27 | 28 | int 29 | mdns_discovery_send(socket_t* sock) { 30 | return mdns_multicast_send(sock, mdns_services_query, sizeof(mdns_services_query)); 31 | } 32 | 33 | size_t 34 | mdns_discovery_recv(socket_t* sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data) { 35 | const network_address_t* address; 36 | size_t data_size = udp_socket_recvfrom(sock, buffer, capacity, &address); 37 | if (!data_size) 38 | return 0; 39 | 40 | size_t records = 0; 41 | const uint16_t* data = (uint16_t*)buffer; 42 | 43 | uint16_t query_id = mdns_ntohs(data++); 44 | uint16_t flags = mdns_ntohs(data++); 45 | uint16_t questions = mdns_ntohs(data++); 46 | uint16_t answer_rrs = mdns_ntohs(data++); 47 | uint16_t authority_rrs = mdns_ntohs(data++); 48 | uint16_t additional_rrs = mdns_ntohs(data++); 49 | 50 | // According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case) 51 | if (query_id || (flags != 0x8400)) 52 | return 0; // Not a reply to our question 53 | 54 | // It seems some implementations do not fill the correct questions field, 55 | // so ignore this check for now and only validate answer string 56 | /* 57 | if (questions != 1) 58 | return 0; 59 | */ 60 | 61 | int i; 62 | for (i = 0; i < questions; ++i) { 63 | size_t offset = (size_t)pointer_diff(data, buffer); 64 | size_t verify_offset = 12; 65 | // Verify it's our question, _services._dns-sd._udp.local. 66 | if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query, sizeof(mdns_services_query), &verify_offset)) 67 | return 0; 68 | data = pointer_offset_const(buffer, offset); 69 | 70 | uint16_t rtype = mdns_ntohs(data++); 71 | uint16_t rclass = mdns_ntohs(data++); 72 | 73 | // Make sure we get a reply based on our PTR question for class IN 74 | if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN)) 75 | return 0; 76 | } 77 | 78 | for (i = 0; i < answer_rrs; ++i) { 79 | size_t offset = (size_t)pointer_diff(data, buffer); 80 | size_t verify_offset = 12; 81 | // Verify it's an answer to our question, _services._dns-sd._udp.local. 82 | size_t name_offset = offset; 83 | int is_answer = 84 | mdns_string_equal(buffer, data_size, &offset, mdns_services_query, sizeof(mdns_services_query), &verify_offset); 85 | if (!is_answer && !mdns_string_skip(buffer, data_size, &offset)) 86 | break; 87 | size_t name_length = offset - name_offset; 88 | if ((offset + 10) > data_size) 89 | return records; 90 | data = pointer_offset_const(buffer, offset); 91 | 92 | uint16_t rtype = mdns_ntohs(data++); 93 | uint16_t rclass = mdns_ntohs(data++); 94 | uint32_t ttl = mdns_ntohl(data); 95 | data += 2; 96 | uint16_t length = mdns_ntohs(data++); 97 | if (length > (data_size - offset)) 98 | return 0; 99 | 100 | if (is_answer) { 101 | ++records; 102 | offset = (size_t)pointer_diff(data, buffer); 103 | if (callback && callback(sock, address, MDNS_ENTRYTYPE_ANSWER, query_id, rtype, rclass, ttl, buffer, 104 | data_size, name_offset, name_length, offset, length, user_data)) 105 | return records; 106 | } 107 | data = pointer_offset_const(data, length); 108 | } 109 | size_t total_records = records; 110 | 111 | size_t offset = (size_t)pointer_diff(data, buffer); 112 | records = mdns_records_parse(sock, address, buffer, data_size, &offset, MDNS_ENTRYTYPE_AUTHORITY, query_id, 113 | authority_rrs, callback, user_data); 114 | total_records += records; 115 | if (records != authority_rrs) 116 | return total_records; 117 | 118 | records = mdns_records_parse(sock, address, buffer, data_size, &offset, MDNS_ENTRYTYPE_ADDITIONAL, query_id, 119 | additional_rrs, callback, user_data); 120 | total_records += records; 121 | if (records != additional_rrs) 122 | return total_records; 123 | 124 | if (callback) 125 | callback(sock, address, MDNS_ENTRYTYPE_END, query_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 126 | 127 | return total_records; 128 | } 129 | -------------------------------------------------------------------------------- /mdns/record.c: -------------------------------------------------------------------------------- 1 | /* record.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | string_const_t 27 | mdns_record_parse_ptr(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { 28 | // PTR record is just a string 29 | if ((size >= offset + length) && (length >= 2)) 30 | return mdns_string_extract(buffer, size, &offset, strbuffer, capacity); 31 | return string_const(0, 0); 32 | } 33 | 34 | mdns_record_srv_t 35 | mdns_record_parse_srv(const void* buffer, size_t size, size_t offset, size_t length, char* strbuffer, size_t capacity) { 36 | mdns_record_srv_t srv; 37 | memset(&srv, 0, sizeof(mdns_record_srv_t)); 38 | // Read the priority, weight, port number and the discovery name 39 | // SRV record format (http://www.ietf.org/rfc/rfc2782.txt): 40 | // 2 bytes network-order unsigned priority 41 | // 2 bytes network-order unsigned weight 42 | // 2 bytes network-order unsigned port 43 | // string: discovery (domain) name, minimum 2 bytes when compressed 44 | if ((size >= offset + length) && (length >= 8)) { 45 | const uint16_t* recorddata = pointer_offset_const(buffer, offset); 46 | srv.priority = mdns_ntohs(recorddata++); 47 | srv.weight = mdns_ntohs(recorddata++); 48 | srv.port = mdns_ntohs(recorddata++); 49 | offset += 6; 50 | srv.name = mdns_string_extract(buffer, size, &offset, strbuffer, capacity); 51 | } 52 | return srv; 53 | } 54 | 55 | network_address_ipv4_t* 56 | mdns_record_parse_a(const void* buffer, size_t size, size_t offset, size_t length, network_address_ipv4_t* addr) { 57 | network_address_ipv4_initialize(addr); 58 | if ((size >= offset + length) && (length == 4)) 59 | memcpy(&addr->saddr.sin_addr.s_addr, pointer_offset_const(buffer, offset), 4); 60 | return addr; 61 | } 62 | 63 | network_address_ipv6_t* 64 | mdns_record_parse_aaaa(const void* buffer, size_t size, size_t offset, size_t length, network_address_ipv6_t* addr) { 65 | network_address_ipv6_initialize(addr); 66 | if ((size >= offset + length) && (length == 16)) 67 | memcpy(&addr->saddr.sin6_addr, pointer_offset_const(buffer, offset), 16); 68 | return addr; 69 | } 70 | 71 | size_t 72 | mdns_record_parse_txt(const void* buffer, size_t size, size_t offset, size_t length, mdns_record_txt_t* records, 73 | size_t capacity) { 74 | size_t parsed = 0; 75 | const char* strdata; 76 | size_t separator, sublength; 77 | size_t end = offset + length; 78 | 79 | if (size < end) 80 | end = size; 81 | 82 | while ((offset < end) && (parsed < capacity)) { 83 | strdata = pointer_offset_const(buffer, offset); 84 | sublength = *(const unsigned char*)strdata; 85 | 86 | ++strdata; 87 | offset += sublength + 1; 88 | 89 | separator = 0; 90 | for (size_t c = 0; c < sublength; ++c) { 91 | // DNS-SD TXT record keys MUST be printable US-ASCII, [0x20, 0x7E] 92 | if ((strdata[c] < 0x20) || (strdata[c] > 0x7E)) 93 | break; 94 | if (strdata[c] == '=') { 95 | separator = c; 96 | break; 97 | } 98 | } 99 | 100 | if (!separator) 101 | continue; 102 | 103 | if (separator < sublength) { 104 | records[parsed].key.str = strdata; 105 | records[parsed].key.length = separator; 106 | records[parsed].value.str = strdata + separator + 1; 107 | records[parsed].value.length = sublength - (separator + 1); 108 | } else { 109 | records[parsed].key.str = strdata; 110 | records[parsed].key.length = sublength; 111 | } 112 | 113 | ++parsed; 114 | } 115 | 116 | return parsed; 117 | } 118 | 119 | size_t 120 | mdns_records_parse(socket_t* sock, const network_address_t* from, const void* buffer, size_t size, size_t* offset, 121 | mdns_entry_type_t type, uint16_t query_id, size_t records, mdns_record_callback_fn callback, 122 | void* user_data) { 123 | size_t parsed = 0; 124 | for (size_t i = 0; i < records; ++i) { 125 | size_t name_offset = *offset; 126 | mdns_string_skip(buffer, size, offset); 127 | if (((*offset) + 10) > size) 128 | return parsed; 129 | size_t name_length = (*offset) - name_offset; 130 | const uint16_t* data = pointer_offset_const(buffer, *offset); 131 | 132 | uint16_t rtype = mdns_ntohs(data++); 133 | uint16_t rclass = mdns_ntohs(data++); 134 | uint32_t ttl = mdns_ntohl(data); 135 | data += 2; 136 | uint16_t length = mdns_ntohs(data++); 137 | 138 | *offset += 10; 139 | 140 | if (length <= (size - (*offset))) { 141 | ++parsed; 142 | if (callback && callback(sock, from, type, query_id, rtype, rclass, ttl, buffer, size, name_offset, 143 | name_length, *offset, length, user_data)) 144 | break; 145 | } 146 | 147 | *offset += length; 148 | } 149 | return parsed; 150 | } 151 | -------------------------------------------------------------------------------- /tools/mdns/main.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | static char recvbuffer[1024]; 7 | static char addrbuffer[256]; 8 | static char entrybuffer[256]; 9 | static char namebuffer[256]; 10 | 11 | static int 12 | query_callback(socket_t* sock, const network_address_t* from, mdns_entry_type_t entry, uint16_t query_id, 13 | uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, 14 | size_t name_length, size_t record_offset, size_t record_length, void* user_data) { 15 | (void)sizeof(sock); 16 | (void)sizeof(query_id); 17 | (void)sizeof(name_length); 18 | (void)sizeof(user_data); 19 | string_t fromaddrstr = network_address_to_string(addrbuffer, sizeof(addrbuffer), from, false); 20 | const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? 21 | "answer" : 22 | ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); 23 | string_const_t entrystr = mdns_string_extract(data, size, &name_offset, entrybuffer, sizeof(entrybuffer)); 24 | if (rtype == MDNS_RECORDTYPE_PTR) { 25 | string_const_t namestr = 26 | mdns_record_parse_ptr(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); 27 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s PTR %.*s rclass 0x%x ttl %u length %d"), 28 | STRING_FORMAT(fromaddrstr), entrytype, STRING_FORMAT(entrystr), STRING_FORMAT(namestr), rclass, ttl, 29 | (int)record_length); 30 | } else if (rtype == MDNS_RECORDTYPE_SRV) { 31 | mdns_record_srv_t srv = 32 | mdns_record_parse_srv(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); 33 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s SRV %.*s priority %d weight %d port %d"), 34 | STRING_FORMAT(fromaddrstr), entrytype, STRING_FORMAT(entrystr), STRING_FORMAT(srv.name), srv.priority, 35 | srv.weight, srv.port); 36 | } else if (rtype == MDNS_RECORDTYPE_A) { 37 | network_address_ipv4_t addr; 38 | mdns_record_parse_a(data, size, record_offset, record_length, &addr); 39 | string_t addrstr = network_address_to_string(namebuffer, sizeof(namebuffer), (network_address_t*)&addr, false); 40 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s A %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 41 | STRING_FORMAT(entrystr), STRING_FORMAT(addrstr)); 42 | } else if (rtype == MDNS_RECORDTYPE_AAAA) { 43 | network_address_ipv6_t addr; 44 | mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr); 45 | string_t addrstr = network_address_to_string(namebuffer, sizeof(namebuffer), (network_address_t*)&addr, false); 46 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s AAAA %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 47 | STRING_FORMAT(entrystr), STRING_FORMAT(addrstr)); 48 | } else if (rtype == MDNS_RECORDTYPE_TXT) { 49 | mdns_record_txt_t records[16]; 50 | size_t parsed = mdns_record_parse_txt(data, size, record_offset, record_length, records, 51 | sizeof(records) / sizeof(records[0])); 52 | for (size_t itxt = 0; itxt < parsed; ++itxt) { 53 | if (records[itxt].value.length) { 54 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s TXT %.*s = %.*s"), STRING_FORMAT(fromaddrstr), 55 | entrytype, STRING_FORMAT(entrystr), STRING_FORMAT(records[itxt].key), 56 | STRING_FORMAT(records[itxt].value)); 57 | } else { 58 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s TXT %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 59 | STRING_FORMAT(entrystr), STRING_FORMAT(records[itxt].key)); 60 | } 61 | } 62 | } else { 63 | log_infof(HASH_MDNS, STRING_CONST("%.*s : %s %.*s type %u rclass 0x%x ttl %u length %d"), 64 | STRING_FORMAT(fromaddrstr), entrytype, STRING_FORMAT(entrystr), rtype, rclass, ttl, 65 | (int)record_length); 66 | } 67 | return 0; 68 | } 69 | 70 | int 71 | main_initialize(void) { 72 | int ret = 0; 73 | application_t application = {0}; 74 | foundation_config_t config = {0}; 75 | 76 | application.name = string_const(STRING_CONST("mdns")); 77 | application.short_name = string_const(STRING_CONST("mdns")); 78 | application.flags = APPLICATION_UTILITY; 79 | 80 | log_enable_prefix(false); 81 | log_set_suppress(0, ERRORLEVEL_WARNING); 82 | 83 | if ((ret = foundation_initialize(memory_system_malloc(), application, config)) < 0) 84 | return ret; 85 | 86 | network_config_t network_config = {0}; 87 | if ((ret = network_module_initialize(network_config)) < 0) 88 | return ret; 89 | 90 | mdns_config_t mdns_config = {0}; 91 | if ((ret = mdns_module_initialize(mdns_config)) < 0) 92 | return ret; 93 | 94 | return 0; 95 | } 96 | 97 | int 98 | main_run(void* main_arg) { 99 | int result = 0; 100 | 101 | FOUNDATION_UNUSED(main_arg); 102 | 103 | socket_t* sock = udp_socket_allocate(); 104 | if (!sock) 105 | return -1; 106 | 107 | if (!mdns_socket_bind(sock, 0)) { 108 | log_error(HASH_MDNS, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Failed to bind mDNS socket")); 109 | result = -1; 110 | goto finalize; 111 | } 112 | 113 | if (mdns_discovery_send(sock) < 0) { 114 | log_error(HASH_MDNS, ERROR_SYSTEM_CALL_FAIL, STRING_CONST("Failed to send DNS-SD packet")); 115 | result = -1; 116 | goto finalize; 117 | } 118 | 119 | log_infof(HASH_MDNS, STRING_CONST("Reading DNS-SD responses\n")); 120 | for (int iloop = 0; iloop < 10; ++iloop) { 121 | mdns_discovery_recv(sock, recvbuffer, sizeof(recvbuffer), query_callback, 0); 122 | thread_sleep(1000); 123 | } 124 | 125 | finalize: 126 | if (sock) 127 | socket_deallocate(sock); 128 | 129 | return result; 130 | } 131 | 132 | void 133 | main_finalize(void) { 134 | mdns_module_finalize(); 135 | network_module_finalize(); 136 | foundation_finalize(); 137 | } 138 | -------------------------------------------------------------------------------- /mdns/string.c: -------------------------------------------------------------------------------- 1 | /* string.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | 24 | #include 25 | 26 | static int 27 | mdns_is_string_ref(uint8_t val) { 28 | return (0xC0 == (val & 0xC0)); 29 | } 30 | 31 | static mdns_string_pair_t 32 | mdns_get_next_substring(const void* rawdata, size_t size, size_t offset) { 33 | const uint8_t* buffer = (const uint8_t*)rawdata; 34 | mdns_string_pair_t pair = {STRING_NPOS, 0, 0}; 35 | if (offset >= size) 36 | return pair; 37 | if (!buffer[offset]) { 38 | pair.offset = offset; 39 | return pair; 40 | } 41 | if (mdns_is_string_ref(buffer[offset])) { 42 | if (size < offset + 2) 43 | return pair; 44 | 45 | offset = mdns_ntohs(pointer_offset_const(buffer, offset)) & 0x3fff; 46 | if (offset >= size) 47 | return pair; 48 | 49 | pair.ref = 1; 50 | } 51 | 52 | size_t length = (size_t)buffer[offset++]; 53 | if (size < offset + length) 54 | return pair; 55 | 56 | pair.offset = offset; 57 | pair.length = length; 58 | 59 | return pair; 60 | } 61 | 62 | int 63 | mdns_string_skip(const void* buffer, size_t size, size_t* offset) { 64 | size_t cur = *offset; 65 | mdns_string_pair_t substr; 66 | unsigned int counter = 0; 67 | do { 68 | substr = mdns_get_next_substring(buffer, size, cur); 69 | if ((substr.offset == STRING_NPOS) || (counter++ > MDNS_MAX_SUBSTRINGS)) 70 | return 0; 71 | if (substr.ref) { 72 | *offset = cur + 2; 73 | return 1; 74 | } 75 | cur = substr.offset + substr.length; 76 | } while (substr.length); 77 | 78 | *offset = cur + 1; 79 | return 1; 80 | } 81 | 82 | int 83 | mdns_string_equal(const void* buffer_lhs, size_t size_lhs, size_t* ofs_lhs, const void* buffer_rhs, size_t size_rhs, 84 | size_t* ofs_rhs) { 85 | size_t lhs_cur = *ofs_lhs; 86 | size_t rhs_cur = *ofs_rhs; 87 | size_t lhs_end = STRING_NPOS; 88 | size_t rhs_end = STRING_NPOS; 89 | mdns_string_pair_t lhs_substr; 90 | mdns_string_pair_t rhs_substr; 91 | unsigned int counter = 0; 92 | do { 93 | lhs_substr = mdns_get_next_substring(buffer_lhs, size_lhs, lhs_cur); 94 | rhs_substr = mdns_get_next_substring(buffer_rhs, size_rhs, rhs_cur); 95 | if ((lhs_substr.offset == STRING_NPOS) || (rhs_substr.offset == STRING_NPOS) || 96 | (counter++ > MDNS_MAX_SUBSTRINGS)) 97 | return 0; 98 | if (!string_equal_nocase(pointer_offset_const(buffer_lhs, lhs_substr.offset), lhs_substr.length, 99 | pointer_offset_const(buffer_rhs, rhs_substr.offset), rhs_substr.length)) 100 | return 0; 101 | if (lhs_substr.ref && (lhs_end == STRING_NPOS)) 102 | lhs_end = lhs_cur + 2; 103 | if (rhs_substr.ref && (rhs_end == STRING_NPOS)) 104 | rhs_end = rhs_cur + 2; 105 | lhs_cur = lhs_substr.offset + lhs_substr.length; 106 | rhs_cur = rhs_substr.offset + rhs_substr.length; 107 | } while (lhs_substr.length); 108 | 109 | if (lhs_end == STRING_NPOS) 110 | lhs_end = lhs_cur + 1; 111 | *ofs_lhs = lhs_end; 112 | 113 | if (rhs_end == STRING_NPOS) 114 | rhs_end = rhs_cur + 1; 115 | *ofs_rhs = rhs_end; 116 | 117 | return 1; 118 | } 119 | 120 | string_const_t 121 | mdns_string_extract(const void* buffer, size_t size, size_t* offset, char* str, size_t capacity) { 122 | size_t cur = *offset; 123 | size_t end = STRING_NPOS; 124 | mdns_string_pair_t substr; 125 | string_const_t result; 126 | result.str = str; 127 | result.length = 0; 128 | char* dst = str; 129 | unsigned int counter = 0; 130 | size_t remain = capacity; 131 | do { 132 | substr = mdns_get_next_substring(buffer, size, cur); 133 | if ((substr.offset == STRING_NPOS) || (counter++ > MDNS_MAX_SUBSTRINGS)) 134 | return result; 135 | if (substr.ref && (end == STRING_NPOS)) 136 | end = cur + 2; 137 | if (substr.length) { 138 | size_t to_copy = (substr.length < remain) ? substr.length : remain; 139 | memcpy(dst, pointer_offset_const(buffer, substr.offset), to_copy); 140 | dst += to_copy; 141 | remain -= to_copy; 142 | if (remain) { 143 | *dst++ = '.'; 144 | --remain; 145 | } 146 | } 147 | cur = substr.offset + substr.length; 148 | } while (substr.length); 149 | 150 | if (end == STRING_NPOS) 151 | end = cur + 1; 152 | *offset = end; 153 | 154 | result.length = capacity - remain; 155 | return result; 156 | } 157 | 158 | static size_t 159 | mdns_string_find(const char* str, size_t length, char c, size_t offset) { 160 | const void* found; 161 | if (offset >= length) 162 | return STRING_NPOS; 163 | found = memchr(str + offset, c, length - offset); 164 | if (found) 165 | return (size_t)pointer_diff(found, str); 166 | return STRING_NPOS; 167 | } 168 | 169 | void* 170 | mdns_string_make(void* buffer, size_t capacity, void* data, const char* name, size_t length, 171 | mdns_string_table_t* string_table) { 172 | size_t pos = 0; 173 | size_t last_pos = 0; 174 | size_t remain = capacity - (size_t)pointer_diff(data, buffer); 175 | if (name[length - 1] == '.') 176 | --length; 177 | while (last_pos < length) { 178 | pos = mdns_string_find(name, length, '.', last_pos); 179 | size_t sub_length = ((pos != STRING_NPOS) ? pos : length) - last_pos; 180 | size_t total_length = length - last_pos; 181 | 182 | size_t ref_offset = mdns_string_table_find(string_table, buffer, capacity, pointer_offset_const(name, last_pos), 183 | sub_length, total_length); 184 | if (ref_offset != STRING_NPOS) 185 | return mdns_string_make_ref(data, remain, ref_offset); 186 | 187 | if (remain <= (sub_length + 1)) 188 | return 0; 189 | 190 | *(unsigned char*)data = (unsigned char)sub_length; 191 | memcpy(pointer_offset(data, 1), name + last_pos, sub_length); 192 | mdns_string_table_add(string_table, (size_t)pointer_diff(data, buffer)); 193 | 194 | data = pointer_offset(data, sub_length + 1); 195 | last_pos = ((pos != STRING_NPOS) ? pos + 1 : length); 196 | remain = capacity - (size_t)pointer_diff(data, buffer); 197 | } 198 | 199 | if (!remain) 200 | return 0; 201 | 202 | *(unsigned char*)data = 0; 203 | return pointer_offset(data, 1); 204 | } 205 | 206 | void* 207 | mdns_string_make_ref(void* data, size_t capacity, size_t ref_offset) { 208 | if (capacity < 2) 209 | return 0; 210 | return mdns_htons(data, 0xC000 | (uint16_t)ref_offset); 211 | } 212 | 213 | size_t 214 | mdns_string_table_find(mdns_string_table_t* string_table, const void* buffer, size_t capacity, const char* str, 215 | size_t first_length, size_t total_length) { 216 | if (!string_table) 217 | return STRING_NPOS; 218 | 219 | for (size_t istr = 0; istr < string_table->count; ++istr) { 220 | if (string_table->offset[istr] >= capacity) 221 | continue; 222 | size_t offset = 0; 223 | mdns_string_pair_t sub_string = mdns_get_next_substring(buffer, capacity, string_table->offset[istr]); 224 | if (!sub_string.length || (sub_string.length != first_length)) 225 | continue; 226 | if (memcmp(str, pointer_offset_const(buffer, sub_string.offset), sub_string.length)) 227 | continue; 228 | 229 | // Initial substring matches, now match all remaining substrings 230 | offset += first_length + 1; 231 | while (offset < total_length) { 232 | size_t dot_pos = string_find(str, total_length, '.', offset); 233 | if (dot_pos == STRING_NPOS) 234 | dot_pos = total_length; 235 | size_t current_length = dot_pos - offset; 236 | 237 | sub_string = mdns_get_next_substring(buffer, capacity, sub_string.offset + sub_string.length); 238 | if (!sub_string.length || (sub_string.length != current_length)) 239 | break; 240 | if (memcmp(str + offset, pointer_offset_const(buffer, sub_string.offset), sub_string.length)) 241 | break; 242 | 243 | offset = dot_pos + 1; 244 | } 245 | 246 | // Return reference offset if entire string matches 247 | if (offset >= total_length) 248 | return string_table->offset[istr]; 249 | } 250 | 251 | return STRING_NPOS; 252 | } 253 | 254 | void 255 | mdns_string_table_add(mdns_string_table_t* string_table, size_t offset) { 256 | if (!string_table) 257 | return; 258 | 259 | string_table->offset[string_table->next] = offset; 260 | 261 | size_t table_capacity = sizeof(string_table->offset) / sizeof(string_table->offset[0]); 262 | if (++string_table->count > table_capacity) 263 | string_table->count = table_capacity; 264 | if (++string_table->next >= table_capacity) 265 | string_table->next = 0; 266 | } 267 | -------------------------------------------------------------------------------- /test/dnssd/main.c: -------------------------------------------------------------------------------- 1 | /* main.c - mDNS library - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/rampantpixels/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/rampantpixels/foundation_lib 15 | * https://github.com/rampantpixels/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify it without any restrictions. 18 | * 19 | */ 20 | 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | static application_t 28 | test_dnssd_application(void) { 29 | application_t app; 30 | memset(&app, 0, sizeof(app)); 31 | app.name = string_const(STRING_CONST("DNS-SD tests")); 32 | app.short_name = string_const(STRING_CONST("test_dnssd")); 33 | app.company = string_const(STRING_CONST("")); 34 | app.flags = APPLICATION_UTILITY; 35 | app.exception_handler = test_exception_handler; 36 | return app; 37 | } 38 | 39 | static memory_system_t 40 | test_dnssd_memory_system(void) { 41 | return memory_system_malloc(); 42 | } 43 | 44 | static foundation_config_t 45 | test_dnssd_foundation_config(void) { 46 | foundation_config_t config; 47 | memset(&config, 0, sizeof(config)); 48 | return config; 49 | } 50 | 51 | static int 52 | test_dnssd_initialize(void) { 53 | network_config_t network_config = {0}; 54 | if (network_module_initialize(network_config) < 0) 55 | return -1; 56 | 57 | mdns_config_t mdns_config = {0}; 58 | if (mdns_module_initialize(mdns_config) < 0) 59 | return -1; 60 | 61 | return 0; 62 | } 63 | 64 | static void 65 | test_dnssd_finalize(void) { 66 | mdns_module_finalize(); 67 | network_module_finalize(); 68 | } 69 | 70 | static int 71 | query_callback(socket_t* sock, const network_address_t* from, mdns_entry_type_t entry, uint16_t query_id, 72 | uint16_t rtype, uint16_t rclass, uint32_t ttl, const void* data, size_t size, size_t name_offset, 73 | size_t name_length, size_t record_offset, size_t record_length, void* user_data) { 74 | char addrbuffer[NETWORK_ADDRESS_NUMERIC_MAX_LENGTH]; 75 | char FOUNDATION_ALIGN(8) namebuffer[256]; 76 | FOUNDATION_UNUSED(sock); 77 | FOUNDATION_UNUSED(entry); 78 | FOUNDATION_UNUSED(user_data); 79 | FOUNDATION_UNUSED(name_length); 80 | FOUNDATION_UNUSED(name_offset); 81 | FOUNDATION_UNUSED(query_id); 82 | string_t fromaddrstr = network_address_to_string(addrbuffer, sizeof(addrbuffer), from, true); 83 | const char* entrytype = (entry == MDNS_ENTRYTYPE_ANSWER) ? 84 | "answer" : 85 | ((entry == MDNS_ENTRYTYPE_AUTHORITY) ? "authority" : "additional"); 86 | if (rtype == MDNS_RECORDTYPE_PTR) { 87 | string_const_t namestr = 88 | mdns_record_parse_ptr(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); 89 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s PTR %.*s type %u rclass 0x%x ttl %u length %" PRIsize), 90 | STRING_FORMAT(fromaddrstr), entrytype, STRING_FORMAT(namestr), rtype, rclass, ttl, record_length); 91 | } else if (rtype == MDNS_RECORDTYPE_SRV) { 92 | mdns_record_srv_t srv = 93 | mdns_record_parse_srv(data, size, record_offset, record_length, namebuffer, sizeof(namebuffer)); 94 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s SRV %.*s priority %d weight %d port %d"), 95 | STRING_FORMAT(fromaddrstr), entrytype, STRING_FORMAT(srv.name), srv.priority, srv.weight, srv.port); 96 | } else if (rtype == MDNS_RECORDTYPE_A) { 97 | network_address_ipv4_t addr; 98 | mdns_record_parse_a(data, size, record_offset, record_length, &addr); 99 | string_t addrstr = network_address_to_string(namebuffer, sizeof(namebuffer), (network_address_t*)&addr, true); 100 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s A %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 101 | STRING_FORMAT(addrstr)); 102 | } else if (rtype == MDNS_RECORDTYPE_AAAA) { 103 | network_address_ipv6_t addr; 104 | mdns_record_parse_aaaa(data, size, record_offset, record_length, &addr); 105 | string_t addrstr = network_address_to_string(namebuffer, sizeof(namebuffer), (network_address_t*)&addr, true); 106 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s AAAA %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 107 | STRING_FORMAT(addrstr)); 108 | } else if (rtype == MDNS_RECORDTYPE_TXT) { 109 | mdns_record_txt_t* txtrecord = (void*)namebuffer; 110 | size_t parsed = mdns_record_parse_txt(data, size, record_offset, record_length, txtrecord, 111 | sizeof(namebuffer) / sizeof(mdns_record_txt_t)); 112 | for (size_t itxt = 0; itxt < parsed; ++itxt) { 113 | if (txtrecord[itxt].value.length) { 114 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s TXT %.*s = %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 115 | STRING_FORMAT(txtrecord[itxt].key), STRING_FORMAT(txtrecord[itxt].value)); 116 | } else { 117 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s TXT %.*s"), STRING_FORMAT(fromaddrstr), entrytype, 118 | STRING_FORMAT(txtrecord[itxt].key)); 119 | } 120 | } 121 | } else { 122 | log_infof(HASH_TEST, STRING_CONST("%.*s : %s type %u rclass 0x%x ttl %u length %" PRIsize), 123 | STRING_FORMAT(fromaddrstr), entrytype, rtype, rclass, ttl, record_length); 124 | } 125 | return 0; 126 | } 127 | 128 | DECLARE_TEST(dnssd, discover) { 129 | socket_t* sock_mdns[16]; 130 | uint32_t databuf[128]; 131 | 132 | // log_set_suppress(HASH_NETWORK, ERRORLEVEL_NONE); 133 | log_set_suppress(HASH_MDNS, ERRORLEVEL_DEBUG); 134 | 135 | network_address_t** local_address = network_address_local(); 136 | EXPECT_NE(local_address, 0); 137 | 138 | size_t sock_count = array_size(local_address); 139 | size_t sock_capacity = sizeof(sock_mdns) / sizeof(sock_mdns[0]); 140 | if (sock_count > sock_capacity) 141 | sock_count = sock_capacity; 142 | 143 | size_t sock_bound_count = 0; 144 | for (size_t isock = 0; isock < sock_count; ++isock) { 145 | sock_mdns[isock] = udp_socket_allocate(); 146 | EXPECT_NE(sock_mdns, nullptr); 147 | 148 | if (mdns_socket_bind(sock_mdns[isock], local_address[isock])) { 149 | ++sock_bound_count; 150 | } else { 151 | socket_deallocate(sock_mdns[isock]); 152 | sock_mdns[isock] = nullptr; 153 | } 154 | } 155 | EXPECT_GT(sock_bound_count, 0); 156 | 157 | for (size_t isock = 0; isock < sock_count; ++isock) { 158 | if (sock_mdns[isock]) 159 | mdns_discovery_send(sock_mdns[isock]); 160 | } 161 | 162 | size_t iloop = 0; 163 | while (iloop++ < 50) { 164 | for (size_t isock = 0; isock < sock_count; ++isock) { 165 | if (sock_mdns[isock]) 166 | mdns_discovery_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr); 167 | } 168 | thread_sleep(100); 169 | } 170 | 171 | for (size_t isock = 0; isock < sock_count; ++isock) 172 | socket_deallocate(sock_mdns[isock]); 173 | for (size_t iaddr = 0, acount = array_size(local_address); iaddr < acount; ++iaddr) 174 | network_address_deallocate(local_address[iaddr]); 175 | array_deallocate(local_address); 176 | 177 | return 0; 178 | } 179 | 180 | DECLARE_TEST(dnssd, query) { 181 | socket_t* sock_mdns[16]; 182 | uint32_t databuf[128]; 183 | 184 | //log_set_suppress(HASH_NETWORK, ERRORLEVEL_NONE); 185 | //log_set_suppress(HASH_TEST, ERRORLEVEL_NONE); 186 | log_set_suppress(HASH_MDNS, ERRORLEVEL_NONE); 187 | 188 | network_address_t** local_address = network_address_local(); 189 | EXPECT_NE(local_address, 0); 190 | 191 | size_t sock_count = array_size(local_address); 192 | size_t sock_capacity = sizeof(sock_mdns) / sizeof(sock_mdns[0]); 193 | if (sock_count > sock_capacity) 194 | sock_count = sock_capacity; 195 | 196 | size_t sock_bound_count = 0; 197 | for (size_t isock = 0; isock < sock_count; ++isock) { 198 | sock_mdns[isock] = udp_socket_allocate(); 199 | EXPECT_NE(sock_mdns, nullptr); 200 | 201 | if (mdns_socket_bind(sock_mdns[isock], local_address[isock])) { 202 | ++sock_bound_count; 203 | } else { 204 | socket_deallocate(sock_mdns[isock]); 205 | sock_mdns[isock] = nullptr; 206 | } 207 | } 208 | EXPECT_GT(sock_bound_count, 0); 209 | 210 | for (size_t isock = 0; isock < sock_count; ++isock) { 211 | if (!sock_mdns[isock]) 212 | continue; 213 | 214 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_PTR, STRING_CONST("_ssh._tcp.local."), databuf, sizeof(databuf), 0); 215 | 216 | size_t iloop = 0; 217 | while (iloop++ < 30) { 218 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 219 | thread_sleep(100); 220 | } 221 | 222 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_PTR, STRING_CONST("_smb_tcp.local."), databuf, 223 | sizeof(databuf), 0); 224 | 225 | iloop = 0; 226 | while (iloop++ < 30) { 227 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 228 | thread_sleep(100); 229 | } 230 | 231 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_PTR, STRING_CONST("_googlecast._tcp.local."), databuf, sizeof(databuf), 232 | 0); 233 | 234 | iloop = 0; 235 | while (iloop++ < 30) { 236 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 237 | thread_sleep(100); 238 | } 239 | 240 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_SRV, STRING_CONST("macdev._smb._tcp.local."), databuf, sizeof(databuf), 241 | 0); 242 | 243 | iloop = 0; 244 | while (iloop++ < 30) { 245 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 246 | thread_sleep(100); 247 | } 248 | 249 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_A, STRING_CONST("macdev.local."), databuf, sizeof(databuf), 0); 250 | 251 | iloop = 0; 252 | while (iloop++ < 30) { 253 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 254 | thread_sleep(100); 255 | } 256 | 257 | mdns_query_send(sock_mdns[isock], MDNS_RECORDTYPE_AAAA, STRING_CONST("macdev.local."), databuf, sizeof(databuf), 0); 258 | 259 | iloop = 0; 260 | while (iloop++ < 30) { 261 | mdns_query_recv(sock_mdns[isock], databuf, sizeof(databuf), query_callback, nullptr, 0); 262 | thread_sleep(100); 263 | } 264 | } 265 | 266 | for (size_t isock = 0; isock < sock_count; ++isock) 267 | socket_deallocate(sock_mdns[isock]); 268 | 269 | for (size_t iaddr = 0, acount = array_size(local_address); iaddr < acount; ++iaddr) 270 | network_address_deallocate(local_address[iaddr]); 271 | array_deallocate(local_address); 272 | 273 | return 0; 274 | } 275 | 276 | static void 277 | test_dnssd_declare(void) { 278 | ADD_TEST(dnssd, discover); 279 | ADD_TEST(dnssd, query); 280 | } 281 | 282 | static test_suite_t test_dnssd_suite = {test_dnssd_application, 283 | test_dnssd_memory_system, 284 | test_dnssd_foundation_config, 285 | test_dnssd_declare, 286 | test_dnssd_initialize, 287 | test_dnssd_finalize, 288 | 0}; 289 | 290 | #if BUILD_MONOLITHIC 291 | 292 | int 293 | test_dnssd_run(void); 294 | 295 | int 296 | test_dnssd_run(void) { 297 | test_suite = test_dnssd_suite; 298 | return test_run_all(); 299 | } 300 | 301 | #else 302 | 303 | test_suite_t 304 | test_suite_define(void); 305 | 306 | test_suite_t 307 | test_suite_define(void) { 308 | return test_dnssd_suite; 309 | } 310 | 311 | #endif 312 | -------------------------------------------------------------------------------- /test/all/main.c: -------------------------------------------------------------------------------- 1 | /* main.c - Foundation test launcher - Public Domain - 2013 Mattias Jansson 2 | * 3 | * This library provides a cross-platform foundation library in C11 providing basic support 4 | * data types and functions to write applications and games in a platform-independent fashion. 5 | * The latest source code is always available at 6 | * 7 | * https://github.com/mjansson/foundation_lib 8 | * 9 | * This library is put in the public domain; you can redistribute it and/or modify it without 10 | * any restrictions. 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | static volatile bool _test_should_start; 19 | static volatile bool _test_have_focus; 20 | static volatile bool _test_should_terminate; 21 | 22 | static void* 23 | event_loop(void* arg) { 24 | event_block_t* block; 25 | event_t* event = 0; 26 | FOUNDATION_UNUSED(arg); 27 | 28 | event_stream_set_beacon(system_event_stream(), &thread_self()->beacon); 29 | 30 | while (!_test_should_terminate) { 31 | block = event_stream_process(system_event_stream()); 32 | event = 0; 33 | while ((event = event_next(block, event))) { 34 | switch (event->id) { 35 | case FOUNDATIONEVENT_START: 36 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 37 | log_debug(HASH_TEST, STRING_CONST("Application start event received")); 38 | _test_should_start = true; 39 | #endif 40 | break; 41 | 42 | case FOUNDATIONEVENT_TERMINATE: 43 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 44 | log_debug(HASH_TEST, STRING_CONST("Application stop/terminate event received")); 45 | _test_should_terminate = true; 46 | break; 47 | #else 48 | log_warn(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Terminating tests due to event")); 49 | process_exit(-2); 50 | #endif 51 | 52 | case FOUNDATIONEVENT_FOCUS_GAIN: 53 | _test_have_focus = true; 54 | break; 55 | 56 | case FOUNDATIONEVENT_FOCUS_LOST: 57 | _test_have_focus = false; 58 | break; 59 | 60 | default: 61 | break; 62 | } 63 | } 64 | thread_wait(); 65 | } 66 | 67 | log_debug(HASH_TEST, STRING_CONST("Application event thread exiting")); 68 | 69 | return 0; 70 | } 71 | 72 | #if (FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID) && BUILD_ENABLE_LOG 73 | 74 | #if FOUNDATION_PLATFORM_ANDROID 75 | #include 76 | #include 77 | #endif 78 | 79 | #include 80 | #include 81 | 82 | static void 83 | test_log_handler(hash_t context, error_level_t severity, const char* msg, size_t length) { 84 | FOUNDATION_UNUSED(context); 85 | FOUNDATION_UNUSED(severity); 86 | 87 | if (_test_should_terminate) 88 | return; 89 | 90 | #if FOUNDATION_PLATFORM_IOS 91 | test_text_view_append(delegate_uiwindow(), 1, msg, length); 92 | #elif FOUNDATION_PLATFORM_ANDROID 93 | jclass _test_log_class = 0; 94 | jmethodID _test_log_append = 0; 95 | const struct JNINativeInterface** jnienv = thread_attach_jvm(); 96 | _test_log_class = (*jnienv)->GetObjectClass(jnienv, android_app()->activity->clazz); 97 | if (_test_log_class) 98 | _test_log_append = (*jnienv)->GetMethodID(jnienv, _test_log_class, "appendLog", "(Ljava/lang/String;)V"); 99 | if (_test_log_append) { 100 | jstring jstr = (*jnienv)->NewStringUTF(jnienv, msg); 101 | (*jnienv)->CallVoidMethod(jnienv, android_app()->activity->clazz, _test_log_append, jstr); 102 | (*jnienv)->DeleteLocalRef(jnienv, jstr); 103 | } 104 | thread_detach_jvm(); 105 | FOUNDATION_UNUSED(length); 106 | #endif 107 | } 108 | 109 | #endif 110 | 111 | #if !BUILD_MONOLITHIC 112 | 113 | void 114 | test_exception_handler(const char* dump_file, size_t length) { 115 | FOUNDATION_UNUSED(dump_file); 116 | FOUNDATION_UNUSED(length); 117 | log_error(HASH_TEST, ERROR_EXCEPTION, STRING_CONST("Test crashed")); 118 | process_exit(-1); 119 | } 120 | 121 | #endif 122 | 123 | bool 124 | test_should_terminate(void) { 125 | return _test_should_terminate; 126 | } 127 | 128 | int 129 | main_initialize(void) { 130 | foundation_config_t config; 131 | network_config_t network_config; 132 | application_t application; 133 | 134 | memset(&config, 0, sizeof(config)); 135 | memset(&network_config, 0, sizeof(network_config)); 136 | 137 | memset(&application, 0, sizeof(application)); 138 | application.name = string_const(STRING_CONST("mDNS library test suite")); 139 | application.short_name = string_const(STRING_CONST("test_all")); 140 | application.company = string_const(STRING_CONST("")); 141 | application.version = foundation_version(); 142 | application.flags = APPLICATION_UTILITY; 143 | application.exception_handler = test_exception_handler; 144 | 145 | log_set_suppress(0, ERRORLEVEL_INFO); 146 | 147 | #if (FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID) && BUILD_ENABLE_LOG 148 | log_set_handler(test_log_handler); 149 | #endif 150 | 151 | #if !FOUNDATION_PLATFORM_IOS && !FOUNDATION_PLATFORM_ANDROID 152 | 153 | _test_should_start = true; 154 | 155 | #endif 156 | 157 | if (foundation_initialize(memory_system_malloc(), application, config) < 0) 158 | return -1; 159 | 160 | #if BUILD_MONOLITHIC 161 | network_config_t network_config; 162 | memset(&network_config, 0, sizeof(network_config)); 163 | if (ret == 0) 164 | ret = network_module_initalize(&network_config); 165 | 166 | mdns_config_t mdns_config; 167 | memset(&mdns_config, 0, sizeof(mdns_config_t)); 168 | if (ret == 0) 169 | ret = mdns_module_initalize(&mdns_config); 170 | 171 | test_set_suitable_working_directory(); 172 | #endif 173 | 174 | return network_module_initialize(network_config); 175 | } 176 | 177 | #if FOUNDATION_PLATFORM_ANDROID 178 | #include 179 | #endif 180 | 181 | #if BUILD_MONOLITHIC 182 | extern int 183 | test_dnssd_run(void); 184 | typedef int (*test_run_fn)(void); 185 | 186 | static void* 187 | test_runner(void* arg) { 188 | test_run_fn* tests = (test_run_fn*)arg; 189 | int test_fn = 0; 190 | int process_result = 0; 191 | 192 | while (tests[test_fn] && (process_result >= 0)) { 193 | if ((process_result = tests[test_fn]()) >= 0) 194 | log_infof(HASH_TEST, STRING_CONST("All tests passed (%d)"), process_result); 195 | ++test_fn; 196 | } 197 | 198 | return (void*)(intptr_t)process_result; 199 | } 200 | 201 | #endif 202 | 203 | int 204 | main_run(void* main_arg) { 205 | #if !BUILD_MONOLITHIC 206 | string_const_t pattern; 207 | string_t* exe_paths = 0; 208 | size_t iexe, exesize; 209 | process_t* process = 0; 210 | string_t process_path = {0, 0}; 211 | unsigned int* exe_flags = 0; 212 | #else 213 | void* test_result; 214 | #endif 215 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 216 | int remain_counter = 0; 217 | #endif 218 | #if BUILD_DEBUG 219 | const string_const_t build_name = string_const(STRING_CONST("debug")); 220 | #elif BUILD_RELEASE 221 | const string_const_t build_name = string_const(STRING_CONST("release")); 222 | #elif BUILD_PROFILE 223 | const string_const_t build_name = string_const(STRING_CONST("profile")); 224 | #elif BUILD_DEPLOY 225 | const string_const_t build_name = string_const(STRING_CONST("deploy")); 226 | #endif 227 | char* pathbuf; 228 | int process_result = 0; 229 | thread_t event_thread; 230 | FOUNDATION_UNUSED(main_arg); 231 | FOUNDATION_UNUSED(build_name); 232 | 233 | log_set_suppress(HASH_TEST, ERRORLEVEL_DEBUG); 234 | 235 | log_infof(HASH_TEST, STRING_CONST("Network library v%s built for %s using %s (%s)"), 236 | string_from_version_static(network_module_version()).str, FOUNDATION_PLATFORM_DESCRIPTION, 237 | FOUNDATION_COMPILER_DESCRIPTION, build_name.str); 238 | 239 | thread_initialize(&event_thread, event_loop, 0, STRING_CONST("event_thread"), THREAD_PRIORITY_NORMAL, 0); 240 | thread_start(&event_thread); 241 | 242 | pathbuf = memory_allocate(HASH_STRING, BUILD_MAX_PATHLEN, 0, MEMORY_PERSISTENT); 243 | 244 | while (!thread_is_running(&event_thread)) 245 | thread_sleep(10); 246 | 247 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 248 | while (!_test_should_start) { 249 | #if FOUNDATION_PLATFORM_ANDROID 250 | system_process_events(); 251 | #endif 252 | thread_sleep(100); 253 | } 254 | #endif 255 | 256 | fs_remove_directory(STRING_ARGS(environment_temporary_directory())); 257 | 258 | #if BUILD_MONOLITHIC 259 | 260 | test_run_fn tests[] = {test_dnssd_run, 0}; 261 | 262 | #if FOUNDATION_PLATFORM_ANDROID 263 | 264 | thread_t test_thread; 265 | thread_initialize(&test_thread, test_runner, tests, STRING_CONST("test_runner"), THREAD_PRIORITY_NORMAL, 0); 266 | thread_start(&test_thread); 267 | 268 | log_debug(HASH_TEST, STRING_CONST("Starting test runner thread")); 269 | 270 | while (!thread_is_running(&test_thread)) { 271 | system_process_events(); 272 | thread_sleep(10); 273 | } 274 | 275 | while (thread_is_running(&test_thread)) { 276 | system_process_events(); 277 | thread_sleep(10); 278 | } 279 | 280 | test_result = thread_join(&test_thread); 281 | process_result = (int)(intptr_t)test_result; 282 | 283 | thread_finalize(&test_thread); 284 | 285 | #else 286 | 287 | test_result = test_runner(tests); 288 | process_result = (int)(intptr_t)test_result; 289 | 290 | #endif 291 | 292 | if (process_result != 0) 293 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), process_result); 294 | 295 | #if FOUNDATION_PLATFORM_IOS || FOUNDATION_PLATFORM_ANDROID 296 | 297 | while (!_test_should_terminate && _test_have_focus && (remain_counter < 50)) { 298 | system_process_events(); 299 | thread_sleep(100); 300 | ++remain_counter; 301 | } 302 | 303 | #endif 304 | 305 | log_debug(HASH_TEST, STRING_CONST("Exiting main loop")); 306 | 307 | #else // !BUILD_MONOLITHIC 308 | 309 | // Find all test executables in the current executable directory 310 | #if FOUNDATION_PLATFORM_WINDOWS 311 | pattern = string_const(STRING_CONST("^test-.*\\.exe$")); 312 | #elif FOUNDATION_PLATFORM_MACOS 313 | pattern = string_const(STRING_CONST("^test-.*$")); 314 | #elif FOUNDATION_PLATFORM_POSIX 315 | pattern = string_const(STRING_CONST("^test-.*$")); 316 | #else 317 | #error Not implemented 318 | #endif 319 | exe_paths = fs_matching_files(STRING_ARGS(environment_executable_directory()), STRING_ARGS(pattern), false); 320 | array_resize(exe_flags, array_size(exe_paths)); 321 | memset(exe_flags, 0, sizeof(unsigned int) * array_size(exe_flags)); 322 | #if FOUNDATION_PLATFORM_MACOS 323 | // Also search for test applications 324 | string_const_t app_pattern = string_const(STRING_CONST("^test-.*\\.app$")); 325 | regex_t* app_regex = regex_compile(app_pattern.str, app_pattern.length); 326 | string_t* subdirs = fs_subdirs(STRING_ARGS(environment_executable_directory())); 327 | for (size_t idir = 0, dirsize = array_size(subdirs); idir < dirsize; ++idir) { 328 | if (regex_match(app_regex, subdirs[idir].str, subdirs[idir].length, 0, 0)) { 329 | string_t exe_path = {subdirs[idir].str, subdirs[idir].length - 4}; 330 | array_push(exe_paths, exe_path); 331 | array_push(exe_flags, PROCESS_MACOS_USE_OPENAPPLICATION); 332 | } 333 | } 334 | string_array_deallocate(subdirs); 335 | regex_deallocate(app_regex); 336 | #endif 337 | for (iexe = 0, exesize = array_size(exe_paths); iexe < exesize; ++iexe) { 338 | string_const_t exe_file_name = path_base_file_name(STRING_ARGS(exe_paths[iexe])); 339 | if (string_equal(STRING_ARGS(exe_file_name), STRING_ARGS(environment_executable_name()))) 340 | continue; // Don't run self 341 | 342 | process_path = path_concat(pathbuf, BUILD_MAX_PATHLEN, STRING_ARGS(environment_executable_directory()), 343 | STRING_ARGS(exe_paths[iexe])); 344 | process = process_allocate(); 345 | 346 | process_set_executable_path(process, STRING_ARGS(process_path)); 347 | process_set_working_directory(process, STRING_ARGS(environment_executable_directory())); 348 | process_set_flags(process, PROCESS_ATTACHED | exe_flags[iexe]); 349 | 350 | log_infof(HASH_TEST, STRING_CONST("Running test executable: %.*s"), STRING_FORMAT(exe_paths[iexe])); 351 | 352 | process_result = process_spawn(process); 353 | while (process_result == PROCESS_WAIT_INTERRUPTED) { 354 | thread_sleep(10); 355 | process_result = process_wait(process); 356 | } 357 | process_deallocate(process); 358 | 359 | if (process_result != 0) { 360 | if (process_result >= PROCESS_INVALID_ARGS) 361 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed, process terminated with error %x"), 362 | process_result); 363 | else 364 | log_warnf(HASH_TEST, WARNING_SUSPICIOUS, STRING_CONST("Tests failed with exit code %d"), 365 | process_result); 366 | process_set_exit_code(-1); 367 | goto exit; 368 | } 369 | 370 | log_infof(HASH_TEST, STRING_CONST("All tests from %.*s passed (%d)"), STRING_FORMAT(exe_paths[iexe]), 371 | process_result); 372 | } 373 | 374 | log_info(HASH_TEST, STRING_CONST("All tests passed")); 375 | 376 | exit: 377 | 378 | if (exe_paths) 379 | string_array_deallocate(exe_paths); 380 | array_deallocate(exe_flags); 381 | 382 | #endif 383 | 384 | _test_should_terminate = true; 385 | 386 | thread_signal(&event_thread); 387 | thread_finalize(&event_thread); 388 | 389 | memory_deallocate(pathbuf); 390 | 391 | log_infof(HASH_TEST, STRING_CONST("Tests exiting: %s (%d)"), process_result ? "FAILED" : "PASSED", process_result); 392 | 393 | return process_result; 394 | } 395 | 396 | void 397 | main_finalize(void) { 398 | #if FOUNDATION_PLATFORM_ANDROID 399 | thread_detach_jvm(); 400 | #endif 401 | 402 | #if BUILD_MONOLITHIC 403 | mdns_module_finalize(); 404 | network_module_finalize(); 405 | #endif 406 | foundation_finalize(); 407 | } 408 | -------------------------------------------------------------------------------- /mdns/query.c: -------------------------------------------------------------------------------- 1 | /* query.c - mDNS library - Public Domain - 2014 Mattias Jansson 2 | * 3 | * This library provides a cross-platform mDNS and DNS-SD library in C based 4 | * on our foundation and network libraries. The implementation is based on RFC 6762 5 | * and RFC 6763. 6 | * 7 | * The latest source code maintained by Mattias Jansson is always available at 8 | * 9 | * https://github.com/mjansson/mdns_lib 10 | * 11 | * The foundation and network library source code maintained by Mattias Jansson 12 | * is always available at 13 | * 14 | * https://github.com/mjansson/foundation_lib 15 | * https://github.com/mjansson/network_lib 16 | * 17 | * This library is put in the public domain; you can redistribute it and/or modify 18 | * it without any restrictions. 19 | * 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | int 27 | mdns_query_send(socket_t* sock, mdns_record_type_t type, const char* name, size_t length, void* buffer, size_t capacity, 28 | uint16_t query_id) { 29 | if (capacity < (17 + length)) 30 | return -1; 31 | 32 | uint16_t rclass = MDNS_CLASS_IN | MDNS_UNICAST_RESPONSE; 33 | 34 | struct sockaddr_storage addr_storage; 35 | struct sockaddr* saddr = (struct sockaddr*)&addr_storage; 36 | struct sockaddr_in* saddrin = (struct sockaddr_in*)&addr_storage; 37 | struct sockaddr_in6* saddrin6 = (struct sockaddr_in6*)&addr_storage; 38 | socklen_t saddrlen = sizeof(addr_storage); 39 | if (getsockname(sock->fd, saddr, &saddrlen) == 0) { 40 | if ((saddr->sa_family == AF_INET) && (ntohs(saddrin->sin_port) == MDNS_PORT)) 41 | rclass &= ~MDNS_UNICAST_RESPONSE; 42 | else if ((saddr->sa_family == AF_INET6) && (ntohs(saddrin6->sin6_port) == MDNS_PORT)) 43 | rclass &= ~MDNS_UNICAST_RESPONSE; 44 | } 45 | 46 | struct mdns_header_t* header = (struct mdns_header_t*)buffer; 47 | // Query ID 48 | header->query_id = htons(query_id); 49 | // Flags 50 | header->flags = 0; 51 | // Questions 52 | header->questions = htons(1); 53 | // No answer, authority or additional RRs 54 | header->answer_rrs = 0; 55 | header->authority_rrs = 0; 56 | header->additional_rrs = 0; 57 | // Fill in question 58 | // Name string 59 | void* data = pointer_offset(buffer, sizeof(struct mdns_header_t)); 60 | data = mdns_string_make(buffer, capacity, data, name, length, 0); 61 | if (!data) 62 | return -1; 63 | // Record type 64 | data = mdns_htons(data, (uint16_t)type); 65 | //! Optional unicast response based on local port, class IN 66 | data = mdns_htons(data, rclass); 67 | 68 | size_t tosend = (size_t)pointer_diff(data, buffer); 69 | if (mdns_multicast_send(sock, buffer, (size_t)tosend)) 70 | return -1; 71 | return query_id; 72 | } 73 | 74 | size_t 75 | mdns_query_recv(socket_t* sock, void* buffer, size_t capacity, mdns_record_callback_fn callback, void* user_data, 76 | int only_query_id) { 77 | const network_address_t* address = 0; 78 | size_t data_size = udp_socket_recvfrom(sock, buffer, capacity, &address); 79 | if (!data_size) 80 | return 0; 81 | 82 | const uint16_t* data = (const uint16_t*)buffer; 83 | 84 | uint16_t query_id = mdns_ntohs(data++); 85 | uint16_t flags = mdns_ntohs(data++); 86 | uint16_t questions = mdns_ntohs(data++); 87 | uint16_t answer_rrs = mdns_ntohs(data++); 88 | uint16_t authority_rrs = mdns_ntohs(data++); 89 | uint16_t additional_rrs = mdns_ntohs(data++); 90 | (void)sizeof(flags); 91 | 92 | if ((only_query_id > 0) && (query_id != only_query_id)) 93 | return 0; // Not a reply to the wanted one-shot query 94 | 95 | if (questions > 1) 96 | return 0; 97 | 98 | // Skip questions part 99 | int i; 100 | for (i = 0; i < questions; ++i) { 101 | size_t offset = (size_t)pointer_diff(data, buffer); 102 | if (!mdns_string_skip(buffer, data_size, &offset)) 103 | return 0; 104 | data = pointer_offset_const(buffer, offset); 105 | /* Record type and class not used, skip 106 | uint16_t rtype = mdns_ntohs(data++); 107 | uint16_t rclass = mdns_ntohs(data++);*/ 108 | data += 2; 109 | } 110 | 111 | size_t total_records = 0; 112 | size_t records = 0; 113 | size_t offset = (size_t)pointer_diff(data, buffer); 114 | records = mdns_records_parse(sock, address, buffer, data_size, &offset, MDNS_ENTRYTYPE_ANSWER, query_id, answer_rrs, 115 | callback, user_data); 116 | total_records += records; 117 | if (records != answer_rrs) 118 | return total_records; 119 | 120 | records = mdns_records_parse(sock, address, buffer, data_size, &offset, MDNS_ENTRYTYPE_AUTHORITY, query_id, 121 | authority_rrs, callback, user_data); 122 | total_records += records; 123 | if (records != authority_rrs) 124 | return total_records; 125 | 126 | records = mdns_records_parse(sock, address, buffer, data_size, &offset, MDNS_ENTRYTYPE_ADDITIONAL, query_id, 127 | additional_rrs, callback, user_data); 128 | total_records += records; 129 | if (records != additional_rrs) 130 | return total_records; 131 | 132 | if (callback) 133 | callback(sock, address, MDNS_ENTRYTYPE_END, query_id, 0, 0, 0, 0, 0, 0, 0, 0, 0, user_data); 134 | 135 | return total_records; 136 | } 137 | 138 | static void* 139 | mdns_answer_add_question_unicast(void* buffer, size_t capacity, void* data, mdns_record_type_t record_type, 140 | const char* name, size_t name_length, mdns_string_table_t* string_table) { 141 | data = mdns_string_make(buffer, capacity, data, name, name_length, string_table); 142 | if (!data) 143 | return 0; 144 | size_t remain = capacity - (size_t)pointer_diff(data, buffer); 145 | if (remain <= 4) 146 | return 0; 147 | 148 | data = mdns_htons(data, (uint16_t)record_type); 149 | data = mdns_htons(data, MDNS_UNICAST_RESPONSE | MDNS_CLASS_IN); 150 | 151 | return data; 152 | } 153 | 154 | static inline void 155 | mdns_record_update_rclass_ttl(mdns_record_t* record, uint16_t rclass, uint32_t ttl) { 156 | if (!record->rclass) 157 | record->rclass = rclass; 158 | if (!record->ttl || !ttl) 159 | record->ttl = ttl; 160 | record->rclass &= (uint16_t)(MDNS_CLASS_IN | MDNS_CACHE_FLUSH); 161 | // Never flush PTR record 162 | if (record->type == MDNS_RECORDTYPE_PTR) 163 | record->rclass &= ~(uint16_t)MDNS_CACHE_FLUSH; 164 | } 165 | 166 | static void* 167 | mdns_answer_add_record_header(void* buffer, size_t capacity, void* data, mdns_record_t record, 168 | mdns_string_table_t* string_table) { 169 | data = mdns_string_make(buffer, capacity, data, record.name.str, record.name.length, string_table); 170 | if (!data) 171 | return 0; 172 | size_t remain = capacity - (size_t)pointer_diff(data, buffer); 173 | if (remain < 10) 174 | return 0; 175 | 176 | data = mdns_htons(data, (uint16_t)record.type); 177 | data = mdns_htons(data, (uint16_t)record.rclass); 178 | data = mdns_htonl(data, record.ttl); 179 | data = mdns_htons(data, 0); // Length, to be filled later 180 | return data; 181 | } 182 | 183 | static void* 184 | mdns_answer_add_record(void* buffer, size_t capacity, void* data, mdns_record_t record, 185 | mdns_string_table_t* string_table) { 186 | // TXT records will be coalesced into one record later 187 | if (!data || (record.type == MDNS_RECORDTYPE_TXT)) 188 | return data; 189 | 190 | data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); 191 | if (!data) 192 | return 0; 193 | 194 | // Pointer to length of record to be filled at end 195 | void* record_length = pointer_offset(data, -2); 196 | void* record_data = data; 197 | 198 | size_t remain = capacity - (size_t)pointer_diff(data, buffer); 199 | switch (record.type) { 200 | case MDNS_RECORDTYPE_PTR: 201 | data = mdns_string_make(buffer, capacity, data, record.data.ptr.name.str, record.data.ptr.name.length, 202 | string_table); 203 | break; 204 | 205 | case MDNS_RECORDTYPE_SRV: 206 | if (remain <= 6) 207 | return 0; 208 | data = mdns_htons(data, record.data.srv.priority); 209 | data = mdns_htons(data, record.data.srv.weight); 210 | data = mdns_htons(data, record.data.srv.port); 211 | data = mdns_string_make(buffer, capacity, data, record.data.srv.name.str, record.data.srv.name.length, 212 | string_table); 213 | break; 214 | 215 | case MDNS_RECORDTYPE_A: 216 | if (remain < 4) 217 | return 0; 218 | memcpy(data, &record.data.a.addr.sin_addr.s_addr, 4); 219 | data = pointer_offset(data, 4); 220 | break; 221 | 222 | case MDNS_RECORDTYPE_AAAA: 223 | if (remain < 16) 224 | return 0; 225 | memcpy(data, &record.data.aaaa.addr.sin6_addr, 16); // ipv6 address 226 | data = pointer_offset(data, 16); 227 | break; 228 | 229 | case MDNS_RECORDTYPE_TXT: 230 | case MDNS_RECORDTYPE_ANY: 231 | case MDNS_RECORDTYPE_IGNORE: 232 | default: 233 | break; 234 | } 235 | 236 | if (!data) 237 | return 0; 238 | 239 | // Fill record length 240 | mdns_htons(record_length, (uint16_t)pointer_diff(data, record_data)); 241 | return data; 242 | } 243 | 244 | static void* 245 | mdns_answer_add_txt_record(void* buffer, size_t capacity, void* data, const mdns_record_t* records, 246 | size_t record_count, uint16_t rclass, uint32_t ttl, 247 | mdns_string_table_t* string_table) { 248 | // Pointer to length of record to be filled at end 249 | void* record_length = 0; 250 | void* record_data = 0; 251 | 252 | size_t remain = 0; 253 | for (size_t irec = 0; data && (irec < record_count); ++irec) { 254 | if (records[irec].type != MDNS_RECORDTYPE_TXT) 255 | continue; 256 | 257 | mdns_record_t record = records[irec]; 258 | mdns_record_update_rclass_ttl(&record, rclass, ttl); 259 | if (!record_data) { 260 | data = mdns_answer_add_record_header(buffer, capacity, data, record, string_table); 261 | if (!data) 262 | return data; 263 | record_length = pointer_offset(data, -2); 264 | record_data = data; 265 | } 266 | 267 | // TXT strings are unlikely to be shared, just make then raw. Also need one byte for 268 | // termination, thus the <= check 269 | size_t string_length = record.data.txt.key.length + record.data.txt.value.length + 1; 270 | remain = capacity - (size_t)pointer_diff(data, buffer); 271 | if (!data || (remain <= string_length) || (string_length > 0x3FFF)) 272 | return 0; 273 | 274 | unsigned char* strdata = (unsigned char*)data; 275 | *strdata++ = (unsigned char)string_length; 276 | memcpy(strdata, record.data.txt.key.str, record.data.txt.key.length); 277 | strdata += record.data.txt.key.length; 278 | *strdata++ = '='; 279 | memcpy(strdata, record.data.txt.value.str, record.data.txt.value.length); 280 | strdata += record.data.txt.value.length; 281 | 282 | data = strdata; 283 | } 284 | 285 | // Fill record length 286 | if (record_data) 287 | mdns_htons(record_length, (uint16_t)pointer_diff(data, record_data)); 288 | 289 | return data; 290 | } 291 | 292 | static uint16_t 293 | mdns_answer_get_record_count(const mdns_record_t* records, size_t record_count) { 294 | // TXT records will be coalesced into one record 295 | uint16_t total_count = 0; 296 | uint16_t txt_record = 0; 297 | for (size_t irec = 0; irec < record_count; ++irec) { 298 | if (records[irec].type == MDNS_RECORDTYPE_TXT) 299 | txt_record = 1; 300 | else 301 | ++total_count; 302 | } 303 | return total_count + txt_record; 304 | } 305 | 306 | int 307 | mdns_query_answer_unicast(socket_t* sock, const network_address_t* address, void* buffer, size_t capacity, 308 | uint16_t query_id, mdns_record_type_t record_type, const char* name, size_t name_length, 309 | mdns_record_t answer, const mdns_record_t* authority, size_t authority_count, 310 | const mdns_record_t* additional, size_t additional_count) { 311 | if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) 312 | return -1; 313 | 314 | // According to RFC 6762: 315 | // The cache-flush bit MUST NOT be set in any resource records in a response message 316 | // sent in legacy unicast responses to UDP ports other than 5353. 317 | uint16_t rclass = MDNS_CLASS_IN; 318 | uint32_t ttl = 10; 319 | 320 | // Basic answer structure 321 | struct mdns_header_t* header = (struct mdns_header_t*)buffer; 322 | header->query_id = htons(query_id); 323 | header->flags = htons(0x8400); 324 | header->questions = htons(1); 325 | header->answer_rrs = htons(1); 326 | header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); 327 | header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); 328 | 329 | mdns_string_table_t string_table = {0}; 330 | void* data = pointer_offset(buffer, sizeof(struct mdns_header_t)); 331 | 332 | // Fill in question 333 | data = mdns_answer_add_question_unicast(buffer, capacity, data, record_type, name, name_length, &string_table); 334 | 335 | // Fill in answer 336 | answer.rclass = rclass; 337 | answer.ttl = ttl; 338 | data = mdns_answer_add_record(buffer, capacity, data, answer, &string_table); 339 | 340 | // Fill in authority records 341 | for (size_t irec = 0; data && (irec < authority_count); ++irec) { 342 | mdns_record_t record = authority[irec]; 343 | record.rclass = rclass; 344 | if (!record.ttl) 345 | record.ttl = ttl; 346 | data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); 347 | } 348 | data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, ttl, &string_table); 349 | 350 | // Fill in additional records 351 | for (size_t irec = 0; data && (irec < additional_count); ++irec) { 352 | mdns_record_t record = additional[irec]; 353 | record.rclass = rclass; 354 | if (!record.ttl) 355 | record.ttl = ttl; 356 | data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); 357 | } 358 | data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, ttl, &string_table); 359 | if (!data) 360 | return -1; 361 | 362 | size_t tosend = (size_t)pointer_diff(data, buffer); 363 | return mdns_unicast_send(sock, address, buffer, tosend); 364 | } 365 | 366 | static int 367 | mdns_answer_multicast_rclass_ttl(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 368 | const mdns_record_t* authority, size_t authority_count, 369 | const mdns_record_t* additional, size_t additional_count, 370 | uint16_t rclass, uint32_t ttl) { 371 | if (capacity < (sizeof(struct mdns_header_t) + 32 + 4)) 372 | return -1; 373 | 374 | // Basic answer structure 375 | struct mdns_header_t* header = (struct mdns_header_t*)buffer; 376 | header->query_id = 0; 377 | header->flags = htons(0x8400); 378 | header->questions = 0; 379 | header->answer_rrs = htons(1); 380 | header->authority_rrs = htons(mdns_answer_get_record_count(authority, authority_count)); 381 | header->additional_rrs = htons(mdns_answer_get_record_count(additional, additional_count)); 382 | 383 | mdns_string_table_t string_table = {0}; 384 | void* data = pointer_offset(buffer, sizeof(struct mdns_header_t)); 385 | 386 | // Fill in answer 387 | mdns_record_t record = answer; 388 | mdns_record_update_rclass_ttl(&record, rclass, ttl); 389 | data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); 390 | 391 | // Fill in authority records 392 | for (size_t irec = 0; data && (irec < authority_count); ++irec) { 393 | record = authority[irec]; 394 | mdns_record_update_rclass_ttl(&record, rclass, ttl); 395 | data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); 396 | } 397 | data = mdns_answer_add_txt_record(buffer, capacity, data, authority, authority_count, rclass, ttl, &string_table); 398 | 399 | // Fill in additional records 400 | for (size_t irec = 0; data && (irec < additional_count); ++irec) { 401 | record = additional[irec]; 402 | mdns_record_update_rclass_ttl(&record, rclass, ttl); 403 | data = mdns_answer_add_record(buffer, capacity, data, record, &string_table); 404 | } 405 | data = mdns_answer_add_txt_record(buffer, capacity, data, additional, additional_count, rclass, ttl, &string_table); 406 | if (!data) 407 | return -1; 408 | 409 | size_t tosend = (size_t)pointer_diff(data, buffer); 410 | return mdns_multicast_send(sock, buffer, tosend); 411 | } 412 | 413 | int 414 | mdns_query_answer_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 415 | const mdns_record_t* authority, size_t authority_count, 416 | const mdns_record_t* additional, size_t additional_count) { 417 | return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, authority_count, additional, 418 | additional_count, MDNS_CLASS_IN, 60); 419 | } 420 | 421 | int 422 | mdns_announce_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 423 | const mdns_record_t* authority, size_t authority_count, 424 | const mdns_record_t* additional, size_t additional_count) { 425 | return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, authority_count, additional, 426 | additional_count, MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 60); 427 | } 428 | 429 | int 430 | mdns_goodbye_multicast(socket_t* sock, void* buffer, size_t capacity, mdns_record_t answer, 431 | const mdns_record_t* authority, size_t authority_count, 432 | const mdns_record_t* additional, size_t additional_count) { 433 | // Goodbye should have ttl of 0 434 | return mdns_answer_multicast_rclass_ttl(sock, buffer, capacity, answer, authority, 435 | authority_count, additional, additional_count, 436 | MDNS_CLASS_IN | MDNS_CACHE_FLUSH, 0); 437 | } 438 | --------------------------------------------------------------------------------