]> git.saurik.com Git - apple/mdnsresponder.git/blob - mDNSWindows/SystemService/Service.c
8671e0b9f47a22e7ecead318a66a934fe767fbf3
[apple/mdnsresponder.git] / mDNSWindows / SystemService / Service.c
1 /* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16
17 Change History (most recent first):
18
19 $Log: Service.c,v $
20 Revision 1.46 2009/06/05 18:28:24 herscher
21 <rdar://problem/6125087> mDNSResponder should be able to identify VPN adapters generically
22 <rdar://problem/6885843> WIN7: Bonjour removes the default gateway entry and thereby breaks network connectivity
23
24 Revision 1.45 2009/04/30 20:07:51 mcguire
25 <rdar://problem/6822674> Support multiple UDSs from launchd
26
27 Revision 1.44 2009/03/30 20:41:36 herscher
28 <rdar://problem/5925472> Current Bonjour code does not compile on Windows
29 <rdar://problem/6330821> Bonjour for Windows incompatible w/Juniper Network Connect. Do a substring search for "Juniper" in the description field
30 of the network adapter.
31 <rdar://problem/6122028> Bonjour for Windows incompatible w/Cisco AnyConnect VPN Client
32 <rdar://problem/5652098> Bonjour for Windows incompatible w/Juniper Network Connect. Update the adapter name per info received from Juniper
33 <rdar://problem/5781566> Core: Default Gateway set to 0.0.0.0 on Vista causes ARP flood
34 <rdar://problem/5991983> Change the registry values from Apple Computer, Inc. to Apple Inc.
35 <rdar://problem/5301328> wchar/sizeof mismatch in CheckFirewall()
36
37 Revision 1.43 2009/01/13 05:31:35 mkrochma
38 <rdar://problem/6491367> Replace bzero, bcopy with mDNSPlatformMemZero, mDNSPlatformMemCopy, memset, memcpy
39
40 Revision 1.42 2007/02/14 01:58:19 cheshire
41 <rdar://problem/4995831> Don't delete Unix Domain Socket on exit if we didn't create it on startup
42
43 Revision 1.41 2007/02/06 19:06:49 cheshire
44 <rdar://problem/3956518> Need to go native with launchd
45
46 Revision 1.40 2007/01/05 05:46:08 cheshire
47 Add mDNS *const m parameter to udsserver_handle_configchange()
48
49 Revision 1.39 2006/08/14 23:26:07 cheshire
50 Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
51
52 Revision 1.38 2005/10/05 20:55:15 herscher
53 <rdar://problem/4096464> Don't call SetLLRoute on loopback interface
54
55 Revision 1.37 2005/10/05 18:05:28 herscher
56 <rdar://problem/4192011> Save Wide-Area preferences in a different spot in the registry so they don't get removed when doing an update install.
57
58 Revision 1.36 2005/09/11 22:12:42 herscher
59 <rdar://problem/4247793> Remove dependency on WMI. Ensure that the Windows firewall is turned on before trying to configure it.
60
61 Revision 1.35 2005/06/30 18:29:49 shersche
62 <rdar://problem/4090059> Don't overwrite the localized service description text
63
64 Revision 1.34 2005/04/22 07:34:23 shersche
65 Check an interface's address and make sure it's valid before using it to set link-local routes.
66
67 Revision 1.33 2005/04/13 17:48:23 shersche
68 <rdar://problem/4079667> Make sure there is only one default route for link-local addresses.
69
70 Revision 1.32 2005/04/06 01:32:05 shersche
71 Remove default route for link-local addressing when another interface comes up with a routable IPv4 address
72
73 Revision 1.31 2005/04/06 01:00:11 shersche
74 <rdar://problem/4080127> GetFullPathName() should be passed the number of TCHARs in the path buffer, not the size in bytes of the path buffer.
75
76 Revision 1.30 2005/04/06 00:52:43 shersche
77 <rdar://problem/4079667> Only add default route if there are no other routable IPv4 addresses on any of the other interfaces. More work needs to be done to correctly configure the routing table when multiple interfaces are extant and none of them have routable IPv4 addresses.
78
79 Revision 1.29 2005/03/06 05:21:56 shersche
80 <rdar://problem/4037635> Fix corrupt UTF-8 name when non-ASCII system name used, enabled unicode support
81
82 Revision 1.28 2005/03/03 02:27:24 shersche
83 Include the RegNames.h header file for names of registry keys
84
85 Revision 1.27 2005/03/02 20:12:59 shersche
86 Update name
87
88 Revision 1.26 2005/02/15 08:00:27 shersche
89 <rdar://problem/4007151> Update name
90
91 Revision 1.25 2005/02/10 22:35:36 cheshire
92 <rdar://problem/3727944> Update name
93
94 Revision 1.24 2005/01/27 20:02:43 cheshire
95 udsSupportRemoveFDFromEventLoop() needs to close the SocketRef as well
96
97 Revision 1.23 2005/01/25 08:14:15 shersche
98 Change CacheRecord to CacheEntity
99
100 Revision 1.22 2004/12/10 13:18:40 cheshire
101 Create no-op function RecordUpdatedNiceLabel(), required by uds_daemon.c
102
103 Revision 1.21 2004/11/10 04:03:41 shersche
104 Remove SharedAccess dependency. This causes problems on XP SP1, and isn't necessary for XP SP2 because we already are dependent on WMI, which itself is dependent on SharedAccess.
105
106 Revision 1.20 2004/10/14 21:44:05 shersche
107 <rdar://problem/3838237> Fix a race condition between the socket thread and the main processing thread that resulted in the socket thread accessing a previously deleted Win32EventSource object.
108 Bug #: 3838237
109
110 Revision 1.19 2004/10/12 17:59:55 shersche
111 <rdar://problem/3718122> Disable routing table modifications when Nortel VPN adapter is active
112 Bug #: 3718122
113
114 Revision 1.18 2004/10/11 21:57:50 shersche
115 <rdar://problem/3832450> The SharedAccess service dependency causes a circular dependency on Windows Server 2003. Only add the SharedAccess service dependency if running on XP. All other platforms do not manipulate the firewall and thus are not dependent on it.
116 Bug #: 3832450
117
118 Revision 1.17 2004/09/17 01:08:58 cheshire
119 Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
120 The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
121 declared in that file are ONLY appropriate to single-address-space embedded applications.
122 For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
123
124 Revision 1.16 2004/09/16 18:49:34 shersche
125 Remove the XP SP2 check before attempting to manage the firewall. There is a race condition in the SP2 updater such that upon first reboot after the upgrade, mDNSResponder might not know that it is running under SP2 yet. This necessitates a second reboot before the firewall is managed. Removing the check will cause mDNSResponder to try and manage the firewall everytime it boots up, if and only if it hasn't managed the firewall a previous time.
126
127 Revision 1.15 2004/09/15 17:13:33 shersche
128 Change Firewall name
129
130 Revision 1.14 2004/09/15 09:37:25 shersche
131 Add SharedAccess to dependency list, call CheckFirewall after sending status back to SCM
132
133 Revision 1.13 2004/09/13 07:35:10 shersche
134 <rdar://problem/3762235> Add mDNSResponder to Windows Firewall application list if SP2 is detected and app hasn't been added before
135 Bug #: 3762235
136
137 Revision 1.12 2004/09/11 21:18:32 shersche
138 <rdar://problem/3779502> Add route to ARP everything when a 169.254.x.x address is selected
139 Bug #: 3779502
140
141 Revision 1.11 2004/09/11 05:39:19 shersche
142 <rdar://problem/3780203> Detect power managment state changes, calling mDNSCoreMachineSleep(m, true) on sleep, and mDNSCoreMachineSleep(m, false) on resume
143 Bug #: 3780203
144
145 Revision 1.10 2004/08/16 21:45:24 shersche
146 Use the full pathname of executable when calling CreateService()
147 Submitted by: prepin@zetron.com
148
149 Revision 1.9 2004/08/11 01:59:41 cheshire
150 Remove "mDNS *globalInstance" parameter from udsserver_init()
151
152 Revision 1.8 2004/08/05 05:40:05 shersche
153 <rdar://problem/3751566> Only invoke SetConsoleCtrlHandler when running directly from command line.
154 <rdar://problem/3751481> Invoke udsserver_handle_configchange() when the computer description changes
155 Bug #: 3751481, 3751566
156
157 Revision 1.7 2004/07/26 05:35:07 shersche
158 ignore non-enet interfaces when setting up link-local routing
159
160 Revision 1.6 2004/07/20 06:48:26 shersche
161 <rdar://problem/3718122> Allow registry entries to dictate whether to manage link local routing
162 Bug #: 3718122
163
164 Revision 1.5 2004/07/09 19:08:07 shersche
165 <rdar://problem/3713762> ServiceSetupEventLogging() errors are handled gracefully
166 Bug #: 3713762
167
168 Revision 1.4 2004/06/24 20:58:15 shersche
169 Fix compiler error in Release build
170 Submitted by: herscher
171
172 Revision 1.3 2004/06/24 15:28:53 shersche
173 Automatically setup routes to link-local addresses upon interface list change events.
174 Submitted by: herscher
175
176 Revision 1.2 2004/06/23 16:56:00 shersche
177 <rdar://problem/3697326> locked call to udsserver_idle().
178 Bug #: 3697326
179 Submitted by: herscher
180
181 Revision 1.1 2004/06/18 04:16:41 rpantos
182 Move up one level.
183
184 Revision 1.1 2004/01/30 02:58:39 bradley
185 mDNSResponder Windows Service. Provides global Bonjour support with an IPC interface.
186
187 */
188
189 #include <stdio.h>
190 #include <stdlib.h>
191 #include <stdarg.h>
192 #include <stddef.h>
193
194
195 #include "CommonServices.h"
196 #include "DebugServices.h"
197 #include "RegNames.h"
198
199 #include "uds_daemon.h"
200 #include "GenLinkedList.h"
201
202 #include "Resource.h"
203
204 #include "mDNSEmbeddedAPI.h"
205 #include "mDNSWin32.h"
206
207 #include "Firewall.h"
208
209 #if( !TARGET_OS_WINDOWS_CE )
210 #include <mswsock.h>
211 #include <process.h>
212 #include <ipExport.h>
213 #include <ws2def.h>
214 #include <ws2ipdef.h>
215 #include <iphlpapi.h>
216 #include <netioapi.h>
217 #include <iptypes.h>
218 #endif
219
220 #ifndef HeapEnableTerminationOnCorruption
221 # define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
222 #endif
223
224 #if 0
225 #pragma mark == Constants ==
226 #endif
227
228 //===========================================================================================================================
229 // Constants
230 //===========================================================================================================================
231
232 #define DEBUG_NAME "[Server] "
233 #define kServiceFirewallName L"Bonjour"
234 #define kServiceDependencies TEXT("Tcpip\0\0")
235 #define kDNSServiceCacheEntryCountDefault 512
236 #define kRetryFirewallPeriod 30 * 1000
237 #define kDefValueSize MAX_PATH + 1
238 #define kZeroIndex 0
239 #define kDefaultRouteMetric 399
240
241 #define RR_CACHE_SIZE 500
242 static CacheEntity gRRCache[RR_CACHE_SIZE];
243 #if 0
244 #pragma mark == Structures ==
245 #endif
246
247 //===========================================================================================================================
248 // Structures
249 //===========================================================================================================================
250 //---------------------------------------------------------------------------------------------------------------------------
251 /*! @typedef EventSourceFlags
252
253 @abstract Session flags.
254
255 @constant EventSourceFlagsNone No flags.
256 @constant EventSourceFlagsThreadDone Thread is no longer active.
257 @constant EventSourceFlagsNoClose Do not close the session when the thread exits.
258 @constant EventSourceFinalized Finalize has been called for this session
259 */
260
261 typedef uint32_t EventSourceFlags;
262
263 #define EventSourceFlagsNone 0
264 #define EventSourceFlagsThreadDone ( 1 << 2 )
265 #define EventSourceFlagsNoClose ( 1 << 3 )
266 #define EventSourceFinalized ( 1 << 4 )
267
268
269 typedef struct Win32EventSource
270 {
271 EventSourceFlags flags;
272 HANDLE threadHandle;
273 unsigned threadID;
274 HANDLE socketEvent;
275 HANDLE closeEvent;
276 udsEventCallback callback;
277 void * context;
278 DWORD waitCount;
279 HANDLE waitList[2];
280 SOCKET sock;
281 struct Win32EventSource * next;
282 } Win32EventSource;
283
284
285 #if 0
286 #pragma mark == Prototypes ==
287 #endif
288
289 //===========================================================================================================================
290 // Prototypes
291 //===========================================================================================================================
292 #if defined(UNICODE)
293 int __cdecl wmain( int argc, LPTSTR argv[] );
294 #else
295 int __cdecl main( int argc, char *argv[] );
296 #endif
297 static void Usage( void );
298 static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent );
299 static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath );
300 static OSStatus RemoveService( LPCTSTR inName );
301 static OSStatus SetServiceParameters();
302 static OSStatus GetServiceParameters();
303 static OSStatus CheckFirewall();
304 static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription );
305 static void ReportStatus( int inType, const char *inFormat, ... );
306 static OSStatus RunDirect( int argc, LPTSTR argv[] );
307
308 static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] );
309 static OSStatus ServiceSetupEventLogging( void );
310 static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext );
311
312 static OSStatus ServiceRun( int argc, LPTSTR argv[] );
313 static void ServiceStop( void );
314
315 static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] );
316 static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] );
317 static OSStatus ServiceSpecificStop( void );
318 static void ServiceSpecificFinalize( int argc, LPTSTR argv[] );
319 static mStatus EventSourceFinalize(Win32EventSource * source);
320 static void EventSourceLock();
321 static void EventSourceUnlock();
322 static mDNSs32 udsIdle(mDNS * const inMDNS, mDNSs32 interval);
323 static void CoreCallback(mDNS * const inMDNS, mStatus result);
324 static void HostDescriptionChanged(mDNS * const inMDNS);
325 static OSStatus GetRouteDestination(DWORD * ifIndex, DWORD * address);
326 static OSStatus SetLLRoute( mDNS * const inMDNS );
327 static bool HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric );
328 static bool IsValidAddress( const char * addr );
329 static bool IsNortelVPN( IP_ADAPTER_INFO * pAdapter );
330 static bool IsJuniperVPN( IP_ADAPTER_INFO * pAdapter );
331 static bool IsCiscoVPN( IP_ADAPTER_INFO * pAdapter );
332 static const char * strnistr( const char * string, const char * subString, size_t max );
333
334 #if defined(UNICODE)
335 # define StrLen(X) wcslen(X)
336 # define StrCmp(X,Y) wcscmp(X,Y)
337 #else
338 # define StrLen(X) strlen(X)
339 # define StrCmp(X,Y) strcmp(X,Y)
340 #endif
341
342
343 #define kLLNetworkAddr "169.254.0.0"
344 #define kLLNetworkAddrMask "255.255.0.0"
345
346
347 #include "mDNSEmbeddedAPI.h"
348
349 #if 0
350 #pragma mark == Globals ==
351 #endif
352
353 //===========================================================================================================================
354 // Globals
355 //===========================================================================================================================
356 #define gMDNSRecord mDNSStorage
357 DEBUG_LOCAL mDNS_PlatformSupport gPlatformStorage;
358 DEBUG_LOCAL BOOL gServiceQuietMode = FALSE;
359 DEBUG_LOCAL SERVICE_TABLE_ENTRY gServiceDispatchTable[] =
360 {
361 { kServiceName, ServiceMain },
362 { NULL, NULL }
363 };
364 DEBUG_LOCAL SERVICE_STATUS gServiceStatus;
365 DEBUG_LOCAL SERVICE_STATUS_HANDLE gServiceStatusHandle = NULL;
366 DEBUG_LOCAL HANDLE gServiceEventSource = NULL;
367 DEBUG_LOCAL bool gServiceAllowRemote = false;
368 DEBUG_LOCAL int gServiceCacheEntryCount = 0; // 0 means to use the DNS-SD default.
369 DEBUG_LOCAL bool gServiceManageLLRouting = true;
370 DEBUG_LOCAL int gWaitCount = 0;
371 DEBUG_LOCAL HANDLE * gWaitList = NULL;
372 DEBUG_LOCAL HANDLE gStopEvent = NULL;
373 DEBUG_LOCAL CRITICAL_SECTION gEventSourceLock;
374 DEBUG_LOCAL GenLinkedList gEventSources;
375 DEBUG_LOCAL BOOL gRetryFirewall = FALSE;
376 DEBUG_LOCAL DWORD gOSMajorVersion;
377 DEBUG_LOCAL DWORD gOSMinorVersion;
378
379 typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW );
380 mDNSlocal HMODULE gIPHelperLibraryInstance = NULL;
381 mDNSlocal GetIpInterfaceEntryFunctionPtr gGetIpInterfaceEntryFunctionPtr = NULL;
382
383
384 #if 0
385 #pragma mark -
386 #endif
387
388 //===========================================================================================================================
389 // main
390 //===========================================================================================================================
391 #if defined(UNICODE)
392 int __cdecl wmain( int argc, wchar_t * argv[] )
393 #else
394 int __cdecl main( int argc, char *argv[] )
395 #endif
396 {
397 OSStatus err;
398 BOOL ok;
399 BOOL start;
400 int i;
401
402 HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
403
404 debug_initialize( kDebugOutputTypeMetaConsole );
405 debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
406
407 // Default to automatically starting the service dispatcher if no extra arguments are specified.
408
409 start = ( argc <= 1 );
410
411 // Parse arguments.
412
413 for( i = 1; i < argc; ++i )
414 {
415 if( StrCmp( argv[ i ], TEXT("-install") ) == 0 ) // Install
416 {
417 TCHAR desc[ 256 ];
418
419 desc[ 0 ] = 0;
420 LoadString( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) );
421 err = InstallService( kServiceName, kServiceName, desc, argv[0] );
422 if( err )
423 {
424 ReportStatus( EVENTLOG_ERROR_TYPE, "install service failed (%d)\n", err );
425 goto exit;
426 }
427 }
428 else if( StrCmp( argv[ i ], TEXT("-remove") ) == 0 ) // Remove
429 {
430 err = RemoveService( kServiceName );
431 if( err )
432 {
433 ReportStatus( EVENTLOG_ERROR_TYPE, "remove service failed (%d)\n", err );
434 goto exit;
435 }
436 }
437 else if( StrCmp( argv[ i ], TEXT("-start") ) == 0 ) // Start
438 {
439 start = TRUE;
440 }
441 else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 ) // Server
442 {
443 err = RunDirect( argc, argv );
444 if( err )
445 {
446 ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err );
447 }
448 goto exit;
449 }
450 else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 ) // Quiet Mode (toggle)
451 {
452 gServiceQuietMode = !gServiceQuietMode;
453 }
454 else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || // Help
455 ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) )
456 {
457 Usage();
458 err = 0;
459 break;
460 }
461 else
462 {
463 Usage();
464 err = kParamErr;
465 break;
466 }
467 }
468
469 // Start the service dispatcher if requested. This does not return until all services have terminated. If any
470 // global initialization is needed, it should be done before starting the service dispatcher, but only if it
471 // will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
472
473 if( start )
474 {
475 ok = StartServiceCtrlDispatcher( gServiceDispatchTable );
476 err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
477 if( err != kNoErr )
478 {
479 ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err );
480 goto exit;
481 }
482 }
483 err = 0;
484
485 exit:
486 dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err );
487 return( (int) err );
488 }
489
490 //===========================================================================================================================
491 // Usage
492 //===========================================================================================================================
493
494 static void Usage( void )
495 {
496 fprintf( stderr, "\n" );
497 fprintf( stderr, "mDNSResponder 1.0d1\n" );
498 fprintf( stderr, "\n" );
499 fprintf( stderr, " <no args> Runs the service normally\n" );
500 fprintf( stderr, " -install Creates the service and starts it\n" );
501 fprintf( stderr, " -remove Stops the service and deletes it\n" );
502 fprintf( stderr, " -start Starts the service dispatcher after processing all other arguments\n" );
503 fprintf( stderr, " -server Runs the service directly as a server (for debugging)\n" );
504 fprintf( stderr, " -q Toggles Quiet Mode (no events or output)\n" );
505 fprintf( stderr, " -remote Allow remote connections\n" );
506 fprintf( stderr, " -cache n Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault );
507 fprintf( stderr, " -h[elp] Display Help/Usage\n" );
508 fprintf( stderr, "\n" );
509 }
510
511 //===========================================================================================================================
512 // ConsoleControlHandler
513 //===========================================================================================================================
514
515 static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent )
516 {
517 BOOL handled;
518 OSStatus err;
519
520 handled = FALSE;
521 switch( inControlEvent )
522 {
523 case CTRL_C_EVENT:
524 case CTRL_BREAK_EVENT:
525 case CTRL_CLOSE_EVENT:
526 case CTRL_LOGOFF_EVENT:
527 case CTRL_SHUTDOWN_EVENT:
528 err = ServiceSpecificStop();
529 require_noerr( err, exit );
530
531 handled = TRUE;
532 break;
533
534 default:
535 break;
536 }
537
538 exit:
539 return( handled );
540 }
541
542 //===========================================================================================================================
543 // InstallService
544 //===========================================================================================================================
545
546 static OSStatus InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath )
547 {
548 OSStatus err;
549 SC_HANDLE scm;
550 SC_HANDLE service;
551 BOOL ok;
552 TCHAR fullPath[ MAX_PATH ];
553 TCHAR * namePtr;
554 DWORD size;
555
556 scm = NULL;
557 service = NULL;
558
559 // Get a full path to the executable since a relative path may have been specified.
560
561 size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr );
562 err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
563 require_noerr( err, exit );
564
565 // Create the service and start it.
566
567 scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
568 err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
569 require_noerr( err, exit );
570
571 service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS,
572 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies,
573 NULL, NULL );
574 err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr );
575 require_noerr( err, exit );
576
577 err = SetServiceParameters();
578 check_noerr( err );
579
580 if( inDescription )
581 {
582 err = SetServiceInfo( scm, inName, inDescription );
583 check_noerr( err );
584 }
585
586 ok = StartService( service, 0, NULL );
587 err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
588 require_noerr( err, exit );
589
590 ReportStatus( EVENTLOG_SUCCESS, "installed service \"%s\"/\"%s\" at \"%s\"\n", inName, inDisplayName, inPath );
591 err = kNoErr;
592
593 exit:
594 if( service )
595 {
596 CloseServiceHandle( service );
597 }
598 if( scm )
599 {
600 CloseServiceHandle( scm );
601 }
602 return( err );
603 }
604
605 //===========================================================================================================================
606 // RemoveService
607 //===========================================================================================================================
608
609 static OSStatus RemoveService( LPCTSTR inName )
610 {
611 OSStatus err;
612 SC_HANDLE scm;
613 SC_HANDLE service;
614 BOOL ok;
615 SERVICE_STATUS status;
616
617 scm = NULL;
618 service = NULL;
619
620 // Open a connection to the service.
621
622 scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS );
623 err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
624 require_noerr( err, exit );
625
626 service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE );
627 err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
628 require_noerr( err, exit );
629
630 // Stop the service, if it is not already stopped, then delete it.
631
632 ok = QueryServiceStatus( service, &status );
633 err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
634 require_noerr( err, exit );
635
636 if( status.dwCurrentState != SERVICE_STOPPED )
637 {
638 ok = ControlService( service, SERVICE_CONTROL_STOP, &status );
639 check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
640 }
641
642 ok = DeleteService( service );
643 err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr );
644 require_noerr( err, exit );
645
646 ReportStatus( EVENTLOG_SUCCESS, "Removed service \"%s\"\n", inName );
647 err = ERROR_SUCCESS;
648
649 exit:
650 if( service )
651 {
652 CloseServiceHandle( service );
653 }
654 if( scm )
655 {
656 CloseServiceHandle( scm );
657 }
658 return( err );
659 }
660
661
662
663 //===========================================================================================================================
664 // SetServiceParameters
665 //===========================================================================================================================
666
667 static OSStatus SetServiceParameters()
668 {
669 DWORD value;
670 DWORD valueLen = sizeof(DWORD);
671 DWORD type;
672 OSStatus err;
673 HKEY key;
674
675 key = NULL;
676
677 //
678 // Add/Open Parameters section under service entry in registry
679 //
680 err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
681 require_noerr( err, exit );
682
683 //
684 // If the value isn't already there, then we create it
685 //
686 err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
687
688 if (err != ERROR_SUCCESS)
689 {
690 value = 1;
691
692 err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) );
693 require_noerr( err, exit );
694 }
695
696 exit:
697
698 if ( key )
699 {
700 RegCloseKey( key );
701 }
702
703 return( err );
704 }
705
706
707
708 //===========================================================================================================================
709 // GetServiceParameters
710 //===========================================================================================================================
711
712 static OSStatus GetServiceParameters()
713 {
714 DWORD value;
715 DWORD valueLen;
716 DWORD type;
717 OSStatus err;
718 HKEY key;
719
720 key = NULL;
721
722 //
723 // Add/Open Parameters section under service entry in registry
724 //
725 err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
726 require_noerr( err, exit );
727
728 valueLen = sizeof(DWORD);
729 err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
730 if (err == ERROR_SUCCESS)
731 {
732 gServiceManageLLRouting = (value) ? true : false;
733 }
734
735 valueLen = sizeof(DWORD);
736 err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen);
737 if (err == ERROR_SUCCESS)
738 {
739 gServiceCacheEntryCount = value;
740 }
741
742 exit:
743
744 if ( key )
745 {
746 RegCloseKey( key );
747 }
748
749 return( err );
750 }
751
752
753 //===========================================================================================================================
754 // CheckFirewall
755 //===========================================================================================================================
756
757 static OSStatus CheckFirewall()
758 {
759 DWORD value;
760 DWORD valueLen;
761 DWORD type;
762 ENUM_SERVICE_STATUS * lpService = NULL;
763 SC_HANDLE sc = NULL;
764 HKEY key = NULL;
765 BOOL ok;
766 DWORD bytesNeeded = 0;
767 DWORD srvCount;
768 DWORD resumeHandle = 0;
769 DWORD srvType;
770 DWORD srvState;
771 DWORD dwBytes = 0;
772 DWORD i;
773 BOOL isRunning = FALSE;
774 OSStatus err = kUnknownErr;
775
776 // Check to see if the firewall service is running. If it isn't, then
777 // we want to return immediately
778
779 sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
780 err = translate_errno( sc, GetLastError(), kUnknownErr );
781 require_noerr( err, exit );
782
783 srvType = SERVICE_WIN32;
784 srvState = SERVICE_STATE_ALL;
785
786 for ( ;; )
787 {
788 // Call EnumServicesStatus using the handle returned by OpenSCManager
789
790 ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
791
792 if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
793 {
794 break;
795 }
796
797 if ( lpService )
798 {
799 free( lpService );
800 }
801
802 dwBytes = bytesNeeded;
803
804 lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
805 require_action( lpService, exit, err = mStatus_NoMemoryErr );
806 }
807
808 err = translate_errno( ok, GetLastError(), kUnknownErr );
809 require_noerr( err, exit );
810
811 for ( i = 0; i < srvCount; i++ )
812 {
813 if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 )
814 {
815 if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING )
816 {
817 isRunning = TRUE;
818 }
819
820 break;
821 }
822 }
823
824 require_action( isRunning, exit, err = kUnknownErr );
825
826 // Check to see if we've managed the firewall.
827 // This package might have been installed, then
828 // the OS was upgraded to SP2 or above. If that's
829 // the case, then we need to manipulate the firewall
830 // so networking works correctly.
831
832 err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
833 require_noerr( err, exit );
834
835 valueLen = sizeof(DWORD);
836 err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen);
837
838 if ((err != ERROR_SUCCESS) || (value == 0))
839 {
840 wchar_t fullPath[ MAX_PATH ];
841 DWORD size;
842
843 // Get a full path to the executable
844
845 size = GetModuleFileNameW( NULL, fullPath, MAX_PATH );
846 err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
847 require_noerr( err, exit );
848
849 err = mDNSAddToFirewall(fullPath, kServiceFirewallName);
850 require_noerr( err, exit );
851
852 value = 1;
853 err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) );
854 require_noerr( err, exit );
855 }
856
857 exit:
858
859 if ( key )
860 {
861 RegCloseKey( key );
862 }
863
864 if ( lpService )
865 {
866 free( lpService );
867 }
868
869 if ( sc )
870 {
871 CloseServiceHandle ( sc );
872 }
873
874 return( err );
875 }
876
877
878
879 //===========================================================================================================================
880 // SetServiceInfo
881 //===========================================================================================================================
882
883 static OSStatus SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription )
884 {
885 OSStatus err;
886 SC_LOCK lock;
887 SC_HANDLE service;
888 SERVICE_DESCRIPTION description;
889 SERVICE_FAILURE_ACTIONS actions;
890 SC_ACTION action;
891 BOOL ok;
892
893 check( inServiceName );
894 check( inDescription );
895
896 lock = NULL;
897 service = NULL;
898
899 // Open the database (if not provided) and lock it to prevent other access while re-configuring.
900
901 if( !inSCM )
902 {
903 inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
904 err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr );
905 require_noerr( err, exit );
906 }
907
908 lock = LockServiceDatabase( inSCM );
909 err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr );
910 require_noerr( err, exit );
911
912 // Open a handle to the service.
913
914 service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START );
915 err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
916 require_noerr( err, exit );
917
918 // Change the description.
919
920 description.lpDescription = (LPTSTR) inDescription;
921 ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description );
922 err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
923 require_noerr( err, exit );
924
925 actions.dwResetPeriod = INFINITE;
926 actions.lpRebootMsg = NULL;
927 actions.lpCommand = NULL;
928 actions.cActions = 1;
929 actions.lpsaActions = &action;
930 action.Delay = 500;
931 action.Type = SC_ACTION_RESTART;
932
933 ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions );
934 err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
935 require_noerr( err, exit );
936
937 err = ERROR_SUCCESS;
938
939 exit:
940 // Close the service and release the lock.
941
942 if( service )
943 {
944 CloseServiceHandle( service );
945 }
946 if( lock )
947 {
948 UnlockServiceDatabase( lock );
949 }
950 return( err );
951 }
952
953 //===========================================================================================================================
954 // ReportStatus
955 //===========================================================================================================================
956
957 static void ReportStatus( int inType, const char *inFormat, ... )
958 {
959 if( !gServiceQuietMode )
960 {
961 va_list args;
962
963 va_start( args, inFormat );
964 if( gServiceEventSource )
965 {
966 char s[ 1024 ];
967 BOOL ok;
968 const char * array[ 1 ];
969
970 vsprintf( s, inFormat, args );
971 array[ 0 ] = s;
972 ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, 0x20000001L, NULL, 1, 0, array, NULL );
973 check_translated_errno( ok, GetLastError(), kUnknownErr );
974 }
975 else
976 {
977 int n;
978
979 n = vfprintf( stderr, inFormat, args );
980 check( n >= 0 );
981 }
982 va_end( args );
983 }
984 }
985
986 //===========================================================================================================================
987 // RunDirect
988 //===========================================================================================================================
989
990 static OSStatus RunDirect( int argc, LPTSTR argv[] )
991 {
992 OSStatus err;
993 BOOL initialized;
994 BOOL ok;
995
996 initialized = FALSE;
997
998 // Install a Console Control Handler to handle things like control-c signals.
999
1000 ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
1001 err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1002 require_noerr( err, exit );
1003
1004 err = ServiceSpecificInitialize( argc, argv );
1005 require_noerr( err, exit );
1006 initialized = TRUE;
1007
1008 // Run the service. This does not return until the service quits or is stopped.
1009
1010 ReportStatus( EVENTLOG_SUCCESS, "Running \"%s\" service directly\n", kServiceName );
1011
1012 err = ServiceSpecificRun( argc, argv );
1013 require_noerr( err, exit );
1014
1015 // Clean up.
1016
1017 exit:
1018 if( initialized )
1019 {
1020 ServiceSpecificFinalize( argc, argv );
1021 }
1022 return( err );
1023 }
1024
1025 #if 0
1026 #pragma mark -
1027 #endif
1028
1029 //===========================================================================================================================
1030 // ServiceMain
1031 //===========================================================================================================================
1032
1033 static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] )
1034 {
1035 OSStatus err;
1036 BOOL ok;
1037
1038 err = ServiceSetupEventLogging();
1039 check_noerr( err );
1040
1041 err = GetServiceParameters();
1042 check_noerr( err );
1043
1044 // Initialize the service status and register the service control handler with the name of the service.
1045
1046 gServiceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
1047 gServiceStatus.dwCurrentState = 0;
1048 gServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_POWEREVENT;
1049 gServiceStatus.dwWin32ExitCode = NO_ERROR;
1050 gServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
1051 gServiceStatus.dwCheckPoint = 0;
1052 gServiceStatus.dwWaitHint = 0;
1053
1054 gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL );
1055 err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr );
1056 require_noerr( err, exit );
1057
1058 // Mark the service as starting.
1059
1060 gServiceStatus.dwCurrentState = SERVICE_START_PENDING;
1061 gServiceStatus.dwCheckPoint = 0;
1062 gServiceStatus.dwWaitHint = 5000; // 5 seconds
1063 ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1064 check_translated_errno( ok, GetLastError(), kParamErr );
1065
1066 // Run the service. This does not return until the service quits or is stopped.
1067
1068 err = ServiceRun( (int) argc, argv );
1069 if( err != kNoErr )
1070 {
1071 gServiceStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
1072 gServiceStatus.dwServiceSpecificExitCode = (DWORD) err;
1073 }
1074
1075 // Service-specific work is done so mark the service as stopped.
1076
1077 gServiceStatus.dwCurrentState = SERVICE_STOPPED;
1078 ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1079 check_translated_errno( ok, GetLastError(), kParamErr );
1080
1081 // Note: The service status handle should not be closed according to Microsoft documentation.
1082
1083 exit:
1084 if( gServiceEventSource )
1085 {
1086 ok = DeregisterEventSource( gServiceEventSource );
1087 check_translated_errno( ok, GetLastError(), kUnknownErr );
1088 gServiceEventSource = NULL;
1089 }
1090 }
1091
1092 //===========================================================================================================================
1093 // ServiceSetupEventLogging
1094 //===========================================================================================================================
1095
1096 static OSStatus ServiceSetupEventLogging( void )
1097 {
1098 OSStatus err;
1099 HKEY key;
1100 LPCTSTR s;
1101 DWORD typesSupported;
1102 TCHAR path[ MAX_PATH ];
1103 DWORD n;
1104
1105 key = NULL;
1106
1107 // Add/Open source name as a sub-key under the Application key in the EventLog registry key.
1108
1109 s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName;
1110 err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key );
1111 require_noerr( err, exit );
1112
1113 // Add the name to the EventMessageFile subkey.
1114
1115 path[ 0 ] = '\0';
1116 GetModuleFileName( NULL, path, MAX_PATH );
1117 n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) );
1118 err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
1119 require_noerr( err, exit );
1120
1121 // Set the supported event types in the TypesSupported subkey.
1122
1123 typesSupported = 0
1124 | EVENTLOG_SUCCESS
1125 | EVENTLOG_ERROR_TYPE
1126 | EVENTLOG_WARNING_TYPE
1127 | EVENTLOG_INFORMATION_TYPE
1128 | EVENTLOG_AUDIT_SUCCESS
1129 | EVENTLOG_AUDIT_FAILURE;
1130 err = RegSetValueEx( key, TEXT("TypesSupported"), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
1131 require_noerr( err, exit );
1132
1133 // Set up the event source.
1134
1135 gServiceEventSource = RegisterEventSource( NULL, kServiceName );
1136 err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr );
1137 require_noerr( err, exit );
1138
1139 exit:
1140 if( key )
1141 {
1142 RegCloseKey( key );
1143 }
1144 return( err );
1145 }
1146
1147 //===========================================================================================================================
1148 // ServiceControlHandler
1149 //===========================================================================================================================
1150
1151 static DWORD WINAPI ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext )
1152 {
1153 BOOL setStatus;
1154 BOOL ok;
1155
1156 DEBUG_UNUSED( inEventData );
1157 DEBUG_UNUSED( inContext );
1158
1159 setStatus = TRUE;
1160 switch( inControl )
1161 {
1162 case SERVICE_CONTROL_STOP:
1163 dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP\n" );
1164
1165 ServiceStop();
1166 setStatus = FALSE;
1167 break;
1168
1169 case SERVICE_CONTROL_POWEREVENT:
1170
1171 if (inEventType == PBT_APMSUSPEND)
1172 {
1173 mDNSCoreMachineSleep(&gMDNSRecord, TRUE);
1174 }
1175 else if (inEventType == PBT_APMRESUMESUSPEND)
1176 {
1177 mDNSCoreMachineSleep(&gMDNSRecord, FALSE);
1178 }
1179
1180 break;
1181
1182 default:
1183 dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl );
1184 break;
1185 }
1186
1187 if( setStatus && gServiceStatusHandle )
1188 {
1189 ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1190 check_translated_errno( ok, GetLastError(), kUnknownErr );
1191 }
1192
1193 return NO_ERROR;
1194 }
1195
1196 //===========================================================================================================================
1197 // ServiceRun
1198 //===========================================================================================================================
1199
1200 static OSStatus ServiceRun( int argc, LPTSTR argv[] )
1201 {
1202 OSStatus err;
1203 BOOL initialized;
1204 BOOL ok;
1205
1206 DEBUG_UNUSED( argc );
1207 DEBUG_UNUSED( argv );
1208
1209 initialized = FALSE;
1210
1211 // <rdar://problem/5727548> Make the service as running before we call ServiceSpecificInitialize. We've
1212 // had reports that some machines with McAfee firewall installed cause a problem with iTunes installation.
1213 // We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a
1214 // simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock
1215 // any installers that are waiting for our state to change.
1216
1217 gServiceStatus.dwCurrentState = SERVICE_RUNNING;
1218 ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1219 check_translated_errno( ok, GetLastError(), kParamErr );
1220
1221 // Initialize the service-specific stuff
1222
1223 err = ServiceSpecificInitialize( argc, argv );
1224 require_noerr( err, exit );
1225 initialized = TRUE;
1226
1227 err = CheckFirewall();
1228 check_noerr( err );
1229
1230 if ( err )
1231 {
1232 gRetryFirewall = TRUE;
1233 }
1234
1235 // Run the service-specific stuff. This does not return until the service quits or is stopped.
1236
1237 ReportStatus( EVENTLOG_INFORMATION_TYPE, "mDNSResponder started\n" );
1238 err = ServiceSpecificRun( argc, argv );
1239 ReportStatus( EVENTLOG_INFORMATION_TYPE, "mDNSResponder stopped (%d)\n", err );
1240 require_noerr( err, exit );
1241
1242 // Service stopped. Clean up and we're done.
1243
1244 exit:
1245 if( initialized )
1246 {
1247 ServiceSpecificFinalize( argc, argv );
1248 }
1249 return( err );
1250 }
1251
1252 //===========================================================================================================================
1253 // ServiceStop
1254 //===========================================================================================================================
1255
1256 static void ServiceStop( void )
1257 {
1258 BOOL ok;
1259 OSStatus err;
1260
1261 // Signal the event to cause the service to exit.
1262
1263 if( gServiceStatusHandle )
1264 {
1265 gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
1266 ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
1267 check_translated_errno( ok, GetLastError(), kParamErr );
1268 }
1269
1270 err = ServiceSpecificStop();
1271 check_noerr( err );
1272 }
1273
1274 #if 0
1275 #pragma mark -
1276 #pragma mark == Service Specific ==
1277 #endif
1278
1279 //===========================================================================================================================
1280 // ServiceSpecificInitialize
1281 //===========================================================================================================================
1282
1283 static OSStatus ServiceSpecificInitialize( int argc, LPTSTR argv[] )
1284 {
1285 OSVERSIONINFO osInfo;
1286 OSStatus err;
1287 BOOL ok;
1288
1289 DEBUG_UNUSED( argc );
1290 DEBUG_UNUSED( argv );
1291
1292 mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord);
1293 mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage);
1294
1295 gPlatformStorage.idleThreadCallback = udsIdle;
1296 gPlatformStorage.hostDescriptionChangedCallback = HostDescriptionChanged;
1297
1298 InitializeCriticalSection(&gEventSourceLock);
1299
1300 gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1301 err = translate_errno( gStopEvent, errno_compat(), kNoResourcesErr );
1302 require_noerr( err, exit );
1303
1304 err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext);
1305 require_noerr( err, exit);
1306
1307 err = udsserver_init(mDNSNULL, 0);
1308 require_noerr( err, exit);
1309
1310 //
1311 // Get the version of Windows that we're running on
1312 //
1313 osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
1314 ok = GetVersionEx( &osInfo );
1315 err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1316 require_noerr( err, exit );
1317 gOSMajorVersion = osInfo.dwMajorVersion;
1318 gOSMinorVersion = osInfo.dwMinorVersion;
1319
1320 SetLLRoute( &gMDNSRecord );
1321
1322 exit:
1323 if( err != kNoErr )
1324 {
1325 ServiceSpecificFinalize( argc, argv );
1326 }
1327 return( err );
1328 }
1329
1330 //===========================================================================================================================
1331 // ServiceSpecificRun
1332 //===========================================================================================================================
1333
1334 static OSStatus ServiceSpecificRun( int argc, LPTSTR argv[] )
1335 {
1336 DWORD timeout;
1337 DWORD result;
1338
1339 DEBUG_UNUSED( argc );
1340 DEBUG_UNUSED( argv );
1341
1342 // Main event loop. Process connection requests and state changes (i.e. quit).
1343
1344 timeout = ( gRetryFirewall ) ? kRetryFirewallPeriod : INFINITE;
1345
1346 while( (result = WaitForSingleObject( gStopEvent, timeout ) ) != WAIT_OBJECT_0 )
1347 {
1348 if ( result == WAIT_TIMEOUT )
1349 {
1350 OSStatus err;
1351
1352 err = CheckFirewall();
1353 check_noerr( err );
1354
1355 timeout = INFINITE;
1356 }
1357 else
1358 {
1359 // Unexpected wait result.
1360 dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result );
1361 }
1362 }
1363
1364 return kNoErr;
1365 }
1366
1367 //===========================================================================================================================
1368 // ServiceSpecificStop
1369 //===========================================================================================================================
1370
1371 static OSStatus ServiceSpecificStop( void )
1372 {
1373 OSStatus err;
1374 BOOL ok;
1375
1376 ok = SetEvent(gStopEvent);
1377 err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
1378 require_noerr( err, exit );
1379 exit:
1380 return( err );
1381 }
1382
1383 //===========================================================================================================================
1384 // ServiceSpecificFinalize
1385 //===========================================================================================================================
1386
1387 static void ServiceSpecificFinalize( int argc, LPTSTR argv[] )
1388 {
1389 DEBUG_UNUSED( argc );
1390 DEBUG_UNUSED( argv );
1391
1392 //
1393 // clean up any open sessions
1394 //
1395 while (gEventSources.Head)
1396 {
1397 EventSourceFinalize((Win32EventSource*) gEventSources.Head);
1398 }
1399 //
1400 // give a chance for the udsserver code to clean up
1401 //
1402 udsserver_exit();
1403
1404 //
1405 // and finally close down the mDNSCore
1406 //
1407 mDNS_Close(&gMDNSRecord);
1408
1409 //
1410 // clean up the event sources mutex...no one should be using it now
1411 //
1412 DeleteCriticalSection(&gEventSourceLock);
1413
1414 //
1415 // clean up loaded library
1416 //
1417
1418 if( gIPHelperLibraryInstance )
1419 {
1420 gGetIpInterfaceEntryFunctionPtr = NULL;
1421
1422 FreeLibrary( gIPHelperLibraryInstance );
1423 gIPHelperLibraryInstance = NULL;
1424 }
1425 }
1426
1427
1428 static void
1429 CoreCallback(mDNS * const inMDNS, mStatus status)
1430 {
1431 if (status == mStatus_ConfigChanged)
1432 {
1433 SetLLRoute( inMDNS );
1434 }
1435 }
1436
1437
1438 static mDNSs32
1439 udsIdle(mDNS * const inMDNS, mDNSs32 interval)
1440 {
1441 DEBUG_UNUSED( inMDNS );
1442
1443 //
1444 // rdar://problem/3697326
1445 //
1446 // udsserver_idle wasn't being locked. This resulted
1447 // in multiple threads contesting for the all_requests
1448 // data structure in uds_daemon.c
1449 //
1450 mDNSPlatformLock(&gMDNSRecord);
1451
1452 interval = udsserver_idle(interval);
1453
1454 mDNSPlatformUnlock(&gMDNSRecord);
1455
1456 return interval;
1457 }
1458
1459
1460 static void
1461 HostDescriptionChanged(mDNS * const inMDNS)
1462 {
1463 DEBUG_UNUSED( inMDNS );
1464
1465 udsserver_handle_configchange(inMDNS);
1466 }
1467
1468
1469 mDNSlocal unsigned WINAPI
1470 udsSocketThread(LPVOID inParam)
1471 {
1472 Win32EventSource * source = (Win32EventSource*) inParam;
1473 DWORD threadID = GetCurrentThreadId();
1474 DWORD waitCount;
1475 HANDLE waitList[2];
1476 bool safeToClose;
1477 bool done;
1478 bool locked = false;
1479 mStatus err = 0;
1480
1481 waitCount = source->waitCount;
1482 waitList[0] = source->waitList[0];
1483 waitList[1] = source->waitList[1];
1484 done = (bool) (source->flags & EventSourceFinalized);
1485
1486 while (!done)
1487 {
1488 DWORD result;
1489
1490 result = WaitForMultipleObjects(waitCount, waitList, FALSE, INFINITE);
1491
1492 mDNSPlatformLock(&gMDNSRecord);
1493 locked = true;
1494
1495 // <rdar://problem/3838237>
1496 //
1497 // Look up the source by the thread id. This will ensure that the
1498 // source is still extant. It could already have been deleted
1499 // by the processing thread.
1500 //
1501
1502 EventSourceLock();
1503
1504 for (source = gEventSources.Head; source; source = source->next)
1505 {
1506 if (source->threadID == threadID)
1507 {
1508 break;
1509 }
1510 }
1511
1512 EventSourceUnlock();
1513
1514 if (source == NULL)
1515 {
1516 goto exit;
1517 }
1518
1519 //
1520 // socket event
1521 //
1522 if (result == WAIT_OBJECT_0)
1523 {
1524 source->callback( (int) source->sock, 0, source->context);
1525 }
1526 //
1527 // close event
1528 //
1529 else if (result == WAIT_OBJECT_0 + 1)
1530 {
1531 //
1532 // this is a bit of a hack. we want to clean up the internal data structures
1533 // so we'll go in here and it will clean up for us
1534 //
1535 shutdown(source->sock, 2);
1536 source->callback( (int) source->sock, 0, source->context);
1537
1538 break;
1539 }
1540 else
1541 {
1542 // Unexpected wait result.
1543 dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result );
1544 goto exit;
1545 }
1546
1547 done = (bool) (source->flags & EventSourceFinalized);
1548
1549 mDNSPlatformUnlock(&gMDNSRecord);
1550 locked = false;
1551 }
1552
1553 EventSourceLock();
1554 source->flags |= EventSourceFlagsThreadDone;
1555 safeToClose = !( source->flags & EventSourceFlagsNoClose );
1556 EventSourceUnlock();
1557
1558 if( safeToClose )
1559 {
1560 EventSourceFinalize( source );
1561 }
1562
1563 exit:
1564
1565 if ( locked )
1566 {
1567 mDNSPlatformUnlock(&gMDNSRecord);
1568 }
1569
1570 _endthreadex_compat( (unsigned) err );
1571 return( (unsigned) err );
1572 }
1573
1574
1575 mStatus
1576 udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context)
1577 {
1578 Win32EventSource * newSource;
1579 DWORD result;
1580 mStatus err;
1581
1582 newSource = malloc(sizeof(Win32EventSource));
1583 require_action( newSource, exit, err = mStatus_NoMemoryErr );
1584 mDNSPlatformMemZero(newSource, sizeof(Win32EventSource));
1585
1586 newSource->flags = 0;
1587 newSource->sock = (SOCKET) fd;
1588 newSource->callback = callback;
1589 newSource->context = context;
1590
1591 newSource->socketEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1592 err = translate_errno( newSource->socketEvent, (mStatus) GetLastError(), kUnknownErr );
1593 require_noerr( err, exit );
1594
1595 newSource->closeEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
1596 err = translate_errno( newSource->closeEvent, (mStatus) GetLastError(), kUnknownErr );
1597 require_noerr( err, exit );
1598
1599 err = WSAEventSelect(newSource->sock, newSource->socketEvent, FD_ACCEPT|FD_READ|FD_CLOSE);
1600 err = translate_errno( err == 0, errno_compat(), kNoResourcesErr );
1601 require_noerr( err, exit );
1602
1603 newSource->waitCount = 0;
1604 newSource->waitList[ newSource->waitCount++ ] = newSource->socketEvent;
1605 newSource->waitList[ newSource->waitCount++ ] = newSource->closeEvent;
1606
1607 //
1608 // lock the list
1609 //
1610 EventSourceLock();
1611
1612 // add the event source to the end of the list, while checking
1613 // to see if the list needs to be initialized
1614 //
1615 if ( gEventSources.LinkOffset == 0)
1616 {
1617 InitLinkedList( &gEventSources, offsetof( Win32EventSource, next));
1618 }
1619
1620 AddToTail( &gEventSources, newSource);
1621
1622 //
1623 // no longer using the list
1624 //
1625 EventSourceUnlock();
1626
1627 // Create thread with _beginthreadex() instead of CreateThread() to avoid memory leaks when using static run-time
1628 // libraries. See <http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/createthread.asp>.
1629 // Create the thread suspended then resume it so the thread handle and ID are valid before the thread starts running.
1630 newSource->threadHandle = (HANDLE) _beginthreadex_compat( NULL, 0, udsSocketThread, newSource, CREATE_SUSPENDED, &newSource->threadID );
1631 err = translate_errno( newSource->threadHandle, (mStatus) GetLastError(), kUnknownErr );
1632 require_noerr( err, exit );
1633
1634 result = ResumeThread( newSource->threadHandle );
1635 err = translate_errno( result != (DWORD) -1, errno_compat(), kNoResourcesErr );
1636 require_noerr( err, exit );
1637
1638 exit:
1639
1640 if (err && newSource)
1641 {
1642 EventSourceFinalize(newSource);
1643 }
1644
1645 return err;
1646 }
1647
1648
1649 mStatus
1650 udsSupportRemoveFDFromEventLoop( SocketRef fd) // Note: This also CLOSES the socket
1651 {
1652 Win32EventSource * source;
1653 mStatus err = mStatus_NoError;
1654
1655 //
1656 // find the event source
1657 //
1658 EventSourceLock();
1659
1660 for (source = gEventSources.Head; source; source = source->next)
1661 {
1662 if (source->sock == (SOCKET) fd)
1663 {
1664 break;
1665 }
1666 }
1667
1668 //
1669 // if we found him, finalize him
1670 //
1671 if (source != NULL)
1672 {
1673 EventSourceFinalize(source);
1674 }
1675
1676 //
1677 // done with the list
1678 //
1679 EventSourceUnlock();
1680
1681 closesocket(fd);
1682
1683 return err;
1684 }
1685
1686
1687 mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
1688 {
1689 (void)m;
1690 (void)delay;
1691 // No-op, for now
1692 }
1693
1694
1695 static mStatus
1696 EventSourceFinalize(Win32EventSource * source)
1697 {
1698 OSStatus err;
1699 bool locked;
1700 Win32EventSource * inserted;
1701 bool sameThread;
1702 bool deferClose;
1703 BOOL ok;
1704 DWORD threadID;
1705 DWORD result;
1706
1707 check( source );
1708
1709 // Find the session in the list.
1710
1711 EventSourceLock();
1712 locked = true;
1713
1714 for( inserted = (Win32EventSource*) gEventSources.Head; inserted; inserted = inserted->next )
1715 {
1716 if( inserted == source )
1717 {
1718 break;
1719 }
1720 }
1721 require_action( inserted, exit, err = kNotFoundErr );
1722
1723 //
1724 // note that we've had finalize called
1725 //
1726 source->flags |= EventSourceFinalized;
1727
1728 // If we're being called from the same thread as the session (e.g. message callback is closing the session) then
1729 // we must defer the close until the thread is done because the thread is still using the session object.
1730
1731 deferClose = false;
1732 threadID = GetCurrentThreadId();
1733 sameThread = source->threadHandle && ( threadID == source->threadID );
1734 if( sameThread && !( source->flags & EventSourceFlagsThreadDone ) )
1735 {
1736 source->flags &= ~EventSourceFlagsNoClose;
1737 deferClose = true;
1738 }
1739
1740 // If the thread we're not being called from the session thread, but the thread has already marked itself as
1741 // as done (e.g. session closed from something like a peer disconnect and at the same time the client also
1742 // tried to close) then we only want to continue with the close if the thread is not going to close itself.
1743
1744 if( !sameThread && ( source->flags & EventSourceFlagsThreadDone ) && !( source->flags & EventSourceFlagsNoClose ) )
1745 {
1746 deferClose = true;
1747 }
1748
1749 // Signal a close so the thread exits.
1750
1751 if( source->closeEvent )
1752 {
1753 ok = SetEvent( source->closeEvent );
1754 check_translated_errno( ok, errno_compat(), kUnknownErr );
1755 }
1756 if( deferClose )
1757 {
1758 err = kNoErr;
1759 goto exit;
1760 }
1761
1762 source->flags |= EventSourceFlagsNoClose;
1763
1764 // Remove the session from the list.
1765 RemoveFromList(&gEventSources, source);
1766
1767 EventSourceUnlock();
1768 locked = false;
1769
1770 // Wait for the thread to exit. Give up after 3 seconds to handle a hung thread.
1771
1772 if( source->threadHandle && ( threadID != source->threadID ) )
1773 {
1774 result = WaitForSingleObject( source->threadHandle, 3 * 1000 );
1775 check_translated_errno( result == WAIT_OBJECT_0, (OSStatus) GetLastError(), result );
1776 }
1777
1778 // Release the thread.
1779
1780 if( source->threadHandle )
1781 {
1782 ok = CloseHandle( source->threadHandle );
1783 check_translated_errno( ok, errno_compat(), kUnknownErr );
1784 source->threadHandle = NULL;
1785 }
1786
1787 // Release the socket event.
1788
1789 if( source->socketEvent )
1790 {
1791 ok = CloseHandle( source->socketEvent );
1792 check_translated_errno( ok, errno_compat(), kUnknownErr );
1793 source->socketEvent = NULL;
1794 }
1795
1796 // Release the close event.
1797
1798 if( source->closeEvent )
1799 {
1800 ok = CloseHandle( source->closeEvent );
1801 check_translated_errno( ok, errno_compat(), kUnknownErr );
1802 source->closeEvent = NULL;
1803 }
1804
1805 // Release the memory used by the object.
1806 free ( source );
1807
1808 err = kNoErr;
1809
1810 dlog( kDebugLevelNotice, DEBUG_NAME "session closed\n" );
1811
1812 exit:
1813
1814 if( locked )
1815 {
1816 EventSourceUnlock();
1817 }
1818
1819 return( err );
1820 }
1821
1822
1823 static void
1824 EventSourceLock()
1825 {
1826 EnterCriticalSection(&gEventSourceLock);
1827 }
1828
1829
1830 static void
1831 EventSourceUnlock()
1832 {
1833 LeaveCriticalSection(&gEventSourceLock);
1834 }
1835
1836
1837 //===========================================================================================================================
1838 // HaveRoute
1839 //===========================================================================================================================
1840
1841 static bool
1842 HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric )
1843 {
1844 PMIB_IPFORWARDTABLE pIpForwardTable = NULL;
1845 DWORD dwSize = 0;
1846 BOOL bOrder = FALSE;
1847 OSStatus err;
1848 bool found = false;
1849 unsigned long int i;
1850
1851 //
1852 // Find out how big our buffer needs to be.
1853 //
1854 err = GetIpForwardTable(NULL, &dwSize, bOrder);
1855 require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
1856
1857 //
1858 // Allocate the memory for the table
1859 //
1860 pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
1861 require_action( pIpForwardTable, exit, err = kNoMemoryErr );
1862
1863 //
1864 // Now get the table.
1865 //
1866 err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
1867 require_noerr( err, exit );
1868
1869 //
1870 // Search for the row in the table we want.
1871 //
1872 for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
1873 {
1874 if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) )
1875 {
1876 memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) );
1877 found = true;
1878 break;
1879 }
1880 }
1881
1882 exit:
1883
1884 if ( pIpForwardTable != NULL )
1885 {
1886 free(pIpForwardTable);
1887 }
1888
1889 return found;
1890 }
1891
1892
1893 //===========================================================================================================================
1894 // IsValidAddress
1895 //===========================================================================================================================
1896
1897 static bool
1898 IsValidAddress( const char * addr )
1899 {
1900 return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false;
1901 }
1902
1903
1904 //===========================================================================================================================
1905 // GetAdditionalMetric
1906 //===========================================================================================================================
1907
1908 static ULONG
1909 GetAdditionalMetric( DWORD ifIndex )
1910 {
1911 ULONG metric = 0;
1912
1913 if( !gIPHelperLibraryInstance )
1914 {
1915 gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
1916
1917 gGetIpInterfaceEntryFunctionPtr =
1918 (GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" );
1919
1920 if( !gGetIpInterfaceEntryFunctionPtr )
1921 {
1922 BOOL ok;
1923
1924 ok = FreeLibrary( gIPHelperLibraryInstance );
1925 check_translated_errno( ok, GetLastError(), kUnknownErr );
1926 gIPHelperLibraryInstance = NULL;
1927 }
1928 }
1929
1930 if ( gGetIpInterfaceEntryFunctionPtr )
1931 {
1932 MIB_IPINTERFACE_ROW row;
1933 DWORD err;
1934
1935 ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) );
1936 row.Family = AF_INET;
1937 row.InterfaceIndex = ifIndex;
1938 err = gGetIpInterfaceEntryFunctionPtr( &row );
1939 require_noerr( err, exit );
1940 metric = row.Metric + 256;
1941 }
1942
1943 exit:
1944
1945 return metric;
1946 }
1947
1948
1949 //===========================================================================================================================
1950 // SetLLRoute
1951 //===========================================================================================================================
1952
1953 static OSStatus
1954 SetLLRoute( mDNS * const inMDNS )
1955 {
1956 OSStatus err = kNoErr;
1957
1958 DEBUG_UNUSED( inMDNS );
1959
1960 //
1961 // <rdar://problem/4096464> Don't call SetLLRoute on loopback
1962 // <rdar://problem/6885843> Default route on Windows 7 breaks network connectivity
1963 //
1964 // Don't mess w/ the routing table on Vista and later OSes, as
1965 // they have a permanent route to link-local addresses. Otherwise,
1966 // set a route to link local addresses (169.254.0.0)
1967 //
1968 if ( ( gOSMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 )
1969 {
1970 DWORD ifIndex;
1971 MIB_IPFORWARDROW rowExtant;
1972 bool addRoute;
1973 MIB_IPFORWARDROW row;
1974
1975 ZeroMemory(&row, sizeof(row));
1976
1977 err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop);
1978 require_noerr( err, exit );
1979 row.dwForwardDest = inet_addr(kLLNetworkAddr);
1980 row.dwForwardIfIndex = ifIndex;
1981 row.dwForwardMask = inet_addr(kLLNetworkAddrMask);
1982 row.dwForwardType = 3;
1983 row.dwForwardProto = MIB_IPPROTO_NETMGMT;
1984 row.dwForwardAge = 0;
1985 row.dwForwardPolicy = 0;
1986 row.dwForwardMetric1 = 20 + GetAdditionalMetric( ifIndex );
1987 row.dwForwardMetric2 = (DWORD) - 1;
1988 row.dwForwardMetric3 = (DWORD) - 1;
1989 row.dwForwardMetric4 = (DWORD) - 1;
1990 row.dwForwardMetric5 = (DWORD) - 1;
1991
1992 addRoute = true;
1993
1994 //
1995 // check to make sure we don't already have a route
1996 //
1997 if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) )
1998 {
1999 //
2000 // set the age to 0 so that we can do a memcmp.
2001 //
2002 rowExtant.dwForwardAge = 0;
2003
2004 //
2005 // check to see if this route is the same as our route
2006 //
2007 if (memcmp(&row, &rowExtant, sizeof(row)) != 0)
2008 {
2009 //
2010 // if it isn't then delete this entry
2011 //
2012 DeleteIpForwardEntry(&rowExtant);
2013 }
2014 else
2015 {
2016 //
2017 // else it is, so we don't want to create another route
2018 //
2019 addRoute = false;
2020 }
2021 }
2022
2023 if (addRoute && row.dwForwardNextHop)
2024 {
2025 err = CreateIpForwardEntry(&row);
2026 check_noerr( err );
2027 }
2028 }
2029
2030 exit:
2031
2032 return ( err );
2033 }
2034
2035
2036 //===========================================================================================================================
2037 // GetRouteDestination
2038 //===========================================================================================================================
2039
2040 static OSStatus
2041 GetRouteDestination(DWORD * ifIndex, DWORD * address)
2042 {
2043 struct in_addr ia;
2044 IP_ADAPTER_INFO * pAdapterInfo = NULL;
2045 IP_ADAPTER_INFO * pAdapter = NULL;
2046 ULONG bufLen;
2047 mDNSBool done = mDNSfalse;
2048 OSStatus err;
2049
2050 //
2051 // GetBestInterface will fail if there is no default gateway
2052 // configured. If that happens, we will just take the first
2053 // interface in the list. MSDN support says there is no surefire
2054 // way to manually determine what the best interface might
2055 // be for a particular network address.
2056 //
2057 ia.s_addr = inet_addr(kLLNetworkAddr);
2058 err = GetBestInterface(*(IPAddr*) &ia, ifIndex);
2059
2060 if (err)
2061 {
2062 *ifIndex = 0;
2063 }
2064
2065 //
2066 // Make an initial call to GetAdaptersInfo to get
2067 // the necessary size into the bufLen variable
2068 //
2069 err = GetAdaptersInfo( NULL, &bufLen);
2070 require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr );
2071
2072 pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen );
2073 require_action( pAdapterInfo, exit, err = kNoMemoryErr );
2074
2075 err = GetAdaptersInfo( pAdapterInfo, &bufLen);
2076 require_noerr( err, exit );
2077
2078 pAdapter = pAdapterInfo;
2079 err = kUnknownErr;
2080
2081 // <rdar://problem/3718122>
2082 // <rdar://problem/5652098>
2083 //
2084 // Look for the Nortel VPN virtual interface, along with Juniper virtual interface.
2085 //
2086 // If these interfaces are active (i.e., has a non-zero IP Address),
2087 // then we want to disable routing table modifications.
2088
2089 while (pAdapter)
2090 {
2091 if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) &&
2092 ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) )
2093 {
2094 dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" );
2095 goto exit;
2096 }
2097
2098 pAdapter = pAdapter->Next;
2099 }
2100
2101 while ( !done )
2102 {
2103 pAdapter = pAdapterInfo;
2104 err = kUnknownErr;
2105
2106 while (pAdapter)
2107 {
2108 // If we don't have an interface selected, choose the first one that is of type ethernet and
2109 // has a valid IP Address
2110
2111 if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex))))
2112 {
2113 *address = inet_addr( pAdapter->IpAddressList.IpAddress.String );
2114 *ifIndex = pAdapter->Index;
2115 err = kNoErr;
2116 break;
2117 }
2118
2119 pAdapter = pAdapter->Next;
2120 }
2121
2122 // If we found the right interface, or we weren't trying to find a specific interface then we're done
2123
2124 if ( !err || !( *ifIndex) )
2125 {
2126 done = mDNStrue;
2127 }
2128
2129 // Otherwise, try again by wildcarding the interface
2130
2131 else
2132 {
2133 *ifIndex = 0;
2134 }
2135 }
2136
2137 exit:
2138
2139 if ( pAdapterInfo != NULL )
2140 {
2141 free( pAdapterInfo );
2142 }
2143
2144 return( err );
2145 }
2146
2147
2148 static bool
2149 IsNortelVPN( IP_ADAPTER_INFO * pAdapter )
2150 {
2151 return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
2152 (pAdapter->AddressLength == 6) &&
2153 (pAdapter->Address[0] == 0x44) &&
2154 (pAdapter->Address[1] == 0x45) &&
2155 (pAdapter->Address[2] == 0x53) &&
2156 (pAdapter->Address[3] == 0x54) &&
2157 (pAdapter->Address[4] == 0x42) &&
2158 (pAdapter->Address[5] == 0x00)) ? true : false;
2159 }
2160
2161
2162 static bool
2163 IsJuniperVPN( IP_ADAPTER_INFO * pAdapter )
2164 {
2165 return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description ) ) != NULL ) ? true : false;
2166 }
2167
2168
2169 static bool
2170 IsCiscoVPN( IP_ADAPTER_INFO * pAdapter )
2171 {
2172 return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
2173 (pAdapter->AddressLength == 6) &&
2174 (pAdapter->Address[0] == 0x00) &&
2175 (pAdapter->Address[1] == 0x05) &&
2176 (pAdapter->Address[2] == 0x9a) &&
2177 (pAdapter->Address[3] == 0x3c) &&
2178 (pAdapter->Address[4] == 0x7a) &&
2179 (pAdapter->Address[5] == 0x00)) ? true : false;
2180 }
2181
2182
2183 static const char *
2184 strnistr( const char * string, const char * subString, size_t max )
2185 {
2186 size_t subStringLen;
2187 size_t offset;
2188 size_t maxOffset;
2189 size_t stringLen;
2190 const char * pPos;
2191
2192 if ( ( string == NULL ) || ( subString == NULL ) )
2193 {
2194 return string;
2195 }
2196
2197 stringLen = ( max > strlen( string ) ) ? strlen( string ) : max;
2198
2199 if ( stringLen == 0 )
2200 {
2201 return NULL;
2202 }
2203
2204 subStringLen = strlen( subString );
2205
2206 if ( subStringLen == 0 )
2207 {
2208 return string;
2209 }
2210
2211 if ( subStringLen > stringLen )
2212 {
2213 return NULL;
2214 }
2215
2216 maxOffset = stringLen - subStringLen;
2217 pPos = string;
2218
2219 for ( offset = 0; offset <= maxOffset; offset++ )
2220 {
2221 if ( _strnicmp( pPos, subString, subStringLen ) == 0 )
2222 {
2223 return pPos;
2224 }
2225
2226 pPos++;
2227 }
2228
2229 return NULL;
2230 }
2231