1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25 * February 2000 - created.
26 */
27
28/* -----------------------------------------------------------------------------
29includes
30----------------------------------------------------------------------------- */
31#include <string.h>
32#include <stdio.h>
33#include <termios.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <sys/errno.h>
37#include <sys/signal.h>
38#include <sys/param.h>
39#include <sys/socket.h>
40#include <sys/sockio.h>
41#include <net/if.h>
42#include <CoreFoundation/CoreFoundation.h>
43#include <SystemConfiguration/SystemConfiguration.h>
44#include <SystemConfiguration/SCPrivate.h>
45
46#include "ppp_msg.h"
47#include "ppp_privmsg.h"
48
49#include "../Family/ppp_domain.h"
50#include "scnc_main.h"
51#include "scnc_client.h"
52#include "ppp_manager.h"
53#include "ppp_option.h"
54#include "scnc_utils.h"
55#include "scnc_main.h"
56
57/* -----------------------------------------------------------------------------
58definitions
59----------------------------------------------------------------------------- */
60
61#ifndef MIN
62#define MIN(a, b)	((a) < (b)? (a): (b))
63#endif
64#ifndef MAX
65#define MAX(a, b)	((a) > (b)? (a): (b))
66#endif
67
68static u_char *empty_str = (u_char *)"";
69
70/* -----------------------------------------------------------------------------
71----------------------------------------------------------------------------- */
72u_long get_addr_option (struct service *serv, CFStringRef entity, CFStringRef property,
73        CFDictionaryRef options, CFDictionaryRef setup, u_int32_t *opt, u_int32_t defaultval)
74{
75    CFDictionaryRef	dict;
76    CFArrayRef		array;
77
78    // first search in the state
79    if (serv->u.ppp.phase != PPP_IDLE
80        && getAddressFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
81                entity, property, opt)) {
82        return 1;
83    }
84    // then, search in the option set
85    if (options
86        && (dict = CFDictionaryGetValue(options, entity))
87        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
88
89        if ((array = CFDictionaryGetValue(dict, property))
90            && (CFGetTypeID(array) == CFArrayGetTypeID())
91            && CFArrayGetCount(array)) {
92            *opt = CFStringAddrToLong(CFArrayGetValueAtIndex(array, 0));
93            return 2;
94        }
95    }
96
97    // at last, search in the setup
98    if (getAddressFromEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID,
99        entity, property, opt)) {
100        return 3;
101    }
102
103    *opt = defaultval;
104    return 0;
105}
106
107/* -----------------------------------------------------------------------------
108----------------------------------------------------------------------------- */
109u_long get_int_option (struct service *serv, CFStringRef entity, CFStringRef property,
110        CFDictionaryRef options, CFDictionaryRef setup, u_int32_t *opt, u_int32_t defaultval)
111{
112    CFDictionaryRef	dict;
113
114    // first search in the state
115    if (serv->u.ppp.phase != PPP_IDLE
116        && getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
117                entity, property, opt)) {
118        return 1;
119    }
120
121    // then, search in the option set
122    if (options
123        && (dict = CFDictionaryGetValue(options, entity))
124        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())
125        && getNumber(dict, property, opt)) {
126        return 2;
127    }
128
129    // at last, search in the setup
130    if ((setup
131        && (dict = CFDictionaryGetValue(setup, entity))
132        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())
133        && getNumber(dict, property, opt))
134        || (!setup && getNumberFromEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID,
135        entity, property, opt))) {
136        return 3;
137    }
138
139    *opt = defaultval;
140    return 0;
141}
142
143/* -----------------------------------------------------------------------------
144----------------------------------------------------------------------------- */
145int get_str_option (struct service *serv, CFStringRef entity, CFStringRef property,
146        CFDictionaryRef options, CFDictionaryRef setup, u_char *opt, u_int32_t optsiz, u_int32_t *outlen, u_char *defaultval)
147{
148    CFDictionaryRef	dict;
149
150    // first search in the state
151    if (serv->u.ppp.phase != PPP_IDLE
152    	&& getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainState, serv->serviceID,
153                entity, property, opt, optsiz)) {
154        *outlen = strlen((char*)opt);
155        return 1;
156    }
157
158    // then, search in the option set
159    if (options
160        && (dict = CFDictionaryGetValue(options, entity))
161    	&& (CFGetTypeID(dict) == CFDictionaryGetTypeID())
162        && getString(dict, property, opt, optsiz)) {
163        *outlen = strlen((char*)opt);
164        return 2;
165    }
166    // at last, search in the setup, only in lookinsetup flag is set
167    if ((setup
168        && (dict = CFDictionaryGetValue(setup, entity))
169        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())
170        && getString(dict, property, opt, optsiz))
171        || (!setup && getStringFromEntity(gDynamicStore, kSCDynamicStoreDomainSetup, serv->serviceID,
172        entity, property, opt, optsiz))) {
173        *outlen = strlen((char*)opt);
174        return 3;
175    }
176
177    strlcpy((char*)opt, (char*)defaultval, optsiz);
178    *outlen = strlen((char*)opt);
179    return 0;
180}
181
182/* -----------------------------------------------------------------------------
183----------------------------------------------------------------------------- */
184CFTypeRef get_cf_option (CFStringRef entity, CFStringRef property, CFTypeID type,
185        CFDictionaryRef options, CFDictionaryRef setup, CFTypeRef defaultval)
186{
187    CFDictionaryRef	dict;
188    CFTypeRef		ref;
189
190    // first, search in the option set
191    if (options
192        && (dict = CFDictionaryGetValue(options, entity))
193    	&& (CFGetTypeID(dict) == CFDictionaryGetTypeID())
194        && (ref = CFDictionaryGetValue(dict, property))
195    	&& (CFGetTypeID(ref) == type)) {
196        return ref;
197    }
198
199    // then, search in the setup
200    if (setup
201        && (dict = CFDictionaryGetValue(setup, entity))
202        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())
203        && (ref = CFDictionaryGetValue(dict, property))
204    	&& (CFGetTypeID(ref) == type)) {
205        return ref;
206    }
207
208    return defaultval;
209}
210
211/* -----------------------------------------------------------------------------
212----------------------------------------------------------------------------- */
213int ppp_getoptval(struct service *serv, CFDictionaryRef opts, CFDictionaryRef setup, u_int32_t otype, void *pdata, u_int32_t pdatasiz, u_int32_t *plen)
214{
215    u_int32_t 			lval, lval1, lval2;
216    u_int32_t			*lopt = (u_int32_t *)pdata;
217    u_char 			*popt = (u_char *)pdata;
218    char 			str[OPT_STR_LEN], str2[OPT_STR_LEN];
219
220    *plen = 4; // init the len for long options
221    *lopt = 0; // init to zero
222
223    switch (otype) {
224
225        // DEVICE options
226        case PPP_OPT_DEV_NAME:
227            if (serv->subtype == PPP_TYPE_SERIAL) {
228                if (setup) {
229                    CFDictionaryRef         dict;
230
231                    if ((dict = CFDictionaryGetValue(setup, kSCEntNetInterface))
232                        && (CFGetTypeID(dict) == CFDictionaryGetTypeID())) {
233                        SCNetworkInterfaceRef   interface;
234
235                        if ((interface = _SCNetworkInterfaceCreateWithEntity(NULL, dict, NULL))) {
236                            CFStringRef path;
237
238                            path = _SCNetworkInterfaceCopySlashDevPath(interface);
239                            CFRelease(interface);
240                            if (path) {
241
242                                CFStringGetCString(path, (char *)popt, OPT_STR_LEN, kCFStringEncodingUTF8);
243                                CFRelease(path);
244                                *plen = strlen((const char *)popt);
245                                break;
246                            }
247                        }
248                    }
249                }
250            }
251            get_str_option(serv, kSCEntNetInterface, kSCPropNetInterfaceDeviceName, opts, setup, popt, pdatasiz, plen,
252                           (serv->subtype == PPP_TYPE_SERIAL) ? (u_char*)OPT_DEV_NAME_DEF :
253                           ((serv->subtype == PPP_TYPE_PPPoE) ? (u_char*)OPT_DEV_NAME_PPPoE_DEF : empty_str));
254            break;
255        case PPP_OPT_DEV_SPEED:
256            *lopt = 0;
257            switch (serv->subtype) {
258                case PPP_TYPE_SERIAL:
259                    get_int_option(serv, kSCEntNetModem, kSCPropNetModemSpeed, opts, setup, lopt, OPT_DEV_SPEED_DEF);
260                    break;
261                case PPP_TYPE_PPPoE:
262                case PPP_TYPE_PPTP:
263                case PPP_TYPE_L2TP:
264                    break;
265            }
266            break;
267        case PPP_OPT_DEV_CONNECTSCRIPT:
268            get_str_option(serv, kSCEntNetModem, kSCPropNetModemConnectionScript, opts, setup, popt, pdatasiz, plen,
269                (serv->subtype == PPP_TYPE_SERIAL) ? (u_char*)OPT_DEV_CONNECTSCRIPT_DEF : empty_str);
270            break;
271
272        case PPP_OPT_DEV_DIALMODE:
273            *plen = 4;
274            *lopt = PPP_DEV_WAITFORDIALTONE;
275            str[0] = 0;
276            lval = sizeof(str);
277            get_str_option(serv, kSCEntNetModem, kSCPropNetModemDialMode, opts, setup, (u_char *)str, sizeof(str), &lval, empty_str);
278            str2[0] = 0;
279            CFStringGetCString(kSCValNetModemDialModeIgnoreDialTone, str2, sizeof(str2), kCFStringEncodingUTF8);
280            if (!strcmp(str, str2))
281                *lopt = PPP_DEV_IGNOREDIALTONE;
282            else {
283                str2[0] = 0;
284                CFStringGetCString(kSCValNetModemDialModeManual, str2, sizeof(str2), kCFStringEncodingUTF8);
285                if (!strcmp(str, str2))
286                    *lopt = PPP_DEV_MANUALDIAL;
287            }
288            break;
289
290        // COMM options
291       case PPP_OPT_DEV_CONNECTSPEED:
292            switch (serv->subtype) {
293                case PPP_TYPE_SERIAL:
294                    get_int_option(serv, kSCEntNetModem, kSCPropNetModemConnectSpeed, opts, setup, lopt, 0);
295                    break;
296                case PPP_TYPE_PPPoE:
297                case PPP_TYPE_PPTP:
298                case PPP_TYPE_L2TP:
299                    break;
300            }
301            break;
302        case PPP_OPT_COMM_TERMINALMODE:
303            *lopt = OPT_COMM_TERMINALMODE_DEF;
304            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommDisplayTerminalWindow, opts, setup, &lval, 0);
305            if (lval)
306                *lopt = PPP_COMM_TERM_WINDOW;
307            else {
308                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommUseTerminalScript, opts, setup, &lval, 0);
309                if (lval)
310                    *lopt = PPP_COMM_TERM_SCRIPT;
311            }
312	    break;
313        case PPP_OPT_COMM_TERMINALSCRIPT:
314            get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPCommTerminalScript, opts, setup, popt, pdatasiz, plen, empty_str);
315            break;
316        case PPP_OPT_COMM_IDLETIMER:
317            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdle, opts, setup, &lval, 0);
318            if (lval)
319                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPDisconnectOnIdleTimer, opts, setup, lopt, OPT_COMM_IDLETIMER_DEF);
320            break;
321        case PPP_OPT_COMM_SESSIONTIMER:
322            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPUseSessionTimer, opts, setup, &lval, 0);
323            if (lval)
324                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPSessionTimer, opts, setup, lopt, OPT_COMM_SESSIONTIMER_DEF);
325            break;
326        case PPP_OPT_COMM_REMINDERTIMER:
327            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminder,opts, setup,  &lval, 0);
328            if (lval)
329                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIdleReminderTimer, opts, setup, lopt, OPT_COMM_REMINDERTIMER_DEF);
330            break;
331        case PPP_OPT_COMM_REMOTEADDR:
332            get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPCommRemoteAddress, opts, setup, popt, pdatasiz, plen, empty_str);
333            break;
334        case PPP_OPT_COMM_CONNECTDELAY:
335            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPCommConnectDelay, opts, setup, lopt, OPT_COMM_CONNECTDELAY_DEF);
336            break;
337
338        // LCP options
339        case PPP_OPT_LCP_HDRCOMP:
340			get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPCompressionPField, opts, setup, &lval, serv->subtype == PPP_TYPE_PPPoE ? 0 : OPT_LCP_PCOMP_DEF);
341            if (serv->subtype == PPP_TYPE_PPPoE)
342				lval1 = 0; // not applicable
343			else
344				get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPCompressionACField, opts, setup, &lval1, OPT_LCP_ACCOMP_DEF);
345            *lopt = lval + (lval1 << 1);
346            break;
347        case PPP_OPT_LCP_MRU:
348            switch (serv->subtype) {
349                case PPP_TYPE_PPPoE:
350                    lval = OPT_LCP_MRU_PPPoE_DEF;
351                    break;
352                case PPP_TYPE_PPTP:
353                    lval = OPT_LCP_MRU_PPTP_DEF;
354                    break;
355                case PPP_TYPE_L2TP:
356                    lval = OPT_LCP_MRU_L2TP_DEF;
357		    break;
358                default:
359                    lval = OPT_LCP_MRU_DEF;
360            }
361            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPMRU, opts, setup, lopt, lval);
362            break;
363        case PPP_OPT_LCP_MTU:
364            switch (serv->subtype) {
365                case PPP_TYPE_PPPoE:
366                    lval = OPT_LCP_MTU_PPPoE_DEF;
367                    break;
368                case PPP_TYPE_PPTP:
369                    lval = OPT_LCP_MTU_PPTP_DEF;
370                    break;
371                case PPP_TYPE_L2TP:
372                    lval = OPT_LCP_MTU_L2TP_DEF;
373                    break;
374                default:
375                    lval = OPT_LCP_MTU_DEF;
376            }
377            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPMTU, opts, setup, lopt, lval);
378            break;
379        case PPP_OPT_LCP_RCACCM:
380            if (serv->subtype == PPP_TYPE_PPPoE) {
381				// not applicable
382				*plen = 0;
383				return 0;
384			}
385            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPReceiveACCM, opts, setup, lopt, OPT_LCP_RCACCM_DEF);
386            break;
387        case PPP_OPT_LCP_TXACCM:
388            if (serv->subtype == PPP_TYPE_PPPoE) {
389				// not applicable
390				*plen = 0;
391				return 0;
392			}
393            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPTransmitACCM, opts, setup, lopt, OPT_LCP_TXACCM_DEF);
394            break;
395        case PPP_OPT_LCP_ECHO:
396            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPEchoEnabled, opts, setup, &lval, 0);
397            if (lval) {
398                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPEchoInterval, opts, setup, &lval1, OPT_LCP_ECHOINTERVAL_DEF);
399                get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPLCPEchoFailure, opts, setup, &lval2, OPT_LCP_ECHOFAILURE_DEF);
400                *lopt = (lval1 << 16) + lval2;
401            }
402            break;
403
404        // AUTH options
405        case PPP_OPT_AUTH_PROTO:
406            *lopt = OPT_AUTH_PROTO_DEF;
407            // XXX To be fixed
408            break;
409        case PPP_OPT_AUTH_NAME:
410             get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthName, opts, setup, popt, pdatasiz, plen, empty_str);
411            break;
412        case PPP_OPT_AUTH_PASSWD:
413            get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPAuthPassword, opts, setup, popt, pdatasiz, plen, empty_str);
414            // don't return the actual pasword.
415            // instead, return len = 1 if password is known, len = 0 if password is unknown
416            if (*plen) {
417                popt[0] = '*';
418                *plen = 1;
419            }
420            break;
421
422            // IPCP options
423        case PPP_OPT_IPCP_HDRCOMP:
424            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPIPCPCompressionVJ, opts, setup, lopt, serv->subtype == PPP_TYPE_PPPoE ? 0 : OPT_IPCP_HDRCOMP_DEF);
425            break;
426        case PPP_OPT_IPCP_LOCALADDR:
427             get_addr_option(serv, kSCEntNetIPv4, kSCPropNetIPv4Addresses, opts, setup, lopt, 0);
428           break;
429        case PPP_OPT_IPCP_REMOTEADDR:
430            get_addr_option(serv, kSCEntNetIPv4, kSCPropNetIPv4DestAddresses, opts, setup, lopt, 0);
431            break;
432
433           // MISC options
434        case PPP_OPT_LOGFILE:
435            // Note: this option is not taken from the user options
436             get_str_option(serv, kSCEntNetPPP, kSCPropNetPPPLogfile, 0 /* opts */, setup, popt, pdatasiz, plen, empty_str);
437             if (popt[0] && popt[0] != '/') {
438                lval = strlen(DIR_LOGS);
439                strncpy((char*)(popt + lval), (char*)popt, *plen);
440                strncpy((char*)popt, DIR_LOGS, lval);
441                *plen += lval;
442             }
443            break;
444        case PPP_OPT_ALERTENABLE:
445            get_int_option(serv, kSCEntNetPPP, CFSTR("AlertEnable"), opts, setup, lopt, 0xFFFFFFFF);
446            break;
447        case PPP_OPT_DIALONDEMAND:
448        case PPP_OPT_AUTOCONNECT_DEPRECATED:
449            get_int_option(serv, kSCEntNetPPP, kSCPropNetPPPDialOnDemand, opts, setup, lopt, 0);
450            break;
451        case PPP_OPT_SERVICEID:
452            popt[0] = 0;
453            CFStringGetCString(serv->serviceID, (char*)popt, 256, kCFStringEncodingUTF8);
454            *plen = strlen((char*)popt);
455            break;
456        case PPP_OPT_IFNAME:
457            strncpy((char*)popt, (char*)serv->if_name, sizeof(serv->if_name));
458            *plen = strlen((char*)popt);
459            break;
460
461        default:
462            *plen = 0;
463            return 0; // not found
464    };
465
466    return 1; // OK
467}
468