aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-05-04 12:22:55 +0000
committerChristian Grothoff <christian@grothoff.org>2011-05-04 12:22:55 +0000
commitc8a5f498155ba7e89273a6882e29291fcd66dc8b (patch)
treeb649ff10ada8ac94e29ff950885b0fdf8c06ae5e /src
parent49a8c3c3374726914aa2c3d9d38a4683e8332c60 (diff)
downloadgnunet-c8a5f498155ba7e89273a6882e29291fcd66dc8b.tar.gz
gnunet-c8a5f498155ba7e89273a6882e29291fcd66dc8b.zip
dead
Diffstat (limited to 'src')
-rw-r--r--src/upnp/Makefile.am61
-rw-r--r--src/upnp/draft-cheshire-nat-pmp.txt1160
-rw-r--r--src/upnp/test_upnp.c110
-rw-r--r--src/upnp/upnp.c721
-rw-r--r--src/upnp/upnp.h80
-rw-r--r--src/upnp/upnp_init.c208
-rw-r--r--src/upnp/upnp_ip.c47
-rw-r--r--src/upnp/upnp_ip.h40
-rw-r--r--src/upnp/upnp_util.c152
-rw-r--r--src/upnp/upnp_util.h68
-rw-r--r--src/upnp/upnp_xmlnode.c487
-rw-r--r--src/upnp/upnp_xmlnode.h92
12 files changed, 0 insertions, 3226 deletions
diff --git a/src/upnp/Makefile.am b/src/upnp/Makefile.am
deleted file mode 100644
index 83ccf5b46..000000000
--- a/src/upnp/Makefile.am
+++ /dev/null
@@ -1,61 +0,0 @@
1INCLUDES = -I$(top_srcdir)/src/include
2
3if MINGW
4 WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols
5endif
6
7if USE_COVERAGE
8 AM_CFLAGS = -fprofile-arcs -ftest-coverage
9endif
10
11lib_LTLIBRARIES = libgnunetupnp.la
12
13libgnunetupnp_la_SOURCES = \
14 upnp.c upnp.h \
15 upnp_ip.c upnp_ip.h \
16 upnp_util.c upnp_util.h \
17 upnp_xmlnode.c upnp_xmlnode.h
18libgnunetupnp_la_CFLAGS = \
19 -I$(top_scrdir)/include \
20 @LIBCURL_CPPFLAGS@ @XML_CPPFLAGS@
21libgnunetupnp_la_LIBADD = \
22 $(top_builddir)/src/util/libgnunetutil.la \
23 $(GN_LIBINTL) @EXT_LIBS@ @XML_LIBS@ @LIBCURL@
24libgnunetupnp_la_LDFLAGS = \
25 $(GN_LIB_LDFLAGS) $(WINFLAGS) \
26 -version-info 0:0:0
27
28
29bin_PROGRAMS = \
30 gnunet-upnp \
31 gnunet-service-upnp
32
33gnunet_upnp_SOURCES = \
34 gnunet-upnp.c
35gnunet_upnp_LDADD = \
36 $(top_builddir)/src/upnp/libgnunetupnp.la \
37 $(top_builddir)/src/util/libgnunetutil.la \
38 $(GN_LIBINTL)
39
40gnunet_service_upnp_SOURCES = \
41 gnunet-service-upnp.c
42gnunet_service_upnp_LDADD = \
43 $(top_builddir)/src/util/libgnunetutil.la \
44 $(GN_LIBINTL)
45
46
47check_PROGRAMS = \
48 test_upnp
49
50if !DISABLE_TEST_RUN
51TESTS = $(check_PROGRAMS)
52endif
53
54test_upnp_SOURCES = \
55 test_upnp.c
56test_upnp_LDADD = \
57 $(top_builddir)/src/transport/libgnunetupnp.la \
58 $(top_builddir)/src/util/libgnunetutil.la
59
60
61# EXTRA_DIST = test_upnp_data.conf
diff --git a/src/upnp/draft-cheshire-nat-pmp.txt b/src/upnp/draft-cheshire-nat-pmp.txt
deleted file mode 100644
index 727b5fad7..000000000
--- a/src/upnp/draft-cheshire-nat-pmp.txt
+++ /dev/null
@@ -1,1160 +0,0 @@
1Document: draft-cheshire-nat-pmp-02.txt Stuart Cheshire
2Internet-Draft Marc Krochmal
3Category: Standards Track Apple Computer, Inc.
4Expires 14th March 2007 Kiren Sekar
5 Sharpcast, Inc.
6 14th September 2006
7
8 NAT Port Mapping Protocol (NAT-PMP)
9
10 <draft-cheshire-nat-pmp-02.txt>
11
12Status of this Memo
13
14 By submitting this Internet-Draft, each author represents that any
15 applicable patent or other IPR claims of which he or she is aware
16 have been or will be disclosed, and any of which he or she becomes
17 aware will be disclosed, in accordance with Section 6 of BCP 79.
18 For the purposes of this document, the term "BCP 79" refers
19 exclusively to RFC 3979, "Intellectual Property Rights in IETF
20 Technology", published March 2005.
21
22 Internet-Drafts are working documents of the Internet Engineering
23 Task Force (IETF), its areas, and its working groups. Note that
24 other groups may also distribute working documents as Internet-
25 Drafts.
26
27 Internet-Drafts are draft documents valid for a maximum of six months
28 and may be updated, replaced, or obsoleted by other documents at any
29 time. It is inappropriate to use Internet-Drafts as reference
30 material or to cite them other than as "work in progress."
31
32 The list of current Internet-Drafts can be accessed at
33 http://www.ietf.org/1id-abstracts.html
34
35 The list of Internet-Draft Shadow Directories can be accessed at
36 http://www.ietf.org/shadow.html
37
38
39Abstract
40
41 This document describes a protocol for automating the process of
42 creating Network Address Translation (NAT) port mappings. Included
43 in the protocol is a method for retrieving the public IP address of
44 a NAT gateway, thus allowing a client to make this public IP address
45 and port number known to peers that may wish to communicate with it.
46 This protocol is implemented in current Apple products including
47 Mac OS X, Bonjour for Windows, and AirPort wireless base stations.
48
49
50
51
52
53
54
55
56
57
58Expires 14th March 2007 Cheshire, et al. [Page 1]
59
60Internet Draft NAT Port Mapping Protocol 14th September 2006
61
62
631. Introduction
64
65 Network Address Translation (NAT) is a method of sharing one public
66 internet address with a number of devices. This document is focused
67 on what "IP Network Address Translator (NAT) Terminology and
68 Considerations" [RFC 2663] calls "NAPTs" (Network Address/Port
69 Translators). A full description of NAT is beyond the scope of this
70 document. The following brief overview will cover the aspects
71 relevant to this port mapping protocol. For more information on
72 NAT, see "Traditional IP Network Address Translator" [RFC 3022].
73
74 NATs have one or more public IP addresses. A private network is set
75 up behind the NAT. Devices behind the NAT are assigned private
76 addresses and the private address of the NAT device is used as the
77 gateway.
78
79 When a packet from any device behind the NAT is sent to an address on
80 the public internet, the packet first passes through the NAT box. The
81 NAT box looks at the source port and address. In some cases, a NAT
82 will also keep track of the destination port and address. The NAT
83 then creates a mapping from the private address and private port to a
84 public address and public port if a mapping does not already exist.
85 The NAT box replaces the private address and port number in the
86 packet with the public entries from the mapping and sends the packet
87 on to the next gateway.
88
89 When a packet from any address on the internet is received on the
90 NAT's public side, the NAT will look up the destination port (public
91 port) in the list of mappings. If an entry is found, it will contain
92 the private address and port that the packet should be sent to. The
93 NAT gateway will then rewrite the destination address and port with
94 those from the mapping. The packet will then be forwarded to the new
95 destination addresses. If the packet did not match any mapping, the
96 packet will most likely be dropped. Various NATs implement different
97 strategies to handle this. The important thing to note is that if
98 there is no mapping, the NAT does not know which private address the
99 packet should be sent to.
100
101 Mappings are usually created automatically as a result of observing
102 outbound traffic. There are a few exceptions. Some NATs may allow
103 manually-created permanent mappings that map a public port to a
104 specific private IP address and port. Such a mapping allows incoming
105 connections to the device with that private address. Some NATs also
106 implement a default mapping where any inbound traffic that does not
107 match a mapping will always be forwarded to a specific private
108 address. Both types of mappings are usually set up manually through
109 some configuration tool.
110
111 Without these manually-created inbound port mappings, clients behind
112 the NAT would be unable to receive inbound connections, which
113 represents a loss of connectivity when compared to the original
114
115
116Expires 14th March 2007 Cheshire, et al. [Page 2]
117
118Internet Draft NAT Port Mapping Protocol 14th September 2006
119
120
121 Internet architecture [ETEAISD]. For those who view this loss of
122 connectivity as a bad thing, NAT-PMP allows clients to operate much
123 more like a host directly connected to the unrestricted public
124 Internet, with an unrestricted public IP address. NAT-PMP allows
125 client hosts to communicate with the NAT gateway to request the
126 creation of inbound mappings on demand. Having created a NAT mapping
127 to allow inbound connections, the client can then record its public
128 IP address and public port number in a public registry (e.g. the
129 world-wide Domain Name System) or otherwise make it accessible to
130 peers that wish to communicate with it.
131
132
1332. Conventions and Terminology Used in this Document
134
135 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
136 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
137 document are to be interpreted as described in "Key words for use in
138 RFCs to Indicate Requirement Levels" [RFC 2119].
139
140
1413. Protocol and Packet Format
142
143 NAT Port Mapping Protocol runs over UDP. Every packet starts with an
144 8 bit version followed by an 8 bit operation code.
145
146 This document specifies version 0 of the protocol. Any NAT-PMP
147 gateway implementing this version of the protocol, receiving a
148 packet with a version number other than 0, MUST return result code 1
149 (Unsupported Version).
150
151 Opcodes between 0 and 127 are client requests. Opcodes from 128 to
152 255 are server responses. Responses always contain a 16 bit result
153 code in network byte order. A result code of zero indicates success.
154 Responses also contain a 32 bit unsigned integer corresponding to the
155 number of seconds since the NAT gateway was rebooted or since its
156 port mapping state was reset.
157
158 This protocol SHOULD only be used when the client determines that
159 its primary IPv4 address is in one of the private IP address ranges
160 defined in "Address Allocation for Private Internets" [RFC 1918].
161 This includes the address ranges 10/8, 172.16/12, and 192.168/16.
162
163 Clients always send their Port Mapping Protocol requests to their
164 default gateway, as learned via DHCP [RFC 2131], or similar means.
165 This protocol is designed for small home networks, with a single
166 logical link (subnet) where the client's default gateway is also the
167 NAT translator for that network. For more complicated networks where
168 the NAT translator is some device other than the client's default
169 gateway, this protocol is not appropriate.
170
171
172
173
174Expires 14th March 2007 Cheshire, et al. [Page 3]
175
176Internet Draft NAT Port Mapping Protocol 14th September 2006
177
178
1793.1 Requests and Responses
180
181 NAT gateways are often low-cost devices, with limited memory and
182 CPU speed. For this reason, to avoid making excessive demands on
183 the NAT gateway, clients machines SHOULD NOT issue multiple requests
184 simultaneously in parallel. If a client needs to perform multiple
185 requests (e.g. on boot, wake from sleep, network connection, etc.)
186 it SHOULD queue them and issue them serially one at a time. Once the
187 NAT gateway responds to one request the client machine may issue the
188 next. In the case of a fast NAT gateway, the client may be able to
189 complete requests at a rate of hundreds per second. In the case of
190 a slow NAT gateway that takes perhaps half a second to respond to
191 a NAT-PMP request, the client SHOULD respect this and allow the
192 NAT gateway to operate at the pace it can manage, and not overload
193 it by issuing requests faster than the rate it's answering them.
194
195 To determine the puclic IP address or request a port mapping,
196 a NAT-PMP client sends its request packet to port 5351 of its
197 configured gateway address, and waits 250ms for a response. If no
198 NAT-PMP response is received from the gateway after 250ms, the client
199 retransmits its request and waits 500ms. The client SHOULD repeat
200 this process with the interval between attempts doubling each time.
201 If, after sending its 9th attempt (and then waiting for 64 seconds),
202 the client has still received no response, then it SHOULD conclude
203 that this gateway does not support NAT Port Mapping Protocol and
204 MAY log an error message indicating this fact. In addition, if the
205 NAT-PMP client receives an "ICMP Port Unreachable" message from the
206 gateway for port 5351 then it can skip any remaining retransmissions
207 and conclude immediately that the gateway does not support NAT-PMP.
208
209 As a performance optimization the client MAY record this information
210 and use it to suppress further attempts to use NAT-PMP, but the
211 client should not retain this information for too long. In
212 particular, any event that may indicate a potential change of gateway
213 or a change in gateway configuration (hardware link change
214 indication, change of gateway MAC address, acquisition of new DHCP
215 lease, receipt of NAT-PMP announcement packet from gateway, etc.)
216 should cause the client to discard its previous information regarding
217 the gateway's lack of NAT-PMP support, and send its next NAT-PMP
218 request packet normally.
219
220
2213.2 Determining the Public Address
222
223 To determine the public address, the client behind the NAT sends the
224 following UDP payload to port 5351 of the configured gateway address:
225
226 0 1
227 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
228 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
229 | Vers = 0 | OP = 0 |
230 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
231
232Expires 14th March 2007 Cheshire, et al. [Page 4]
233
234Internet Draft NAT Port Mapping Protocol 14th September 2006
235
236
237 A compatible NAT gateway MUST generate a response with the following
238 format:
239
240 0 1 2 3
241 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
242 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
243 | Vers = 0 | OP = 128 + 0 | Result Code |
244 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
245 | Seconds Since Start of Epoch |
246 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
247 | Public IP Address (a.b.c.d) |
248 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
249
250 This response indicates that the NAT gateway implements this version
251 of the protocol and returns the public IP address of the NAT gateway.
252 If the result code is non-zero, the value of Public IP Address is
253 undefined (MUST be set to zero on transmission, and MUST be ignored
254 on reception).
255
256 The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
257 with the time elapsed since its port mapping table was initialized on
258 startup or reset for any other reason (see Section 3.6 "Seconds Since
259 Start of Epoch").
260
261 Upon receiving the response packet, the client MUST check the source
262 IP address, and silently discard the packet if the address is not the
263 address of the gateway to which the request was sent.
264
265
2663.2.1 Announcing Address Changes
267
268 When the public IP address of the NAT changes, the NAT gateway MUST
269 send a gratuitous response to the link-local multicast address
270 224.0.0.1, port 5351 with the packet format above to notify clients
271 of the new public IP address. To accommodate packet loss, the
272 NAT gateway SHOULD multicast 10 address change notifications.
273 The interval between the first two notifications SHOULD be 250ms,
274 and the interval between each subsequent notification SHOULD double.
275
276 Upon receiving a gratuitous address change announcement packet,
277 the client MUST check the source IP address, and silently discard
278 the packet if the address is not the address of the client's
279 current configured gateway. This is to guard against inadvertent
280 misconfigurations where there may be more than one NAT gateway
281 active on the network.
282
283
284
285
286
287
288
289
290Expires 14th March 2007 Cheshire, et al. [Page 5]
291
292Internet Draft NAT Port Mapping Protocol 14th September 2006
293
294
2953.3 Creating a Mapping
296
297 To create a mapping, the client sends a UDP packet to port 5351
298 of the gateway's private IP address with the following format:
299
300 0 1 2 3
301 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
302 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
303 | Vers = 0 | OP = x | Reserved (MUST be zero) |
304 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
305 | Private Port | Requested Public Port |
306 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
307 | Requested Port Mapping Lifetime in Seconds |
308 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
309
310 Opcodes supported:
311 1 - Map UDP
312 2 - Map TCP
313
314 The Reserved field MUST be set to zero on transmission and MUST
315 be ignored on reception.
316
317 The Private Port is set to the local port on which the client is
318 listening.
319
320 The Requested Public Port SHOULD usually be set to the same value as
321 the local Private Port, or zero if the client has no preference for
322 what port is assigned. However, the gateway is not obliged to assign
323 the port requested, and may choose not to, either for policy reasons
324 (e.g. port 80 is reserved and clients may not request it) or because
325 that port has already been assigned to some other client. Because
326 of this, some product developers have questioned the value of having
327 the Requested Public Port field at all. The reason is for failure
328 recovery. Most low-cost home NAT gateways do not record temporary
329 port mappings in persistent storage, so if the gateway crashes or is
330 rebooted, all the mappings are lost. A renewal packet is formatted
331 identically to an initial mapping request packet, except that for
332 renewals the client sets the Requested Public Port field to the
333 port the gateway actually assigned, rather than the port the client
334 originally wanted. When a freshly-rebooted NAT gateway receives a
335 renewal packet from a client, it appears to the gateway just like
336 an ordinary initial request for a port mapping, except that in this
337 case the Requested Public Port is likely to be one that the NAT
338 gateway *is* willing to allocate (it allocated it to this client
339 right before the reboot, so it should presumably be willing to
340 allocate it again).
341
342 The RECOMMENDED Port Mapping Lifetime is 3600 seconds.
343
344
345
346
347
348Expires 14th March 2007 Cheshire, et al. [Page 6]
349
350Internet Draft NAT Port Mapping Protocol 14th September 2006
351
352
353 After sending the port mapping request, the client then waits for the
354 NAT gateway to respond. If after 250ms, the gateway doesn't respond,
355 the client SHOULD re-issue its request as described above in Section
356 3.1 "Requests and Responses".
357
358 The NAT gateway responds with the following packet format:
359
360 0 1 2 3
361 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
362 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
363 | Vers = 0 | OP = 128 + x | Result Code |
364 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
365 | Seconds Since Start of Epoch |
366 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
367 | Private Port | Mapped Public Port |
368 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
369 | Port Mapping Lifetime in Seconds |
370 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
371
372 The 'x' in the OP field MUST match what the client requested. Some
373 NAT gateways are incapable of creating a UDP port mapping without
374 also creating a corresponding TCP port mapping, and vice versa, and
375 these gateways MUST NOT implement NAT Port Mapping Protocol until
376 this deficiency is fixed. A NAT gateway which implements this
377 protocol MUST be able to create TCP-only and UDP-only port mappings.
378
379 If a NAT gateway silently creates a pair of mappings for a client
380 that only requested one mapping, then it may expose that client to
381 receiving inbound UDP packets or inbound TCP connection requests
382 that it did not ask for and does not want.
383
384 While a NAT gateway MUST NOT automatically create mappings for TCP
385 when the client requests UDP, and vice versa, the NAT gateway MUST
386 reserve the companion port so the same client can choose to map it
387 in the future. For example, if a client requests to map TCP port 80,
388 as long as the client maintains the lease for that TCP port mapping,
389 another client with a different IP address MUST NOT be able to
390 successfully acquire the mapping for UDP port 80.
391
392 The client normally requests the public port matching the private
393 port. If that public port is not available, the NAT gateway MUST
394 return a public port that is available or return an error code if
395 no ports are available.
396
397 The source address of the packet MUST be used for the private address
398 in the mapping. This protocol is not intended to facilitate one
399 device behind a NAT creating mappings for other devices. If there
400 are legacy devices that require inbound mappings, permanent mappings
401 can be created manually by the administrator, just as they are today.
402
403
404
405
406Expires 14th March 2007 Cheshire, et al. [Page 7]
407
408Internet Draft NAT Port Mapping Protocol 14th September 2006
409
410
411 If a mapping already exists for a given private port on a given local
412 client (whether that mapping was created explicitly using NAT-PMP,
413 implicitly as a result of an outgoing TCP SYN packet, or manually by
414 a human administrator) and that client requests another mapping for
415 the same private port (possibly requesting a different public port)
416 then the mapping request should succeed, returning the already-
417 assigned public port. This is necessary to handle the case where
418 a client requests a mapping with requested public port X, and is
419 granted a mapping with actual public port Y, but the acknowledgement
420 packet gets lost. When the client retransmits its mapping request,
421 it should get back the same positive acknowledgement as was sent (and
422 lost) the first time.
423
424 The NAT gateway SHOULD NOT accept mapping requests destined to the
425 NAT gateway's public IP address or received on its public network
426 interface. Only packets received on the private interface(s) with
427 a destination address matching the private address(es) of the NAT
428 gateway should be allowed.
429
430 The NAT gateway MUST fill in the "Seconds Since Start of Epoch" field
431 with the time elapsed since its port mapping table was initialized on
432 startup or reset for any other reason (see Section 3.6 "Seconds Since
433 Start of Epoch").
434
435 The Port Mapping Lifetime is an unsigned integer in seconds. The NAT
436 gateway MAY reduce the lifetime from what the client requested. The
437 NAT gateway SHOULD NOT offer a lease lifetime greater than that
438 requested by the client.
439
440 Upon receiving the response packet, the client MUST check the source
441 IP address, and silently discard the packet if the address is not the
442 address of the gateway to which the request was sent.
443
444 The client SHOULD begin trying to renew the mapping halfway to expiry
445 time, like DHCP. The renewal packet should look exactly the same as
446 a request packet, except that the client SHOULD set the requested
447 public port to what the NAT gateway previously mapped, not what the
448 client originally requested. As described above, this enables the
449 gateway to automatically recover its mapping state after a crash or
450 reboot.
451
452
4533.4 Destroying a Mapping
454
455 A mapping may be destroyed in a variety of ways. If a client fails
456 to renew a mapping, then when its lifetime expires the mapping MUST
457 be automatically deleted. In the common case where the gateway
458 device is a combined DHCP server and NAT gateway, when a client's
459 DHCP address lease expires, the gateway device MAY automatically
460 delete any mappings belonging to that client. Otherwise a new client
461 being assigned the same IP address could receive unexpected inbound
462
463
464Expires 14th March 2007 Cheshire, et al. [Page 8]
465
466Internet Draft NAT Port Mapping Protocol 14th September 2006
467
468
469 UDP packets or inbound TCP connection requests that it did not ask
470 for and does not want.
471
472 A client MAY also send an explicit packet to request deletion of a
473 mapping that is no longer needed. A client requests explicit
474 deletion of a mapping by sending a message to the NAT gateway
475 requesting the mapping, with the Requested Lifetime in Seconds set
476 to 0. The requested public port MUST be set to zero by the client
477 on sending, and MUST be ignored by the gateway on reception.
478
479 When a mapping is destroyed successfully as a result of the client
480 explicitly requesting the deletion, the NAT gateway MUST send a
481 response packet which is formatted as defined in section 3.3
482 "Creating a Mapping". The response MUST contain a result code of 0,
483 the private port as indicated in the deletion request, a public port
484 of 0, and a lifetime of 0. The NAT gateway MUST respond to a request
485 to destroy a mapping that does not exist as if the request were
486 successful. This is because of the case where the acknowledgement is
487 lost, and the client retransmits its request to delete the mapping.
488 In this case the second request to delete the mapping MUST return the
489 same response packet as the first request.
490
491 If the deletion request was unsuccessful, the response MUST contain a
492 non-zero result code and the requested mapping; the lifetime is
493 undefined (MUST be set to zero on transmission, and MUST be ignored
494 on reception). If the client attempts to delete a port mapping which
495 was manually assigned by some kind of configuration tool, the NAT
496 gateway MUST respond with a 'Not Authorized' error, result code 2.
497
498 When a mapping is destroyed as a result of its lifetime expiring or
499 for any other reason, if the NAT gateway's internal state indicates
500 that there are still active TCP connections traversing that now-
501 defunct mapping, then the NAT gateway SHOULD send appropriately-
502 constructed TCP RST (reset) packets both to the local client and to
503 the remote peer on the Internet to terminate that TCP connection.
504
505 A client can request the explicit deletion of all its UDP or TCP
506 mappings by sending the same deletion request to the NAT gateway
507 with public port, private port, and lifetime set to 0. A client MAY
508 choose to do this when it first acquires a new IP address in order to
509 protect itself from port mappings that were performed by a previous
510 owner of the IP address. After receiving such a deletion request,
511 the gateway MUST delete all its UDP or TCP port mappings (depending
512 on the opcode). The gateway responds to such a deletion request with
513 a response as described above, with the private port set to zero. If
514 the gateway is unable to delete a port mapping, for example, because
515 the mapping was manually configured by the administrator, the gateway
516 MUST still delete as many port mappings as possible, but respond with
517 a non-zero result code. The exact result code to return depends on
518 the cause of the failure. If the gateway is able to successfully
519 delete all port mappings as requested, it MUST respond with a result
520 code of 0.
521
522Expires 14th March 2007 Cheshire, et al. [Page 9]
523
524Internet Draft NAT Port Mapping Protocol 14th September 2006
525
526
5273.5 Result Codes
528
529 Currently defined result codes:
530 0 - Success
531 1 - Unsupported Version
532 2 - Not Authorized/Refused
533 (e.g. box supports mapping, but user has turned feature off)
534 3 - Network Failure
535 (e.g. NAT box itself has not obtained a DHCP lease)
536 4 - Out of resources
537 (NAT box cannot create any more mappings at this time)
538 5 - Unsupported opcode
539
540 If the result code is non-zero, the format of the packet following
541 the result code may be truncated. For example, if the client sends a
542 request to the server with an opcode of 17 and the server does not
543 recognize that opcode, the server SHOULD respond with a message where
544 the opcode is 17 + 128 and the result code is 5 (opcode not
545 supported). Since the server does not understand the format of
546 opcode 17, it may not know what to place after the result code. In
547 some cases, relevant data may follow the opcode to identify the
548 operation that failed. For example, a client may request a mapping
549 but that mapping may fail due to resource exhaustion. The server
550 SHOULD respond with the result code to indicate resource exhaustion
551 (4) followed by the requested port mapping so the client may identify
552 which operation failed.
553
554 Clients MUST be able to properly handle result codes not defined in
555 this document. Undefined results codes MUST be treated as fatal
556 errors of the request.
557
558
5593.6 Seconds Since Start of Epoch
560
561 Every packet sent by the NAT gateway includes a "Seconds since start
562 of epoch" field (SSSOE). If the NAT gateway resets or loses the
563 state of its port mapping table, due to reboot, power failure, or any
564 other reason, it MUST reset its epoch time and begin counting SSSOE
565 from 0 again. Whenever a client receives any packet from the NAT
566 gateway, either gratuitously or in response to a client request, the
567 client computes its own conservative estimate of the expected SSSOE
568 value by taking the SSSOE value in the last packet it received from
569 the gateway and adding 7/8 (87.5%) of the time elapsed since that
570 packet was received. If the SSSOE in the newly received packet is
571 less than the client's conservative estimate by more than one second,
572 then the client concludes that the NAT gateway has undergone a reboot
573 or other loss of port mapping state, and the client MUST immediately
574 renew all its active port mapping leases as described in Section 3.7
575 "Recreating Mappings On NAT Gateway Reboot".
576
577
578
579
580Expires 14th March 2007 Cheshire, et al. [Page 10]
581
582Internet Draft NAT Port Mapping Protocol 14th September 2006
583
584
5853.7 Recreating Mappings On NAT Gateway Reboot
586
587 The NAT gateway MAY store mappings in persistent storage so when it
588 is powered off or rebooted, it remembers the port mapping state of
589 the network.
590
591 However, maintaining this state is not essential for correct
592 operation. When the NAT gateway powers on or clears its port mapping
593 state as the result of a configuration change, it MUST reset the
594 epoch time and re-announce its IP address as described in Section
595 3.2.1 "Announcing Address Changes". Reception of this packet where
596 time has apparently gone backwards serves as a hint to clients
597 on the network that they SHOULD immediately send renewal packets
598 (to immediately recreate their mappings) instead of waiting until
599 the originally scheduled time for those renewals. Clients who miss
600 receiving those gateway announcement packets for any reason will
601 still renew their mappings at the originally scheduled time and cause
602 their mappings to be recreated; it will just take a little longer for
603 these clients.
604
605 A mapping renewal packet is formatted identically to an original
606 mapping request; from the point of view of the client it is a
607 renewal of an existing mapping, but from the point of view of the
608 freshly-rebooted NAT gateway it appears as a new mapping request.
609
610 This self-healing property of the protocol is very important.
611
612 The remarkable reliability of the Internet as a whole derives
613 in large part from the fact that important state is held in the
614 endpoints, not in the network itself [ETEAISD]. Power-cycling an
615 Ethernet switch results only in a brief interruption in the flow
616 of packets; established TCP connections through that switch are not
617 broken, merely delayed for a few seconds. Indeed, an old Ethernet
618 switch can even be replaced with a new one, and as long as the cables
619 are transferred over reasonably quickly, after the upgrade all the
620 TCP connections that were previously going though the old switch will
621 be unbroken and now going through the new one. The same is true of
622 IP routers, wireless base stations, etc. The one exception is NAT
623 gateways. Because the port mapping state is required for the NAT
624 gateway to know where to forward inbound packets, loss of that state
625 breaks connectivity through the NAT gateway. By allowing clients to
626 detect when this loss of NAT gateway state has occurred, and recreate
627 it on demand, we turn hard state in the network into soft state, and
628 allow it to be recovered automatically when needed.
629
630 Without this automatic recreation of soft state in the NAT gateway,
631 reliable long-term networking would not be achieved. As mentioned
632 above, the reliability of the Internet does not come from trying
633 to build a perfect network in which errors never happen, but from
634 accepting that in any sufficiently large system there will always be
635 some component somewhere that's failing, and designing mechanisms
636
637
638Expires 14th March 2007 Cheshire, et al. [Page 11]
639
640Internet Draft NAT Port Mapping Protocol 14th September 2006
641
642
643 that can handle those failures and recover. To illustrate this point
644 with an example, consider the following scenario: Imagine a network
645 security camera that has a web interface and accepts incoming
646 connections from web browser clients. Imagine this network security
647 camera uses NAT-PMP or a similar protocol to set up an inbound
648 port mapping in the NAT gateway so that it can receive incoming
649 connections from clients the other side of the NAT gateway.
650 Now, this camera may well operate for weeks, months, or even years.
651 During that time it's possible that the NAT gateway could experience
652 a power failure or be rebooted. The user could upgrade the NAT
653 gateway's firmware, or even replace the entire NAT gateway device
654 with a newer model. The general point is that if the camera operates
655 for a long enough period of time, some kind of disruption to the NAT
656 gateway becomes inevitable. The question is not whether the NAT
657 gateway will lose its port mappings, but when, and how often.
658 If the network camera and devices like it on the network can detect
659 when the NAT gateway has lost its port mappings, and recreate them
660 automatically, then these disruptions are self-correcting and
661 invisible to the end user. If, on the other hand, the disruptions are
662 not self-correcting, and after a NAT gateway reboot the user has to
663 manually reset or reboot all the other devices on the network too,
664 then these disruptions are *very* visible to the end user. This
665 aspect of the design is what makes the difference between a protocol
666 that keeps on working indefinitely over a time scale of months or
667 years, and a protocol that works in brief testing, but in the real
668 world is continually failing and requiring manual intervention to get
669 it going again.
670
671 When a client renews its port mappings as the result of receiving
672 a packet where the "Seconds since start of epoch" field (SSSOE)
673 indicates that a reboot or similar loss of state has occurred,
674 the client MUST first delay by a random amount of time selected
675 with uniform random distribution in the range 0 to 5 seconds, and
676 then send its first port mapping request. After that request is
677 acknowledged by the gateway, the client may then send its second
678 request, and so on, as rapidly as the gateway allows. The requests
679 SHOULD be issued serially, one at a time; the client SHOULD NOT issue
680 multiple requests simultaneously in parallel.
681
682 The discussion in this section focusses on recreating inbound port
683 mappings after loss of NAT gateway state, because that is the more
684 serious problem. Losing port mappings for outgoing connections
685 destroys those currently active connections, but does not prevent
686 clients from establishing new outgoing connections. In contrast,
687 losing inbound port mappings not only destroys all existing inbound
688 connections, but also prevents the reception of any new inbound
689 connections until the port mapping is recreated. Accordingly,
690 we consider recovery of inbound port mappings the more important
691 priority. However, clients that want outgoing connections to survive
692 a NAT gateway reboot can also achieve that using NAT-PMP. After
693 initiating an outbound TCP connection (which will cause the NAT
694
695
696Expires 14th March 2007 Cheshire, et al. [Page 12]
697
698Internet Draft NAT Port Mapping Protocol 14th September 2006
699
700
701 gateway to establish an implicit port mapping) the client should send
702 the NAT gateway a port mapping request for the source port of its TCP
703 connection, which will cause the NAT gateway to send a response
704 giving the public port it allocated for that mapping. The client can
705 then store this information, and use later to recreate the mapping
706 if it determines that the NAT gateway has lost its mapping state.
707
708
7093.8 NAT Gateways with NAT Function Disabled
710
711 Note that *only* devices currently acting in the role of NAT gateway
712 should participate in NAT-PMP protocol exchanges with clients.
713 A network device that is capable of NAT (and NAT-PMP), but is
714 currently configured not to perform that function, (e.g. it is
715 acting as a traditional IP router, forwarding packets without
716 modifying them), MUST NOT respond to NAT-PMP requests from clients,
717 or send spontaneous NAT-PMP address-change announcements.
718
719 In particular, a network device not currently acting in the role of
720 NAT gateway should not even respond to NAT-PMP requests by returning
721 an error code such as "2 - Not Authorized/Refused", since to do so
722 is misleading to clients -- it suggests that NAT port mapping is
723 necessary on this network for the client to successfully receive
724 inbound connections, but is not available because the administrator
725 has chosen to disable that functionality.
726
727 Clients should also be careful to avoid making unfounded assumptions,
728 such as the assumption that if the client has an IPv4 address in
729 one of the RFC 1918 private IP address ranges then that means
730 NAT necessarily must be in use. Net 10/8 has enough addresses
731 to build a private network with millions of hosts and thousands
732 of interconnected subnets, all without any use of NAT. Many
733 organizations have built such private networks that benefit from
734 using standard TCP/IP technology, but by choice do not connect
735 to the public Internet. The purpose of NAT-PMP is to mitigate some
736 of the damage caused by NAT. It would be an ironic and unwanted
737 side-effect of this protocol if it were to lead well-meaning but
738 misguided developers to create products that refuse to work on a
739 private network *unless* they can find a NAT gateway to talk to.
740 Consequently, a client finding that NAT-PMP is not available on its
741 network should not give up, but should proceed on the assumption
742 that the network may be a traditional routed IP network, with no
743 address translation being used. This assumption may not always be
744 true, but it is better than the alternative of falsely assuming
745 the worst and not even trying to use normal (non-NAT) IP networking.
746
747 If a network device not currently acting in the role of NAT gateway
748 receives UDP packets addressed to port 5351, it SHOULD respond
749 immediately with an "ICMP Port Unreachable" message to tell the
750 client that it needn't continue with timeouts and retransmissions,
751 and it should assume that NAT-PMP is not available and not needed
752 on this network.
753
754Expires 14th March 2007 Cheshire, et al. [Page 13]
755
756Internet Draft NAT Port Mapping Protocol 14th September 2006
757
758
7594. UNSAF Considerations
760
761 The document "IAB Considerations for UNSAF Across NAT" [RFC 3424]
762 covers a number of issues when working with NATs. RFC 3424 outlines
763 some requirements for any document that attempts to work around
764 problems associated with NATs. This section addresses those
765 requirements.
766
767
7684.1 Scope
769
770 This protocol addresses the needs of TCP and UDP transport peers that
771 are separated from the public internet by exactly one NAT. Such
772 peers must have access to some form of directory server for
773 registering the public IP address and port at which they can be
774 reached.
775
776
7774.2 Transition Plan
778
779 Any client making use of this protocol SHOULD implement IPv6 support.
780 If a client supports IPv6 and is running on a device with a global
781 IPv6 address, that IPv6 address SHOULD be preferred to the IPv4
782 public address using this NAT mapping protocol. In case other
783 clients do not have IPv6 connectivity, both the IPv4 and IPv6
784 addresses SHOULD be registered with whatever form of directory server
785 is used. Preference SHOULD be given to IPv6 addresses when
786 available. By implementing support for IPv6 and using this protocol
787 for IPv4, vendors can ship products today that will work under both
788 scenarios. As IPv6 is more widely deployed, clients of this protocol
789 following these recommendations will transparently make use of IPv6.
790
791
7924.3 Failure Cases
793
794 Aside from NATs that do not implement this protocol, there are a
795 number of situations where this protocol may not work.
796
797
7984.3.1 NAT Behind NAT
799
800 Some people's primary IP address, assigned by their ISP, may itself
801 be a NAT address. In addition, some people may have a public IP
802 address, but may then double NAT themselves, perhaps by choice or
803 perhaps by accident. Although it might be possible in principle for
804 one NAT gateway to recursively request a mapping from the next one,
805 this document does not advocate that and does not try to prescribe
806 how it would be done.
807
808 It would be a lot of work to implement nested NAT port mapping
809 correctly, and there are a number of reasons why the end result might
810
811
812Expires 14th March 2007 Cheshire, et al. [Page 14]
813
814Internet Draft NAT Port Mapping Protocol 14th September 2006
815
816
817 not be as useful as we might hope. Consider the case of an ISP that
818 offers each of its customers only a single NAT address. This ISP
819 could instead have chosen to provide each customer with a single
820 public IP address, or, if the ISP insists on running NAT, it could
821 have chosen to allow each customer a reasonable number of addresses,
822 enough for each customer device to have its own NAT address directly
823 from the ISP. If instead this ISP chooses to allow each customer
824 just one and only one NAT address, forcing said customer to run
825 nested NAT in order to use more than one device, it seems unlikely
826 that such an ISP would be so obliging as to provide a NAT service
827 that supports NAT Port Mapping Protocol. Supposing that such an ISP
828 did wish to offer its customers NAT service with NAT-PMP so as to
829 give them the ability to receive inbound connections, this ISP could
830 easily choose to allow each client to request a reasonable number of
831 DHCP addresses from that gateway. Remember that Net 10/8 [RFC 1918]
832 allows for over 16 million addresses, so NAT addresses are not in any
833 way in short supply. A single NAT gateway with 16 million available
834 addresses is likely to run out of packet forwarding capacity before
835 it runs out of private addresses to hand out. In this way the ISP
836 could offer single-level NAT with NAT-PMP, obviating the need to
837 support nested NAT-PMP. In addition, an ISP that is motivated to
838 provide their customers with unhindered access to the Internet by
839 allowing incoming as well as outgoing connections has better ways
840 to offer this service. Such an ISP could offer its customers real
841 public IP addresses instead of NAT addresses, or could even choose
842 to offer its customers full IPv6 connectivity, where no mapping or
843 translation is required at all.
844
845
8464.3.2 NATs with Multiple Public IP Addresses
847
848 If a NAT maps private addresses to multiple public addresses,
849 then it SHOULD pick one of those addresses as the one it will
850 support for inbound connections, and for the purposes of this
851 protocol it SHOULD act as if that address were its only address.
852
853
8544.3.3 NATs and Routed Private Networks
855
856 In some cases, a large network may be subnetted. Some sites
857 may install a NAT gateway and subnet the private network.
858 Such subnetting breaks this protocol because the router address
859 is not necessarily the address of the device performing NAT.
860
861 Addressing this problem is not a high priority. Any site with the
862 resources to set up such a configuration should have the resources to
863 add manual mappings or attain a range of globally unique addresses.
864
865 Not all NATs will support this protocol. In the case where a client
866 is run behind a NAT that does not support this protocol, the software
867 relying on the functionality of this protocol may be unusable.
868
869
870Expires 14th March 2007 Cheshire, et al. [Page 15]
871
872Internet Draft NAT Port Mapping Protocol 14th September 2006
873
874
8754.3.4 Communication Between Hosts Behind the Same NAT
876
877 NAT gateways supporting NAT-PMP should also implement "hairpin
878 translation". Hairpin translation means supporting communication
879 between two local clients being served by the same NAT gateway.
880
881 Suppose device A is listening on private address and port 10.0.0.2:80
882 for incoming connections. Using NAT-PMP, device A has obtained a
883 mapping to public address and port x.x.x.x:80, and has recorded this
884 public address and port in a public directory of some kind. For
885 example, it could have created a DNS SRV record containing this
886 information, and recorded it, using DNS Dynamic Update [RFC 3007], in
887 a publicly accessible DNS server. Suppose then that device B, behind
888 the same NAT gateway as device A, but unknowing or uncaring of this
889 fact, retrieves device A's DNS SRV record and attempts to open a TCP
890 connection to x.x.x.x:80. The outgoing packets addressed to this
891 public Internet address will be sent to the NAT gateway for
892 translation and forwarding. Having translated the source address and
893 port number on the outgoing packet, the NAT gateway needs to be smart
894 enough to recognize that the destination address is in fact itself,
895 and then feed this packet back into its packet reception engine, to
896 perform the destination port mapping lookup to translate and forward
897 this packet to device A at address and port 10.0.0.2:80.
898
8994.3.5 Non UDP/TCP Transport Traffic
900
901 Any communication over transport protocols other than TCP and UDP
902 will not be served by this protocol. Examples are Generic Routing
903 Encapsulation (GRE), Authentication Header (AH) and Encapsulating
904 Security Payload (ESP).
905
9064.4 Long Term Solution
907
908 As IPv6 is deployed, clients of this protocol supporting IPv6 will be
909 able to bypass this protocol and the NAT when communicating with
910 other IPv6 devices. In order to ensure this transition, any client
911 implementing this protocol SHOULD also implement IPv6 and use this
912 solution only when IPv6 is not available to both peers.
913
9144.5 Existing Deployed NATs
915
916 Existing deployed NATs will not support this protocol. This protocol
917 will only work with NATs that are upgraded to support it.
918
919
920
921
922
923
924
925
926
927
928Expires 14th March 2007 Cheshire, et al. [Page 16]
929
930Internet Draft NAT Port Mapping Protocol 14th September 2006
931
932
9335. Security Considerations
934
935 As discussed in section 3.2 "Determining the Public Address", only
936 clients on the private side of the NAT may create port mappings, and
937 only on behalf of themselves. By using IP address spoofing, it's
938 possible for one client to delete the port mappings of another
939 client. It's also possible for one client to create port mappings on
940 behalf of another client. The best way to deal with this
941 vulnerability is to use IPSec [RFC 2401].
942
943 Since allowing incoming connections is often a policy decision, any
944 NAT gateway implementing this protocol SHOULD have an administrative
945 mechanism to disable it.
946
947 Some people view the property that NATs block inbound connections as
948 a security benefit which is undermined by this protocol. The authors
949 of this document have a different point of view. In the days before
950 NAT, all hosts had unique public IP addresses, and had unhindered
951 ability to communicate with any other host on the Internet. When NAT
952 came along it broke this unhindered connectivity, relegating many
953 hosts to second-class status, unable to receive inbound connections.
954 This protocol goes some way to undo some of that damage. The purpose
955 of a NAT gateway should be to allow several hosts to share a single
956 address, not to simultaneously impede those host's ability to
957 communicate freely. Security is most properly provided by end-to-end
958 cryptographic security, and/or by explicit firewall functionality, as
959 appropriate. Blocking of certain connections should occur only as a
960 result of explicit and intentional firewall policy, not as an
961 accidental side-effect of some other technology.
962
963
9646. IANA Considerations
965
966 No IANA services are required by this document.
967
968
9697. Acknowledgments
970
971 The concepts described in this document have been explored, developed
972 and implemented with help from Bob Bradley, Josh Graessley, Rob
973 Newberry, Roger Pantos, John Saxton, and James Woodyatt.
974
975
9768. Deployment History
977
978 NAT-PMP client software first became available to the public
979 through Apple's Darwin Open Source code in August 2004.
980 NAT-PMP implementations began shipping to end users in large
981 volumes (i.e. millions) with the launch of Mac OS X 10.4 Tiger
982 and Bonjour for Windows 1.0 in April 2005.
983
984
985
986Expires 14th March 2007 Cheshire, et al. [Page 17]
987
988Internet Draft NAT Port Mapping Protocol 14th September 2006
989
990
991 The NAT-PMP client in Mac OS X 10.4 Tiger and Bonjour for Windows
992 exists as part of the mDNSResponder system service. When a client
993 advertises a service using Wide Area Bonjour [DNS-SD], and the
994 machine is behind a NAT-PMP-capable NAT gateway, then if the machine
995 is so configured, the mDNSResponder system service automatically uses
996 NAT-PMP to set up an inbound port mapping, and then records the
997 public IP address and port in the global DNS. Existing client
998 software using the existing Bonjour programming APIs [Bonjour]
999 gets this functionality automatically. The logic is that if client
1000 software publishes its information into the global DNS via Wide Area
1001 Bonjour service advertising, then it's reasonable to infer an
1002 expectation that this information should be usable by the peers
1003 retrieving it. Generally speaking, recording a private IP address
1004 like 10.0.0.2 in the public DNS is completely pointless because that
1005 address is not reachable from clients on the other side of the NAT
1006 gateway. In the case of a home user with a single computer directly
1007 connected to their Cable or DSL modem, with a single global IPv4
1008 address and no NAT gateway (a surprisingly common configuration),
1009 publishing that IP address into the global DNS is useful because that
1010 IP address is reachable. In contrast, a home user using a NAT gateway
1011 to share a single global IPv4 address between several computers loses
1012 this ability to receive inbound connections easily. This breaks many
1013 peer-to-peer collaborative applications, like the multi-user text
1014 editor SubEthaEdit [SEE]. Automatically creating the necessary
1015 inbound port mappings helps remedy this unintended side-effect of
1016 NAT.
1017
1018 The server side of the NAT-PMP protocol is implemented in Apple's
1019 "AirPort Extreme" and "AirPort Express" wireless base stations.
1020
1021
10229. Copyright Notice
1023
1024 Copyright (C) The Internet Society (2006).
1025
1026 This document is subject to the rights, licenses and restrictions
1027 contained in BCP 78, and except as set forth therein, the authors
1028 retain all their rights. For the purposes of this document,
1029 the term "BCP 78" refers exclusively to RFC 3978, "IETF Rights
1030 in Contributions", published March 2005.
1031
1032 This document and the information contained herein are provided on
1033 an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE
1034 REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE
1035 INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR
1036 IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
1037 THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
1038 WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
1039
1040
1041
1042
1043
1044Expires 14th March 2007 Cheshire, et al. [Page 18]
1045
1046Internet Draft NAT Port Mapping Protocol 14th September 2006
1047
1048
104910. Normative References
1050
1051 [RFC 1918] Y. Rekhter et.al., "Address Allocation for Private
1052 Internets", RFC 1918, February 1996.
1053
1054 [RFC 2119] RFC 2119 - Key words for use in RFCs to Indicate
1055 Requirement Levels
1056
1057
105811. Informative References
1059
1060 [Bonjour] Apple "Bonjour" <http://developer.apple.com/bonjour/>
1061
1062 [ETEAISD] J. Saltzer, D. Reed and D. Clark: "End-to-end arguments in
1063 system design", ACM Trans. Comp. Sys., 2(4):277-88, Nov.
1064 1984
1065
1066 [DNS-SD] Cheshire, S., and M. Krochmal, "DNS-Based Service
1067 Discovery", Internet-Draft (work in progress),
1068 draft-cheshire-dnsext-dns-sd-04.txt, August 2006.
1069
1070 [mDNS] Cheshire, S., and M. Krochmal, "Multicast DNS",
1071 Internet-Draft (work in progress),
1072 draft-cheshire-dnsext-multicastdns-06.txt, August 2006.
1073
1074 [RFC 2131] R. Droms, "Dynamic Host Configuration Protocol", RFC 2131,
1075 March 1997.
1076
1077 [RFC 2401] Atkinson, R. and S. Kent, "Security Architecture for the
1078 Internet Protocol", RFC 2401, November 1998.
1079
1080 [RFC 2663] Srisuresh, P. and M. Holdrege, "IP Network Address
1081 Translator (NAT) Terminology and Considerations", RFC
1082 2663, August 1999.
1083
1084 [RFC 3007] Wellington, B., "Simple Secure Domain Name System
1085 (DNS) Dynamic Update", RFC 3007, November 2000.
1086
1087 [SEE] <http://www.codingmonkeys.de/subethaedit/>
1088
1089 [RFC 3022] RFC 3022 - Network Address Translator
1090
1091 [RFC 3424] RFC 3424 - IAB Considerations for UNilateral Self-Address
1092 Fixing (UNSAF) Across Network Address Translation
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102Expires 14th March 2007 Cheshire, et al. [Page 19]
1103
1104Internet Draft NAT Port Mapping Protocol 14th September 2006
1105
1106
110712. Authors' Addresses
1108
1109 Stuart Cheshire
1110 Apple Computer, Inc.
1111 1 Infinite Loop
1112 Cupertino
1113 California 95014
1114 USA
1115
1116 Phone: +1 408 974 3207
1117 EMail: rfc [at] stuartcheshire [dot] org
1118
1119
1120 Marc Krochmal
1121 Apple Computer, Inc.
1122 1 Infinite Loop
1123 Cupertino
1124 California 95014
1125 USA
1126
1127 Phone: +1 408 974 4368
1128 EMail: marc [at] apple [dot] com
1129
1130
1131 Kiren Sekar
1132 Sharpcast, Inc.
1133 250 Cambridge Ave, Suite 101
1134 Palo Alto
1135 California 94306
1136 USA
1137
1138 Phone: +1 650 323 1960
1139 EMail: ksekar [at] sharpcast [dot] com
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160Expires 14th March 2007 Cheshire, et al. [Page 20]
diff --git a/src/upnp/test_upnp.c b/src/upnp/test_upnp.c
deleted file mode 100644
index f20028dc9..000000000
--- a/src/upnp/test_upnp.c
+++ /dev/null
@@ -1,110 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20/**
21 * @file upnp/test_upnp.c
22 * @brief Testcase for UPnP
23 * @author Christian Grothoff
24 */
25
26#include "gnunet_util.h"
27#include "gnunet_upnp_service.h"
28#include "gnunet_core.h"
29#include "platform.h"
30
31
32
33int
34main (int argc, const char *argv[])
35{
36 static GNUNET_CoreAPIForPlugins capi;
37 struct GNUNET_GE_Context *ectx;
38 struct GNUNET_GC_Configuration *cfg;
39 struct in_addr addr;
40 int i;
41 GNUNET_UPnP_ServiceAPI *upnp;
42 struct GNUNET_PluginHandle *plug;
43 GNUNET_ServicePluginInitializationMethod init;
44 GNUNET_ServicePluginShutdownMethod done;
45 char ntop_buf[INET_ADDRSTRLEN];
46
47 ectx = GNUNET_GE_create_context_stderr (GNUNET_NO,
48 GNUNET_GE_WARNING | GNUNET_GE_ERROR
49 | GNUNET_GE_FATAL | GNUNET_GE_USER |
50 GNUNET_GE_ADMIN |
51 GNUNET_GE_DEVELOPER |
52 GNUNET_GE_IMMEDIATE |
53 GNUNET_GE_BULK);
54 GNUNET_GE_setDefaultContext (ectx);
55 cfg = GNUNET_GC_create ();
56 GNUNET_GE_ASSERT (ectx, cfg != NULL);
57 GNUNET_os_init (ectx);
58 capi.ectx = ectx;
59 capi.cfg = cfg;
60 plug = GNUNET_plugin_load (ectx, "libgnunet", "module_upnp");
61 if (plug == NULL)
62 {
63 GNUNET_GC_free (cfg);
64 GNUNET_GE_free_context (ectx);
65 return 1;
66 }
67 init = GNUNET_plugin_resolve_function (plug, "provide_", GNUNET_YES);
68 if (init == NULL)
69 {
70 GNUNET_plugin_unload (plug);
71 GNUNET_GC_free (cfg);
72 GNUNET_GE_free_context (ectx);
73 return 1;
74 }
75 upnp = init (&capi);
76 if (upnp == NULL)
77 {
78 GNUNET_plugin_unload (plug);
79 GNUNET_GC_free (cfg);
80 GNUNET_GE_free_context (ectx);
81 return 1;
82 }
83 for (i = 0; i < 10; i++)
84 {
85 if (GNUNET_shutdown_test () != GNUNET_NO)
86 break;
87 if (GNUNET_OK == upnp->get_ip (2086, "TCP", &addr))
88 {
89 printf ("UPnP returned external IP %s\n",
90 inet_ntop (AF_INET, &addr, ntop_buf, INET_ADDRSTRLEN));
91 }
92 else
93 {
94 /* we cannot be sure that there is a UPnP-capable
95 NAT-box out there, so test should not fail
96 just because of this! */
97 printf ("No UPnP response (yet).\n");
98 }
99 GNUNET_thread_sleep (2 * GNUNET_CRON_SECONDS);
100 }
101 done = GNUNET_plugin_resolve_function (plug, "release_", GNUNET_YES);
102 if (done != NULL)
103 done ();
104 GNUNET_plugin_unload (plug);
105 GNUNET_GC_free (cfg);
106 GNUNET_GE_free_context (ectx);
107 return 0;
108}
109
110/* end of upnptest.c */
diff --git a/src/upnp/upnp.c b/src/upnp/upnp.c
deleted file mode 100644
index 5e15b12b4..000000000
--- a/src/upnp/upnp.c
+++ /dev/null
@@ -1,721 +0,0 @@
1/**
2 * @file upnp/upnp.c UPnP Implementation
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#include "platform.h"
27#include "upnp_xmlnode.h"
28#include "upnp_util.h"
29#include "upnp.h"
30
31#include <curl/curl.h>
32
33#define TRUE GNUNET_YES
34#define FALSE GNUNET_NO
35#define g_return_if_fail(a) if(!(a)) return;
36#define g_return_val_if_fail(a, val) if(!(a)) return (val);
37
38#define HTTP_OK "200 OK"
39#define NUM_UDP_ATTEMPTS 2
40#define HTTPMU_HOST_ADDRESS "239.255.255.250"
41#define HTTPMU_HOST_PORT 1900
42#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"
43#define SEARCH_REQUEST_STRING \
44 "M-SEARCH * HTTP/1.1\r\n" \
45 "MX: 2\r\n" \
46 "HOST: 239.255.255.250:1900\r\n" \
47 "MAN: \"ssdp:discover\"\r\n" \
48 "ST: urn:schemas-upnp-org:service:%s\r\n" \
49 "\r\n"
50#define WAN_IP_CONN_SERVICE "WANIPConnection:1"
51#define WAN_PPP_CONN_SERVICE "WANPPPConnection:1"
52#define HTTP_POST_SOAP_HEADER \
53 "SOAPACTION: \"urn:schemas-upnp-org:service:%s#%s\""
54#define HTTP_POST_SIZE_HEADER "CONTENT-LENGTH: %u"
55#define SOAP_ACTION \
56 "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
57 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
58 "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
59 "<s:Body>\r\n" \
60 "<u:%s xmlns:u=\"urn:schemas-upnp-org:service:%s\">\r\n" \
61 "%s" \
62 "</u:%s>\r\n" \
63 "</s:Body>\r\n" \
64 "</s:Envelope>"
65#define PORT_MAPPING_LEASE_TIME "0"
66#define PORT_MAPPING_DESCRIPTION "GNUNET_UPNP_PORT_FORWARD"
67#define ADD_PORT_MAPPING_PARAMS \
68 "<NewRemoteHost></NewRemoteHost>\r\n" \
69 "<NewExternalPort>%i</NewExternalPort>\r\n" \
70 "<NewProtocol>%s</NewProtocol>\r\n" \
71 "<NewInternalPort>%i</NewInternalPort>\r\n" \
72 "<NewInternalClient>%s</NewInternalClient>\r\n" \
73 "<NewEnabled>1</NewEnabled>\r\n" \
74 "<NewPortMappingDescription>" \
75 PORT_MAPPING_DESCRIPTION \
76 "</NewPortMappingDescription>\r\n" \
77 "<NewLeaseDuration>" \
78 PORT_MAPPING_LEASE_TIME \
79 "</NewLeaseDuration>\r\n"
80#define DELETE_PORT_MAPPING_PARAMS \
81 "<NewRemoteHost></NewRemoteHost>\r\n" \
82 "<NewExternalPort>%i</NewExternalPort>\r\n" \
83 "<NewProtocol>%s</NewProtocol>\r\n"
84
85typedef enum
86{
87 GAIM_UPNP_STATUS_UNDISCOVERED = -1,
88 GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER,
89 GAIM_UPNP_STATUS_DISCOVERING,
90 GAIM_UPNP_STATUS_DISCOVERED
91} GaimUPnPStatus;
92
93typedef struct
94{
95 GaimUPnPStatus status;
96 char *control_url;
97 const char *service_type;
98 char publicip[16];
99} GaimUPnPControlInfo;
100
101typedef struct
102{
103 const char *service_type;
104 char *full_url;
105 char *buf;
106 unsigned int buf_len;
107 struct GNUNET_NETWORK_Handle *sock;
108} UPnPDiscoveryData;
109
110static GaimUPnPControlInfo control_info = {
111 GAIM_UPNP_STATUS_UNDISCOVERED,
112 NULL,
113 NULL,
114 "",
115};
116
117/**
118 * This is the signature used for functions that act as a callback
119 * to CURL.
120 */
121typedef size_t (*GaimUtilFetchUrlCallback) (void *url_data,
122 size_t size,
123 size_t nmemb, void *user_data);
124
125
126
127static char *
128g_strstr_len (const char *haystack, int haystack_len, const char *needle)
129{
130 int i;
131
132 g_return_val_if_fail (haystack != NULL, NULL);
133 g_return_val_if_fail (needle != NULL, NULL);
134
135 if (haystack_len < 0)
136 return strstr (haystack, needle);
137 else
138 {
139 const char *p = haystack;
140 int needle_len = strlen (needle);
141 const char *end = haystack + haystack_len - needle_len;
142
143 if (needle_len == 0)
144 return (char *) haystack;
145
146 while (*p && p <= end)
147 {
148 for (i = 0; i < needle_len; i++)
149 if (p[i] != needle[i])
150 goto next;
151
152 return (char *) p;
153
154 next:
155 p++;
156 }
157 }
158
159 return NULL;
160}
161
162static int
163gaim_upnp_compare_device (const xmlnode * device, const char *deviceType)
164{
165 xmlnode *deviceTypeNode = xmlnode_get_child (device, "deviceType");
166 char *tmp;
167 int ret;
168
169 if (deviceTypeNode == NULL)
170 return FALSE;
171 tmp = xmlnode_get_data (deviceTypeNode);
172 ret = !strcasecmp (tmp, deviceType);
173 GNUNET_free (tmp);
174 return ret;
175}
176
177static int
178gaim_upnp_compare_service (const xmlnode * service, const char *serviceType)
179{
180 xmlnode *serviceTypeNode;
181 char *tmp;
182 int ret;
183
184 if (service == NULL)
185 return FALSE;
186 serviceTypeNode = xmlnode_get_child (service, "serviceType");
187 if (serviceTypeNode == NULL)
188 return FALSE;
189 tmp = xmlnode_get_data (serviceTypeNode);
190 ret = !strcasecmp (tmp, serviceType);
191 GNUNET_free (tmp);
192 return ret;
193}
194
195static char *
196gaim_upnp_parse_description_response (const char *httpResponse,
197 size_t len,
198 const char *httpURL,
199 const char *serviceType)
200{
201 char *xmlRoot, *baseURL, *controlURL, *service;
202 xmlnode *xmlRootNode, *serviceTypeNode, *controlURLNode, *baseURLNode;
203 char *tmp;
204
205 /* find the root of the xml document */
206 xmlRoot = g_strstr_len (httpResponse, len, "<root");
207 if (xmlRoot == NULL)
208 return NULL;
209 if (g_strstr_len (httpResponse, len, "</root") == NULL)
210 return NULL;
211
212 /* create the xml root node */
213 xmlRootNode = xmlnode_from_str (xmlRoot, len - (xmlRoot - httpResponse));
214 if (xmlRootNode == NULL)
215 return NULL;
216
217 /* get the baseURL of the device */
218 baseURLNode = xmlnode_get_child (xmlRootNode, "URLBase");
219 if (baseURLNode != NULL)
220 {
221 baseURL = xmlnode_get_data (baseURLNode);
222 }
223 else
224 {
225 baseURL = GNUNET_strdup (httpURL);
226 }
227
228 /* get the serviceType child that has the service type as its data */
229 /* get urn:schemas-upnp-org:device:InternetGatewayDevice:1 and its devicelist */
230 serviceTypeNode = xmlnode_get_child (xmlRootNode, "device");
231 while (!gaim_upnp_compare_device (serviceTypeNode,
232 "urn:schemas-upnp-org:device:InternetGatewayDevice:1")
233 && serviceTypeNode != NULL)
234 {
235 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
236 }
237 if (serviceTypeNode == NULL)
238 {
239 GNUNET_free (baseURL);
240 xmlnode_free (xmlRootNode);
241 return NULL;
242 }
243 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
244 if (serviceTypeNode == NULL)
245 {
246 GNUNET_free (baseURL);
247 xmlnode_free (xmlRootNode);
248 return NULL;
249 }
250
251 /* get urn:schemas-upnp-org:device:WANDevice:1 and its devicelist */
252 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
253 while (!gaim_upnp_compare_device (serviceTypeNode,
254 "urn:schemas-upnp-org:device:WANDevice:1")
255 && serviceTypeNode != NULL)
256 {
257 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
258 }
259 if (serviceTypeNode == NULL)
260 {
261 GNUNET_free (baseURL);
262 xmlnode_free (xmlRootNode);
263 return NULL;
264 }
265 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "deviceList");
266 if (serviceTypeNode == NULL)
267 {
268 GNUNET_free (baseURL);
269 xmlnode_free (xmlRootNode);
270 return NULL;
271 }
272
273 /* get urn:schemas-upnp-org:device:WANConnectionDevice:1 and its servicelist */
274 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "device");
275 while (serviceTypeNode && !gaim_upnp_compare_device (serviceTypeNode,
276 "urn:schemas-upnp-org:device:WANConnectionDevice:1"))
277 {
278 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
279 }
280 if (serviceTypeNode == NULL)
281 {
282 GNUNET_free (baseURL);
283 xmlnode_free (xmlRootNode);
284 return NULL;
285 }
286 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "serviceList");
287 if (serviceTypeNode == NULL)
288 {
289 GNUNET_free (baseURL);
290 xmlnode_free (xmlRootNode);
291 return NULL;
292 }
293
294 /* get the serviceType variable passed to this function */
295 service = g_strdup_printf (SEARCH_REQUEST_DEVICE, serviceType);
296 serviceTypeNode = xmlnode_get_child (serviceTypeNode, "service");
297 while (!gaim_upnp_compare_service (serviceTypeNode, service) &&
298 serviceTypeNode != NULL)
299 {
300 serviceTypeNode = xmlnode_get_next_twin (serviceTypeNode);
301 }
302
303 GNUNET_free (service);
304 if (serviceTypeNode == NULL)
305 {
306 GNUNET_free (baseURL);
307 xmlnode_free (xmlRootNode);
308 return NULL;
309 }
310
311 /* get the controlURL of the service */
312 if ((controlURLNode = xmlnode_get_child (serviceTypeNode,
313 "controlURL")) == NULL)
314 {
315 GNUNET_free (baseURL);
316 xmlnode_free (xmlRootNode);
317 return NULL;
318 }
319
320 tmp = xmlnode_get_data (controlURLNode);
321 if (baseURL && !gaim_str_has_prefix (tmp, "http://") &&
322 !gaim_str_has_prefix (tmp, "HTTP://"))
323 {
324 if (tmp[0] == '/')
325 {
326 size_t len;
327 const char *end;
328 /* absolute path */
329 end = strstr (&baseURL[strlen ("http://")], "/");
330 if (end == NULL)
331 len = strlen (&baseURL[strlen ("http://")]);
332 else
333 len = end - &baseURL[strlen ("http://")];
334 controlURL = g_strdup_printf ("http://%.*s%s",
335 len,
336 &baseURL[strlen ("http://")], tmp);
337 }
338 else
339 {
340 controlURL = g_strdup_printf ("%s%s", baseURL, tmp);
341 }
342 GNUNET_free (tmp);
343 }
344 else
345 {
346 controlURL = tmp;
347 }
348 GNUNET_free (baseURL);
349 xmlnode_free (xmlRootNode);
350
351 return controlURL;
352}
353
354#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);
355
356/**
357 * Do the generic curl setup.
358 */
359static int
360setup_curl (const char *proxy, CURL * curl)
361{
362 int ret;
363
364 CURL_EASY_SETOPT (curl, CURLOPT_FAILONERROR, 1);
365 if (strlen (proxy) > 0)
366 CURL_EASY_SETOPT (curl, CURLOPT_PROXY, proxy);
367 CURL_EASY_SETOPT (curl, CURLOPT_BUFFERSIZE, 1024); /* a bit more than one HELLO */
368 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 150L);
369 /* NOTE: use of CONNECTTIMEOUT without also
370 setting NOSIGNAL results in really weird
371 crashes on my system! */
372 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
373 return GNUNET_OK;
374}
375
376static int
377gaim_upnp_generate_action_message_and_send (const char *proxy,
378 const char *actionName,
379 const char *actionParams,
380 GaimUtilFetchUrlCallback cb,
381 void *cb_data)
382{
383 CURL *curl;
384 int ret;
385 char *soapHeader;
386 char *sizeHeader;
387 char *soapMessage;
388 struct curl_slist *headers = NULL;
389
390 GNUNET_assert (cb != NULL);
391 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
392 return GNUNET_SYSERR;
393 /* set the soap message */
394 soapMessage = g_strdup_printf (SOAP_ACTION,
395 actionName,
396 control_info.service_type,
397 actionParams, actionName);
398 soapHeader = g_strdup_printf (HTTP_POST_SOAP_HEADER,
399 control_info.service_type, actionName);
400 sizeHeader = g_strdup_printf (HTTP_POST_SIZE_HEADER, strlen (soapMessage));
401 curl = curl_easy_init ();
402 setup_curl (proxy, curl);
403 CURL_EASY_SETOPT (curl, CURLOPT_URL, control_info.control_url);
404 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, cb);
405 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, cb_data);
406 CURL_EASY_SETOPT (curl, CURLOPT_POST, 1);
407 headers = curl_slist_append (headers,
408 "CONTENT-TYPE: text/xml ; charset=\"utf-8\"");
409 headers = curl_slist_append (headers, soapHeader);
410 headers = curl_slist_append (headers, sizeHeader);
411 CURL_EASY_SETOPT (curl, CURLOPT_HTTPHEADER, headers);
412 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDS, soapMessage);
413 CURL_EASY_SETOPT (curl, CURLOPT_POSTFIELDSIZE, strlen (soapMessage));
414 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
415 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
416 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
417 /* NOTE: use of CONNECTTIMEOUT without also
418 setting NOSIGNAL results in really weird
419 crashes on my system! */
420 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
421 if (ret == CURLE_OK)
422 ret = curl_easy_perform (curl);
423#if 0
424 if (ret != CURLE_OK)
425 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
426 _
427 ("%s failed for url `%s' and post-data `%s' at %s:%d: `%s'\n"),
428 "curl_easy_perform", control_info.control_url, soapMessage,
429 __FILE__, __LINE__, curl_easy_strerror (ret));
430#endif
431 curl_slist_free_all (headers);
432 curl_easy_cleanup (curl);
433 curl_global_cleanup ();
434 GNUNET_free (sizeHeader);
435 GNUNET_free (soapMessage);
436 GNUNET_free (soapHeader);
437 if (ret != CURLE_OK)
438 return GNUNET_SYSERR;
439 return GNUNET_OK;
440}
441
442
443static size_t
444looked_up_public_ip_cb (void *url_data,
445 size_t size, size_t nmemb, void *user_data)
446{
447 UPnPDiscoveryData *dd = user_data;
448 size_t len = size * nmemb;
449 const char *temp;
450 const char *temp2;
451
452 if (len + dd->buf_len > 1024 * 1024 * 4)
453 return 0; /* refuse to process - too big! */
454 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
455 memcpy (&dd->buf[dd->buf_len - len], url_data, len);
456 if (dd->buf_len == 0)
457 return len;
458 /* extract the ip, or see if there is an error */
459 if ((temp = g_strstr_len (dd->buf,
460 dd->buf_len, "<NewExternalIPAddress")) == NULL)
461 return len;
462 if (!(temp = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), ">")))
463 return len;
464 if (!(temp2 = g_strstr_len (temp, dd->buf_len - (temp - dd->buf), "<")))
465 return len;
466 memset (control_info.publicip, 0, sizeof (control_info.publicip));
467 if (temp2 - temp >= sizeof (control_info.publicip))
468 temp2 = temp + sizeof (control_info.publicip) - 1;
469 memcpy (control_info.publicip, temp + 1, temp2 - (temp + 1));
470 GNUNET_log (GNUNET_ERROR_TYPE_INFO | GNUNET_ERROR_TYPE_BULK,
471 _("upnp: NAT Returned IP: %s\n"), control_info.publicip);
472 return len;
473}
474
475
476static size_t
477ignore_response (void *url_data, size_t size, size_t nmemb, void *user_data)
478{
479 return size * nmemb;
480}
481
482/**
483 * Process downloaded bits of service description.
484 */
485static size_t
486upnp_parse_description_cb (void *httpResponse,
487 size_t size, size_t nmemb, void *user_data)
488{
489 UPnPDiscoveryData *dd = user_data;
490 size_t len = size * nmemb;
491 char *control_url = NULL;
492
493 if (len + dd->buf_len > 1024 * 1024 * 4)
494 return len; /* refuse to process - too big! */
495 GNUNET_array_grow (dd->buf, dd->buf_len, dd->buf_len + len);
496 memcpy (&dd->buf[dd->buf_len - len], httpResponse, len);
497 if (dd->buf_len > 0)
498 control_url = gaim_upnp_parse_description_response (dd->buf,
499 dd->buf_len,
500 dd->full_url,
501 dd->service_type);
502 control_info.status = control_url ? GAIM_UPNP_STATUS_DISCOVERED
503 : GAIM_UPNP_STATUS_UNABLE_TO_DISCOVER;
504 GNUNET_free_non_null (control_info.control_url);
505 control_info.control_url = control_url;
506 control_info.service_type = dd->service_type;
507 return len;
508}
509
510static int
511gaim_upnp_parse_description (char *proxy, UPnPDiscoveryData * dd)
512{
513 CURL *curl;
514 int ret;
515
516 if (0 != curl_global_init (CURL_GLOBAL_WIN32))
517 return GNUNET_SYSERR;
518 curl = curl_easy_init ();
519 setup_curl (proxy, curl);
520 ret = CURLE_OK;
521 CURL_EASY_SETOPT (curl, CURLOPT_URL, dd->full_url);
522 CURL_EASY_SETOPT (curl, CURLOPT_WRITEFUNCTION, &upnp_parse_description_cb);
523 CURL_EASY_SETOPT (curl, CURLOPT_WRITEDATA, dd);
524 CURL_EASY_SETOPT (curl, CURLOPT_MAXREDIRS, 1L);
525 CURL_EASY_SETOPT (curl, CURLOPT_CONNECTTIMEOUT, 1L);
526 CURL_EASY_SETOPT (curl, CURLOPT_TIMEOUT, 2L);
527
528 /* NOTE: use of CONNECTTIMEOUT without also
529 setting NOSIGNAL results in really weird
530 crashes on my system! */
531 CURL_EASY_SETOPT (curl, CURLOPT_NOSIGNAL, 1);
532 ret = curl_easy_perform (curl);
533 if (ret != CURLE_OK)
534 GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
535 _("%s failed at %s:%d: `%s'\n"),
536 "curl_easy_perform", __FILE__, __LINE__,
537 curl_easy_strerror (ret));
538 curl_easy_cleanup (curl);
539 curl_global_cleanup ();
540 if (control_info.control_url == NULL)
541 return GNUNET_SYSERR;
542 return GNUNET_OK;
543}
544
545int
546gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, struct GNUNET_NETWORK_Handle *sock)
547{
548 char *proxy;
549 socklen_t avail;
550 struct sockaddr_in server;
551 int retry_count;
552 char *sendMessage;
553 size_t totalSize;
554 int sentSuccess;
555 char buf[65536];
556 int buf_len;
557 const char *startDescURL;
558 const char *endDescURL;
559 int ret;
560 UPnPDiscoveryData dd;
561 struct sockaddr *sa;
562
563 memset (&dd, 0, sizeof (UPnPDiscoveryData));
564 if (control_info.status == GAIM_UPNP_STATUS_DISCOVERING)
565 return GNUNET_NO;
566 dd.sock = sock;
567 memset (&server, 0, sizeof (struct sockaddr_in));
568 server.sin_family = AF_INET;
569 avail = sizeof (struct sockaddr_in);
570 sa = (struct sockaddr *) &server;
571 if (GNUNET_OK !=
572 GNUNET_get_ip_from_hostname (HTTPMU_HOST_ADDRESS, AF_INET, &sa, &avail))
573 {
574 return GNUNET_SYSERR;
575 }
576 server.sin_port = htons (HTTPMU_HOST_PORT);
577 control_info.status = GAIM_UPNP_STATUS_DISCOVERING;
578
579 /* because we are sending over UDP, if there is a failure
580 we should retry the send NUM_UDP_ATTEMPTS times. Also,
581 try different requests for WANIPConnection and WANPPPConnection */
582 for (retry_count = 0; retry_count < NUM_UDP_ATTEMPTS; retry_count++)
583 {
584 sentSuccess = FALSE;
585 if ((retry_count % 2) == 0)
586 dd.service_type = WAN_IP_CONN_SERVICE;
587 else
588 dd.service_type = WAN_PPP_CONN_SERVICE;
589 sendMessage = g_strdup_printf (SEARCH_REQUEST_STRING, dd.service_type);
590 totalSize = strlen (sendMessage);
591 do
592 {
593 if (SENDTO (dd.sock,
594 sendMessage,
595 totalSize,
596 0,
597 (struct sockaddr *) &server,
598 sizeof (struct sockaddr_in)) == totalSize)
599 {
600 sentSuccess = TRUE;
601 break;
602 }
603 }
604 while (((errno == EINTR) || (errno == EAGAIN)) &&
605 (GNUNET_shutdown_test () == GNUNET_NO));
606 GNUNET_free (sendMessage);
607 if (sentSuccess)
608 break;
609 }
610 if (sentSuccess == FALSE)
611 return GNUNET_SYSERR;
612
613 /* try to read response */
614 do
615 {
616 buf_len = GNUNET_IO_recv (dd.sock, buf, sizeof (buf) - 1, 0);
617 if (buf_len > 0)
618 {
619 buf[buf_len] = '\0';
620 break;
621 }
622 else if (errno != EINTR)
623 {
624 continue;
625 }
626 }
627 while ((errno == EINTR) && (GNUNET_shutdown_test () == GNUNET_NO));
628
629 /* parse the response, and see if it was a success */
630 if (g_strstr_len (buf, buf_len, HTTP_OK) == NULL)
631 return GNUNET_SYSERR;
632 if ((startDescURL = g_strstr_len (buf, buf_len, "http://")) == NULL)
633 return GNUNET_SYSERR;
634
635 endDescURL = g_strstr_len (startDescURL,
636 buf_len - (startDescURL - buf), "\r");
637 if (endDescURL == NULL)
638 endDescURL = g_strstr_len (startDescURL,
639 buf_len - (startDescURL - buf), "\n");
640 if (endDescURL == NULL)
641 return GNUNET_SYSERR;
642 if (endDescURL == startDescURL)
643 return GNUNET_SYSERR;
644 dd.full_url = GNUNET_strdup (startDescURL);
645 dd.full_url[endDescURL - startDescURL] = '\0';
646 proxy = NULL;
647 GNUNET_CONFIGURATION_get_value_string (cfg,
648 "GNUNETD", "HTTP-PROXY", &proxy);
649 ret = gaim_upnp_parse_description (proxy, &dd);
650 GNUNET_free (dd.full_url);
651 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
652 if (ret == GNUNET_OK)
653 {
654 ret = gaim_upnp_generate_action_message_and_send (proxy,
655 "GetExternalIPAddress",
656 "",
657 looked_up_public_ip_cb,
658 &dd);
659 GNUNET_array_grow (dd.buf, dd.buf_len, 0);
660 }
661 GNUNET_free (proxy);
662 return ret;
663}
664
665const char *
666gaim_upnp_get_public_ip ()
667{
668 if ((control_info.status == GAIM_UPNP_STATUS_DISCOVERED)
669 && (strlen (control_info.publicip) > 0))
670 return control_info.publicip;
671 return NULL;
672}
673
674int
675gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
676 int do_add,
677 unsigned short portmap, const char *protocol)
678{
679 const char *action_name;
680 char *action_params;
681 char *internal_ip;
682 char *proxy;
683 int ret;
684
685 if (control_info.status != GAIM_UPNP_STATUS_DISCOVERED)
686 return GNUNET_NO;
687 if (do_add)
688 {
689 internal_ip = GNUNET_upnp_get_internal_ip (cfg);
690 if (internal_ip == NULL)
691 {
692 gaim_debug_error ("upnp",
693 "gaim_upnp_set_port_mapping(): couldn't get local ip\n");
694 return GNUNET_NO;
695 }
696 action_name = "AddPortMapping";
697 action_params = g_strdup_printf (ADD_PORT_MAPPING_PARAMS,
698 portmap,
699 protocol, portmap, internal_ip);
700 GNUNET_free (internal_ip);
701 }
702 else
703 {
704 action_name = "DeletePortMapping";
705 action_params = g_strdup_printf (DELETE_PORT_MAPPING_PARAMS,
706 portmap, protocol);
707 }
708 proxy = NULL;
709 GNUNET_CONFIGURATION_get_value_string (cfg,
710 "GNUNETD", "HTTP-PROXY", &proxy);
711 ret =
712 gaim_upnp_generate_action_message_and_send (proxy, action_name,
713 action_params,
714 &ignore_response, NULL);
715
716 GNUNET_free (action_params);
717 GNUNET_free (proxy);
718 return ret;
719}
720
721/* end of upnp.c */
diff --git a/src/upnp/upnp.h b/src/upnp/upnp.h
deleted file mode 100644
index 537f9f3e7..000000000
--- a/src/upnp/upnp.h
+++ /dev/null
@@ -1,80 +0,0 @@
1/**
2 * @file upnp/upnp.h Universal Plug N Play API
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#ifndef _GAIM_UPNP_H_
27#define _GAIM_UPNP_H_
28
29#include <libxml/parser.h>
30#include <string.h>
31#include "gnunet_util_lib.h"
32
33#ifdef __cplusplus
34extern "C"
35{
36#if 0 /* keep Emacsens' auto-indent happy */
37}
38#endif
39#endif
40
41/**
42 * Sends a discovery request to search for a UPnP enabled IGD that
43 * contains the WANIPConnection service that will allow us to receive the
44 * public IP address of the IGD, and control it for forwarding ports.
45 * The result will be cached for further use.
46 */
47int gaim_upnp_discover (struct GNUNET_CONFIGURATION_Handle *cfg, int sock);
48
49/**
50 * Gets the IP address from a UPnP enabled IGD that sits on the local
51 * network, so when getting the network IP, instead of returning the
52 * local network IP, the public IP is retrieved. This is a cached value from
53 * the time of the UPnP discovery.
54 *
55 * @return The IP address of the network, or NULL if something went wrong
56 */
57const char *gaim_upnp_get_public_ip (void);
58
59/**
60 * Maps Ports in a UPnP enabled IGD that sits on the local network to
61 * this gaim client. Essentially, this function takes care of the port
62 * forwarding so things like file transfers can work behind NAT firewalls
63 *
64 * @param cfg configuration to use
65 * @param do_add TRUE/GNUNET_YES to add, FALSE/GNUNET_NO to remove
66 * @param portmap The port to map to this client
67 * @param protocol The protocol to map, either "TCP" or "UDP"
68 */
69int gaim_upnp_change_port_mapping (struct GNUNET_CONFIGURATION_Handle *cfg,
70 int do_add,
71 uint16_t portmap, const char *protocol);
72
73#if 0 /* keep Emacsens' auto-indent happy */
74{
75#endif
76#ifdef __cplusplus
77}
78#endif
79
80#endif /* _GAIM_UPNP_H_ */
diff --git a/src/upnp/upnp_init.c b/src/upnp/upnp_init.c
deleted file mode 100644
index 2c612e159..000000000
--- a/src/upnp/upnp_init.c
+++ /dev/null
@@ -1,208 +0,0 @@
1/*
2 This file is part of GNUnet
3 (C) 2006 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file upnp/upnp_init.c
23 * @brief API for UPnP access
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util.h"
29#include "upnp.h"
30#include "gnunet_upnp_service.h"
31#include "gnunet_core.h"
32
33static struct GNUNET_GE_Context *ectx;
34
35static struct GNUNET_GC_Configuration *cfg;
36
37static struct GNUNET_CronManager *cron;
38
39static struct GNUNET_Mutex *lock;
40
41typedef struct
42{
43 unsigned short port;
44 const char *proto;
45} PMap;
46
47static PMap *maps;
48
49static unsigned int maps_size;
50
51static struct GNUNET_ThreadHandle *discovery;
52
53static struct GNUNET_NETWORK_Handle *discovery_socket;
54
55/**
56 * Obtain the public/external IP address.
57 *
58 * @return GNUNET_SYSERR on error, GNUNET_OK on success
59 */
60static int
61gnunet_upnp_get_public_ip (struct in_addr *address)
62{
63 const char *ip;
64 socklen_t socklen;
65 struct sockaddr *sa;
66 struct sockaddr_in s4;
67 int ret;
68
69 ip = gaim_upnp_get_public_ip ();
70 if (ip == NULL)
71 return GNUNET_SYSERR;
72 socklen = sizeof (struct sockaddr_in);
73 sa = (struct sockaddr *) &s4;
74 ret = GNUNET_get_ip_from_hostname (NULL, ip, AF_INET, &sa, &socklen);
75 if (ret == GNUNET_OK)
76 *address = s4.sin_addr;
77 return ret;
78}
79
80static void
81kill_discovery ()
82{
83 void *unused;
84
85 if (discovery != NULL)
86 {
87 GNUNET_IO_shutdown (discovery_socket, SHUT_RDWR);
88 GNUNET_IO_close (&discovery_socket);
89 GNUNET_thread_join (discovery, &unused);
90 discovery = NULL;
91 }
92}
93
94static void *
95discover_thread ()
96{
97 gaim_upnp_discover (ectx, cfg, discovery_socket);
98 return NULL;
99}
100
101/**
102 * Periodically try to (re)discover UPnP access points.
103 */
104static void
105discover (void *unused)
106{
107 kill_discovery ();
108 discovery_socket = GNUNET_IO_socket (PF_INET, SOCK_DGRAM, 0);
109 if (NULL == discovery_socket)
110 return;
111 discovery = GNUNET_thread_create (&discover_thread, NULL, 1024 * 128);
112}
113
114/**
115 * Periodically repeat our requests for port mappings.
116 */
117static void
118portmap (void *unused)
119{
120 unsigned int i;
121
122 GNUNET_mutex_lock (lock);
123 for (i = 0; i < maps_size; i++)
124 gaim_upnp_change_port_mapping (ectx,
125 cfg, GNUNET_NO, maps[i].port,
126 maps[i].proto);
127 GNUNET_mutex_unlock (lock);
128}
129
130
131/**
132 * Get the external IP address for the local machine.
133 *
134 * @return GNUNET_SYSERR on error, GNUNET_OK on success
135 */
136static int
137gnunet_upnp_get_ip (unsigned short port,
138 const char *protocol, struct in_addr *address)
139{
140 unsigned int i;
141
142 GNUNET_mutex_lock (lock);
143 for (i = 0; i < maps_size; i++)
144 if ((0 == strcmp (maps[i].proto, protocol)) && (maps[i].port == port))
145 break;
146 if (i == maps_size)
147 {
148 /* new entry! */
149 GNUNET_array_grow (maps, maps_size, maps_size + 1);
150 maps[i].proto = protocol;
151 maps[i].port = port;
152 gaim_upnp_change_port_mapping (ectx, cfg, GNUNET_YES, port, protocol);
153 }
154 GNUNET_mutex_unlock (lock);
155 return gnunet_upnp_get_public_ip (address);
156}
157
158
159/**
160 * Get the external IP address for the local machine.
161 */
162GNUNET_UPnP_ServiceAPI *
163provide_module_upnp (GNUNET_CoreAPIForPlugins * capi)
164{
165 static GNUNET_UPnP_ServiceAPI api;
166
167 ectx = capi->ectx;
168 cfg = capi->cfg;
169 cron = GNUNET_cron_create (ectx);
170 lock = GNUNET_mutex_create (GNUNET_NO);
171 GNUNET_cron_start (cron);
172 GNUNET_cron_add_job (cron, &discover, 0, 5 * GNUNET_CRON_MINUTES, NULL);
173 GNUNET_cron_add_job (cron, &portmap, 150 * GNUNET_CRON_SECONDS,
174 5 * GNUNET_CRON_MINUTES, NULL);
175 api.get_ip = gnunet_upnp_get_ip;
176 return &api;
177}
178
179/**
180 * Shutdown UPNP.
181 */
182int
183release_module_upnp ()
184{
185 unsigned int i;
186
187 if (cron == NULL)
188 return GNUNET_SYSERR; /* not loaded! */
189 for (i = 0; i < maps_size; i++)
190 gaim_upnp_change_port_mapping (ectx,
191 cfg, GNUNET_NO, maps[i].port,
192 maps[i].proto);
193 GNUNET_cron_stop (cron);
194 GNUNET_cron_del_job (cron, &discover, 5 * GNUNET_CRON_MINUTES, NULL);
195 GNUNET_cron_del_job (cron, &portmap, 5 * GNUNET_CRON_MINUTES, NULL);
196 GNUNET_cron_destroy (cron);
197 kill_discovery ();
198 cron = NULL;
199 GNUNET_mutex_destroy (lock);
200 lock = NULL;
201 GNUNET_array_grow (maps, maps_size, 0);
202 ectx = NULL;
203 cfg = NULL;
204 return GNUNET_OK;
205}
206
207
208/* end of init.c */
diff --git a/src/upnp/upnp_ip.c b/src/upnp/upnp_ip.c
deleted file mode 100644
index 928f02b49..000000000
--- a/src/upnp/upnp_ip.c
+++ /dev/null
@@ -1,47 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2006, 2007 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file upnp/upnp_ip.c
23 * @brief code to determine the IP of the local machine
24 *
25 * @author Christian Grothoff
26 */
27
28#include <stdlib.h>
29#include "platform.h"
30#include "gnunet_util.h"
31#include "ip.h"
32
33/**
34 * Get the IP address for the local machine.
35 * @return NULL on error
36 */
37char *
38GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
39 struct GNUNET_GE_Context *ectx)
40{
41 struct in_addr address;
42
43 return GNUNET_get_local_ip (cfg, ectx, &address);
44}
45
46
47/* end of ip.c */
diff --git a/src/upnp/upnp_ip.h b/src/upnp/upnp_ip.h
deleted file mode 100644
index 1b80d328a..000000000
--- a/src/upnp/upnp_ip.h
+++ /dev/null
@@ -1,40 +0,0 @@
1/*
2 This file is part of GNUnet.
3 (C) 2001, 2002, 2003, 2004, 2005 Christian Grothoff (and other contributing authors)
4
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19*/
20
21/**
22 * @file upnp/upnp_ip.h
23 * @brief
24 *
25 * @author Christian Grothoff
26 */
27
28#ifndef IP_H
29#define IP_H
30
31
32/**
33 * Get the IP address for the local machine.
34 * @return NULL on error
35 */
36char *GNUNET_upnp_get_internal_ip (struct GNUNET_GC_Configuration *cfg,
37 struct GNUNET_GE_Context *ectx);
38
39
40#endif
diff --git a/src/upnp/upnp_util.c b/src/upnp/upnp_util.c
deleted file mode 100644
index a080b6470..000000000
--- a/src/upnp/upnp_util.c
+++ /dev/null
@@ -1,152 +0,0 @@
1/*
2 * @file upnp_util.cUtility Functions
3 * @ingroup core
4 *
5 * Gaim is the legal property of its developers, whose names are too numerous
6 * to list here. Please refer to the COPYRIGHT file distributed with this
7 * source distribution.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24#include "platform.h"
25#include "util.h"
26#include "gnunet_util.h"
27
28/* Returns a NULL-terminated string after unescaping an entity
29 * (eg. &amp;, &lt; &#38 etc.) starting at s. Returns NULL on failure.*/
30static char *
31detect_entity (const char *text, int *length)
32{
33 const char *pln;
34 int len;
35 int pound;
36 char b[7];
37 char *buf;
38
39 if (!text || *text != '&')
40 return NULL;
41
42#define IS_ENTITY(s) (!strncasecmp(text, s, (len = sizeof(s) - 1)))
43
44 if (IS_ENTITY ("&amp;"))
45 pln = "&";
46 else if (IS_ENTITY ("&lt;"))
47 pln = "<";
48 else if (IS_ENTITY ("&gt;"))
49 pln = ">";
50 else if (IS_ENTITY ("&nbsp;"))
51 pln = " ";
52 else if (IS_ENTITY ("&copy;"))
53 pln = "\302\251"; /* or use g_unichar_to_utf8(0xa9); */
54 else if (IS_ENTITY ("&quot;"))
55 pln = "\"";
56 else if (IS_ENTITY ("&reg;"))
57 pln = "\302\256"; /* or use g_unichar_to_utf8(0xae); */
58 else if (IS_ENTITY ("&apos;"))
59 pln = "\'";
60 else if (*(text + 1) == '#' && (sscanf (text, "&#%u;", &pound) == 1) &&
61 pound != 0 && *(text + 3 + (int) log10 (pound)) == ';')
62 {
63 buf = GNUNET_convert_string_to_utf8 (NULL,
64 (const char *) &pound,
65 2, "UNICODE");
66 if (strlen (buf) > 6)
67 buf[6] = '\0';
68 strcpy (b, buf);
69 pln = b;
70 GNUNET_free (buf);
71 len = 2;
72 while (isdigit ((int) text[len]))
73 len++;
74 if (text[len] == ';')
75 len++;
76 }
77 else
78 return NULL;
79
80 if (length)
81 *length = len;
82 return GNUNET_strdup (pln);
83}
84
85char *
86g_strdup_printf (const char *fmt, ...)
87{
88 size_t size;
89 char *buf;
90 va_list va;
91
92 va_start (va, fmt);
93 size = VSNPRINTF (NULL, 0, fmt, va) + 1;
94 va_end (va);
95 buf = GNUNET_malloc (size);
96 va_start (va, fmt);
97 VSNPRINTF (buf, size, fmt, va);
98 va_end (va);
99 return buf;
100}
101
102char *
103gaim_unescape_html (const char *html)
104{
105 if (html != NULL)
106 {
107 const char *c = html;
108 char *ret = GNUNET_strdup ("");
109 char *app;
110 while (*c)
111 {
112 int len;
113 char *ent;
114
115 if ((ent = detect_entity (c, &len)) != NULL)
116 {
117 app = g_strdup_printf ("%s%s", ret, ent);
118 GNUNET_free (ret);
119 ret = app;
120 c += len;
121 GNUNET_free (ent);
122 }
123 else if (!strncmp (c, "<br>", 4))
124 {
125 app = g_strdup_printf ("%s%s", ret, "\n");
126 GNUNET_free (ret);
127 ret = app;
128 c += 4;
129 }
130 else
131 {
132 app = g_strdup_printf ("%s%c", ret, *c);
133 GNUNET_free (ret);
134 ret = app;
135 c++;
136 }
137 }
138 return ret;
139 }
140 return NULL;
141}
142
143
144int
145gaim_str_has_prefix (const char *s, const char *p)
146{
147 if ((s == NULL) || (p == NULL))
148 return 0;
149 return !strncmp (s, p, strlen (p));
150}
151
152/* end of util.c */
diff --git a/src/upnp/upnp_util.h b/src/upnp/upnp_util.h
deleted file mode 100644
index 1c7555b09..000000000
--- a/src/upnp/upnp_util.h
+++ /dev/null
@@ -1,68 +0,0 @@
1/**
2 * @file upnp/upnp_util.h Utility Functions
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 * @todo Rename the functions so that they live somewhere in the gaim
26 * namespace.
27 */
28#ifndef _GAIM_UTIL_H_
29#define _GAIM_UTIL_H_
30
31#include <stdio.h>
32#include <string.h>
33
34#ifdef __cplusplus
35extern "C"
36{
37#endif
38
39/**
40 * Unescapes HTML entities to their literal characters.
41 * For example "&amp;" is replaced by '&' and so on.
42 * Actually only "&amp;", "&quot;", "&lt;" and "&gt;" are currently
43 * supported.
44 *
45 * @param html The string in which to unescape any HTML entities
46 *
47 * @return the text with HTML entities literalized
48 */
49 char *gaim_unescape_html (const char *html);
50
51/**
52 * Compares two strings to see if the first contains the second as
53 * a proper prefix.
54 *
55 * @param s The string to check.
56 * @param p The prefix in question.
57 *
58 * @return TRUE if p is a prefix of s, otherwise FALSE.
59 */
60 int gaim_str_has_prefix (const char *s, const char *p);
61
62 char *g_strdup_printf (const char *fmt, ...);
63
64#ifdef __cplusplus
65}
66#endif
67
68#endif
diff --git a/src/upnp/upnp_xmlnode.c b/src/upnp/upnp_xmlnode.c
deleted file mode 100644
index 46e66e11c..000000000
--- a/src/upnp/upnp_xmlnode.c
+++ /dev/null
@@ -1,487 +0,0 @@
1/**
2 * @file upnp/upnp_xmlnode.c XML DOM functions
3 *
4 * gaim
5 *
6 * Gaim is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25/* A lot of this code at least resembles the code in libxode, but since
26 * libxode uses memory pools that we simply have no need for, I decided to
27 * write my own stuff. Also, re-writing this lets me be as lightweight
28 * as I want to be. Thank you libxode for giving me a good starting point */
29
30#include "platform.h"
31
32#include "util.h"
33#include "gnunet_util.h"
34#include "xmlnode.h"
35
36#include <libxml/parser.h>
37#include <string.h>
38
39
40#ifdef _WIN32
41# define NEWLINE_S "\r\n"
42#else
43# define NEWLINE_S "\n"
44#endif
45
46#define TRUE GNUNET_YES
47#define FALSE GNUNET_NO
48
49#define g_return_if_fail(a) if(!(a)) return;
50#define g_return_val_if_fail(a, val) if(!(a)) return (val);
51
52/**
53 * The valid types for an xmlnode
54 */
55typedef enum _XMLNodeType
56{
57 XMLNODE_TYPE_TAG, /**< Just a tag */
58 XMLNODE_TYPE_ATTRIB, /**< Has attributes */
59 XMLNODE_TYPE_DATA /**< Has data */
60} XMLNodeType;
61
62typedef struct
63{
64 xmlnode *current;
65 xmlnode **nodes;
66 unsigned int pos;
67 unsigned int size;
68} XMLNodePool;
69
70struct _xmlnode
71{
72 char *name; /**< The name of the node. */
73 char *xmlns; /**< The namespace of the node */
74 XMLNodeType type; /**< The type of the node. */
75 char *data; /**< The data for the node. */
76 size_t data_sz; /**< The size of the data. */
77 struct _xmlnode *parent; /**< The parent node or @c NULL.*/
78 struct _xmlnode *child; /**< The child node or @c NULL.*/
79 struct _xmlnode *lastchild; /**< The last child node or @c NULL.*/
80 struct _xmlnode *next; /**< The next node or @c NULL. */
81 XMLNodePool *pool;
82 int free_pool; /* set to GNUNET_YES for the root node, which must free the pool */
83};
84
85
86static void *
87g_memdup (const void *data, size_t s)
88{
89 void *ret;
90
91 ret = GNUNET_malloc (s);
92 memcpy (ret, data, s);
93 return ret;
94}
95
96static char *
97g_string_append_len (char *prefix, const void *data, size_t s)
98{
99 char *ret;
100
101 ret = g_strdup_printf ("%s%.*s", prefix, s, data);
102 GNUNET_free (prefix);
103 return ret;
104}
105
106static xmlnode *
107new_node (const char *name, XMLNodeType type, void *user_data)
108{
109 xmlnode *node = GNUNET_malloc (sizeof (xmlnode));
110
111 node->name = name == NULL ? NULL : GNUNET_strdup (name);
112 node->type = type;
113 node->pool = user_data;
114 if (node->pool->size == node->pool->pos)
115 GNUNET_array_grow (node->pool->nodes, node->pool->size,
116 node->pool->size * 2 + 64);
117 node->pool->nodes[node->pool->pos++] = node;
118 node->free_pool = 0;
119 return node;
120}
121
122static xmlnode *
123xmlnode_new (const char *name, void *user_data)
124{
125 g_return_val_if_fail (name != NULL, NULL);
126 return new_node (name, XMLNODE_TYPE_TAG, user_data);
127}
128
129static void
130xmlnode_insert_child (xmlnode * parent, xmlnode * child)
131{
132 g_return_if_fail (parent != NULL);
133 g_return_if_fail (child != NULL);
134
135 child->parent = parent;
136 if (parent->lastchild)
137 parent->lastchild->next = child;
138 else
139 parent->child = child;
140 parent->lastchild = child;
141}
142
143static xmlnode *
144xmlnode_new_child (xmlnode * parent, const char *name, void *user_data)
145{
146 xmlnode *node;
147
148 g_return_val_if_fail (parent != NULL, NULL);
149 g_return_val_if_fail (name != NULL, NULL);
150 node = new_node (name, XMLNODE_TYPE_TAG, user_data);
151 xmlnode_insert_child (parent, node);
152 return node;
153}
154
155static void
156xmlnode_insert_data (xmlnode * node,
157 const char *data, int size, void *user_data)
158{
159 xmlnode *child;
160 size_t real_size;
161
162 g_return_if_fail (node != NULL);
163 g_return_if_fail (data != NULL);
164 g_return_if_fail (size != 0);
165 real_size = size == -1 ? strlen (data) : size;
166 child = new_node (NULL, XMLNODE_TYPE_DATA, user_data);
167 child->data = g_memdup (data, real_size);
168 child->data_sz = real_size;
169 xmlnode_insert_child (node, child);
170}
171
172static void
173xmlnode_remove_attrib (xmlnode * node, const char *attr)
174{
175 xmlnode *attr_node, *sibling = NULL;
176
177 g_return_if_fail (node != NULL);
178 g_return_if_fail (attr != NULL);
179
180 for (attr_node = node->child; attr_node; attr_node = attr_node->next)
181 {
182 if (attr_node->type == XMLNODE_TYPE_ATTRIB &&
183 !strcmp (attr_node->name, attr))
184 {
185 if (node->child == attr_node)
186 {
187 node->child = attr_node->next;
188 }
189 else
190 {
191 sibling->next = attr_node->next;
192 }
193 if (node->lastchild == attr_node)
194 {
195 node->lastchild = sibling;
196 }
197 xmlnode_free (attr_node);
198 return;
199 }
200 sibling = attr_node;
201 }
202}
203
204static void
205xmlnode_set_attrib (xmlnode * node,
206 const char *attr, const char *value, void *user_data)
207{
208 xmlnode *attrib_node;
209
210 g_return_if_fail (node != NULL);
211 g_return_if_fail (attr != NULL);
212 g_return_if_fail (value != NULL);
213 xmlnode_remove_attrib (node, attr);
214 attrib_node = new_node (attr, XMLNODE_TYPE_ATTRIB, user_data);
215 attrib_node->data = GNUNET_strdup (value);
216 xmlnode_insert_child (node, attrib_node);
217}
218
219static void
220xmlnode_set_namespace (xmlnode * node, const char *xmlns)
221{
222 g_return_if_fail (node != NULL);
223 GNUNET_free_non_null (node->xmlns);
224 node->xmlns = GNUNET_strdup (xmlns);
225}
226
227static const char *
228xmlnode_get_namespace (xmlnode * node)
229{
230 g_return_val_if_fail (node != NULL, NULL);
231 return node->xmlns;
232}
233
234static void
235freePool (XMLNodePool * pool)
236{
237 unsigned int i;
238 xmlnode *x;
239
240 for (i = 0; i < pool->pos; i++)
241 {
242 x = pool->nodes[i];
243 GNUNET_free_non_null (x->name);
244 GNUNET_free_non_null (x->data);
245 GNUNET_free_non_null (x->xmlns);
246 GNUNET_free (x);
247 }
248 GNUNET_array_grow (pool->nodes, pool->size, 0);
249 GNUNET_free (pool);
250}
251
252void
253xmlnode_free (xmlnode * node)
254{
255 g_return_if_fail (node != NULL);
256 if (node->free_pool != GNUNET_YES)
257 return;
258 freePool (node->pool);
259}
260
261static xmlnode *
262xmlnode_get_child_with_namespace (const xmlnode * parent,
263 const char *name, const char *ns)
264{
265 xmlnode *x;
266 xmlnode *ret = NULL;
267 char *parent_name;
268 char *child_name;
269
270 if (parent == NULL)
271 return NULL;
272 if (name == NULL)
273 return NULL;
274
275 parent_name = GNUNET_strdup (name);
276 child_name = strstr (parent_name, "/");
277 if (child_name != NULL)
278 {
279 child_name[0] = '\0';
280 child_name++;
281 }
282
283 for (x = parent->child; x; x = x->next)
284 {
285 const char *xmlns = NULL;
286 if (ns)
287 xmlns = xmlnode_get_namespace (x);
288
289 if (x->type == XMLNODE_TYPE_TAG && name
290 && !strcmp (parent_name, x->name) && (!ns
291 || (xmlns
292 && !strcmp (ns, xmlns))))
293 {
294 ret = x;
295 break;
296 }
297 }
298
299 if (child_name && ret)
300 ret = xmlnode_get_child (ret, child_name);
301
302 GNUNET_free (parent_name);
303 return ret;
304}
305
306xmlnode *
307xmlnode_get_child (const xmlnode * parent, const char *name)
308{
309 return xmlnode_get_child_with_namespace (parent, name, NULL);
310}
311
312char *
313xmlnode_get_data (xmlnode * node)
314{
315 char *str = NULL;
316 xmlnode *c;
317
318 if (node == NULL)
319 return NULL;
320 for (c = node->child; c; c = c->next)
321 {
322 if (c->type == XMLNODE_TYPE_DATA)
323 {
324 if (!str)
325 str = GNUNET_strdup ("");
326 str = g_string_append_len (str, c->data, c->data_sz);
327 }
328 }
329 if (str == NULL)
330 return NULL;
331
332 return str;
333}
334
335static void
336xmlnode_parser_element_start_libxml (void *user_data,
337 const xmlChar * element_name,
338 const xmlChar * prefix,
339 const xmlChar * xmlns,
340 int nb_namespaces,
341 const xmlChar ** namespaces,
342 int nb_attributes,
343 int nb_defaulted,
344 const xmlChar ** attributes)
345{
346 XMLNodePool *xpd = user_data;
347 xmlnode *node;
348 int i;
349
350 if (!element_name)
351 return;
352 if (xpd->current)
353 node =
354 xmlnode_new_child (xpd->current, (const char *) element_name,
355 user_data);
356 else
357 node = xmlnode_new ((const char *) element_name, user_data);
358
359 xmlnode_set_namespace (node, (const char *) xmlns);
360
361 for (i = 0; i < nb_attributes * 5; i += 5)
362 {
363 char *txt;
364 int attrib_len = attributes[i + 4] - attributes[i + 3];
365 char *attrib = GNUNET_malloc (attrib_len + 1);
366 memcpy (attrib, attributes[i + 3], attrib_len);
367 attrib[attrib_len] = '\0';
368 txt = attrib;
369 attrib = gaim_unescape_html (txt);
370 GNUNET_free (txt);
371 xmlnode_set_attrib (node, (const char *) attributes[i], attrib,
372 user_data);
373 GNUNET_free (attrib);
374 }
375 xpd->current = node;
376}
377
378static void
379xmlnode_parser_element_end_libxml (void *user_data,
380 const xmlChar * element_name,
381 const xmlChar * prefix,
382 const xmlChar * xmlns)
383{
384 XMLNodePool *xpd = user_data;
385
386 if (!element_name || !xpd->current)
387 return;
388 if (xpd->current->parent)
389 {
390 if (!xmlStrcmp ((xmlChar *) xpd->current->name, element_name))
391 xpd->current = xpd->current->parent;
392 }
393}
394
395static void
396xmlnode_parser_element_text_libxml (void *user_data,
397 const xmlChar * text, int text_len)
398{
399 XMLNodePool *xpd = user_data;
400
401 if (!xpd->current || !text || !text_len)
402 return;
403 xmlnode_insert_data (xpd->current,
404 (const char *) text, text_len, user_data);
405}
406
407static xmlSAXHandler xmlnode_parser_libxml = {
408 .internalSubset = NULL,
409 .isStandalone = NULL,
410 .hasInternalSubset = NULL,
411 .hasExternalSubset = NULL,
412 .resolveEntity = NULL,
413 .getEntity = NULL,
414 .entityDecl = NULL,
415 .notationDecl = NULL,
416 .attributeDecl = NULL,
417 .elementDecl = NULL,
418 .unparsedEntityDecl = NULL,
419 .setDocumentLocator = NULL,
420 .startDocument = NULL,
421 .endDocument = NULL,
422 .startElement = NULL,
423 .endElement = NULL,
424 .reference = NULL,
425 .characters = xmlnode_parser_element_text_libxml,
426 .ignorableWhitespace = NULL,
427 .processingInstruction = NULL,
428 .comment = NULL,
429 .warning = NULL,
430 .error = NULL,
431 .fatalError = NULL,
432 .getParameterEntity = NULL,
433 .cdataBlock = NULL,
434 .externalSubset = NULL,
435 .initialized = XML_SAX2_MAGIC,
436 ._private = NULL,
437 .startElementNs = xmlnode_parser_element_start_libxml,
438 .endElementNs = xmlnode_parser_element_end_libxml,
439 .serror = NULL
440};
441
442xmlnode *
443xmlnode_from_str (const char *str, int size)
444{
445 XMLNodePool *xpd;
446 xmlnode *ret;
447 size_t real_size;
448
449 g_return_val_if_fail (str != NULL, NULL);
450
451 real_size = size < 0 ? strlen (str) : size;
452 xpd = GNUNET_malloc (sizeof (XMLNodePool));
453 memset (xpd, 0, sizeof (XMLNodePool));
454 if (xmlSAXUserParseMemory (&xmlnode_parser_libxml, xpd, str, real_size) < 0)
455 {
456 freePool (xpd);
457 return NULL;
458 }
459 ret = xpd->current;
460 ret->free_pool = GNUNET_YES;
461 return ret;
462}
463
464xmlnode *
465xmlnode_get_next_twin (xmlnode * node)
466{
467 xmlnode *sibling;
468 const char *ns = xmlnode_get_namespace (node);
469
470 g_return_val_if_fail (node != NULL, NULL);
471 g_return_val_if_fail (node->type == XMLNODE_TYPE_TAG, NULL);
472
473 for (sibling = node->next; sibling; sibling = sibling->next)
474 {
475 const char *xmlns = NULL;
476 if (ns)
477 xmlns = xmlnode_get_namespace (sibling);
478
479 if (sibling->type == XMLNODE_TYPE_TAG
480 && !strcmp (node->name, sibling->name) && (!ns
481 || (xmlns
482 && !strcmp (ns,
483 xmlns))))
484 return sibling;
485 }
486 return NULL;
487}
diff --git a/src/upnp/upnp_xmlnode.h b/src/upnp/upnp_xmlnode.h
deleted file mode 100644
index e3b9136a5..000000000
--- a/src/upnp/upnp_xmlnode.h
+++ /dev/null
@@ -1,92 +0,0 @@
1/**
2 * @file upnp/upnp_xmlnode.h XML DOM functions
3 * @ingroup core
4 *
5 * gaim
6 *
7 * Gaim is the legal property of its developers, whose names are too numerous
8 * to list here. Please refer to the COPYRIGHT file distributed with this
9 * source distribution.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25#ifndef _GGAIM_XMLNODE_H_
26#define _GGAIM_XMLNODE_H_
27
28
29#ifdef __cplusplus
30extern "C"
31{
32#endif
33
34/**
35 * An xmlnode.
36 */
37 typedef struct _xmlnode xmlnode;
38
39/**
40 * Gets a child node named name.
41 *
42 * @param parent The parent node.
43 * @param name The child's name.
44 *
45 * @return The child or NULL.
46 */
47 xmlnode *xmlnode_get_child (const xmlnode * parent, const char *name);
48
49/**
50 * Gets the next node with the same name as node.
51 *
52 * @param node The node of a twin to find.
53 *
54 * @return The twin of node or NULL.
55 */
56 xmlnode *xmlnode_get_next_twin (xmlnode * node);
57
58/**
59 * Gets data from a node.
60 *
61 * @param node The node to get data from.
62 *
63 * @return The data from the node. You must g_free
64 * this string when finished using it.
65 */
66 char *xmlnode_get_data (xmlnode * node);
67
68/**
69 * Creates a node from a string of XML. Calling this on the
70 * root node of an XML document will parse the entire document
71 * into a tree of nodes, and return the xmlnode of the root.
72 *
73 * @param str The string of xml.
74 * @param size The size of the string, or -1 if @a str is
75 * NUL-terminated.
76 *
77 * @return The new node.
78 */
79 xmlnode *xmlnode_from_str (const char *str, int size);
80
81/**
82 * Frees a node and all of it's children.
83 *
84 * @param node The node to free.
85 */
86 void xmlnode_free (xmlnode * node);
87
88#ifdef __cplusplus
89}
90#endif
91
92#endif /* _GAIM_XMLNODE_H_ */