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