1 | /*
|
---|
2 | * Copyright (C) 2021 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 | #import "config.h"
|
---|
27 | #import "WebPushToolConnection.h"
|
---|
28 |
|
---|
29 | #import "DaemonEncoder.h"
|
---|
30 | #import "DaemonUtilities.h"
|
---|
31 | #import "WebPushDaemonConstants.h"
|
---|
32 | #import <mach/mach_init.h>
|
---|
33 | #import <mach/task.h>
|
---|
34 | #import <pal/spi/cocoa/ServersSPI.h>
|
---|
35 | #import <wtf/MainThread.h>
|
---|
36 | #import <wtf/RetainPtr.h>
|
---|
37 |
|
---|
38 | namespace WebPushTool {
|
---|
39 |
|
---|
40 | std::unique_ptr<Connection> Connection::create(Action action, PreferTestService preferTestService, Reconnect reconnect)
|
---|
41 | {
|
---|
42 | return makeUnique<Connection>(action, preferTestService, reconnect);
|
---|
43 | }
|
---|
44 |
|
---|
45 | static mach_port_t maybeConnectToService(const char* serviceName)
|
---|
46 | {
|
---|
47 | mach_port_t bsPort;
|
---|
48 | task_get_special_port(mach_task_self(), TASK_BOOTSTRAP_PORT, &bsPort);
|
---|
49 |
|
---|
50 | mach_port_t servicePort;
|
---|
51 | kern_return_t err = bootstrap_look_up(bsPort, serviceName, &servicePort);
|
---|
52 |
|
---|
53 | if (err == KERN_SUCCESS)
|
---|
54 | return servicePort;
|
---|
55 |
|
---|
56 | return MACH_PORT_NULL;
|
---|
57 | }
|
---|
58 |
|
---|
59 | Connection::Connection(Action action, PreferTestService preferTestService, Reconnect reconnect)
|
---|
60 | : m_action(action)
|
---|
61 | , m_reconnect(reconnect == Reconnect::Yes)
|
---|
62 | {
|
---|
63 | if (preferTestService == PreferTestService::Yes)
|
---|
64 | m_serviceName = "org.webkit.webpushtestdaemon.service";
|
---|
65 | else
|
---|
66 | m_serviceName = "com.apple.webkit.webpushd.service";
|
---|
67 | }
|
---|
68 |
|
---|
69 | void Connection::connectToService()
|
---|
70 | {
|
---|
71 | if (m_connection)
|
---|
72 | return;
|
---|
73 |
|
---|
74 | m_connection = adoptNS(xpc_connection_create_mach_service(m_serviceName, dispatch_get_main_queue(), 0));
|
---|
75 |
|
---|
76 | xpc_connection_set_event_handler(m_connection.get(), [this, weakThis = WeakPtr { *this }](xpc_object_t event) {
|
---|
77 | if (!weakThis)
|
---|
78 | return;
|
---|
79 |
|
---|
80 | if (event == XPC_ERROR_CONNECTION_INVALID) {
|
---|
81 | printf("Failed to start listening for connections to mach service\n");
|
---|
82 | connectionDropped();
|
---|
83 | return;
|
---|
84 | }
|
---|
85 |
|
---|
86 | if (event == XPC_ERROR_CONNECTION_INTERRUPTED) {
|
---|
87 | printf("Connection closed\n");
|
---|
88 | if (m_reconnect)
|
---|
89 | printf("===============\nReconnecting...\n");
|
---|
90 | connectionDropped();
|
---|
91 | return;
|
---|
92 | }
|
---|
93 |
|
---|
94 | if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
|
---|
95 | messageReceived(event);
|
---|
96 | return;
|
---|
97 | }
|
---|
98 |
|
---|
99 | RELEASE_ASSERT_NOT_REACHED();
|
---|
100 | });
|
---|
101 |
|
---|
102 | auto result = maybeConnectToService(m_serviceName);
|
---|
103 | if (result == MACH_PORT_NULL)
|
---|
104 | printf("Waiting for service '%s' to be available\n", m_serviceName);
|
---|
105 |
|
---|
106 | while (result == MACH_PORT_NULL) {
|
---|
107 | usleep(1000);
|
---|
108 | result = maybeConnectToService(m_serviceName);
|
---|
109 | }
|
---|
110 |
|
---|
111 | xpc_connection_activate(m_connection.get());
|
---|
112 |
|
---|
113 | sendAuditToken();
|
---|
114 | startAction();
|
---|
115 | }
|
---|
116 |
|
---|
117 | void Connection::startAction()
|
---|
118 | {
|
---|
119 | switch (m_action) {
|
---|
120 | case Action::StreamDebugMessages:
|
---|
121 | startDebugStreamAction();
|
---|
122 | break;
|
---|
123 | };
|
---|
124 |
|
---|
125 | if (m_pushMessage)
|
---|
126 | sendPushMessage();
|
---|
127 | }
|
---|
128 |
|
---|
129 | void Connection::sendPushMessage()
|
---|
130 | {
|
---|
131 | ASSERT(m_pushMessage);
|
---|
132 |
|
---|
133 | WebKit::Daemon::Encoder encoder;
|
---|
134 | encoder << *m_pushMessage;
|
---|
135 |
|
---|
136 | auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
|
---|
137 | xpc_dictionary_set_uint64(dictionary.get(), WebKit::WebPushD::protocolVersionKey, WebKit::WebPushD::protocolVersionValue);
|
---|
138 | xpc_dictionary_set_value(dictionary.get(), WebKit::WebPushD::protocolEncodedMessageKey, WebKit::vectorToXPCData(encoder.takeBuffer()).get());
|
---|
139 | xpc_dictionary_set_uint64(dictionary.get(), WebKit::WebPushD::protocolMessageTypeKey, static_cast<uint64_t>(WebKit::WebPushD::MessageType::InjectPushMessageForTesting));
|
---|
140 |
|
---|
141 | xpc_connection_send_message_with_reply(m_connection.get(), dictionary.get(), dispatch_get_main_queue(), ^(xpc_object_t resultMessage) {
|
---|
142 | // This reply handler intentionally left blank
|
---|
143 | });
|
---|
144 | }
|
---|
145 |
|
---|
146 | void Connection::startDebugStreamAction()
|
---|
147 | {
|
---|
148 | auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
|
---|
149 | std::array<uint8_t, 1> encodedMessage { 1 };
|
---|
150 | xpc_dictionary_set_uint64(dictionary.get(), "protocol version", 1);
|
---|
151 | xpc_dictionary_set_uint64(dictionary.get(), "message type", 5);
|
---|
152 | xpc_dictionary_set_data(dictionary.get(), "encoded message", encodedMessage.data(), encodedMessage.size());
|
---|
153 |
|
---|
154 | xpc_connection_send_message(m_connection.get(), dictionary.get());
|
---|
155 |
|
---|
156 | printf("Now streaming debug messages\n");
|
---|
157 | }
|
---|
158 |
|
---|
159 | void Connection::sendAuditToken()
|
---|
160 | {
|
---|
161 | audit_token_t token = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
---|
162 | mach_msg_type_number_t auditTokenCount = TASK_AUDIT_TOKEN_COUNT;
|
---|
163 | kern_return_t result = task_info(mach_task_self(), TASK_AUDIT_TOKEN, (task_info_t)(&token), &auditTokenCount);
|
---|
164 | if (result != KERN_SUCCESS) {
|
---|
165 | printf("Unable to get audit token to send\n");
|
---|
166 | return;
|
---|
167 | }
|
---|
168 |
|
---|
169 | std::array<uint8_t, 42> encodedMessage;
|
---|
170 | encodedMessage.fill(0);
|
---|
171 | encodedMessage[1] = 1;
|
---|
172 | encodedMessage[2] = 32;
|
---|
173 | memcpy(&encodedMessage[10], &token, sizeof(token));
|
---|
174 | auto dictionary = adoptNS(xpc_dictionary_create(nullptr, nullptr, 0));
|
---|
175 | xpc_dictionary_set_uint64(dictionary.get(), "protocol version", 1);
|
---|
176 | xpc_dictionary_set_uint64(dictionary.get(), "message type", 6);
|
---|
177 | xpc_dictionary_set_data(dictionary.get(), "encoded message", encodedMessage.data(), encodedMessage.size());
|
---|
178 | xpc_connection_send_message(m_connection.get(), dictionary.get());
|
---|
179 | }
|
---|
180 |
|
---|
181 | void Connection::connectionDropped()
|
---|
182 | {
|
---|
183 | m_connection = nullptr;
|
---|
184 | if (m_reconnect) {
|
---|
185 | callOnMainRunLoop([this, weakThis = WeakPtr { this }] {
|
---|
186 | if (weakThis)
|
---|
187 | connectToService();
|
---|
188 | });
|
---|
189 | return;
|
---|
190 | }
|
---|
191 |
|
---|
192 | CFRunLoopStop(CFRunLoopGetCurrent());
|
---|
193 | }
|
---|
194 |
|
---|
195 | void Connection::messageReceived(xpc_object_t message)
|
---|
196 | {
|
---|
197 | const char* debugMessage = xpc_dictionary_get_string(message, "debug message");
|
---|
198 | if (!debugMessage)
|
---|
199 | return;
|
---|
200 |
|
---|
201 | printf("%s\n", debugMessage);
|
---|
202 | }
|
---|
203 |
|
---|
204 | } // namespace WebPushTool
|
---|