diff --git a/Makefile b/Makefile index b5739c46..d9d9b4c9 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,8 @@ CFLAGS = -g -Wall -std=c++0x OBJECTS = obj/i2p.o obj/base64.o obj/NTCPSession.o obj/RouterInfo.o obj/Transports.o \ obj/RouterContext.o obj/NetDb.o obj/LeaseSet.o obj/Tunnel.o obj/TunnelEndpoint.o \ obj/TunnelGateway.o obj/TransitTunnel.o obj/I2NPProtocol.o obj/Log.o obj/Garlic.o \ - obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o + obj/HTTPServer.o obj/Streaming.o obj/Identity.o obj/SSU.o obj/util.o obj/Reseed.o \ + obj/UPnP.o INCFLAGS = LDFLAGS = -Wl,-rpath,/usr/local/lib -lcryptopp -lboost_system -lboost_filesystem -lboost_regex -lboost_program_options -lpthread LIBS = diff --git a/UPnP.cpp b/UPnP.cpp new file mode 100644 index 00000000..ebadc80f --- /dev/null +++ b/UPnP.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include "Log.h" +#include "UPnP.h" + +namespace i2p +{ + UPnP::UPnP (): m_Timer (m_Service), + m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT), + m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT), + m_Socket (m_Service, m_Endpoint.protocol ()) + { + m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535)); + m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535)); + m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true)); + } + + UPnP::~UPnP () + { + } + + void UPnP::Run () + { + DiscoverRouter (); + m_Service.run (); + } + + void UPnP::DiscoverRouter () + { + m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds + m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error)); + + std::string address = UPNP_GROUP; + address += ":" + boost::lexical_cast(UPNP_PORT); + std::string request = "M-SEARCH * HTTP/1.1\r\n" + "HOST: " + address + "\r\n" + "ST:" + UPNP_ROUTER + "\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n" + "\r\n\r\n"; + m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint); + Receive (); + } + + void UPnP::Receive () + { + m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint, + boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)); + } + + void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred) + { + LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ()); + std::string str (m_ReceiveBuffer, bytes_transferred); + LogPrint (str); + m_Timer.cancel (); + } + + void UPnP::HandleTimer (const boost::system::error_code& ecode) + { + if (ecode != boost::asio::error::operation_aborted) + { + LogPrint ("UPnP: timeout expired"); + m_Service.stop (); + } + } +} diff --git a/UPnP.h b/UPnP.h new file mode 100644 index 00000000..3b5122d1 --- /dev/null +++ b/UPnP.h @@ -0,0 +1,41 @@ +#ifndef UPNP_H__ +#define UPNP_H__ + +#include + +namespace i2p +{ + const int UPNP_MAX_PACKET_LEN = 1500; + const char UPNP_GROUP[] = "239.255.255.250"; + const int UPNP_PORT = 1900; + const int UPNP_REPLY_PORT = 1901; + const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1"; + + class UPnP + { + public: + + UPnP (); + ~UPnP (); + + void Run (); + + + private: + + void DiscoverRouter (); + void Receive (); + void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred); + void HandleTimer (const boost::system::error_code& ecode); + + private: + + boost::asio::io_service m_Service; + boost::asio::deadline_timer m_Timer; + boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint; + boost::asio::ip::udp::socket m_Socket; + char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN]; + }; +} + +#endif