+++ /dev/null
-INCLUDES = -I$(top_srcdir)/src/include
-
-if MINGW
- WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
-endif
-
-if USE_COVERAGE
- AM_CFLAGS = -fprofile-arcs -ftest-coverage
-endif
-
-lib_LTLIBRARIES = libgnunetupnp.la
-
-libgnunetupnp_la_SOURCES = \
- upnp.c upnp.h \
- upnp_ip.c upnp_ip.h \
- upnp_util.c upnp_util.h \
- upnp_xmlnode.c upnp_xmlnode.h
-libgnunetupnp_la_CFLAGS = \
- -I$(top_scrdir)/include \
- @LIBCURL_CPPFLAGS@ @XML_CPPFLAGS@
-libgnunetupnp_la_LIBADD = \
- $(top_builddir)/src/util/libgnunetutil.la \
- $(GN_LIBINTL) @EXT_LIBS@ @XML_LIBS@ @LIBCURL@
-libgnunetupnp_la_LDFLAGS = \
- $(GN_LIB_LDFLAGS) $(WINFLAGS) \
- -version-info 0:0:0
-
-
-bin_PROGRAMS = \
- gnunet-upnp \
- gnunet-service-upnp
-
-gnunet_upnp_SOURCES = \
- gnunet-upnp.c
-gnunet_upnp_LDADD = \
- $(top_builddir)/src/upnp/libgnunetupnp.la \
- $(top_builddir)/src/util/libgnunetutil.la \
- $(GN_LIBINTL)
-
-gnunet_service_upnp_SOURCES = \
- gnunet-service-upnp.c
-gnunet_service_upnp_LDADD = \
- $(top_builddir)/src/util/libgnunetutil.la \
- $(GN_LIBINTL)
-
-
-check_PROGRAMS = \
- test_upnp
-
-if !DISABLE_TEST_RUN
-TESTS = $(check_PROGRAMS)
-endif
-
-test_upnp_SOURCES = \
- test_upnp.c
-test_upnp_LDADD = \
- $(top_builddir)/src/transport/libgnunetupnp.la \
- $(top_builddir)/src/util/libgnunetutil.la
-
-
-# EXTRA_DIST = test_upnp_data.conf
+++ /dev/null
-Document: draft-cheshire-nat-pmp-02.txt Stuart Cheshire
-Internet-Draft Marc Krochmal
-Category: Standards Track Apple Computer, Inc.
-Expires 14th March 2007 Kiren Sekar
- Sharpcast, Inc.
- 14th September 2006
-
- NAT Port Mapping Protocol (NAT-PMP)
-
- <draft-cheshire-nat-pmp-02.txt>
-
-Status of this Memo
-
- By submitting this Internet-Draft, each author represents that any
- applicable patent or other IPR claims of which he or she is aware
- have been or will be disclosed, and any of which he or she becomes
- aware will be disclosed, in accordance with Section 6 of BCP 79.
- For the purposes of this document, the term "BCP 79" refers
- exclusively to RFC 3979, "Intellectual Property Rights in IETF
- Technology", published March 2005.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF), its areas, and its working groups. Note that
- other groups may also distribute working documents as Internet-
- Drafts.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
-
- The list of current Internet-Drafts can be accessed at
- http://www.ietf.org/1id-abstracts.html
-
- The list of Internet-Draft Shadow Directories can be accessed at
- http://www.ietf.org/shadow.html
-
-
-Abstract
-
- This document describes a protocol for automating the process of
- creating Network Address Translation (NAT) port mappings. Included
- in the protocol is a method for retrieving the public IP address of
- a NAT gateway, thus allowing a client to make this public IP address
- and port number known to peers that may wish to communicate with it.
- This protocol is implemented in current Apple products including
- Mac OS X, Bonjour for Windows, and AirPort wireless base stations.
-
-
-
-
-
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 1]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-1. Introduction
-
- Network Address Translation (NAT) is a method of sharing one public
- internet address with a number of devices. This document is focused
- on what "IP Network Address Translator (NAT) Terminology and
- Considerations" [RFC 2663] calls "NAPTs" (Network Address/Port
- Translators). A full description of NAT is beyond the scope of this
- document. The following brief overview will cover the aspects
- relevant to this port mapping protocol. For more information on
- NAT, see "Traditional IP Network Address Translator" [RFC 3022].
-
- NATs have one or more public IP addresses. A private network is set
- up behind the NAT. Devices behind the NAT are assigned private
- addresses and the private address of the NAT device is used as the
- gateway.
-
- When a packet from any device behind the NAT is sent to an address on
- the public internet, the packet first passes through the NAT box. The
- NAT box looks at the source port and address. In some cases, a NAT
- will also keep track of the destination port and address. The NAT
- then creates a mapping from the private address and private port to a
- public address and public port if a mapping does not already exist.
- The NAT box replaces the private address and port number in the
- packet with the public entries from the mapping and sends the packet
- on to the next gateway.
-
- When a packet from any address on the internet is received on the
- NAT's public side, the NAT will look up the destination port (public
- port) in the list of mappings. If an entry is found, it will contain
- the private address and port that the packet should be sent to. The
- NAT gateway will then rewrite the destination address and port with
- those from the mapping. The packet will then be forwarded to the new
- destination addresses. If the packet did not match any mapping, the
- packet will most likely be dropped. Various NATs implement different
- strategies to handle this. The important thing to note is that if
- there is no mapping, the NAT does not know which private address the
- packet should be sent to.
-
- Mappings are usually created automatically as a result of observing
- outbound traffic. There are a few exceptions. Some NATs may allow
- manually-created permanent mappings that map a public port to a
- specific private IP address and port. Such a mapping allows incoming
- connections to the device with that private address. Some NATs also
- implement a default mapping where any inbound traffic that does not
- match a mapping will always be forwarded to a specific private
- address. Both types of mappings are usually set up manually through
- some configuration tool.
-
- Without these manually-created inbound port mappings, clients behind
- the NAT would be unable to receive inbound connections, which
- represents a loss of connectivity when compared to the original
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 2]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- Internet architecture [ETEAISD]. For those who view this loss of
- connectivity as a bad thing, NAT-PMP allows clients to operate much
- more like a host directly connected to the unrestricted public
- Internet, with an unrestricted public IP address. NAT-PMP allows
- client hosts to communicate with the NAT gateway to request the
- creation of inbound mappings on demand. Having created a NAT mapping
- to allow inbound connections, the client can then record its public
- IP address and public port number in a public registry (e.g. the
- world-wide Domain Name System) or otherwise make it accessible to
- peers that wish to communicate with it.
-
-
-2. Conventions and Terminology Used in this Document
-
- The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
- "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
- document are to be interpreted as described in "Key words for use in
- RFCs to Indicate Requirement Levels" [RFC 2119].
-
-
-3. Protocol and Packet Format
-
- NAT Port Mapping Protocol runs over UDP. Every packet starts with an
- 8 bit version followed by an 8 bit operation code.
-
- This document specifies version 0 of the protocol. Any NAT-PMP
- gateway implementing this version of the protocol, receiving a
- packet with a version number other than 0, MUST return result code 1
- (Unsupported Version).
-
- Opcodes between 0 and 127 are client requests. Opcodes from 128 to
- 255 are server responses. Responses always contain a 16 bit result
- code in network byte order. A result code of zero indicates success.
- Responses also contain a 32 bit unsigned integer corresponding to the
- number of seconds since the NAT gateway was rebooted or since its
- port mapping state was reset.
-
- This protocol SHOULD only be used when the client determines that
- its primary IPv4 address is in one of the private IP address ranges
- defined in "Address Allocation for Private Internets" [RFC 1918].
- This includes the address ranges 10/8, 172.16/12, and 192.168/16.
-
- Clients always send their Port Mapping Protocol requests to their
- default gateway, as learned via DHCP [RFC 2131], or similar means.
- This protocol is designed for small home networks, with a single
- logical link (subnet) where the client's default gateway is also the
- NAT translator for that network. For more complicated networks where
- the NAT translator is some device other than the client's default
- gateway, this protocol is not appropriate.
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 3]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-3.1 Requests and Responses
-
- NAT gateways are often low-cost devices, with limited memory and
- CPU speed. For this reason, to avoid making excessive demands on
- the NAT gateway, clients machines SHOULD NOT issue multiple requests
- simultaneously in parallel. If a client needs to perform multiple
- requests (e.g. on boot, wake from sleep, network connection, etc.)
- it SHOULD queue them and issue them serially one at a time. Once the
- NAT gateway responds to one request the client machine may issue the
- next. In the case of a fast NAT gateway, the client may be able to
- complete requests at a rate of hundreds per second. In the case of
- a slow NAT gateway that takes perhaps half a second to respond to
- a NAT-PMP request, the client SHOULD respect this and allow the
- NAT gateway to operate at the pace it can manage, and not overload
- it by issuing requests faster than the rate it's answering them.
-
- To determine the puclic IP address or request a port mapping,
- a NAT-PMP client sends its request packet to port 5351 of its
- configured gateway address, and waits 250ms for a response. If no
- NAT-PMP response is received from the gateway after 250ms, the client
- retransmits its request and waits 500ms. The client SHOULD repeat
- this process with the interval between attempts doubling each time.
- If, after sending its 9th attempt (and then waiting for 64 seconds),
- the client has still received no response, then it SHOULD conclude
- that this gateway does not support NAT Port Mapping Protocol and
- MAY log an error message indicating this fact. In addition, if the
- NAT-PMP client receives an "ICMP Port Unreachable" message from the
- gateway for port 5351 then it can skip any remaining retransmissions
- and conclude immediately that the gateway does not support NAT-PMP.
-
- As a performance optimization the client MAY record this information
- and use it to suppress further attempts to use NAT-PMP, but the
- client should not retain this information for too long. In
- particular, any event that may indicate a potential change of gateway
- or a change in gateway configuration (hardware link change
- indication, change of gateway MAC address, acquisition of new DHCP
- lease, receipt of NAT-PMP announcement packet from gateway, etc.)
- should cause the client to discard its previous information regarding
- the gateway's lack of NAT-PMP support, and send its next NAT-PMP
- request packet normally.
-
-
-3.2 Determining the Public Address
-
- To determine the public address, the client behind the NAT sends the
- following UDP payload to port 5351 of the configured gateway address:
-
- 0 1
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Vers = 0 | OP = 0 |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
-Expires 14th March 2007 Cheshire, et al. [Page 4]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- A compatible NAT gateway MUST generate a response with the following
- format:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Vers = 0 | OP = 128 + 0 | Result Code |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Seconds Since Start of Epoch |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Public IP Address (a.b.c.d) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- This response indicates that the NAT gateway implements this version
- of the protocol and returns the public IP address of the NAT gateway.
- If the result code is non-zero, the value of Public IP Address is
- undefined (MUST be set to zero on transmission, and MUST be ignored
- on reception).
-
- The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
- with the time elapsed since its port mapping table was initialized on
- startup or reset for any other reason (see Section 3.6 "Seconds Since
- Start of Epoch").
-
- Upon receiving the response packet, the client MUST check the source
- IP address, and silently discard the packet if the address is not the
- address of the gateway to which the request was sent.
-
-
-3.2.1 Announcing Address Changes
-
- When the public IP address of the NAT changes, the NAT gateway MUST
- send a gratuitous response to the link-local multicast address
- 224.0.0.1, port 5351 with the packet format above to notify clients
- of the new public IP address. To accommodate packet loss, the
- NAT gateway SHOULD multicast 10 address change notifications.
- The interval between the first two notifications SHOULD be 250ms,
- and the interval between each subsequent notification SHOULD double.
-
- Upon receiving a gratuitous address change announcement packet,
- the client MUST check the source IP address, and silently discard
- the packet if the address is not the address of the client's
- current configured gateway. This is to guard against inadvertent
- misconfigurations where there may be more than one NAT gateway
- active on the network.
-
-
-
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 5]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-3.3 Creating a Mapping
-
- To create a mapping, the client sends a UDP packet to port 5351
- of the gateway's private IP address with the following format:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Vers = 0 | OP = x | Reserved (MUST be zero) |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Private Port | Requested Public Port |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Requested Port Mapping Lifetime in Seconds |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- Opcodes supported:
- 1 - Map UDP
- 2 - Map TCP
-
- The Reserved field MUST be set to zero on transmission and MUST
- be ignored on reception.
-
- The Private Port is set to the local port on which the client is
- listening.
-
- The Requested Public Port SHOULD usually be set to the same value as
- the local Private Port, or zero if the client has no preference for
- what port is assigned. However, the gateway is not obliged to assign
- the port requested, and may choose not to, either for policy reasons
- (e.g. port 80 is reserved and clients may not request it) or because
- that port has already been assigned to some other client. Because
- of this, some product developers have questioned the value of having
- the Requested Public Port field at all. The reason is for failure
- recovery. Most low-cost home NAT gateways do not record temporary
- port mappings in persistent storage, so if the gateway crashes or is
- rebooted, all the mappings are lost. A renewal packet is formatted
- identically to an initial mapping request packet, except that for
- renewals the client sets the Requested Public Port field to the
- port the gateway actually assigned, rather than the port the client
- originally wanted. When a freshly-rebooted NAT gateway receives a
- renewal packet from a client, it appears to the gateway just like
- an ordinary initial request for a port mapping, except that in this
- case the Requested Public Port is likely to be one that the NAT
- gateway *is* willing to allocate (it allocated it to this client
- right before the reboot, so it should presumably be willing to
- allocate it again).
-
- The RECOMMENDED Port Mapping Lifetime is 3600 seconds.
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 6]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- After sending the port mapping request, the client then waits for the
- NAT gateway to respond. If after 250ms, the gateway doesn't respond,
- the client SHOULD re-issue its request as described above in Section
- 3.1 "Requests and Responses".
-
- The NAT gateway responds with the following packet format:
-
- 0 1 2 3
- 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Vers = 0 | OP = 128 + x | Result Code |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Seconds Since Start of Epoch |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Private Port | Mapped Public Port |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- | Port Mapping Lifetime in Seconds |
- +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-
- The 'x' in the OP field MUST match what the client requested. Some
- NAT gateways are incapable of creating a UDP port mapping without
- also creating a corresponding TCP port mapping, and vice versa, and
- these gateways MUST NOT implement NAT Port Mapping Protocol until
- this deficiency is fixed. A NAT gateway which implements this
- protocol MUST be able to create TCP-only and UDP-only port mappings.
-
- If a NAT gateway silently creates a pair of mappings for a client
- that only requested one mapping, then it may expose that client to
- receiving inbound UDP packets or inbound TCP connection requests
- that it did not ask for and does not want.
-
- While a NAT gateway MUST NOT automatically create mappings for TCP
- when the client requests UDP, and vice versa, the NAT gateway MUST
- reserve the companion port so the same client can choose to map it
- in the future. For example, if a client requests to map TCP port 80,
- as long as the client maintains the lease for that TCP port mapping,
- another client with a different IP address MUST NOT be able to
- successfully acquire the mapping for UDP port 80.
-
- The client normally requests the public port matching the private
- port. If that public port is not available, the NAT gateway MUST
- return a public port that is available or return an error code if
- no ports are available.
-
- The source address of the packet MUST be used for the private address
- in the mapping. This protocol is not intended to facilitate one
- device behind a NAT creating mappings for other devices. If there
- are legacy devices that require inbound mappings, permanent mappings
- can be created manually by the administrator, just as they are today.
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 7]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- If a mapping already exists for a given private port on a given local
- client (whether that mapping was created explicitly using NAT-PMP,
- implicitly as a result of an outgoing TCP SYN packet, or manually by
- a human administrator) and that client requests another mapping for
- the same private port (possibly requesting a different public port)
- then the mapping request should succeed, returning the already-
- assigned public port. This is necessary to handle the case where
- a client requests a mapping with requested public port X, and is
- granted a mapping with actual public port Y, but the acknowledgement
- packet gets lost. When the client retransmits its mapping request,
- it should get back the same positive acknowledgement as was sent (and
- lost) the first time.
-
- The NAT gateway SHOULD NOT accept mapping requests destined to the
- NAT gateway's public IP address or received on its public network
- interface. Only packets received on the private interface(s) with
- a destination address matching the private address(es) of the NAT
- gateway should be allowed.
-
- The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
- with the time elapsed since its port mapping table was initialized on
- startup or reset for any other reason (see Section 3.6 "Seconds Since
- Start of Epoch").
-
- The Port Mapping Lifetime is an unsigned integer in seconds. The NAT
- gateway MAY reduce the lifetime from what the client requested. The
- NAT gateway SHOULD NOT offer a lease lifetime greater than that
- requested by the client.
-
- Upon receiving the response packet, the client MUST check the source
- IP address, and silently discard the packet if the address is not the
- address of the gateway to which the request was sent.
-
- The client SHOULD begin trying to renew the mapping halfway to expiry
- time, like DHCP. The renewal packet should look exactly the same as
- a request packet, except that the client SHOULD set the requested
- public port to what the NAT gateway previously mapped, not what the
- client originally requested. As described above, this enables the
- gateway to automatically recover its mapping state after a crash or
- reboot.
-
-
-3.4 Destroying a Mapping
-
- A mapping may be destroyed in a variety of ways. If a client fails
- to renew a mapping, then when its lifetime expires the mapping MUST
- be automatically deleted. In the common case where the gateway
- device is a combined DHCP server and NAT gateway, when a client's
- DHCP address lease expires, the gateway device MAY automatically
- delete any mappings belonging to that client. Otherwise a new client
- being assigned the same IP address could receive unexpected inbound
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 8]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- UDP packets or inbound TCP connection requests that it did not ask
- for and does not want.
-
- A client MAY also send an explicit packet to request deletion of a
- mapping that is no longer needed. A client requests explicit
- deletion of a mapping by sending a message to the NAT gateway
- requesting the mapping, with the Requested Lifetime in Seconds set
- to 0. The requested public port MUST be set to zero by the client
- on sending, and MUST be ignored by the gateway on reception.
-
- When a mapping is destroyed successfully as a result of the client
- explicitly requesting the deletion, the NAT gateway MUST send a
- response packet which is formatted as defined in section 3.3
- "Creating a Mapping". The response MUST contain a result code of 0,
- the private port as indicated in the deletion request, a public port
- of 0, and a lifetime of 0. The NAT gateway MUST respond to a request
- to destroy a mapping that does not exist as if the request were
- successful. This is because of the case where the acknowledgement is
- lost, and the client retransmits its request to delete the mapping.
- In this case the second request to delete the mapping MUST return the
- same response packet as the first request.
-
- If the deletion request was unsuccessful, the response MUST contain a
- non-zero result code and the requested mapping; the lifetime is
- undefined (MUST be set to zero on transmission, and MUST be ignored
- on reception). If the client attempts to delete a port mapping which
- was manually assigned by some kind of configuration tool, the NAT
- gateway MUST respond with a 'Not Authorized' error, result code 2.
-
- When a mapping is destroyed as a result of its lifetime expiring or
- for any other reason, if the NAT gateway's internal state indicates
- that there are still active TCP connections traversing that now-
- defunct mapping, then the NAT gateway SHOULD send appropriately-
- constructed TCP RST (reset) packets both to the local client and to
- the remote peer on the Internet to terminate that TCP connection.
-
- A client can request the explicit deletion of all its UDP or TCP
- mappings by sending the same deletion request to the NAT gateway
- with public port, private port, and lifetime set to 0. A client MAY
- choose to do this when it first acquires a new IP address in order to
- protect itself from port mappings that were performed by a previous
- owner of the IP address. After receiving such a deletion request,
- the gateway MUST delete all its UDP or TCP port mappings (depending
- on the opcode). The gateway responds to such a deletion request with
- a response as described above, with the private port set to zero. If
- the gateway is unable to delete a port mapping, for example, because
- the mapping was manually configured by the administrator, the gateway
- MUST still delete as many port mappings as possible, but respond with
- a non-zero result code. The exact result code to return depends on
- the cause of the failure. If the gateway is able to successfully
- delete all port mappings as requested, it MUST respond with a result
- code of 0.
-
-Expires 14th March 2007 Cheshire, et al. [Page 9]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-3.5 Result Codes
-
- Currently defined result codes:
- 0 - Success
- 1 - Unsupported Version
- 2 - Not Authorized/Refused
- (e.g. box supports mapping, but user has turned feature off)
- 3 - Network Failure
- (e.g. NAT box itself has not obtained a DHCP lease)
- 4 - Out of resources
- (NAT box cannot create any more mappings at this time)
- 5 - Unsupported opcode
-
- If the result code is non-zero, the format of the packet following
- the result code may be truncated. For example, if the client sends a
- request to the server with an opcode of 17 and the server does not
- recognize that opcode, the server SHOULD respond with a message where
- the opcode is 17 + 128 and the result code is 5 (opcode not
- supported). Since the server does not understand the format of
- opcode 17, it may not know what to place after the result code. In
- some cases, relevant data may follow the opcode to identify the
- operation that failed. For example, a client may request a mapping
- but that mapping may fail due to resource exhaustion. The server
- SHOULD respond with the result code to indicate resource exhaustion
- (4) followed by the requested port mapping so the client may identify
- which operation failed.
-
- Clients MUST be able to properly handle result codes not defined in
- this document. Undefined results codes MUST be treated as fatal
- errors of the request.
-
-
-3.6 Seconds Since Start of Epoch
-
- Every packet sent by the NAT gateway includes a "Seconds since start
- of epoch" field (SSSOE). If the NAT gateway resets or loses the
- state of its port mapping table, due to reboot, power failure, or any
- other reason, it MUST reset its epoch time and begin counting SSSOE
- from 0 again. Whenever a client receives any packet from the NAT
- gateway, either gratuitously or in response to a client request, the
- client computes its own conservative estimate of the expected SSSOE
- value by taking the SSSOE value in the last packet it received from
- the gateway and adding 7/8 (87.5%) of the time elapsed since that
- packet was received. If the SSSOE in the newly received packet is
- less than the client's conservative estimate by more than one second,
- then the client concludes that the NAT gateway has undergone a reboot
- or other loss of port mapping state, and the client MUST immediately
- renew all its active port mapping leases as described in Section 3.7
- "Recreating Mappings On NAT Gateway Reboot".
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 10]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-3.7 Recreating Mappings On NAT Gateway Reboot
-
- The NAT gateway MAY store mappings in persistent storage so when it
- is powered off or rebooted, it remembers the port mapping state of
- the network.
-
- However, maintaining this state is not essential for correct
- operation. When the NAT gateway powers on or clears its port mapping
- state as the result of a configuration change, it MUST reset the
- epoch time and re-announce its IP address as described in Section
- 3.2.1 "Announcing Address Changes". Reception of this packet where
- time has apparently gone backwards serves as a hint to clients
- on the network that they SHOULD immediately send renewal packets
- (to immediately recreate their mappings) instead of waiting until
- the originally scheduled time for those renewals. Clients who miss
- receiving those gateway announcement packets for any reason will
- still renew their mappings at the originally scheduled time and cause
- their mappings to be recreated; it will just take a little longer for
- these clients.
-
- A mapping renewal packet is formatted identically to an original
- mapping request; from the point of view of the client it is a
- renewal of an existing mapping, but from the point of view of the
- freshly-rebooted NAT gateway it appears as a new mapping request.
-
- This self-healing property of the protocol is very important.
-
- The remarkable reliability of the Internet as a whole derives
- in large part from the fact that important state is held in the
- endpoints, not in the network itself [ETEAISD]. Power-cycling an
- Ethernet switch results only in a brief interruption in the flow
- of packets; established TCP connections through that switch are not
- broken, merely delayed for a few seconds. Indeed, an old Ethernet
- switch can even be replaced with a new one, and as long as the cables
- are transferred over reasonably quickly, after the upgrade all the
- TCP connections that were previously going though the old switch will
- be unbroken and now going through the new one. The same is true of
- IP routers, wireless base stations, etc. The one exception is NAT
- gateways. Because the port mapping state is required for the NAT
- gateway to know where to forward inbound packets, loss of that state
- breaks connectivity through the NAT gateway. By allowing clients to
- detect when this loss of NAT gateway state has occurred, and recreate
- it on demand, we turn hard state in the network into soft state, and
- allow it to be recovered automatically when needed.
-
- Without this automatic recreation of soft state in the NAT gateway,
- reliable long-term networking would not be achieved. As mentioned
- above, the reliability of the Internet does not come from trying
- to build a perfect network in which errors never happen, but from
- accepting that in any sufficiently large system there will always be
- some component somewhere that's failing, and designing mechanisms
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 11]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- that can handle those failures and recover. To illustrate this point
- with an example, consider the following scenario: Imagine a network
- security camera that has a web interface and accepts incoming
- connections from web browser clients. Imagine this network security
- camera uses NAT-PMP or a similar protocol to set up an inbound
- port mapping in the NAT gateway so that it can receive incoming
- connections from clients the other side of the NAT gateway.
- Now, this camera may well operate for weeks, months, or even years.
- During that time it's possible that the NAT gateway could experience
- a power failure or be rebooted. The user could upgrade the NAT
- gateway's firmware, or even replace the entire NAT gateway device
- with a newer model. The general point is that if the camera operates
- for a long enough period of time, some kind of disruption to the NAT
- gateway becomes inevitable. The question is not whether the NAT
- gateway will lose its port mappings, but when, and how often.
- If the network camera and devices like it on the network can detect
- when the NAT gateway has lost its port mappings, and recreate them
- automatically, then these disruptions are self-correcting and
- invisible to the end user. If, on the other hand, the disruptions are
- not self-correcting, and after a NAT gateway reboot the user has to
- manually reset or reboot all the other devices on the network too,
- then these disruptions are *very* visible to the end user. This
- aspect of the design is what makes the difference between a protocol
- that keeps on working indefinitely over a time scale of months or
- years, and a protocol that works in brief testing, but in the real
- world is continually failing and requiring manual intervention to get
- it going again.
-
- When a client renews its port mappings as the result of receiving
- a packet where the "Seconds since start of epoch" field (SSSOE)
- indicates that a reboot or similar loss of state has occurred,
- the client MUST first delay by a random amount of time selected
- with uniform random distribution in the range 0 to 5 seconds, and
- then send its first port mapping request. After that request is
- acknowledged by the gateway, the client may then send its second
- request, and so on, as rapidly as the gateway allows. The requests
- SHOULD be issued serially, one at a time; the client SHOULD NOT issue
- multiple requests simultaneously in parallel.
-
- The discussion in this section focusses on recreating inbound port
- mappings after loss of NAT gateway state, because that is the more
- serious problem. Losing port mappings for outgoing connections
- destroys those currently active connections, but does not prevent
- clients from establishing new outgoing connections. In contrast,
- losing inbound port mappings not only destroys all existing inbound
- connections, but also prevents the reception of any new inbound
- connections until the port mapping is recreated. Accordingly,
- we consider recovery of inbound port mappings the more important
- priority. However, clients that want outgoing connections to survive
- a NAT gateway reboot can also achieve that using NAT-PMP. After
- initiating an outbound TCP connection (which will cause the NAT
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 12]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- gateway to establish an implicit port mapping) the client should send
- the NAT gateway a port mapping request for the source port of its TCP
- connection, which will cause the NAT gateway to send a response
- giving the public port it allocated for that mapping. The client can
- then store this information, and use later to recreate the mapping
- if it determines that the NAT gateway has lost its mapping state.
-
-
-3.8 NAT Gateways with NAT Function Disabled
-
- Note that *only* devices currently acting in the role of NAT gateway
- should participate in NAT-PMP protocol exchanges with clients.
- A network device that is capable of NAT (and NAT-PMP), but is
- currently configured not to perform that function, (e.g. it is
- acting as a traditional IP router, forwarding packets without
- modifying them), MUST NOT respond to NAT-PMP requests from clients,
- or send spontaneous NAT-PMP address-change announcements.
-
- In particular, a network device not currently acting in the role of
- NAT gateway should not even respond to NAT-PMP requests by returning
- an error code such as "2 - Not Authorized/Refused", since to do so
- is misleading to clients -- it suggests that NAT port mapping is
- necessary on this network for the client to successfully receive
- inbound connections, but is not available because the administrator
- has chosen to disable that functionality.
-
- Clients should also be careful to avoid making unfounded assumptions,
- such as the assumption that if the client has an IPv4 address in
- one of the RFC 1918 private IP address ranges then that means
- NAT necessarily must be in use. Net 10/8 has enough addresses
- to build a private network with millions of hosts and thousands
- of interconnected subnets, all without any use of NAT. Many
- organizations have built such private networks that benefit from
- using standard TCP/IP technology, but by choice do not connect
- to the public Internet. The purpose of NAT-PMP is to mitigate some
- of the damage caused by NAT. It would be an ironic and unwanted
- side-effect of this protocol if it were to lead well-meaning but
- misguided developers to create products that refuse to work on a
- private network *unless* they can find a NAT gateway to talk to.
- Consequently, a client finding that NAT-PMP is not available on its
- network should not give up, but should proceed on the assumption
- that the network may be a traditional routed IP network, with no
- address translation being used. This assumption may not always be
- true, but it is better than the alternative of falsely assuming
- the worst and not even trying to use normal (non-NAT) IP networking.
-
- If a network device not currently acting in the role of NAT gateway
- receives UDP packets addressed to port 5351, it SHOULD respond
- immediately with an "ICMP Port Unreachable" message to tell the
- client that it needn't continue with timeouts and retransmissions,
- and it should assume that NAT-PMP is not available and not needed
- on this network.
-
-Expires 14th March 2007 Cheshire, et al. [Page 13]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-4. UNSAF Considerations
-
- The document "IAB Considerations for UNSAF Across NAT" [RFC 3424]
- covers a number of issues when working with NATs. RFC 3424 outlines
- some requirements for any document that attempts to work around
- problems associated with NATs. This section addresses those
- requirements.
-
-
-4.1 Scope
-
- This protocol addresses the needs of TCP and UDP transport peers that
- are separated from the public internet by exactly one NAT. Such
- peers must have access to some form of directory server for
- registering the public IP address and port at which they can be
- reached.
-
-
-4.2 Transition Plan
-
- Any client making use of this protocol SHOULD implement IPv6 support.
- If a client supports IPv6 and is running on a device with a global
- IPv6 address, that IPv6 address SHOULD be preferred to the IPv4
- public address using this NAT mapping protocol. In case other
- clients do not have IPv6 connectivity, both the IPv4 and IPv6
- addresses SHOULD be registered with whatever form of directory server
- is used. Preference SHOULD be given to IPv6 addresses when
- available. By implementing support for IPv6 and using this protocol
- for IPv4, vendors can ship products today that will work under both
- scenarios. As IPv6 is more widely deployed, clients of this protocol
- following these recommendations will transparently make use of IPv6.
-
-
-4.3 Failure Cases
-
- Aside from NATs that do not implement this protocol, there are a
- number of situations where this protocol may not work.
-
-
-4.3.1 NAT Behind NAT
-
- Some people's primary IP address, assigned by their ISP, may itself
- be a NAT address. In addition, some people may have a public IP
- address, but may then double NAT themselves, perhaps by choice or
- perhaps by accident. Although it might be possible in principle for
- one NAT gateway to recursively request a mapping from the next one,
- this document does not advocate that and does not try to prescribe
- how it would be done.
-
- It would be a lot of work to implement nested NAT port mapping
- correctly, and there are a number of reasons why the end result might
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 14]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- not be as useful as we might hope. Consider the case of an ISP that
- offers each of its customers only a single NAT address. This ISP
- could instead have chosen to provide each customer with a single
- public IP address, or, if the ISP insists on running NAT, it could
- have chosen to allow each customer a reasonable number of addresses,
- enough for each customer device to have its own NAT address directly
- from the ISP. If instead this ISP chooses to allow each customer
- just one and only one NAT address, forcing said customer to run
- nested NAT in order to use more than one device, it seems unlikely
- that such an ISP would be so obliging as to provide a NAT service
- that supports NAT Port Mapping Protocol. Supposing that such an ISP
- did wish to offer its customers NAT service with NAT-PMP so as to
- give them the ability to receive inbound connections, this ISP could
- easily choose to allow each client to request a reasonable number of
- DHCP addresses from that gateway. Remember that Net 10/8 [RFC 1918]
- allows for over 16 million addresses, so NAT addresses are not in any
- way in short supply. A single NAT gateway with 16 million available
- addresses is likely to run out of packet forwarding capacity before
- it runs out of private addresses to hand out. In this way the ISP
- could offer single-level NAT with NAT-PMP, obviating the need to
- support nested NAT-PMP. In addition, an ISP that is motivated to
- provide their customers with unhindered access to the Internet by
- allowing incoming as well as outgoing connections has better ways
- to offer this service. Such an ISP could offer its customers real
- public IP addresses instead of NAT addresses, or could even choose
- to offer its customers full IPv6 connectivity, where no mapping or
- translation is required at all.
-
-
-4.3.2 NATs with Multiple Public IP Addresses
-
- If a NAT maps private addresses to multiple public addresses,
- then it SHOULD pick one of those addresses as the one it will
- support for inbound connections, and for the purposes of this
- protocol it SHOULD act as if that address were its only address.
-
-
-4.3.3 NATs and Routed Private Networks
-
- In some cases, a large network may be subnetted. Some sites
- may install a NAT gateway and subnet the private network.
- Such subnetting breaks this protocol because the router address
- is not necessarily the address of the device performing NAT.
-
- Addressing this problem is not a high priority. Any site with the
- resources to set up such a configuration should have the resources to
- add manual mappings or attain a range of globally unique addresses.
-
- Not all NATs will support this protocol. In the case where a client
- is run behind a NAT that does not support this protocol, the software
- relying on the functionality of this protocol may be unusable.
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 15]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-4.3.4 Communication Between Hosts Behind the Same NAT
-
- NAT gateways supporting NAT-PMP should also implement "hairpin
- translation". Hairpin translation means supporting communication
- between two local clients being served by the same NAT gateway.
-
- Suppose device A is listening on private address and port 10.0.0.2:80
- for incoming connections. Using NAT-PMP, device A has obtained a
- mapping to public address and port x.x.x.x:80, and has recorded this
- public address and port in a public directory of some kind. For
- example, it could have created a DNS SRV record containing this
- information, and recorded it, using DNS Dynamic Update [RFC 3007], in
- a publicly accessible DNS server. Suppose then that device B, behind
- the same NAT gateway as device A, but unknowing or uncaring of this
- fact, retrieves device A's DNS SRV record and attempts to open a TCP
- connection to x.x.x.x:80. The outgoing packets addressed to this
- public Internet address will be sent to the NAT gateway for
- translation and forwarding. Having translated the source address and
- port number on the outgoing packet, the NAT gateway needs to be smart
- enough to recognize that the destination address is in fact itself,
- and then feed this packet back into its packet reception engine, to
- perform the destination port mapping lookup to translate and forward
- this packet to device A at address and port 10.0.0.2:80.
-
-4.3.5 Non UDP/TCP Transport Traffic
-
- Any communication over transport protocols other than TCP and UDP
- will not be served by this protocol. Examples are Generic Routing
- Encapsulation (GRE), Authentication Header (AH) and Encapsulating
- Security Payload (ESP).
-
-4.4 Long Term Solution
-
- As IPv6 is deployed, clients of this protocol supporting IPv6 will be
- able to bypass this protocol and the NAT when communicating with
- other IPv6 devices. In order to ensure this transition, any client
- implementing this protocol SHOULD also implement IPv6 and use this
- solution only when IPv6 is not available to both peers.
-
-4.5 Existing Deployed NATs
-
- Existing deployed NATs will not support this protocol. This protocol
- will only work with NATs that are upgraded to support it.
-
-
-
-
-
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 16]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-5. Security Considerations
-
- As discussed in section 3.2 "Determining the Public Address", only
- clients on the private side of the NAT may create port mappings, and
- only on behalf of themselves. By using IP address spoofing, it's
- possible for one client to delete the port mappings of another
- client. It's also possible for one client to create port mappings on
- behalf of another client. The best way to deal with this
- vulnerability is to use IPSec [RFC 2401].
-
- Since allowing incoming connections is often a policy decision, any
- NAT gateway implementing this protocol SHOULD have an administrative
- mechanism to disable it.
-
- Some people view the property that NATs block inbound connections as
- a security benefit which is undermined by this protocol. The authors
- of this document have a different point of view. In the days before
- NAT, all hosts had unique public IP addresses, and had unhindered
- ability to communicate with any other host on the Internet. When NAT
- came along it broke this unhindered connectivity, relegating many
- hosts to second-class status, unable to receive inbound connections.
- This protocol goes some way to undo some of that damage. The purpose
- of a NAT gateway should be to allow several hosts to share a single
- address, not to simultaneously impede those host's ability to
- communicate freely. Security is most properly provided by end-to-end
- cryptographic security, and/or by explicit firewall functionality, as
- appropriate. Blocking of certain connections should occur only as a
- result of explicit and intentional firewall policy, not as an
- accidental side-effect of some other technology.
-
-
-6. IANA Considerations
-
- No IANA services are required by this document.
-
-
-7. Acknowledgments
-
- The concepts described in this document have been explored, developed
- and implemented with help from Bob Bradley, Josh Graessley, Rob
- Newberry, Roger Pantos, John Saxton, and James Woodyatt.
-
-
-8. Deployment History
-
- NAT-PMP client software first became available to the public
- through Apple's Darwin Open Source code in August 2004.
- NAT-PMP implementations began shipping to end users in large
- volumes (i.e. millions) with the launch of Mac OS X 10.4 Tiger
- and Bonjour for Windows 1.0 in April 2005.
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 17]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
- The NAT-PMP client in Mac OS X 10.4 Tiger and Bonjour for Windows
- exists as part of the mDNSResponder system service. When a client
- advertises a service using Wide Area Bonjour [DNS-SD], and the
- machine is behind a NAT-PMP-capable NAT gateway, then if the machine
- is so configured, the mDNSResponder system service automatically uses
- NAT-PMP to set up an inbound port mapping, and then records the
- public IP address and port in the global DNS. Existing client
- software using the existing Bonjour programming APIs [Bonjour]
- gets this functionality automatically. The logic is that if client
- software publishes its information into the global DNS via Wide Area
- Bonjour service advertising, then it's reasonable to infer an
- expectation that this information should be usable by the peers
- retrieving it. Generally speaking, recording a private IP address
- like 10.0.0.2 in the public DNS is completely pointless because that
- address is not reachable from clients on the other side of the NAT
- gateway. In the case of a home user with a single computer directly
- connected to their Cable or DSL modem, with a single global IPv4
- address and no NAT gateway (a surprisingly common configuration),
- publishing that IP address into the global DNS is useful because that
- IP address is reachable. In contrast, a home user using a NAT gateway
- to share a single global IPv4 address between several computers loses
- this ability to receive inbound connections easily. This breaks many
- peer-to-peer collaborative applications, like the multi-user text
- editor SubEthaEdit [SEE]. Automatically creating the necessary
- inbound port mappings helps remedy this unintended side-effect of
- NAT.
-
- The server side of the NAT-PMP protocol is implemented in Apple's
- "AirPort Extreme" and "AirPort Express" wireless base stations.
-
-
-9. Copyright Notice
-
- Copyright (C) The Internet Society (2006).
-
- This document is subject to the rights, licenses and restrictions
- contained in BCP 78, and except as set forth therein, the authors
- retain all their rights. For the purposes of this document,
- the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights
- in Contributions", published March 2005.
-
- This document and the information contained herein are provided on
- an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
- REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE
- INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
- THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
- WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 18]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-10. Normative References
-
- [RFC 1918] Y. Rekhter et.al., "Address Allocation for Private
- Internets", RFC 1918, February 1996.
-
- [RFC 2119] RFC 2119 - Key words for use in RFCs to Indicate
- Requirement Levels
-
-
-11. Informative References
-
- [Bonjour] Apple "Bonjour" <http://developer.apple.com/bonjour/>
-
- [ETEAISD] J. Saltzer, D. Reed and D. Clark: "End-to-end arguments in
- system design", ACM Trans. Comp. Sys., 2(4):277-88, Nov.
- 1984
-
- [DNS-SD] Cheshire, S., and M. Krochmal, "DNS-Based Service
- Discovery", Internet-Draft (work in progress),
- draft-cheshire-dnsext-dns-sd-04.txt, August 2006.
-
- [mDNS] Cheshire, S., and M. Krochmal, "Multicast DNS",
- Internet-Draft (work in progress),
- draft-cheshire-dnsext-multicastdns-06.txt, August 2006.
-
- [RFC 2131] R. Droms, "Dynamic Host Configuration Protocol", RFC 2131,
- March 1997.
-
- [RFC 2401] Atkinson, R. and S. Kent, "Security Architecture for the
- Internet Protocol", RFC 2401, November 1998.
-
- [RFC 2663] Srisuresh, P. and M. Holdrege, "IP Network Address
- Translator (NAT) Terminology and Considerations", RFC
- 2663, August 1999.
-
- [RFC 3007] Wellington, B., "Simple Secure Domain Name System
- (DNS) Dynamic Update", RFC 3007, November 2000.
-
- [SEE] <http://www.codingmonkeys.de/subethaedit/>
-
- [RFC 3022] RFC 3022 - Network Address Translator
-
- [RFC 3424] RFC 3424 - IAB Considerations for UNilateral Self-Address
- Fixing (UNSAF) Across Network Address Translation
-
-
-
-
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 19]
-\f
-Internet Draft NAT Port Mapping Protocol 14th September 2006
-
-
-12. Authors' Addresses
-
- Stuart Cheshire
- Apple Computer, Inc.
- 1 Infinite Loop
- Cupertino
- California 95014
- USA
-
- Phone: +1 408 974 3207
- EMail: rfc [at] stuartcheshire [dot] org
-
-
- Marc Krochmal
- Apple Computer, Inc.
- 1 Infinite Loop
- Cupertino
- California 95014
- USA
-
- Phone: +1 408 974 4368
- EMail: marc [at] apple [dot] com
-
-
- Kiren Sekar
- Sharpcast, Inc.
- 250 Cambridge Ave, Suite 101
- Palo Alto
- California 94306
- USA
-
- Phone: +1 650 323 1960
- EMail: ksekar [at] sharpcast [dot] com
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Expires 14th March 2007 Cheshire, et al. [Page 20]
+++ /dev/null
-/*
- This file is part of GNUnet.
- (C) 2006 Christian Grothoff (and other contributing authors)
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-/**
- * @file upnp/test_upnp.c
- * @brief Testcase for UPnP
- * @author Christian Grothoff
- */
-
-#include "gnunet_util.h"
-#include "gnunet_upnp_service.h"
-#include "gnunet_core.h"
-#include "platform.h"
-
-
-
-int
-main (int argc, const char *argv[])
-{
- static GNUNET_CoreAPIForPlugins capi;
- struct GNUNET_GE_Context *ectx;
- struct GNUNET_GC_Configuration *cfg;
- struct in_addr addr;
- int i;
- GNUNET_UPnP_ServiceAPI *upnp;
- struct GNUNET_PluginHandle *plug;
- GNUNET_ServicePluginInitializationMethod init;
- GNUNET_ServicePluginShutdownMethod done;
- char ntop_buf[INET_ADDRSTRLEN];
-
- ectx = GNUNET_GE_create_context_stderr (GNUNET_NO,
- GNUNET_GE_WARNING | GNUNET_GE_ERROR
- | GNUNET_GE_FATAL | GNUNET_GE_USER |
- GNUNET_GE_ADMIN |
- GNUNET_GE_DEVELOPER |
- GNUNET_GE_IMMEDIATE |
- GNUNET_GE_BULK);
- GNUNET_GE_setDefaultContext (ectx);
- cfg = GNUNET_GC_create ();
- GNUNET_GE_ASSERT (ectx, cfg != NULL);
- GNUNET_os_init (ectx);
- capi.ectx = ectx;
- capi.cfg = cfg;
- plug = GNUNET_plugin_load (ectx, "libgnunet", "module_upnp");
- if (plug == NULL)
- {
- GNUNET_GC_free (cfg);
- GNUNET_GE_free_context (ectx);
- return 1;
- }
- init = GNUNET_plugin_resolve_function (plug, "provide_", GNUNET_YES);
- if (init == NULL)
- {
- GNUNET_plugin_unload (plug);
- GNUNET_GC_free (cfg);
- GNUNET_GE_free_context (ectx);
- return 1;
- }
- upnp = init (&capi);
- if (upnp == NULL)
- {
- GNUNET_plugin_unload (plug);
- GNUNET_GC_free (cfg);
- GNUNET_GE_free_context (ectx);
- return 1;
- }
- for (i = 0; i < 10; i++)
- {
- if (GNUNET_shutdown_test () != GNUNET_NO)
- break;
- if (GNUNET_OK == upnp->get_ip (2086, "TCP", &addr))
- {
- printf ("UPnP returned external IP %s\n",
- inet_ntop (AF_INET, &addr, ntop_buf, INET_ADDRSTRLEN));
- }
- else
- {
- /* we cannot be sure that there is a UPnP-capable
- NAT-box out there, so test should not fail
- just because of this! */
- printf ("No UPnP response (yet).\n");
- }
- GNUNET_thread_sleep (2 * GNUNET_CRON_SECONDS);
- }
- done = GNUNET_plugin_resolve_function (plug, "release_", GNUNET_YES);
- if (done != NULL)
- done ();
- GNUNET_plugin_unload (plug);
- GNUNET_GC_free (cfg);
- GNUNET_GE_free_context (ectx);
- return 0;
-}
-
-/* end of upnptest.c */
+++ /dev/null
-/**
- * @file upnp/upnp.c UPnP Implementation
- * @ingroup core
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "platform.h"
-#include "upnp_xmlnode.h"
-#include "upnp_util.h"
-#include "upnp.h"
-
-#include <curl/curl.h>
-
-#define TRUE GNUNET_YES
-#define FALSE GNUNET_NO
-#define g_return_if_fail(a) if(!(a)) return;
-#define g_return_val_if_fail(a, val) if(!(a)) return (val);
-
-#define HTTP_OK "200 OK"
-#define NUM_UDP_ATTEMPTS 2
-#define HTTPMU_HOST_ADDRESS "239.255.255.250"
-#define HTTPMU_HOST_PORT 1900
-#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
-#define SEARCH_REQUEST_STRING \
- "M-SEARCH * HTTP/1.1\r\n" \
- "MX: 2\r\n" \
- "HOST: 239.255.255.250:1900\r\n" \
- "MAN: \"ssdp:discover\"\r\n" \
- "ST: urn:schemas-upnp-org:service:%s\r\n" \
- "\r\n"
-#define WAN_IP_CONN_SERVICE "WANIPConnection:1"
-#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
-#define HTTP_POST_SOAP_HEADER \
- "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
-#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u"
-#define SOAP_ACTION \
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
- "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
- "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
- "<s:Body>\r\n" \
- "<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s\">\r\n" \
- "%s" \
- "</u:%s>\r\n" \
- "</s:Body>\r\n" \
- "</s:Envelope>"
-#define PORT_MAPPING_LEASE_TIME "0"
-#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
-#define ADD_PORT_MAPPING_PARAMS \
- "<NewRemoteHost></NewRemoteHost>\r\n" \
- "<NewExternalPort>%i</NewExternalPort>\r\n" \
- "<NewProtocol>%s</NewProtocol>\r\n" \
- "<NewInternalPort>%i</NewInternalPort>\r\n" \
- "<NewInternalClient>%s</NewInternalClient>\r\n" \
- "<NewEnabled>1</NewEnabled>\r\n" \
- "<NewPortMappingDescription>" \
- PORT_MAPPING_DESCRIPTION \
- "</NewPortMappingDescription>\r\n" \
- "<NewLeaseDuration>" \
- PORT_MAPPING_LEASE_TIME \
- "</NewLeaseDuration>\r\n"
-#define DELETE_PORT_MAPPING_PARAMS \
- "<NewRemoteHost></NewRemoteHost>\r\n" \
- "<NewExternalPort>%i</NewExternalPort>\r\n" \
- "<NewProtocol>%s</NewProtocol>\r\n"
-
-typedef enum
-{
- GAIM_UPNP_STATUS_UNDISCOVERED = -1,
- GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER,
- GAIM_UPNP_STATUS_DISCOVERING,
- GAIM_UPNP_STATUS_DISCOVERED
-} GaimUPnPStatus;
-
-typedef struct
-{
- GaimUPnPStatus status;
- char *control_url;
- const char *service_type;
- char publicip[16];
-} GaimUPnPControlInfo;
-
-typedef struct
-{
- const char *service_type;
- char *full_url;
- char *buf;
- unsigned int buf_len;
- struct GNUNET_NETWORK_Handle *sock;
-} UPnPDiscoveryData;
-
-static GaimUPnPControlInfo control_info = {
- GAIM_UPNP_STATUS_UNDISCOVERED,
- NULL,
- NULL,
- "",
-};
-
-/**
- * This is the signature used for functions that act as a callback
- * to CURL.
- */
-typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data,
- size_t size,
- size_t nmemb, void *user_data);
-
-
-
-static char *
-g_strstr_len (const char *haystack, int haystack_len, const char *needle)
-{
- int i;
-
- g_return_val_if_fail (haystack != NULL, NULL);
- g_return_val_if_fail (needle != NULL, NULL);
-
- if (haystack_len < 0)
- return strstr (haystack, needle);
- else
- {
- const char *p = haystack;
- int needle_len = strlen (needle);
- const char *end = haystack + haystack_len - needle_len;
-
- if (needle_len == 0)
- return (char *) haystack;
-
- while (*p && p <= end)
- {
- for (i = 0; i < needle_len; i++)
- if (p[i] != needle[i])
- goto next;
-
- return (char *) p;
-
- next:
- p++;
- }
- }
-
- return NULL;
-}
-
-static int
-gaim_upnp_compare_device (const xmlnode * device, const char *deviceType)
-{
- xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType");
- char *tmp;
- int ret;
-
- if (deviceTypeNode == NULL)
- return FALSE;
- tmp = xmlnode_get_data (deviceTypeNode);
- ret = !strcasecmp (tmp, deviceType);
- GNUNET_free (tmp);
- return ret;
-}
-
-static int
-gaim_upnp_compare_service (const xmlnode * service, const char *serviceType)
-{
- xmlnode *serviceTypeNode;
- char *tmp;
- int ret;
-
- if (service == NULL)
- return FALSE;
- serviceTypeNode = xmlnode_get_child (service, "serviceType");
- if (serviceTypeNode == NULL)
- return FALSE;
- tmp = xmlnode_get_data (serviceTypeNode);
- ret = !strcasecmp (tmp, serviceType);
- GNUNET_free (tmp);
- return ret;
-}
-
-static char *
-gaim_upnp_parse_description_response (const char *httpResponse,
- size_t len,
- const char *httpURL,
- const char *serviceType)
-{
- char *xmlRoot, *baseURL, *controlURL, *service;
- xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
- char *tmp;
-
- /* find the root of the xml document */
- xmlRoot = g_strstr_len (httpResponse, len, "<root");
- if (xmlRoot == NULL)
- return NULL;
- if (g_strstr_len (httpResponse, len, "</root") == NULL)
- return NULL;
-
- /* create the xml root node */
- xmlRootNode = xmlnode_from_str (xmlRoot, len - (xmlRoot - httpResponse));
- if (xmlRootNode == NULL)
- return NULL;
-
- /* get the baseURL of the device */
- baseURLNode = xmlnode_get_child (xmlRootNode, "URLBase");
- if (baseURLNode != NULL)
- {
- baseURL = xmlnode_get_data (baseURLNode);
- }
- else
- {
- baseURL = GNUNET_strdup (httpURL);
- }
-
- /* get the serviceType child that has the service type as its data */
- /* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
- serviceTypeNode = xmlnode_get_child (xmlRootNode, "device");
- while (!gaim_upnp_compare_device (serviceTypeNode,
- "urn:schemas-upnp-org:device:InternetGatewayDevice:1")
- && serviceTypeNode != NULL)
- {
- serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
- }
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
-
- /* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
- while (!gaim_upnp_compare_device (serviceTypeNode,
- "urn:schemas-upnp-org:device:WANDevice:1")
- && serviceTypeNode != NULL)
- {
- serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
- }
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
-
- /* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
- while (serviceTypeNode && !gaim_upnp_compare_device (serviceTypeNode,
- "urn:schemas-upnp-org:device:WANConnectionDevice:1"))
- {
- serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
- }
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "serviceList");
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
-
- /* get the serviceType variable passed to this function */
- service = g_strdup_printf (SEARCH_REQUEST_DEVICE, serviceType);
- serviceTypeNode = xmlnode_get_child (serviceTypeNode, "service");
- while (!gaim_upnp_compare_service (serviceTypeNode, service) &&
- serviceTypeNode != NULL)
- {
- serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
- }
-
- GNUNET_free (service);
- if (serviceTypeNode == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
-
- /* get the controlURL of the service */
- if ((controlURLNode = xmlnode_get_child (serviceTypeNode,
- "controlURL")) == NULL)
- {
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
- return NULL;
- }
-
- tmp = xmlnode_get_data (controlURLNode);
- if (baseURL && !gaim_str_has_prefix (tmp, "http://") &&
- !gaim_str_has_prefix (tmp, "HTTP://"))
- {
- if (tmp[0] == '/')
- {
- size_t len;
- const char *end;
- /* absolute path */
- end = strstr (&baseURL[strlen ("http://")], "/");
- if (end == NULL)
- len = strlen (&baseURL[strlen ("http://")]);
- else
- len = end - &baseURL[strlen ("http://")];
- controlURL = g_strdup_printf ("http://%.*s%s",
- len,
- &baseURL[strlen ("http://")], tmp);
- }
- else
- {
- controlURL = g_strdup_printf ("%s%s", baseURL, tmp);
- }
- GNUNET_free (tmp);
- }
- else
- {
- controlURL = tmp;
- }
- GNUNET_free (baseURL);
- xmlnode_free (xmlRootNode);
-
- return controlURL;
-}
-
-#define CURL_EASY_SETOPT(c, a, b) do { ret = curl_easy_setopt(c, a, b); if (ret != CURLE_OK) GNUNET_log(GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, _("%s failed at %s:%d: `%s'\n"), "curl_easy_setopt", __FILE__, __LINE__, curl_easy_strerror(ret)); } while (0);
-
-/**
- * Do the generic curl setup.
- */
-static int
-setup_curl (const char *proxy, CURL * curl)
-{
- int ret;
-
- CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
- if (strlen (proxy) > 0)
- CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
- CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, 1024); /* a bit more than one HELLO */
- CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 150L);
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
- CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
- return GNUNET_OK;
-}
-
-static int
-gaim_upnp_generate_action_message_and_send (const char *proxy,
- const char *actionName,
- const char *actionParams,
- GaimUtilFetchUrlCallback cb,
- void *cb_data)
-{
- CURL *curl;
- int ret;
- char *soapHeader;
- char *sizeHeader;
- char *soapMessage;
- struct curl_slist *headers = NULL;
-
- GNUNET_assert (cb != NULL);
- if (0 != curl_global_init (CURL_GLOBAL_WIN32))
- return GNUNET_SYSERR;
- /* set the soap message */
- soapMessage = g_strdup_printf (SOAP_ACTION,
- actionName,
- control_info.service_type,
- actionParams, actionName);
- soapHeader = g_strdup_printf (HTTP_POST_SOAP_HEADER,
- control_info.service_type, actionName);
- sizeHeader = g_strdup_printf (HTTP_POST_SIZE_HEADER, strlen (soapMessage));
- curl = curl_easy_init ();
- setup_curl (proxy, curl);
- CURL_EASY_SETOPT (curl, CURLOPT_URL, control_info.control_url);
- CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, cb);
- CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cb_data);
- CURL_EASY_SETOPT (curl, CURLOPT_POST, 1);
- headers = curl_slist_append (headers,
- "CONTENT-TYPE: text/xml ; charset=\"utf-8\"");
- headers = curl_slist_append (headers, soapHeader);
- headers = curl_slist_append (headers, sizeHeader);
- CURL_EASY_SETOPT (curl, CURLOPT_HTTPHEADER, headers);
- CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDS, soapMessage);
- CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDSIZE, strlen (soapMessage));
- CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
- CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
- CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
- CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
- if (ret == CURLE_OK)
- ret = curl_easy_perform (curl);
-#if 0
- if (ret != CURLE_OK)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- _
- ("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"),
- "curl_easy_perform", control_info.control_url, soapMessage,
- __FILE__, __LINE__, curl_easy_strerror (ret));
-#endif
- curl_slist_free_all (headers);
- curl_easy_cleanup (curl);
- curl_global_cleanup ();
- GNUNET_free (sizeHeader);
- GNUNET_free (soapMessage);
- GNUNET_free (soapHeader);
- if (ret != CURLE_OK)
- return GNUNET_SYSERR;
- return GNUNET_OK;
-}
-
-
-static size_t
-looked_up_public_ip_cb (void *url_data,
- size_t size, size_t nmemb, void *user_data)
-{
- UPnPDiscoveryData *dd = user_data;
- size_t len = size * nmemb;
- const char *temp;
- const char *temp2;
-
- if (len + dd->buf_len > 1024 * 1024 * 4)
- return 0; /* refuse to process - too big! */
- GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
- memcpy (&dd->buf[dd->buf_len - len], url_data, len);
- if (dd->buf_len == 0)
- return len;
- /* extract the ip, or see if there is an error */
- if ((temp = g_strstr_len (dd->buf,
- dd->buf_len, "<NewExternalIPAddress")) == NULL)
- return len;
- if (!(temp = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), ">")))
- return len;
- if (!(temp2 = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), "<")))
- return len;
- memset (control_info.publicip, 0, sizeof (control_info.publicip));
- if (temp2 - temp >= sizeof (control_info.publicip))
- temp2 = temp + sizeof (control_info.publicip) - 1;
- memcpy (control_info.publicip, temp + 1, temp2 - (temp + 1));
- GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
- _("upnp: NAT Returned IP: %s\n"), control_info.publicip);
- return len;
-}
-
-
-static size_t
-ignore_response (void *url_data, size_t size, size_t nmemb, void *user_data)
-{
- return size * nmemb;
-}
-
-/**
- * Process downloaded bits of service description.
- */
-static size_t
-upnp_parse_description_cb (void *httpResponse,
- size_t size, size_t nmemb, void *user_data)
-{
- UPnPDiscoveryData *dd = user_data;
- size_t len = size * nmemb;
- char *control_url = NULL;
-
- if (len + dd->buf_len > 1024 * 1024 * 4)
- return len; /* refuse to process - too big! */
- GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
- memcpy (&dd->buf[dd->buf_len - len], httpResponse, len);
- if (dd->buf_len > 0)
- control_url = gaim_upnp_parse_description_response (dd->buf,
- dd->buf_len,
- dd->full_url,
- dd->service_type);
- control_info.status = control_url ? GAIM_UPNP_STATUS_DISCOVERED
- : GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER;
- GNUNET_free_non_null (control_info.control_url);
- control_info.control_url = control_url;
- control_info.service_type = dd->service_type;
- return len;
-}
-
-static int
-gaim_upnp_parse_description (char *proxy, UPnPDiscoveryData * dd)
-{
- CURL *curl;
- int ret;
-
- if (0 != curl_global_init (CURL_GLOBAL_WIN32))
- return GNUNET_SYSERR;
- curl = curl_easy_init ();
- setup_curl (proxy, curl);
- ret = CURLE_OK;
- CURL_EASY_SETOPT (curl, CURLOPT_URL, dd->full_url);
- CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &upnp_parse_description_cb);
- CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, dd);
- CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
- CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
- CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
-
- /* NOTE: use of CONNECTTIMEOUT without also
- setting NOSIGNAL results in really weird
- crashes on my system! */
- CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
- ret = curl_easy_perform (curl);
- if (ret != CURLE_OK)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- _("%s failed at %s:%d: `%s'\n"),
- "curl_easy_perform", __FILE__, __LINE__,
- curl_easy_strerror (ret));
- curl_easy_cleanup (curl);
- curl_global_cleanup ();
- if (control_info.control_url == NULL)
- return GNUNET_SYSERR;
- return GNUNET_OK;
-}
-
-int
-gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_NETWORK_Handle *sock)
-{
- char *proxy;
- socklen_t avail;
- struct sockaddr_in server;
- int retry_count;
- char *sendMessage;
- size_t totalSize;
- int sentSuccess;
- char buf[65536];
- int buf_len;
- const char *startDescURL;
- const char *endDescURL;
- int ret;
- UPnPDiscoveryData dd;
- struct sockaddr *sa;
-
- memset (&dd, 0, sizeof (UPnPDiscoveryData));
- if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING)
- return GNUNET_NO;
- dd.sock = sock;
- memset (&server, 0, sizeof (struct sockaddr_in));
- server.sin_family = AF_INET;
- avail = sizeof (struct sockaddr_in);
- sa = (struct sockaddr *) &server;
- if (GNUNET_OK !=
- GNUNET_get_ip_from_hostname (HTTPMU_HOST_ADDRESS, AF_INET, &sa, &avail))
- {
- return GNUNET_SYSERR;
- }
- server.sin_port = htons (HTTPMU_HOST_PORT);
- control_info.status = GAIM_UPNP_STATUS_DISCOVERING;
-
- /* because we are sending over UDP, if there is a failure
- we should retry the send NUM_UDP_ATTEMPTS times. Also,
- try different requests for WANIPConnection and WANPPPConnection */
- for (retry_count = 0; retry_count < NUM_UDP_ATTEMPTS; retry_count++)
- {
- sentSuccess = FALSE;
- if ((retry_count % 2) == 0)
- dd.service_type = WAN_IP_CONN_SERVICE;
- else
- dd.service_type = WAN_PPP_CONN_SERVICE;
- sendMessage = g_strdup_printf (SEARCH_REQUEST_STRING, dd.service_type);
- totalSize = strlen (sendMessage);
- do
- {
- if (SENDTO (dd.sock,
- sendMessage,
- totalSize,
- 0,
- (struct sockaddr *) &server,
- sizeof (struct sockaddr_in)) == totalSize)
- {
- sentSuccess = TRUE;
- break;
- }
- }
- while (((errno == EINTR) || (errno == EAGAIN)) &&
- (GNUNET_shutdown_test () == GNUNET_NO));
- GNUNET_free (sendMessage);
- if (sentSuccess)
- break;
- }
- if (sentSuccess == FALSE)
- return GNUNET_SYSERR;
-
- /* try to read response */
- do
- {
- buf_len = GNUNET_IO_recv (dd.sock, buf, sizeof (buf) - 1, 0);
- if (buf_len > 0)
- {
- buf[buf_len] = '\0';
- break;
- }
- else if (errno != EINTR)
- {
- continue;
- }
- }
- while ((errno == EINTR) && (GNUNET_shutdown_test () == GNUNET_NO));
-
- /* parse the response, and see if it was a success */
- if (g_strstr_len (buf, buf_len, HTTP_OK) == NULL)
- return GNUNET_SYSERR;
- if ((startDescURL = g_strstr_len (buf, buf_len, "http://")) == NULL)
- return GNUNET_SYSERR;
-
- endDescURL = g_strstr_len (startDescURL,
- buf_len - (startDescURL - buf), "\r");
- if (endDescURL == NULL)
- endDescURL = g_strstr_len (startDescURL,
- buf_len - (startDescURL - buf), "\n");
- if (endDescURL == NULL)
- return GNUNET_SYSERR;
- if (endDescURL == startDescURL)
- return GNUNET_SYSERR;
- dd.full_url = GNUNET_strdup (startDescURL);
- dd.full_url[endDescURL - startDescURL] = '\0';
- proxy = NULL;
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "GNUNETD", "HTTP-PROXY", &proxy);
- ret = gaim_upnp_parse_description (proxy, &dd);
- GNUNET_free (dd.full_url);
- GNUNET_array_grow (dd.buf, dd.buf_len, 0);
- if (ret == GNUNET_OK)
- {
- ret = gaim_upnp_generate_action_message_and_send (proxy,
- "GetExternalIPAddress",
- "",
- looked_up_public_ip_cb,
- &dd);
- GNUNET_array_grow (dd.buf, dd.buf_len, 0);
- }
- GNUNET_free (proxy);
- return ret;
-}
-
-const char *
-gaim_upnp_get_public_ip ()
-{
- if ((control_info.status == GAIM_UPNP_STATUS_DISCOVERED)
- && (strlen (control_info.publicip) > 0))
- return control_info.publicip;
- return NULL;
-}
-
-int
-gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
- int do_add,
- unsigned short portmap, const char *protocol)
-{
- const char *action_name;
- char *action_params;
- char *internal_ip;
- char *proxy;
- int ret;
-
- if (control_info.status != GAIM_UPNP_STATUS_DISCOVERED)
- return GNUNET_NO;
- if (do_add)
- {
- internal_ip = GNUNET_upnp_get_internal_ip (cfg);
- if (internal_ip == NULL)
- {
- gaim_debug_error ("upnp",
- "gaim_upnp_set_port_mapping(): couldn't get local ip\n");
- return GNUNET_NO;
- }
- action_name = "AddPortMapping";
- action_params = g_strdup_printf (ADD_PORT_MAPPING_PARAMS,
- portmap,
- protocol, portmap, internal_ip);
- GNUNET_free (internal_ip);
- }
- else
- {
- action_name = "DeletePortMapping";
- action_params = g_strdup_printf (DELETE_PORT_MAPPING_PARAMS,
- portmap, protocol);
- }
- proxy = NULL;
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "GNUNETD", "HTTP-PROXY", &proxy);
- ret =
- gaim_upnp_generate_action_message_and_send (proxy, action_name,
- action_params,
- &ignore_response, NULL);
-
- GNUNET_free (action_params);
- GNUNET_free (proxy);
- return ret;
-}
-
-/* end of upnp.c */
+++ /dev/null
-/**
- * @file upnp/upnp.h Universal Plug N Play API
- * @ingroup core
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#ifndef _GAIM_UPNP_H_
-#define _GAIM_UPNP_H_
-
-#include <libxml/parser.h>
-#include <string.h>
-#include "gnunet_util_lib.h"
-
-#ifdef __cplusplus
-extern "C"
-{
-#if 0 /* keep Emacsens' auto-indent happy */
-}
-#endif
-#endif
-
-/**
- * Sends a discovery request to search for a UPnP enabled IGD that
- * contains the WANIPConnection service that will allow us to receive the
- * public IP address of the IGD, and control it for forwarding ports.
- * The result will be cached for further use.
- */
-int gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock);
-
-/**
- * Gets the IP address from a UPnP enabled IGD that sits on the local
- * network, so when getting the network IP, instead of returning the
- * local network IP, the public IP is retrieved. This is a cached value from
- * the time of the UPnP discovery.
- *
- * @return The IP address of the network, or NULL if something went wrong
- */
-const char *gaim_upnp_get_public_ip (void);
-
-/**
- * Maps Ports in a UPnP enabled IGD that sits on the local network to
- * this gaim client. Essentially, this function takes care of the port
- * forwarding so things like file transfers can work behind NAT firewalls
- *
- * @param cfg configuration to use
- * @param do_add TRUE/GNUNET_YES to add, FALSE/GNUNET_NO to remove
- * @param portmap The port to map to this client
- * @param protocol The protocol to map, either "TCP" or "UDP"
- */
-int gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
- int do_add,
- uint16_t portmap, const char *protocol);
-
-#if 0 /* keep Emacsens' auto-indent happy */
-{
-#endif
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _GAIM_UPNP_H_ */
+++ /dev/null
-/*
- This file is part of GNUnet
- (C) 2006 Christian Grothoff (and other contributing authors)
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file upnp/upnp_init.c
- * @brief API for UPnP access
- * @author Christian Grothoff
- */
-
-#include "platform.h"
-#include "gnunet_util.h"
-#include "upnp.h"
-#include "gnunet_upnp_service.h"
-#include "gnunet_core.h"
-
-static struct GNUNET_GE_Context *ectx;
-
-static struct GNUNET_GC_Configuration *cfg;
-
-static struct GNUNET_CronManager *cron;
-
-static struct GNUNET_Mutex *lock;
-
-typedef struct
-{
- unsigned short port;
- const char *proto;
-} PMap;
-
-static PMap *maps;
-
-static unsigned int maps_size;
-
-static struct GNUNET_ThreadHandle *discovery;
-
-static struct GNUNET_NETWORK_Handle *discovery_socket;
-
-/**
- * Obtain the public/external IP address.
- *
- * @return GNUNET_SYSERR on error, GNUNET_OK on success
- */
-static int
-gnunet_upnp_get_public_ip (struct in_addr *address)
-{
- const char *ip;
- socklen_t socklen;
- struct sockaddr *sa;
- struct sockaddr_in s4;
- int ret;
-
- ip = gaim_upnp_get_public_ip ();
- if (ip == NULL)
- return GNUNET_SYSERR;
- socklen = sizeof (struct sockaddr_in);
- sa = (struct sockaddr *) &s4;
- ret = GNUNET_get_ip_from_hostname (NULL, ip, AF_INET, &sa, &socklen);
- if (ret == GNUNET_OK)
- *address = s4.sin_addr;
- return ret;
-}
-
-static void
-kill_discovery ()
-{
- void *unused;
-
- if (discovery != NULL)
- {
- GNUNET_IO_shutdown (discovery_socket, SHUT_RDWR);
- GNUNET_IO_close (&discovery_socket);
- GNUNET_thread_join (discovery, &unused);
- discovery = NULL;
- }
-}
-
-static void *
-discover_thread ()
-{
- gaim_upnp_discover (ectx, cfg, discovery_socket);
- return NULL;
-}
-
-/**
- * Periodically try to (re)discover UPnP access points.
- */
-static void
-discover (void *unused)
-{
- kill_discovery ();
- discovery_socket = GNUNET_IO_socket (PF_INET, SOCK_DGRAM, 0);
- if (NULL == discovery_socket)
- return;
- discovery = GNUNET_thread_create (&discover_thread, NULL, 1024 * 128);
-}
-
-/**
- * Periodically repeat our requests for port mappings.
- */
-static void
-portmap (void *unused)
-{
- unsigned int i;
-
- GNUNET_mutex_lock (lock);
- for (i = 0; i < maps_size; i++)
- gaim_upnp_change_port_mapping (ectx,
- cfg, GNUNET_NO, maps[i].port,
- maps[i].proto);
- GNUNET_mutex_unlock (lock);
-}
-
-
-/**
- * Get the external IP address for the local machine.
- *
- * @return GNUNET_SYSERR on error, GNUNET_OK on success
- */
-static int
-gnunet_upnp_get_ip (unsigned short port,
- const char *protocol, struct in_addr *address)
-{
- unsigned int i;
-
- GNUNET_mutex_lock (lock);
- for (i = 0; i < maps_size; i++)
- if ((0 == strcmp (maps[i].proto, protocol)) && (maps[i].port == port))
- break;
- if (i == maps_size)
- {
- /* new entry! */
- GNUNET_array_grow (maps, maps_size, maps_size + 1);
- maps[i].proto = protocol;
- maps[i].port = port;
- gaim_upnp_change_port_mapping (ectx, cfg, GNUNET_YES, port, protocol);
- }
- GNUNET_mutex_unlock (lock);
- return gnunet_upnp_get_public_ip (address);
-}
-
-
-/**
- * Get the external IP address for the local machine.
- */
-GNUNET_UPnP_ServiceAPI *
-provide_module_upnp (GNUNET_CoreAPIForPlugins * capi)
-{
- static GNUNET_UPnP_ServiceAPI api;
-
- ectx = capi->ectx;
- cfg = capi->cfg;
- cron = GNUNET_cron_create (ectx);
- lock = GNUNET_mutex_create (GNUNET_NO);
- GNUNET_cron_start (cron);
- GNUNET_cron_add_job (cron, &discover, 0, 5 * GNUNET_CRON_MINUTES, NULL);
- GNUNET_cron_add_job (cron, &portmap, 150 * GNUNET_CRON_SECONDS,
- 5 * GNUNET_CRON_MINUTES, NULL);
- api.get_ip = gnunet_upnp_get_ip;
- return &api;
-}
-
-/**
- * Shutdown UPNP.
- */
-int
-release_module_upnp ()
-{
- unsigned int i;
-
- if (cron == NULL)
- return GNUNET_SYSERR; /* not loaded! */
- for (i = 0; i < maps_size; i++)
- gaim_upnp_change_port_mapping (ectx,
- cfg, GNUNET_NO, maps[i].port,
- maps[i].proto);
- GNUNET_cron_stop (cron);
- GNUNET_cron_del_job (cron, &discover, 5 * GNUNET_CRON_MINUTES, NULL);
- GNUNET_cron_del_job (cron, &portmap, 5 * GNUNET_CRON_MINUTES, NULL);
- GNUNET_cron_destroy (cron);
- kill_discovery ();
- cron = NULL;
- GNUNET_mutex_destroy (lock);
- lock = NULL;
- GNUNET_array_grow (maps, maps_size, 0);
- ectx = NULL;
- cfg = NULL;
- return GNUNET_OK;
-}
-
-
-/* end of init.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- (C) 2006, 2007 Christian Grothoff (and other contributing authors)
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file upnp/upnp_ip.c
- * @brief code to determine the IP of the local machine
- *
- * @author Christian Grothoff
- */
-
-#include <stdlib.h>
-#include "platform.h"
-#include "gnunet_util.h"
-#include "ip.h"
-
-/**
- * Get the IP address for the local machine.
- * @return NULL on error
- */
-char *
-GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
- struct GNUNET_GE_Context *ectx)
-{
- struct in_addr address;
-
- return GNUNET_get_local_ip (cfg, ectx, &address);
-}
-
-
-/* end of ip.c */
+++ /dev/null
-/*
- This file is part of GNUnet.
- (C) 2001, 2002, 2003, 2004, 2005 Christian Grothoff (and other contributing authors)
-
- GNUnet is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published
- by the Free Software Foundation; either version 3, or (at your
- option) any later version.
-
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNUnet; see the file COPYING. If not, write to the
- Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA.
-*/
-
-/**
- * @file upnp/upnp_ip.h
- * @brief
- *
- * @author Christian Grothoff
- */
-
-#ifndef IP_H
-#define IP_H
-
-
-/**
- * Get the IP address for the local machine.
- * @return NULL on error
- */
-char *GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
- struct GNUNET_GE_Context *ectx);
-
-
-#endif
+++ /dev/null
-/*
- * @file upnp_util.cUtility Functions
- * @ingroup core
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#include "platform.h"
-#include "util.h"
-#include "gnunet_util.h"
-
-/* Returns a NULL-terminated string after unescaping an entity
- * (eg. &, < & etc.) starting at s. Returns NULL on failure.*/
-static char *
-detect_entity (const char *text, int *length)
-{
- const char *pln;
- int len;
- int pound;
- char b[7];
- char *buf;
-
- if (!text || *text != '&')
- return NULL;
-
-#define IS_ENTITY(s) (!strncasecmp(text, s, (len = sizeof(s) - 1)))
-
- if (IS_ENTITY ("&"))
- pln = "&";
- else if (IS_ENTITY ("<"))
- pln = "<";
- else if (IS_ENTITY (">"))
- pln = ">";
- else if (IS_ENTITY (" "))
- pln = " ";
- else if (IS_ENTITY ("©"))
- pln = "\302\251"; /* or use g_unichar_to_utf8(0xa9); */
- else if (IS_ENTITY ("""))
- pln = "\"";
- else if (IS_ENTITY ("®"))
- pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */
- else if (IS_ENTITY ("'"))
- pln = "\'";
- else if (*(text + 1) == '#' && (sscanf (text, "&#%u;", £) == 1) &&
- pound != 0 && *(text + 3 + (int) log10 (pound)) == ';')
- {
- buf = GNUNET_convert_string_to_utf8 (NULL,
- (const char *) £,
- 2, "UNICODE");
- if (strlen (buf) > 6)
- buf[6] = '\0';
- strcpy (b, buf);
- pln = b;
- GNUNET_free (buf);
- len = 2;
- while (isdigit ((int) text[len]))
- len++;
- if (text[len] == ';')
- len++;
- }
- else
- return NULL;
-
- if (length)
- *length = len;
- return GNUNET_strdup (pln);
-}
-
-char *
-g_strdup_printf (const char *fmt, ...)
-{
- size_t size;
- char *buf;
- va_list va;
-
- va_start (va, fmt);
- size = VSNPRINTF (NULL, 0, fmt, va) + 1;
- va_end (va);
- buf = GNUNET_malloc (size);
- va_start (va, fmt);
- VSNPRINTF (buf, size, fmt, va);
- va_end (va);
- return buf;
-}
-
-char *
-gaim_unescape_html (const char *html)
-{
- if (html != NULL)
- {
- const char *c = html;
- char *ret = GNUNET_strdup ("");
- char *app;
- while (*c)
- {
- int len;
- char *ent;
-
- if ((ent = detect_entity (c, &len)) != NULL)
- {
- app = g_strdup_printf ("%s%s", ret, ent);
- GNUNET_free (ret);
- ret = app;
- c += len;
- GNUNET_free (ent);
- }
- else if (!strncmp (c, "<br>", 4))
- {
- app = g_strdup_printf ("%s%s", ret, "\n");
- GNUNET_free (ret);
- ret = app;
- c += 4;
- }
- else
- {
- app = g_strdup_printf ("%s%c", ret, *c);
- GNUNET_free (ret);
- ret = app;
- c++;
- }
- }
- return ret;
- }
- return NULL;
-}
-
-
-int
-gaim_str_has_prefix (const char *s, const char *p)
-{
- if ((s == NULL) || (p == NULL))
- return 0;
- return !strncmp (s, p, strlen (p));
-}
-
-/* end of util.c */
+++ /dev/null
-/**
- * @file upnp/upnp_util.h Utility Functions
- * @ingroup core
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * @todo Rename the functions so that they live somewhere in the gaim
- * namespace.
- */
-#ifndef _GAIM_UTIL_H_
-#define _GAIM_UTIL_H_
-
-#include <stdio.h>
-#include <string.h>
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/**
- * Unescapes HTML entities to their literal characters.
- * For example "&" is replaced by '&' and so on.
- * Actually only "&", """, "<" and ">" are currently
- * supported.
- *
- * @param html The string in which to unescape any HTML entities
- *
- * @return the text with HTML entities literalized
- */
- char *gaim_unescape_html (const char *html);
-
-/**
- * Compares two strings to see if the first contains the second as
- * a proper prefix.
- *
- * @param s The string to check.
- * @param p The prefix in question.
- *
- * @return TRUE if p is a prefix of s, otherwise FALSE.
- */
- int gaim_str_has_prefix (const char *s, const char *p);
-
- char *g_strdup_printf (const char *fmt, ...);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
+++ /dev/null
-/**
- * @file upnp/upnp_xmlnode.c XML DOM functions
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-/* A lot of this code at least resembles the code in libxode, but since
- * libxode uses memory pools that we simply have no need for, I decided to
- * write my own stuff. Also, re-writing this lets me be as lightweight
- * as I want to be. Thank you libxode for giving me a good starting point */
-
-#include "platform.h"
-
-#include "util.h"
-#include "gnunet_util.h"
-#include "xmlnode.h"
-
-#include <libxml/parser.h>
-#include <string.h>
-
-
-#ifdef _WIN32
-# define NEWLINE_S "\r\n"
-#else
-# define NEWLINE_S "\n"
-#endif
-
-#define TRUE GNUNET_YES
-#define FALSE GNUNET_NO
-
-#define g_return_if_fail(a) if(!(a)) return;
-#define g_return_val_if_fail(a, val) if(!(a)) return (val);
-
-/**
- * The valid types for an xmlnode
- */
-typedef enum _XMLNodeType
-{
- XMLNODE_TYPE_TAG, /**< Just a tag */
- XMLNODE_TYPE_ATTRIB, /**< Has attributes */
- XMLNODE_TYPE_DATA /**< Has data */
-} XMLNodeType;
-
-typedef struct
-{
- xmlnode *current;
- xmlnode **nodes;
- unsigned int pos;
- unsigned int size;
-} XMLNodePool;
-
-struct _xmlnode
-{
- char *name; /**< The name of the node. */
- char *xmlns; /**< The namespace of the node */
- XMLNodeType type; /**< The type of the node. */
- char *data; /**< The data for the node. */
- size_t data_sz; /**< The size of the data. */
- struct _xmlnode *parent; /**< The parent node or @c NULL.*/
- struct _xmlnode *child; /**< The child node or @c NULL.*/
- struct _xmlnode *lastchild; /**< The last child node or @c NULL.*/
- struct _xmlnode *next; /**< The next node or @c NULL. */
- XMLNodePool *pool;
- int free_pool; /* set to GNUNET_YES for the root node, which must free the pool */
-};
-
-
-static void *
-g_memdup (const void *data, size_t s)
-{
- void *ret;
-
- ret = GNUNET_malloc (s);
- memcpy (ret, data, s);
- return ret;
-}
-
-static char *
-g_string_append_len (char *prefix, const void *data, size_t s)
-{
- char *ret;
-
- ret = g_strdup_printf ("%s%.*s", prefix, s, data);
- GNUNET_free (prefix);
- return ret;
-}
-
-static xmlnode *
-new_node (const char *name, XMLNodeType type, void *user_data)
-{
- xmlnode *node = GNUNET_malloc (sizeof (xmlnode));
-
- node->name = name == NULL ? NULL : GNUNET_strdup (name);
- node->type = type;
- node->pool = user_data;
- if (node->pool->size == node->pool->pos)
- GNUNET_array_grow (node->pool->nodes, node->pool->size,
- node->pool->size * 2 + 64);
- node->pool->nodes[node->pool->pos++] = node;
- node->free_pool = 0;
- return node;
-}
-
-static xmlnode *
-xmlnode_new (const char *name, void *user_data)
-{
- g_return_val_if_fail (name != NULL, NULL);
- return new_node (name, XMLNODE_TYPE_TAG, user_data);
-}
-
-static void
-xmlnode_insert_child (xmlnode * parent, xmlnode * child)
-{
- g_return_if_fail (parent != NULL);
- g_return_if_fail (child != NULL);
-
- child->parent = parent;
- if (parent->lastchild)
- parent->lastchild->next = child;
- else
- parent->child = child;
- parent->lastchild = child;
-}
-
-static xmlnode *
-xmlnode_new_child (xmlnode * parent, const char *name, void *user_data)
-{
- xmlnode *node;
-
- g_return_val_if_fail (parent != NULL, NULL);
- g_return_val_if_fail (name != NULL, NULL);
- node = new_node (name, XMLNODE_TYPE_TAG, user_data);
- xmlnode_insert_child (parent, node);
- return node;
-}
-
-static void
-xmlnode_insert_data (xmlnode * node,
- const char *data, int size, void *user_data)
-{
- xmlnode *child;
- size_t real_size;
-
- g_return_if_fail (node != NULL);
- g_return_if_fail (data != NULL);
- g_return_if_fail (size != 0);
- real_size = size == -1 ? strlen (data) : size;
- child = new_node (NULL, XMLNODE_TYPE_DATA, user_data);
- child->data = g_memdup (data, real_size);
- child->data_sz = real_size;
- xmlnode_insert_child (node, child);
-}
-
-static void
-xmlnode_remove_attrib (xmlnode * node, const char *attr)
-{
- xmlnode *attr_node, *sibling = NULL;
-
- g_return_if_fail (node != NULL);
- g_return_if_fail (attr != NULL);
-
- for (attr_node = node->child; attr_node; attr_node = attr_node->next)
- {
- if (attr_node->type == XMLNODE_TYPE_ATTRIB &&
- !strcmp (attr_node->name, attr))
- {
- if (node->child == attr_node)
- {
- node->child = attr_node->next;
- }
- else
- {
- sibling->next = attr_node->next;
- }
- if (node->lastchild == attr_node)
- {
- node->lastchild = sibling;
- }
- xmlnode_free (attr_node);
- return;
- }
- sibling = attr_node;
- }
-}
-
-static void
-xmlnode_set_attrib (xmlnode * node,
- const char *attr, const char *value, void *user_data)
-{
- xmlnode *attrib_node;
-
- g_return_if_fail (node != NULL);
- g_return_if_fail (attr != NULL);
- g_return_if_fail (value != NULL);
- xmlnode_remove_attrib (node, attr);
- attrib_node = new_node (attr, XMLNODE_TYPE_ATTRIB, user_data);
- attrib_node->data = GNUNET_strdup (value);
- xmlnode_insert_child (node, attrib_node);
-}
-
-static void
-xmlnode_set_namespace (xmlnode * node, const char *xmlns)
-{
- g_return_if_fail (node != NULL);
- GNUNET_free_non_null (node->xmlns);
- node->xmlns = GNUNET_strdup (xmlns);
-}
-
-static const char *
-xmlnode_get_namespace (xmlnode * node)
-{
- g_return_val_if_fail (node != NULL, NULL);
- return node->xmlns;
-}
-
-static void
-freePool (XMLNodePool * pool)
-{
- unsigned int i;
- xmlnode *x;
-
- for (i = 0; i < pool->pos; i++)
- {
- x = pool->nodes[i];
- GNUNET_free_non_null (x->name);
- GNUNET_free_non_null (x->data);
- GNUNET_free_non_null (x->xmlns);
- GNUNET_free (x);
- }
- GNUNET_array_grow (pool->nodes, pool->size, 0);
- GNUNET_free (pool);
-}
-
-void
-xmlnode_free (xmlnode * node)
-{
- g_return_if_fail (node != NULL);
- if (node->free_pool != GNUNET_YES)
- return;
- freePool (node->pool);
-}
-
-static xmlnode *
-xmlnode_get_child_with_namespace (const xmlnode * parent,
- const char *name, const char *ns)
-{
- xmlnode *x;
- xmlnode *ret = NULL;
- char *parent_name;
- char *child_name;
-
- if (parent == NULL)
- return NULL;
- if (name == NULL)
- return NULL;
-
- parent_name = GNUNET_strdup (name);
- child_name = strstr (parent_name, "/");
- if (child_name != NULL)
- {
- child_name[0] = '\0';
- child_name++;
- }
-
- for (x = parent->child; x; x = x->next)
- {
- const char *xmlns = NULL;
- if (ns)
- xmlns = xmlnode_get_namespace (x);
-
- if (x->type == XMLNODE_TYPE_TAG && name
- && !strcmp (parent_name, x->name) && (!ns
- || (xmlns
- && !strcmp (ns, xmlns))))
- {
- ret = x;
- break;
- }
- }
-
- if (child_name && ret)
- ret = xmlnode_get_child (ret, child_name);
-
- GNUNET_free (parent_name);
- return ret;
-}
-
-xmlnode *
-xmlnode_get_child (const xmlnode * parent, const char *name)
-{
- return xmlnode_get_child_with_namespace (parent, name, NULL);
-}
-
-char *
-xmlnode_get_data (xmlnode * node)
-{
- char *str = NULL;
- xmlnode *c;
-
- if (node == NULL)
- return NULL;
- for (c = node->child; c; c = c->next)
- {
- if (c->type == XMLNODE_TYPE_DATA)
- {
- if (!str)
- str = GNUNET_strdup ("");
- str = g_string_append_len (str, c->data, c->data_sz);
- }
- }
- if (str == NULL)
- return NULL;
-
- return str;
-}
-
-static void
-xmlnode_parser_element_start_libxml (void *user_data,
- const xmlChar * element_name,
- const xmlChar * prefix,
- const xmlChar * xmlns,
- int nb_namespaces,
- const xmlChar ** namespaces,
- int nb_attributes,
- int nb_defaulted,
- const xmlChar ** attributes)
-{
- XMLNodePool *xpd = user_data;
- xmlnode *node;
- int i;
-
- if (!element_name)
- return;
- if (xpd->current)
- node =
- xmlnode_new_child (xpd->current, (const char *) element_name,
- user_data);
- else
- node = xmlnode_new ((const char *) element_name, user_data);
-
- xmlnode_set_namespace (node, (const char *) xmlns);
-
- for (i = 0; i < nb_attributes * 5; i += 5)
- {
- char *txt;
- int attrib_len = attributes[i + 4] - attributes[i + 3];
- char *attrib = GNUNET_malloc (attrib_len + 1);
- memcpy (attrib, attributes[i + 3], attrib_len);
- attrib[attrib_len] = '\0';
- txt = attrib;
- attrib = gaim_unescape_html (txt);
- GNUNET_free (txt);
- xmlnode_set_attrib (node, (const char *) attributes[i], attrib,
- user_data);
- GNUNET_free (attrib);
- }
- xpd->current = node;
-}
-
-static void
-xmlnode_parser_element_end_libxml (void *user_data,
- const xmlChar * element_name,
- const xmlChar * prefix,
- const xmlChar * xmlns)
-{
- XMLNodePool *xpd = user_data;
-
- if (!element_name || !xpd->current)
- return;
- if (xpd->current->parent)
- {
- if (!xmlStrcmp ((xmlChar *) xpd->current->name, element_name))
- xpd->current = xpd->current->parent;
- }
-}
-
-static void
-xmlnode_parser_element_text_libxml (void *user_data,
- const xmlChar * text, int text_len)
-{
- XMLNodePool *xpd = user_data;
-
- if (!xpd->current || !text || !text_len)
- return;
- xmlnode_insert_data (xpd->current,
- (const char *) text, text_len, user_data);
-}
-
-static xmlSAXHandler xmlnode_parser_libxml = {
- .internalSubset = NULL,
- .isStandalone = NULL,
- .hasInternalSubset = NULL,
- .hasExternalSubset = NULL,
- .resolveEntity = NULL,
- .getEntity = NULL,
- .entityDecl = NULL,
- .notationDecl = NULL,
- .attributeDecl = NULL,
- .elementDecl = NULL,
- .unparsedEntityDecl = NULL,
- .setDocumentLocator = NULL,
- .startDocument = NULL,
- .endDocument = NULL,
- .startElement = NULL,
- .endElement = NULL,
- .reference = NULL,
- .characters = xmlnode_parser_element_text_libxml,
- .ignorableWhitespace = NULL,
- .processingInstruction = NULL,
- .comment = NULL,
- .warning = NULL,
- .error = NULL,
- .fatalError = NULL,
- .getParameterEntity = NULL,
- .cdataBlock = NULL,
- .externalSubset = NULL,
- .initialized = XML_SAX2_MAGIC,
- ._private = NULL,
- .startElementNs = xmlnode_parser_element_start_libxml,
- .endElementNs = xmlnode_parser_element_end_libxml,
- .serror = NULL
-};
-
-xmlnode *
-xmlnode_from_str (const char *str, int size)
-{
- XMLNodePool *xpd;
- xmlnode *ret;
- size_t real_size;
-
- g_return_val_if_fail (str != NULL, NULL);
-
- real_size = size < 0 ? strlen (str) : size;
- xpd = GNUNET_malloc (sizeof (XMLNodePool));
- memset (xpd, 0, sizeof (XMLNodePool));
- if (xmlSAXUserParseMemory (&xmlnode_parser_libxml, xpd, str, real_size) < 0)
- {
- freePool (xpd);
- return NULL;
- }
- ret = xpd->current;
- ret->free_pool = GNUNET_YES;
- return ret;
-}
-
-xmlnode *
-xmlnode_get_next_twin (xmlnode * node)
-{
- xmlnode *sibling;
- const char *ns = xmlnode_get_namespace (node);
-
- g_return_val_if_fail (node != NULL, NULL);
- g_return_val_if_fail (node->type == XMLNODE_TYPE_TAG, NULL);
-
- for (sibling = node->next; sibling; sibling = sibling->next)
- {
- const char *xmlns = NULL;
- if (ns)
- xmlns = xmlnode_get_namespace (sibling);
-
- if (sibling->type == XMLNODE_TYPE_TAG
- && !strcmp (node->name, sibling->name) && (!ns
- || (xmlns
- && !strcmp (ns,
- xmlns))))
- return sibling;
- }
- return NULL;
-}
+++ /dev/null
-/**
- * @file upnp/upnp_xmlnode.h XML DOM functions
- * @ingroup core
- *
- * gaim
- *
- * Gaim is the legal property of its developers, whose names are too numerous
- * to list here. Please refer to the COPYRIGHT file distributed with this
- * source distribution.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef _GGAIM_XMLNODE_H_
-#define _GGAIM_XMLNODE_H_
-
-
-#ifdef __cplusplus
-extern "C"
-{
-#endif
-
-/**
- * An xmlnode.
- */
- typedef struct _xmlnode xmlnode;
-
-/**
- * Gets a child node named name.
- *
- * @param parent The parent node.
- * @param name The child's name.
- *
- * @return The child or NULL.
- */
- xmlnode *xmlnode_get_child (const xmlnode * parent, const char *name);
-
-/**
- * Gets the next node with the same name as node.
- *
- * @param node The node of a twin to find.
- *
- * @return The twin of node or NULL.
- */
- xmlnode *xmlnode_get_next_twin (xmlnode * node);
-
-/**
- * Gets data from a node.
- *
- * @param node The node to get data from.
- *
- * @return The data from the node. You must g_free
- * this string when finished using it.
- */
- char *xmlnode_get_data (xmlnode * node);
-
-/**
- * Creates a node from a string of XML. Calling this on the
- * root node of an XML document will parse the entire document
- * into a tree of nodes, and return the xmlnode of the root.
- *
- * @param str The string of xml.
- * @param size The size of the string, or -1 if @a str is
- * NUL-terminated.
- *
- * @return The new node.
- */
- xmlnode *xmlnode_from_str (const char *str, int size);
-
-/**
- * Frees a node and all of it's children.
- *
- * @param node The node to free.
- */
- void xmlnode_free (xmlnode * node);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _GAIM_XMLNODE_H_ */