mirror of
https://github.com/swisspol/GCDWebServer.git
synced 2024-10-06 15:52:14 +08:00
Added support for NAT port mapping
This commit is contained in:
parent
3c33e9f056
commit
e70a3338a5
@ -90,14 +90,26 @@ extern NSString* const GCDWebServerOption_BonjourName;
|
|||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BonjourType;
|
extern NSString* const GCDWebServerOption_BonjourType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a port mapping in the NAT gateway (NSNumber / BOOL).
|
||||||
|
*
|
||||||
|
* This uses the DNSService API under the hood which supports IPv4 mappings only.
|
||||||
|
*
|
||||||
|
* The default value is NO.
|
||||||
|
*
|
||||||
|
* @warning The external port set up by the NAT gateway may be different than
|
||||||
|
* the one used by the GCDWebServer.
|
||||||
|
*/
|
||||||
|
extern NSString* const GCDWebServerOption_RequestNATPortMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
* Only accept HTTP requests coming from localhost i.e. not from the outside
|
||||||
* network (NSNumber / BOOL).
|
* network (NSNumber / BOOL).
|
||||||
*
|
*
|
||||||
* The default value is NO.
|
* The default value is NO.
|
||||||
*
|
*
|
||||||
* @warning Bonjour should be disabled if using this option since the server
|
* @warning Bonjour and NAT port mapping should be disabled if using this option
|
||||||
* will not be reachable from the outside network anyway.
|
* since the server will not be reachable from the outside network anyway.
|
||||||
*/
|
*/
|
||||||
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
extern NSString* const GCDWebServerOption_BindToLocalhost;
|
||||||
|
|
||||||
@ -213,9 +225,20 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
/**
|
/**
|
||||||
* This method is called after the Bonjour registration for the server has
|
* This method is called after the Bonjour registration for the server has
|
||||||
* successfully completed.
|
* successfully completed.
|
||||||
|
*
|
||||||
|
* Use the "bonjourServerURL" property to retrieve the Bonjour address of the
|
||||||
|
* server.
|
||||||
*/
|
*/
|
||||||
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
- (void)webServerDidCompleteBonjourRegistration:(GCDWebServer*)server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the NAT port mapping has been updated.
|
||||||
|
*
|
||||||
|
* Use the "publicServerURL" property to retrieve the public address of the
|
||||||
|
* server.
|
||||||
|
*/
|
||||||
|
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called when the first GCDWebServerConnection is opened by the
|
* This method is called when the first GCDWebServerConnection is opened by the
|
||||||
* server to serve a series of HTTP requests.
|
* server to serve a series of HTTP requests.
|
||||||
@ -362,6 +385,14 @@ extern NSString* const GCDWebServerAuthenticationMethod_DigestAccess;
|
|||||||
*/
|
*/
|
||||||
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
@property(nonatomic, readonly) NSURL* bonjourServerURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the server's public URL.
|
||||||
|
*
|
||||||
|
* @warning This property is only valid if the server is running and NAT port
|
||||||
|
* mapping is active.
|
||||||
|
*/
|
||||||
|
@property(nonatomic, readonly) NSURL* publicServerURL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
* Starts the server on port 8080 (OS X & iOS Simulator) or port 80 (iOS)
|
||||||
* using the default Bonjour name.
|
* using the default Bonjour name.
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#import <netinet/in.h>
|
#import <netinet/in.h>
|
||||||
|
#import <dns_sd.h>
|
||||||
|
|
||||||
#import "GCDWebServerPrivate.h"
|
#import "GCDWebServerPrivate.h"
|
||||||
|
|
||||||
@ -50,6 +51,7 @@
|
|||||||
NSString* const GCDWebServerOption_Port = @"Port";
|
NSString* const GCDWebServerOption_Port = @"Port";
|
||||||
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
|
NSString* const GCDWebServerOption_BonjourName = @"BonjourName";
|
||||||
NSString* const GCDWebServerOption_BonjourType = @"BonjourType";
|
NSString* const GCDWebServerOption_BonjourType = @"BonjourType";
|
||||||
|
NSString* const GCDWebServerOption_RequestNATPortMapping = @"RequestNATPortMapping";
|
||||||
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
|
NSString* const GCDWebServerOption_BindToLocalhost = @"BindToLocalhost";
|
||||||
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
NSString* const GCDWebServerOption_MaxPendingConnections = @"MaxPendingConnections";
|
||||||
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
NSString* const GCDWebServerOption_ServerName = @"ServerName";
|
||||||
@ -171,6 +173,11 @@ static void _ExecuteMainThreadRunLoopSources() {
|
|||||||
dispatch_source_t _source6;
|
dispatch_source_t _source6;
|
||||||
CFNetServiceRef _registrationService;
|
CFNetServiceRef _registrationService;
|
||||||
CFNetServiceRef _resolutionService;
|
CFNetServiceRef _resolutionService;
|
||||||
|
DNSServiceRef _dnsService;
|
||||||
|
CFSocketRef _dnsSocket;
|
||||||
|
CFRunLoopSourceRef _dnsSource;
|
||||||
|
NSString* _dnsAddress;
|
||||||
|
NSUInteger _dnsPort;
|
||||||
BOOL _bindToLocalhost;
|
BOOL _bindToLocalhost;
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
BOOL _suspendInBackground;
|
BOOL _suspendInBackground;
|
||||||
@ -383,7 +390,7 @@ static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* e
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||||
GWS_LOG_INFO(@"%@ now reachable at %@", [server class], server.bonjourServerURL);
|
GWS_LOG_INFO(@"%@ now locally reachable at %@", [server class], server.bonjourServerURL);
|
||||||
if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) {
|
if ([server.delegate respondsToSelector:@selector(webServerDidCompleteBonjourRegistration:)]) {
|
||||||
[server.delegate webServerDidCompleteBonjourRegistration:server];
|
[server.delegate webServerDidCompleteBonjourRegistration:server];
|
||||||
}
|
}
|
||||||
@ -391,6 +398,41 @@ static void _NetServiceResolveCallBack(CFNetServiceRef service, CFStreamError* e
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _DNSServiceCallBack(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, uint32_t externalAddress, DNSServiceProtocol protocol, uint16_t internalPort, uint16_t externalPort, uint32_t ttl, void* context) {
|
||||||
|
GWS_DCHECK([NSThread isMainThread]);
|
||||||
|
@autoreleasepool {
|
||||||
|
GCDWebServer* server = (__bridge GCDWebServer*)context;
|
||||||
|
if ((errorCode == kDNSServiceErr_NoError) || (errorCode == kDNSServiceErr_DoubleNAT)) {
|
||||||
|
struct sockaddr_in addr4;
|
||||||
|
bzero(&addr4, sizeof(addr4));
|
||||||
|
addr4.sin_len = sizeof(addr4);
|
||||||
|
addr4.sin_family = AF_INET;
|
||||||
|
addr4.sin_addr.s_addr = externalAddress; // Already in network byte order
|
||||||
|
server->_dnsAddress = GCDWebServerStringFromSockAddr((const struct sockaddr*)&addr4, NO);
|
||||||
|
server->_dnsPort = ntohs(externalPort);
|
||||||
|
GWS_LOG_INFO(@"%@ now publicly reachable at %@", [server class], server.publicServerURL);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"DNS service error %i", errorCode);
|
||||||
|
server->_dnsAddress = nil;
|
||||||
|
server->_dnsPort = 0;
|
||||||
|
}
|
||||||
|
if ([server.delegate respondsToSelector:@selector(webServerDidUpdateNATPortMapping:)]) {
|
||||||
|
[server.delegate webServerDidUpdateNATPortMapping:server];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _SocketCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info) {
|
||||||
|
GWS_DCHECK([NSThread isMainThread]);
|
||||||
|
@autoreleasepool {
|
||||||
|
GCDWebServer* server = (__bridge GCDWebServer*)info;
|
||||||
|
DNSServiceErrorType status = DNSServiceProcessResult(server->_dnsService);
|
||||||
|
if (status != kDNSServiceErr_NoError) {
|
||||||
|
GWS_LOG_ERROR(@"DNS service error %i", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
|
static inline id _GetOption(NSDictionary* options, NSString* key, id defaultValue) {
|
||||||
id value = [options objectForKey:key];
|
id value = [options objectForKey:key];
|
||||||
return value ? value : defaultValue;
|
return value ? value : defaultValue;
|
||||||
@ -580,6 +622,29 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ([_GetOption(_options, GCDWebServerOption_RequestNATPortMapping, @NO) boolValue]) {
|
||||||
|
DNSServiceErrorType status = DNSServiceNATPortMappingCreate(&_dnsService, 0, 0, kDNSServiceProtocol_TCP, htons(port), htons(port), 0, _DNSServiceCallBack, (__bridge void*)self);
|
||||||
|
if (status == kDNSServiceErr_NoError) {
|
||||||
|
CFSocketContext context = {0, (__bridge void*)self, NULL, NULL, NULL};
|
||||||
|
_dnsSocket = CFSocketCreateWithNative(kCFAllocatorDefault, DNSServiceRefSockFD(_dnsService), kCFSocketReadCallBack, _SocketCallBack, &context);
|
||||||
|
if (_dnsSocket) {
|
||||||
|
CFSocketSetSocketFlags(_dnsSocket, CFSocketGetSocketFlags(_dnsSocket) & ~kCFSocketCloseOnInvalidate);
|
||||||
|
_dnsSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _dnsSocket, 0);
|
||||||
|
if (_dnsSource) {
|
||||||
|
CFRunLoopAddSource(CFRunLoopGetMain(), _dnsSource, kCFRunLoopCommonModes);
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating CFRunLoopSource");
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating CFSocket");
|
||||||
|
GWS_DNOT_REACHED();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
GWS_LOG_ERROR(@"Failed creating NAT port mapping (%i)", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dispatch_resume(_source4);
|
dispatch_resume(_source4);
|
||||||
dispatch_resume(_source6);
|
dispatch_resume(_source6);
|
||||||
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
GWS_LOG_INFO(@"%@ started on port %i and reachable at %@", [self class], (int)_port, self.serverURL);
|
||||||
@ -595,6 +660,22 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
- (void)_stop {
|
- (void)_stop {
|
||||||
GWS_DCHECK(_source4 != NULL);
|
GWS_DCHECK(_source4 != NULL);
|
||||||
|
|
||||||
|
if (_dnsService) {
|
||||||
|
_dnsAddress = nil;
|
||||||
|
_dnsPort = 0;
|
||||||
|
if (_dnsSource) {
|
||||||
|
CFRunLoopSourceInvalidate(_dnsSource);
|
||||||
|
CFRelease(_dnsSource);
|
||||||
|
_dnsSource = NULL;
|
||||||
|
}
|
||||||
|
if (_dnsSocket) {
|
||||||
|
CFRelease(_dnsSocket);
|
||||||
|
_dnsSocket = NULL;
|
||||||
|
}
|
||||||
|
DNSServiceRefDeallocate(_dnsService);
|
||||||
|
_dnsService = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (_registrationService) {
|
if (_registrationService) {
|
||||||
if (_resolutionService) {
|
if (_resolutionService) {
|
||||||
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
CFNetServiceUnscheduleFromRunLoop(_resolutionService, CFRunLoopGetMain(), kCFRunLoopCommonModes);
|
||||||
@ -746,6 +827,17 @@ static inline NSString* _EncodeBase64(NSString* string) {
|
|||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSURL*)publicServerURL {
|
||||||
|
if (_source4 && _dnsService && _dnsAddress && _dnsPort) {
|
||||||
|
if (_dnsPort != 80) {
|
||||||
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%i/", _dnsAddress, (int)_dnsPort]];
|
||||||
|
} else {
|
||||||
|
return [NSURL URLWithString:[NSString stringWithFormat:@"http://%@/", _dnsAddress]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
- (BOOL)start {
|
- (BOOL)start {
|
||||||
return [self startWithPort:kDefaultPort bonjourName:@""];
|
return [self startWithPort:kDefaultPort bonjourName:@""];
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,10 @@ typedef enum {
|
|||||||
[self _logDelegateCall:_cmd];
|
[self _logDelegateCall:_cmd];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)webServerDidUpdateNATPortMapping:(GCDWebServer*)server {
|
||||||
|
[self _logDelegateCall:_cmd];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)webServerDidConnect:(GCDWebServer*)server {
|
- (void)webServerDidConnect:(GCDWebServer*)server {
|
||||||
[self _logDelegateCall:_cmd];
|
[self _logDelegateCall:_cmd];
|
||||||
}
|
}
|
||||||
@ -142,6 +146,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
NSString* authenticationUser = nil;
|
NSString* authenticationUser = nil;
|
||||||
NSString* authenticationPassword = nil;
|
NSString* authenticationPassword = nil;
|
||||||
BOOL bindToLocalhost = NO;
|
BOOL bindToLocalhost = NO;
|
||||||
|
BOOL requestNATPortMapping = NO;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
fprintf(stdout, "Usage: %s [-mode webServer | htmlPage | htmlForm | htmlFileUpload | webDAV | webUploader | streamingResponse | asyncResponse] [-record] [-root directory] [-tests directory] [-authenticationMethod Basic | Digest] [-authenticationRealm realm] [-authenticationUser user] [-authenticationPassword password] [--localhost]\n\n", basename((char*)argv[0]));
|
||||||
@ -191,6 +196,8 @@ int main(int argc, const char* argv[]) {
|
|||||||
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
|
authenticationPassword = [NSString stringWithUTF8String:argv[i]];
|
||||||
} else if (!strcmp(argv[i], "--localhost")) {
|
} else if (!strcmp(argv[i], "--localhost")) {
|
||||||
bindToLocalhost = YES;
|
bindToLocalhost = YES;
|
||||||
|
} else if (!strcmp(argv[i], "--nat")) {
|
||||||
|
requestNATPortMapping = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,6 +419,7 @@ int main(int argc, const char* argv[]) {
|
|||||||
fprintf(stdout, "\n");
|
fprintf(stdout, "\n");
|
||||||
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
NSMutableDictionary* options = [NSMutableDictionary dictionary];
|
||||||
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
[options setObject:@8080 forKey:GCDWebServerOption_Port];
|
||||||
|
[options setObject:@(requestNATPortMapping) forKey:GCDWebServerOption_RequestNATPortMapping];
|
||||||
[options setObject:@(bindToLocalhost) forKey:GCDWebServerOption_BindToLocalhost];
|
[options setObject:@(bindToLocalhost) forKey:GCDWebServerOption_BindToLocalhost];
|
||||||
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
[options setObject:@"" forKey:GCDWebServerOption_BonjourName];
|
||||||
if (authenticationUser && authenticationPassword) {
|
if (authenticationUser && authenticationPassword) {
|
||||||
|
Loading…
Reference in New Issue
Block a user