├── 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 |
--------------------------------------------------------------------------------