1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2012-2015 Apple Inc. All rights reserved.
4 *
5 * dnsctl.c
6 * Command-line tool using libdns_services.dylib
7 *
8 * To build only this tool, copy and paste the following on the command line:
9 * On Apple 64bit Platforms ONLY OSX/iOS:
10 * clang -Wall dnsctl.c /usr/lib/libdns_services.dylib -o dnsctl
11 *
12 */
13
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/time.h>
18#include <net/if.h> // if_nametoindex()
19
20#include "dns_services.h"
21#include <xpc/xpc.h>
22#include "dns_xpc.h"
23
24//*************************************************************************************************************
25// Globals:
26//*************************************************************************************************************
27
28static const char kFilePathSep   =  '/';
29
30static DNSXConnRef ClientRef     =  NULL;
31
32static xpc_connection_t dnsctl_conn = NULL;
33
34//*************************************************************************************************************
35// Utility Funcs:
36//*************************************************************************************************************
37
38static void printtimestamp(void)
39{
40    struct tm tm;
41    int ms;
42    static char date[16];
43    static char new_date[16];
44    struct timeval tv;
45    gettimeofday(&tv, NULL);
46    localtime_r((time_t*)&tv.tv_sec, &tm);
47    ms = tv.tv_usec/1000;
48    strftime(new_date, sizeof(new_date), "%a %d %b %Y", &tm);
49    //display date only if it has changed
50    if (strncmp(date, new_date, sizeof(new_date)))
51    {
52        printf("DATE: ---%s---\n", new_date);
53        strlcpy(date, new_date, sizeof(date));
54    }
55    printf("%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
56}
57
58static void print_usage(const char *arg0)
59{
60    fprintf(stderr, "%s USAGE:                                                                  \n", arg0);
61    fprintf(stderr, "%s -DP Enable DNS Proxy with Default Parameters                            \n", arg0);
62    fprintf(stderr, "%s -DP [-o <output interface>] [-i <input interface(s)>] Enable DNS Proxy  \n", arg0);
63    fprintf(stderr, "%s -L [1/2/3/4] Change mDNSResponder Logging Level                         \n", arg0);
64    fprintf(stderr, "%s -I Print mDNSResponder STATE INFO                                       \n", arg0);
65}
66
67
68static bool DebugEnabled()
69{
70    return true; // keep this true to debug the XPC msgs
71}
72
73static void DebugLog(const char *prefix, xpc_object_t o)
74{
75    if (!DebugEnabled())
76        return;
77
78    char *desc = xpc_copy_description(o);
79    printf("%s: %s \n", prefix, desc);
80    free(desc);
81}
82
83//*************************************************************************************************************
84// CallBack Funcs:
85//*************************************************************************************************************
86
87
88// DNSXEnableProxy Callback from the Daemon
89static void dnsproxy_reply(DNSXConnRef connRef, DNSXErrorType errCode)
90{
91    (void) connRef;
92    printtimestamp();
93    switch (errCode)
94    {
95        case kDNSX_NoError          :  printf("  SUCCESS   \n");
96            break;
97        case kDNSX_DaemonNotRunning :  printf(" NO DAEMON  \n");
98            DNSXRefDeAlloc(ClientRef);    break;
99        case kDNSX_BadParam          :  printf(" BAD PARAMETER \n");
100            DNSXRefDeAlloc(ClientRef);    break;
101        case kDNSX_Busy             :  printf(" BUSY \n");
102            DNSXRefDeAlloc(ClientRef);    break;
103        case kDNSX_UnknownErr       :
104        default                     :  printf(" UNKNOWN ERR \n");
105            DNSXRefDeAlloc(ClientRef);    break;
106    }
107    fflush(NULL);
108
109}
110
111//*************************************************************************************************************
112// XPC Funcs:
113//*************************************************************************************************************
114
115static void Init_Connection(const char *servname)
116{
117    dnsctl_conn = xpc_connection_create_mach_service(servname, dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
118
119    xpc_connection_set_event_handler(dnsctl_conn, ^(xpc_object_t event)
120    {
121         printf("InitConnection: [%s] \n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
122    });
123
124    xpc_connection_resume(dnsctl_conn);
125}
126
127static void SendDictToServer(xpc_object_t msg)
128{
129
130    DebugLog("SendDictToServer Sending msg to Daemon", msg);
131
132    xpc_connection_send_message_with_reply(dnsctl_conn, msg, dispatch_get_main_queue(), ^(xpc_object_t recv_msg)
133    {
134        xpc_type_t type = xpc_get_type(recv_msg);
135
136        if (type == XPC_TYPE_DICTIONARY)
137        {
138            DebugLog("SendDictToServer Received reply msg from Daemon", recv_msg);
139            /*
140            // If we ever want to do something based on the reply of the daemon
141            switch (daemon_status)
142            {
143                default:
144                    break;
145            }
146            */
147        }
148        else
149        {
150            printf("SendDictToServer Received unexpected reply from daemon [%s]",
151                                xpc_dictionary_get_string(recv_msg, XPC_ERROR_KEY_DESCRIPTION));
152            DebugLog("SendDictToServer Unexpected Reply contents", recv_msg);
153        }
154        exit(1);
155    });
156}
157
158//*************************************************************************************************************
159
160int main(int argc, char **argv)
161{
162    // Extract program name from argv[0], which by convention contains the path to this executable
163    const char *a0 = strrchr(argv[0], kFilePathSep) + 1;
164    if (a0 == (const char *)1)
165        a0 = argv[0];
166
167    // Must run as root
168    if (0 != geteuid())
169    {
170        fprintf(stderr, "%s MUST run as root!!\n", a0);
171        exit(-1);
172    }
173    if ((sizeof(argv) == 8))
174        printf("dnsctl running in 64-bit mode\n");
175    else if ((sizeof(argv) == 4))
176        printf("dnsctl running in 32-bit mode\n");
177
178    // expects atleast one argument
179    if (argc < 2)
180        goto Usage;
181
182    printtimestamp();
183    if (!strcasecmp(argv[1], "-DP"))
184    {
185        DNSXErrorType err;
186        // Default i/p intf is lo0 and o/p intf is primary interface
187        IfIndex Ipintfs[MaxInputIf] =  {1, 0, 0, 0, 0};
188        IfIndex Opintf = kDNSIfindexAny;
189
190        if (argc == 2)
191        {
192            dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
193            err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);
194            if (err)
195                fprintf(stderr, "DNSXEnableProxy returned %d\n", err);
196        }
197        else if (argc > 2)
198        {
199            argc--;
200            argv++;
201            if (!strcmp(argv[1], "-o"))
202            {
203                Opintf = if_nametoindex(argv[2]);
204                if (!Opintf)
205                    Opintf = atoi(argv[2]);
206                if (!Opintf)
207                {
208                    fprintf(stderr, "Could not parse o/p interface [%s]: Passing default primary \n", argv[2]);
209                    Opintf = kDNSIfindexAny;
210                }
211                argc -= 2;
212                argv += 2;
213            }
214            if (argc > 2 && !strcmp(argv[1], "-i"))
215            {
216                int i;
217                argc--;
218                argv++;
219                for (i = 0; i < MaxInputIf && argc > 1; i++)
220                {
221                    Ipintfs[i] = if_nametoindex(argv[1]);
222                    if (!Ipintfs[i])
223                        Ipintfs[i] = atoi(argv[1]);
224                    if (!Ipintfs[i])
225                    {
226                        fprintf(stderr, "Could not parse i/p interface [%s]: Passing default lo0 \n", argv[2]);
227                        Ipintfs[i] = 1;
228                    }
229                    argc--;
230                    argv++;
231                }
232            }
233            printf("Enabling DNSProxy on mDNSResponder \n");
234            dispatch_queue_t my_Q = dispatch_queue_create("com.apple.dnsctl.callback_queue", NULL);
235            err = DNSXEnableProxy(&ClientRef, kDNSProxyEnable, Ipintfs, Opintf, my_Q, dnsproxy_reply);
236            if (err)
237                fprintf(stderr, "DNSXEnableProxy returned %d\n", err);
238        }
239    }
240    else if (!strcasecmp(argv[1], "-l"))
241    {
242        printf("Changing loglevel of mDNSResponder \n");
243        Init_Connection(kDNSCTLService);
244
245        // Create Dictionary To Send
246        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
247
248        if (argc == 2)
249        {
250            xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1);
251
252            SendDictToServer(dict);
253            xpc_release(dict);
254            dict = NULL;
255        }
256        else if (argc > 2)
257        {
258            argc--;
259            argv++;
260            switch (atoi(argv[1]))
261            {
262                case log_level1:
263                    xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1);
264                    break;
265
266                case log_level2:
267                    xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level2);
268                    break;
269
270                case log_level3:
271                    xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level3);
272                    break;
273
274                case log_level4:
275                    xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level4);
276                    break;
277
278                default:
279                    xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level1);
280                    break;
281            }
282            SendDictToServer(dict);
283            xpc_release(dict);
284            dict = NULL;
285        }
286    }
287    else if(!strcasecmp(argv[1], "-i"))
288    {
289        printf("Get STATE INFO of mDNSResponder \n");
290        Init_Connection(kDNSCTLService);
291
292        // Create Dictionary To Send
293        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
294        xpc_dictionary_set_uint64(dict, kDNSStateInfo, full_state);
295        SendDictToServer(dict);
296        xpc_release(dict);
297        dict = NULL;
298    }
299    else if(!strcasecmp(argv[1], "-th"))
300    {
301        printf("Sending Test message to mDNSResponder to forward to mDNSResponderHelper\n");
302        Init_Connection(kDNSCTLService);
303
304        // Create Dictionary To Send
305        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
306        xpc_dictionary_set_uint64(dict, kmDNSResponderTests, test_helper_ipc);
307        SendDictToServer(dict);
308        xpc_release(dict);
309        dict = NULL;
310    }
311    else if(!strcasecmp(argv[1], "-tl"))
312    {
313        printf("Testing mDNSResponder Logging\n");
314        Init_Connection(kDNSCTLService);
315
316        // Create Dictionary To Send
317        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
318        xpc_dictionary_set_uint64(dict, kmDNSResponderTests, test_mDNS_log);
319        SendDictToServer(dict);
320        xpc_release(dict);
321        dict = NULL;
322    }
323    else
324    {
325        goto Usage;
326    }
327
328    dispatch_main();
329
330Usage:
331    print_usage(a0);
332    return 0;
333}
334
335/*
336
337#include <getopt.h>
338
339static int operation;
340
341static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd)
342{
343    // Return the recognized option in optstr and the option index of the next arg.
344    int o = getopt(argc, (char *const *)argv, optstr);
345    *pOptInd = optind;
346    return o;
347}
348
349int opindex;
350operation = getfirstoption(argc, argv, "lLDdPp", &opindex);
351if (operation == -1)
352    goto Usage;
353
354
355
356switch (operation)
357{
358    case 'L':
359    case 'l':
360    {
361        printtimestamp();
362        printf("Change Verbosity Level of mDNSResponder\n");
363
364        Init_Connection(kDNSCTLService);
365
366        // Create Dictionary To Send
367        xpc_object_t dict = xpc_dictionary_create(NULL, NULL, 0);
368        if (dict == NULL)
369            printf("could not create the Msg Dict To Send! \n");
370        xpc_dictionary_set_uint64(dict, kDNSLogLevel, log_level2);
371
372        SendDictToServer(dict);
373
374        xpc_release(dict);
375        dict = NULL;
376        break;
377    }
378 // exit(1);
379
380}
381
382*/
383
384