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 *
26 *  Theory of operation :
27 *
28 *  plugin to add a generic socket support to pppd, instead of tty.
29 *
30----------------------------------------------------------------------------- */
31
32
33/* -----------------------------------------------------------------------------
34  Includes
35----------------------------------------------------------------------------- */
36
37#include <stdio.h>
38#include <ctype.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42#include <signal.h>
43#include <errno.h>
44#include <fcntl.h>
45#include <syslog.h>
46#include <netdb.h>
47#include <pwd.h>
48#include <setjmp.h>
49#include <sys/param.h>
50#include <sys/types.h>
51#include <sys/wait.h>
52#include <sys/time.h>
53#include <sys/resource.h>
54#include <sys/socket.h>
55#include <sys/stat.h>
56#include <sys/socket.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include <syslog.h>
60#include <sys/ioctl.h>
61#include <sys/un.h>
62#include <sys/uio.h>                     /* struct iovec */
63
64#include <net/if.h>
65#include <CoreFoundation/CFBundle.h>
66#include <ApplicationServices/ApplicationServices.h>
67#include <SystemConfiguration/SCSchemaDefinitions.h>
68
69#define APPLE 1
70
71#include "../../../Family/ppp_defs.h"
72#include "../../../Family/if_ppp.h"
73#include "../../../Family/ppp_domain.h"
74#include "../../../Helpers/pppd/pppd.h"
75#include "../../../Helpers/pppd/fsm.h"
76#include "../../../Helpers/pppd/lcp.h"
77
78#include <cclkeys.h>		// XX okay to #include Apple-specific header?
79
80
81/* -----------------------------------------------------------------------------
82 Definitions
83----------------------------------------------------------------------------- */
84
85#define DIR_MODEMS_USER         "/Library/Modem Scripts/"
86#define DIR_MODEMS_SYS          "/System/Library/Modem Scripts/"
87#define DIR_TERMINALS           "/Library/Terminal Scripts/"
88#define DIR_TTYS		"/dev/"
89
90#define SUFFIX_CCLENGINE	  	"/CCLEngine"
91#define PATH_MINITERM	  	"/usr/libexec/MiniTerm.app"
92
93
94// ppp serial error codes (bits 8..15 of last cause key)
95#define EXIT_PPPSERIAL_NOCARRIER  	1
96#define EXIT_PPPSERIAL_NONUMBER  	2
97#define EXIT_PPPSERIAL_BUSY	  	3
98#define EXIT_PPPSERIAL_NODIALTONE  	4
99#define EXIT_PPPSERIAL_ERROR	  	5
100#define EXIT_PPPSERIAL_NOANSWER	  	6
101#define EXIT_PPPSERIAL_HANGUP	  	7
102#define EXIT_PPPSERIAL_MODEMSCRIPTNOTFOUND  	8
103#define EXIT_PPPSERIAL_BADSCRIPT  	9
104
105/* -----------------------------------------------------------------------------
106 Forward declarations
107----------------------------------------------------------------------------- */
108void serial_check_options();
109int serial_connect(int *errorcode);
110void serial_process_extra_options();
111void serial_connect_notifier(void *param, uintptr_t code);
112void serial_lcpdown_notifier(void *param, uintptr_t code);
113int serial_terminal_window(char *script, int infd, int outfd);
114
115static int modemdict(char **argv);
116
117/* -----------------------------------------------------------------------------
118 PPP globals
119----------------------------------------------------------------------------- */
120
121extern char *serviceid;   			/* configuration service ID to publish */
122extern CFStringRef serviceidRef;	/* configuration service ID to publish */
123extern int	kill_link;
124
125static CFBundleRef 	bundle = 0;		/* our bundle ref */
126static CFURLRef    	url = 0;		/* our bundle url ref */
127
128/* option variables */
129static bool 	modemsound = 1;
130static bool 	modemreliable = 1;
131static bool 	modemcompress = 1;
132static bool 	modempulse = 0;
133static int	modemdialmode = 0;
134static u_char	fullmodemscript[1024] = { 0 };
135static u_char	fullterminalscript[1024] = { 0 };
136static u_char	connectcommand[1024] = { 0 };
137static u_char	pathccl[1024] = { 0 };
138static u_char	altconnectcommand[1024] = { 0 };
139static u_char	disconnectcommand[1024] = { 0 };
140static u_char	terminalcommand[1024] = { 0 };
141static u_char	cancelstr[32] = { 0 };
142static CFStringRef	cancelstrref = NULL;
143static u_char	icstr[32] = { 0 };
144static CFStringRef	icstrref = NULL;
145static u_char	iconstr[1024] = { 0 };
146static CFStringRef	iconstrref = NULL;
147static u_char	*modemscript = NULL;
148static u_char	*terminalscript = NULL;
149static bool 	terminalwindow = 0;
150void (*old_check_options) __P((void));
151int (*old_connect) __P((int *));
152void (*old_process_extra_options) __P((void));
153
154CFDictionaryRef modemdictref = NULL;
155
156static CFDataRef connectdataref = NULL;
157
158static CFDataRef terminaldataref = NULL;
159
160static CFDataRef altconnectdataref = NULL;
161
162static CFDataRef disconnectdataref = NULL;
163
164/* option descriptors */
165option_t serial_options[] = {
166    { "modemscript", o_string, &modemscript,
167      "CCL to use" },
168    { "modemsound", o_bool, &modemsound,
169      "Turn modem sound on", 1 },
170    { "nomodemsound", o_bool, &modemsound,
171      "Turn modem sound off", 0 },
172    { "modemreliable", o_bool, &modemreliable,
173      "Turn modem error correction on", 1 },
174    { "nomodemreliable", o_bool, &modemreliable,
175      "Turn modem error correction off", 0 },
176    { "modemcompress", o_bool, &modemcompress,
177      "Turn modem data compression on", 1 },
178    { "nomodemcompress", o_bool, &modemcompress,
179      "Turn modem data compression off", 0 },
180    { "modemtone", o_bool, &modempulse,
181      "Use modem tone mode", 0 },
182    { "modempulse", o_bool, &modempulse,
183      "Use modem pulse tone", 1 },
184    { "modemdialmode", o_int, &modemdialmode,
185      "dialmode : 0 = normal, 1 = blind(ignoredialtone), 2 = manual" },
186    { "terminalscript", o_string, &terminalscript,
187      "Terminal CCL to use" },
188    { "terminalwindow", o_bool, &terminalwindow,
189      "Use terminal window", 1 },
190    { "modemdict", o_special_cfarg, (void *)modemdict,
191      "Serialized Modem Dictionary ", OPT_PRIV },
192    { NULL }
193};
194
195
196
197
198/* -----------------------------------------------------------------------------
199plugin entry point, called by pppd
200----------------------------------------------------------------------------- */
201int start(CFBundleRef ref)
202{
203    CFStringRef 	strref;
204    CFURLRef 		urlref;
205
206    bundle = ref;
207    CFRetain(bundle);
208
209    url = CFBundleCopyBundleURL(bundle);
210
211    // hookup our handlers
212    old_check_options = the_channel->check_options;
213    the_channel->check_options = serial_check_options;
214
215    old_connect = the_channel->connect;
216    the_channel->connect = serial_connect;
217
218    old_process_extra_options = the_channel->process_extra_options;
219    the_channel->process_extra_options = serial_process_extra_options;
220
221    add_notifier(&connect_fail_notify, serial_connect_notifier, 0);
222    add_notifier(&lcp_lowerdown_notify, serial_lcpdown_notifier, 0);
223
224    cancelstrref = CFBundleCopyLocalizedString(bundle, CFSTR("Cancel"), CFSTR("Cancel"), NULL);
225    if (cancelstrref == 0) return 1;
226    CFStringGetCString(cancelstrref, (char*)cancelstr, sizeof(cancelstr), kCFStringEncodingUTF8);
227
228    icstrref = CFBundleCopyLocalizedString(bundle, CFSTR("Network Connection"), CFSTR("Network Connection"), NULL);
229    if (icstrref == 0) return 1;
230    CFStringGetCString(icstrref, (char*)icstr, sizeof(icstr), kCFStringEncodingUTF8);
231
232    urlref = CFBundleCopyResourceURL(bundle, CFSTR("NetworkConnect.icns"), NULL, NULL);
233    if (urlref == 0 || ((strref = CFURLGetString(urlref)) == 0)) {
234		if (urlref)
235            CFRelease(urlref);
236        return 1;
237    }
238    CFStringGetCString(strref, (char*)iconstr, sizeof(iconstr), kCFStringEncodingUTF8);
239
240	iconstrref = CFStringCreateCopy(NULL, strref);
241    CFRelease(urlref);
242
243	urlref = CFBundleCopyBuiltInPlugInsURL(bundle);
244	if (urlref == 0 || ((CFURLGetFileSystemRepresentation(urlref, TRUE, pathccl, sizeof(pathccl))) == FALSE)) {
245		if (urlref)
246            CFRelease(urlref);
247        return 1;
248    }
249    strlcat((char*)pathccl, SUFFIX_CCLENGINE, sizeof(pathccl));
250    CFRelease(urlref);
251
252    // add the socket specific options
253    add_options(serial_options);
254
255    return 0;
256}
257
258/* -----------------------------------------------------------------------------
259----------------------------------------------------------------------------- */
260void serial_process_extra_options()
261{
262    char str[MAXPATHLEN];
263    struct stat statbuf;
264
265    if (device && !ptycommand) {
266
267        // first, transform device name
268        str[0] = 0;
269        if (device[0] != '/') {
270            strlcat(str, DIR_TTYS, sizeof(str));
271            if ((device[0] != 't')
272                    || (device[1] != 't')
273                    || (device[2] != 'y')
274                    || (device[3] != 'd'))
275                    strlcat(str, "cu.", sizeof(str));
276        }
277        strlcat(str, device, sizeof(str));
278        strlcpy(devnam, str, sizeof(devnam));
279        default_device = 0;
280
281        // then check if device is there
282        if (stat(devnam, &statbuf) < 0) {
283            if (errno == ENOENT) {
284                option_error("Device '%s' does not exist", devnam);
285                die(EXIT_DEVICE_ERROR);
286            }
287            else
288                fatal("Couldn't stat device %s: %m", devnam);
289        }
290    }
291
292    if (old_process_extra_options)
293        (*old_process_extra_options)();
294}
295
296/* -----------------------------------------------------------------------------
297----------------------------------------------------------------------------- */
298void serial_check_options()
299{
300
301    //Fix me : we only get the 8 low bits return code from the wait_pid
302    cancelcode = 136; /*cclErr_ScriptCancelled*/
303
304    if (modemscript || modemdictref) {
305        // actual command will be filled in at connection time
306		connector_uid = 0;
307		disconnector_uid = 0;
308        connect_script = (char*)connectcommand;
309        disconnect_script = (char*)disconnectcommand;
310        if (altremoteaddress) {
311            altconnect_script = (char*)altconnectcommand;
312	    redialalternate = 1;
313	}
314        if (redialcount)
315            busycode = 122; /*cclErr_LineBusyErr*/
316    }
317
318    if (terminalwindow || terminalscript) {
319        // actual command will be filled in at connection time
320        terminal_script = (char*)terminalcommand;
321    }
322
323    if (old_check_options)
324        (*old_check_options)();
325}
326
327/* -------------------------------------------------------------------------------------------
328------------------------------------------------------------------------------------------- */
329CFDataRef Serialize(CFPropertyListRef obj, void **data, u_int32_t *dataLen)
330{
331    CFDataRef           	xml;
332
333    xml = CFPropertyListCreateXMLData(NULL, obj);
334    if (xml) {
335        *data = (void*)CFDataGetBytePtr(xml);
336        *dataLen = CFDataGetLength(xml);
337    }
338    return xml;
339}
340
341/* -----------------------------------------------------------------------------
342----------------------------------------------------------------------------- */
343int serial_connect(int *errorcode)
344{
345    struct stat 	statbuf;
346    int 		err;
347	CFMutableDictionaryRef ccldict, moddict;
348	int			val;
349	CFNumberRef	numRef;
350	CFStringRef	strRef;
351	CFMutableDictionaryRef connectdict = NULL;
352	CFMutableDictionaryRef terminaldict = NULL;
353
354	*errorcode = 0;
355
356    if (modemscript || modemdictref) {
357
358		// ---------- connect and altconnect scripts ----------
359
360		snprintf((char*)connectcommand, sizeof(connectcommand), "%s -l %s -x",
361		pathccl, serviceid);
362
363		// duplicate that into the alternate script
364		strlcpy((char*)altconnectcommand, (char*)connectcommand, sizeof(altconnectcommand));
365
366		// ---------- disconnect script ----------
367		snprintf((char*)disconnectcommand, sizeof(disconnectcommand), "%s -m 1 -l %s -x",
368			pathccl, serviceid);
369
370		connectdict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
371		if (!connectdict) {
372            option_error("Could't create the CCLEngine dictionary");
373            status = EXIT_CONNECT_FAILED;
374            return -1;
375		}
376
377		/* create the CCLEngine dictionary and add the keys */
378		ccldict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
379		if (!ccldict) {
380            option_error("Could't create the CCLEngine dictionary");
381            status = EXIT_CONNECT_FAILED;
382			CFRelease(connectdict);
383            return -1;
384		}
385
386		CFDictionaryAddValue(ccldict, kCCLEngineServiceIDKey, serviceidRef);
387		CFDictionaryAddValue(ccldict, kCCLEngineBundlePathKey, CFURLGetString(url));
388
389		val = log_to_fd >= 0 ? 1 : 0;
390		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
391		CFDictionaryAddValue(ccldict, kCCLEngineLogToStdErrKey, numRef);
392		CFRelease(numRef);
393
394		val = debug ? 1 : 0;
395		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
396		CFDictionaryAddValue(ccldict, kCCLEngineVerboseLoggingKey, numRef);
397		CFRelease(numRef);
398
399		val = LOG_NOTICE;
400		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
401		CFDictionaryAddValue(ccldict, kCCLEngineSyslogLevelKey, numRef);
402		CFRelease(numRef);
403
404		val = LOG_PPP;
405		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
406		CFDictionaryAddValue(ccldict, kCCLEngineSyslogFacilityKey, numRef);
407		CFRelease(numRef);
408
409		CFDictionaryAddValue(ccldict, kCCLEngineAlertNameKey, icstrref);
410		CFDictionaryAddValue(ccldict, kCCLEngineIconPathKey, iconstrref);
411		CFDictionaryAddValue(ccldict, kCCLEngineCancelNameKey, cancelstrref);
412
413		CFDictionaryAddValue(ccldict, kCCLEngineModeKey, kCCLEngineModeConnect);
414
415		CFDictionaryAddValue(connectdict, kCCLEngineDictKey, ccldict);
416
417		// if a modem dictionary was given, use it
418		if (modemdictref) {
419			/* create the Modem dictionary and add the keys */
420			moddict = CFDictionaryCreateMutableCopy(NULL, 0, modemdictref);
421			if (!moddict) {
422				option_error("Could't create the Modem dictionary");
423				status = EXIT_CONNECT_FAILED;
424				CFRelease(ccldict);
425				CFRelease(connectdict);
426				return -1;
427			}
428		}
429		// if a modem dictionary was not given, build one from arguments
430		else {
431			/* create the Modem dictionary and add the keys */
432			moddict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
433			if (!moddict) {
434				option_error("Could't create the Modem dictionary");
435				status = EXIT_CONNECT_FAILED;
436				CFRelease(ccldict);
437				CFRelease(connectdict);
438				return -1;
439			}
440
441			if (modemscript) {
442			   /* check for ccl */
443				err = 0;
444				if (modemscript[0] != '/') {
445					snprintf((char*)fullmodemscript, sizeof(fullmodemscript), "%s%s", DIR_MODEMS_SYS, modemscript);
446					if (stat((char*)fullmodemscript, &statbuf) < 0) {
447						snprintf((char*)fullmodemscript, sizeof(fullmodemscript), "%s%s", DIR_MODEMS_USER, modemscript);
448						err = stat((char*)fullmodemscript, &statbuf);
449					}
450				}
451				else {
452					strlcpy((char*)fullmodemscript, (char*)modemscript, sizeof(fullmodemscript));
453					err = stat((char*)fullmodemscript, &statbuf);
454				}
455
456				if (err) {
457					option_error("Could't find modem script '%s'", modemscript);
458					devstatus = EXIT_PPPSERIAL_MODEMSCRIPTNOTFOUND;
459					status = EXIT_CONNECT_FAILED;
460					CFRelease(moddict);
461					CFRelease(ccldict);
462					CFRelease(connectdict);
463					return -1;
464				}
465				strRef = CFStringCreateWithCString(NULL, (char*)fullmodemscript, kCFStringEncodingMacRoman);
466				if (strRef) {
467					CFDictionaryAddValue(moddict, kSCPropNetModemConnectionScript, strRef);
468					CFRelease(strRef);
469				}
470			}
471
472			val = modemsound;
473			numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
474			CFDictionaryAddValue(moddict, kSCPropNetModemSpeaker, numRef);
475			CFRelease(numRef);
476
477			val = modempulse;
478			numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
479			CFDictionaryAddValue(moddict, kSCPropNetModemPulseDial, numRef);
480			CFRelease(numRef);
481
482			val = modemcompress;
483			numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
484			CFDictionaryAddValue(moddict, kSCPropNetModemDataCompression, numRef);
485			CFRelease(numRef);
486
487			val = modemreliable;
488			numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
489			CFDictionaryAddValue(moddict, kSCPropNetModemErrorCorrection, numRef);
490			CFRelease(numRef);
491
492			CFDictionaryAddValue(moddict, kSCPropNetModemDialMode, modemdialmode == 1 ? kSCValNetModemDialModeIgnoreDialTone : (modemdialmode == 2 ? kSCValNetModemDialModeManual : kSCValNetModemDialModeWaitForDialTone) );
493		}
494
495		if (remoteaddress) {
496			strRef = CFStringCreateWithCString(NULL, remoteaddress, kCFStringEncodingMacRoman);
497			if (strRef) {
498				CFDictionaryAddValue(moddict, kModemPhoneNumberKey, strRef);
499				CFRelease(strRef);
500			}
501		}
502
503		CFDictionaryAddValue(connectdict, kSCEntNetModem, moddict);
504
505		if (connectdataref) {
506			CFRelease(connectdataref);
507		}
508		connectdataref = Serialize(connectdict, (void**)&connect_data, (uint32_t *)&connect_data_len);
509
510		if (altremoteaddress) {
511			strRef = CFStringCreateWithCString(NULL, altremoteaddress, kCFStringEncodingMacRoman);
512			if (strRef) {
513				CFDictionarySetValue(moddict, kModemPhoneNumberKey, strRef);
514				CFRelease(strRef);
515			}
516
517			if (altconnectdataref) {
518				CFRelease(altconnectdataref);
519			}
520			altconnectdataref = Serialize(connectdict, (void**)&altconnect_data, (uint32_t*)&altconnect_data_len);
521		}
522
523		CFDictionaryRemoveValue(moddict, kModemPhoneNumberKey);
524		CFDictionarySetValue(ccldict, kCCLEngineModeKey, kCCLEngineModeDisconnect);
525		if (disconnectdataref) {
526			CFRelease(disconnectdataref);
527		}
528		disconnectdataref = Serialize(connectdict, (void**)&disconnect_data, (uint32_t*)&disconnect_data_len);
529
530		CFRelease(ccldict);
531		CFRelease(moddict);
532		CFRelease(connectdict);
533    }
534
535    if (terminalwindow) {
536
537        terminal_window_hook = serial_terminal_window;
538        strlcpy((char*)terminalcommand, PATH_MINITERM, sizeof(terminalcommand));
539    }
540
541    if (terminalscript) {
542
543		snprintf((char*)terminalcommand, sizeof(terminalcommand), "%s -l %s -x", pathccl, serviceid);
544
545		terminaldict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
546		if (!terminaldict) {
547            option_error("Could't create the Terminal Script dictionary");
548            status = EXIT_CONNECT_FAILED;
549            return -1;
550		}
551
552		/* create the CCLEngine dictionary and add the keys */
553		ccldict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
554		if (!ccldict) {
555            option_error("Could't create the CCLEngine dictionary for Terminal script");
556            status = EXIT_CONNECT_FAILED;
557			CFRelease(terminaldict);
558            return -1;
559		}
560
561		CFDictionaryAddValue(ccldict, kCCLEngineServiceIDKey, serviceidRef);
562		CFDictionaryAddValue(ccldict, kCCLEngineBundlePathKey, CFURLGetString(url));
563
564		val = log_to_fd >= 0 ? 1 : 0;
565		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
566		CFDictionaryAddValue(ccldict, kCCLEngineLogToStdErrKey, numRef);
567		CFRelease(numRef);
568
569		val = debug ? 1 : 0;
570		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
571		CFDictionaryAddValue(ccldict, kCCLEngineVerboseLoggingKey, numRef);
572		CFRelease(numRef);
573
574		val = LOG_NOTICE;
575		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
576		CFDictionaryAddValue(ccldict, kCCLEngineSyslogLevelKey, numRef);
577		CFRelease(numRef);
578
579		val = LOG_PPP;
580		numRef = CFNumberCreate(NULL, kCFNumberIntType, &val);
581		CFDictionaryAddValue(ccldict, kCCLEngineSyslogFacilityKey, numRef);
582		CFRelease(numRef);
583
584		CFDictionaryAddValue(ccldict, kCCLEngineAlertNameKey, icstrref);
585		CFDictionaryAddValue(ccldict, kCCLEngineIconPathKey, iconstrref);
586		CFDictionaryAddValue(ccldict, kCCLEngineCancelNameKey, cancelstrref);
587
588		CFDictionaryAddValue(ccldict, kCCLEngineModeKey, kCCLEngineModeConnect);
589
590		CFDictionaryAddValue(terminaldict, kCCLEngineDictKey, ccldict);
591
592		/* create the Modem dictionary and add the keys */
593		moddict = CFDictionaryCreateMutable(NULL, 0, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
594		if (!moddict) {
595			option_error("Could't create the Modem dictionary for Terminal script");
596			status = EXIT_CONNECT_FAILED;
597			CFRelease(ccldict);
598			CFRelease(terminaldict);
599			return -1;
600		}
601
602	   /* check for ccl */
603		snprintf((char*)fullterminalscript, sizeof(fullterminalscript), "%s%s", (terminalscript[0] == '/') ? "" : DIR_TERMINALS, terminalscript);
604		err = stat((char*)fullterminalscript, &statbuf);
605		if (err) {
606			option_error("Could't find terminal script '%s'", terminalscript);
607			devstatus = EXIT_PPPSERIAL_MODEMSCRIPTNOTFOUND;
608			status = EXIT_CONNECT_FAILED;
609			CFRelease(moddict);
610			CFRelease(ccldict);
611			CFRelease(terminaldict);
612			return -1;
613		}
614		strRef = CFStringCreateWithCString(NULL, (char*)fullterminalscript, kCFStringEncodingMacRoman);
615		if (strRef) {
616			CFDictionaryAddValue(moddict, kSCPropNetModemConnectionScript, strRef);
617			CFRelease(strRef);
618		}
619
620
621		strRef = CFStringCreateWithCString(NULL, user, kCFStringEncodingMacRoman);
622		if (strRef) {
623			CFDictionaryAddValue(moddict, kSCPropNetPPPAuthName, strRef);
624			CFRelease(strRef);
625		}
626
627		strRef = CFStringCreateWithCString(NULL, passwd, kCFStringEncodingMacRoman);
628		if (strRef) {
629			CFDictionaryAddValue(moddict, kSCPropNetPPPAuthPassword, strRef);
630			CFRelease(strRef);
631		}
632
633		CFDictionaryAddValue(terminaldict, kSCEntNetModem, moddict);
634
635		if (terminaldataref) {
636			CFRelease(terminaldataref);
637		}
638		terminaldataref = Serialize(terminaldict, (void**)&terminal_data, (uint32_t *)&terminal_data_len);
639
640		CFRelease(ccldict);
641		CFRelease(moddict);
642		CFRelease(terminaldict);
643    }
644
645	if (remoteaddress)
646		set_network_signature("Modem.RemoteAddress", remoteaddress, 0, 0);
647
648    if (old_connect)
649        return (*old_connect)(errorcode);
650
651    return 0;
652}
653
654/* -----------------------------------------------------------------------------
655----------------------------------------------------------------------------- */
656void serial_connect_notifier(void *param, uintptr_t code)
657{
658
659    switch (code) {
660        // cclErr_BadScriptErr = -6028  	// Incorrect script for the modem.
661        case 116:
662            devstatus = EXIT_PPPSERIAL_BADSCRIPT;
663            break;
664        // cclErr_NoNumberErr = -6027  	// Can't connect because number is empty.
665        case 117:
666            devstatus = EXIT_PPPSERIAL_NONUMBER;
667            break;
668        // cclErr_NoAnswerErr = -6023	No answer.
669        case 121:
670            devstatus = EXIT_PPPSERIAL_NOANSWER;
671            break;
672        // cclErr_LineBusyErr = -6022	Line busy.
673        case 122:
674            devstatus = EXIT_PPPSERIAL_BUSY;
675            break;
676        // cclErr_NoCarrierErr = -6021	No carrier.
677        case 123:
678            devstatus = EXIT_PPPSERIAL_NOCARRIER;
679            break;
680        // cclErr_NoDialTone = -6020	No dial tone.
681       case 124: //
682            devstatus = EXIT_PPPSERIAL_NODIALTONE;
683            break;
684        // cclErr_ModemErr = -6019  Modem error, modem not responding
685        case 125:
686            devstatus = EXIT_PPPSERIAL_ERROR;
687            break;
688    }
689}
690
691/* -----------------------------------------------------------------------------
692----------------------------------------------------------------------------- */
693void serial_lcpdown_notifier(void *param, uintptr_t code)
694{
695
696    if (status == EXIT_HANGUP)
697        devstatus = EXIT_PPPSERIAL_HANGUP;
698}
699
700/* -----------------------------------------------------------------------------
701----------------------------------------------------------------------------- */
702static int start_listen (char *filestr)
703{
704    struct sockaddr_un	addr;
705    int			err, s;
706    mode_t		mask;
707
708    if ((s = socket(AF_LOCAL, SOCK_STREAM, 0)) == -1)
709        goto fail;
710
711    unlink(filestr);
712    bzero(&addr, sizeof(addr));
713    addr.sun_family = AF_LOCAL;
714    strlcpy(addr.sun_path, filestr, sizeof(addr.sun_path));
715    mask = umask(0);
716    err = bind(s, (struct sockaddr *)&addr, SUN_LEN(&addr));
717    umask(mask);
718    if (err)
719        goto fail;
720    listen(s, 1);
721    return s;
722
723fail:
724    if (s != -1)
725        close(s);
726    return -1;
727}
728
729/* -----------------------------------------------------------------------------
730 Pass a file descriptor to another process.
731 If fd<0, then -fd is sent back instead as the error status.
732----------------------------------------------------------------------------- */
733static int send_fd(int clifd, int fd)
734{
735    struct cmsg {
736        struct cmsghdr 	hdr;
737        int		fd;
738    } cmsg;
739    struct iovec	iov[1];
740    struct msghdr   	msg;
741    char		buf[2]; /* send_fd()/recv_fd() 2-byte protocol */
742
743    iov[0].iov_base = buf;
744    iov[0].iov_len = 2;
745    msg.msg_iov = iov;
746    msg.msg_iovlen = 1;
747    msg.msg_name = NULL;
748    msg.msg_namelen = 0;
749
750    if (fd < 0) {
751        msg.msg_control = NULL;
752	msg.msg_controllen = 0;
753	buf[1] = -fd;   /* nonzero status means error */
754	if (buf[1] == 0)
755            buf[1] = 1;     /* -256, etc. would screw up protocol */
756    } else {
757	cmsg.hdr.cmsg_level = SOL_SOCKET;
758	cmsg.hdr.cmsg_type = SCM_RIGHTS;
759	cmsg.hdr.cmsg_len = sizeof(struct cmsg);
760        cmsg.fd = fd;	/* the fd to pass */
761	msg.msg_control = (caddr_t) &cmsg;
762	msg.msg_controllen = sizeof(struct cmsg);
763	buf[1] = 0;	/* zero status means OK */
764    }
765    buf[0] = 0;	/* null byte flag to recv_fd() */
766
767    if (sendmsg(clifd, &msg, 0) != 2)
768	return -1;
769
770    return 0;
771}
772
773/* -----------------------------------------------------------------------------
774 use launch services to launch an application
775 return < 0 if the application cannot be launched
776----------------------------------------------------------------------------- */
777static int launch_app(char *app, char *params)
778{
779#ifdef HAVE_LAUNCHSERVICES
780
781    CFURLRef 		urlref;
782    LSLaunchURLSpec 	urlspec;
783    OSStatus 		err;
784#if 0
785    OSErr		oserr;
786    AEDesc		desc;
787#endif
788
789    urlref = CFURLCreateFromFileSystemRepresentation(NULL, (u_char*)app, strlen(app), FALSE);
790    if (urlref == 0)
791        return -1;
792
793#if 0
794    oserr = AECreateDesc(typeChar, params, strlen(params), &desc);
795    if (oserr != noErr) {
796        CFRelease(urlref);
797        return -1;
798    }
799#endif
800
801    urlspec.appURL = urlref;
802    urlspec.itemURLs = 0;
803    urlspec.passThruParams = 0;
804#if 0
805    urlspec.passThruParams = &desc;
806#endif
807    urlspec.launchFlags = kLSLaunchAsync + kLSLaunchDontAddToRecents
808                + kLSLaunchNewInstance + kLSLaunchNoParams;
809    urlspec.asyncRefCon = 0;
810
811    err = LSOpenFromURLSpec(&urlspec, NULL);
812    if (err != 0) {
813#if 0
814        AEDisposeDesc(&desc);
815#endif
816        CFRelease(urlref);
817        return -2;
818    }
819
820#if 0
821    AEDisposeDesc(&desc);
822#endif
823    CFRelease(urlref);
824
825#endif /* HAVE_LAUNCHSERVICES */
826    return 0;
827}
828
829/* -----------------------------------------------------------------------------
830----------------------------------------------------------------------------- */
831static int wait_accept(int fd)
832{
833    int			sacc = 0, nready, maxfd;
834	socklen_t	len;
835    fd_set	allset, rset;
836    struct timeval 	timenow, timeout, timeend;
837    struct sockaddr_un	addr;
838
839    FD_ZERO(&allset);
840    FD_SET(fd, &allset);
841    maxfd = fd;
842
843    getabsolutetime(&timeend);
844    timeend.tv_sec += 30; // allow 30 seconds for contact
845
846    // now wait for contact
847    for ( ; ; ) {
848
849        getabsolutetime(&timenow);
850        timeout.tv_sec = timeend.tv_sec - timenow.tv_sec;
851        timeout.tv_usec = timeend.tv_usec - timenow.tv_usec;
852        if (timeout.tv_usec < 0) {
853            timeout.tv_usec += 1000000;
854            timeout.tv_sec -= 1;
855        }
856
857        if (timeout.tv_sec < 0)
858            return -1; // time out expires
859
860        rset = allset;
861        nready = select(maxfd + 1, &rset, NULL, NULL, &timeout);
862
863        if (kill_link)
864            return -1;
865
866        if (FD_ISSET(fd, &rset)) {
867
868            len = sizeof(addr);
869            if ((sacc = accept(fd, (struct sockaddr *) &addr, &len)) == -1) {
870                return -2; // contact failed
871            }
872            break;
873        }
874    }
875
876    return sacc;
877}
878
879/* -----------------------------------------------------------------------------
880----------------------------------------------------------------------------- */
881static int readn(int ref, void *data, int len)
882{
883    int 	n, left = len;
884    void 	*p = data;
885
886    while (left > 0) {
887        if ((n = read(ref, p, left)) < 0) {
888            if (kill_link)
889                return 0;
890            if (errno != EINTR)
891                return -1;
892            n = 0;
893        }
894        else if (n == 0)
895            break; /* EOF */
896
897        left -= n;
898        p += n;
899    }
900    return (len - left);
901}
902
903/* -----------------------------------------------------------------------------
904----------------------------------------------------------------------------- */
905int serial_terminal_window(char *script, int infd, int outfd)
906{
907    int 	slis, sacc = 0, n;
908    char 	c;
909    char 	str[32];
910
911    //sprintf(str, "/var/run/pppd-%d", getpid());
912    snprintf(str, sizeof(str), "/var/run/pppd-miniterm");
913
914    slis = start_listen(str);
915    if (slis == -1) {
916        error("Cannot listen for terminal window application");
917        status = EXIT_TERMINAL_FAILED;
918        return -2;
919    }
920
921    if (launch_app(PATH_MINITERM, str) < 0) {
922        error("Cannot launch terminal window application");
923        status = EXIT_TERMINAL_FAILED;
924        close(slis);
925        return -2;
926    }
927
928    sacc = wait_accept(slis);
929    close(slis);
930    if (sacc < 0) {
931        if (kill_link)
932            return 0;
933        error("Cannot communicate with terminal window application.");
934        status = EXIT_TERMINAL_FAILED;
935        return -2;
936    }
937
938    send_fd(sacc, infd);
939
940    n = readn(sacc, &c, 1);
941    close(sacc);
942    if (n != 1) {
943        if (kill_link)
944            return 0;
945        error("Cannot get status from terminal window application (error %m)");
946        status = EXIT_TERMINAL_FAILED;
947        return -2;
948    }
949
950    return (unsigned char)c;
951}
952
953/* -----------------------------------------------------------------------------
954----------------------------------------------------------------------------- */
955static int
956modemdict(argv)
957    char **argv;
958{
959    CFDataRef          	xml;
960    CFStringRef        	xmlError;
961	u_int32_t			len;
962    char *				ptr;
963
964    len = strtoul(argv[0], &ptr, 0);
965
966    xml = CFDataCreate(NULL, (u_char*)argv[1], len);
967    if (xml) {
968        modemdictref = CFPropertyListCreateFromXMLData(NULL,
969                xml,  kCFPropertyListImmutable, &xmlError);
970        CFRelease(xml);
971    }
972
973	return 1;
974}
975