jens@26: // jens@26: // MYPortMapper.m jens@26: // MYNetwork jens@26: // jens@26: // Created by Jens Alfke on 1/4/08. jens@26: // Copyright 2008 Jens Alfke. All rights reserved. jens@26: // jens@26: jens@27: #import "MYDNSService.h" jens@26: @class IPAddress; jens@26: jens@26: jens@26: /* MYPortMapper attempts to make a particular network port on this computer publicly reachable jens@26: for incoming connections, by "opening a hole" through a Network Address Translator jens@26: (NAT) or firewall that may be in between the computer and the public Internet. jens@26: jens@26: The port mapping may fail if: jens@26: * the NAT/router/firewall does not support either the UPnP or NAT-PMP protocols; jens@26: * the device doesn't implement the protocols correctly (this happens); jens@26: * the network administrator has disabled port-mapping; jens@26: * there is a second layer of NAT/firewall (this happens in some ISP networks.) jens@26: jens@26: The PortMapper is asynchronous. It will take a nonzero amount of time to set up the jens@26: mapping, and the mapping may change in the future as the network configuration changes. jens@26: To be informed of changes, either use key-value observing to watch the "error" and jens@26: "publicAddress" properties, or observe the MYPortMapperChangedNotification. jens@26: jens@26: Typical usage is to: jens@26: * Start a network service that listens for incoming connections on a port jens@26: * Open a MYPortMapper jens@26: * When the MYPortMapper reports the public address and port of the mapping, you somehow jens@26: notify other peers of that address and port, so they can connect to you. jens@26: * When the MYPortMapper reports changes, you (somehow) notify peers of the changes. jens@26: * When closing the network service, close the MYPortMapper object too. jens@26: */ jens@27: @interface MYPortMapper : MYDNSService jens@26: { jens@27: @private jens@26: UInt16 _localPort, _desiredPublicPort; jens@26: BOOL _mapTCP, _mapUDP; jens@26: IPAddress *_publicAddress, *_localAddress; jens@26: } jens@26: jens@26: /** Initializes a PortMapper that will map the given local (private) port. jens@26: By default it will map TCP and not UDP, and will not suggest a desired public port, jens@26: but this can be configured by setting properties before opening the PortMapper. */ jens@26: - (id) initWithLocalPort: (UInt16)localPort; jens@26: jens@26: /** Initializes a PortMapper that will not map any ports. jens@26: This is useful if you just want to find out your public IP address. jens@26: (For a simplified, but synchronous, convenience method for this, see jens@26: +findPublicAddress.) */ jens@26: - (id) initWithNullMapping; jens@26: jens@26: /** Should the TCP or UDP port, or both, be mapped? By default, TCP only. jens@26: These properties have no effect if changed while the PortMapper is open. */ jens@26: @property BOOL mapTCP, mapUDP; jens@26: jens@26: /** You can set this to the public port number you'd like to get. jens@26: It defaults to 0, which means "no preference". jens@26: This property has no effect if changed while the PortMapper is open. */ jens@26: @property UInt16 desiredPublicPort; jens@26: jens@26: /** Blocks till the PortMapper finishes opening. Returns YES if it opened, NO on error. jens@26: It's not usually a good idea to use this, as it will lock up your application jens@26: until a response arrives from the NAT. Listen for asynchronous notifications instead. jens@26: If called when the PortMapper is closed, it will call -open for you. jens@26: If called when it's already open, it just returns YES. */ jens@26: - (BOOL) waitTillOpened; jens@26: jens@26: /** The known public IPv4 address/port, once it's been determined. jens@26: This property is KV observable. */ jens@26: @property (readonly,retain) IPAddress* publicAddress; jens@26: jens@26: /** The current local address/port, as of the time the port mapping was last updated. jens@26: The address part is of the main interface; the port is the specified local port. jens@26: This property is KV observable. */ jens@26: @property (readonly,retain) IPAddress* localAddress; jens@26: jens@26: /** Returns YES if a non-null port mapping is in effect: jens@26: that is, if the public address differs from the local one. */ jens@26: @property (readonly) BOOL isMapped; jens@26: jens@26: jens@26: // UTILITY CLASS METHOD: jens@26: jens@26: /** Determine the main interface's public IP address, without mapping any ports. jens@26: This method internally calls -waitTillOpened, so it may take a nontrivial amount jens@26: of time (and will crank the runloop while it waits.) jens@26: If you want to do this asynchronously, you should instead create a new jens@26: MYPortMapper instance using -initWithNullMapping. */ jens@26: + (IPAddress*) findPublicAddress; jens@26: jens@26: @end jens@26: jens@26: jens@26: /** This notification is posted asynchronously when the status of a PortMapper jens@26: (its error, publicAddress or publicPort) changes. */ jens@26: extern NSString* const MYPortMapperChangedNotification;