diff options
Diffstat (limited to 'mDNSResponder/mDNSMacOSX/P2PPacketFilter.c')
-rw-r--r-- | mDNSResponder/mDNSMacOSX/P2PPacketFilter.c | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c new file mode 100644 index 00000000..1ab8a02f --- /dev/null +++ b/mDNSResponder/mDNSMacOSX/P2PPacketFilter.c @@ -0,0 +1,298 @@ +/* + * + * Copyright (c) 2011 Apple Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <net/if.h> +#include <System/net/pfvar.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <unistd.h> +#include <AssertMacros.h> +#include "P2PPacketFilter.h" + +#define AIRDROP_ANCHOR_PATH "com.apple/200.AirDrop" +#define MDNS_ANCHOR_NAME "Bonjour" +#define MDNS_ANCHOR_PATH AIRDROP_ANCHOR_PATH "/" MDNS_ANCHOR_NAME + +#define PF_DEV_PATH "/dev/pf" +#define BONJOUR_PORT 5353 + +static int openPFDevice( int * outFD ) +{ + int err; + int fd = open( PF_DEV_PATH, O_RDWR ); + + if( fd >= 0 ) + { + err = 0; + *outFD = fd; + } + else + { + err = errno; + } + + return err; +} + +static int getTicket( int devFD, u_int32_t * outTicket, char * anchorPath ) +{ + struct pfioc_trans_e trans_e; + + trans_e.rs_num = PF_RULESET_FILTER; + strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); + + struct pfioc_trans trans; + + trans.size = 1; + trans.esize = sizeof( trans_e ); + trans.array = &trans_e; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCXBEGIN, &trans ); + if( ioctlError ) + { + result = errno; + } + else + { + result = 0; + *outTicket = trans_e.ticket; + } + + return result; +} + +static int commitChange( int devFD, u_int32_t ticket, char * anchorPath ) +{ + struct pfioc_trans_e trans_e; + + trans_e.rs_num = PF_RULESET_FILTER; + strlcpy( trans_e.anchor, anchorPath, sizeof( trans_e.anchor ) ); + trans_e.ticket = ticket; + + struct pfioc_trans trans; + + trans.size = 1; + trans.esize = sizeof( trans_e ); + trans.array = &trans_e; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCXCOMMIT, &trans ); + if( ioctlError ) + result = errno; + else + result = 0; + + return result; +} + +static int getPoolTicket( int devFD, u_int32_t * outPoolTicket ) +{ + struct pfioc_pooladdr pp; + + int result, ioctlError; + + ioctlError = ioctl( devFD, DIOCBEGINADDRS, &pp ); + if( ioctlError ) + { + result = errno; + } + else + { + result = 0; + *outPoolTicket = pp.ticket; + } + + return result; +} + +static int addRule( int devFD, struct pfioc_rule * pr ) +{ + int result, ioctlResult; + + ioctlResult = ioctl( devFD, DIOCADDRULE, pr ); + if( ioctlResult ) + result = errno; + else + result = 0; + + return result; +} + +static void initRuleHeader( struct pfioc_rule * pr, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + pr->action = PF_CHANGE_NONE; + pr->ticket = ticket; + pr->pool_ticket = poolTicket; + strlcpy( pr->anchor, anchorPath, sizeof( pr->anchor ) ); +} + +// allow inbound traffice on the Bonjour port (5353) +static void initBonjourRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + pr->rule.dst.xport.range.port[0] = htons( BONJOUR_PORT ); + pr->rule.dst.xport.range.op = PF_OP_EQ; + + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_IN; + pr->rule.keep_state = 1; + pr->rule.af = AF_INET6; + pr->rule.proto = IPPROTO_UDP; + pr->rule.extfilter = PF_EXTFILTER_APD; +} + +// allow outbound TCP connections and return traffic for those connections +static void initOutboundTCPRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_OUT; + pr->rule.keep_state = 1; + pr->rule.proto = IPPROTO_TCP; +} + +// allow inbound traffic on the specified port and protocol +static void initPortRule( struct pfioc_rule * pr, + const char * interfaceName, + u_int32_t ticket, + u_int32_t poolTicket, + char * anchorPath, + u_int16_t port, + u_int16_t protocol ) +{ + memset( pr, 0, sizeof( *pr ) ); + + // Header + initRuleHeader( pr, ticket, poolTicket, anchorPath ); + + // Rule + // mDNSResponder passes the port in Network Byte Order, so htons(port) is not required + pr->rule.dst.xport.range.port[0] = port; + pr->rule.dst.xport.range.op = PF_OP_EQ; + + strlcpy( pr->rule.ifname, interfaceName, sizeof( pr->rule.ifname ) ); + + pr->rule.action = PF_PASS; + pr->rule.direction = PF_IN; + pr->rule.keep_state = 1; + pr->rule.af = AF_INET6; + pr->rule.proto = protocol; + pr->rule.extfilter = PF_EXTFILTER_APD; +} + +// allow inbound traffic on the Bonjour port (5353) and the specified port and protocol sets +int P2PPacketFilterAddBonjourRuleSet(const char * interfaceName, u_int32_t count, pfArray_t portArray, pfArray_t protocolArray ) +{ + int result; + u_int32_t i, ticket, poolTicket; + int devFD = -1; + char * anchorPath = MDNS_ANCHOR_PATH; + + result = openPFDevice( &devFD ); + require( result == 0, exit ); + + result = getTicket( devFD, &ticket, anchorPath ); + require( result == 0, exit ); + + result = getPoolTicket( devFD, &poolTicket ); + require( result == 0, exit ); + + struct pfioc_rule pr; + + // allow inbound Bonjour traffice to port 5353 + initBonjourRule( &pr, interfaceName, ticket, poolTicket, anchorPath); + + result = addRule( devFD, &pr ); + require( result == 0, exit ); + + // open inbound port for each service + for (i = 0; i < count; i++) { + initPortRule( &pr, interfaceName, ticket, poolTicket, anchorPath, portArray[i], protocolArray[i] ); + result = addRule( devFD, &pr ); + require( result == 0, exit ); + } + + // allow outbound TCP connections and return traffic for those connections + initOutboundTCPRule( &pr, interfaceName, ticket, poolTicket, anchorPath); + + result = addRule( devFD, &pr ); + require( result == 0, exit ); + + result = commitChange( devFD, ticket, anchorPath ); + require( result == 0, exit ); + +exit: + + if( devFD >= 0 ) + close( devFD ); + + return result; +} + +int P2PPacketFilterClearBonjourRules() +{ + int result; + int pfDev = -1; + u_int32_t ticket; + char * anchorPath = MDNS_ANCHOR_PATH; + + result = openPFDevice( &pfDev ); + require( result == 0, exit ); + + result = getTicket( pfDev, &ticket, anchorPath ); + require( result == 0, exit ); + + result = commitChange( pfDev, ticket, anchorPath ); + +exit: + + if( pfDev >= 0 ) + close( pfDev ); + + return result; +} + |