~ubuntu-branches/ubuntu/maverick/libtorrent-rasterbar/maverick

« back to all changes in this revision

Viewing changes to src/upnp.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Christophe Sauthier
  • Date: 2010-08-10 12:59:37 UTC
  • mfrom: (1.3.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20100810125937-jbcmmf17y8yo9hgz
Tags: 0.15.0-0ubuntu1
* New upstream version.
* debian/patches/100_fix_html_docs.patch: refreshed.
* debian/control: bump up standards-version to 3.9.1 (no changes).

Show diffs side-by-side

added added

removed removed

Lines of Context:
53
53
#include <boost/thread/mutex.hpp>
54
54
#include <cstdlib>
55
55
 
56
 
#if (defined(TORRENT_LOGGING) || defined(TORRENT_VERBOSE_LOGGING)) && !defined(TORRENT_UPNP_LOGGING)
57
 
#define TORRENT_UPNP_LOGGING
58
 
#endif
59
 
 
60
56
using boost::bind;
61
57
using namespace libtorrent;
62
58
 
64
60
 
65
61
upnp::upnp(io_service& ios, connection_queue& cc
66
62
        , address const& listen_interface, std::string const& user_agent
67
 
        , portmap_callback_t const& cb, bool ignore_nonrouters, void* state)
68
 
        :  m_user_agent(user_agent)
 
63
        , portmap_callback_t const& cb, log_callback_t const& lcb
 
64
        , bool ignore_nonrouters, void* state)
 
65
        : m_user_agent(user_agent)
69
66
        , m_callback(cb)
 
67
        , m_log_callback(lcb)
70
68
        , m_retry_count(0)
71
69
        , m_io_service(ios)
72
70
        , m_socket(ios, udp::endpoint(address_v4::from_string("239.255.255.250", ec), 1900)
73
 
                , bind(&upnp::on_reply, self(), _1, _2, _3), false)
 
71
                , bind(&upnp::on_reply, self(), _1, _2, _3))
74
72
        , m_broadcast_timer(ios)
75
73
        , m_refresh_timer(ios)
76
74
        , m_disabled(false)
78
76
        , m_ignore_non_routers(ignore_nonrouters)
79
77
        , m_cc(cc)
80
78
{
81
 
#ifdef TORRENT_UPNP_LOGGING
82
 
        m_log.open("upnp.log", std::ios::in | std::ios::out | std::ios::trunc);
83
 
#endif
84
 
        m_retry_count = 0;
 
79
        TORRENT_ASSERT(cb);
85
80
 
86
81
        if (state)
87
82
        {
111
106
void upnp::discover_device()
112
107
{
113
108
        mutex_t::scoped_lock l(m_mutex);
114
 
 
115
 
        discover_device_impl();
116
 
}
117
 
 
118
 
void upnp::discover_device_impl()
 
109
        if (m_socket.num_send_sockets() == 0)
 
110
                log("No network interfaces to broadcast to", l);
 
111
 
 
112
        discover_device_impl(l);
 
113
}
 
114
 
 
115
void upnp::log(char const* msg, mutex_t::scoped_lock& l)
 
116
{
 
117
        l.unlock();
 
118
        m_log_callback(msg);
 
119
        l.lock();
 
120
}
 
121
 
 
122
void upnp::discover_device_impl(mutex_t::scoped_lock& l)
119
123
{
120
124
        const char msearch[] = 
121
125
                "M-SEARCH * HTTP/1.1\r\n"
134
138
 
135
139
        if (ec)
136
140
        {
137
 
#ifdef TORRENT_UPNP_LOGGING
138
 
                m_log << time_now_string()
139
 
                        << " ==> Broadcast FAILED: " << ec.message() << std::endl
140
 
                        << "aborting" << std::endl;
141
 
#endif
142
 
                disable(ec.message().c_str());
 
141
                char msg[200];
 
142
                snprintf(msg, sizeof(msg), "broadcast failed: %s. Aborting.", ec.message().c_str());
 
143
                log(msg, l);
 
144
                disable(ec, l);
143
145
                return;
144
146
        }
145
147
 
148
150
        m_broadcast_timer.async_wait(bind(&upnp::resend_request
149
151
                , self(), _1));
150
152
 
151
 
#ifdef TORRENT_UPNP_LOGGING
152
 
        m_log << time_now_string()
153
 
                << " ==> Broadcasting search for rootdevice" << std::endl;
154
 
#endif
 
153
        log("broadcasting search for rootdevice", l);
155
154
}
156
155
 
157
156
// returns a reference to a mapping or -1 on failure
159
158
{
160
159
        mutex_t::scoped_lock l(m_mutex);
161
160
 
162
 
#ifdef TORRENT_UPNP_LOGGING
163
 
        m_log << time_now_string()
164
 
                << " *** add mapping [ proto: " << (p == tcp?"tcp":"udp")
165
 
                << " ext_port: " << external_port
166
 
                << " local_port :" << local_port << " ]";
167
 
        if (m_disabled) m_log << " DISABLED";
168
 
        m_log << std::endl;
169
 
#endif
 
161
        char msg[200];
 
162
        snprintf(msg, sizeof(msg), "adding port map: [ protocol: %s ext_port: %u "
 
163
                "local_port: %u ] %s", (p == tcp?"tcp":"udp"), external_port
 
164
                , local_port, m_disabled ? "DISABLED": "");
 
165
        log(msg, l);
170
166
        if (m_disabled) return -1;
171
167
 
172
168
        std::vector<global_mapping_t>::iterator i = std::find_if(
200
196
                m.external_port = external_port;
201
197
                m.local_port = local_port;
202
198
 
203
 
                if (d.service_namespace) update_map(d, mapping_index);
 
199
                if (d.service_namespace) update_map(d, mapping_index, l);
204
200
        }
205
201
 
206
202
        return mapping_index;
214
210
 
215
211
        global_mapping_t& m = m_mappings[mapping];
216
212
 
217
 
#ifdef TORRENT_UPNP_LOGGING
218
 
        m_log << time_now_string()
219
 
                << " *** delete mapping [ proto: " << (m.protocol == tcp?"tcp":"udp")
220
 
                << " ext_port:" << m.external_port
221
 
                << " local_port:" << m.local_port << " ]";
222
 
        m_log << std::endl;
223
 
#endif
 
213
        char msg[200];
 
214
        snprintf(msg, sizeof(msg), "deleting port map: [ protocol: %s ext_port: %u "
 
215
                "local_port: %u ]", (m.protocol == tcp?"tcp":"udp"), m.external_port
 
216
                , m.local_port);
 
217
        log(msg, l);
224
218
 
225
219
        if (m.protocol == none) return;
226
220
        
233
227
                TORRENT_ASSERT(mapping < int(d.mapping.size()));
234
228
                d.mapping[mapping].action = mapping_t::action_delete;
235
229
 
236
 
                if (d.service_namespace) update_map(d, mapping);
 
230
                if (d.service_namespace) update_map(d, mapping, l);
237
231
        }
238
232
}
239
233
 
 
234
bool upnp::get_mapping(int index, int& local_port, int& external_port, int& protocol) const
 
235
{
 
236
        TORRENT_ASSERT(index < int(m_mappings.size()) && index >= 0);
 
237
        if (index >= int(m_mappings.size()) || index < 0) return false;
 
238
        global_mapping_t const& m = m_mappings[index];
 
239
        if (m.protocol == none) return false;
 
240
        local_port = m.local_port;
 
241
        external_port = m.external_port;
 
242
        protocol = m.protocol;
 
243
        return true;
 
244
}
 
245
 
240
246
void upnp::resend_request(error_code const& e)
241
247
{
242
248
        if (e) return;
250
256
        if (m_retry_count < 12
251
257
                && (m_devices.empty() || m_retry_count < 4))
252
258
        {
253
 
                discover_device_impl();
 
259
                discover_device_impl(l);
254
260
                return;
255
261
        }
256
262
 
257
263
        if (m_devices.empty())
258
264
        {
259
 
#ifdef TORRENT_UPNP_LOGGING
260
 
                m_log << time_now_string()
261
 
                        << " *** Got no response in 12 retries. Giving up, "
262
 
                        "disabling UPnP." << std::endl;
263
 
#endif
264
 
                disable("no UPnP router found");
 
265
                disable(errors::no_router, l);
265
266
                return;
266
267
        }
267
268
        
274
275
                        // ask for it
275
276
                        rootdevice& d = const_cast<rootdevice&>(*i);
276
277
                        TORRENT_ASSERT(d.magic == 1337);
 
278
#ifndef BOOST_NO_EXCEPTIONS
277
279
                        try
278
280
                        {
279
 
#ifdef TORRENT_UPNP_LOGGING
280
 
                                m_log << time_now_string()
281
 
                                        << " ==> connecting to " << d.url << std::endl;
282
281
#endif
 
282
                                char msg[200];
 
283
                                snprintf(msg, sizeof(msg), "connecting to: %s", d.url.c_str());
 
284
                                log(msg, l);
283
285
                                if (d.upnp_connection) d.upnp_connection->close();
284
286
                                d.upnp_connection.reset(new http_connection(m_io_service
285
287
                                        , m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
286
288
                                        , boost::ref(d), _5)));
287
289
                                d.upnp_connection->get(d.url, seconds(30), 1);
 
290
#ifndef BOOST_NO_EXCEPTIONS
288
291
                        }
289
292
                        catch (std::exception& e)
290
293
                        {
291
294
                                (void)e;
292
 
#ifdef TORRENT_UPNP_LOGGING
293
 
                                m_log << time_now_string()
294
 
                                        << " *** Connection failed to: " << d.url
295
 
                                        << " " << e.what() << std::endl;
296
 
#endif
 
295
                                char msg[200];
 
296
                                snprintf(msg, sizeof(msg), "connection failed to: %s %s", d.url.c_str(), e.what());
 
297
                                log(msg, l);
297
298
                                d.disabled = true;
298
299
                        }
 
300
#endif
299
301
                }
300
302
        }
301
303
}
338
340
        error_code ec;
339
341
        if (!in_local_network(m_io_service, from.address(), ec))
340
342
        {
341
 
#ifdef TORRENT_UPNP_LOGGING
342
343
                if (ec)
343
344
                {
344
 
                        m_log << time_now_string() << " <== (" << from << ") error: "
345
 
                                << ec.message() << std::endl;
 
345
                        char msg[200];
 
346
                        snprintf(msg, sizeof(msg), "when receiving response from: %s: %s"
 
347
                                , print_endpoint(from).c_str(), ec.message().c_str());
 
348
                        log(msg, l);
346
349
                }
347
350
                else
348
351
                {
349
 
                        m_log << time_now_string() << " <== (" << from << ") UPnP device "
350
 
                                "ignored because it's not on our local network ";
 
352
                        char msg[200];
 
353
                        int num_chars = snprintf(msg, sizeof(msg)
 
354
                                , "ignoring response from: %s. IP is not on local network. "
 
355
                                , print_endpoint(from).c_str());
 
356
                        log(msg, l);
 
357
 
351
358
                        std::vector<ip_interface> net = enum_net_interfaces(m_io_service, ec);
352
359
                        for (std::vector<ip_interface>::const_iterator i = net.begin()
353
360
                                , end(net.end()); i != end; ++i)
354
361
                        {
355
 
                                m_log << "(" << i->interface_address << ", " << i->netmask << ") ";
 
362
                                num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) "
 
363
                                        , print_address(i->interface_address).c_str(), print_address(i->netmask).c_str());
356
364
                        }
357
 
                        m_log << std::endl;
 
365
                        log(msg, l);
358
366
                }
359
 
#endif
360
367
                return;
361
368
        } 
362
369
 
363
370
        if (m_ignore_non_routers)
364
371
        {
365
372
                std::vector<ip_route> routes = enum_routes(m_io_service, ec);
366
 
        
367
373
                if (std::find_if(routes.begin(), routes.end()
368
374
                        , bind(&ip_route::gateway, _1) == from.address()) == routes.end())
369
375
                {
370
376
                        // this upnp device is filtered because it's not in the
371
377
                        // list of configured routers
372
 
#ifdef TORRENT_UPNP_LOGGING
373
378
                        if (ec)
374
379
                        {
375
 
                                m_log << time_now_string() << " <== (" << from << ") error: "
376
 
                                        << ec.message() << std::endl;
 
380
                                char msg[200];
 
381
                                snprintf(msg, sizeof(msg), "when receiving response from: %s: %s"
 
382
                                        , print_endpoint(from).c_str(), ec.message().c_str());
 
383
                                log(msg, l);
377
384
                        }
378
385
                        else
379
386
                        {
380
 
                                m_log << time_now_string() << " <== (" << from << ") UPnP device "
381
 
                                        "ignored because it's not a router on our network ";
 
387
                                char msg[200];
 
388
                                int num_chars = snprintf(msg, sizeof(msg), "ignoring response from: %s: IP is not a router. "
 
389
                                        , print_endpoint(from).c_str());
382
390
                                for (std::vector<ip_route>::const_iterator i = routes.begin()
383
391
                                        , end(routes.end()); i != end; ++i)
384
392
                                {
385
 
                                        m_log << "(" << i->gateway << ", " << i->netmask << ") ";
 
393
                                        num_chars += snprintf(msg + num_chars, sizeof(msg) - num_chars, "(%s,%s) "
 
394
                                                , print_address(i->gateway).c_str(), print_address(i->netmask).c_str());
386
395
                                }
387
 
                                m_log << std::endl;
 
396
                                log(msg, l);
388
397
                        }
389
 
#endif
390
398
                        return;
391
399
                }
392
400
        }
397
405
                , buffer + bytes_transferred), error);
398
406
        if (error)
399
407
        {
400
 
#ifdef TORRENT_UPNP_LOGGING
401
 
                m_log << time_now_string() << " <== (" << from << ") Rootdevice "
402
 
                        "responded with incorrect HTTP packet. Ignoring device" << std::endl;
403
 
#endif
 
408
                char msg[200];
 
409
                snprintf(msg, sizeof(msg), "received malformed HTTP from: %s"
 
410
                        , print_endpoint(from).c_str());
 
411
                log(msg, l);
404
412
                return;
405
413
        }
406
414
 
407
415
        if (p.status_code() != 200 && p.method() != "notify")
408
416
        {
409
 
#ifdef TORRENT_UPNP_LOGGING
410
417
                if (p.method().empty())
411
 
                        m_log << time_now_string()
412
 
                                << " <== (" << from << ") Device responded with HTTP status: " << p.status_code()
413
 
                                << ". Ignoring device" << std::endl;
 
418
                {
 
419
                        char msg[200];
 
420
                        snprintf(msg, sizeof(msg), "HTTP status %u from %s"
 
421
                                , p.status_code(), print_endpoint(from).c_str());
 
422
                        log(msg, l);
 
423
                }
414
424
                else
415
 
                        m_log << time_now_string()
416
 
                                << " <== (" << from << ") Device with HTTP method: " << p.method()
417
 
                                << ". Ignoring device" << std::endl;
418
 
#endif
 
425
                {
 
426
                        char msg[200];
 
427
                        snprintf(msg, sizeof(msg), "HTTP method %s from %s"
 
428
                                , p.method().c_str(), print_endpoint(from).c_str());
 
429
                        log(msg, l);
 
430
                }
419
431
                return;
420
432
        }
421
433
 
422
434
        if (!p.header_finished())
423
435
        {
424
 
#ifdef TORRENT_UPNP_LOGGING
425
 
                m_log << time_now_string()
426
 
                        << " <== (" << from << ") Rootdevice responded with incomplete HTTP "
427
 
                        "packet. Ignoring device" << std::endl;
428
 
#endif
 
436
                char msg[200];
 
437
                snprintf(msg, sizeof(msg), "incomplete HTTP packet from %s"
 
438
                        , print_endpoint(from).c_str());
 
439
                log(msg, l);
429
440
                return;
430
441
        }
431
442
 
432
443
        std::string url = p.header("location");
433
444
        if (url.empty())
434
445
        {
435
 
#ifdef TORRENT_UPNP_LOGGING
436
 
                m_log << time_now_string()
437
 
                        << " <== (" << from << ") Rootdevice response is missing a location header. "
438
 
                        "Ignoring device" << std::endl;
439
 
#endif
 
446
                char msg[200];
 
447
                snprintf(msg, sizeof(msg), "missing location header from %s"
 
448
                        , print_endpoint(from).c_str());
 
449
                log(msg, l);
440
450
                return;
441
451
        }
442
452
 
450
460
 
451
461
                std::string protocol;
452
462
                std::string auth;
453
 
                char const* error;
 
463
                error_code ec;
454
464
                // we don't have this device in our list. Add it
455
 
                boost::tie(protocol, auth, d.hostname, d.port, d.path, error)
456
 
                        = parse_url_components(d.url);
 
465
                boost::tie(protocol, auth, d.hostname, d.port, d.path)
 
466
                        = parse_url_components(d.url, ec);
457
467
 
458
 
                if (error)
 
468
                if (ec)
459
469
                {
460
 
#ifdef TORRENT_UPNP_LOGGING
461
 
                        m_log << time_now_string()
462
 
                                << " <== (" << from << ") Rootdevice advertized an invalid url: '" << d.url
463
 
                                << "'. " << error << ". Ignoring device" << std::endl;
464
 
#endif
 
470
                        char msg[200];
 
471
                        snprintf(msg, sizeof(msg), "invalid URL %s from %s: %s"
 
472
                                , d.url.c_str(), print_endpoint(from).c_str(), ec.message().c_str());
 
473
                        log(msg, l);
465
474
                        return;
466
475
                }
467
476
 
470
479
 
471
480
                if (protocol != "http")
472
481
                {
473
 
#ifdef TORRENT_UPNP_LOGGING
474
 
                        m_log << time_now_string()
475
 
                                << " <== (" << from << ") Rootdevice uses unsupported protocol: '" << protocol
476
 
                                << "'. Ignoring device" << std::endl;
477
 
#endif
 
482
                        char msg[200];
 
483
                        snprintf(msg, sizeof(msg), "unsupported protocol %s from %s"
 
484
                                , protocol.c_str(), print_endpoint(from).c_str());
 
485
                        log(msg, l);
478
486
                        return;
479
487
                }
480
488
 
481
489
                if (d.port == 0)
482
490
                {
483
 
#ifdef TORRENT_UPNP_LOGGING
484
 
                        m_log << time_now_string()
485
 
                                << " <== (" << from << ") Rootdevice responded with a url with port 0. "
486
 
                                "Ignoring device" << std::endl;
487
 
#endif
 
491
                        char msg[200];
 
492
                        snprintf(msg, sizeof(msg), "URL with port 0 from %s"
 
493
                                , print_endpoint(from).c_str());
 
494
                        log(msg, l);
488
495
                        return;
489
496
                }
490
 
#ifdef TORRENT_UPNP_LOGGING
491
 
                m_log << time_now_string()
492
 
                        << " <== (" << from << ") Found rootdevice: " << d.url
493
 
                        << " total: " << m_devices.size() << std::endl;
494
 
#endif
 
497
 
 
498
                char msg[200];
 
499
                snprintf(msg, sizeof(msg), "found rootdevice: %s (%d)"
 
500
                        , d.url.c_str(), int(m_devices.size()));
 
501
                log(msg, l);
495
502
 
496
503
                if (m_devices.size() >= 50)
497
504
                {
498
 
#ifdef TORRENT_UPNP_LOGGING
499
 
                        m_log << time_now_string()
500
 
                                << " <== (" << from << ") Too many devices (" << m_devices.size() << "), "
501
 
                                "ignoring: " << d.url << std::endl;
502
 
#endif
 
505
                        char msg[200];
 
506
                        snprintf(msg, sizeof(msg), "too many rootdevices: (%d). Ignoring %s"
 
507
                                , int(m_devices.size()), d.url.c_str());
 
508
                        log(msg, l);
503
509
                        return;
504
510
                }
505
511
 
517
523
                boost::tie(i, boost::tuples::ignore) = m_devices.insert(d);
518
524
        }
519
525
 
 
526
 
520
527
        if (!m_devices.empty())
521
528
        {
522
529
                for (std::set<rootdevice>::iterator i = m_devices.begin()
532
539
                                try
533
540
                                {
534
541
#endif
535
 
#ifdef TORRENT_UPNP_LOGGING
536
 
                                        m_log << time_now_string()
537
 
                                                << " ==> connecting to " << d.url << std::endl;
538
 
#endif
 
542
                                        char msg[200];
 
543
                                        snprintf(msg, sizeof(msg), "connecting to: %s"
 
544
                                                , d.url.c_str());
 
545
                                        log(msg, l);
 
546
 
539
547
                                        if (d.upnp_connection) d.upnp_connection->close();
540
548
                                        d.upnp_connection.reset(new http_connection(m_io_service
541
549
                                                , m_cc, bind(&upnp::on_upnp_xml, self(), _1, _2
546
554
                                catch (std::exception& e)
547
555
                                {
548
556
                                        (void)e;
549
 
#ifdef TORRENT_UPNP_LOGGING
550
 
                                        m_log << time_now_string()
551
 
                                                << " *** Connection failed to: " << d.url
552
 
                                                << " " << e.what() << std::endl;
553
 
#endif
 
557
 
 
558
                                        char msg[200];
 
559
                                        snprintf(msg, sizeof(msg), "connection failed to: %s %s"
 
560
                                                , d.url.c_str(), e.what());
 
561
                                        log(msg, l);
554
562
                                        d.disabled = true;
555
563
                                }
556
564
#endif
559
567
        }
560
568
}
561
569
 
562
 
void upnp::post(upnp::rootdevice const& d, std::string const& soap
563
 
        , std::string const& soap_action)
 
570
void upnp::post(upnp::rootdevice const& d, char const* soap
 
571
        , char const* soap_action, mutex_t::scoped_lock& l)
564
572
{
565
573
        TORRENT_ASSERT(d.magic == 1337);
566
574
        TORRENT_ASSERT(d.upnp_connection);
567
575
 
568
 
        std::stringstream header;
569
 
        
570
 
        header << "POST " << d.path << " HTTP/1.0\r\n"
571
 
                "Host: " << d.hostname << ":" << d.port << "\r\n"
 
576
        char header[2048];
 
577
        snprintf(header, sizeof(header), "POST %s HTTP/1.0\r\n"
 
578
                "Host: %s:%u\r\n"
572
579
                "Content-Type: text/xml; charset=\"utf-8\"\r\n"
573
 
                "Content-Length: " << soap.size() << "\r\n"
574
 
                "Soapaction: \"" << d.service_namespace << "#" << soap_action << "\"\r\n\r\n" << soap;
575
 
 
576
 
        d.upnp_connection->sendbuffer = header.str();
577
 
 
578
 
#ifdef TORRENT_UPNP_LOGGING
579
 
        m_log << time_now_string()
580
 
                << " ==> sending: " << header.str() << std::endl;
581
 
#endif
582
 
        
 
580
                "Content-Length: %d\r\n"
 
581
                "Soapaction: \"%s#%s\"\r\n\r\n"
 
582
                "%s"
 
583
                , d.path.c_str(), d.hostname.c_str(), d.port
 
584
                , int(strlen(soap)), d.service_namespace, soap_action
 
585
                , soap);
 
586
 
 
587
        d.upnp_connection->sendbuffer = header;
 
588
 
 
589
        char msg[1024];
 
590
        snprintf(msg, sizeof(msg), "sending: %s", header);
 
591
        log(msg, l);
583
592
}
584
593
 
585
594
void upnp::create_port_mapping(http_connection& c, rootdevice& d, int i)
591
600
        if (!d.upnp_connection)
592
601
        {
593
602
                TORRENT_ASSERT(d.disabled);
594
 
#ifdef TORRENT_UPNP_LOGGING
595
 
                m_log << time_now_string() << " *** mapping (" << i
596
 
                        << ") aborted" << std::endl;
597
 
#endif
 
603
                char msg[200];
 
604
                snprintf(msg, sizeof(msg), "mapping %u aborted", i);
 
605
                log(msg, l);
598
606
                return;
599
607
        }
600
608
        
601
 
        std::string soap_action = "AddPortMapping";
602
 
 
603
 
        std::stringstream soap;
604
 
        
605
 
        soap << "<?xml version=\"1.0\"?>\n"
 
609
        char const* soap_action = "AddPortMapping";
 
610
 
 
611
        std::string local_endpoint = print_address(c.socket().local_endpoint(ec).address());
 
612
 
 
613
        char soap[2048];
 
614
        error_code ec;
 
615
        snprintf(soap, sizeof(soap), "<?xml version=\"1.0\"?>\n"
606
616
                "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
607
617
                "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
608
 
                "<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
609
 
 
610
 
        error_code ec;
611
 
        soap << "<NewRemoteHost></NewRemoteHost>"
612
 
                "<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
613
 
                "<NewProtocol>" << (d.mapping[i].protocol == udp ? "UDP" : "TCP") << "</NewProtocol>"
614
 
                "<NewInternalPort>" << d.mapping[i].local_port << "</NewInternalPort>"
615
 
                "<NewInternalClient>" << c.socket().local_endpoint(ec).address() << "</NewInternalClient>"
 
618
                "<s:Body><u:%s xmlns:u=\"%s\">"
 
619
                "<NewRemoteHost></NewRemoteHost>"
 
620
                "<NewExternalPort>%u</NewExternalPort>"
 
621
                "<NewProtocol>%s</NewProtocol>"
 
622
                "<NewInternalPort>%u</NewInternalPort>"
 
623
                "<NewInternalClient>%s</NewInternalClient>"
616
624
                "<NewEnabled>1</NewEnabled>"
617
 
                "<NewPortMappingDescription>" << m_user_agent << " at " <<
618
 
                        c.socket().local_endpoint(ec).address() << ":" << to_string(d.mapping[i].local_port).elems
619
 
                        << "</NewPortMappingDescription>"
620
 
                "<NewLeaseDuration>" << d.lease_duration << "</NewLeaseDuration>";
621
 
        soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
 
625
                "<NewPortMappingDescription>%s at %s:%d</NewPortMappingDescription>"
 
626
                "<NewLeaseDuration>%u</NewLeaseDuration>"
 
627
                "</u:%s></s:Body></s:Envelope>"
 
628
                , soap_action, d.service_namespace, d.mapping[i].external_port
 
629
                , (d.mapping[i].protocol == udp ? "UDP" : "TCP")
 
630
                , d.mapping[i].local_port
 
631
                , local_endpoint.c_str()
 
632
                , m_user_agent.c_str(), local_endpoint.c_str(), d.mapping[i].local_port
 
633
                , d.lease_duration, soap_action);
622
634
 
623
 
        post(d, soap.str(), soap_action);
 
635
        post(d, soap, soap_action, l);
624
636
}
625
637
 
626
 
void upnp::next(rootdevice& d, int i)
 
638
void upnp::next(rootdevice& d, int i, mutex_t::scoped_lock& l)
627
639
{
628
640
        if (i < num_mappings() - 1)
629
641
        {
630
 
                update_map(d, i + 1);
 
642
                update_map(d, i + 1, l);
631
643
        }
632
644
        else
633
645
        {
636
648
                        , boost::bind(&mapping_t::action, _1) != int(mapping_t::action_none));
637
649
                if (i == d.mapping.end()) return;
638
650
 
639
 
                update_map(d, i - d.mapping.begin());
 
651
                update_map(d, i - d.mapping.begin(), l);
640
652
        }
641
653
}
642
654
 
643
 
void upnp::update_map(rootdevice& d, int i)
 
655
void upnp::update_map(rootdevice& d, int i, mutex_t::scoped_lock& l)
644
656
{
645
657
        TORRENT_ASSERT(d.magic == 1337);
646
658
        TORRENT_ASSERT(i < int(d.mapping.size()));
655
667
        if (m.action == mapping_t::action_none
656
668
                || m.protocol == none)
657
669
        {
658
 
#ifdef TORRENT_UPNP_LOGGING
659
 
                if (m.protocol != none)
660
 
                        m_log << time_now_string() << " *** mapping (" << i
661
 
                                << ") does not need update, skipping" << std::endl;
662
 
#endif
 
670
                char msg[200];
 
671
                snprintf(msg, sizeof(msg), "mapping %u does not need updating, skipping", i);
 
672
                log(msg, l);
663
673
                m.action = mapping_t::action_none;
664
 
                next(d, i);
 
674
                next(d, i, l);
665
675
                return;
666
676
        }
667
677
 
668
678
        TORRENT_ASSERT(!d.upnp_connection);
669
679
        TORRENT_ASSERT(d.service_namespace);
670
680
 
671
 
#ifdef TORRENT_UPNP_LOGGING
672
 
                m_log << time_now_string()
673
 
                        << " ==> connecting to " << d.hostname << std::endl;
674
 
#endif
 
681
        char msg[200];
 
682
        snprintf(msg, sizeof(msg), "connecting to %s", d.hostname.c_str());
 
683
        log(msg, l);
675
684
        if (m.action == mapping_t::action_add)
676
685
        {
677
686
                if (m.failcount > 5)
678
687
                {
679
688
                        m.action = mapping_t::action_none;
680
689
                        // giving up
681
 
                        next(d, i);
 
690
                        next(d, i, l);
682
691
                        return;
683
692
                }
684
693
 
714
723
        if (!d.upnp_connection)
715
724
        {
716
725
                TORRENT_ASSERT(d.disabled);
717
 
#ifdef TORRENT_UPNP_LOGGING
718
 
                m_log << time_now_string() << " *** unmapping (" << i
719
 
                        << ") aborted" << std::endl;
720
 
#endif
 
726
                char msg[200];
 
727
                snprintf(msg, sizeof(msg), "unmapping %u aborted", i);
 
728
                log(msg, l);
721
729
                return;
722
730
        }
723
731
 
724
 
        std::stringstream soap;
725
 
        
726
 
        std::string soap_action = "DeletePortMapping";
 
732
        char const* soap_action = "DeletePortMapping";
727
733
 
728
 
        soap << "<?xml version=\"1.0\"?>\n"
 
734
        char soap[2048];
 
735
        error_code ec;
 
736
        snprintf(soap, sizeof(soap), "<?xml version=\"1.0\"?>\n"
729
737
                "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
730
738
                "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
731
 
                "<s:Body><u:" << soap_action << " xmlns:u=\"" << d.service_namespace << "\">";
732
 
 
733
 
        soap << "<NewRemoteHost></NewRemoteHost>"
734
 
                "<NewExternalPort>" << d.mapping[i].external_port << "</NewExternalPort>"
735
 
                "<NewProtocol>" << (d.mapping[i].protocol == udp ? "UDP" : "TCP") << "</NewProtocol>";
736
 
        soap << "</u:" << soap_action << "></s:Body></s:Envelope>";
 
739
                "<s:Body><u:%s xmlns:u=\"%s\">"
 
740
                "<NewExternalPort>%u</NewExternalPort>"
 
741
                "<NewProtocol>%s</NewProtocol>"
 
742
                "</u:%s></s:Body></s:Envelope>"
 
743
                , soap_action, d.service_namespace
 
744
                , d.mapping[i].external_port
 
745
                , (d.mapping[i].protocol == udp ? "UDP" : "TCP")
 
746
                , soap_action);
737
747
        
738
 
        post(d, soap.str(), soap_action);
 
748
        post(d, soap, soap_action, l);
739
749
}
740
750
 
741
751
namespace
742
752
{
743
 
        char tolower(char c)
744
 
        {
745
 
                if (c >= 'A' && c <= 'Z') return c + ('a' - 'A');
746
 
                return c;
747
 
        }
748
 
 
749
753
        void copy_tolower(std::string& dst, char const* src)
750
754
        {
751
755
                dst.clear();
752
 
                while (*src) dst.push_back(tolower(*src++));
753
 
        }
754
 
        
755
 
        bool string_equal_nocase(char const* lhs, char const* rhs)
756
 
        {
757
 
                while (tolower(*lhs) == tolower(*rhs))
758
 
                {
759
 
                        if (*lhs == 0) return true;
760
 
                        ++lhs;
761
 
                        ++rhs;
762
 
                }
763
 
                return false;
 
756
                while (*src) dst.push_back(to_lower(*src++));
764
757
        }
765
758
}
766
759
 
786
779
        {
787
780
                std::list<std::string>::reverse_iterator i = tag_stack.rbegin();
788
781
                if (i == tag_stack.rend()) return false;
789
 
                if (!string_equal_nocase(i->c_str(), str2)) return false;
 
782
                if (!string_equal_no_case(i->c_str(), str2)) return false;
790
783
                ++i;
791
784
                if (i == tag_stack.rend()) return false;
792
 
                if (!string_equal_nocase(i->c_str(), str1)) return false;
 
785
                if (!string_equal_no_case(i->c_str(), str1)) return false;
793
786
                return true;
794
787
        }
795
788
};
796
789
 
797
 
void find_control_url(int type, char const* string, parse_state& state)
 
790
TORRENT_EXPORT void find_control_url(int type, char const* string, parse_state& state)
798
791
{
799
792
        if (type == xml_start_tag)
800
793
        {
819
812
//              std::cout << " " << string << std::endl;
820
813
                if (!state.in_service && state.top_tags("service", "servicetype"))
821
814
                {
822
 
                        if (string_equal_nocase(string, state.service_type))
 
815
                        if (string_equal_no_case(string, state.service_type))
823
816
                                state.in_service = true;
824
817
                }
825
818
                else if (state.in_service && state.top_tags("service", "controlurl"))
854
847
 
855
848
        if (e && e != asio::error::eof)
856
849
        {
857
 
#ifdef TORRENT_UPNP_LOGGING
858
 
                m_log << time_now_string()
859
 
                        << " <== (" << d.url << ") error while fetching control url: "
860
 
                        << e.message() << std::endl;
861
 
#endif
 
850
                char msg[200];
 
851
                snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s"
 
852
                        , d.url.c_str(), e.message().c_str());
 
853
                log(msg, l);
862
854
                d.disabled = true;
863
855
                return;
864
856
        }
865
857
 
866
858
        if (!p.header_finished())
867
859
        {
868
 
#ifdef TORRENT_UPNP_LOGGING
869
 
                m_log << time_now_string()
870
 
                        << " <== (" << d.url << ") error while fetching control url: incomplete http message" << std::endl;
871
 
#endif
 
860
                char msg[200];
 
861
                snprintf(msg, sizeof(msg), "error while fetching control url from: %s: incomplete HTTP message"
 
862
                        , d.url.c_str());
 
863
                log(msg, l);
872
864
                d.disabled = true;
873
865
                return;
874
866
        }
875
867
 
876
868
        if (p.status_code() != 200)
877
869
        {
878
 
#ifdef TORRENT_UPNP_LOGGING
879
 
                m_log << time_now_string()
880
 
                        << " <== (" << d.url << ") error while fetching control url: " << p.message() << std::endl;
881
 
#endif
 
870
                char msg[200];
 
871
                snprintf(msg, sizeof(msg), "error while fetching control url from: %s: %s"
 
872
                        , d.url.c_str(), p.message().c_str());
 
873
                log(msg, l);
882
874
                d.disabled = true;
883
875
                return;
884
876
        }
906
898
                }
907
899
                else
908
900
                {
909
 
#ifdef TORRENT_UPNP_LOGGING
910
 
                        m_log << time_now_string()
911
 
                                << " <== (" << d.url << ") Rootdevice response, did not find "
912
 
                                "a port mapping interface" << std::endl;
913
 
#endif
 
901
                        char msg[200];
 
902
                        snprintf(msg, sizeof(msg), "could not find a port mapping interface in response from: %s"
 
903
                                , d.url.c_str());
 
904
                        log(msg, l);
914
905
                        d.disabled = true;
915
906
                        return;
916
907
                }
929
920
 
930
921
        std::string protocol;
931
922
        std::string auth;
932
 
        char const* error;
 
923
        error_code ec;
933
924
        if (!d.control_url.empty() && d.control_url[0] == '/')
934
925
        {
935
 
                boost::tie(protocol, auth, d.hostname, d.port, d.path, error)
936
 
                        = parse_url_components(d.url);
 
926
                boost::tie(protocol, auth, d.hostname, d.port, d.path)
 
927
                        = parse_url_components(d.url, ec);
937
928
                d.control_url = protocol + "://" + d.hostname + ":"
938
929
                        + to_string(d.port).elems + s.control_url;
939
930
        }
940
931
 
941
 
#ifdef TORRENT_UPNP_LOGGING
942
 
        m_log << time_now_string()
943
 
                << " <== (" << d.url << ") Rootdevice response, found control URL: " << d.control_url
944
 
                << " urlbase: " << s.url_base << " namespace: " << d.service_namespace << std::endl;
945
 
#endif
946
 
 
947
 
        boost::tie(protocol, auth, d.hostname, d.port, d.path, error)
948
 
                = parse_url_components(d.control_url);
949
 
 
950
 
        if (error)
 
932
        char msg[200];
 
933
        snprintf(msg, sizeof(msg), "found control URL: %s namespace %s "
 
934
                "urlbase: %s in response from %s"
 
935
                , d.control_url.c_str(), d.service_namespace
 
936
                , s.url_base.c_str(), d.url.c_str());
 
937
        log(msg, l);
 
938
 
 
939
        boost::tie(protocol, auth, d.hostname, d.port, d.path)
 
940
                = parse_url_components(d.control_url, ec);
 
941
 
 
942
        if (ec)
951
943
        {
952
 
#ifdef TORRENT_UPNP_LOGGING
953
 
                m_log << time_now_string()
954
 
                        << " *** Failed to parse URL '" << d.control_url << "': " << error << std::endl;
955
 
#endif
 
944
                char msg[200];
 
945
                snprintf(msg, sizeof(msg), "failed to parse URL '%s': %s"
 
946
                        , d.control_url.c_str(), ec.message().c_str());
 
947
                log(msg, l);
956
948
                d.disabled = true;
957
949
                return;
958
950
        }
959
951
 
960
 
        if (num_mappings() > 0) update_map(d, 0);
 
952
        if (num_mappings() > 0) update_map(d, 0, l);
961
953
}
962
954
 
963
 
void upnp::disable(char const* msg)
 
955
void upnp::disable(error_code const& ec, mutex_t::scoped_lock& l)
964
956
{
965
957
        m_disabled = true;
966
958
 
970
962
        {
971
963
                if (i->protocol == none) continue;
972
964
                i->protocol = none;
973
 
                m_callback(i - m_mappings.begin(), 0, msg);
 
965
                l.unlock();
 
966
                m_callback(i - m_mappings.begin(), 0, ec);
 
967
                l.lock();
974
968
        }
975
969
        
976
 
//      m_devices.clear();
977
 
        error_code ec;
978
 
        m_broadcast_timer.cancel(ec);
979
 
        m_refresh_timer.cancel(ec);
 
970
        // we cannot clear the devices since there
 
971
        // might be outstanding requests relying on
 
972
        // the device entry being present when they
 
973
        // complete
 
974
        error_code e;
 
975
        m_broadcast_timer.cancel(e);
 
976
        m_refresh_timer.cancel(e);
980
977
        m_socket.close();
981
978
}
982
979
 
1003
1000
                        state.exit = true;
1004
1001
                }
1005
1002
        }
1006
 
}
1007
1003
 
1008
 
namespace
1009
 
{
1010
1004
        struct error_code_t
1011
1005
        {
1012
1006
                int code;
1015
1009
        
1016
1010
        error_code_t error_codes[] =
1017
1011
        {
1018
 
                {402, "Invalid Arguments"}
 
1012
                {0, "no error"}
 
1013
                , {402, "Invalid Arguments"}
1019
1014
                , {501, "Action Failed"}
1020
1015
                , {714, "The specified value does not exist in the array"}
1021
1016
                , {715, "The source IP address cannot be wild-carded"}
1032
1027
 
1033
1028
}
1034
1029
 
 
1030
#if BOOST_VERSION >= 103500
 
1031
 
 
1032
 
 
1033
const char* upnp_error_category::name() const
 
1034
{
 
1035
        return "UPnP error";
 
1036
}
 
1037
 
 
1038
std::string upnp_error_category::message(int ev) const
 
1039
{
 
1040
        int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
 
1041
        error_code_t* end = error_codes + num_errors;
 
1042
        error_code_t tmp = {ev, 0};
 
1043
        error_code_t* e = std::lower_bound(error_codes, end, tmp
 
1044
                , bind(&error_code_t::code, _1) < bind(&error_code_t::code, _2));
 
1045
        if (e != end && e->code == ev)
 
1046
        {
 
1047
                return e->msg;
 
1048
        }
 
1049
        return "unknown UPnP error";
 
1050
}
 
1051
 
 
1052
namespace libtorrent
 
1053
{
 
1054
        TORRENT_EXPORT upnp_error_category upnp_category;
 
1055
}
 
1056
 
 
1057
#else
 
1058
 
 
1059
namespace libtorrent
 
1060
{
 
1061
        TORRENT_EXPORT ::asio::error::error_category upnp_category(21);
 
1062
}
 
1063
 
 
1064
#endif
 
1065
 
1035
1066
void upnp::on_upnp_map_response(error_code const& e
1036
1067
        , libtorrent::http_parser const& p, rootdevice& d, int mapping
1037
1068
        , http_connection& c)
1049
1080
 
1050
1081
        if (e && e != asio::error::eof)
1051
1082
        {
1052
 
#ifdef TORRENT_UPNP_LOGGING
1053
 
                m_log << time_now_string()
1054
 
                        << " <== error while adding portmap: " << e.message() << std::endl;
1055
 
#endif
 
1083
                char msg[200];
 
1084
                snprintf(msg, sizeof(msg), "error while adding port map: %s"
 
1085
                        , e.message().c_str());
 
1086
                log(msg, l);
1056
1087
                d.disabled = true;
1057
1088
                return;
1058
1089
        }
1078
1109
 
1079
1110
        if (!p.header_finished())
1080
1111
        {
1081
 
#ifdef TORRENT_UPNP_LOGGING
1082
 
                m_log << time_now_string()
1083
 
                        << " <== error while adding portmap: incomplete http message" << std::endl;
1084
 
#endif
1085
 
                next(d, mapping);
 
1112
                log("error while adding port map: incomplete http message", l);
 
1113
                next(d, mapping, l);
1086
1114
                return;
1087
1115
        }
1088
1116
 
1093
1121
        xml_parse((char*)p.get_body().begin, (char*)p.get_body().end
1094
1122
                , bind(&find_error_code, _1, _2, boost::ref(s)));
1095
1123
 
1096
 
#ifdef TORRENT_UPNP_LOGGING
1097
1124
        if (s.error_code != -1)
1098
1125
        {
1099
 
                m_log << time_now_string()
1100
 
                        << " <== got error message: " << s.error_code << std::endl;
 
1126
                char msg[200];
 
1127
                snprintf(msg, sizeof(msg), "error while adding port map, code: %u"
 
1128
                        , s.error_code);
 
1129
                log(msg, l);
1101
1130
        }
1102
 
#endif
1103
1131
        
1104
1132
        mapping_t& m = d.mapping[mapping];
1105
1133
 
1109
1137
                d.lease_duration = 0;
1110
1138
                m.action = mapping_t::action_add;
1111
1139
                ++m.failcount;
1112
 
                update_map(d, mapping);
 
1140
                update_map(d, mapping, l);
1113
1141
                return;
1114
1142
        }
1115
1143
        else if (s.error_code == 718 || s.error_code == 727)
1121
1149
                        m.external_port = 0;
1122
1150
                        m.action = mapping_t::action_add;
1123
1151
                        ++m.failcount;
1124
 
                        update_map(d, mapping);
 
1152
                        update_map(d, mapping, l);
1125
1153
                        return;
1126
1154
                }
1127
 
                return_error(mapping, s.error_code);
 
1155
                return_error(mapping, s.error_code, l);
1128
1156
        }
1129
1157
        else if (s.error_code == 716)
1130
1158
        {
1133
1161
                m.external_port = 40000 + (std::rand() % 10000);
1134
1162
                m.action = mapping_t::action_add;
1135
1163
                ++m.failcount;
1136
 
                update_map(d, mapping);
 
1164
                update_map(d, mapping, l);
1137
1165
                return;
1138
1166
        }
1139
1167
        else if (s.error_code != -1)
1140
1168
        {
1141
 
                return_error(mapping, s.error_code);
 
1169
                return_error(mapping, s.error_code, l);
1142
1170
        }
1143
1171
 
1144
 
#ifdef TORRENT_UPNP_LOGGING
1145
 
        m_log << time_now_string()
1146
 
                << " <== map response: " << std::string(p.get_body().begin, p.get_body().end)
1147
 
                << std::endl;
1148
 
#endif
 
1172
        char msg[500];
 
1173
        snprintf(msg, sizeof(msg), "map response: %s"
 
1174
                , std::string(p.get_body().begin, p.get_body().end).c_str());
 
1175
        log(msg, l);
1149
1176
 
1150
1177
        if (s.error_code == -1)
1151
1178
        {
1152
 
                m_callback(mapping, m.external_port, "");
 
1179
                l.unlock();
 
1180
                m_callback(mapping, m.external_port, error_code());
 
1181
                l.lock();
1153
1182
                if (d.lease_duration > 0)
1154
1183
                {
1155
1184
                        m.expires = time_now()
1170
1199
                m.failcount = 0;
1171
1200
        }
1172
1201
 
1173
 
        next(d, mapping);
 
1202
        next(d, mapping, l);
1174
1203
}
1175
1204
 
1176
 
void upnp::return_error(int mapping, int code)
 
1205
void upnp::return_error(int mapping, int code, mutex_t::scoped_lock& l)
1177
1206
{
1178
1207
        int num_errors = sizeof(error_codes) / sizeof(error_codes[0]);
1179
1208
        error_code_t* end = error_codes + num_errors;
1187
1216
                error_string += ": ";
1188
1217
                error_string += e->msg;
1189
1218
        }
1190
 
        m_callback(mapping, 0, error_string);
 
1219
        l.unlock();
 
1220
        m_callback(mapping, 0, error_code(code, upnp_category));
 
1221
        l.lock();
1191
1222
}
1192
1223
 
1193
1224
void upnp::on_upnp_unmap_response(error_code const& e
1207
1238
 
1208
1239
        if (e && e != asio::error::eof)
1209
1240
        {
1210
 
#ifdef TORRENT_UPNP_LOGGING
1211
 
                m_log << time_now_string()
1212
 
                        << " <== error while deleting portmap: " << e.message() << std::endl;
1213
 
#endif
1214
 
        } else if (!p.header_finished())
 
1241
                char msg[200];
 
1242
                snprintf(msg, sizeof(msg), "error while deleting portmap: %s", e.message().c_str());
 
1243
                log(msg, l);
 
1244
        }
 
1245
        else if (!p.header_finished())
1215
1246
        {
1216
 
#ifdef TORRENT_UPNP_LOGGING
1217
 
                m_log << time_now_string()
1218
 
                        << " <== error while deleting portmap: incomplete http message" << std::endl;
1219
 
#endif
 
1247
                log("error while deleting portmap: incomplete http message", l);
1220
1248
        }
1221
1249
        else if (p.status_code() != 200)
1222
1250
        {
1223
 
#ifdef TORRENT_UPNP_LOGGING
1224
 
                m_log << time_now_string()
1225
 
                        << " <== error while deleting portmap: " << p.message() << std::endl;
1226
 
#endif
 
1251
                char msg[200];
 
1252
                snprintf(msg, sizeof(msg), "error while deleting portmap: %s", p.message().c_str());
 
1253
                log(msg, l);
1227
1254
        }
1228
1255
        else
1229
1256
        {
1230
 
 
1231
 
#ifdef TORRENT_UPNP_LOGGING
1232
 
                m_log << time_now_string()
1233
 
                        << " <== unmap response: " << std::string(p.get_body().begin, p.get_body().end)
1234
 
                        << std::endl;
1235
 
#endif
 
1257
                char msg[200];
 
1258
                snprintf(msg, sizeof(msg), "unmap response: %s"
 
1259
                        , std::string(p.get_body().begin, p.get_body().end).c_str());
 
1260
                log(msg, l);
1236
1261
        }
1237
1262
 
1238
1263
        d.mapping[mapping].protocol = none;
1239
1264
 
1240
 
        next(d, mapping);
 
1265
        next(d, mapping, l);
1241
1266
}
1242
1267
 
1243
1268
void upnp::on_expire(error_code const& e)
1262
1287
                        if (d.mapping[m].expires < now)
1263
1288
                        {
1264
1289
                                d.mapping[m].expires = max_time();
1265
 
                                update_map(d, m);
 
1290
                                update_map(d, m, l);
1266
1291
                        }
1267
1292
                        else if (d.mapping[m].expires < next_expire)
1268
1293
                        {
1306
1331
                        j->action = mapping_t::action_delete;
1307
1332
                        m_mappings[j - d.mapping.begin()].protocol = none;
1308
1333
                }
1309
 
                if (num_mappings() > 0) update_map(d, 0);
 
1334
                if (num_mappings() > 0) update_map(d, 0, l);
1310
1335
        }
1311
1336
}
1312
1337