mirror of
https://github.com/lucannez64/darling-lol.git
synced 2026-10-27 19:54:38 +00:00
745 lines
24 KiB
Diff
745 lines
24 KiB
Diff
diff --git a/darling/src/SystemConfiguration.fproj/stubs.c b/darling/src/SystemConfiguration.fproj/stubs.c
|
|
index 1f31c08..3299e6a 100644
|
|
--- a/darling/src/SystemConfiguration.fproj/stubs.c
|
|
+++ b/darling/src/SystemConfiguration.fproj/stubs.c
|
|
@@ -1,4 +1,13 @@
|
|
#include <SystemConfiguration/SystemConfiguration.h>
|
|
+#include <CoreFoundation/CoreFoundation.h>
|
|
+#include <CoreFoundation/CFRuntime.h>
|
|
+#include <sys/socket.h>
|
|
+#include <netinet/in.h>
|
|
+#include <string.h>
|
|
+#include <ifaddrs.h>
|
|
+#include <net/if.h>
|
|
+#include <arpa/inet.h>
|
|
+#include <unistd.h>
|
|
|
|
#define STUB() printf("STUB %s\n", __func__)
|
|
|
|
@@ -50,95 +59,348 @@ SCNetworkConnectionRef SCNetworkConnectionCreateWithServiceID(CFAllocatorRef all
|
|
return NULL;
|
|
}
|
|
|
|
+/*
|
|
+ * darling-lol: minimal but FUNCTIONAL network reachability.
|
|
+ *
|
|
+ * Previously every SCNetworkReachability* entry point was a stub:
|
|
+ * CreateWithAddress/Name returned NULL and GetFlags returned garbage.
|
|
+ * Chromium's network_change_notifier_mac / dns_config_service start by
|
|
+ * creating a reachability object and calling GetFlags; with NULL/garbage
|
|
+ * it took a failing path (and `dns_config_service_posix ... watch failed
|
|
+ * to start` / `Could not get initial network connection type` follow).
|
|
+ *
|
|
+ * Darling has no reachability subsystem, but the honest answer for a host
|
|
+ * that has a working network stack is "reachable, direct". We return a
|
|
+ * real heap object so callers can schedule it, and GetFlags reports
|
|
+ * kSCNetworkReachabilityFlagsReachable. If the host is genuinely offline
|
|
+ * the caller finds out when its connect() fails; this only removes the
|
|
+ * false negative at startup.
|
|
+ */
|
|
+struct __SCNetworkReachability {
|
|
+ CFRuntimeBase _base;
|
|
+ CFStringRef name;
|
|
+ struct sockaddr_storage addr;
|
|
+ Boolean hasAddr;
|
|
+};
|
|
+
|
|
+static void _reachabilityRelease(CFTypeRef cf)
|
|
+{
|
|
+ struct __SCNetworkReachability* ref = (struct __SCNetworkReachability*) cf;
|
|
+ if (ref->name != NULL)
|
|
+ CFRelease(ref->name);
|
|
+}
|
|
+
|
|
+static CFStringRef _reachabilityCopyDescription(CFTypeRef cf)
|
|
+{
|
|
+ return CFSTR("SCNetworkReachability");
|
|
+}
|
|
+
|
|
+static const CFRuntimeClass _reachabilityClass = {
|
|
+ 0,
|
|
+ "SCNetworkReachability",
|
|
+ NULL, /* init */
|
|
+ NULL, /* copy */
|
|
+ _reachabilityRelease,
|
|
+ NULL, /* equal */
|
|
+ NULL, /* hash */
|
|
+ NULL, /* copyFormattingDesc */
|
|
+ _reachabilityCopyDescription
|
|
+};
|
|
+
|
|
+static SCNetworkReachabilityRef _reachabilityCreate(CFAllocatorRef allocator, const struct sockaddr* addr)
|
|
+{
|
|
+ static CFTypeID classID = _kCFRuntimeNotATypeID;
|
|
+ if (classID == _kCFRuntimeNotATypeID)
|
|
+ classID = _CFRuntimeRegisterClass(&_reachabilityClass);
|
|
+
|
|
+ struct __SCNetworkReachability* ref = (struct __SCNetworkReachability*)
|
|
+ _CFRuntimeCreateInstance(allocator, classID,
|
|
+ sizeof(struct __SCNetworkReachability) - sizeof(CFRuntimeBase), NULL);
|
|
+ if (ref == NULL)
|
|
+ return NULL;
|
|
+
|
|
+ if (addr != NULL) {
|
|
+ size_t len = sizeof(struct sockaddr_in);
|
|
+ if (addr->sa_family == AF_INET6)
|
|
+ len = sizeof(struct sockaddr_in6);
|
|
+ if (len > sizeof(ref->addr))
|
|
+ len = sizeof(ref->addr);
|
|
+ memcpy(&ref->addr, addr, len);
|
|
+ ref->hasAddr = true;
|
|
+ }
|
|
+ return (SCNetworkReachabilityRef) ref;
|
|
+}
|
|
+
|
|
CFTypeID SCNetworkReachabilityGetTypeID(void)
|
|
{
|
|
- STUB();
|
|
- return 0;
|
|
+ /* darling-lol: return the real registered class id (some clients
|
|
+ * compare it and misbehave on 0). */
|
|
+ static CFTypeID classID = _kCFRuntimeNotATypeID;
|
|
+ if (classID == _kCFRuntimeNotATypeID)
|
|
+ classID = _CFRuntimeRegisterClass(&_reachabilityClass);
|
|
+ return classID;
|
|
}
|
|
|
|
Boolean SCNetworkReachabilityScheduleWithRunLoop(SCNetworkReachabilityRef ref, CFRunLoopRef rl, CFStringRef rlMode)
|
|
{
|
|
- STUB();
|
|
- return 0;
|
|
+ /* No dynamic reachability changes to report; accepting the schedule
|
|
+ * (instead of failing) lets callers proceed to GetFlags. */
|
|
+ return (ref != NULL) ? true : false;
|
|
}
|
|
|
|
Boolean SCNetworkReachabilityUnscheduleFromRunLoop(SCNetworkReachabilityRef ref, CFRunLoopRef rl, CFStringRef rlMode)
|
|
{
|
|
- STUB();
|
|
- return 0;
|
|
+ return (ref != NULL) ? true : false;
|
|
}
|
|
|
|
Boolean SCNetworkReachabilitySetCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityCallBack cb, SCNetworkReachabilityContext* ctxt)
|
|
{
|
|
- STUB();
|
|
- return 0;
|
|
+ /* Accepted but never invoked (no change notifications implemented). */
|
|
+ return (target != NULL) ? true : false;
|
|
}
|
|
|
|
Boolean SCNetworkReachabilityGetFlags(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags* flags)
|
|
{
|
|
- STUB();
|
|
- return 0;
|
|
+ if (target == NULL || flags == NULL)
|
|
+ return false;
|
|
+ /* Host has a network stack: report reachable + direct, not required. */
|
|
+ *flags = kSCNetworkReachabilityFlagsReachable
|
|
+ | kSCNetworkReachabilityFlagsTransientConnection
|
|
+ | kSCNetworkReachabilityFlagsConnectionAutomatic;
|
|
+ return true;
|
|
}
|
|
|
|
SCNetworkReachabilityRef SCNetworkReachabilityCreateWithName(CFAllocatorRef allocator, const char* nodeName)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ struct __SCNetworkReachability* ref = (struct __SCNetworkReachability*)
|
|
+ _reachabilityCreate(allocator, NULL);
|
|
+ if (ref != NULL && nodeName != NULL)
|
|
+ ref->name = CFStringCreateWithCString(allocator, nodeName, kCFStringEncodingUTF8);
|
|
+ return (SCNetworkReachabilityRef) ref;
|
|
}
|
|
|
|
SCNetworkReachabilityRef SCNetworkReachabilityCreateWithAddressPair(CFAllocatorRef allocator, const struct sockaddr* localAddress, const struct sockaddr* remoteAddress)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ /* Local address is irrelevant to our answer; key off the remote. */
|
|
+ return _reachabilityCreate(allocator, remoteAddress);
|
|
}
|
|
|
|
SCNetworkReachabilityRef SCNetworkReachabilityCreateWithAddress(CFAllocatorRef allocator, const struct sockaddr* remoteAddress)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ return _reachabilityCreate(allocator, remoteAddress);
|
|
+}
|
|
+
|
|
+/*
|
|
+ * darling-lol: minimal but FUNCTIONAL SCDynamicStore.
|
|
+ *
|
|
+ * Chromium's dns_config_service_posix (macOS) creates an SCDynamicStore,
|
|
+ * calls SCDynamicStoreSetNotificationKeys with the DNS/State keys, and
|
|
+ * schedules it -- that is how it "starts" its config watcher. All of
|
|
+ * these used to be STUBs returning NULL/FALSE, so the watcher could never
|
|
+ * start (`dns_config_service_posix.cc(137/147) ... watch failed to
|
|
+ * start`). We provide a real object and accept the notification setup;
|
|
+ * `CopyValue` reports "key absent" (NULL, never bogus) and the runloop
|
|
+ * source is a real never-firing source. No change callbacks are delivered
|
|
+ * (Darling has no configd store to watch), which is sufficient for startup.
|
|
+ * (Proxy values are served by the pre-existing SCDynamicStoreCopyProxies
|
|
+ * in stubs.cpp, untouched.)
|
|
+ */
|
|
+struct __SCDynamicStore {
|
|
+ CFRuntimeBase _base;
|
|
+ CFStringRef name;
|
|
+ CFArrayRef notificationKeys;
|
|
+ CFArrayRef notificationPatterns;
|
|
+ dispatch_queue_t queue;
|
|
+ SCDynamicStoreCallBack callout;
|
|
+ SCDynamicStoreContext context;
|
|
+};
|
|
+
|
|
+static void _storeRelease(CFTypeRef cf)
|
|
+{
|
|
+ struct __SCDynamicStore* store = (struct __SCDynamicStore*) cf;
|
|
+ if (store->name) CFRelease(store->name);
|
|
+ if (store->notificationKeys) CFRelease(store->notificationKeys);
|
|
+ if (store->notificationPatterns) CFRelease(store->notificationPatterns);
|
|
+ if (store->context.release && store->context.info)
|
|
+ store->context.release(store->context.info);
|
|
+}
|
|
+
|
|
+static CFStringRef _storeCopyDescription(CFTypeRef cf)
|
|
+{
|
|
+ return CFSTR("SCDynamicStore");
|
|
+}
|
|
+
|
|
+static const CFRuntimeClass _storeClass = {
|
|
+ 0,
|
|
+ "SCDynamicStore",
|
|
+ NULL, NULL,
|
|
+ _storeRelease,
|
|
+ NULL, NULL, NULL,
|
|
+ _storeCopyDescription
|
|
+};
|
|
+
|
|
+static CFTypeID _storeGetClassID(void)
|
|
+{
|
|
+ static CFTypeID id = _kCFRuntimeNotATypeID;
|
|
+ if (id == _kCFRuntimeNotATypeID)
|
|
+ id = _CFRuntimeRegisterClass(&_storeClass);
|
|
+ return id;
|
|
+}
|
|
+
|
|
+CFTypeID SCDynamicStoreGetTypeID(void)
|
|
+{
|
|
+ return _storeGetClassID();
|
|
}
|
|
|
|
CFStringRef SCDynamicStoreKeyCreateNetworkGlobalEntity(CFAllocatorRef allocator, CFStringRef domain, CFStringRef entity)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ if (domain == NULL || entity == NULL)
|
|
+ return NULL;
|
|
+ return CFStringCreateWithFormat(allocator, NULL, CFSTR("%@/Network/Global/%@"),
|
|
+ domain, entity);
|
|
+}
|
|
+
|
|
+CFStringRef SCDynamicStoreKeyCreateProxies(CFAllocatorRef allocator)
|
|
+{
|
|
+ /* darling-lol: Electron (Chromium network stack) lazy-binds this at
|
|
+ * login-network time; a missing export aborts the process in dyld
|
|
+ * ("Symbol not found"). Built on the same constructor as Apple's. */
|
|
+ return SCDynamicStoreKeyCreateNetworkGlobalEntity(allocator,
|
|
+ kSCDynamicStoreDomainState, kSCEntNetProxies);
|
|
+}
|
|
+
|
|
+/* Query the kernel's route selection without sending a packet. Match its
|
|
+ * local address against getifaddrs, rather than inventing a primary adapter. */
|
|
+static CFStringRef _primaryInterface(int family)
|
|
+{
|
|
+ struct sockaddr_storage remote = {0}, local = {0};
|
|
+ socklen_t size;
|
|
+ if (family == AF_INET) {
|
|
+ struct sockaddr_in *p = (struct sockaddr_in *)&remote;
|
|
+ p->sin_len = sizeof(*p); p->sin_family = family; p->sin_port = htons(53);
|
|
+ inet_pton(family, "8.8.8.8", &p->sin_addr); size = sizeof(*p);
|
|
+ } else {
|
|
+ struct sockaddr_in6 *p = (struct sockaddr_in6 *)&remote;
|
|
+ p->sin6_len = sizeof(*p); p->sin6_family = family; p->sin6_port = htons(53);
|
|
+ inet_pton(family, "2001:4860:4860::8888", &p->sin6_addr); size = sizeof(*p);
|
|
+ }
|
|
+ int fd = socket(family, SOCK_DGRAM, 0);
|
|
+ if (fd < 0) return NULL;
|
|
+ socklen_t localSize = sizeof(local);
|
|
+ int result = connect(fd, (struct sockaddr *)&remote, size);
|
|
+ if (!result) result = getsockname(fd, (struct sockaddr *)&local, &localSize);
|
|
+ close(fd);
|
|
+ if (result) return NULL;
|
|
+ struct ifaddrs *interfaces = NULL;
|
|
+ if (getifaddrs(&interfaces)) return NULL;
|
|
+ CFStringRef name = NULL;
|
|
+ for (struct ifaddrs *p = interfaces; p; p = p->ifa_next) {
|
|
+ if (!p->ifa_addr || p->ifa_addr->sa_family != family ||
|
|
+ !(p->ifa_flags & IFF_UP) || (p->ifa_flags & IFF_LOOPBACK)) continue;
|
|
+ const void *a, *b;
|
|
+ size_t bytes;
|
|
+ if (family == AF_INET) {
|
|
+ a = &((struct sockaddr_in *)p->ifa_addr)->sin_addr;
|
|
+ b = &((struct sockaddr_in *)&local)->sin_addr; bytes = sizeof(struct in_addr);
|
|
+ } else {
|
|
+ a = &((struct sockaddr_in6 *)p->ifa_addr)->sin6_addr;
|
|
+ b = &((struct sockaddr_in6 *)&local)->sin6_addr; bytes = sizeof(struct in6_addr);
|
|
+ }
|
|
+ if (!memcmp(a, b, bytes)) {
|
|
+ name = CFStringCreateWithCString(NULL, p->ifa_name, kCFStringEncodingUTF8);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ freeifaddrs(interfaces);
|
|
+ return name;
|
|
}
|
|
|
|
CFPropertyListRef SCDynamicStoreCopyValue(SCDynamicStoreRef store, CFStringRef key)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ if (!key) return NULL;
|
|
+ int family;
|
|
+ if (CFEqual(key, CFSTR("State:/Network/Global/IPv4"))) family = AF_INET;
|
|
+ else if (CFEqual(key, CFSTR("State:/Network/Global/IPv6"))) family = AF_INET6;
|
|
+ else return NULL;
|
|
+ CFStringRef primary = _primaryInterface(family);
|
|
+ if (!primary) return NULL;
|
|
+ const void *keys[] = { kSCDynamicStorePropNetPrimaryInterface };
|
|
+ const void *values[] = { primary };
|
|
+ CFDictionaryRef result = CFDictionaryCreate(NULL, keys, values, 1,
|
|
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
+ CFRelease(primary);
|
|
+ return result;
|
|
}
|
|
|
|
SCDynamicStoreRef SCDynamicStoreCreate(CFAllocatorRef allocator, CFStringRef name, SCDynamicStoreCallBack callout, SCDynamicStoreContext *context)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ struct __SCDynamicStore* store = (struct __SCDynamicStore*)
|
|
+ _CFRuntimeCreateInstance(allocator, _storeGetClassID(),
|
|
+ sizeof(struct __SCDynamicStore) - sizeof(CFRuntimeBase), NULL);
|
|
+ if (store == NULL)
|
|
+ return NULL;
|
|
+ store->name = (name != NULL) ? CFStringCreateCopy(allocator, name) : NULL;
|
|
+ store->callout = callout;
|
|
+ if (context != NULL) {
|
|
+ store->context = *context;
|
|
+ if (store->context.retain && store->context.info)
|
|
+ store->context.info = (void*) store->context.retain(store->context.info);
|
|
+ }
|
|
+ return (SCDynamicStoreRef) store;
|
|
}
|
|
|
|
SCDynamicStoreRef __nullable SCDynamicStoreCreateWithOptions(CFAllocatorRef __nullable allocator, CFStringRef name, CFDictionaryRef __nullable storeOptions, SCDynamicStoreCallBack __nullable callout, SCDynamicStoreContext* __nullable context)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ return SCDynamicStoreCreate(allocator, name, callout, context);
|
|
};
|
|
|
|
CFStringRef SCDynamicStoreKeyCreate(CFAllocatorRef allocator, CFStringRef fmt, ...)
|
|
{
|
|
- STUB();
|
|
- return NULL;
|
|
+ if (fmt == NULL)
|
|
+ return NULL;
|
|
+ /* Best effort: return the format string itself when no args are
|
|
+ * readily available; callers only use these as dictionary keys. */
|
|
+ return CFStringCreateCopy(allocator, fmt);
|
|
}
|
|
|
|
Boolean SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
|
|
{
|
|
- STUB();
|
|
- return FALSE;
|
|
+ if (store == NULL)
|
|
+ return FALSE;
|
|
+ ((struct __SCDynamicStore*) store)->queue = queue;
|
|
+ return TRUE;
|
|
}
|
|
|
|
Boolean SCDynamicStoreSetNotificationKeys(SCDynamicStoreRef store, CFArrayRef __nullable keys, CFArrayRef __nullable patterns) {
|
|
- STUB();
|
|
- return FALSE;
|
|
+ if (store == NULL)
|
|
+ return FALSE;
|
|
+ struct __SCDynamicStore* s = (struct __SCDynamicStore*) store;
|
|
+ if (s->notificationKeys) CFRelease(s->notificationKeys);
|
|
+ if (s->notificationPatterns) CFRelease(s->notificationPatterns);
|
|
+ s->notificationKeys = keys ? (CFArrayRef) CFRetain(keys) : NULL;
|
|
+ s->notificationPatterns = patterns ? (CFArrayRef) CFRetain(patterns) : NULL;
|
|
+ /* Accepted: we simply never fire callbacks (no configd to watch). */
|
|
+ return TRUE;
|
|
}
|
|
|
|
+CFRunLoopSourceRef SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator, SCDynamicStoreRef store, CFIndex order) {
|
|
+ if (store == NULL)
|
|
+ return NULL;
|
|
+ /*
|
|
+ * darling-lol: return a real (never-firing) runloop source. Chromium's
|
|
+ * dns watcher does SCDynamicStoreCreateRunLoopSource + CFRunLoopAddSource;
|
|
+ * a NULL source left it with nothing to schedule ("watch failed to
|
|
+ * start") and risked a NULL deref/abort downstream. This dummy source
|
|
+ * satisfies scheduling; it never signals because Darling has no configd
|
|
+ * to deliver changes from.
|
|
+ */
|
|
+ CFRunLoopSourceContext ctx;
|
|
+ memset(&ctx, 0, sizeof(ctx));
|
|
+ ctx.info = (void*) store;
|
|
+ return CFRunLoopSourceCreate(allocator, order, &ctx);
|
|
+}
|
|
+
|
|
+
|
|
CFStringRef SCDynamicStoreKeyCreateComputerName(CFAllocatorRef allocator) {
|
|
STUB();
|
|
return NULL;
|
|
@@ -163,3 +425,36 @@ CFStringRef SCDynamicStoreKeyCreateNetworkInterfaceEntity(CFAllocatorRef allocat
|
|
STUB();
|
|
return NULL;
|
|
};
|
|
+
|
|
+const char* SCErrorString(int status) {
|
|
+ /* Minimal Apple-compatible mapping (full table lives in SCD.c,
|
|
+ * which is not compiled into Darling's stub framework). Callers
|
|
+ * such as Chromium's network-change notifier only log the result,
|
|
+ * so a generic fallback is acceptable for unknown codes. */
|
|
+ static const struct { int status; const char *message; } msgs[] = {
|
|
+ { kSCStatusOK, "Success!" },
|
|
+ { kSCStatusFailed, "Failed!" },
|
|
+ { kSCStatusInvalidArgument, "Invalid argument" },
|
|
+ { kSCStatusAccessError, "Permission denied" },
|
|
+ { kSCStatusNoKey, "No such key" },
|
|
+ { kSCStatusKeyExists, "Key already defined" },
|
|
+ { kSCStatusLocked, "Lock already held" },
|
|
+ { kSCStatusNeedLock, "Lock required for this operation" },
|
|
+ { kSCStatusNoStoreSession, "Configuration daemon session not active" },
|
|
+ { kSCStatusNoStoreServer, "Configuration daemon not (no longer) available" },
|
|
+ { kSCStatusNotifierActive, "Notifier is currently active" },
|
|
+ { kSCStatusNoPrefsSession, "Preference session not active" },
|
|
+ { kSCStatusPrefsBusy, "Preferences update currently in progress" },
|
|
+ { kSCStatusNoConfigFile, "Configuration file not found" },
|
|
+ { kSCStatusNoLink, "No such link" },
|
|
+ { kSCStatusStale, "Write attempted on stale version of object" },
|
|
+ };
|
|
+ unsigned i;
|
|
+ for (i = 0; i < sizeof(msgs)/sizeof(msgs[0]); i++) {
|
|
+ if (msgs[i].status == status)
|
|
+ return msgs[i].message;
|
|
+ }
|
|
+ if (status > 0)
|
|
+ return strerror(status);
|
|
+ return "Unknown error";
|
|
+};
|
|
diff --git a/darling/src/dnsinfo/dnsinfo.c b/darling/src/dnsinfo/dnsinfo.c
|
|
index 40f8673..d6b4a1a 100644
|
|
--- a/darling/src/dnsinfo/dnsinfo.c
|
|
+++ b/darling/src/dnsinfo/dnsinfo.c
|
|
@@ -7,8 +7,6 @@
|
|
#include <arpa/inet.h>
|
|
#include <netinet/in.h>
|
|
|
|
-static char* remove_comment(char* line);
|
|
-static char* remove_whitespace(char* line);
|
|
|
|
/**
|
|
* This is a very simple parser of /etc/resolv.conf.
|
|
@@ -19,160 +17,113 @@ static char* remove_whitespace(char* line);
|
|
const char *
|
|
dns_configuration_notify_key ()
|
|
{
|
|
- return NULL;
|
|
+ /*
|
|
+ * darling-lol: must be non-NULL. Chromium's mac DNS watcher
|
|
+ * (dns_config_watcher_mac.cc) dlopens libSystem, dlsyms this function,
|
|
+ * and refuses to start its config watch ("DNS config watch failed to
|
|
+ * start", dns_config_service_posix.cc:137) when the returned key is
|
|
+ * NULL. The key names a notify(3) registration delivered via notifyd
|
|
+ * (now functional); no configd exists to post changes, so the watch
|
|
+ * simply never fires -- which is the correct fail-soft. This is Apple's
|
|
+ * key string for DNS configuration changes.
|
|
+ */
|
|
+ return "com.apple.system.dns.configuration";
|
|
}
|
|
|
|
dns_config_t *
|
|
-dns_configuration_copy ()
|
|
+dns_configuration_copy()
|
|
{
|
|
-#if 0
|
|
- dns_config_t* config;
|
|
- dns_resolver_t* resolver;
|
|
- FILE* file;
|
|
- char buf[256];
|
|
-
|
|
- config = (dns_config_t*) malloc(sizeof(dns_config_t));
|
|
- memset(config, 0, sizeof(*config));
|
|
-
|
|
- file = fopen("/etc/resolv.conf", "r");
|
|
- if (file == NULL)
|
|
- return config;
|
|
-
|
|
- resolver = (dns_resolver_t*) malloc(sizeof(dns_resolver_t));
|
|
- memset(resolver, 0, sizeof(*resolver));
|
|
-
|
|
- config->n_resolver = 1;
|
|
- config->resolver = (dns_resolver_t**) malloc(sizeof(void*));
|
|
- config->resolver[0] = resolver;
|
|
-
|
|
- while (fgets(buf, sizeof(buf), file) != NULL)
|
|
- {
|
|
- char* line;
|
|
-
|
|
- line = remove_comment(buf);
|
|
- if (line == NULL)
|
|
- continue;
|
|
-
|
|
- line = remove_whitespace(line);
|
|
- if (line == NULL || line[0] == 0)
|
|
- continue;
|
|
-
|
|
- if (strncmp(line, "nameserver", 10) == 0)
|
|
- {
|
|
- char* srv;
|
|
- int is_ipv6;
|
|
- struct sockaddr* addr;
|
|
- int res;
|
|
-
|
|
- srv = line + 11;
|
|
- while (isspace(*srv))
|
|
- srv++;
|
|
-
|
|
- is_ipv6 = strchr(srv, ':') != NULL;
|
|
-
|
|
- if (is_ipv6)
|
|
- {
|
|
- struct sockaddr_in6* a6;
|
|
-
|
|
- a6 = (struct sockaddr_in6*) malloc(sizeof(struct sockaddr_in6));
|
|
- addr = (struct sockaddr*) a6;
|
|
-
|
|
- a6->sin6_family = AF_INET6;
|
|
- a6->sin6_port = htons(53);
|
|
-
|
|
- res = inet_pton(AF_INET6, srv, &a6->sin6_addr);
|
|
- }
|
|
- else
|
|
- {
|
|
- struct sockaddr_in* a4;
|
|
-
|
|
- a4 = (struct sockaddr_in*) malloc(sizeof(struct sockaddr_in));
|
|
- addr = (struct sockaddr*) a4;
|
|
-
|
|
- a4->sin_family = AF_INET;
|
|
- a4->sin_port = htons(53);
|
|
-
|
|
- res = inet_pton(AF_INET, srv, &a4->sin_addr);
|
|
- }
|
|
-
|
|
- if (res == 1)
|
|
- {
|
|
- resolver->n_nameserver++;
|
|
-
|
|
- resolver->nameserver = (struct sockaddr**) realloc(resolver->nameserver,
|
|
- sizeof(void*) * resolver->n_nameserver);
|
|
-
|
|
- resolver->nameserver[resolver->n_nameserver-1] = addr;
|
|
- }
|
|
- else
|
|
- {
|
|
- free(addr);
|
|
- }
|
|
- }
|
|
- else if (strncmp(line, "search", 6) == 0)
|
|
- {
|
|
- char* dom;
|
|
-
|
|
- dom = line + 7;
|
|
- while (isspace(*dom))
|
|
- dom++;
|
|
-
|
|
- resolver->n_search++;
|
|
- resolver->search = (char**) realloc(resolver->search,
|
|
- sizeof(char*) * resolver->n_search);
|
|
-
|
|
- resolver->search[resolver->n_search-1] = strdup(dom);
|
|
- }
|
|
- else if (strncmp(line, "domain", 6) == 0)
|
|
- {
|
|
- char* dom;
|
|
-
|
|
- dom = line + 7;
|
|
- while (isspace(*dom))
|
|
- dom++;
|
|
-
|
|
- resolver->domain = strdup(dom);
|
|
- }
|
|
- }
|
|
-
|
|
- fclose(file);
|
|
-
|
|
- return config;
|
|
-#else
|
|
- return NULL;
|
|
-#endif
|
|
+ FILE *file = fopen("/etc/resolv.conf", "r");
|
|
+ if (!file) return NULL;
|
|
+ dns_config_t *config = calloc(1, sizeof(*config));
|
|
+ dns_resolver_t *resolver = calloc(1, sizeof(*resolver));
|
|
+ if (!config || !resolver) {
|
|
+ free(config); free(resolver); fclose(file); return NULL;
|
|
+ }
|
|
+ config->resolver = calloc(1, sizeof(*config->resolver));
|
|
+ if (!config->resolver) {
|
|
+ free(config); free(resolver); fclose(file); return NULL;
|
|
+ }
|
|
+ config->resolver[0] = resolver;
|
|
+ config->n_resolver = 1;
|
|
+ resolver->flags = DNS_RESOLVER_FLAGS_REQUEST_ALL_RECORDS;
|
|
+ resolver->search_order = DEFAULT_SEARCH_ORDER;
|
|
+ char buf[4096];
|
|
+ int failed = 0;
|
|
+ while (!failed && fgets(buf, sizeof(buf), file)) {
|
|
+ char *comment = strpbrk(buf, "#;");
|
|
+ if (comment) *comment = 0;
|
|
+ char *save = NULL;
|
|
+ char *key = strtok_r(buf, " \t\r\n", &save);
|
|
+ char *value = strtok_r(NULL, " \t\r\n", &save);
|
|
+ if (!key || !value) continue;
|
|
+ if (!strcmp(key, "nameserver")) {
|
|
+ struct sockaddr_storage address = {0};
|
|
+ size_t length;
|
|
+ if (strchr(value, ':')) {
|
|
+ struct sockaddr_in6 *a = (void *)&address;
|
|
+ length = sizeof(*a); a->sin6_len = length;
|
|
+ a->sin6_family = AF_INET6; a->sin6_port = htons(53);
|
|
+ if (inet_pton(AF_INET6, value, &a->sin6_addr) != 1) continue;
|
|
+ } else {
|
|
+ struct sockaddr_in *a = (void *)&address;
|
|
+ length = sizeof(*a); a->sin_len = length;
|
|
+ a->sin_family = AF_INET; a->sin_port = htons(53);
|
|
+ if (inet_pton(AF_INET, value, &a->sin_addr) != 1) continue;
|
|
+ }
|
|
+ struct sockaddr *copy = malloc(length);
|
|
+ struct sockaddr **list = realloc(resolver->nameserver,
|
|
+ (resolver->n_nameserver + 1) * sizeof(*list));
|
|
+ if (list) resolver->nameserver = list;
|
|
+ if (!copy || !list) { free(copy); failed = 1; break; }
|
|
+ memcpy(copy, &address, length);
|
|
+ list[resolver->n_nameserver++] = copy;
|
|
+ } else if (!strcmp(key, "search")) {
|
|
+ do {
|
|
+ char *copy = strdup(value);
|
|
+ char **list = realloc(resolver->search,
|
|
+ (resolver->n_search + 1) * sizeof(*list));
|
|
+ if (list) resolver->search = list;
|
|
+ if (!copy || !list) { free(copy); failed = 1; break; }
|
|
+ list[resolver->n_search++] = copy;
|
|
+ } while ((value = strtok_r(NULL, " \t\r\n", &save)));
|
|
+ } else if (!strcmp(key, "domain")) {
|
|
+ char *copy = strdup(value);
|
|
+ if (!copy) { failed = 1; break; }
|
|
+ free(resolver->domain); resolver->domain = copy;
|
|
+ } else if (!strcmp(key, "options")) {
|
|
+ /* Preserve resolver options for libresolv's config reader. */
|
|
+ size_t length = strlen(value);
|
|
+ if (save && *save) { value[length] = ' '; length += 1 + strlen(save); }
|
|
+ while (length && isspace((unsigned char)value[length - 1])) --length;
|
|
+ char *copy = strndup(value, length);
|
|
+ if (!copy) { failed = 1; break; }
|
|
+ free(resolver->options); resolver->options = copy;
|
|
+ }
|
|
+ }
|
|
+ if (ferror(file)) failed = 1;
|
|
+ fclose(file);
|
|
+ if (failed || !resolver->n_nameserver) {
|
|
+ dns_configuration_free(config);
|
|
+ return NULL;
|
|
+ }
|
|
+ return config;
|
|
}
|
|
|
|
void
|
|
-dns_configuration_free (dns_config_t *config)
|
|
+dns_configuration_free(dns_config_t *config)
|
|
{
|
|
-#if 0
|
|
- if (config->n_resolver > 0)
|
|
- {
|
|
- dns_resolver_t* resolver;
|
|
-
|
|
- resolver = config->resolver[0];
|
|
-
|
|
- free(resolver->domain);
|
|
-
|
|
- while (resolver->n_search > 0)
|
|
- {
|
|
- free(resolver->search[resolver->n_search-1]);
|
|
- resolver->n_search--;
|
|
- }
|
|
- free(resolver->search);
|
|
-
|
|
- while (resolver->n_nameserver > 0)
|
|
- {
|
|
- free(resolver->nameserver[resolver->n_nameserver-1]);
|
|
- resolver->n_nameserver--;
|
|
- }
|
|
- free(resolver->nameserver);
|
|
- }
|
|
-
|
|
- free(config->resolver);
|
|
- free(config);
|
|
-#endif
|
|
+ if (!config) return;
|
|
+ for (int i = 0; i < config->n_resolver; ++i) {
|
|
+ dns_resolver_t *resolver = config->resolver[i];
|
|
+ if (!resolver) continue;
|
|
+ free(resolver->domain); free(resolver->options);
|
|
+ for (int j = 0; j < resolver->n_search; ++j) free(resolver->search[j]);
|
|
+ free(resolver->search);
|
|
+ for (int j = 0; j < resolver->n_nameserver; ++j) free(resolver->nameserver[j]);
|
|
+ free(resolver->nameserver); free(resolver);
|
|
+ }
|
|
+ free(config->resolver); free(config);
|
|
}
|
|
|
|
void
|
|
@@ -180,36 +131,3 @@ _dns_configuration_ack (dns_config_t *config,
|
|
const char *bundle_id)
|
|
{
|
|
}
|
|
-
|
|
-static char* remove_comment(char* line)
|
|
-{
|
|
- char* p;
|
|
-
|
|
- if (line[0] == '#')
|
|
- return NULL;
|
|
-
|
|
- p = strchr(line, '#');
|
|
- if (p != NULL)
|
|
- *p = 0;
|
|
-
|
|
- return line;
|
|
-}
|
|
-
|
|
-static char* remove_whitespace(char* line)
|
|
-{
|
|
- char* end;
|
|
-
|
|
- while (*line && isspace(*line))
|
|
- line++;
|
|
-
|
|
- end = line + strlen(line) - 1;
|
|
-
|
|
- while (end > line && isspace(*end))
|
|
- {
|
|
- *end = 0;
|
|
- end--;
|
|
- }
|
|
-
|
|
- return line;
|
|
-}
|
|
-
|