1/* -*- Mode: C; tab-width: 4 -*-
2 *
3 * Copyright (c) 2002-2005 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
18#if 0
19#pragma mark == Configuration ==
20#endif
21
22//===========================================================================================================================
23//	Configuration
24//===========================================================================================================================
25
26#define DEBUG_NAME                          "[mDNS] "
27#define MDNS_AAAA_OVER_IPV4                 1   // 1=Send AAAA & A records over IPv4 & IPv6, 0=Send AAAA over IPv6, A over IPv4.
28#define MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6     1   // 1=Don't use IPv6 socket if non-link-local IPv4 available on same interface.
29#define MDNS_ENABLE_PPP                     0   // 1=Enable Unicast DNS over PPP interfaces. 0=Don't enable it.
30#define MDNS_DEBUG_PACKETS                  1   // 1=Enable debug output for packet send/recv if debug level high enough.
31#define MDNS_DEBUG_SHOW                     1   // 1=Enable console show routines.
32#define DEBUG_USE_DEFAULT_CATEGORY          1   // Set up to use the default category (see DebugServices.h for details).
33
34#include    <stdarg.h>
35#include    <stddef.h>
36#include    <stdio.h>
37#include    <stdlib.h>
38#include    <string.h>
39#include    <time.h>
40
41#include    "vxWorks.h"
42#include    "config.h"
43
44#include    <sys/types.h>
45
46#include    <arpa/inet.h>
47#include    <net/if.h>
48#include    <net/if_dl.h>
49#include    <net/if_types.h>
50#include    <net/ifaddrs.h>
51#include    <netinet6/in6_var.h>
52#include    <netinet/if_ether.h>
53#include    <netinet/in.h>
54#include    <netinet/ip.h>
55#include    <sys/ioctl.h>
56#include    <sys/socket.h>
57#include    <unistd.h>
58
59#include    "ifLib.h"
60#include    "inetLib.h"
61#include    "pipeDrv.h"
62#include    "selectLib.h"
63#include    "semLib.h"
64#include    "sockLib.h"
65#include    "sysLib.h"
66#include    "taskLib.h"
67#include    "tickLib.h"
68
69#include    "CommonServices.h"
70#include    "DebugServices.h"
71#include    "DNSCommon.h"
72#include    "mDNSEmbeddedAPI.h"
73
74#include    "mDNSVxWorks.h"
75
76#if 0
77#pragma mark == Constants ==
78#endif
79
80//===========================================================================================================================
81//	Constants
82//===========================================================================================================================
83
84typedef uint8_t MDNSPipeCommandCode;
85
86#define kMDNSPipeCommandCodeInvalid         0
87#define kMDNSPipeCommandCodeReschedule      1
88#define kMDNSPipeCommandCodeReconfigure     2
89#define kMDNSPipeCommandCodeQuit            3
90
91#if 0
92#pragma mark == Prototypes ==
93#endif
94
95//===========================================================================================================================
96//	Prototypes
97//===========================================================================================================================
98
99#if ( DEBUG )
100mDNSlocal void  DebugMsg( DebugLevel inLevel, const char *inFormat, ... );
101
102    #define dmsg( LEVEL, ARGS... )  DebugMsg( LEVEL, ## ARGS )
103#else
104    #define dmsg( LEVEL, ARGS... )
105#endif
106
107#if ( DEBUG && MDNS_DEBUG_PACKETS )
108    #define dpkt( LEVEL, ARGS... )  DebugMsg( LEVEL, ## ARGS )
109#else
110    #define dpkt( LEVEL, ARGS... )
111#endif
112
113#define ForgetSem( X )      do { if( *( X ) ) { semDelete( ( *X ) ); *( X ) = 0; } } while( 0 )
114#define ForgetSocket( X )   do { if( IsValidSocket( *( X ) ) ) { close_compat( *( X ) ); *( X ) = kInvalidSocketRef; } } while( 0 )
115
116// Interfaces
117
118mDNSlocal mStatus   UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC );
119mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC );
120mDNSlocal int   SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC );
121mDNSlocal void  MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC );
122mDNSlocal int   ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing );
123mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID );
124mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex );
125mDNSlocal mStatus   SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS );
126mDNSlocal mStatus   SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP );
127
128// Commands
129
130mDNSlocal mStatus   SetupCommandPipe( mDNS * const inMDNS );
131mDNSlocal mStatus   TearDownCommandPipe( mDNS * const inMDNS );
132mDNSlocal mStatus   SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode );
133mDNSlocal mStatus   ProcessCommand( mDNS * const inMDNS );
134
135// Threads
136
137mDNSlocal void      Task( mDNS *inMDNS );
138mDNSlocal mStatus   TaskInit( mDNS *inMDNS );
139mDNSlocal void      TaskTerm( mDNS *inMDNS );
140mDNSlocal void      TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout );
141mDNSlocal void      TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock );
142mDNSlocal ssize_t
143mDNSRecvMsg(
144    SocketRef inSock,
145    void *      inBuffer,
146    size_t inBufferSize,
147    void *      outFrom,
148    size_t inFromSize,
149    size_t *    outFromSize,
150    mDNSAddr *  outDstAddr,
151    uint32_t *  outIndex );
152
153// DNSServices compatibility. When all clients move to DNS-SD, this section can be removed.
154
155#ifdef  __cplusplus
156extern "C" {
157#endif
158
159typedef struct mDNSPlatformInterfaceInfo mDNSPlatformInterfaceInfo;
160struct  mDNSPlatformInterfaceInfo
161{
162    const char *        name;
163    mDNSAddr ip;
164};
165
166mDNSexport mStatus  mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
167mDNSexport mStatus  mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
168
169#ifdef  __cplusplus
170}
171#endif
172
173#if 0
174#pragma mark == Globals ==
175#endif
176
177//===========================================================================================================================
178//	Globals
179//===========================================================================================================================
180
181debug_log_new_default_category( mdns );
182
183mDNSexport mDNSs32 mDNSPlatformOneSecond;
184mDNSlocal mDNSs32 gMDNSTicksToMicro       = 0;
185mDNSlocal mDNS *                    gMDNSPtr                = NULL;
186mDNSlocal mDNS_PlatformSupport gMDNSPlatformSupport;
187mDNSlocal mDNSBool gMDNSDeferIPv4          = mDNSfalse;
188#if ( DEBUG )
189DebugLevel gMDNSDebugOverrideLevel = kDebugLevelMax;
190#endif
191
192#if 0
193#pragma mark -
194#endif
195
196//===========================================================================================================================
197//	mDNSReconfigure
198//===========================================================================================================================
199
200void    mDNSReconfigure( void )
201{
202    if( gMDNSPtr ) SendCommand( gMDNSPtr, kMDNSPipeCommandCodeReconfigure );
203}
204
205//===========================================================================================================================
206//	mDNSDeferIPv4
207//===========================================================================================================================
208
209void    mDNSDeferIPv4( mDNSBool inDefer )
210{
211    gMDNSDeferIPv4 = inDefer;
212}
213
214#if 0
215#pragma mark -
216#endif
217
218//===========================================================================================================================
219//	mDNSPlatformInit
220//===========================================================================================================================
221
222mStatus mDNSPlatformInit( mDNS * const inMDNS )
223{
224    mStatus err;
225    int id;
226
227    mDNSPlatformOneSecond   = sysClkRateGet();
228    gMDNSTicksToMicro       = ( 1000000L / mDNSPlatformOneSecond );
229
230    // Do minimal initialization to get the task started and so we can cleanup safely if an error occurs.
231
232    mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) );
233    if( !inMDNS->p ) inMDNS->p  = &gMDNSPlatformSupport;
234    inMDNS->p->unicastSS.info   = NULL;
235    inMDNS->p->unicastSS.sockV4 = kInvalidSocketRef;
236    inMDNS->p->unicastSS.sockV6 = kInvalidSocketRef;
237    inMDNS->p->initErr          = mStatus_NotInitializedErr;
238    inMDNS->p->commandPipe      = ERROR;
239    inMDNS->p->taskID           = ERROR;
240
241    inMDNS->p->lock = semMCreate( SEM_Q_FIFO );
242    require_action( inMDNS->p->lock, exit, err = mStatus_NoMemoryErr );
243
244    inMDNS->p->initEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY );
245    require_action( inMDNS->p->initEvent, exit, err = mStatus_NoMemoryErr );
246
247    inMDNS->p->quitEvent = semBCreate( SEM_Q_FIFO, SEM_EMPTY );
248    require_action( inMDNS->p->quitEvent, exit, err = mStatus_NoMemoryErr );
249
250    // Start the task and wait for it to initialize. The task does the full initialization from its own context
251    // to avoid potential issues with stack space and APIs that key off the current task (e.g. watchdog timers).
252    // We wait here until the init is complete because it needs to be ready to use as soon as this function returns.
253
254    id = taskSpawn( "tMDNS", 102, 0, 16384, (FUNCPTR) Task, (int) inMDNS, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
255    err = translate_errno( id != ERROR, errno_compat(), mStatus_NoMemoryErr );
256    require_noerr( err, exit );
257
258    err = semTake( inMDNS->p->initEvent, WAIT_FOREVER );
259    if( err == OK ) err = inMDNS->p->initErr;
260    require_noerr( err, exit );
261
262    gMDNSPtr = inMDNS;
263    mDNSCoreInitComplete( inMDNS, err );
264
265exit:
266    if( err ) mDNSPlatformClose( inMDNS );
267    return( err );
268}
269
270//===========================================================================================================================
271//	mDNSPlatformClose
272//===========================================================================================================================
273
274void    mDNSPlatformClose( mDNS * const inMDNS )
275{
276    mStatus err;
277
278    check( inMDNS );
279
280    gMDNSPtr = NULL;
281
282    // Signal the task to quit and wait for it to signal back that it exited. Timeout in 10 seconds to handle a hung thread.
283
284    if( inMDNS->p->taskID != ERROR )
285    {
286        SendCommand( inMDNS, kMDNSPipeCommandCodeQuit );
287        if( inMDNS->p->quitEvent )
288        {
289            err = semTake( inMDNS->p->quitEvent, sysClkRateGet() * 10 );
290            check_noerr( err );
291        }
292        inMDNS->p->taskID = ERROR;
293    }
294
295    // Clean up resources set up in mDNSPlatformInit. All other resources should have been cleaned up already by TaskTerm.
296
297    ForgetSem( &inMDNS->p->quitEvent );
298    ForgetSem( &inMDNS->p->initEvent );
299    ForgetSem( &inMDNS->p->lock );
300
301    dmsg( kDebugLevelNotice, DEBUG_NAME "CLOSED\n" );
302}
303
304//===========================================================================================================================
305//	mDNSPlatformSendUDP
306//===========================================================================================================================
307
308mStatus
309mDNSPlatformSendUDP(
310    const mDNS * const inMDNS,
311    const void * const inMsg,
312    const mDNSu8 * const inEnd,
313    mDNSInterfaceID inInterfaceID,
314    const mDNSAddr *            inDstIP,
315    mDNSIPPort inDstPort )
316{
317    mStatus err;
318    NetworkInterfaceInfoVxWorks *       info;
319    SocketRef sock;
320    struct sockaddr_storage to;
321    int n;
322
323    // Set up the sockaddr to sent to and the socket to send on.
324
325    info = (NetworkInterfaceInfoVxWorks *) inInterfaceID;
326    if( inDstIP->type == mDNSAddrType_IPv4 )
327    {
328        struct sockaddr_in *        sa4;
329
330        sa4 = (struct sockaddr_in *) &to;
331        sa4->sin_len            = sizeof( *sa4 );
332        sa4->sin_family         = AF_INET;
333        sa4->sin_port           = inDstPort.NotAnInteger;
334        sa4->sin_addr.s_addr    = inDstIP->ip.v4.NotAnInteger;
335        sock = info ? info->ss.sockV4 : inMDNS->p->unicastSS.sockV4;
336    }
337    else if( inDstIP->type == mDNSAddrType_IPv6 )
338    {
339        struct sockaddr_in6 *       sa6;
340
341        sa6 = (struct sockaddr_in6 *) &to;
342        sa6->sin6_len       = sizeof( *sa6 );
343        sa6->sin6_family    = AF_INET6;
344        sa6->sin6_port      = inDstPort.NotAnInteger;
345        sa6->sin6_flowinfo  = 0;
346        sa6->sin6_addr      = *( (struct in6_addr *) &inDstIP->ip.v6 );
347        sa6->sin6_scope_id  = info ? info->scopeID : 0;
348        sock = info ? info->ss.sockV6 : inMDNS->p->unicastSS.sockV6;
349    }
350    else
351    {
352        dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! destination is not an IPv4 or IPv6 address\n", __ROUTINE__ );
353        err = mStatus_BadParamErr;
354        goto exit;
355    }
356
357    // Send the packet if we've got a valid socket of this type. Note: mDNSCore may ask us to send an IPv4 packet and then
358    // an IPv6 multicast packet. If we don't have the corresponding type of socket available, quietly return an error.
359
360    n = (int)( (mDNSu8 *) inEnd - (mDNSu8 *) inMsg );
361    if( !IsValidSocket( sock ) )
362    {
363        dpkt( kDebugLevelChatty - 1,
364              DEBUG_NAME "DROP: %4d bytes,                                                     DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n",
365              n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info );
366        err = mStatus_Invalid;
367        goto exit;
368    }
369
370    dpkt( kDebugLevelChatty,
371          DEBUG_NAME "SEND %4d bytes,                                                      DST=[%#39a]:%5hu, IF=%8s(%u) %#p\n",
372          n, inDstIP, mDNSVal16( inDstPort ), info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, info );
373
374    n = sendto( sock, (mDNSu8 *) inMsg, n, 0, (struct sockaddr *) &to, to.ss_len );
375    if( n < 0 )
376    {
377        // Don't warn about ARP failures or no route to host for unicast destinations.
378
379        err = errno_compat();
380        if( ( ( err == EHOSTDOWN ) || ( err == ENETDOWN ) || ( err == EHOSTUNREACH ) ) && !mDNSAddressIsAllDNSLinkGroup( inDstIP ) )
381        {
382            goto exit;
383        }
384
385        dmsg( kDebugLevelError, "%s: ERROR! sendto failed on %8s(%u) to %#a:%d, sock %d, err %d, time %u\n",
386              __ROUTINE__, info ? info->ifinfo.ifname : "unicast", info ? info->scopeID : 0, inDstIP, mDNSVal16( inDstPort ),
387              sock, err, (unsigned int) inMDNS->timenow );
388        if( err == 0 ) err = mStatus_UnknownErr;
389        goto exit;
390    }
391    err = mStatus_NoError;
392
393exit:
394    return( err );
395}
396
397//===========================================================================================================================
398//	Connection-oriented (TCP) functions
399//===========================================================================================================================
400
401mDNSexport mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
402                                          TCPConnectionCallback callback, void *context, int *descriptor)
403{
404    (void)dst;          // Unused
405    (void)dstport;      // Unused
406    (void)InterfaceID;  // Unused
407    (void)callback;     // Unused
408    (void)context;      // Unused
409    (void)descriptor;   // Unused
410    return(mStatus_UnsupportedErr);
411}
412
413mDNSexport void mDNSPlatformTCPCloseConnection(int sd)
414{
415    (void)sd;           // Unused
416}
417
418mDNSexport long mDNSPlatformReadTCP(int sd, void *buf, unsigned long buflen)
419{
420    (void)sd;           // Unused
421    (void)buf;          // Unused
422    (void)buflen;       // Unused
423    return(0);
424}
425
426mDNSexport long mDNSPlatformWriteTCP(int sd, const char *msg, unsigned long len)
427{
428    (void)sd;           // Unused
429    (void)msg;          // Unused
430    (void)len;          // Unused
431    return(0);
432}
433
434//===========================================================================================================================
435//	mDNSPlatformLock
436//===========================================================================================================================
437
438void    mDNSPlatformLock( const mDNS * const inMDNS )
439{
440    check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" );
441
442#if ( DEBUG )
443    if( semTake( inMDNS->p->lock, 60 * sysClkRateGet() ) != OK )
444    {
445        dmsg( kDebugLevelTragic, "\n### DEADLOCK DETECTED ### (sem=%#p, task=%#p)\n\n", inMDNS->p->lock, taskIdSelf() );
446        debug_stack_trace();            // 1) Print Stack Trace.
447        semShow( inMDNS->p->lock, 1 );  // 2) Print semaphore info, including which tasks are pending on it.
448        taskSuspend( 0 );               // 3) Suspend task. Can be resumed from the console for debugging.
449    }
450#else
451    semTake( inMDNS->p->lock, WAIT_FOREVER );
452#endif
453}
454
455//===========================================================================================================================
456//	mDNSPlatformUnlock
457//===========================================================================================================================
458
459void    mDNSPlatformUnlock( const mDNS * const inMDNS )
460{
461    check_string( inMDNS->p && ( inMDNS->p->taskID != ERROR ), "mDNS task not started" );
462
463    // Wake up the mDNS task to handle any work initiated by an API call and to calculate the next event time.
464    // We only need to wake up if we're not already inside the task. This avoids filling up the command queue.
465
466    if( taskIdSelf() != inMDNS->p->taskID )
467    {
468        SendCommand( inMDNS, kMDNSPipeCommandCodeReschedule );
469    }
470    semGive( inMDNS->p->lock );
471}
472
473//===========================================================================================================================
474//	mDNSPlatformStrLen
475//===========================================================================================================================
476
477mDNSu32 mDNSPlatformStrLen( const void *inSrc )
478{
479    check( inSrc );
480
481    return( (mDNSu32) strlen( (const char *) inSrc ) );
482}
483
484//===========================================================================================================================
485//	mDNSPlatformStrCopy
486//===========================================================================================================================
487
488void    mDNSPlatformStrCopy( void *inDst, const void *inSrc )
489{
490    check( inSrc );
491    check( inDst );
492
493    strcpy( (char *) inDst, (const char*) inSrc );
494}
495
496//===========================================================================================================================
497//	mDNSPlatformMemCopy
498//===========================================================================================================================
499
500void    mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize )
501{
502    check( inSrc );
503    check( inDst );
504
505    memcpy( inDst, inSrc, inSize );
506}
507
508//===========================================================================================================================
509//	mDNSPlatformMemSame
510//===========================================================================================================================
511
512mDNSBool    mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize )
513{
514    check( inSrc );
515    check( inDst );
516
517    return( memcmp( inSrc, inDst, inSize ) == 0 );
518}
519
520//===========================================================================================================================
521//	mDNSPlatformMemZero
522//===========================================================================================================================
523
524void    mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
525{
526    check( inDst );
527
528    memset( inDst, 0, inSize );
529}
530
531//===========================================================================================================================
532//	mDNSPlatformMemAllocate
533//===========================================================================================================================
534
535mDNSexport void *   mDNSPlatformMemAllocate( mDNSu32 inSize )
536{
537    void *      mem;
538
539    check( inSize > 0 );
540
541    mem = malloc( inSize );
542    check( mem );
543
544    return( mem );
545}
546
547//===========================================================================================================================
548//	mDNSPlatformMemFree
549//===========================================================================================================================
550
551mDNSexport void mDNSPlatformMemFree( void *inMem )
552{
553    check( inMem );
554    if( inMem ) free( inMem );
555}
556
557//===========================================================================================================================
558//	mDNSPlatformRandomSeed
559//===========================================================================================================================
560
561mDNSexport mDNSu32  mDNSPlatformRandomSeed( void )
562{
563    return( tickGet() );
564}
565
566//===========================================================================================================================
567//	mDNSPlatformTimeInit
568//===========================================================================================================================
569
570mDNSexport mStatus  mDNSPlatformTimeInit( void )
571{
572    // No special setup is required on VxWorks -- we just use tickGet().
573
574    return( mStatus_NoError );
575}
576
577//===========================================================================================================================
578//	mDNSPlatformRawTime
579//===========================================================================================================================
580
581mDNSs32 mDNSPlatformRawTime( void )
582{
583    return( (mDNSs32) tickGet() );
584}
585
586//===========================================================================================================================
587//	mDNSPlatformUTC
588//===========================================================================================================================
589
590mDNSexport mDNSs32  mDNSPlatformUTC( void )
591{
592    return( (mDNSs32) time( NULL ) );
593}
594
595//===========================================================================================================================
596//	mDNSPlatformInterfaceIDfromInterfaceIndex
597//===========================================================================================================================
598
599mDNSexport mDNSInterfaceID  mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS *const inMDNS, mDNSu32 inIndex )
600{
601    NetworkInterfaceInfoVxWorks *       i;
602
603    if( inIndex == (mDNSu32) -1 ) return( mDNSInterface_LocalOnly );
604    if( inIndex != 0 )
605    {
606        for( i = inMDNS->p->interfaceList; i; i = i->next )
607        {
608            // Don't get tricked by inactive interfaces with no InterfaceID set.
609
610            if( i->ifinfo.InterfaceID && ( i->scopeID == inIndex ) ) return( i->ifinfo.InterfaceID );
611        }
612    }
613    return( NULL );
614}
615
616//===========================================================================================================================
617//	mDNSPlatformInterfaceIndexfromInterfaceID
618//===========================================================================================================================
619
620mDNSexport mDNSu32  mDNSPlatformInterfaceIndexfromInterfaceID( mDNS *const inMDNS, mDNSInterfaceID inID )
621{
622    NetworkInterfaceInfoVxWorks *       i;
623
624    if( inID == mDNSInterface_LocalOnly ) return( (mDNSu32) -1 );
625    if( inID )
626    {
627        // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces.
628
629        for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {}
630        if( i ) return( i->scopeID );
631    }
632    return( 0 );
633}
634
635//===========================================================================================================================
636//	mDNSPlatformInterfaceNameToID
637//===========================================================================================================================
638
639mStatus mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
640{
641    NetworkInterfaceInfoVxWorks *       i;
642
643    for( i = inMDNS->p->interfaceList; i; i = i->next )
644    {
645        // Don't get tricked by inactive interfaces with no InterfaceID set.
646
647        if( i->ifinfo.InterfaceID && ( strcmp( i->ifinfo.ifname, inName ) == 0 ) )
648        {
649            *outID = (mDNSInterfaceID) i;
650            return( mStatus_NoError );
651        }
652    }
653    return( mStatus_NoSuchNameErr );
654}
655
656//===========================================================================================================================
657//	mDNSPlatformInterfaceIDToInfo
658//===========================================================================================================================
659
660mStatus mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
661{
662    NetworkInterfaceInfoVxWorks *       i;
663
664    // Don't use i->ifinfo.InterfaceID here, because we DO want to find inactive interfaces.
665
666    for( i = inMDNS->p->interfaceList; i && ( (mDNSInterfaceID) i != inID ); i = i->next ) {}
667    if( !i ) return( mStatus_NoSuchNameErr );
668
669    outInfo->name   = i->ifinfo.ifname;
670    outInfo->ip     = i->ifinfo.ip;
671    return( mStatus_NoError );
672}
673
674//===========================================================================================================================
675//	debugf_
676//===========================================================================================================================
677
678#if ( MDNS_DEBUGMSGS > 0 )
679mDNSexport void debugf_( const char *inFormat, ... )
680{
681    char buffer[ 512 ];
682    va_list args;
683
684    va_start( args, inFormat );
685    mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
686    va_end( args );
687
688    dlog( kDebugLevelInfo, "%s\n", buffer );
689}
690#endif
691
692//===========================================================================================================================
693//	verbosedebugf_
694//===========================================================================================================================
695
696#if ( MDNS_DEBUGMSGS > 1 )
697mDNSexport void verbosedebugf_( const char *inFormat, ... )
698{
699    char buffer[ 512 ];
700    va_list args;
701
702    va_start( args, inFormat );
703    mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
704    va_end( args );
705
706    dlog( kDebugLevelVerbose, "%s\n", buffer );
707}
708#endif
709
710//===========================================================================================================================
711//	LogMsg
712//===========================================================================================================================
713
714mDNSexport void LogMsg( const char *inFormat, ... )
715{
716#if ( DEBUG )
717    char buffer[ 512 ];
718    va_list args;
719
720    va_start( args, inFormat );
721    mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
722    va_end( args );
723
724    dlog( kDebugLevelWarning, "%s\n", buffer );
725#else
726    DEBUG_UNUSED( inFormat );
727#endif
728}
729
730#if ( DEBUG )
731//===========================================================================================================================
732//	DebugMsg
733//===========================================================================================================================
734
735mDNSlocal void  DebugMsg( DebugLevel inLevel, const char *inFormat, ... )
736{
737    char buffer[ 512 ];
738    va_list args;
739
740    va_start( args, inFormat );
741    mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
742    va_end( args );
743
744    if( inLevel >= gMDNSDebugOverrideLevel ) inLevel = kDebugLevelMax;
745    dlog( inLevel, "%s", buffer );
746}
747#endif
748
749#if 0
750#pragma mark -
751#pragma mark == Interfaces ==
752#endif
753
754//===========================================================================================================================
755//	UpdateInterfaceList
756//===========================================================================================================================
757
758#if ( MDNS_ENABLE_PPP )
759
760// Note: This includes PPP dial-in interfaces (pppXYZ), but not PPP dial-out interface (pppdXYZ).
761
762    #define IsCompatibleInterface( IFA )                                                                        \
763    ( ( ( IFA )->ifa_flags & IFF_UP )                                                                   &&  \
764      ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) )   &&  \
765      ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) )   &&  \
766      ( !( ( IFA )->ifa_flags & IFF_POINTOPOINT ) || ( strncmp( ( IFA )->ifa_name, "pppd", 4 ) != 0 ) ) )
767#else
768    #define IsCompatibleInterface( IFA )                                                                                \
769    ( ( ( ( IFA )->ifa_flags & ( IFF_UP | IFF_MULTICAST | IFF_POINTOPOINT ) ) == ( IFF_UP | IFF_MULTICAST ) )   &&  \
770      ( ( ( IFA )->ifa_addr->sa_family == AF_INET ) || ( ( IFA )->ifa_addr->sa_family == AF_INET6 ) )           &&  \
771      ( ( IFA )->ifa_netmask && ( ( IFA )->ifa_addr->sa_family == ( IFA )->ifa_netmask->sa_family ) ) )
772#endif
773
774#define IsLinkLocalSockAddr( SA )                                                                   \
775    ( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET )                                 \
776      ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) &&   \
777          ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) )    \
778      : IN6_IS_ADDR_LINKLOCAL( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) )
779
780#define FamilyToString( X )                 \
781    ( ( ( X ) == AF_INET )  ? "AF_INET"  :  \
782      ( ( ( X ) == AF_INET6 ) ? "AF_INET6" :  \
783        ( ( ( X ) == AF_LINK )  ? "AF_LINK"  :  \
784        "UNKNOWN" ) ) )
785
786mDNSlocal mStatus   UpdateInterfaceList( mDNS *const inMDNS, mDNSs32 inUTC )
787{
788    mStatus err;
789    struct ifaddrs *                    ifaList;
790    struct ifaddrs *                    ifa;
791    int family;
792    mDNSBool foundV4;
793    mDNSBool foundV6;
794    struct ifaddrs *                    loopbackV4;
795    struct ifaddrs *                    loopbackV6;
796    mDNSEthAddr primaryMAC;
797    SocketRef infoSock;
798    char defaultName[ 64 ];
799    NetworkInterfaceInfoVxWorks *       i;
800    domainlabel nicelabel;
801    domainlabel hostlabel;
802    domainlabel tmp;
803
804    ifaList     = NULL;
805    foundV4     = mDNSfalse;
806    foundV6     = mDNSfalse;
807    loopbackV4  = NULL;
808    loopbackV6  = NULL;
809    primaryMAC  = zeroEthAddr;
810
811    // Set up an IPv6 socket so we can check the state of interfaces using SIOCGIFAFLAG_IN6.
812
813    infoSock = socket( AF_INET6, SOCK_DGRAM, 0 );
814    check_translated_errno( IsValidSocket( infoSock ), errno_compat(), kUnknownErr );
815
816    // Run through the entire list of interfaces.
817
818    err = getifaddrs( &ifaList );
819    check_translated_errno( err == 0, errno_compat(), kUnknownErr );
820
821    for( ifa = ifaList; ifa; ifa = ifa->ifa_next )
822    {
823        int flags;
824
825        family = ifa->ifa_addr->sa_family;
826        dmsg( kDebugLevelVerbose, DEBUG_NAME "%s: %8s(%d), Flags 0x%08X, Family %8s(%2d)\n", __ROUTINE__,
827              ifa->ifa_name, if_nametoindex( ifa->ifa_name ), ifa->ifa_flags, FamilyToString( family ), family );
828
829        // Save off the MAC address of the first Ethernet-ish interface.
830
831        if( family == AF_LINK )
832        {
833            struct sockaddr_dl *        sdl;
834
835            sdl = (struct sockaddr_dl *) ifa->ifa_addr;
836            if( ( sdl->sdl_type == IFT_ETHER ) && ( sdl->sdl_alen == sizeof( primaryMAC ) &&
837                                                    mDNSSameEthAddress( &primaryMAC, &zeroEthAddr ) ) )
838            {
839                memcpy( primaryMAC.b, sdl->sdl_data + sdl->sdl_nlen, 6 );
840            }
841        }
842
843        if( !IsCompatibleInterface( ifa ) ) continue;
844
845        // If this is a link-local address and there's a non-link-local address on this interface, skip this alias.
846
847        if( IsLinkLocalSockAddr( ifa->ifa_addr ) )
848        {
849            struct ifaddrs *        ifaLL;
850
851            for( ifaLL = ifaList; ifaLL; ifaLL = ifaLL->ifa_next )
852            {
853                if( ifaLL->ifa_addr->sa_family != family ) continue;
854                if( !IsCompatibleInterface( ifaLL ) ) continue;
855                if( strcmp( ifaLL->ifa_name, ifa->ifa_name ) != 0 ) continue;
856                if( !IsLinkLocalSockAddr( ifaLL->ifa_addr ) ) break;
857            }
858            if( ifaLL )
859            {
860                dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %8s(%d) skipping link-local alias\n", __ROUTINE__,
861                      ifa->ifa_name, if_nametoindex( ifa->ifa_name ) );
862                continue;
863            }
864        }
865
866        // If this is an IPv6 interface, perform additional checks to make sure it is really ready for use.
867        // If this is a loopback interface, save it off since we may add it later if there are no other interfaces.
868        // Otherwise, add the interface to the list.
869
870        flags = 0;
871        if( ( family == AF_INET6 ) && IsValidSocket( infoSock ) )
872        {
873            struct sockaddr_in6 *       sa6;
874            struct in6_ifreq ifr6;
875
876            sa6 = (struct sockaddr_in6 *) ifa->ifa_addr;
877            mDNSPlatformMemZero( &ifr6, sizeof( ifr6 ) );
878            strcpy( ifr6.ifr_name, ifa->ifa_name );
879            ifr6.ifr_addr = *sa6;
880            if( ioctl( infoSock, SIOCGIFAFLAG_IN6, (int) &ifr6 ) != -1 )
881            {
882                flags = ifr6.ifr_ifru.ifru_flags6;
883            }
884        }
885
886        // HACK: This excludes interfaces with IN6_IFF_DUPLICATED set instead of using IN6_IFF_NOTREADY (which is
887        // HACK: IN6_IFF_TENTATIVE | IN6_IFF_DUPLICATED) because we currently do not get a notification when an
888        // HACK: interface goes from the tentative state to the fully ready state. So as a short-term workaround,
889        // HACK: this allows tentative interfaces to be registered. We should revisit if we get notification hooks.
890
891        if( flags & ( IN6_IFF_DUPLICATED | IN6_IFF_DETACHED | IN6_IFF_DEPRECATED | IN6_IFF_TEMPORARY ) )
892        {
893            dmsg( kDebugLevelNotice, DEBUG_NAME "%s: %8s(%d), SIOCGIFAFLAG_IN6 not ready yet (0x%X)\n", __ROUTINE__,
894                  ifa->ifa_name, if_nametoindex( ifa->ifa_name ), flags );
895            continue;
896        }
897        if( ifa->ifa_flags & IFF_LOOPBACK )
898        {
899            if( family == AF_INET ) loopbackV4 = ifa;
900            else loopbackV6 = ifa;
901        }
902        else
903        {
904            if( ( family == AF_INET ) && gMDNSDeferIPv4 && IsLinkLocalSockAddr( ifa->ifa_addr ) ) continue;
905            i = AddInterfaceToList( inMDNS, ifa, inUTC );
906            if( i && i->multicast )
907            {
908                if( family == AF_INET ) foundV4 = mDNStrue;
909                else foundV6 = mDNStrue;
910            }
911        }
912    }
913
914    // For efficiency, we don't register a loopback interface when other interfaces of that family are available.
915
916    if( !foundV4 && loopbackV4 ) AddInterfaceToList( inMDNS, loopbackV4, inUTC );
917    if( !foundV6 && loopbackV6 ) AddInterfaceToList( inMDNS, loopbackV6, inUTC );
918    freeifaddrs( ifaList );
919    if( IsValidSocket( infoSock ) ) close_compat( infoSock );
920
921    // The list is complete. Set the McastTxRx setting for each interface. We always send and receive using IPv4.
922    // To reduce traffic, we send and receive using IPv6 only on interfaces that have no routable IPv4 address.
923    // Having a routable IPv4 address assigned is a reasonable indicator of being on a large, configured network,
924    // which means there's a good chance that most or all the other devices on that network should also have v4.
925    // By doing this we lose the ability to talk to true v6-only devices on that link, but we cut the packet rate in half.
926    // At this time, reducing the packet rate is more important than v6-only devices on a large configured network,
927    // so we are willing to make that sacrifice.
928
929    for( i = inMDNS->p->interfaceList; i; i = i->next )
930    {
931        if( i->exists )
932        {
933            mDNSBool txrx;
934
935            txrx = i->multicast && ( ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) || !FindRoutableIPv4( inMDNS, i->scopeID ) );
936            if( i->ifinfo.McastTxRx != txrx )
937            {
938                i->ifinfo.McastTxRx = txrx;
939                i->exists           = 2;    // 2=state change; need to de-register and re-register this interface.
940            }
941        }
942    }
943
944    // Set up the user-specified, friendly name, which is allowed to be full UTF-8.
945
946    mDNS_snprintf( defaultName, sizeof( defaultName ), "Device-%02X:%02X:%02X:%02X:%02X:%02X",
947                   primaryMAC.b[ 0 ], primaryMAC.b[ 1 ], primaryMAC.b[ 2 ], primaryMAC.b[ 3 ], primaryMAC.b[ 4 ], primaryMAC.b[ 5 ] );
948
949    MakeDomainLabelFromLiteralString( &nicelabel, "Put Nice Name Here" ); // $$$ Implementers: Fill in nice name of device.
950    if( nicelabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &nicelabel, defaultName );
951
952    // Set up the RFC 1034-compliant label. If not set or it is not RFC 1034 compliant, try the user-specified nice name.
953
954    MakeDomainLabelFromLiteralString( &tmp, "Put-DNS-Name-Here" ); // $$$ Implementers: Fill in DNS name of device.
955    ConvertUTF8PstringToRFC1034HostLabel( tmp.c, &hostlabel );
956    if( hostlabel.c[ 0 ] == 0 ) ConvertUTF8PstringToRFC1034HostLabel( nicelabel.c, &hostlabel );
957    if( hostlabel.c[ 0 ] == 0 ) MakeDomainLabelFromLiteralString( &hostlabel, defaultName );
958
959    // Update our globals and mDNS with the new labels.
960
961    if( !SameDomainLabelCS( inMDNS->p->userNiceLabel.c, nicelabel.c ) )
962    {
963        dmsg( kDebugLevelInfo, DEBUG_NAME "Updating nicelabel to \"%#s\"\n", nicelabel.c );
964        inMDNS->p->userNiceLabel    = nicelabel;
965        inMDNS->nicelabel           = nicelabel;
966    }
967    if( !SameDomainLabelCS( inMDNS->p->userHostLabel.c, hostlabel.c ) )
968    {
969        dmsg( kDebugLevelInfo, DEBUG_NAME "Updating hostlabel to \"%#s\"\n", hostlabel.c );
970        inMDNS->p->userHostLabel    = hostlabel;
971        inMDNS->hostlabel           = hostlabel;
972        mDNS_SetFQDN( inMDNS );
973    }
974    return( mStatus_NoError );
975}
976
977//===========================================================================================================================
978//	AddInterfaceToList
979//===========================================================================================================================
980
981mDNSlocal NetworkInterfaceInfoVxWorks * AddInterfaceToList( mDNS *const inMDNS, struct ifaddrs *inIFA, mDNSs32 inUTC )
982{
983    mStatus err;
984    mDNSAddr ip;
985    mDNSAddr mask;
986    mDNSu32 scopeID;
987    NetworkInterfaceInfoVxWorks **      p;
988    NetworkInterfaceInfoVxWorks *       i;
989
990    i = NULL;
991
992    err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ip );
993    require_noerr( err, exit );
994
995    err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &mask );
996    require_noerr( err, exit );
997
998    // Search for an existing interface with the same info. If found, just return that one.
999
1000    scopeID = if_nametoindex( inIFA->ifa_name );
1001    check( scopeID );
1002    for( p = &inMDNS->p->interfaceList; *p; p = &( *p )->next )
1003    {
1004        if( ( scopeID == ( *p )->scopeID ) && mDNSSameAddress( &ip, &( *p )->ifinfo.ip ) )
1005        {
1006            dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Found existing interface %u with address %#a at %#p\n", __ROUTINE__,
1007                  scopeID, &ip, *p );
1008            ( *p )->exists = mDNStrue;
1009            i = *p;
1010            goto exit;
1011        }
1012    }
1013
1014    // Allocate the new interface info and fill it out.
1015
1016    i = (NetworkInterfaceInfoVxWorks *) calloc( 1, sizeof( *i ) );
1017    require( i, exit );
1018
1019    dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Making   new   interface %u with address %#a at %#p\n", __ROUTINE__, scopeID, &ip, i );
1020    strncpy( i->ifinfo.ifname, inIFA->ifa_name, sizeof( i->ifinfo.ifname ) );
1021    i->ifinfo.ifname[ sizeof( i->ifinfo.ifname ) - 1 ] = '\0';
1022    i->ifinfo.InterfaceID   = NULL;
1023    i->ifinfo.ip            = ip;
1024    i->ifinfo.mask          = mask;
1025    i->ifinfo.Advertise     = inMDNS->AdvertiseLocalAddresses;
1026    i->ifinfo.McastTxRx     = mDNSfalse; // For now; will be set up later at the end of UpdateInterfaceList.
1027
1028    i->next                 = NULL;
1029    i->exists               = mDNStrue;
1030    i->lastSeen             = inUTC;
1031    i->scopeID              = scopeID;
1032    i->family               = inIFA->ifa_addr->sa_family;
1033    i->multicast            = ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTOPOINT );
1034
1035    i->ss.info              = i;
1036    i->ss.sockV4            = kInvalidSocketRef;
1037    i->ss.sockV6            = kInvalidSocketRef;
1038    *p = i;
1039
1040exit:
1041    return( i );
1042}
1043
1044//===========================================================================================================================
1045//	SetupActiveInterfaces
1046//
1047//	Returns a count of non-link local IPv4 addresses registered.
1048//===========================================================================================================================
1049
1050#define mDNSAddressIsNonLinkLocalIPv4( X )  \
1051    ( ( ( X )->type == mDNSAddrType_IPv4 ) && ( ( ( X )->ip.v4.b[ 0 ] != 169 ) || ( ( X )->ip.v4.b[ 1 ] != 254 ) ) )
1052
1053mDNSlocal int   SetupActiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC )
1054{
1055    int count;
1056    NetworkInterfaceInfoVxWorks *       i;
1057
1058    count = 0;
1059    for( i = inMDNS->p->interfaceList; i; i = i->next )
1060    {
1061        NetworkInterfaceInfo *              n;
1062        NetworkInterfaceInfoVxWorks *       primary;
1063
1064        if( !i->exists ) continue;
1065
1066        // Search for the primary interface and sanity check it.
1067
1068        n = &i->ifinfo;
1069        primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID );
1070        if( !primary )
1071        {
1072            dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! didn't find %s(%u)\n", __ROUTINE__, i->ifinfo.ifname, i->scopeID );
1073            continue;
1074        }
1075        if( n->InterfaceID && ( n->InterfaceID != (mDNSInterfaceID) primary ) )
1076        {
1077            dmsg( kDebugLevelError, DEBUG_NAME "%s: ERROR! n->InterfaceID %#p != primary %#p\n", __ROUTINE__,
1078                  n->InterfaceID, primary );
1079            n->InterfaceID = NULL;
1080        }
1081
1082        // If n->InterfaceID is set, it means we've already called mDNS_RegisterInterface() for this interface.
1083        // so we don't need to call it again. Otherwise, register the interface with mDNS.
1084
1085        if( !n->InterfaceID )
1086        {
1087            mDNSBool flapping;
1088
1089            n->InterfaceID = (mDNSInterfaceID) primary;
1090
1091            // If lastSeen == inUTC, then this is a brand-new interface, or an interface that never went away.
1092            // If lastSeen != inUTC, then this is an old interface, that went away for (inUTC - lastSeen) seconds.
1093            // If it's is an old one that went away and came back in less than a minute, we're in a flapping scenario.
1094
1095            flapping = ( ( inUTC - i->lastSeen ) > 0 ) && ( ( inUTC - i->lastSeen ) < 60 );
1096            mDNS_RegisterInterface( inMDNS, n, flapping );
1097            if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count;
1098
1099            dmsg( kDebugLevelInfo, DEBUG_NAME "%s:   Registered    %8s(%u) InterfaceID %#p %#a%s%s\n", __ROUTINE__,
1100                  i->ifinfo.ifname, i->scopeID, primary, &n->ip,
1101                  flapping            ? " (Flapping)" : "",
1102                  n->InterfaceActive  ? " (Primary)"  : "" );
1103        }
1104
1105        // Set up a socket if it's not already set up. If multicast is not enabled on this interface then we
1106        // don't need a socket since unicast traffic will be handled on the unicast socket.
1107
1108        if( n->McastTxRx )
1109        {
1110            mStatus err;
1111
1112            if( ( ( i->family == AF_INET  ) && !IsValidSocket( primary->ss.sockV4 ) ) ||
1113                ( ( i->family == AF_INET6 ) && !IsValidSocket( primary->ss.sockV6 ) ) )
1114            {
1115                err = SetupSocket( inMDNS, &i->ifinfo.ip, mDNStrue, i->family, &primary->ss );
1116                check_noerr( err );
1117            }
1118        }
1119        else
1120        {
1121            dmsg( kDebugLevelInfo, DEBUG_NAME "%s:   No Tx/Rx on   %8s(%u) InterfaceID %#p %#a\n", __ROUTINE__,
1122                  i->ifinfo.ifname, i->scopeID, primary, &n->ip );
1123        }
1124    }
1125    return( count );
1126}
1127
1128//===========================================================================================================================
1129//	MarkAllInterfacesInactive
1130//===========================================================================================================================
1131
1132mDNSlocal void  MarkAllInterfacesInactive( mDNS *const inMDNS, mDNSs32 inUTC )
1133{
1134    NetworkInterfaceInfoVxWorks *       i;
1135
1136    for( i = inMDNS->p->interfaceList; i; i = i->next )
1137    {
1138        if( !i->exists ) continue;
1139        i->lastSeen = inUTC;
1140        i->exists   = mDNSfalse;
1141    }
1142}
1143
1144//===========================================================================================================================
1145//	ClearInactiveInterfaces
1146//
1147//	Returns count of non-link local IPv4 addresses de-registered.
1148//===========================================================================================================================
1149
1150mDNSlocal int   ClearInactiveInterfaces( mDNS *const inMDNS, mDNSs32 inUTC, mDNSBool inClosing )
1151{
1152    int count;
1153    NetworkInterfaceInfoVxWorks *       i;
1154    NetworkInterfaceInfoVxWorks **      p;
1155
1156    // First pass:
1157    // If an interface is going away, then de-register it from mDNSCore.
1158    // We also have to de-register it if the primary interface that it's using for its InterfaceID is going away.
1159    // We have to do this because mDNSCore will use that InterfaceID when sending packets, and if the memory
1160    // it refers to has gone away, we'll crash. Don't actually close the sockets or free the memory yet though:
1161    // When the last representative of an interface goes away mDNSCore may want to send goodbye packets on that
1162    // interface. (Not yet implemented, but a good idea anyway.).
1163
1164    count = 0;
1165    for( i = inMDNS->p->interfaceList; i; i = i->next )
1166    {
1167        NetworkInterfaceInfoVxWorks *       primary;
1168
1169        // 1. If this interface is no longer active, or its InterfaceID is changing, de-register it.
1170
1171        if( !i->ifinfo.InterfaceID ) continue;
1172        primary = FindInterfaceByIndex( inMDNS, i->family, i->scopeID );
1173        if( ( i->exists == 0 ) || ( i->exists == 2 ) || ( i->ifinfo.InterfaceID != (mDNSInterfaceID) primary ) )
1174        {
1175            dmsg( kDebugLevelInfo, DEBUG_NAME "%s: Deregistering %8s(%u) InterfaceID %#p %#a%s\n", __ROUTINE__,
1176                  i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip,
1177                  i->ifinfo.InterfaceActive ? " (Primary)" : "" );
1178
1179            mDNS_DeregisterInterface( inMDNS, &i->ifinfo, mDNSfalse );
1180            i->ifinfo.InterfaceID = NULL;
1181            if( mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) ) ++count;
1182        }
1183    }
1184
1185    // Second pass:
1186    // Now that everything that's going to de-register has done so, we can close sockets and free the memory.
1187
1188    p = &inMDNS->p->interfaceList;
1189    while( *p )
1190    {
1191        i = *p;
1192
1193        // 2. Close all our sockets. We'll recreate them later as needed.
1194        // (We may have previously had both v4 and v6, and we may not need both any more.).
1195
1196        ForgetSocket( &i->ss.sockV4 );
1197        ForgetSocket( &i->ss.sockV6 );
1198
1199        // 3. If no longer active, remove the interface from the list and free its memory.
1200
1201        if( !i->exists )
1202        {
1203            mDNSBool deleteIt;
1204
1205            if( inClosing )
1206            {
1207                check_string( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0, "closing with in-use records!" );
1208                deleteIt = mDNStrue;
1209            }
1210            else
1211            {
1212                if( i->lastSeen == inUTC ) i->lastSeen = inUTC - 1;
1213                deleteIt = ( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) i ) == 0 ) && ( ( inUTC - i->lastSeen ) >= 60 );
1214            }
1215            dmsg( kDebugLevelInfo, DEBUG_NAME "%s: %-13s %8s(%u) InterfaceID %#p %#a Age %d%s\n", __ROUTINE__,
1216                  deleteIt ? "Deleting" : "Holding", i->ifinfo.ifname, i->scopeID, i->ifinfo.InterfaceID, &i->ifinfo.ip,
1217                  inUTC - i->lastSeen, i->ifinfo.InterfaceActive ? " (Primary)" : "" );
1218            if( deleteIt )
1219            {
1220                *p = i->next;
1221                free( i );
1222                continue;
1223            }
1224        }
1225        p = &i->next;
1226    }
1227    return( count );
1228}
1229
1230//===========================================================================================================================
1231//	FindRoutableIPv4
1232//===========================================================================================================================
1233
1234mDNSlocal NetworkInterfaceInfoVxWorks * FindRoutableIPv4( mDNS *const inMDNS, mDNSu32 inScopeID )
1235{
1236#if ( MDNS_EXCLUDE_IPV4_ROUTABLE_IPV6 )
1237    NetworkInterfaceInfoVxWorks *       i;
1238
1239    for( i = inMDNS->p->interfaceList; i; i = i->next )
1240    {
1241        if( i->exists && ( i->scopeID == inScopeID ) && mDNSAddressIsNonLinkLocalIPv4( &i->ifinfo.ip ) )
1242        {
1243            break;
1244        }
1245    }
1246    return( i );
1247#else
1248    DEBUG_UNUSED( inMDNS );
1249    DEBUG_UNUSED( inScopeID );
1250
1251    return( NULL );
1252#endif
1253}
1254
1255//===========================================================================================================================
1256//	FindInterfaceByIndex
1257//===========================================================================================================================
1258
1259mDNSlocal NetworkInterfaceInfoVxWorks * FindInterfaceByIndex( mDNS *const inMDNS, int inFamily, mDNSu32 inIndex )
1260{
1261    NetworkInterfaceInfoVxWorks *       i;
1262
1263    check( inIndex != 0 );
1264
1265    for( i = inMDNS->p->interfaceList; i; i = i->next )
1266    {
1267        if( i->exists && ( i->scopeID == inIndex ) &&
1268            ( MDNS_AAAA_OVER_IPV4                                                           ||
1269              ( ( inFamily == AF_INET  ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv4 ) )    ||
1270              ( ( inFamily == AF_INET6 ) && ( i->ifinfo.ip.type == mDNSAddrType_IPv6 ) ) ) )
1271        {
1272            return( i );
1273        }
1274    }
1275    return( NULL );
1276}
1277
1278//===========================================================================================================================
1279//	SetupSocket
1280//===========================================================================================================================
1281
1282mDNSlocal mStatus   SetupSocket( mDNS *const inMDNS, const mDNSAddr *inAddr, mDNSBool inMcast, int inFamily, SocketSet *inSS )
1283{
1284    mStatus err;
1285    SocketRef *     sockPtr;
1286    mDNSIPPort port;
1287    SocketRef sock;
1288    const int on = 1;
1289
1290    check( inAddr );
1291    check( inSS );
1292
1293    sockPtr = ( inFamily == AF_INET ) ? &inSS->sockV4 : &inSS->sockV6;
1294    port    = ( inMcast || inMDNS->CanReceiveUnicastOn5353 ) ? MulticastDNSPort : zeroIPPort;
1295
1296    sock = socket( inFamily, SOCK_DGRAM, IPPROTO_UDP );
1297    err = translate_errno( IsValidSocket( sock ), errno_compat(), mStatus_UnknownErr );
1298    require_noerr( err, exit );
1299
1300    // Allow multiple listeners if this is a multicast port.
1301
1302    if( port.NotAnInteger )
1303    {
1304        err = setsockopt( sock, SOL_SOCKET, SO_REUSEPORT, (char *) &on, sizeof( on ) );
1305        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1306    }
1307
1308    // Set up the socket based on the family (IPv4 or IPv6).
1309
1310    if( inFamily == AF_INET )
1311    {
1312        const int ttlV4       = 255;
1313        const u_char ttlV4Mcast  = 255;
1314        struct sockaddr_in sa4;
1315
1316        // Receive destination addresses so we know which address the packet was sent to.
1317
1318        err = setsockopt( sock, IPPROTO_IP, IP_RECVDSTADDR, (char *) &on, sizeof( on ) );
1319        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1320
1321        // Receive interface indexes so we know which interface received the packet.
1322
1323        err = setsockopt( sock, IPPROTO_IP, IP_RECVIF, (char *) &on, sizeof( on ) );
1324        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1325
1326        // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving.
1327
1328        if( inMcast )
1329        {
1330            struct in_addr addrV4;
1331            struct ip_mreq mreqV4;
1332
1333            addrV4.s_addr               = inAddr->ip.v4.NotAnInteger;
1334            mreqV4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
1335            mreqV4.imr_interface        = addrV4;
1336            err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqV4, sizeof( mreqV4 ) );
1337            check_translated_errno( err == 0, errno_compat(), kOptionErr );
1338
1339            err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &addrV4, sizeof( addrV4 ) );
1340            check_translated_errno( err == 0, errno_compat(), kOptionErr );
1341        }
1342
1343        // Send unicast packets with TTL 255 (helps against spoofing).
1344
1345        err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &ttlV4, sizeof( ttlV4 ) );
1346        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1347
1348        // Send multicast packets with TTL 255 (helps against spoofing).
1349
1350        err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &ttlV4Mcast, sizeof( ttlV4Mcast ) );
1351        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1352
1353        // Start listening for packets.
1354
1355        mDNSPlatformMemZero( &sa4, sizeof( sa4 ) );
1356        sa4.sin_len         = sizeof( sa4 );
1357        sa4.sin_family      = AF_INET;
1358        sa4.sin_port        = port.NotAnInteger;
1359        sa4.sin_addr.s_addr = htonl( INADDR_ANY ); // We want to receive multicasts AND unicasts on this socket.
1360        err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) );
1361        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1362    }
1363    else if( inFamily == AF_INET6 )
1364    {
1365        struct sockaddr_in6 sa6;
1366        const int ttlV6 = 255;
1367
1368        // Receive destination addresses and interface index so we know where the packet was received and intended.
1369
1370        err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &on, sizeof( on ) );
1371        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1372
1373        // Receive only IPv6 packets because otherwise, we may get IPv4 addresses as IPv4-mapped IPv6 addresses.
1374
1375        err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &on, sizeof( on ) );
1376        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1377
1378        // Join the multicast group on this interface and specify the outgoing interface, if it's for multicast receiving.
1379
1380        if( inMcast )
1381        {
1382            u_int ifindex;
1383            struct ipv6_mreq mreqV6;
1384
1385            ifindex                 = inSS->info->scopeID;
1386            mreqV6.ipv6mr_interface = ifindex;
1387            mreqV6.ipv6mr_multiaddr = *( (struct in6_addr * ) &AllDNSLinkGroup_v6.ip.v6 );
1388            err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqV6, sizeof( mreqV6 ) );
1389            check_translated_errno( err == 0, errno_compat(), kOptionErr );
1390
1391            err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &ifindex, sizeof( ifindex ) );
1392            check_translated_errno( err == 0, errno_compat(), kOptionErr );
1393        }
1394
1395        // Send unicast packets with TTL 255 (helps against spoofing).
1396
1397        err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) );
1398        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1399
1400        // Send multicast packets with TTL 255 (helps against spoofing).
1401
1402        err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &ttlV6, sizeof( ttlV6 ) );
1403        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1404
1405        // Receive our own packets for same-machine operation.
1406
1407        err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &on, sizeof( on ) );
1408        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1409
1410        // Start listening for packets.
1411
1412        mDNSPlatformMemZero( &sa6, sizeof( sa6 ) );
1413        sa6.sin6_len        = sizeof( sa6 );
1414        sa6.sin6_family     = AF_INET6;
1415        sa6.sin6_port       = port.NotAnInteger;
1416        sa6.sin6_flowinfo   = 0;
1417        sa6.sin6_addr       = in6addr_any; // We want to receive multicasts AND unicasts on this socket.
1418        sa6.sin6_scope_id   = 0;
1419        err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) );
1420        check_translated_errno( err == 0, errno_compat(), kOptionErr );
1421    }
1422    else
1423    {
1424        dmsg( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inFamily );
1425        err = kUnsupportedErr;
1426        goto exit;
1427    }
1428
1429    // Make the socket non-blocking so we can potentially get multiple packets per select call.
1430
1431    err = ioctl( sock, FIONBIO, (int) &on );
1432    check_translated_errno( err == 0, errno_compat(), kOptionErr );
1433
1434    *sockPtr = sock;
1435    sock = kInvalidSocketRef;
1436    err = mStatus_NoError;
1437
1438exit:
1439    if( IsValidSocket( sock ) ) close_compat( sock );
1440    return( err );
1441}
1442
1443//===========================================================================================================================
1444//	SockAddrToMDNSAddr
1445//===========================================================================================================================
1446
1447mDNSlocal mStatus   SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP )
1448{
1449    mStatus err;
1450
1451    check( inSA );
1452    check( outIP );
1453
1454    if( inSA->sa_family == AF_INET )
1455    {
1456        struct sockaddr_in *        sa4;
1457
1458        sa4 = (struct sockaddr_in *) inSA;
1459        outIP->type                 = mDNSAddrType_IPv4;
1460        outIP->ip.v4.NotAnInteger   = sa4->sin_addr.s_addr;
1461        err = mStatus_NoError;
1462    }
1463    else if( inSA->sa_family == AF_INET6 )
1464    {
1465        struct sockaddr_in6 *       sa6;
1466
1467        sa6 = (struct sockaddr_in6 *) inSA;
1468        outIP->type     = mDNSAddrType_IPv6;
1469        outIP->ip.v6    = *( (mDNSv6Addr *) &sa6->sin6_addr );
1470        if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) ) outIP->ip.v6.w[ 1 ] = 0;
1471        err = mStatus_NoError;
1472    }
1473    else
1474    {
1475        dmsg( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family (%d)\n", __ROUTINE__, inSA->sa_family );
1476        err = mStatus_BadParamErr;
1477    }
1478    return( err );
1479}
1480
1481#if 0
1482#pragma mark -
1483#pragma mark == Commands ==
1484#endif
1485
1486//===========================================================================================================================
1487//	SetupCommandPipe
1488//===========================================================================================================================
1489
1490mDNSlocal mStatus   SetupCommandPipe( mDNS * const inMDNS )
1491{
1492    mStatus err;
1493
1494    err = pipeDevCreate( "/pipe/mDNS", 32, 1 );
1495    check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1496
1497    inMDNS->p->commandPipe = open( "/pipe/mDNS", O_RDWR, 0 );
1498    err = translate_errno( inMDNS->p->commandPipe != ERROR, errno_compat(), mStatus_UnsupportedErr );
1499    require_noerr( err, exit );
1500
1501exit:
1502    return( err );
1503}
1504
1505//===========================================================================================================================
1506//	TearDownCommandPipe
1507//===========================================================================================================================
1508
1509mDNSlocal mStatus   TearDownCommandPipe( mDNS * const inMDNS )
1510{
1511    mStatus err;
1512
1513    if( inMDNS->p->commandPipe != ERROR )
1514    {
1515        err = close( inMDNS->p->commandPipe );
1516        check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1517        inMDNS->p->commandPipe = ERROR;
1518
1519        err = pipeDevDelete( "/pipe/mDNS", FALSE );
1520        check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1521    }
1522    return( mStatus_NoError );
1523}
1524
1525//===========================================================================================================================
1526//	SendCommand
1527//===========================================================================================================================
1528
1529mDNSlocal mStatus   SendCommand( const mDNS * const inMDNS, MDNSPipeCommandCode inCommandCode )
1530{
1531    mStatus err;
1532
1533    require_action_quiet( inMDNS->p->commandPipe != ERROR, exit, err = mStatus_NotInitializedErr );
1534
1535    err = write( inMDNS->p->commandPipe, &inCommandCode, sizeof( inCommandCode ) );
1536    err = translate_errno( err >= 0, errno_compat(), kWriteErr );
1537    require_noerr( err, exit );
1538
1539exit:
1540    return( err );
1541}
1542
1543//===========================================================================================================================
1544//	ProcessCommand
1545//===========================================================================================================================
1546
1547mDNSlocal mStatus   ProcessCommand( mDNS * const inMDNS )
1548{
1549    mStatus err;
1550    MDNSPipeCommandCode cmd;
1551    mDNSs32 utc;
1552
1553    err = read( inMDNS->p->commandPipe, &cmd, sizeof( cmd ) );
1554    err = translate_errno( err >= 0, errno_compat(), kReadErr );
1555    require_noerr( err, exit );
1556
1557    switch( cmd )
1558    {
1559    case kMDNSPipeCommandCodeReschedule:        // Reschedule: just break out to re-run mDNS_Execute.
1560        break;
1561
1562    case kMDNSPipeCommandCodeReconfigure:       // Reconfigure: rebuild the interface list after a config change.
1563        dmsg( kDebugLevelInfo, DEBUG_NAME "***   NETWORK CONFIGURATION CHANGE   ***\n" );
1564        mDNSPlatformLock( inMDNS );
1565
1566        utc = mDNSPlatformUTC();
1567        MarkAllInterfacesInactive( inMDNS, utc );
1568        UpdateInterfaceList( inMDNS, utc );
1569        ClearInactiveInterfaces( inMDNS, utc, mDNSfalse );
1570        SetupActiveInterfaces( inMDNS, utc );
1571
1572        mDNSPlatformUnlock( inMDNS );
1573        mDNS_ConfigChanged(inMDNS);
1574        break;
1575
1576    case kMDNSPipeCommandCodeQuit:              // Quit: just set a flag so the task exits cleanly.
1577        inMDNS->p->quit = mDNStrue;
1578        break;
1579
1580    default:
1581        dmsg( kDebugLevelError, DEBUG_NAME "unknown pipe command (%d)\n", cmd );
1582        err = mStatus_BadParamErr;
1583        goto exit;
1584    }
1585
1586exit:
1587    return( err );
1588}
1589
1590#if 0
1591#pragma mark -
1592#pragma mark == Threads ==
1593#endif
1594
1595//===========================================================================================================================
1596//	Task
1597//===========================================================================================================================
1598
1599mDNSlocal void  Task( mDNS *inMDNS )
1600{
1601    mStatus err;
1602    mDNSs32 nextEvent;
1603    fd_set readSet;
1604    int maxFd;
1605    struct timeval timeout;
1606    NetworkInterfaceInfoVxWorks *       i;
1607    int fd;
1608    int n;
1609
1610    check( inMDNS );
1611
1612    err = TaskInit( inMDNS );
1613    require_noerr( err, exit );
1614
1615    while( !inMDNS->p->quit )
1616    {
1617        // Let mDNSCore do its work then wait for an event. On idle timeouts (n == 0), just loop back to mDNS_Exceute.
1618
1619        nextEvent = mDNS_Execute( inMDNS );
1620        TaskSetupSelect( inMDNS, &readSet, &maxFd, nextEvent, &timeout );
1621        n = select( maxFd + 1, &readSet, NULL, NULL, &timeout );
1622        check_translated_errno( n >= 0, errno_compat(), kUnknownErr );
1623        if( n == 0 ) continue;
1624
1625        // Process interface-specific sockets with pending data.
1626
1627        n = 0;
1628        for( i = inMDNS->p->interfaceList; i; i = i->next )
1629        {
1630            fd = i->ss.sockV4;
1631            if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) )
1632            {
1633                TaskProcessPackets( inMDNS, &i->ss, fd );
1634                ++n;
1635            }
1636            fd = i->ss.sockV6;
1637            if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) )
1638            {
1639                TaskProcessPackets( inMDNS, &i->ss, fd );
1640                ++n;
1641            }
1642        }
1643
1644        // Process unicast sockets with pending data.
1645
1646        fd = inMDNS->p->unicastSS.sockV4;
1647        if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) )
1648        {
1649            TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd );
1650            ++n;
1651        }
1652        fd = inMDNS->p->unicastSS.sockV6;
1653        if( IsValidSocket( fd ) && FD_ISSET( fd, &readSet ) )
1654        {
1655            TaskProcessPackets( inMDNS, &inMDNS->p->unicastSS, fd );
1656            ++n;
1657        }
1658
1659        // Processing pending commands.
1660
1661        fd = inMDNS->p->commandPipe;
1662        check( fd >= 0 );
1663        if( FD_ISSET( fd, &readSet ) )
1664        {
1665            ProcessCommand( inMDNS );
1666            ++n;
1667        }
1668        check_string( n > 0, "select said something was readable, but nothing was" );
1669    }
1670
1671exit:
1672    TaskTerm( inMDNS );
1673}
1674
1675//===========================================================================================================================
1676//	TaskInit
1677//===========================================================================================================================
1678
1679mDNSlocal mStatus   TaskInit( mDNS *inMDNS )
1680{
1681    mStatus err;
1682    mDNSs32 utc;
1683    socklen_t len;
1684
1685    inMDNS->p->taskID = taskIdSelf();
1686
1687    err = SetupCommandPipe( inMDNS );
1688    require_noerr( err, exit );
1689
1690    inMDNS->CanReceiveUnicastOn5353 = mDNStrue;
1691
1692    // Set up the HINFO string using the description property (e.g. "Device V1.0").
1693
1694    inMDNS->HIHardware.c[ 0 ] = 11;
1695    memcpy( &inMDNS->HIHardware.c[ 1 ], "Device V1.0", inMDNS->HIHardware.c[ 0 ] ); // $$$ Implementers: Fill in real info.
1696
1697    // Set up the unicast sockets.
1698
1699    err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET, &inMDNS->p->unicastSS );
1700    check_noerr( err );
1701    if( err == mStatus_NoError )
1702    {
1703        struct sockaddr_in sa4;
1704
1705        len = sizeof( sa4 );
1706        err = getsockname( inMDNS->p->unicastSS.sockV4, (struct sockaddr *) &sa4, &len );
1707        check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1708        if( err == 0 ) inMDNS->UnicastPort4.NotAnInteger = sa4.sin_port;
1709    }
1710
1711    err = SetupSocket( inMDNS, &zeroAddr, mDNSfalse, AF_INET6, &inMDNS->p->unicastSS );
1712    check_noerr( err );
1713    if( err == mStatus_NoError )
1714    {
1715        struct sockaddr_in6 sa6;
1716
1717        len = sizeof( sa6 );
1718        err = getsockname( inMDNS->p->unicastSS.sockV6, (struct sockaddr *) &sa6, &len );
1719        check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1720        if( err == 0 ) inMDNS->UnicastPort6.NotAnInteger = sa6.sin6_port;
1721    }
1722
1723    // Set up the interfaces.
1724
1725    utc = mDNSPlatformUTC();
1726    UpdateInterfaceList( inMDNS, utc );
1727    SetupActiveInterfaces( inMDNS, utc );
1728    err = mStatus_NoError;
1729
1730exit:
1731    // Signal the "ready" semaphore to indicate the task initialization code has completed (success or not).
1732
1733    inMDNS->p->initErr = err;
1734    semGive( inMDNS->p->initEvent );
1735    return( err );
1736}
1737
1738//===========================================================================================================================
1739//	TaskTerm
1740//===========================================================================================================================
1741
1742mDNSlocal void  TaskTerm( mDNS *inMDNS )
1743{
1744    mStatus err;
1745    mDNSs32 utc;
1746
1747    // Tear down all interfaces.
1748
1749    utc = mDNSPlatformUTC();
1750    MarkAllInterfacesInactive( inMDNS, utc );
1751    ClearInactiveInterfaces( inMDNS, utc, mDNStrue );
1752    check_string( !inMDNS->p->interfaceList, "LEAK: closing without deleting all interfaces" );
1753
1754    // Close unicast sockets.
1755
1756    ForgetSocket( &inMDNS->p->unicastSS.sockV4);
1757    ForgetSocket( &inMDNS->p->unicastSS.sockV6 );
1758
1759    // Tear down everything else that was set up in TaskInit then signal back that we're done.
1760
1761    err = TearDownCommandPipe( inMDNS );
1762    check_noerr( err );
1763
1764    err = semGive( inMDNS->p->quitEvent );
1765    check_translated_errno( err == 0, errno_compat(), kUnknownErr );
1766}
1767
1768//===========================================================================================================================
1769//	TaskSetupSelect
1770//===========================================================================================================================
1771
1772mDNSlocal void  TaskSetupSelect( mDNS *inMDNS, fd_set *outSet, int *outMaxFd, mDNSs32 inNextEvent, struct timeval *outTimeout )
1773{
1774    NetworkInterfaceInfoVxWorks *       i;
1775    int maxFd;
1776    int fd;
1777    mDNSs32 delta;
1778
1779    FD_ZERO( outSet );
1780    maxFd = -1;
1781
1782    // Add the interface-specific sockets.
1783
1784    for( i = inMDNS->p->interfaceList; i; i = i->next )
1785    {
1786        fd = i->ss.sockV4;
1787        if( IsValidSocket( fd ) )
1788        {
1789            FD_SET( fd, outSet );
1790            if( fd > maxFd ) maxFd = fd;
1791        }
1792
1793        fd = i->ss.sockV6;
1794        if( IsValidSocket( fd ) )
1795        {
1796            FD_SET( fd, outSet );
1797            if( fd > maxFd ) maxFd = fd;
1798        }
1799    }
1800
1801    // Add the unicast sockets.
1802
1803    fd = inMDNS->p->unicastSS.sockV4;
1804    if( IsValidSocket( fd ) )
1805    {
1806        FD_SET( fd, outSet );
1807        if( fd > maxFd ) maxFd = fd;
1808    }
1809
1810    fd = inMDNS->p->unicastSS.sockV6;
1811    if( IsValidSocket( fd ) )
1812    {
1813        FD_SET( fd, outSet );
1814        if( fd > maxFd ) maxFd = fd;
1815    }
1816
1817    // Add the command pipe.
1818
1819    fd = inMDNS->p->commandPipe;
1820    check( fd >= 0 );
1821    FD_SET( fd, outSet );
1822    if( fd > maxFd ) maxFd = fd;
1823
1824    check( maxFd > 0 );
1825    *outMaxFd = maxFd;
1826
1827    // Calculate how long to wait before performing idle processing.
1828
1829    delta = inNextEvent - mDNS_TimeNow( inMDNS );
1830    if( delta <= 0 )
1831    {
1832        // The next task time is now or in the past. Set the timeout to fire immediately.
1833
1834        outTimeout->tv_sec  = 0;
1835        outTimeout->tv_usec = 0;
1836    }
1837    else
1838    {
1839        // Calculate the seconds and microseconds until the timeout should occur. Add one to the ticks remainder
1840        // before multiplying to account for integer rounding error and avoid firing the timeout too early.
1841
1842        outTimeout->tv_sec  = delta / mDNSPlatformOneSecond;
1843        outTimeout->tv_usec = ( ( delta % mDNSPlatformOneSecond ) + 1 ) * gMDNSTicksToMicro;
1844        if( outTimeout->tv_usec >= 1000000L )
1845        {
1846            outTimeout->tv_sec += 1;
1847            outTimeout->tv_usec = 0;
1848        }
1849    }
1850}
1851
1852//===========================================================================================================================
1853//	TaskProcessPackets
1854//===========================================================================================================================
1855
1856mDNSlocal void  TaskProcessPackets( mDNS *inMDNS, SocketSet *inSS, SocketRef inSock )
1857{
1858    mDNSu32 ifindex;
1859    ssize_t n;
1860    mDNSu8 *                    buf;
1861    size_t size;
1862    struct sockaddr_storage from;
1863    size_t fromSize;
1864    mDNSAddr destAddr;
1865    mDNSAddr senderAddr;
1866    mDNSIPPort senderPort;
1867    mDNSInterfaceID id;
1868
1869    buf  = (mDNSu8 *) &inMDNS->imsg;
1870    size = sizeof( inMDNS->imsg );
1871    for( ;; )
1872    {
1873        ifindex = 0;
1874        n = mDNSRecvMsg( inSock, buf, size, &from, sizeof( from ), &fromSize, &destAddr, &ifindex );
1875        if( n < 0 ) break;
1876        if( from.ss_family == AF_INET )
1877        {
1878            struct sockaddr_in *        sa4;
1879
1880            sa4 = (struct sockaddr_in *) &from;
1881            senderAddr.type                 = mDNSAddrType_IPv4;
1882            senderAddr.ip.v4.NotAnInteger   = sa4->sin_addr.s_addr;
1883            senderPort.NotAnInteger         = sa4->sin_port;
1884        }
1885        else if( from.ss_family == AF_INET6 )
1886        {
1887            struct sockaddr_in6 *       sa6;
1888
1889            sa6 = (struct sockaddr_in6 *) &from;
1890            senderAddr.type         = mDNSAddrType_IPv6;
1891            senderAddr.ip.v6        = *( (mDNSv6Addr *) &sa6->sin6_addr );
1892            senderPort.NotAnInteger = sa6->sin6_port;
1893        }
1894        else
1895        {
1896            dmsg( kDebugLevelWarning, DEBUG_NAME "%s: WARNING! from addr unknown family %d\n", __ROUTINE__, from.ss_family );
1897            continue;
1898        }
1899
1900        // Even though we indicated a specific interface when joining the multicast group, a weirdness of the
1901        // sockets API means that even though this socket has only officially joined the multicast group
1902        // on one specific interface, the kernel will still deliver multicast packets to it no matter which
1903        // interface they arrive on. According to the official Unix Powers That Be, this is Not A Bug.
1904        // To work around this weirdness, we use the IP_RECVIF/IPV6_PKTINFO options to find the interface
1905        // on which the packet arrived, and ignore the packet if it really arrived on some other interface.
1906
1907        if( mDNSAddrIsDNSMulticast( &destAddr ) )
1908        {
1909            if( !inSS->info || !inSS->info->exists )
1910            {
1911                dpkt( kDebugLevelChatty - 3, DEBUG_NAME "  ignored mcast, src=[%#39a],       dst=[%#39a],       if= unicast socket %d\n",
1912                      &senderAddr, &destAddr, inSock );
1913                continue;
1914            }
1915            if( ifindex != inSS->info->scopeID )
1916            {
1917                #if ( DEBUG && MDNS_DEBUG_PACKETS )
1918                char ifname[ IF_NAMESIZE ];
1919                #endif
1920
1921                dpkt( kDebugLevelChatty - 3,
1922                      DEBUG_NAME "  ignored mcast, src=[%#39a]        dst=[%#39a],       if=%8s(%u) -- really for %8s(%u)\n",
1923                      &senderAddr, &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID,
1924                      if_indextoname( ifindex, ifname ), ifindex );
1925                continue;
1926            }
1927
1928            id = inSS->info->ifinfo.InterfaceID;
1929            dpkt( kDebugLevelChatty - 2, DEBUG_NAME "recv %4d bytes, src=[%#39a]:%5hu, dst=[%#39a],       if=%8s(%u) %#p\n",
1930                  n, &senderAddr, mDNSVal16( senderPort ), &destAddr, inSS->info->ifinfo.ifname, inSS->info->scopeID, id );
1931        }
1932        else
1933        {
1934            NetworkInterfaceInfoVxWorks *       i;
1935
1936            // For unicast packets, try to find the matching interface.
1937
1938            for( i = inMDNS->p->interfaceList; i && ( i->scopeID != ifindex ); i = i->next ) {}
1939            if( i ) id = i->ifinfo.InterfaceID;
1940            else id = NULL;
1941        }
1942        mDNSCoreReceive( inMDNS, buf, buf + n, &senderAddr, senderPort, &destAddr, MulticastDNSPort, id );
1943    }
1944}
1945
1946//===========================================================================================================================
1947//	mDNSRecvMsg
1948//===========================================================================================================================
1949
1950mDNSlocal ssize_t
1951mDNSRecvMsg(
1952    SocketRef inSock,
1953    void *      inBuffer,
1954    size_t inBufferSize,
1955    void *      outFrom,
1956    size_t inFromSize,
1957    size_t *    outFromSize,
1958    mDNSAddr *  outDstAddr,
1959    uint32_t *  outIndex )
1960{
1961    struct msghdr msg;
1962    struct iovec iov;
1963    ssize_t n;
1964    char ancillary[ 1024 ];
1965    struct cmsghdr *        cmPtr;
1966    int err;
1967
1968    // Read a packet and any ancillary data. Note: EWOULDBLOCK errors are expected when we've read all pending packets.
1969
1970    iov.iov_base        = (char *) inBuffer;
1971    iov.iov_len         = inBufferSize;
1972    msg.msg_name        = (caddr_t) outFrom;
1973    msg.msg_namelen     = inFromSize;
1974    msg.msg_iov         = &iov;
1975    msg.msg_iovlen      = 1;
1976    msg.msg_control     = (caddr_t) &ancillary;
1977    msg.msg_controllen  = sizeof( ancillary );
1978    msg.msg_flags       = 0;
1979    n = recvmsg( inSock, &msg, 0 );
1980    if( n < 0 )
1981    {
1982        err = errno_compat();
1983        if( err != EWOULDBLOCK ) dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) returned %d, errno %d\n",
1984                                       __ROUTINE__, inSock, n, err );
1985        goto exit;
1986    }
1987    if( msg.msg_controllen < sizeof( struct cmsghdr ) )
1988    {
1989        dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) msg_controllen %d < sizeof( struct cmsghdr ) %u\n",
1990              __ROUTINE__, inSock, msg.msg_controllen, sizeof( struct cmsghdr ) );
1991        n = mStatus_UnknownErr;
1992        goto exit;
1993    }
1994    if( msg.msg_flags & MSG_CTRUNC )
1995    {
1996        dmsg( kDebugLevelWarning, DEBUG_NAME "%s: recvmsg(%d) MSG_CTRUNC (%d recv'd)\n", __ROUTINE__, inSock, n );
1997        n = mStatus_BadFlagsErr;
1998        goto exit;
1999    }
2000    *outFromSize = msg.msg_namelen;
2001
2002    // Parse each option out of the ancillary data.
2003
2004    for( cmPtr = CMSG_FIRSTHDR( &msg ); cmPtr; cmPtr = CMSG_NXTHDR( &msg, cmPtr ) )
2005    {
2006        if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVDSTADDR ) )
2007        {
2008            outDstAddr->type                = mDNSAddrType_IPv4;
2009            outDstAddr->ip.v4.NotAnInteger  = *( (mDNSu32 *) CMSG_DATA( cmPtr ) );
2010        }
2011        else if( ( cmPtr->cmsg_level == IPPROTO_IP ) && ( cmPtr->cmsg_type == IP_RECVIF ) )
2012        {
2013            struct sockaddr_dl *        sdl;
2014
2015            sdl = (struct sockaddr_dl *) CMSG_DATA( cmPtr );
2016            *outIndex = sdl->sdl_index;
2017        }
2018        else if( ( cmPtr->cmsg_level == IPPROTO_IPV6 ) && ( cmPtr->cmsg_type == IPV6_PKTINFO ) )
2019        {
2020            struct in6_pktinfo *        pi6;
2021
2022            pi6 = (struct in6_pktinfo *) CMSG_DATA( cmPtr );
2023            outDstAddr->type    = mDNSAddrType_IPv6;
2024            outDstAddr->ip.v6   = *( (mDNSv6Addr *) &pi6->ipi6_addr );
2025            *outIndex           = pi6->ipi6_ifindex;
2026        }
2027    }
2028
2029exit:
2030    return( n );
2031}
2032
2033#if 0
2034#pragma mark -
2035#pragma mark == Debugging ==
2036#endif
2037
2038#if ( DEBUG && MDNS_DEBUG_SHOW )
2039//===========================================================================================================================
2040//	mDNSShow
2041//===========================================================================================================================
2042
2043void    mDNSShow( void );
2044
2045void    mDNSShow( void )
2046{
2047    NetworkInterfaceInfoVxWorks *       i;
2048    int num;
2049    AuthRecord *                        r;
2050    mDNSs32 utc;
2051
2052    // Globals
2053
2054    dmsg( kDebugLevelMax, "\n-- mDNS globals --\n" );
2055    dmsg( kDebugLevelMax, "    sizeof( mDNS )           = %d\n", (int) sizeof( mDNS ) );
2056    dmsg( kDebugLevelMax, "    sizeof( ResourceRecord ) = %d\n", (int) sizeof( ResourceRecord ) );
2057    dmsg( kDebugLevelMax, "    sizeof( AuthRecord )     = %d\n", (int) sizeof( AuthRecord ) );
2058    dmsg( kDebugLevelMax, "    sizeof( CacheRecord )    = %d\n", (int) sizeof( CacheRecord ) );
2059    dmsg( kDebugLevelMax, "    mDNSPlatformOneSecond    = %ld\n", mDNSPlatformOneSecond );
2060    dmsg( kDebugLevelMax, "    gMDNSTicksToMicro        = %ld\n", gMDNSTicksToMicro );
2061    dmsg( kDebugLevelMax, "    gMDNSPtr                 = %#p\n", gMDNSPtr );
2062    if( !gMDNSPtr )
2063    {
2064        dmsg( kDebugLevelMax, "### mDNS not initialized\n" );
2065        return;
2066    }
2067    dmsg( kDebugLevelMax, "    nicelabel                = \"%#s\"\n", gMDNSPtr->nicelabel.c );
2068    dmsg( kDebugLevelMax, "    hostLabel                = \"%#s\"\n", gMDNSPtr->hostlabel.c );
2069    dmsg( kDebugLevelMax, "    MulticastHostname        = \"%##s\"\n", gMDNSPtr->MulticastHostname.c );
2070    dmsg( kDebugLevelMax, "    HIHardware               = \"%#s\"\n", gMDNSPtr->HIHardware.c );
2071    dmsg( kDebugLevelMax, "    HISoftware               = \"%#s\"\n", gMDNSPtr->HISoftware.c );
2072    dmsg( kDebugLevelMax, "    UnicastPort4/6           = %d/%d\n",
2073          mDNSVal16( gMDNSPtr->UnicastPort4 ), mDNSVal16( gMDNSPtr->UnicastPort6 ) );
2074    dmsg( kDebugLevelMax, "    unicastSS.sockV4/V6      = %d/%d\n",
2075          gMDNSPtr->p->unicastSS.sockV4, gMDNSPtr->p->unicastSS.sockV6 );
2076    dmsg( kDebugLevelMax, "    lock                     = %#p\n", gMDNSPtr->p->lock );
2077    dmsg( kDebugLevelMax, "    initEvent                = %#p\n", gMDNSPtr->p->initEvent );
2078    dmsg( kDebugLevelMax, "    initErr                  = %ld\n", gMDNSPtr->p->initErr );
2079    dmsg( kDebugLevelMax, "    quitEvent                = %#p\n", gMDNSPtr->p->quitEvent );
2080    dmsg( kDebugLevelMax, "    commandPipe              = %d\n", gMDNSPtr->p->commandPipe );
2081    dmsg( kDebugLevelMax, "    taskID                   = %#p\n", gMDNSPtr->p->taskID );
2082    dmsg( kDebugLevelMax, "\n" );
2083
2084    // Interfaces
2085
2086    utc = mDNSPlatformUTC();
2087    dmsg( kDebugLevelMax, "-- mDNS interfaces --\n" );
2088    num = 0;
2089    for( i = gMDNSPtr->p->interfaceList; i; i = i->next )
2090    {
2091        dmsg( kDebugLevelMax, "    interface %2d %8s(%u) [%#39a] %s, sockV4 %2d, sockV6 %2d, Age %d\n",
2092              num, i->ifinfo.ifname, i->scopeID, &i->ifinfo.ip,
2093              i->ifinfo.InterfaceID ? "      REGISTERED" : "*NOT* registered",
2094              i->ss.sockV4, i->ss.sockV6, utc - i->lastSeen );
2095        ++num;
2096    }
2097    dmsg( kDebugLevelMax, "\n" );
2098
2099    // Resource Records
2100
2101    dmsg( kDebugLevelMax, "-- mDNS resource records --\n" );
2102    num = 0;
2103    for( r = gMDNSPtr->ResourceRecords; r; r = r->next )
2104    {
2105        i = (NetworkInterfaceInfoVxWorks *) r->resrec.InterfaceID;
2106        if( r->resrec.rrtype == kDNSType_TXT )
2107        {
2108            RDataBody *         rd;
2109            const mDNSu8 *      txt;
2110            const mDNSu8 *      end;
2111            mDNSu8 size;
2112            int nEntries;
2113
2114            rd = &r->resrec.rdata->u;
2115            dmsg( kDebugLevelMax, "    record %2d: %#p %8s(%u): %4d %##s %s:\n", num, i,
2116                  i ? i->ifinfo.ifname    : "<any>",
2117                  i ? i->scopeID          : 0,
2118                  r->resrec.rdlength, r->resrec.name->c, DNSTypeName( r->resrec.rrtype ) );
2119
2120            nEntries = 0;
2121            txt = rd->txt.c;
2122            end = txt + r->resrec.rdlength;
2123            while( txt < end )
2124            {
2125                size = *txt++;
2126                if( ( txt + size ) > end )
2127                {
2128                    dmsg( kDebugLevelMax, "        ### ERROR! txt length byte too big (%u, %u max)\n", size, end - txt );
2129                    break;
2130                }
2131                dmsg( kDebugLevelMax, "        string %2d (%3d bytes): \"%.*s\"\n", nEntries, size, size, txt );
2132                txt += size;
2133                ++nEntries;
2134            }
2135        }
2136        else
2137        {
2138            dmsg( kDebugLevelMax, "    record %2d: %#p %8s(%u): %s\n", num, i,
2139                  i ? i->ifinfo.ifname    : "<any>",
2140                  i ? i->scopeID          : 0,
2141                  ARDisplayString( gMDNSPtr, r ) );
2142        }
2143        ++num;
2144    }
2145    dmsg( kDebugLevelMax, "\n");
2146}
2147#endif  // DEBUG && MDNS_DEBUG_SHOW
2148