3
Copyright (c) 2007, Arvid Norberg
6
Redistribution and use in source and binary forms, with or without
7
modification, are permitted provided that the following conditions
10
* Redistributions of source code must retain the above copyright
11
notice, this list of conditions and the following disclaimer.
12
* Redistributions in binary form must reproduce the above copyright
13
notice, this list of conditions and the following disclaimer in
14
the documentation and/or other materials provided with the distribution.
15
* Neither the name of the author nor the names of its
16
contributors may be used to endorse or promote products derived
17
from this software without specific prior written permission.
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
POSSIBILITY OF SUCH DAMAGE.
33
#include "libtorrent/pch.hpp"
35
#include "libtorrent/socket.hpp"
36
#include "libtorrent/upnp.hpp"
37
#include "libtorrent/io.hpp"
38
#include "libtorrent/http_tracker_connection.hpp"
39
#include "libtorrent/xml_parse.hpp"
40
#include "libtorrent/connection_queue.hpp"
41
#include "libtorrent/enum_net.hpp"
43
#include <boost/bind.hpp>
44
#include <boost/ref.hpp>
45
#include <asio/ip/host_name.hpp>
46
#include <asio/ip/multicast.hpp>
47
#include <boost/thread/mutex.hpp>
50
#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined(TORRENT_UPNP_LOGGING)
51
#define TORRENT_UPNP_LOGGING
55
using namespace libtorrent;
59
bool is_local(address const& a);
60
address guess_local_address(asio::io_service&);
63
upnp::upnp(io_service& ios, connection_queue& cc
64
, address const& listen_interface, std::string const& user_agent
65
, portmap_callback_t const& cb, bool ignore_nonrouters)
68
, m_user_agent(user_agent)
73
, m_socket(ios, udp::endpoint(address_v4::from_string("239.255.255.250"), 1900)
74
, bind(&upnp::on_reply, self(), _1, _2, _3), false)
75
, m_broadcast_timer(ios)
76
, m_refresh_timer(ios)
79
, m_ignore_outside_network(ignore_nonrouters)
82
#ifdef TORRENT_UPNP_LOGGING
83
m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc);
92
void upnp::discover_device() try
94
const char msearch[] =
95
"M-SEARCH * HTTP/1.1\r\n"
96
"HOST: 239.255.255.250:1900\r\n"
97
"ST:upnp:rootdevice\r\n"
98
"MAN:\"ssdp:discover\"\r\n"
103
#ifdef TORRENT_DEBUG_UPNP
104
// simulate packet loss
105
if (m_retry_count & 1)
107
m_socket.send(msearch, sizeof(msearch) - 1, ec);
111
#ifdef TORRENT_UPNP_LOGGING
112
m_log << time_now_string()
113
<< " ==> Broadcast FAILED: " << ec.message() << std::endl
114
<< "aborting" << std::endl;
121
m_broadcast_timer.expires_from_now(milliseconds(250 * m_retry_count));
122
m_broadcast_timer.async_wait(bind(&upnp::resend_request
125
#ifdef TORRENT_UPNP_LOGGING
126
m_log << time_now_string()
127
<< " ==> Broadcasting search for rootdevice" << std::endl;
130
catch (std::exception&)
135
void upnp::set_mappings(int tcp, int udp)
137
#ifdef TORRENT_UPNP_LOGGING
138
m_log << time_now_string()
139
<< " *** set mappings " << tcp << " " << udp;
140
if (m_disabled) m_log << " DISABLED";
144
if (m_disabled) return;
145
if (udp != 0) m_udp_local_port = udp;
146
if (tcp != 0) m_tcp_local_port = tcp;
148
for (std::set<rootdevice>::iterator i = m_devices.begin()
149
, end(m_devices.end()); i != end; ++i)
151
rootdevice& d = const_cast<rootdevice&>(*i);
152
TORRENT_ASSERT(d.magic == 1337);
153
if (d.mapping[0].local_port != m_tcp_local_port)
155
if (d.mapping[0].external_port == 0)
156
d.mapping[0].external_port = m_tcp_local_port;
157
d.mapping[0].local_port = m_tcp_local_port;
158
d.mapping[0].need_update = true;
160
if (d.mapping[1].local_port != m_udp_local_port)
162
if (d.mapping[1].external_port == 0)
163
d.mapping[1].external_port = m_udp_local_port;
164
d.mapping[1].local_port = m_udp_local_port;
165
d.mapping[1].need_update = true;
167
if (d.service_namespace
168
&& (d.mapping[0].need_update || d.mapping[1].need_update))
173
void upnp::resend_request(asio::error_code const& e)
179
if (m_retry_count < 9
180
&& (m_devices.empty() || m_retry_count < 4))
186
if (m_devices.empty())
188
#ifdef TORRENT_UPNP_LOGGING
189
m_log << time_now_string()
190
<< " *** Got no response in 9 retries. Giving up, "
191
"disabling UPnP." << std::endl;
197
for (std::set<rootdevice>::iterator i = m_devices.begin()
198
, end(m_devices.end()); i != end; ++i)
200
if (i->control_url.empty() && !i->upnp_connection && !i->disabled)
202
// we don't have a WANIP or WANPPP url for this device,
204
rootdevice& d = const_cast<rootdevice&>(*i);
205
TORRENT_ASSERT(d.magic == 1337);
208
#ifdef TORRENT_UPNP_LOGGING
209
m_log << time_now_string()
210
<< " ==> connecting to " << d.url << std::endl;
212
if (d.upnp_connection) d.upnp_connection->close();
213
d.upnp_connection.reset(new http_connection(m_io_service
214
, m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
215
, boost::ref(d), _5)));
216
d.upnp_connection->get(d.url);
218
catch (std::exception& e)
221
#ifdef TORRENT_UPNP_LOGGING
222
m_log << time_now_string()
223
<< " *** Connection failed to: " << d.url
224
<< " " << e.what() << std::endl;
232
catch (std::exception&)
234
TORRENT_ASSERT(false);
238
void upnp::on_reply(udp::endpoint const& from, char* buffer
239
, std::size_t bytes_transferred)
244
using namespace libtorrent::detail;
246
// parse out the url for the device
249
the response looks like this:
253
USN:uuid:000f-66d6-7296000099dc::upnp:rootdevice
254
Location: http://192.168.1.1:5431/dyndev/uuid:000f-66d6-7296000099dc
255
Server: Custom/1.0 UPnP/1.0 Proc/Ver
257
Cache-Control:max-age=180
258
DATE: Fri, 02 Jan 1970 08:10:38 GMT
260
a notification looks like this:
263
Host:239.255.255.250:1900
264
NT:urn:schemas-upnp-org:device:MediaServer:1
266
Location:http://10.0.3.169:2869/upnphost/udhisapi.dll?content=uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e
267
USN:uuid:c17f0c32-d19b-4938-ae94-65f945c3a26e::urn:schemas-upnp-org:device:MediaServer:1
268
Cache-Control:max-age=900
269
Server:Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0
273
if (m_ignore_outside_network && !in_local_network(m_io_service, from.address(), ec))
275
// this upnp device is filtered because it's not in the
276
// list of configured routers
277
#ifdef TORRENT_UPNP_LOGGING
280
m_log << time_now_string() << " <== (" << from << ") error: "
281
<< ec.message() << std::endl;
285
std::vector<ip_interface> const& net = enum_net_interfaces(m_io_service, ec);
286
m_log << time_now_string() << " <== (" << from << ") UPnP device "
287
"ignored because it's not on our network ";
288
for (std::vector<ip_interface>::const_iterator i = net.begin()
289
, end(net.end()); i != end; ++i)
291
m_log << "(" << i->interface_address << ", " << i->netmask << ") ";
302
p.incoming(buffer::const_interval(buffer
303
, buffer + bytes_transferred));
305
catch (std::exception& e)
308
#ifdef TORRENT_UPNP_LOGGING
309
m_log << time_now_string()
310
<< " <== (" << from << ") Rootdevice responded with incorrect HTTP packet. Ignoring device (" << e.what() << ")" << std::endl;
315
if (p.status_code() != 200 && p.method() != "notify")
317
#ifdef TORRENT_UPNP_LOGGING
318
if (p.method().empty())
319
m_log << time_now_string()
320
<< " <== (" << from << ") Device responded with HTTP status: " << p.status_code()
321
<< ". Ignoring device" << std::endl;
323
m_log << time_now_string()
324
<< " <== (" << from << ") Device with HTTP method: " << p.method()
325
<< ". Ignoring device" << std::endl;
330
if (!p.header_finished())
332
#ifdef TORRENT_UPNP_LOGGING
333
m_log << time_now_string()
334
<< " <== (" << from << ") Rootdevice responded with incomplete HTTP "
335
"packet. Ignoring device" << std::endl;
340
std::string url = p.header("location");
343
#ifdef TORRENT_UPNP_LOGGING
344
m_log << time_now_string()
345
<< " <== (" << from << ") Rootdevice response is missing a location header. "
346
"Ignoring device" << std::endl;
354
std::set<rootdevice>::iterator i = m_devices.find(d);
356
if (i == m_devices.end())
359
std::string protocol;
361
// we don't have this device in our list. Add it
364
boost::tie(protocol, auth, d.hostname, d.port, d.path)
365
= parse_url_components(d.url);
367
catch (std::exception& e)
369
#ifdef TORRENT_UPNP_LOGGING
370
m_log << time_now_string()
371
<< " <== (" << from << ") invalid url: '" << d.url
372
<< "'. Ignoring device" << std::endl;
377
// ignore the auth here. It will be re-parsed
378
// by the http connection later
380
if (protocol != "http")
382
#ifdef TORRENT_UPNP_LOGGING
383
m_log << time_now_string()
384
<< " <== (" << from << ") Rootdevice uses unsupported protocol: '" << protocol
385
<< "'. Ignoring device" << std::endl;
392
#ifdef TORRENT_UPNP_LOGGING
393
m_log << time_now_string()
394
<< " <== (" << from << ") Rootdevice responded with a url with port 0. "
395
"Ignoring device" << std::endl;
399
#ifdef TORRENT_UPNP_LOGGING
400
m_log << time_now_string()
401
<< " <== (" << from << ") Found rootdevice: " << d.url
402
<< " total: " << m_devices.size() << std::endl;
405
if (m_devices.size() >= 50)
407
#ifdef TORRENT_UPNP_LOGGING
408
m_log << time_now_string()
409
<< " <== (" << from << ") Too many devices (" << m_devices.size() << "), "
410
"ignoring: " << d.url << std::endl;
415
if (m_tcp_local_port != 0)
417
d.mapping[0].need_update = true;
418
d.mapping[0].local_port = m_tcp_local_port;
419
if (d.mapping[0].external_port == 0)
420
d.mapping[0].external_port = d.mapping[0].local_port;
421
#ifdef TORRENT_UPNP_LOGGING
422
m_log << time_now_string() << " *** Mapping 0 will be updated" << std::endl;
425
if (m_udp_local_port != 0)
427
d.mapping[1].need_update = true;
428
d.mapping[1].local_port = m_udp_local_port;
429
if (d.mapping[1].external_port == 0)
430
d.mapping[1].external_port = d.mapping[1].local_port;
431
#ifdef TORRENT_UPNP_LOGGING
432
m_log << time_now_string() << " *** Mapping 1 will be updated" << std::endl;
435
boost::tie(i, boost::tuples::ignore) = m_devices.insert(d);
439
// since we're using udp, send the query 4 times
440
// just to make sure we find all devices
441
if (m_retry_count >= 4 && !m_devices.empty())
443
m_broadcast_timer.cancel();
445
for (std::set<rootdevice>::iterator i = m_devices.begin()
446
, end(m_devices.end()); i != end; ++i)
448
if (i->control_url.empty() && !i->upnp_connection && !i->disabled)
450
// we don't have a WANIP or WANPPP url for this device,
452
rootdevice& d = const_cast<rootdevice&>(*i);
453
TORRENT_ASSERT(d.magic == 1337);
456
#ifdef TORRENT_UPNP_LOGGING
457
m_log << time_now_string()
458
<< " ==> connecting to " << d.url << std::endl;
460
if (d.upnp_connection) d.upnp_connection->close();
461
d.upnp_connection.reset(new http_connection(m_io_service
462
, m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
463
, boost::ref(d), _5)));
464
d.upnp_connection->get(d.url);
466
catch (std::exception& e)
469
#ifdef TORRENT_UPNP_LOGGING
470
m_log << time_now_string()
471
<< " *** Connection failed to: " << d.url
472
<< " " << e.what() << std::endl;
481
catch (std::exception&)
483
TORRENT_ASSERT(false);
487
void upnp::post(upnp::rootdevice const& d, std::string const& soap
488
, std::string const& soap_action)
490
TORRENT_ASSERT(d.magic == 1337);
491
TORRENT_ASSERT(d.upnp_connection);
493
std::stringstream header;
495
header << "POST " << d.control_url << " HTTP/1.1\r\n"
496
"Host: " << d.hostname << ":" << d.port << "\r\n"
497
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
498
"Content-Length: " << soap.size() << "\r\n"
499
"Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap;
501
d.upnp_connection->sendbuffer = header.str();
503
#ifdef TORRENT_UPNP_LOGGING
504
m_log << time_now_string()
505
<< " ==> sending: " << header.str() << std::endl;
510
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
512
TORRENT_ASSERT(d.magic == 1337);
514
if (!d.upnp_connection)
516
TORRENT_ASSERT(d.disabled);
517
#ifdef TORRENT_UPNP_LOGGING
518
m_log << time_now_string() << " *** mapping (" << i
519
<< ") aborted" << std::endl;
524
std::string soap_action = "AddPortMapping";
526
std::stringstream soap;
528
soap << "<?xml version=\"1.0\"?>\n"
529
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
530
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
531
"<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
533
soap << "<NewRemoteHost></NewRemoteHost>"
534
"<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
535
"<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>"
536
"<NewInternalPort>" << d.mapping[i].local_port << "</NewInternalPort>"
537
"<NewInternalClient>" << c.socket().local_endpoint().address().to_string() << "</NewInternalClient>"
538
"<NewEnabled>1</NewEnabled>"
539
"<NewPortMappingDescription>" << m_user_agent << "</NewPortMappingDescription>"
540
"<NewLeaseDuration>" << d.lease_duration << "</NewLeaseDuration>";
541
soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
543
post(d, soap.str(), soap_action);
546
void upnp::map_port(rootdevice& d, int i)
548
TORRENT_ASSERT(d.magic == 1337);
549
if (d.upnp_connection) return;
551
if (!d.mapping[i].need_update)
553
#ifdef TORRENT_UPNP_LOGGING
554
m_log << time_now_string() << " *** mapping (" << i
555
<< ") does not need update, skipping" << std::endl;
557
if (i < num_mappings - 1)
561
d.mapping[i].need_update = false;
562
TORRENT_ASSERT(!d.upnp_connection);
563
TORRENT_ASSERT(d.service_namespace);
565
#ifdef TORRENT_UPNP_LOGGING
566
m_log << time_now_string()
567
<< " ==> connecting to " << d.hostname << std::endl;
569
if (d.upnp_connection) d.upnp_connection->close();
570
d.upnp_connection.reset(new http_connection(m_io_service
571
, m_cc, bind(&upnp::on_upnp_map_response, self(), _1, _2
572
, boost::ref(d), i, _5), true
573
, bind(&upnp::create_port_mapping, self(), _1, boost::ref(d), i)));
575
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
579
void upnp::delete_port_mapping(rootdevice& d, int i)
581
TORRENT_ASSERT(d.magic == 1337);
583
if (!d.upnp_connection)
585
TORRENT_ASSERT(d.disabled);
586
#ifdef TORRENT_UPNP_LOGGING
587
m_log << time_now_string() << " *** unmapping (" << i
588
<< ") aborted" << std::endl;
593
std::stringstream soap;
595
std::string soap_action = "DeletePortMapping";
597
soap << "<?xml version=\"1.0\"?>\n"
598
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
599
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
600
"<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
602
soap << "<NewRemoteHost></NewRemoteHost>"
603
"<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
604
"<NewProtocol>" << (d.mapping[i].protocol ? "UDP" : "TCP") << "</NewProtocol>";
605
soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
607
post(d, soap.str(), soap_action);
610
// requires the mutex to be locked
611
void upnp::unmap_port(rootdevice& d, int i)
613
TORRENT_ASSERT(d.magic == 1337);
614
if (d.mapping[i].external_port == 0
617
if (i < num_mappings - 1)
619
unmap_port(d, i + 1);
623
#ifdef TORRENT_UPNP_LOGGING
624
m_log << time_now_string()
625
<< " ==> connecting to " << d.hostname << std::endl;
628
if (d.upnp_connection) d.upnp_connection->close();
629
d.upnp_connection.reset(new http_connection(m_io_service
630
, m_cc, bind(&upnp::on_upnp_unmap_response, self(), _1, _2
631
, boost::ref(d), i, _5), true
632
, bind(&upnp::delete_port_mapping, self(), boost::ref(d), i)));
633
d.upnp_connection->start(d.hostname, boost::lexical_cast<std::string>(d.port)
641
parse_state(): found_service(false), exit(false) {}
642
void reset(char const* st)
644
found_service = false;
651
std::string control_url;
652
char const* service_type;
655
void find_control_url(int type, char const* string, parse_state& state)
657
if (state.exit) return;
659
if (type == xml_start_tag)
661
if ((!state.top_tag.empty() && state.top_tag == "service")
662
|| !strcmp(string, "service"))
664
state.top_tag = string;
667
else if (type == xml_end_tag)
669
if (!strcmp(string, "service"))
671
state.top_tag.clear();
672
if (state.found_service) state.exit = true;
674
else if (!state.top_tag.empty() && state.top_tag != "service")
675
state.top_tag = "service";
677
else if (type == xml_string)
679
if (state.top_tag == "serviceType")
681
if (!strcmp(string, state.service_type))
682
state.found_service = true;
684
else if (state.top_tag == "controlURL")
686
state.control_url = string;
687
if (state.found_service) state.exit = true;
694
void upnp::on_upnp_xml(asio::error_code const& e
695
, libtorrent::http_parser const& p, rootdevice& d
696
, http_connection& c) try
698
TORRENT_ASSERT(d.magic == 1337);
699
if (d.upnp_connection && d.upnp_connection.get() == &c)
701
d.upnp_connection->close();
702
d.upnp_connection.reset();
705
if (e && e != asio::error::eof)
707
#ifdef TORRENT_UPNP_LOGGING
708
m_log << time_now_string()
709
<< " <== (" << d.url << ") error while fetching control url: "
710
<< e.message() << std::endl;
716
if (!p.header_finished())
718
#ifdef TORRENT_UPNP_LOGGING
719
m_log << time_now_string()
720
<< " <== (" << d.url << ") error while fetching control url: incomplete http message" << std::endl;
726
if (p.status_code() != 200)
728
#ifdef TORRENT_UPNP_LOGGING
729
m_log << time_now_string()
730
<< " <== (" << d.url << ") error while fetching control url: " << p.message() << std::endl;
737
s.reset("urn:schemas-upnp-org:service:WANIPConnection:1");
738
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
739
, bind(&find_control_url, _1, _2, boost::ref(s)));
742
d.service_namespace = s.service_type;
746
// we didn't find the WAN IP connection, look for
748
s.reset("urn:schemas-upnp-org:service:WANPPPConnection:1");
749
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
750
, bind(&find_control_url, _1, _2, boost::ref(s)));
753
d.service_namespace = s.service_type;
757
#ifdef TORRENT_UPNP_LOGGING
758
m_log << time_now_string()
759
<< " <== (" << d.url << ") Rootdevice response, did not find "
760
"a port mapping interface" << std::endl;
767
#ifdef TORRENT_UPNP_LOGGING
768
m_log << time_now_string()
769
<< " <== (" << d.url << ") Rootdevice response, found control URL: " << s.control_url
770
<< " namespace: " << d.service_namespace << std::endl;
773
d.control_url = s.control_url;
777
catch (std::exception&)
786
m_broadcast_timer.cancel();
787
m_refresh_timer.cancel();
793
struct error_code_parse_state
795
error_code_parse_state(): in_error_code(false), exit(false), error_code(-1) {}
801
void find_error_code(int type, char const* string, error_code_parse_state& state)
803
if (state.exit) return;
804
if (type == xml_start_tag && !strcmp("errorCode", string))
806
state.in_error_code = true;
808
else if (type == xml_string && state.in_error_code)
810
state.error_code = std::atoi(string);
824
error_code_t error_codes[] =
826
{402, "Invalid Arguments"}
827
, {501, "Action Failed"}
828
, {714, "The specified value does not exist in the array"}
829
, {715, "The source IP address cannot be wild-carded"}
830
, {716, "The external port cannot be wild-carded"}
831
, {718, "The port mapping entry specified conflicts with "
832
"a mapping assigned previously to another client"}
833
, {724, "Internal and External port values must be the same"}
834
, {725, "The NAT implementation only supports permanent "
835
"lease times on port mappings"}
836
, {726, "RemoteHost must be a wildcard and cannot be a "
837
"specific IP address or DNS name"}
838
, {727, "ExternalPort must be a wildcard and cannot be a specific port "}
843
void upnp::on_upnp_map_response(asio::error_code const& e
844
, libtorrent::http_parser const& p, rootdevice& d, int mapping
845
, http_connection& c) try
847
TORRENT_ASSERT(d.magic == 1337);
848
if (d.upnp_connection && d.upnp_connection.get() == &c)
850
d.upnp_connection->close();
851
d.upnp_connection.reset();
854
if (e && e != asio::error::eof)
856
#ifdef TORRENT_UPNP_LOGGING
857
m_log << time_now_string()
858
<< " <== error while adding portmap: " << e.message() << std::endl;
864
if (m_closing) return;
866
// error code response may look like this:
867
// <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
868
// s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
871
// <faultcode>s:Client</faultcode>
872
// <faultstring>UPnPError</faultstring>
874
// <UPnPErrorxmlns="urn:schemas-upnp-org:control-1-0">
875
// <errorCode>402</errorCode>
876
// <errorDescription>Invalid Args</errorDescription>
883
if (!p.header_finished())
885
#ifdef TORRENT_UPNP_LOGGING
886
m_log << time_now_string()
887
<< " <== error while adding portmap: incomplete http message" << std::endl;
893
// We don't want to ignore responses with return codes other than 200
894
// since those might contain valid UPnP error codes
896
error_code_parse_state s;
897
xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
898
, bind(&find_error_code, _1, _2, boost::ref(s)));
900
#ifdef TORRENT_UPNP_LOGGING
901
if (s.error_code != -1)
903
m_log << time_now_string()
904
<< " <== got error message: " << s.error_code << std::endl;
908
if (s.error_code == 725)
910
// only permanent leases supported
911
d.lease_duration = 0;
912
d.mapping[mapping].need_update = true;
913
map_port(d, mapping);
916
else if (s.error_code == 718)
918
// conflict in mapping, try next external port
919
++d.mapping[mapping].external_port;
920
d.mapping[mapping].need_update = true;
921
map_port(d, mapping);
924
else if (s.error_code != -1)
926
int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
927
error_code_t* end = error_codes + num_errors;
928
error_code_t tmp = {s.error_code, 0};
929
error_code_t* e = std::lower_bound(error_codes, end, tmp
930
, bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2));
931
std::string error_string = "UPnP mapping error ";
932
error_string += boost::lexical_cast<std::string>(s.error_code);
933
if (e != end && e->code == s.error_code)
935
error_string += ": ";
936
error_string += e->msg;
938
m_callback(0, 0, error_string);
941
#ifdef TORRENT_UPNP_LOGGING
942
m_log << time_now_string()
943
<< " <== map response: " << std::string(p.get_body().begin, p.get_body().end)
947
if (s.error_code == -1)
953
tcp = d.mapping[mapping].external_port;
955
udp = d.mapping[mapping].external_port;
957
m_callback(tcp, udp, "");
958
if (d.lease_duration > 0)
960
d.mapping[mapping].expires = time_now()
961
+ seconds(int(d.lease_duration * 0.75f));
962
ptime next_expire = m_refresh_timer.expires_at();
963
if (next_expire < time_now()
964
|| next_expire > d.mapping[mapping].expires)
966
m_refresh_timer.expires_at(d.mapping[mapping].expires);
967
m_refresh_timer.async_wait(bind(&upnp::on_expire, self(), _1));
972
d.mapping[mapping].expires = max_time();
976
for (int i = 0; i < num_mappings; ++i)
978
if (d.mapping[i].need_update)
985
catch (std::exception&)
990
void upnp::on_upnp_unmap_response(asio::error_code const& e
991
, libtorrent::http_parser const& p, rootdevice& d, int mapping
992
, http_connection& c) try
994
TORRENT_ASSERT(d.magic == 1337);
995
if (d.upnp_connection && d.upnp_connection.get() == &c)
997
d.upnp_connection->close();
998
d.upnp_connection.reset();
1001
if (e && e != asio::error::eof)
1003
#ifdef TORRENT_UPNP_LOGGING
1004
m_log << time_now_string()
1005
<< " <== error while deleting portmap: " << e.message() << std::endl;
1009
if (!p.header_finished())
1011
#ifdef TORRENT_UPNP_LOGGING
1012
m_log << time_now_string()
1013
<< " <== error while deleting portmap: incomplete http message" << std::endl;
1018
if (p.status_code() != 200)
1020
#ifdef TORRENT_UPNP_LOGGING
1021
m_log << time_now_string()
1022
<< " <== error while deleting portmap: " << p.message() << std::endl;
1028
#ifdef TORRENT_UPNP_LOGGING
1029
m_log << time_now_string()
1030
<< " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
1034
// ignore errors and continue with the next mapping for this device
1035
if (mapping < num_mappings - 1)
1037
unmap_port(d, mapping + 1);
1041
catch (std::exception&)
1046
void upnp::on_expire(asio::error_code const& e) try
1050
ptime now = time_now();
1051
ptime next_expire = max_time();
1053
for (std::set<rootdevice>::iterator i = m_devices.begin()
1054
, end(m_devices.end()); i != end; ++i)
1056
rootdevice& d = const_cast<rootdevice&>(*i);
1057
TORRENT_ASSERT(d.magic == 1337);
1058
for (int m = 0; m < num_mappings; ++m)
1060
if (d.mapping[m].expires != max_time())
1063
if (d.mapping[m].expires < now)
1065
d.mapping[m].expires = max_time();
1068
else if (d.mapping[m].expires < next_expire)
1070
next_expire = d.mapping[m].expires;
1074
if (next_expire != max_time())
1076
m_refresh_timer.expires_at(next_expire);
1077
m_refresh_timer.async_wait(bind(&upnp::on_expire, self(), _1));
1080
catch (std::exception&)
1087
m_refresh_timer.cancel();
1088
m_broadcast_timer.cancel();
1098
for (std::set<rootdevice>::iterator i = m_devices.begin()
1099
, end(m_devices.end()); i != end; ++i)
1101
rootdevice& d = const_cast<rootdevice&>(*i);
1102
TORRENT_ASSERT(d.magic == 1337);
1103
if (d.control_url.empty()) continue;