jens@26
|
1 |
//
|
jens@26
|
2 |
// MYPortMapper.m
|
jens@26
|
3 |
// MYNetwork
|
jens@26
|
4 |
//
|
jens@26
|
5 |
// Created by Jens Alfke on 1/4/08.
|
jens@26
|
6 |
// Copyright 2008 Jens Alfke. All rights reserved.
|
jens@26
|
7 |
//
|
jens@26
|
8 |
|
jens@26
|
9 |
#import "MYPortMapper.h"
|
jens@26
|
10 |
#import "IPAddress.h"
|
jens@26
|
11 |
#import "CollectionUtils.h"
|
jens@26
|
12 |
#import "Logging.h"
|
jens@26
|
13 |
#import "ExceptionUtils.h"
|
jens@26
|
14 |
|
jens@26
|
15 |
#import <dns_sd.h>
|
jens@26
|
16 |
|
jens@26
|
17 |
|
jens@26
|
18 |
NSString* const MYPortMapperChangedNotification = @"MYPortMapperChanged";
|
jens@26
|
19 |
|
jens@26
|
20 |
|
jens@26
|
21 |
@interface MYPortMapper ()
|
jens@27
|
22 |
@property (retain) IPAddress* publicAddress, *localAddress; // redeclare as settable
|
jens@26
|
23 |
- (void) priv_updateLocalAddress;
|
jens@26
|
24 |
@end
|
jens@26
|
25 |
|
jens@26
|
26 |
|
jens@26
|
27 |
@implementation MYPortMapper
|
jens@26
|
28 |
|
jens@26
|
29 |
|
jens@26
|
30 |
- (id) initWithLocalPort: (UInt16)localPort
|
jens@26
|
31 |
{
|
jens@26
|
32 |
self = [super init];
|
jens@26
|
33 |
if (self != nil) {
|
jens@26
|
34 |
_localPort = localPort;
|
jens@26
|
35 |
_mapTCP = YES;
|
jens@28
|
36 |
self.continuous = YES;
|
jens@26
|
37 |
[self priv_updateLocalAddress];
|
jens@26
|
38 |
}
|
jens@26
|
39 |
return self;
|
jens@26
|
40 |
}
|
jens@26
|
41 |
|
jens@26
|
42 |
- (id) initWithNullMapping
|
jens@26
|
43 |
{
|
jens@26
|
44 |
// A PortMapper with no port or protocols will cause the DNSService to look up
|
jens@26
|
45 |
// our public address without creating a mapping.
|
jens@26
|
46 |
if ([self initWithLocalPort: 0]) {
|
jens@26
|
47 |
_mapTCP = _mapUDP = NO;
|
jens@26
|
48 |
}
|
jens@26
|
49 |
return self;
|
jens@26
|
50 |
}
|
jens@26
|
51 |
|
jens@26
|
52 |
|
jens@26
|
53 |
- (void) dealloc
|
jens@26
|
54 |
{
|
jens@26
|
55 |
[_publicAddress release];
|
jens@26
|
56 |
[_localAddress release];
|
jens@26
|
57 |
[super dealloc];
|
jens@26
|
58 |
}
|
jens@26
|
59 |
|
jens@26
|
60 |
|
jens@26
|
61 |
@synthesize localAddress=_localAddress, publicAddress=_publicAddress,
|
jens@26
|
62 |
mapTCP=_mapTCP, mapUDP=_mapUDP,
|
jens@26
|
63 |
desiredPublicPort=_desiredPublicPort;
|
jens@26
|
64 |
|
jens@26
|
65 |
|
jens@26
|
66 |
- (BOOL) isMapped
|
jens@26
|
67 |
{
|
jens@26
|
68 |
return ! $equal(_publicAddress,_localAddress);
|
jens@26
|
69 |
}
|
jens@26
|
70 |
|
jens@26
|
71 |
- (void) priv_updateLocalAddress
|
jens@26
|
72 |
{
|
jens@26
|
73 |
IPAddress *localAddress = [IPAddress localAddressWithPort: _localPort];
|
jens@26
|
74 |
if (!$equal(localAddress,_localAddress))
|
jens@26
|
75 |
self.localAddress = localAddress;
|
jens@26
|
76 |
}
|
jens@26
|
77 |
|
jens@26
|
78 |
|
jens@26
|
79 |
static IPAddress* makeIPAddr( UInt32 rawAddr, UInt16 port ) {
|
jens@26
|
80 |
if (rawAddr)
|
jens@26
|
81 |
return [[[IPAddress alloc] initWithIPv4: rawAddr port: port] autorelease];
|
jens@26
|
82 |
else
|
jens@26
|
83 |
return nil;
|
jens@26
|
84 |
}
|
jens@26
|
85 |
|
jens@26
|
86 |
/** Called whenever the port mapping changes (see comment for callback, below.) */
|
jens@26
|
87 |
- (void) priv_portMapStatus: (DNSServiceErrorType)errorCode
|
jens@26
|
88 |
publicAddress: (UInt32)rawPublicAddress
|
jens@26
|
89 |
publicPort: (UInt16)publicPort
|
jens@26
|
90 |
{
|
jens@26
|
91 |
if( errorCode==kDNSServiceErr_NoError ) {
|
jens@26
|
92 |
if( rawPublicAddress==0 || (publicPort==0 && (_mapTCP || _mapUDP)) ) {
|
jens@28
|
93 |
LogTo(PortMapper,@"%@: No port-map available", self);
|
jens@26
|
94 |
errorCode = kDNSServiceErr_NATPortMappingUnsupported;
|
jens@26
|
95 |
}
|
jens@26
|
96 |
}
|
jens@26
|
97 |
|
jens@26
|
98 |
[self priv_updateLocalAddress];
|
jens@26
|
99 |
IPAddress *publicAddress = makeIPAddr(rawPublicAddress,publicPort);
|
jens@26
|
100 |
if (!$equal(publicAddress,_publicAddress))
|
jens@26
|
101 |
self.publicAddress = publicAddress;
|
jens@26
|
102 |
|
jens@26
|
103 |
if( ! errorCode ) {
|
jens@28
|
104 |
LogTo(PortMapper,@"%@: Public addr is %@ (mapped=%i)",
|
jens@28
|
105 |
self, self.publicAddress, self.isMapped);
|
jens@26
|
106 |
}
|
jens@31
|
107 |
|
jens@31
|
108 |
[self gotResponse: errorCode];
|
jens@26
|
109 |
[[NSNotificationCenter defaultCenter] postNotificationName: MYPortMapperChangedNotification
|
jens@26
|
110 |
object: self];
|
jens@26
|
111 |
}
|
jens@26
|
112 |
|
jens@26
|
113 |
|
jens@26
|
114 |
/** Asynchronous callback from DNSServiceNATPortMappingCreate.
|
jens@26
|
115 |
This is invoked whenever the status of the port mapping changes.
|
jens@26
|
116 |
All it does is dispatch to the object's priv_portMapStatus:publicAddress:publicPort: method. */
|
jens@26
|
117 |
static void portMapCallback (
|
jens@26
|
118 |
DNSServiceRef sdRef,
|
jens@26
|
119 |
DNSServiceFlags flags,
|
jens@26
|
120 |
uint32_t interfaceIndex,
|
jens@26
|
121 |
DNSServiceErrorType errorCode,
|
jens@26
|
122 |
uint32_t publicAddress, /* four byte IPv4 address in network byte order */
|
jens@26
|
123 |
DNSServiceProtocol protocol,
|
jens@26
|
124 |
uint16_t privatePort,
|
jens@26
|
125 |
uint16_t publicPort, /* may be different than the requested port */
|
jens@26
|
126 |
uint32_t ttl, /* may be different than the requested ttl */
|
jens@26
|
127 |
void *context
|
jens@26
|
128 |
)
|
jens@26
|
129 |
{
|
jens@26
|
130 |
@try{
|
jens@26
|
131 |
[(MYPortMapper*)context priv_portMapStatus: errorCode
|
jens@26
|
132 |
publicAddress: publicAddress
|
jens@26
|
133 |
publicPort: ntohs(publicPort)]; // port #s in network byte order!
|
jens@26
|
134 |
}catchAndReport(@"PortMapper");
|
jens@26
|
135 |
}
|
jens@26
|
136 |
|
jens@26
|
137 |
|
jens@31
|
138 |
- (DNSServiceErrorType) createServiceRef: (DNSServiceRef*)sdRefPtr {
|
jens@26
|
139 |
DNSServiceProtocol protocols = 0;
|
jens@26
|
140 |
if( _mapTCP ) protocols |= kDNSServiceProtocol_TCP;
|
jens@26
|
141 |
if( _mapUDP ) protocols |= kDNSServiceProtocol_UDP;
|
jens@31
|
142 |
return DNSServiceNATPortMappingCreate(sdRefPtr,
|
jens@31
|
143 |
kDNSServiceFlagsShareConnection,
|
jens@31
|
144 |
0 /*interfaceIndex*/,
|
jens@31
|
145 |
protocols,
|
jens@31
|
146 |
htons(_localPort),
|
jens@31
|
147 |
htons(_desiredPublicPort),
|
jens@31
|
148 |
0 /*ttl*/,
|
jens@31
|
149 |
&portMapCallback,
|
jens@31
|
150 |
self);
|
jens@27
|
151 |
}
|
jens@27
|
152 |
|
jens@27
|
153 |
|
jens@26
|
154 |
- (BOOL) waitTillOpened
|
jens@26
|
155 |
{
|
jens@27
|
156 |
if( ! self.serviceRef )
|
jens@28
|
157 |
if( ! [self start] )
|
jens@26
|
158 |
return NO;
|
jens@28
|
159 |
[self waitForReply];
|
jens@27
|
160 |
return (self.error==0);
|
jens@26
|
161 |
}
|
jens@26
|
162 |
|
jens@26
|
163 |
|
jens@26
|
164 |
+ (IPAddress*) findPublicAddress
|
jens@26
|
165 |
{
|
jens@26
|
166 |
IPAddress *addr = nil;
|
jens@26
|
167 |
MYPortMapper *mapper = [[self alloc] initWithNullMapping];
|
jens@28
|
168 |
mapper.continuous = NO;
|
jens@26
|
169 |
if( [mapper waitTillOpened] )
|
jens@26
|
170 |
addr = [mapper.publicAddress retain];
|
jens@28
|
171 |
[mapper stop];
|
jens@26
|
172 |
[mapper release];
|
jens@26
|
173 |
return [addr autorelease];
|
jens@26
|
174 |
}
|
jens@26
|
175 |
|
jens@26
|
176 |
|
jens@26
|
177 |
@end
|
jens@26
|
178 |
|
jens@26
|
179 |
|
jens@26
|
180 |
/*
|
jens@26
|
181 |
Copyright (c) 2008-2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
|
jens@26
|
182 |
|
jens@26
|
183 |
Redistribution and use in source and binary forms, with or without modification, are permitted
|
jens@26
|
184 |
provided that the following conditions are met:
|
jens@26
|
185 |
|
jens@26
|
186 |
* Redistributions of source code must retain the above copyright notice, this list of conditions
|
jens@26
|
187 |
and the following disclaimer.
|
jens@26
|
188 |
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions
|
jens@26
|
189 |
and the following disclaimer in the documentation and/or other materials provided with the
|
jens@26
|
190 |
distribution.
|
jens@26
|
191 |
|
jens@26
|
192 |
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
jens@26
|
193 |
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
jens@26
|
194 |
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
|
jens@26
|
195 |
BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
jens@26
|
196 |
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
jens@26
|
197 |
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
jens@26
|
198 |
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
jens@26
|
199 |
THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
jens@26
|
200 |
*/
|