2 * Copyright (c) 2020 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * https://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "mdns_powerlog.h"
18 #include "mdns_helpers.h"
20 #include "DNSMessage.h"
22 #include <CoreUtils/CoreUtils.h>
25 #include <PowerLog/PowerLog.h>
26 #include <SoftLinking/WeakLinking.h>
27 #include <sys/proc_info.h>
29 //======================================================================================================================
30 // MARK: - Weak Linking
32 WEAK_LINK_FORCE_IMPORT(PLLogRegisteredEvent
); // The PowerLog framework isn't present in the BaseSystem
34 //======================================================================================================================
35 // MARK: - PowerLog Event Names
37 #define MDNS_POWERLOG_EVENT_AWDL_BROWSE_START CFSTR("startAWDLBrowse")
38 #define MDNS_POWERLOG_EVENT_AWDL_BROWSE_STOP CFSTR("stopAWDLBrowse")
39 #define MDNS_POWERLOG_EVENT_AWDL_ADVERTISE_START CFSTR("startAWDLAdvertise")
40 #define MDNS_POWERLOG_EVENT_AWDL_ADVERTISE_STOP CFSTR("stopAWDLAdvertise")
41 #define MDNS_POWERLOG_EVENT_AWDL_RESOLVE_START CFSTR("startAWDLResolve")
42 #define MDNS_POWERLOG_EVENT_AWDL_RESOLVE_STOP CFSTR("stopAWDLResolve")
44 //======================================================================================================================
45 // MARK: - PowerLog Event Dictionary Keys
47 #define MDNS_POWERLOG_EVENT_KEY_SERVICE_TYPE CFSTR("service") // String: Service name w/protocol, e.g., _ssh._tcp.
48 #define MDNS_POWERLOG_EVENT_KEY_RECORD_TYPE CFSTR("recordType") // String: Record type, e.g., PTR, SRV, A.
49 #define MDNS_POWERLOG_EVENT_KEY_CLIENT_PID CFSTR("clientPID") // Number: Client's PID.
50 #define MDNS_POWERLOG_EVENT_KEY_CLIENT_NAME CFSTR("clientName") // String: Client's process name.
52 //======================================================================================================================
53 // MARK: - Helper Prototypes
56 _mdns_powerlog_bonjour_event(CFStringRef event_name
, const uint8_t *record_name
, int record_type
, pid_t client_pid
);
58 //======================================================================================================================
59 // MARK: - Debug Logging
61 // The purpose of this os_log category is to log debug messages about logging to PowerLog.
62 // It has nothing to do with actually logging to PowerLog.
63 MDNS_LOG_CATEGORY_DEFINE(powerlog
, "powerlog");
65 //======================================================================================================================
66 // MARK: - External Functions
69 mdns_powerlog_awdl_browse_start(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
71 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_BROWSE_START
, record_name
, record_type
, client_pid
);
74 //======================================================================================================================
77 mdns_powerlog_awdl_browse_stop(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
79 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_BROWSE_STOP
, record_name
, record_type
, client_pid
);
82 //======================================================================================================================
85 mdns_powerlog_awdl_advertise_start(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
87 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_ADVERTISE_START
, record_name
, record_type
, client_pid
);
90 //======================================================================================================================
93 mdns_powerlog_awdl_advertise_stop(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
95 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_ADVERTISE_STOP
, record_name
, record_type
, client_pid
);
98 //======================================================================================================================
101 mdns_powerlog_awdl_resolve_start(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
103 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_RESOLVE_START
, record_name
, record_type
, client_pid
);
106 //======================================================================================================================
109 mdns_powerlog_awdl_resolve_stop(const uint8_t * const record_name
, const int record_type
, const pid_t client_pid
)
111 _mdns_powerlog_bonjour_event(MDNS_POWERLOG_EVENT_AWDL_RESOLVE_STOP
, record_name
, record_type
, client_pid
);
114 //======================================================================================================================
117 static CFDictionaryRef
118 _mdns_powerlog_create_event_dictionary(const uint8_t *record_name
, int record_type
, pid_t client_pid
);
121 _mdns_powerlog_bonjour_event(const CFStringRef event_name
, const uint8_t * const record_name
, const int record_type
,
122 const pid_t client_pid
)
124 require_quiet(PLLogRegisteredEvent
, exit
);
126 const PLClientID plc_id
= PLClientIDMDNSResponder
;
127 const CFDictionaryRef event_dict
= _mdns_powerlog_create_event_dictionary(record_name
, record_type
, client_pid
);
128 os_log_debug(_mdns_powerlog_log(),
129 "Logging to powerlog -- id: %ld, event: %@, dictionary: %@", (long)plc_id
, event_name
, event_dict
);
130 PLLogRegisteredEvent(plc_id
, event_name
, event_dict
, NULL
);
131 CFReleaseNullSafe(event_dict
);
137 //======================================================================================================================
140 _mdns_powerlog_event_dictionary_add_service_type(CFMutableDictionaryRef event_dict
, const uint8_t *record_name
);
143 _mdns_powerlog_event_dictionary_add_record_type(CFMutableDictionaryRef event_dict
, int record_type
);
146 _mdns_powerlog_event_dictionary_add_client_info(CFMutableDictionaryRef event_dict
, pid_t client_pid
);
148 static CFDictionaryRef
149 _mdns_powerlog_create_event_dictionary(const uint8_t * const record_name
, const int record_type
,
150 const pid_t client_pid
)
152 CFMutableDictionaryRef event_dict
= CFDictionaryCreateMutable(NULL
, 0, &kCFTypeDictionaryKeyCallBacks
,
153 &kCFTypeDictionaryValueCallBacks
);
154 require_quiet(event_dict
, exit
);
156 _mdns_powerlog_event_dictionary_add_service_type(event_dict
, record_name
);
157 _mdns_powerlog_event_dictionary_add_record_type(event_dict
, record_type
);
158 _mdns_powerlog_event_dictionary_add_client_info(event_dict
, client_pid
);
164 //======================================================================================================================
167 _mdns_get_service_name_string_from_domain_name(const uint8_t *name
,
168 char service_name_str
[static kDNSServiceMaxDomainName
]);
171 _mdns_powerlog_event_dictionary_add_service_type(const CFMutableDictionaryRef event_dict
,
172 const uint8_t * const record_name
)
174 char service_type
[kDNSServiceMaxDomainName
];
175 if (_mdns_get_service_name_string_from_domain_name(record_name
, service_type
)) {
176 CFStringRef service_type_value
= CFStringCreateWithCString(NULL
, service_type
, kCFStringEncodingUTF8
);
177 if (service_type_value
) {
178 CFDictionaryAddValue(event_dict
, MDNS_POWERLOG_EVENT_KEY_SERVICE_TYPE
, service_type_value
);
179 ForgetCF(&service_type_value
);
184 #define MDNS_TCP_PROTOCOL_LABEL ((const uint8_t *)"\x4" "_tcp")
185 #define MDNS_UDP_PROTOCOL_LABEL ((const uint8_t *)"\x4" "_udp")
186 #define MDNS_LOCAL_DOMAIN_NAME ((const uint8_t *)"\x5" "local")
189 _mdns_get_service_name_string_from_domain_name(const uint8_t * const record_name
,
190 char service_name_str
[static kDNSServiceMaxDomainName
])
193 const uint8_t *l1
= NULL
;
194 const uint8_t *l2
= NULL
;
195 const uint8_t *l3
= NULL
;
196 // Get the three labels leading up to the root label.
197 for (const uint8_t *ptr
= record_name
; (len
= *ptr
) != 0; ptr
= &ptr
[1 + len
]) {
202 // Make sure the first label begins with '_', the second label is '_tcp' or '_udp', and the third label is 'local'.
203 // See <https://tools.ietf.org/html/rfc6763#section-4.1.2>.
205 if (l1
&& (l1
[1] == '_') &&
206 l2
&& (DomainLabelEqual(l2
, MDNS_TCP_PROTOCOL_LABEL
) || DomainLabelEqual(l2
, MDNS_UDP_PROTOCOL_LABEL
)) &&
207 l3
&& DomainNameEqual(l3
, MDNS_LOCAL_DOMAIN_NAME
)) {
208 uint8_t service_name
[kDomainNameLengthMax
];
209 uint8_t *ptr
= service_name
;
210 const uint8_t * const limit
= &service_name
[countof(service_name
)];
212 // Append service name label.
214 require_quiet(((size_t)(limit
- ptr
)) >= len
, exit
);
215 memcpy(ptr
, l1
, len
);
218 // Append protocol label, i.e., '_tcp' or '_udp'.
220 require_quiet(((size_t)(limit
- ptr
)) >= len
, exit
);
221 memcpy(ptr
, l2
, len
);
224 // Append root label.
225 require_quiet((limit
- ptr
) >= 1, exit
);
228 // Convert to string.
229 const OSStatus err
= DomainNameToString(service_name
, limit
, service_name_str
, NULL
);
231 // Remove trailing root dot.
232 len
= strlen(service_name_str
);
234 char * const cptr
= &service_name_str
[len
- 1];
239 result
= service_name_str
;
247 //======================================================================================================================
250 _mdns_powerlog_event_dictionary_add_record_type(const CFMutableDictionaryRef event_dict
, const int record_type
)
252 char qtype_str_buf
[32];
253 const char *qtype_str
= DNSRecordTypeValueToString(record_type
);
255 snprintf(qtype_str_buf
, sizeof(qtype_str_buf
), "TYPE%d", record_type
);
256 qtype_str
= qtype_str_buf
;
258 CFStringRef qtype_str_value
= CFStringCreateWithCString(NULL
, qtype_str
, kCFStringEncodingUTF8
);
259 if (qtype_str_value
) {
260 CFDictionaryAddValue(event_dict
, MDNS_POWERLOG_EVENT_KEY_RECORD_TYPE
, qtype_str_value
);
261 ForgetCF(&qtype_str_value
);
265 //======================================================================================================================
268 _mdns_pid_to_name(pid_t pid
, char name
[STATIC_PARAM MAXCOMLEN
]);
271 _mdns_powerlog_event_dictionary_add_client_info(const CFMutableDictionaryRef event_dict
, const pid_t client_pid
)
273 const long long client_pid_ll
= client_pid
;
274 CFNumberRef client_pid_value
= CFNumberCreate(NULL
, kCFNumberLongLongType
, &client_pid_ll
);
275 if (client_pid_value
!= NULL
) {
276 CFDictionaryAddValue(event_dict
, MDNS_POWERLOG_EVENT_KEY_CLIENT_PID
, client_pid_value
);
277 ForgetCF(&client_pid_value
);
279 char client_name
[MAXCOMLEN
];
280 if (_mdns_pid_to_name(client_pid
, client_name
)) {
281 CFStringRef client_name_value
= CFStringCreateWithCString(NULL
, client_name
, kCFStringEncodingUTF8
);
282 if (client_name_value
) {
283 CFDictionaryAddValue(event_dict
, MDNS_POWERLOG_EVENT_KEY_CLIENT_NAME
, client_name_value
);
284 ForgetCF(&client_name_value
);
290 _mdns_pid_to_name(const pid_t pid
, char name
[STATIC_PARAM MAXCOMLEN
])
293 struct proc_bsdshortinfo info
;
294 const int n
= proc_pidinfo(pid
, PROC_PIDT_SHORTBSDINFO
, 1, &info
, PROC_PIDT_SHORTBSDINFO_SIZE
);
295 if (n
== (int)sizeof(info
)) {
296 strlcpy(name
, info
.pbsi_comm
, MAXCOMLEN
);