1/*
2 * Copyright (C) 2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NetworkMDNSRegister.h"
28
29#if ENABLE(WEB_RTC)
30
31#include "Logging.h"
32#include "NetworkConnectionToWebProcess.h"
33#include "WebMDNSRegisterMessages.h"
34#include <pal/SessionID.h>
35#include <wtf/UUID.h>
36#include <wtf/text/StringConcatenateNumbers.h>
37
38namespace WebKit {
39
40#define RELEASE_LOG_IF_ALLOWED(sessionID, fmt, ...) RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), Network, "%p - NetworkMDNSRegister::" fmt, this, ##__VA_ARGS__)
41#define RELEASE_LOG_IF_ALLOWED_IN_CALLBACK(sessionID, fmt, ...) RELEASE_LOG_IF(sessionID.isAlwaysOnLoggingAllowed(), Network, "NetworkMDNSRegister callback - " fmt, ##__VA_ARGS__)
42
43NetworkMDNSRegister::NetworkMDNSRegister(NetworkConnectionToWebProcess& connection)
44 : m_connection(connection)
45{
46}
47
48NetworkMDNSRegister::~NetworkMDNSRegister()
49{
50#if ENABLE_MDNS
51 for (auto& value : m_services.values())
52 DNSServiceRefDeallocate(value);
53#endif
54}
55
56#if ENABLE_MDNS
57void NetworkMDNSRegister::unregisterMDNSNames(WebCore::DocumentIdentifier documentIdentifier)
58{
59 auto iterator = m_services.find(documentIdentifier);
60 if (iterator == m_services.end())
61 return;
62 DNSServiceRefDeallocate(iterator->value);
63 m_services.remove(iterator);
64}
65
66struct PendingRegistrationRequest {
67 PendingRegistrationRequest(Ref<IPC::Connection> connection, uint64_t requestIdentifier, String&& name, PAL::SessionID sessionID)
68 : connection(WTFMove(connection))
69 , requestIdentifier(requestIdentifier)
70 , name(WTFMove(name))
71 , sessionID(sessionID)
72 {
73 }
74
75 Ref<IPC::Connection> connection;
76 uint64_t requestIdentifier { 0 };
77 String name;
78 PAL::SessionID sessionID;
79 DNSRecordRef record;
80};
81
82
83static uintptr_t pendingRegistrationRequestCount = 1;
84static HashMap<uintptr_t, std::unique_ptr<PendingRegistrationRequest>>& pendingRegistrationRequests()
85{
86 ASSERT(RunLoop::isMain());
87 static NeverDestroyed<HashMap<uintptr_t, std::unique_ptr<PendingRegistrationRequest>>> map;
88 return map;
89}
90
91static void registerMDNSNameCallback(DNSServiceRef, DNSRecordRef record, DNSServiceFlags, DNSServiceErrorType errorCode, void *context)
92{
93 auto request = pendingRegistrationRequests().take(reinterpret_cast<uintptr_t>(context));
94 if (!request)
95 return;
96
97 RELEASE_LOG_IF_ALLOWED_IN_CALLBACK(request->sessionID, "registerMDNSNameCallback with error %d", errorCode);
98
99 if (errorCode) {
100 request->connection->send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { request->requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::DNSSD) }, 0);
101 return;
102 }
103 request->connection->send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { request->requestIdentifier, request->name }, 0);
104}
105
106void NetworkMDNSRegister::registerMDNSName(uint64_t requestIdentifier, PAL::SessionID sessionID, WebCore::DocumentIdentifier documentIdentifier, const String& ipAddress)
107{
108 DNSServiceRef service;
109 auto iterator = m_services.find(documentIdentifier);
110 if (iterator == m_services.end()) {
111 auto error = DNSServiceCreateConnection(&service);
112 if (error) {
113 RELEASE_LOG_IF_ALLOWED(sessionID, "registerMDNSName DNSServiceCreateConnection error %d", error);
114 m_connection.connection().send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::DNSSD) }, 0);
115 return;
116 }
117 error = DNSServiceSetDispatchQueue(service, dispatch_get_main_queue());
118 if (error) {
119 RELEASE_LOG_IF_ALLOWED(sessionID, "registerMDNSName DNSServiceCreateConnection error %d", error);
120 m_connection.connection().send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::DNSSD) }, 0);
121 return;
122 }
123 ASSERT(service);
124 m_services.add(documentIdentifier, service);
125 } else
126 service = iterator->value;
127
128 String name = makeString(createCanonicalUUIDString(), pendingRegistrationRequestCount, ".local");
129
130 auto ip = inet_addr(ipAddress.utf8().data());
131
132 if (ip == ( in_addr_t)(-1)) {
133 RELEASE_LOG_IF_ALLOWED(sessionID, "registerMDNSName inet_addr error");
134 m_connection.connection().send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::BadParameter) }, 0);
135 return;
136 }
137
138 auto pendingRequest = std::make_unique<PendingRegistrationRequest>(makeRef(m_connection.connection()), requestIdentifier, WTFMove(name), sessionID);
139 auto* record = &pendingRequest->record;
140 auto error = DNSServiceRegisterRecord(service,
141 record,
142#if HAVE(MDNS_FAST_REGISTRATION)
143 kDNSServiceFlagsKnownUnique,
144#else
145 kDNSServiceFlagsUnique,
146#endif
147 0,
148 pendingRequest->name.utf8().data(),
149 kDNSServiceType_A,
150 kDNSServiceClass_IN,
151 4,
152 &ip,
153 0,
154 registerMDNSNameCallback,
155 reinterpret_cast<void*>(pendingRegistrationRequestCount));
156 if (error) {
157 RELEASE_LOG_IF_ALLOWED(sessionID, "registerMDNSName DNSServiceRegisterRecord error %d", error);
158 m_connection.connection().send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::DNSSD) }, 0);
159 return;
160 }
161 pendingRegistrationRequests().add(pendingRegistrationRequestCount++, WTFMove(pendingRequest));
162}
163
164#else
165
166void NetworkMDNSRegister::unregisterMDNSNames(WebCore::DocumentIdentifier)
167{
168}
169
170void NetworkMDNSRegister::registerMDNSName(uint64_t requestIdentifier, PAL::SessionID sessionID, WebCore::DocumentIdentifier documentIdentifier, const String& ipAddress)
171{
172 RELEASE_LOG_IF_ALLOWED(sessionID, "registerMDNSName not implemented");
173 m_connection.connection().send(Messages::WebMDNSRegister::FinishedRegisteringMDNSName { requestIdentifier, makeUnexpected(WebCore::MDNSRegisterError::NotImplemented) }, 0);
174}
175
176#endif
177
178} // namespace WebKit
179
180#undef RELEASE_LOG_IF_ALLOWED
181#undef RELEASE_LOG_IF_ALLOWED_IN_CALLBACK
182
183#endif // ENABLE(WEB_RTC)
184