1 /*
2  * Copyright (c) 2015-2019 Apple Inc. All rights reserved.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 
17 #include "SymptomReporter.h"
18 
19 #if MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS) && !MDNSRESPONDER_SUPPORTS(APPLE, QUERIER)
20 #include <arpa/inet.h>
21 #include <dlfcn.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <sys/socket.h>
25 #include <AvailabilityMacros.h>
26 #include <SymptomReporter/SymptomReporter.h>
27 
28 #define SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID  101
29 #define SYMPTOM_REPORTER_mDNSResponder_TEXT_ID     "com.apple.mDNSResponder"
30 
31 #define SYMPTOM_DNS_NO_REPLIES          0x00065001
32 #define SYMPTOM_DNS_RESUMED_RESPONDING  0x00065002
33 
34 mDNSu32 NumUnreachableDNSServers;
35 
36 static symptom_framework_t symptomReporter = mDNSNULL;
37 static symptom_framework_t (*symptom_framework_init_f)(symptom_ident_t id, const char *originator_string) = mDNSNULL;
38 static symptom_t (*symptom_new_f)(symptom_framework_t framework, symptom_ident_t id) = mDNSNULL;
39 static int (*symptom_set_additional_qualifier_f)(symptom_t symptom, uint32_t qualifier_type, size_t qualifier_len, void *qualifier_data) = mDNSNULL;
40 static int (*symptom_send_f)(symptom_t symptom) = mDNSNULL;
41 
SymptomReporterInitCheck(void)42 mDNSlocal mStatus SymptomReporterInitCheck(void)
43 {
44     mStatus err;
45     static mDNSBool triedInit = mDNSfalse;
46     static void *symptomReporterLib = mDNSNULL;
47     static const char path[] = "/System/Library/PrivateFrameworks/SymptomReporter.framework/SymptomReporter";
48 
49     if (!triedInit)
50     {
51         triedInit = mDNStrue;
52 
53         symptomReporterLib = dlopen(path, RTLD_LAZY | RTLD_LOCAL);
54         if (!symptomReporterLib)
55             goto exit;
56 
57         symptom_framework_init_f = dlsym(symptomReporterLib, "symptom_framework_init");
58         if (!symptom_framework_init_f)
59             goto exit;
60 
61         symptom_new_f = dlsym(symptomReporterLib, "symptom_new");
62         if (!symptom_new_f)
63             goto exit;
64 
65         symptom_set_additional_qualifier_f = dlsym(symptomReporterLib, "symptom_set_additional_qualifier");
66         if (!symptom_set_additional_qualifier_f)
67             goto exit;
68 
69         symptom_send_f = dlsym(symptomReporterLib, "symptom_send");
70         if (!symptom_send_f)
71             goto exit;
72 
73         symptomReporter = symptom_framework_init_f(SYMPTOM_REPORTER_mDNSResponder_NUMERIC_ID, SYMPTOM_REPORTER_mDNSResponder_TEXT_ID);
74     }
75 
76 exit:
77     err = symptomReporter ? mStatus_NoError : mStatus_NotInitializedErr;
78     return err;
79 }
80 
SymptomReporterReportDNSReachability(const mDNSAddr * addr,mDNSBool isReachable)81 mDNSlocal mStatus SymptomReporterReportDNSReachability(const mDNSAddr *addr, mDNSBool isReachable)
82 {
83     mStatus err;
84     symptom_t symptom;
85     struct sockaddr_storage sockAddr;
86     size_t sockAddrSize;
87 
88     LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_INFO,
89         "SymptomReporterReportDNSReachability: DNS server " PRI_IP_ADDR " is " PUB_S "reachable", addr, isReachable ? "" : "un");
90 
91     if (addr->type == mDNSAddrType_IPv4)
92     {
93         struct sockaddr_in *sin = (struct sockaddr_in *)&sockAddr;
94         sockAddrSize = sizeof(*sin);
95         mDNSPlatformMemZero(sin, sockAddrSize);
96         sin->sin_len = sockAddrSize;
97         sin->sin_family = AF_INET;
98         sin->sin_addr.s_addr = addr->ip.v4.NotAnInteger;
99     }
100     else if (addr->type == mDNSAddrType_IPv6)
101     {
102         struct sockaddr_in6* sin6 = (struct sockaddr_in6*)&sockAddr;
103         sockAddrSize = sizeof(*sin6);
104         mDNSPlatformMemZero(sin6, sockAddrSize);
105         sin6->sin6_len = sockAddrSize;
106         sin6->sin6_family = AF_INET6;
107         sin6->sin6_addr = *(struct in6_addr *)&addr->ip.v6;
108     }
109     else
110     {
111         LogRedact(MDNS_LOG_CATEGORY_DEFAULT, MDNS_LOG_ERROR,
112             "SymptomReporterReportDNSReachability: addr is not an IPv4 or IPv6 address!");
113         err = mStatus_BadParamErr;
114         goto exit;
115     }
116 
117     symptom = symptom_new_f(symptomReporter, isReachable ? SYMPTOM_DNS_RESUMED_RESPONDING : SYMPTOM_DNS_NO_REPLIES);
118     symptom_set_additional_qualifier_f(symptom, 1, sockAddrSize, (void *)&sockAddr);
119     symptom_send_f(symptom);
120     err = mStatus_NoError;
121 
122 exit:
123     return err;
124 }
125 
SymptomReporterDNSServerReachable(mDNS * const m,const mDNSAddr * addr)126 mDNSexport mStatus SymptomReporterDNSServerReachable(mDNS *const m, const mDNSAddr *addr)
127 {
128     mStatus err;
129     DNSServer *s;
130     mDNSBool found = mDNSfalse;
131 
132     err = SymptomReporterInitCheck();
133     if (err != mStatus_NoError)
134         goto exit;
135 
136     for (s = m->DNSServers; s; s = s->next)
137     {
138         if (s->flags & DNSServerFlag_Delete)
139             continue;
140         if ((s->flags & DNSServerFlag_Unreachable) && mDNSSameAddress(addr, &s->addr))
141         {
142             s->flags &= ~DNSServerFlag_Unreachable;
143             NumUnreachableDNSServers--;
144             found = mDNStrue;
145         }
146     }
147 
148     if (!found)
149     {
150         err = mStatus_NoSuchNameErr;
151         goto exit;
152     }
153 
154     err = SymptomReporterReportDNSReachability(addr, mDNStrue);
155 
156 exit:
157     return err;
158 }
159 
SymptomReporterDNSServerUnreachable(DNSServer * s)160 mDNSexport mStatus SymptomReporterDNSServerUnreachable(DNSServer *s)
161 {
162     mStatus err;
163 
164     err = SymptomReporterInitCheck();
165     if (err != mStatus_NoError)
166         goto exit;
167 
168     if ((s->flags & DNSServerFlag_Delete) || (s->flags & DNSServerFlag_Unreachable))
169         goto exit;
170 
171     s->flags |= DNSServerFlag_Unreachable;
172     NumUnreachableDNSServers++;
173 
174     err = SymptomReporterReportDNSReachability(&s->addr, mDNSfalse);
175 
176 exit:
177     return err;
178 }
179 #endif // MDNSRESPONDER_SUPPORTS(APPLE, SYMPTOMS)
180