1#ifndef ZEROCONF_DETAIL_HPP
2#define ZEROCONF_DETAIL_HPP
15#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
20#define WIN32_LEAN_AND_MEAN
26#include <mach/error.h>
31#include <sys/socket.h>
32#include <netinet/in.h>
35#include "zeroconf-util.hpp"
39const size_t MdnsMessageMaxLength = 512;
40const size_t MdnsRecordHeaderLength = 12;
42const uint8_t MdnsOffsetToken = 0xC0;
43const uint16_t MdnsResponseFlag = 0x8400;
45const uint32_t SockTrue = 1;
47const uint8_t MdnsQueryHeader[] = {
56const uint8_t MdnsQueryFooter[] = {
62 sockaddr_storage peer;
63 std::vector<uint8_t> data;
74 sockaddr_storage peer;
77 std::vector<uint8_t> data;
78 std::vector<mdns_record> records;
81inline int GetSocketError() {
83 return WSAGetLastError();
89inline void CloseSocket(
int fd) {
97inline void WriteFqdn(
const std::string& name, std::vector<uint8_t>* result) {
99 size_t pos = result->size();
100 result->push_back(0);
102 for (
size_t i = 0; i < name.size(); i++) {
103 if (name[i] !=
'.') {
104 result->push_back(name[i]);
107 if (len > UINT8_MAX) {
113 if (name[i] ==
'.' || i == name.size() - 1) {
114 if (len == 0)
continue;
116 result->at(pos) = len;
119 pos = result->size();
120 result->push_back(0);
125inline size_t ReadFqdn(
const std::vector<uint8_t>& data,
size_t offset,
126 std::string* result) {
131 if (pos >= data.size())
return 0;
133 uint8_t len = data[pos++];
135 if (pos + len > data.size())
return 0;
139 if (!result->empty()) result->append(
".");
141 result->append(
reinterpret_cast<const char*
>(&data[pos]), len);
148inline bool CreateSocket(
int* result) {
149 int fd = socket(AF_INET, SOCK_DGRAM, 0);
151 Log::Error(
"Failed to create socket with code " +
152 std::to_string(GetSocketError()));
157 setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
158 reinterpret_cast<const char*
>(&SockTrue),
sizeof(SockTrue));
161 Log::Error(
"Failed to set socket option SO_BROADCAST with code " +
162 std::to_string(GetSocketError()));
170inline bool Send(
int fd,
const std::vector<uint8_t>& data) {
171 sockaddr_in broadcastAddr = {0};
172 broadcastAddr.sin_family = AF_INET;
173 broadcastAddr.sin_port = htons(5353);
174 broadcastAddr.sin_addr.s_addr = INADDR_BROADCAST;
176 auto st = sendto(fd,
reinterpret_cast<const char*
>(&data[0]), data.size(), 0,
177 reinterpret_cast<const sockaddr*
>(&broadcastAddr),
178 sizeof(broadcastAddr));
182 Log::Error(
"Failed to send the query with code " +
183 std::to_string(GetSocketError()));
190inline bool Receive(
int fd, time_t scanTime,
191 std::vector<raw_responce>* result) {
192 auto start = std::chrono::system_clock::now();
195 auto now = std::chrono::system_clock::now();
196 if (now - start > std::chrono::seconds(scanTime))
break;
203 tv.tv_sec =
static_cast<long>(scanTime);
205 int st = select(fd + 1, &fds,
nullptr,
nullptr, &tv);
208 Log::Error(
"Failed to wait on socket with code " +
209 std::to_string(GetSocketError()));
215 int salen =
sizeof(sockaddr_storage);
217 unsigned int salen =
sizeof(sockaddr_storage);
221 item.data.resize(MdnsMessageMaxLength);
223#ifndef __OCPN__ANDROID__
225 recvfrom(fd,
reinterpret_cast<char*
>(&item.data[0]), item.data.size(),
226 0,
reinterpret_cast<sockaddr*
>(&item.peer), &salen);
231 fd,
reinterpret_cast<char*
>(&item.data[0]), item.data.size(), 0,
232 reinterpret_cast<sockaddr*
>(&item.peer), (
unsigned int*)&salen);
235 recvfrom(fd,
reinterpret_cast<char*
>(&item.data[0]), item.data.size(),
236 0,
reinterpret_cast<sockaddr*
>(&item.peer), (
int*)&salen);
241 Log::Error(
"Failed to receive with code " +
242 std::to_string(GetSocketError()));
246 item.data.resize((
size_t)cb);
247 result->push_back(item);
253inline bool Parse(
const raw_responce& input, mdns_responce* result) {
266 if (input.data.empty())
return false;
268 result->qname.clear();
269 result->records.clear();
271 memcpy(&result->peer, &input.peer,
sizeof(sockaddr_storage));
272 result->data = input.data;
274 stdext::membuf buf(&input.data[0], input.data.size());
275 std::istream is(&buf);
278 std::istream::failbit | std::istream::badbit | std::istream::eofbit;
279 is.exceptions(Flags);
288 is.read(
reinterpret_cast<char*
>(&u16), 2);
289 if (ntohs(u16) != MdnsResponseFlag) {
290 Log::Warning(
"Found unexpected Flags value while parsing responce");
294 for (
auto i = 0; i < 8; i++)
298 ReadFqdn(input.data,
static_cast<size_t>(is.tellg()), &result->qname);
300 Log::Error(
"Failed to parse query name");
304 for (
unsigned int i = 0; i < cb; i++) is.ignore();
306 is.read(
reinterpret_cast<char*
>(&u16), 2);
307 result->qtype = ntohs(u16);
313 is.exceptions(std::istream::goodbit);
314 if (is.peek() == EOF)
break;
315 is.exceptions(Flags);
317 mdns_record rr = {0};
318 rr.pos =
static_cast<size_t>(is.tellg());
320 is.read(
reinterpret_cast<char*
>(&u8), 1);
321 if (u8 != MdnsOffsetToken) {
322 Log::Warning(
"Found incorrect offset token while parsing responce");
326 is.read(
reinterpret_cast<char*
>(&u8), 1);
327 if ((
size_t)u8 >= input.data.size() ||
328 (
size_t)u8 + input.data[u8] >= input.data.size()) {
329 Log::Warning(
"Failed to parse record name");
333 rr.name = std::string(
reinterpret_cast<const char*
>(&input.data[u8 + 1]),
336 is.read(
reinterpret_cast<char*
>(&u16), 2);
337 rr.type = ntohs(u16);
339 for (
auto i = 0; i < 6; i++) is.ignore();
341 is.read(
reinterpret_cast<char*
>(&u16), 2);
343 for (
auto i = 0; i < ntohs(u16); i++) is.ignore();
345 rr.len = MdnsRecordHeaderLength + ntohs(u16);
346 result->records.push_back(rr);
348 }
catch (
const std::istream::failure& ex) {
349 Log::Warning(std::string(
"Stream error while parsing responce: ") +
357inline bool Resolve(
const std::string& serviceName, time_t scanTime,
358 std::vector<mdns_responce>* result) {
361 std::vector<uint8_t> query;
362 query.insert(query.end(), std::begin(MdnsQueryHeader),
363 std::end(MdnsQueryHeader));
364 WriteFqdn(serviceName, &query);
365 query.insert(query.end(), std::begin(MdnsQueryFooter),
366 std::end(MdnsQueryFooter));
369 if (!CreateSocket(&fd))
return false;
371 std::shared_ptr<void> guard(0, [fd](
void*) { CloseSocket(fd); });
373 if (!Send(fd, query))
return false;
375 std::vector<raw_responce> responces;
376 if (!Receive(fd, scanTime, &responces))
return false;
378 for (
auto& raw : responces) {
379 mdns_responce parsed = {{0}};
380 if (Parse(raw, &parsed)) result->push_back(parsed);