├── .gitignore ├── .gitmodules ├── Jamroot.jam ├── LICENSE ├── README.md ├── examples ├── Jamfile └── client_test.cpp ├── include ├── dht_session.hpp ├── scout.hpp ├── udp_socket.hpp └── utils.hpp ├── libminiupnpc ├── .gitignore ├── CMakeLists.txt ├── Changelog.txt ├── Jamfile ├── LICENSE ├── Makefile ├── Makefile.mingw ├── README ├── VERSION ├── bsdqueue.h ├── codelength.h ├── connecthostport.c ├── connecthostport.h ├── declspec.h ├── igd_desc_parse.c ├── igd_desc_parse.h ├── minihttptestserver.c ├── minisoap.c ├── minisoap.h ├── minissdpc.c ├── minissdpc.h ├── miniupnpc.c ├── miniupnpc.def ├── miniupnpc.h ├── miniupnpcstrings.h ├── miniupnpctypes.h ├── miniwget.c ├── miniwget.h ├── minixml.c ├── minixml.h ├── minixmlvalid.c ├── portlistingparse.c ├── portlistingparse.h ├── receivedata.c ├── receivedata.h ├── upnpcommands.c ├── upnpcommands.h ├── upnperrors.c ├── upnperrors.h ├── upnpreplyparse.c └── upnpreplyparse.h ├── libnatpmp ├── .gitignore ├── Changelog.txt ├── Jamfile ├── LICENSE ├── Makefile ├── README ├── declspec.h ├── getgateway.c ├── getgateway.h ├── libnatpmpmodule.c ├── natpmp.c ├── natpmp.h ├── wingettimeofday.c └── wingettimeofday.h ├── src ├── LoadLibraryList.cpp ├── LoadLibraryList.h ├── dht_session.cpp ├── file.cpp ├── file.hpp ├── scout.cpp ├── sockaddr.cpp ├── sockaddr.hpp ├── upnp-portmap.cpp ├── upnp-portmap.h └── utils.cpp └── test ├── Jamfile ├── fake_dht.h ├── test_scout_api.cpp └── test_serialization.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | 3 | # Visual studio files 4 | *.sln 5 | *.opensdf 6 | *.sdf 7 | *.suo 8 | *.vcxproj 9 | *.filters 10 | *.user 11 | scout.VC.* -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "GSL"] 2 | path = GSL 3 | url = https://github.com/Microsoft/GSL.git 4 | [submodule "btdht"] 5 | path = btdht 6 | url = https://github.com/bittorrent/libbtdht.git 7 | -------------------------------------------------------------------------------- /Jamroot.jam: -------------------------------------------------------------------------------- 1 | import feature : feature ; 2 | import modules ; 3 | import os ; 4 | import path ; 5 | 6 | BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; 7 | SODIUM_ROOT = [ modules.peek : SODIUM_ROOT ] ; 8 | 9 | if (BOOST_ROOT) 10 | { 11 | ECHO "building boost from source directory: " $(BOOST_ROOT) ; 12 | use-project /boost : $(BOOST_ROOT) ; 13 | alias boost_system : /boost/system//boost_system : : : $(BOOST_ROOT) ; 14 | } 15 | else 16 | { 17 | lib boost_system : : boost_system : : ; 18 | } 19 | 20 | if (SODIUM_ROOT) 21 | { 22 | ECHO "building libsodium from source directory: " $(SODIUM_ROOT) ; 23 | 24 | LIBSODIUM_SRC = [ path.glob-tree $(SODIUM_ROOT)/src/libsodium : *.c 25 | : .git try.c ] ; 26 | 27 | lib sodium 28 | : # sources 29 | $(LIBSODIUM_SRC) 30 | : # requirements 31 | $(SODIUM_ROOT)/src/libsodium/include 32 | $(SODIUM_ROOT)/src/libsodium/include/sodium 33 | static 34 | 35 | # TODO: make this conditional on architecture 36 | NATIVE_LITTLE_ENDIAN 37 | SODIUM_STATIC 38 | msvc:inline= 39 | 40 | : # default build 41 | 42 | : # usage requirements 43 | $(SODIUM_ROOT)/src/libsodium/include 44 | SODIUM_STATIC 45 | SODIUM_EXPORT= 46 | ; 47 | } 48 | else 49 | { 50 | lib sodium : : sodium : : ; 51 | } 52 | 53 | ECHO "OS =" [ os.name ] ; 54 | 55 | use-project /btdht : btdht ; 56 | use-project /btutils : btdht/btutils ; 57 | use-project /miniupnpc : libminiupnpc ; 58 | use-project /natpmpc : libnatpmp ; 59 | 60 | lib Iphlpapi : : Iphlpapi ; 61 | 62 | feature dhtd : on verbose off : composite propagated link-incompatible ; 63 | feature.compose on : g_log_dht=1 _DEBUG_DHT=1 ; 64 | feature.compose verbose : g_log_dht=1 _DEBUG_DHT_VERBOSE=1 ; 65 | 66 | feature dhti : off on : composite propagated link-incompatible ; 67 | feature.compose on : _DEBUG_DHT_INSTRUMENT=1 ; 68 | 69 | local usage-requirements = 70 | GSL/include 71 | include 72 | $(BOOST_ROOT) 73 | . 74 | GSL_THROW_ON_CONTRACT_VIOLATION 75 | BOOST_ASIO_HAS_STD_CHRONO 76 | ; 77 | 78 | lib scout 79 | : # sources 80 | src/dht_session.cpp 81 | src/file.cpp 82 | src/LoadLibraryList.cpp 83 | src/scout.cpp 84 | src/sockaddr.cpp 85 | src/upnp-portmap.cpp 86 | src/utils.cpp 87 | : # requirements 88 | multi 89 | /btdht//btdht/static 90 | /btutils//btutils/static 91 | /miniupnpc//miniupnpc/static 92 | /natpmpc//natpmpc/static 93 | boost_system 94 | sodium 95 | windows:Iphlpapi 96 | windows:_CRT_SECURE_NO_WARNINGS 97 | windows:_SCL_SECURE_NO_WARNINGS 98 | $(usage-requirements) 99 | : # default build 100 | : # usage requirements 101 | $(usage-requirements) 102 | ; 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | Scout is a library enabling two users to contact each other over the Internet using only their respective public keys. The Bittorrent distributed hash table (DHT) is used to store and retrieve contact information. Scout can also be used to store and retrieve short messages from the DHT so that peers can communicate even if they are never online at the same time. 4 | 5 | # Prerequisites 6 | 7 | Scout requires Boost 1.58 or newer and Libsodium 1.0 or newer 8 | 9 | # Building 10 | 11 | Scout uses Boost.Build version 2 to build. For information on installing BBv2 see the [Boost.Build manual](http://www.boost.org/build/doc/html/bbv2/installation.html). By default scout uses the versions of boost and libsodium which are installed on the system. If you are building on Windows you will need to specify the paths to the boost and libsodium source trees using the BOOST_ROOT and SODIUM_ROOT environment variables. Example commands to build scout on Windows: 12 | 13 | C:\scout> set BOOST_ROOT=C:\boost_1_60_0 14 | C:\scout> set SODIUM_ROOT=C:\libsodium-1.0.8 15 | C:\scout> bjam toolset=msvc-14 16 | 17 | # Setting up a DHT session 18 | 19 | Most users will want to use the dht_session class to easily set up a DHT node which can be used with the rest of scout's functions. To start a node create an instance of dht_session and call the start function. 20 | 21 | scout::dht_session ses; 22 | ses.start(); 23 | 24 | The start function will start a DHT node in a separate thread and return immediately. To stop the DHT node call the stop function. 25 | 26 | ses.stop(); 27 | 28 | This function will block until the dht node thread has exited. If stop is not called explicitly it will be called from the dht_session destructor. 29 | 30 | # Generating a key pair 31 | 32 | Scout provides the `generate_keypair` function to generate a new ed25519 key pair. 33 | 34 | std::pair keypair = scout::generate_keypair(); 35 | 36 | # Callbacks 37 | 38 | The scout API involves many callback functions. When using the dht_session class it is important to keep in mind that callbacks will be invoked in the DHT node's thread rather than the main thread of your application. This means you need to be careful when accessing your application's data structures from a callback. Ideally callbacks will carry a copy of any data they might need to store in the DHT and post notifications to the main application event loop for new data retrieved from the DHT. 39 | 40 | # Storage lifetime 41 | 42 | Data stored in the DHT can only be expected to remain there for up to two hours. It is recommended that data be stored/synchronized roughly once an hour. 43 | 44 | # Synchronizing contact information 45 | 46 | Scout stores contact information as a vector of entries. Each entry must be assigned an id which is unique within that vector. The contents of the entries are left up to the application. Scout encrypts the entry vector before storing it in the DHT so applications do not need to encrypt each entry's contents. 47 | 48 | To communicate entries between peers, scout uses a synchronize operation which retrieves the existing vector of entries from the DHT then writes a new vector with whatever updates the application specifies. To synchronize with a peer you need to have a shared secret to use as a key. Scout provides a key exchange function with uses Diffie-Hellman to generate a shared secret from the user's private key and a remote peer's public key. 49 | 50 | scout::secret_key shared_secret = scout::key_exchange(my_secret_key, remote_public_key); 51 | ses.synchronize(shared_secret, entries, entry_updated, finalize_entries, sync_finished); 52 | 53 | The entries vector should contain the entries which the application is currently aware of. The `entry_updated` callback will be invoked when a new or updated entry is retrieved from the DHT. The `finalize_entries` callback will be invoked after all updates have been retrieved and before the updated entry vector is stored in the DHT, it provides the application a final opportunity to update the entries. The `sync_finished` callback is invoked once all store requests have completed, any resources associated with the operation may be freed by this function. 54 | 55 | # Storing offline messages 56 | 57 | Scout supports storing messages in the DHT so that a peer can retrieve them later even if the originator has gone offline. Messages are limited to 1000 bytes each. Scout does not encrypt message contents, the application is expected to have it's own message encryption scheme. Messages are stored in the DHT using the hash of their content as the key, thus the content of a message cannot be changed. A series of messages are stored as a linked list which can be retrieved using just the hash of the most recently stored message. Message lists are always retrieved in last-in-first-out order. 58 | 59 | The first step is to create a message list. 60 | 61 | scout::list_head message_list; 62 | 63 | You can then add one-or-more messages to the list. 64 | 65 | scout::list_token msg_token = message_list.push_front(message_contents); 66 | 67 | The list_token contains the hash of the next message in the list, which corresponds to the previously added message. It should be stored alongside the message contents so that the message can be periodically stored in the DHT. 68 | 69 | ses.put(msg_token, message_contents, put_finished); 70 | 71 | The msg_token must match the one returned from push_front for the given message. The storage backing the message contents must remain valid until the `put_finished` callback is invoked. 72 | 73 | # Retrieving offline messages 74 | 75 | To retrieve a list of offline messages you first must obtain the hash of the first message. The sender can get this hash from the `list_head`. 76 | 77 | hash head_hash = message_list.head(); 78 | 79 | Typically this hash will be included in the contents of an entry. Once the receiving peer has the hash it can retrieve the first message. 80 | 81 | ses.get(head_hash, message_received); 82 | 83 | The `message_received` callback is passed the message contents along with the hash of the next message in the list. 84 | -------------------------------------------------------------------------------- /examples/Jamfile: -------------------------------------------------------------------------------- 1 | import modules ; 2 | 3 | use-project /scout : .. ; 4 | 5 | project client_test 6 | : requirements 7 | multi 8 | /scout//scout 9 | : default-build 10 | static 11 | ; 12 | 13 | exe client_test : client_test.cpp ; 14 | -------------------------------------------------------------------------------- /examples/client_test.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | namespace 25 | { 26 | const static char hex_alphabet[] = "0123456789abcdef"; 27 | 28 | std::string base16_encode(std::string s) 29 | { 30 | std::string ret; 31 | ret.resize(s.size() * 2); 32 | for (int i = 0; i < int(s.size()); ++i) 33 | { 34 | ret[i * 2] = hex_alphabet[(s[i] >> 4) & 0xf]; 35 | ret[i * 2 + 1] = hex_alphabet[s[i] & 0xf]; 36 | } 37 | return ret; 38 | } 39 | 40 | std::string base16_decode(std::string s) 41 | { 42 | std::string ret; 43 | ret.resize((s.size() + 1) / 2); 44 | // the & ~1 is to round down to an even multiple of 45 | // 2, since the loop requires that. 46 | for (int i = 0; i < (s.size() & ~1); i += 2) 47 | { 48 | ret[i / 2] = (strchr(hex_alphabet, s[i]) - hex_alphabet) << 4; 49 | ret[i / 2] |= (strchr(hex_alphabet, s[i + 1]) - hex_alphabet) & 0xf; 50 | } 51 | return ret; 52 | } 53 | } 54 | 55 | void usage() 56 | { 57 | std::cerr << "Usage: client_test [args]\n" 58 | "Commands:\n" 59 | "gen-key - generate a key pair and store it in the specified file\n" 60 | "dump-key - print the public key from the specified file\n" 61 | "sync " 62 | " - Synchronize entries using the secret key from the specified file and the speficied\n" 63 | " public key of a remote peer. Add or update the entry with the specified id with\n" 64 | " the specified value.\n" 65 | "put - Store the given string in the DHT as an offline message\n" 66 | "get - get the message with the given hash\n"; 67 | exit(1); 68 | } 69 | 70 | secret_key load_key_from_file(std::string const& filename) 71 | { 72 | std::FILE* keyfile = std::fopen(filename.c_str(), "rb+"); 73 | if (keyfile == nullptr) 74 | { 75 | std::cerr << "Failed to open key file" << std::endl; 76 | throw std::system_error(std::error_code(errno 77 | , std::system_category())); 78 | } 79 | scout::secret_key sk; 80 | size_t read = std::fread(sk.data(), 1, sk.size(), keyfile); 81 | std::fclose(keyfile); 82 | if (read != sk.size()) 83 | { 84 | std::cerr << "Failed to read key file" << std::endl; 85 | if (std::feof(keyfile)) 86 | { 87 | throw std::runtime_error("file too short"); 88 | } 89 | else 90 | { 91 | throw std::system_error(std::error_code(ferror(keyfile) 92 | , std::system_category())); 93 | } 94 | } 95 | return sk; 96 | } 97 | 98 | int main(int argc, char const* argv[]) 99 | { 100 | // skip path to self 101 | ++argv; 102 | --argc; 103 | if (argc < 1) usage(); 104 | 105 | if (strcmp(argv[0], "gen-key") == 0) 106 | { 107 | ++argv; 108 | --argc; 109 | if (argc < 1) usage(); 110 | 111 | auto key = scout::generate_keypair(); 112 | std::FILE* keyfile = std::fopen(argv[0], "wb+"); 113 | if (keyfile == nullptr) 114 | { 115 | std::cerr << "Failed to open key file" << std::endl; 116 | return 1; 117 | } 118 | size_t written = std::fwrite(key.first.data(), 1, key.first.size(), keyfile); 119 | if (written != key.first.size()) 120 | { 121 | std::cerr << "Failed to write key to file" << std::endl; 122 | std::fclose(keyfile); 123 | return 1; 124 | } 125 | std::fclose(keyfile); 126 | 127 | return 0; 128 | } 129 | 130 | if (strcmp(argv[0], "dump-key") == 0) 131 | { 132 | ++argv; 133 | --argc; 134 | if (argc < 1) usage(); 135 | 136 | scout::secret_key sk; 137 | try { 138 | sk = load_key_from_file(argv[0]); 139 | } 140 | catch (std::exception const& e) { 141 | std::cerr << "Failed to load key from file. You must generate a key first using " 142 | "the gen-key command\n"; 143 | return 1; 144 | } 145 | scout::public_key pk; 146 | crypto_scalarmult_base((unsigned char*)pk.data() 147 | , (unsigned char*)sk.data()); 148 | std::cout << "Public key: " << base16_encode({ (char*)pk.data(), pk.size() }) << std::endl; 149 | return 0; 150 | } 151 | 152 | scout::dht_session ses; 153 | ses.start(); 154 | 155 | std::condition_variable op_complete; 156 | 157 | if (strcmp(argv[0], "sync") == 0) 158 | { 159 | ++argv; 160 | --argc; 161 | if (argc < 4) usage(); 162 | 163 | scout::secret_key sk; 164 | try { 165 | sk = load_key_from_file(argv[0]); 166 | } catch (std::exception const& e) { 167 | std::cerr << "Failed to load key from file. You must generate a key first using " 168 | "the gen-key command\n"; 169 | return 1; 170 | } 171 | std::string pk_str = base16_decode(argv[1]); 172 | scout::public_key pk; 173 | if (pk_str.size() != pk.size()) usage(); 174 | std::memcpy(pk.data(), pk_str.data(), pk.size()); 175 | scout::secret_key shared_secret = scout::key_exchange(sk, pk); 176 | std::uint32_t eid = std::uint32_t(std::stoul(argv[2])); 177 | scout::entry e(eid); 178 | std::vector content((gsl::byte const*)argv[3] 179 | , (gsl::byte const*)argv[3] + std::strlen(argv[3])); 180 | e.assign(content); 181 | std::vector entries; 182 | entries.push_back(e); 183 | ses.synchronize(shared_secret, std::move(entries) 184 | , [](entry const&) {} 185 | , [=](std::vector& entries) 186 | { 187 | for (entry& e : entries) 188 | { 189 | if (e.id() == eid && e.value() != content) 190 | e.assign(content); 191 | std::cout << e.id() << ' ' << std::string(e.value().begin(), e.value().end()) 192 | << '\n'; 193 | } 194 | } 195 | , [&]() { op_complete.notify_all(); }); 196 | } 197 | 198 | if (strcmp(argv[0], "put") == 0) 199 | { 200 | ++argv; 201 | --argc; 202 | if (argc < 1) usage(); 203 | 204 | scout::list_head head; 205 | gsl::span contents((gsl::byte const*)argv[0] 206 | , (gsl::byte const*)argv[0] + std::strlen(argv[0])); 207 | scout::list_token token = head.push_front(contents); 208 | ses.put(token, contents, [&]() { op_complete.notify_all(); }); 209 | std::cout << "inserted message with hash " 210 | << base16_encode({ (char const *)head.head().data() 211 | , head.head().size() }) << std::endl; 212 | } 213 | else if (strcmp(argv[0], "get") == 0) 214 | { 215 | ++argv; 216 | --argc; 217 | if (argc < 1) usage(); 218 | 219 | std::string addr_str = base16_decode(argv[0]); 220 | scout::hash address; 221 | if (addr_str.size() != address.size()) usage(); 222 | std::memcpy(address.data(), addr_str.data(), addr_str.size()); 223 | ses.get(address, [&](std::vector contents, hash const& next_hash) 224 | { 225 | std::cout << "Got message: " << std::string((char*)contents.data(), contents.size()) << "\n" 226 | << "Next hash: " << base16_encode({ (char*)next_hash.data(), next_hash.size() }) << "\n"; 227 | op_complete.notify_all(); 228 | }); 229 | } 230 | 231 | std::mutex m; 232 | std::unique_lock l(m); 233 | op_complete.wait(l); 234 | } 235 | -------------------------------------------------------------------------------- /include/dht_session.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef BITTORRENT_DHT_SESSION_HPP 18 | #define BITTORRENT_DHT_SESSION_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "udp_socket.hpp" 28 | #include "scout.hpp" 29 | 30 | namespace scout 31 | { 32 | 33 | struct upnp_mapping 34 | { 35 | upnp_mapping(char* ctrlURL, char st[MINIUPNPC_URL_MAXSIZE]) 36 | : controlURL(ctrlURL) 37 | { 38 | memcpy(servicetype, st, sizeof(servicetype)); 39 | } 40 | 41 | std::string controlURL; 42 | char servicetype[MINIUPNPC_URL_MAXSIZE]; 43 | }; 44 | 45 | class dht_session 46 | { 47 | friend struct ip_change_observer_session; 48 | 49 | public: 50 | enum run_state 51 | { 52 | INITIAL, 53 | RUNNING, 54 | QUITTING, 55 | }; 56 | 57 | dht_session(); 58 | ~dht_session(); 59 | 60 | // start the dht client 61 | // the client will start listening on a random port and bootstrap its routing table 62 | // if this function returns zero the session is ready to handle requests 63 | // otherwise an error occurred 64 | int start(); 65 | 66 | // stop the client 67 | // this will terminate all network activity and any outstanding requests will be aborted 68 | void stop(); 69 | 70 | // Note: See the corresponding functions in scout.hpp for more details on 71 | // each type of request. 72 | 73 | // synchronize a list of entries with the DHT 74 | // this will first update the given vector with any new or updated entries from the DHT 75 | // then store the updated list in the DHT 76 | void synchronize(secret_key_span shared_key, std::vector entries 77 | , entry_updated entry_cb, finalize_entries finalize_cb, sync_finished finished_cb); 78 | 79 | // store an immutable item in the DHT 80 | void put(list_token const& token, gsl::span contents 81 | , put_finished finished_cb); 82 | 83 | // retrieve an immutable item from the DHT 84 | void get(hash_span address, item_received received_cb); 85 | 86 | private: 87 | bool is_quitting() const { return m_state == QUITTING; } 88 | void resolve_bootstrap_servers(); 89 | void update_mappings(); 90 | void network_thread_fun(std::promise& promise); 91 | void on_dht_timer(error_code const& ec); 92 | void on_natpmp_timer(error_code const& ec); 93 | void on_ip_changed(udp::endpoint const& new_ip); 94 | void incoming_packet(char* buf, size_t len, udp::endpoint const& ep); 95 | 96 | boost::asio::io_service m_ios; 97 | std::uint16_t m_dht_external_port; 98 | run_state m_state; 99 | ExternalIPCounter m_external_ip; 100 | std::shared_ptr m_socket; 101 | std::thread m_thread; 102 | smart_ptr m_dht; 103 | boost::asio::steady_timer m_dht_timer; 104 | // io service for handling various async tasks (such as nat-pmp): 105 | io_service m_ios_worker; 106 | // worker thread used by the worker io service: 107 | std::thread m_worker_thread; 108 | boost::asio::steady_timer m_natpmp_timer; 109 | std::vector m_upnp_mappings; 110 | bool m_is_natpmp_mapped; 111 | std::vector> m_bootstrap_nodes; 112 | int m_dht_rate_limit; 113 | }; 114 | 115 | } // namespace scout 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /include/scout.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SCOUT_HPP 18 | # define SCOUT_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace scout 28 | { 29 | 30 | using secret_key = std::array; 31 | using secret_key_span = gsl::span; 32 | using csecret_key_span = gsl::span; 33 | using public_key = std::array; 34 | using public_key_span = gsl::span; 35 | using cpublic_key_span = gsl::span; 36 | using hash = std::array; 37 | using hash_span = gsl::span; 38 | using chash_span = gsl::span; 39 | 40 | // a mutable blob of data 41 | // each entry has an id associated with it which must be unique among the entries it is stored with 42 | class entry 43 | { 44 | public: 45 | entry(uint32_t id) : m_seq(0), m_id(id) {} 46 | 47 | static std::pair> parse(gsl::span input); 48 | gsl::span serialize(gsl::span output) const; 49 | 50 | uint32_t id() const { return m_id; } 51 | std::vector const& value() const { return m_contents; } 52 | void assign(gsl::span contents) 53 | { 54 | m_contents.assign(contents.begin(), contents.end()); 55 | ++m_seq; 56 | } 57 | 58 | // convenience function to save callers from having to do an 59 | // explicit to_bytes 60 | template 61 | void assign(gsl::span s) 62 | { 63 | assign(gsl::as_bytes(s)); 64 | } 65 | 66 | bool operator==(entry const& o) const 67 | { 68 | return m_id == o.m_id 69 | && m_seq == o.m_seq 70 | && m_contents == o.m_contents; 71 | } 72 | 73 | entry& operator=(entry o) 74 | { 75 | m_id = o.m_id; 76 | m_seq = o.m_seq; 77 | m_contents = o.m_contents; 78 | return *this; 79 | } 80 | 81 | // for internal use: 82 | int64_t seq() const { return m_seq; } 83 | void update_seq(int64_t seq) { m_seq = seq; } 84 | void update_contents(std::vector contents) { m_contents = contents; } 85 | 86 | private: 87 | entry(int64_t seq, uint32_t id, std::vector content) 88 | : m_contents(std::move(content)), m_seq(seq), m_id(id) {} 89 | 90 | std::vector m_contents; 91 | int64_t m_seq; 92 | uint32_t m_id; 93 | }; 94 | 95 | // a token is associated with each piece of immutable data stored in a list 96 | // it should be stored alongside the data and passed along with it to put 97 | class list_token 98 | { 99 | public: 100 | friend class list_head; 101 | 102 | // input span must be extactly the size of the serialized token 103 | static list_token parse(gsl::span input); 104 | gsl::span serialize(gsl::span output) const; 105 | 106 | hash const& next() const { return m_next; } 107 | 108 | bool operator==(list_token const& o) const 109 | { 110 | return m_next == o.m_next; 111 | } 112 | 113 | private: 114 | list_token(hash next) : m_next(next) {} 115 | 116 | hash m_next; 117 | }; 118 | 119 | // The head of a linked-list stored in the DHT. New items can only be inserted 120 | // at the head of the list and list can only be retrieved starting at the head (LIFO). 121 | class list_head 122 | { 123 | public: 124 | list_head() 125 | { 126 | m_head.fill(gsl::byte(0)); 127 | } 128 | 129 | // input span must be extactly the size of the serialized list head 130 | static list_head parse(gsl::span input); 131 | gsl::span serialize(gsl::span output) const; 132 | 133 | // add an item to the linked-list 134 | // the returned list_token should be stored with the contents and passed to put() 135 | list_token push_front(gsl::span contents); 136 | 137 | // get the hash of the head of the list 138 | // this can be passed to get() to retrieve the first item in the list 139 | hash const& head() const { return m_head; } 140 | 141 | bool operator==(list_head const& o) const 142 | { 143 | return m_head == o.m_head; 144 | } 145 | 146 | private: 147 | list_head(hash head) : m_head(head) {} 148 | 149 | hash m_head; 150 | }; 151 | 152 | // generate a new, random key pair 153 | std::pair generate_keypair(); 154 | 155 | // do an ECDH key exchange 156 | secret_key key_exchange(csecret_key_span sk, cpublic_key_span pk); 157 | 158 | // takes a span of entries and write them out to a buffer 159 | // returns a new span pointing to to one past the last byte used to store 160 | // the entries 161 | gsl::span serialize(gsl::span entries, gsl::span output); 162 | 163 | // parse a list of entries as generated by serialize() 164 | // returns a span starting at one past the last byte used to store the entries 165 | gsl::span parse(gsl::span input, std::vector& entries); 166 | 167 | // called when a new or updated entry is received from the DHT 168 | using entry_updated = std::function; 169 | // called just before the list of entries is stored in the DHT 170 | using finalize_entries = std::function& entries)>; 171 | // called when storing the current entry list has completed 172 | using sync_finished = std::function; 173 | // called when the value associated with the hash has been received 174 | // the DHT transaction ends after this function is called 175 | // if no value is found an empty span will be passed 176 | using item_received = std::function contents, hash const& next_hash)>; 177 | // called when a put has completed 178 | using put_finished = std::function; 179 | 180 | // Synchronize a list of entries with the DHT. First entries are read from the 181 | // DHT then an updated list is written back. 182 | // 183 | // entry_cb will be called for any entries retrived from the DHT which are not in 184 | // the passed in entries vector or are newer than the entry found there 185 | // 186 | // finalize_cb will be called once the get operation has completed. It is passed a vector 187 | // containing the passed in entries merged with any new or updated entries retrived from the DHT. 188 | // The finalize_cb provides a final opportunity to modify the list of entries before it is written. 189 | // 190 | // All entries in the vector passed to finalize_cb are written to the DHT. Once the put operation 191 | // is complete finished_cb is invoked 192 | void synchronize(IDht& dht, secret_key_span shared_key, std::vector const& entries 193 | , entry_updated entry_cb, finalize_entries finalize_cb, sync_finished finished_cb); 194 | 195 | // store an immutable item in the DHT 196 | // 197 | // the token must be a value returned from list_head::push_front called with 198 | // the contents 199 | // 200 | // finished_cb will be called once the put operation has completed 201 | void put(IDht& dht, list_token const& token, gsl::span contents 202 | , put_finished finished_cb); 203 | 204 | // retrieve an immutable item from the DHT identified by the given hash 205 | // 206 | // received_cb will be called with the message contents and the hash of the next 207 | // message in the list 208 | // 209 | // the next hash will be all zeros if it is the last message in the list 210 | // if the message is not found then received_cb will be called with empty contents 211 | void get(IDht& dht, chash_span address, item_received received_cb); 212 | 213 | } 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /include/udp_socket.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef UDP_SOCKET_HPP 18 | # define UDP_SOCKET_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "utils.hpp" // for log_debug 28 | 29 | // for _1, _2 etc. 30 | using namespace std::placeholders; 31 | namespace asio = boost::asio; 32 | using boost::system::error_code; 33 | using boost::asio::io_service; 34 | using boost::asio::ip::udp; 35 | using boost::asio::ip::address_v4; 36 | using boost::asio::ip::address_v6; 37 | using boost::asio::buffer; 38 | using boost::asio::signal_set; 39 | 40 | enum { 41 | ip_overhead = 20 + 8 42 | }; 43 | 44 | struct udp_socket : public std::enable_shared_from_this 45 | { 46 | static std::shared_ptr construct(io_service& ios) 47 | { 48 | return std::shared_ptr(new udp_socket(ios)); 49 | } 50 | 51 | static std::shared_ptr construct(io_service& ios, udp::endpoint bindto) 52 | { 53 | return std::shared_ptr(new udp_socket(ios, bindto)); 54 | } 55 | 56 | udp_socket(udp_socket const& o) = delete; 57 | udp_socket& operator=(udp_socket const& o) = delete; 58 | 59 | udp_socket(udp_socket&& o) 60 | : m_socket(std::move(o.m_socket)) 61 | , m_sender(std::move(o.m_sender)) 62 | , m_handler(std::move(o.m_handler)) 63 | , m_abort(std::move(o.m_abort)) 64 | { 65 | std::memcpy(m_receive_buffer, o.m_receive_buffer, sizeof(m_receive_buffer)); 66 | } 67 | 68 | typedef std::function handler_t; 69 | 70 | void start(handler_t const& h, udp::endpoint const& ep, error_code& ec) 71 | { 72 | m_socket.bind(ep, ec); 73 | if (ec) return; 74 | 75 | m_bind_ep = ep; 76 | m_abort = false; 77 | 78 | m_handler = h; 79 | m_socket.async_receive_from(buffer(m_receive_buffer, sizeof(m_receive_buffer)) 80 | , m_sender, std::bind(&udp_socket::on_receive, shared_from_this(), _1, _2)); 81 | } 82 | 83 | int send_to(char const* buf, int len, udp::endpoint const& ep, error_code& ec) 84 | { 85 | int ret = m_socket.send_to(asio::buffer(buf, len), ep, 0, ec); 86 | if (ec && !m_abort && (ec == boost::system::errc::bad_file_descriptor 87 | || ec == boost::system::errc::broken_pipe)) { 88 | 89 | // on iOS, when we wake up from background mode and our socket 90 | // has been reclaimed, we may get this error. Try to re-open it. 91 | reopen_socket(ec); 92 | if (ec) return -1; 93 | 94 | ret = m_socket.send_to(asio::buffer(buf, len), ep, 0, ec); 95 | } 96 | return ret; 97 | } 98 | 99 | udp::endpoint local_endpoint() const 100 | { 101 | error_code ec; 102 | return m_socket.local_endpoint(ec); 103 | } 104 | 105 | void cancel() 106 | { 107 | #if _WIN32_WINNT <= 0x0502 108 | // XP and earlier do not support cancel 109 | m_socket.close(); 110 | #else 111 | m_socket.cancel(); 112 | #endif 113 | m_abort = true; 114 | } 115 | 116 | void close() 117 | { 118 | m_socket.close(); 119 | m_abort = true; 120 | } 121 | 122 | void set_recv_buffer(std::size_t size) 123 | { 124 | boost::asio::socket_base::receive_buffer_size recv_size(size); 125 | m_socket.set_option(recv_size); 126 | } 127 | 128 | private: 129 | 130 | // these constructors are private. use the construct functions 131 | udp_socket(io_service& ios) 132 | : m_socket(ios, udp::v4()) 133 | , m_abort(false) 134 | {} 135 | 136 | udp_socket(io_service& ios, udp::endpoint bindto) 137 | : m_socket(ios, bindto) 138 | , m_abort(false) 139 | {} 140 | 141 | void reopen_socket(error_code& ec) 142 | { 143 | m_socket.close(); 144 | m_socket.open(m_bind_ep.protocol(), ec); 145 | if (ec) return; 146 | m_socket.bind(m_bind_ep, ec); 147 | if (ec) return; 148 | m_socket.async_receive_from(buffer(m_receive_buffer, sizeof(m_receive_buffer)) 149 | , m_sender, std::bind(&udp_socket::on_receive, shared_from_this(), _1, _2)); 150 | } 151 | 152 | void on_receive(error_code const& ec, size_t bytes_transferred) 153 | { 154 | if (m_abort) { 155 | log_debug("udp_socket::on_receive aborted, exiting"); 156 | return; 157 | } 158 | 159 | if (ec == boost::system::errc::bad_file_descriptor 160 | || ec == boost::system::errc::broken_pipe 161 | || ec == boost::system::errc::not_connected) { 162 | 163 | // on iOS, when we wake up from background mode and our socket 164 | // has been reclaimed, we may get this error. Try to re-open it and try 165 | // again. 166 | error_code ignore; 167 | reopen_socket(ignore); 168 | log_debug("udp_socket::on_receive re-opening socket failed: %s", 169 | ignore.message().c_str()); 170 | return; 171 | } 172 | 173 | if (ec == asio::error::operation_aborted) { 174 | log_debug("udp_socket::on_receive canceled, exiting: %s", ec.message().c_str()); 175 | return; 176 | } 177 | 178 | // pass on the packet to the handler 179 | if (!ec) m_handler((char *)m_receive_buffer, bytes_transferred, m_sender); 180 | else log_debug("udp_socket::on_receive ignoring error: %s", ec.message().c_str()); 181 | 182 | // receive the next packet 183 | m_socket.async_receive_from(buffer(m_receive_buffer, sizeof(m_receive_buffer)) 184 | , m_sender, std::bind(&udp_socket::on_receive, shared_from_this(), _1, _2)); 185 | } 186 | 187 | // the buffer used for receiving packets into 188 | uint64_t m_receive_buffer[2000/8]; 189 | 190 | // the socket we send and receive packets over 191 | udp::socket m_socket; 192 | 193 | // the sender's address of packets we receive 194 | udp::endpoint m_sender; 195 | 196 | // our address and port we've bound this socket to 197 | udp::endpoint m_bind_ep; 198 | 199 | // handler function to be called on incoming packets 200 | handler_t m_handler; 201 | 202 | // set to true if it's time to quit 203 | bool m_abort; 204 | }; 205 | 206 | typedef std::shared_ptr udp_socket_ptr; 207 | 208 | #endif 209 | -------------------------------------------------------------------------------- /include/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef UTILS_HPP 18 | # define UTILS_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace be = boost::endian; 28 | using namespace scout; 29 | 30 | struct entry_header 31 | { 32 | be::big_int64_t seq; 33 | be::big_uint32_t id; 34 | std::array reserved; 35 | uint8_t content_length; 36 | // offset from this field to the first byte of content 37 | // This is for extensibility. If we want to add more header fields 38 | // later on we can increase the offset and old clients will apply 39 | // the offset and skip the part of the header they don't understand. 40 | uint8_t content_offset; 41 | }; 42 | 43 | struct entries_header 44 | { 45 | uint8_t entry_count; 46 | uint8_t entries_offset; // offset from this field to the first entry 47 | }; 48 | 49 | struct dht_msg_header 50 | { 51 | hash next_hash; 52 | be::big_uint16_t msg_length; 53 | uint8_t msg_offset; 54 | }; 55 | 56 | sha1_hash sha1_fun(const byte* buf, int len); 57 | 58 | template 59 | gsl::span extract(gsl::span dest, gsl::span src) 60 | { 61 | static_assert(std::is_trivial>::value, "Target type must be a trivial type"); 62 | 63 | if (src.size_bytes() < dest.size_bytes()) 64 | throw std::length_error("bytes span smaller than destination"); 65 | std::memcpy(dest.data(), src.data(), dest.size_bytes()); 66 | return{ src.data() + dest.size_bytes(), src.size_bytes() - dest.size_bytes() }; 67 | } 68 | 69 | template 70 | gsl::span flatten(gsl::span dest, gsl::span src) 71 | { 72 | static_assert(std::is_trivial>::value, "Target type must be a trivial type"); 73 | 74 | if (dest.size_bytes() < src.size_bytes()) 75 | throw std::length_error("source span larger than destination"); 76 | std::memcpy(dest.data(), src.data(), src.size_bytes()); 77 | return{ dest.data() + src.size_bytes(), dest.size_bytes() - src.size_bytes() }; 78 | } 79 | 80 | std::vector message_dht_blob_write(gsl::span msg_data, chash_span next_msg_hash); 81 | std::vector message_dht_blob_read(gsl::span dht_blob, hash& next_msg_hash); 82 | 83 | void log_debug(char const* fmt, ...); 84 | void log_error(char const* fmt, ...); 85 | 86 | // crypto helper functions: 87 | std::vector decrypt_buffer(std::vector buffer, secret_key_span secret); 88 | std::vector encrypt_buffer(std::vector buffer, secret_key_span secret, const unsigned char* nonce_in = nullptr); 89 | 90 | #endif -------------------------------------------------------------------------------- /libminiupnpc/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | /autom4te.cache 3 | /aclocal.m4 4 | /compile 5 | /configure 6 | /depcomp 7 | /install-sh 8 | /missing 9 | /m4 10 | /config.guess 11 | /config.sub -------------------------------------------------------------------------------- /libminiupnpc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | 3 | project (miniupnpc C) 4 | set (MINIUPNPC_VERSION 1.7) 5 | set (MINIUPNPC_API_VERSION 9) 6 | 7 | if (NOT CMAKE_BUILD_TYPE) 8 | if (WIN32) 9 | set (DEFAULT_BUILD_TYPE MinSizeRel) 10 | else (WIN32) 11 | set (DEFAULT_BUILD_TYPE RelWithDebInfo) 12 | endif(WIN32) 13 | set (CMAKE_BUILD_TYPE ${DEFAULT_BUILD_TYPE} CACHE STRING 14 | "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." 15 | FORCE) 16 | endif() 17 | 18 | option (UPNPC_BUILD_STATIC "Build static library" TRUE) 19 | option (UPNPC_BUILD_SHARED "Build shared library" TRUE) 20 | if (NOT WIN32) 21 | option (UPNPC_BUILD_TESTS "Build test executables" TRUE) 22 | endif (NOT WIN32) 23 | option (NO_GETADDRINFO "Define NO_GETADDRINFO" FALSE) 24 | 25 | mark_as_advanced (NO_GETADDRINFO) 26 | 27 | if (NO_GETADDRINFO) 28 | add_definitions (-DNO_GETADDRINFO) 29 | endif (NO_GETADDRINFO) 30 | 31 | if (NOT WIN32) 32 | add_definitions (-DMINIUPNPC_SET_SOCKET_TIMEOUT) 33 | add_definitions (-D_BSD_SOURCE -D_POSIX_C_SOURCE=1) 34 | else (NOT WIN32) 35 | add_definitions (-D_WIN32_WINNT=0x0501) # XP or higher for getnameinfo and friends 36 | endif (NOT WIN32) 37 | 38 | if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") 39 | add_definitions (-DMACOSX -D_DARWIN_C_SOURCE) 40 | endif () 41 | 42 | # Set compiler specific build flags 43 | if (CMAKE_COMPILER_IS_GNUC) 44 | # Set our own default flags at first run. 45 | if (NOT CONFIGURED) 46 | 47 | if (NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") 48 | set (_PIC -fPIC) 49 | endif (CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") 50 | 51 | set (CMAKE_C_FLAGS "${_PIC} -Wall $ENV{CFLAGS}" # CMAKE_C_FLAGS gets appended to the other C flags 52 | CACHE STRING "Flags used by the C compiler during normal builds." FORCE) 53 | set (CMAKE_C_FLAGS_DEBUG "-g -DDDEBUG" 54 | CACHE STRING "Flags used by the C compiler during debug builds." FORCE) 55 | set (CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG" 56 | CACHE STRING "Flags used by the C compiler during release builds." FORCE) 57 | set (CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -g -DNDEBUG" 58 | CACHE STRING "Flags used by the C compiler during release builds." FORCE) 59 | set (CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG" 60 | CACHE STRING "Flags used by the C compiler during release builds." FORCE) 61 | 62 | endif (NOT CONFIGURED) 63 | endif () 64 | 65 | configure_file (${CMAKE_SOURCE_DIR}/miniupnpcstrings.h.cmake ${CMAKE_BINARY_DIR}/miniupnpcstrings.h) 66 | include_directories (${CMAKE_BINARY_DIR}) 67 | 68 | set (MINIUPNPC_SOURCES 69 | igd_desc_parse.c 70 | miniupnpc.c 71 | minixml.c 72 | minisoap.c 73 | miniwget.c 74 | upnpc.c 75 | upnpcommands.c 76 | upnpreplyparse.c 77 | upnperrors.c 78 | connecthostport.c 79 | portlistingparse.c 80 | receivedata.c 81 | ) 82 | 83 | if (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") 84 | set (MINIUPNPC_SOURCES ${MINIUPNPC_SOURCES} minissdpc.c) 85 | endif (NOT WIN32 AND NOT CMAKE_SYSTEM_NAME STREQUAL "AmigaOS") 86 | 87 | if (WIN32) 88 | set_source_files_properties (${MINIUPNPC_SOURCES} PROPERTIES 89 | COMPILE_DEFINITIONS STATICLIB 90 | COMPILE_DEFINITIONS MINIUPNP_EXPORTS 91 | ) 92 | endif (WIN32) 93 | 94 | if (WIN32) 95 | find_library (WINSOCK2_LIBRARY NAMES ws2_32 WS2_32 Ws2_32) 96 | find_library (IPHLPAPI_LIBRARY NAMES iphlpapi) 97 | set (LDLIBS ${WINSOCK2_LIBRARY} ${IPHLPAPI_LIBRARY} ${LDLIBS}) 98 | #elseif (CMAKE_SYSTEM_NAME STREQUAL "Solaris") 99 | # find_library (SOCKET_LIBRARY NAMES socket) 100 | # find_library (NSL_LIBRARY NAMES nsl) 101 | # find_library (RESOLV_LIBRARY NAMES resolv) 102 | # set (LDLIBS ${SOCKET_LIBRARY} ${NSL_LIBRARY} ${RESOLV_LIBRARY} ${LDLIBS}) 103 | endif (WIN32) 104 | 105 | if (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED) 106 | message (FATAL "Both shared and static libraries are disabled!") 107 | endif (NOT UPNPC_BUILD_STATIC AND NOT UPNPC_BUILD_SHARED) 108 | 109 | if (UPNPC_BUILD_STATIC) 110 | add_library (upnpc-static STATIC ${MINIUPNPC_SOURCES}) 111 | set_target_properties (upnpc-static PROPERTIES OUTPUT_NAME "miniupnpc") 112 | target_link_libraries (upnpc-static ${LDLIBS}) 113 | set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-static) 114 | set (UPNPC_LIBRARY_TARGET upnpc-static) 115 | endif (UPNPC_BUILD_STATIC) 116 | 117 | if (UPNPC_BUILD_SHARED) 118 | add_library (upnpc-shared SHARED ${MINIUPNPC_SOURCES}) 119 | set_target_properties (upnpc-shared PROPERTIES OUTPUT_NAME "miniupnpc") 120 | set_target_properties (upnpc-shared PROPERTIES VERSION ${MINIUPNPC_VERSION}) 121 | set_target_properties (upnpc-shared PROPERTIES SOVERSION ${MINIUPNPC_API_VERSION}) 122 | target_link_libraries (upnpc-shared ${LDLIBS}) 123 | set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} upnpc-shared) 124 | set (UPNPC_LIBRARY_TARGET upnpc-shared) 125 | endif (UPNPC_BUILD_SHARED) 126 | 127 | if (UPNPC_BUILD_TESTS) 128 | add_executable (testminixml testminixml.c minixml.c igd_desc_parse.c) 129 | target_link_libraries (testminixml ${LDLIBS}) 130 | 131 | add_executable (minixmlvalid minixmlvalid.c minixml.c) 132 | target_link_libraries (minixmlvalid ${LDLIBS}) 133 | 134 | add_executable (testupnpreplyparse testupnpreplyparse.c 135 | minixml.c upnpreplyparse.c) 136 | target_link_libraries (testupnpreplyparse ${LDLIBS}) 137 | 138 | add_executable (testigddescparse testigddescparse.c 139 | igd_desc_parse.c minixml.c miniupnpc.c miniwget.c minissdpc.c 140 | upnpcommands.c upnpreplyparse.c minisoap.c connecthostport.c 141 | portlistingparse.c receivedata.c 142 | ) 143 | target_link_libraries (testigddescparse ${LDLIBS}) 144 | 145 | add_executable (testminiwget testminiwget.c 146 | miniwget.c miniupnpc.c minisoap.c upnpcommands.c minissdpc.c 147 | upnpreplyparse.c minixml.c igd_desc_parse.c connecthostport.c 148 | portlistingparse.c receivedata.c 149 | ) 150 | target_link_libraries (testminiwget ${LDLIBS}) 151 | 152 | # set (UPNPC_INSTALL_TARGETS ${UPNPC_INSTALL_TARGETS} testminixml minixmlvalid testupnpreplyparse testigddescparse testminiwget) 153 | endif (UPNPC_BUILD_TESTS) 154 | 155 | 156 | install (TARGETS ${UPNPC_INSTALL_TARGETS} 157 | RUNTIME DESTINATION bin 158 | LIBRARY DESTINATION lib${LIB_SUFFIX} 159 | ARCHIVE DESTINATION lib${LIB_SUFFIX} 160 | ) 161 | install (FILES 162 | miniupnpc.h 163 | miniwget.h 164 | upnpcommands.h 165 | igd_desc_parse.h 166 | upnpreplyparse.h 167 | upnperrors.h 168 | declspec.h 169 | DESTINATION include/miniupnpc 170 | ) 171 | 172 | set (CONFIGURED YES CACHE INTERNAL "") 173 | 174 | # vim: ts=2:sw=2 175 | -------------------------------------------------------------------------------- /libminiupnpc/Jamfile: -------------------------------------------------------------------------------- 1 | 2 | lib miniupnpc : [ glob *.c : .git minihttptestserver.c minixmlvalid.c ] 3 | : # requirements 4 | . 5 | _BSD_SOURCE 6 | _GNU_SOURCE 7 | windows:_WIN32 8 | windows:STATICLIB 9 | # work around a bug in the clang toolset 10 | # it uses the c++ compiler to build C code 11 | -std=c99 12 | -std=c99 13 | 14 | windows:_WIN32 15 | : # default build 16 | static 17 | : # usage requirements 18 | . 19 | windows:STATICLIB 20 | ; 21 | 22 | -------------------------------------------------------------------------------- /libminiupnpc/LICENSE: -------------------------------------------------------------------------------- 1 | MiniUPnPc 2 | Copyright (c) 2005-2011, Thomas BERNARD 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * The name of the author may not be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | POSSIBILITY OF SUCH DAMAGE. 27 | 28 | -------------------------------------------------------------------------------- /libminiupnpc/Makefile.mingw: -------------------------------------------------------------------------------- 1 | # $Id: Makefile.mingw,v 1.17 2012/12/02 14:12:45 nanard Exp $ 2 | # Miniupnp project. 3 | # http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | # (c) 2005-2011 Thomas Bernard 5 | # This Makefile is made for MinGW 6 | # 7 | CC = gcc 8 | #CFLAGS = -Wall -g -DDEBUG -D_WIN32_WINNT=0X501 9 | CFLAGS = -Wall -Os -DNDEBUG -D_WIN32_WINNT=0X501 10 | LDLIBS = -lws2_32 -liphlpapi 11 | # -lwsock32 12 | # -liphlpapi is needed for GetBestRoute() and GetIpAddrTable() 13 | PYTHON=\utils\python25\python 14 | OBJS=miniwget.o minixml.o igd_desc_parse.o minisoap.o \ 15 | miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \ 16 | connecthostport.o portlistingparse.o receivedata.o 17 | OBJSDLL=$(addprefix dll/, $(OBJS)) 18 | 19 | all: init upnpc-static upnpc-shared testminixml libminiupnpc.a miniupnpc.dll 20 | 21 | init: 22 | mkdir dll 23 | echo init > init 24 | 25 | clean: 26 | del upnpc testminixml *.o 27 | del dll\*.o 28 | del *.exe 29 | del miniupnpc.dll 30 | del libminiupnpc.a 31 | 32 | libminiupnpc.a: $(OBJS) 33 | $(AR) cr $@ $? 34 | 35 | pythonmodule: libminiupnpc.a 36 | $(PYTHON) setupmingw32.py build --compiler=mingw32 37 | $(PYTHON) setupmingw32.py install --skip-build 38 | 39 | miniupnpc.dll: libminiupnpc.a $(OBJSDLL) 40 | dllwrap -k --driver-name gcc \ 41 | --def miniupnpc.def \ 42 | --output-def miniupnpc.dll.def \ 43 | --implib miniupnpc.lib -o $@ \ 44 | $(OBJSDLL) $(LDLIBS) 45 | 46 | miniupnpc.lib: miniupnpc.dll 47 | echo $@ generated with $< 48 | 49 | dll/upnpc.o: upnpc.o 50 | echo $@ generated with $< 51 | 52 | .c.o: 53 | $(CC) $(CFLAGS) -DSTATICLIB -c -o $@ $< 54 | $(CC) $(CFLAGS) -DMINIUPNP_EXPORTS -c -o dll/$@ $< 55 | 56 | upnpc.o: 57 | $(CC) $(CFLAGS) -DSTATICLIB -c -o $@ $< 58 | $(CC) $(CFLAGS) -c -o dll/$@ $< 59 | 60 | upnpc-static: upnpc.o libminiupnpc.a 61 | $(CC) -enable-stdcall-fixup -o $@ $^ $(LDLIBS) 62 | 63 | upnpc-shared: dll/upnpc.o miniupnpc.lib 64 | $(CC) -enable-stdcall-fixup -o $@ $^ $(LDLIBS) 65 | 66 | wingenminiupnpcstrings: wingenminiupnpcstrings.o 67 | 68 | wingenminiupnpcstrings.o: wingenminiupnpcstrings.c 69 | 70 | miniupnpcstrings.h: miniupnpcstrings.h.in wingenminiupnpcstrings 71 | wingenminiupnpcstrings $< $@ 72 | 73 | minixml.o: minixml.c minixml.h miniupnpcstrings.h 74 | 75 | upnpc.o: upnpc.c miniwget.h minisoap.h miniupnpc.h igd_desc_parse.h upnpreplyparse.h upnpcommands.h upnperrors.h 76 | 77 | miniwget.o: miniwget.c miniwget.h miniupnpcstrings.h connecthostport.h 78 | 79 | minisoap.o: minisoap.c minisoap.h miniupnpcstrings.h 80 | 81 | miniupnpc.o: miniupnpc.c miniupnpc.h minisoap.h miniwget.h minixml.h 82 | 83 | igd_desc_parse.o: igd_desc_parse.c igd_desc_parse.h 84 | 85 | testminixml: minixml.o igd_desc_parse.o testminixml.c 86 | 87 | upnpreplyparse.o: upnpreplyparse.c upnpreplyparse.h minixml.h 88 | 89 | upnpcommands.o: upnpcommands.c upnpcommands.h upnpreplyparse.h miniupnpc.h portlistingparse.h 90 | 91 | -------------------------------------------------------------------------------- /libminiupnpc/README: -------------------------------------------------------------------------------- 1 | Project: miniupnp 2 | Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 3 | github: https://github.com/miniupnp/miniupnp 4 | freecode: http://freecode.com/projects/miniupnp 5 | Author: Thomas Bernard 6 | Copyright (c) 2005-2012 Thomas Bernard 7 | This software is subject to the conditions detailed in the 8 | LICENSE file provided within this distribution. 9 | 10 | 11 | For the comfort of Win32 users, bsdqueue.h is included in the distribution. 12 | Its licence is included in the header of the file. 13 | bsdqueue.h is a copy of the sys/queue.h of an OpenBSD system. 14 | 15 | 16 | * miniUPnP Client - miniUPnPc * 17 | 18 | To compile, simply run 'gmake' (could be 'make' on your system). 19 | Under win32, to compile with MinGW, type "mingw32make.bat". 20 | MS Visual C solution and project files are supplied in the msvc/ subdirectory. 21 | 22 | The compilation is known to work under linux, FreeBSD, 23 | OpenBSD, MacOS X, AmigaOS and cygwin. 24 | The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3. 25 | upx (http://upx.sourceforge.net) is used to compress the win32 .exe files. 26 | 27 | To install the library and headers on the system use : 28 | > su 29 | > make install 30 | > exit 31 | 32 | alternatively, to install into a specific location, use : 33 | > INSTALLPREFIX=/usr/local make install 34 | 35 | upnpc.c is a sample client using the libminiupnpc. 36 | To use the libminiupnpc in your application, link it with 37 | libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h, 38 | upnpcommands.h and miniwget.h : 39 | - upnpDiscover() 40 | - miniwget() 41 | - parserootdesc() 42 | - GetUPNPUrls() 43 | - UPNP_* (calling UPNP methods) 44 | 45 | Note : use #include etc... for the includes 46 | and -lminiupnpc for the link 47 | 48 | Discovery process is speeded up when MiniSSDPd is running on the machine. 49 | 50 | 51 | * Python module * 52 | 53 | you can build a python module with 'make pythonmodule' 54 | and install it with 'make installpythonmodule'. 55 | setup.py (and setupmingw32.py) are included in the distribution. 56 | 57 | 58 | Feel free to contact me if you have any problem : 59 | e-mail : miniupnp@free.fr 60 | 61 | If you are using libminiupnpc in your application, please 62 | send me an email ! 63 | 64 | For any question, you can use the web forum : 65 | http://miniupnp.tuxfamily.org/forum/ 66 | 67 | -------------------------------------------------------------------------------- /libminiupnpc/VERSION: -------------------------------------------------------------------------------- 1 | 1.8 2 | -------------------------------------------------------------------------------- /libminiupnpc/codelength.h: -------------------------------------------------------------------------------- 1 | /* $Id: codelength.h,v 1.4 2012/09/27 15:40:29 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas BERNARD 4 | * copyright (c) 2005-2011 Thomas Bernard 5 | * This software is subjet to the conditions detailed in the 6 | * provided LICENCE file. */ 7 | #ifndef CODELENGTH_H_INCLUDED 8 | #define CODELENGTH_H_INCLUDED 9 | 10 | /* Encode length by using 7bit per Byte : 11 | * Most significant bit of each byte specifies that the 12 | * following byte is part of the code */ 13 | #define DECODELENGTH(n, p) n = 0; \ 14 | do { n = (n << 7) | (*p & 0x7f); } \ 15 | while((*(p++)&0x80) && (n<(1<<25))); 16 | 17 | #define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \ 18 | n = 0; \ 19 | do { \ 20 | if((p) >= (p_limit)) break; \ 21 | n = (n << 7) | (*(p) & 0x7f); \ 22 | } while((*((p)++)&0x80) && (n<(1<<25))); 23 | 24 | #define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \ 25 | if(n>=2097152) *(p++) = (n >> 21) | 0x80; \ 26 | if(n>=16384) *(p++) = (n >> 14) | 0x80; \ 27 | if(n>=128) *(p++) = (n >> 7) | 0x80; \ 28 | *(p++) = n & 0x7f; 29 | 30 | #endif 31 | 32 | -------------------------------------------------------------------------------- /libminiupnpc/connecthostport.c: -------------------------------------------------------------------------------- 1 | /* $Id: connecthostport.c,v 1.11 2013/08/01 21:21:25 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2010-2013 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided in this distribution. */ 7 | 8 | /* use getaddrinfo() or gethostbyname() 9 | * uncomment the following line in order to use gethostbyname() */ 10 | #ifdef NO_GETADDRINFO 11 | #define USE_GETHOSTBYNAME 12 | #endif 13 | 14 | #include 15 | #include 16 | #ifdef _WIN32 17 | #include 18 | #include 19 | #include 20 | #define MAXHOSTNAMELEN 64 21 | #define snprintf _snprintf 22 | #define herror 23 | #define socklen_t int 24 | #else /* #ifdef _WIN32 */ 25 | #include 26 | #include 27 | #include 28 | #include 29 | #define closesocket close 30 | #include 31 | #include 32 | /* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions 33 | * during the connect() call */ 34 | #define MINIUPNPC_IGNORE_EINTR 35 | #ifndef USE_GETHOSTBYNAME 36 | #include 37 | #include 38 | #endif /* #ifndef USE_GETHOSTBYNAME */ 39 | #endif /* #else _WIN32 */ 40 | 41 | /* definition of PRINT_SOCKET_ERROR */ 42 | #ifdef _WIN32 43 | #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); 44 | #else 45 | #define PRINT_SOCKET_ERROR(x) perror(x) 46 | #endif 47 | 48 | #if defined(__amigaos__) || defined(__amigaos4__) 49 | #define herror(A) printf("%s\n", A) 50 | #endif 51 | 52 | #include "connecthostport.h" 53 | 54 | #ifndef MAXHOSTNAMELEN 55 | #define MAXHOSTNAMELEN 64 56 | #endif 57 | 58 | /* connecthostport() 59 | * return a socket connected (TCP) to the host and port 60 | * or -1 in case of error */ 61 | int connecthostport(const char * host, unsigned short port, 62 | unsigned int scope_id) 63 | { 64 | int s, n; 65 | #ifdef USE_GETHOSTBYNAME 66 | struct sockaddr_in dest; 67 | struct hostent *hp; 68 | #else /* #ifdef USE_GETHOSTBYNAME */ 69 | char tmp_host[MAXHOSTNAMELEN+1]; 70 | char port_str[8]; 71 | struct addrinfo *ai, *p; 72 | struct addrinfo hints; 73 | #endif /* #ifdef USE_GETHOSTBYNAME */ 74 | #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT 75 | struct timeval timeout; 76 | #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ 77 | 78 | #ifdef USE_GETHOSTBYNAME 79 | hp = gethostbyname(host); 80 | if(hp == NULL) 81 | { 82 | herror(host); 83 | return -1; 84 | } 85 | memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr)); 86 | memset(dest.sin_zero, 0, sizeof(dest.sin_zero)); 87 | s = socket(PF_INET, SOCK_STREAM, 0); 88 | if(s < 0) 89 | { 90 | PRINT_SOCKET_ERROR("socket"); 91 | return -1; 92 | } 93 | #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT 94 | /* setting a 3 seconds timeout for the connect() call */ 95 | timeout.tv_sec = 3; 96 | timeout.tv_usec = 0; 97 | if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) 98 | { 99 | PRINT_SOCKET_ERROR("setsockopt"); 100 | } 101 | timeout.tv_sec = 3; 102 | timeout.tv_usec = 0; 103 | if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) 104 | { 105 | PRINT_SOCKET_ERROR("setsockopt"); 106 | } 107 | #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ 108 | dest.sin_family = AF_INET; 109 | dest.sin_port = htons(port); 110 | n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in)); 111 | #ifdef MINIUPNPC_IGNORE_EINTR 112 | while(n < 0 && errno == EINTR) 113 | { 114 | socklen_t len; 115 | fd_set wset; 116 | int err; 117 | FD_ZERO(&wset); 118 | FD_SET(s, &wset); 119 | if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) 120 | continue; 121 | /*len = 0;*/ 122 | /*n = getpeername(s, NULL, &len);*/ 123 | len = sizeof(err); 124 | if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { 125 | PRINT_SOCKET_ERROR("getsockopt"); 126 | closesocket(s); 127 | return -1; 128 | } 129 | if(err != 0) { 130 | errno = err; 131 | n = -1; 132 | } 133 | } 134 | #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */ 135 | if(n<0) 136 | { 137 | PRINT_SOCKET_ERROR("connect"); 138 | closesocket(s); 139 | return -1; 140 | } 141 | #else /* #ifdef USE_GETHOSTBYNAME */ 142 | /* use getaddrinfo() instead of gethostbyname() */ 143 | memset(&hints, 0, sizeof(hints)); 144 | /* hints.ai_flags = AI_ADDRCONFIG; */ 145 | #ifdef AI_NUMERICSERV 146 | hints.ai_flags = AI_NUMERICSERV; 147 | #endif 148 | hints.ai_socktype = SOCK_STREAM; 149 | hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */ 150 | /* hints.ai_protocol = IPPROTO_TCP; */ 151 | snprintf(port_str, sizeof(port_str), "%hu", port); 152 | if(host[0] == '[') 153 | { 154 | /* literal ip v6 address */ 155 | int i, j; 156 | for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++) 157 | { 158 | tmp_host[i] = host[j]; 159 | if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */ 160 | j+=2; /* skip "25" */ 161 | } 162 | tmp_host[i] = '\0'; 163 | } 164 | else 165 | { 166 | strncpy(tmp_host, host, MAXHOSTNAMELEN); 167 | } 168 | tmp_host[MAXHOSTNAMELEN] = '\0'; 169 | n = getaddrinfo(tmp_host, port_str, &hints, &ai); 170 | if(n != 0) 171 | { 172 | #ifdef _WIN32 173 | fprintf(stderr, "getaddrinfo() error : %d\n", n); 174 | #else 175 | fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n)); 176 | #endif 177 | return -1; 178 | } 179 | s = -1; 180 | for(p = ai; p; p = p->ai_next) 181 | { 182 | s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); 183 | if(s < 0) 184 | continue; 185 | if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) { 186 | struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr; 187 | addr6->sin6_scope_id = scope_id; 188 | } 189 | #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT 190 | /* setting a 3 seconds timeout for the connect() call */ 191 | timeout.tv_sec = 3; 192 | timeout.tv_usec = 0; 193 | if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0) 194 | { 195 | PRINT_SOCKET_ERROR("setsockopt"); 196 | } 197 | timeout.tv_sec = 3; 198 | timeout.tv_usec = 0; 199 | if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0) 200 | { 201 | PRINT_SOCKET_ERROR("setsockopt"); 202 | } 203 | #endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */ 204 | n = connect(s, p->ai_addr, p->ai_addrlen); 205 | #ifdef MINIUPNPC_IGNORE_EINTR 206 | while(n < 0 && errno == EINTR) 207 | { 208 | socklen_t len; 209 | fd_set wset; 210 | int err; 211 | FD_ZERO(&wset); 212 | FD_SET(s, &wset); 213 | if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR) 214 | continue; 215 | /*len = 0;*/ 216 | /*n = getpeername(s, NULL, &len);*/ 217 | len = sizeof(err); 218 | if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) { 219 | PRINT_SOCKET_ERROR("getsockopt"); 220 | closesocket(s); 221 | freeaddrinfo(ai); 222 | return -1; 223 | } 224 | if(err != 0) { 225 | errno = err; 226 | n = -1; 227 | } 228 | } 229 | #endif /* #ifdef MINIUPNPC_IGNORE_EINTR */ 230 | if(n < 0) 231 | { 232 | closesocket(s); 233 | continue; 234 | } 235 | else 236 | { 237 | break; 238 | } 239 | } 240 | freeaddrinfo(ai); 241 | if(s < 0) 242 | { 243 | PRINT_SOCKET_ERROR("socket"); 244 | return -1; 245 | } 246 | if(n < 0) 247 | { 248 | PRINT_SOCKET_ERROR("connect"); 249 | return -1; 250 | } 251 | #endif /* #ifdef USE_GETHOSTBYNAME */ 252 | return s; 253 | } 254 | 255 | -------------------------------------------------------------------------------- /libminiupnpc/connecthostport.h: -------------------------------------------------------------------------------- 1 | /* $Id: connecthostport.h,v 1.3 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Project: miniupnp 3 | * http://miniupnp.free.fr/ 4 | * Author: Thomas Bernard 5 | * Copyright (c) 2010-2012 Thomas Bernard 6 | * This software is subjects to the conditions detailed 7 | * in the LICENCE file provided within this distribution */ 8 | #ifndef CONNECTHOSTPORT_H_INCLUDED 9 | #define CONNECTHOSTPORT_H_INCLUDED 10 | 11 | /* connecthostport() 12 | * return a socket connected (TCP) to the host and port 13 | * or -1 in case of error */ 14 | int connecthostport(const char * host, unsigned short port, 15 | unsigned int scope_id); 16 | 17 | #endif 18 | 19 | -------------------------------------------------------------------------------- /libminiupnpc/declspec.h: -------------------------------------------------------------------------------- 1 | #ifndef DECLSPEC_H_INCLUDED 2 | #define DECLSPEC_H_INCLUDED 3 | 4 | #if defined(_WIN32) && !defined(STATICLIB) 5 | #ifdef MINIUPNP_EXPORTS 6 | #define LIBSPEC __declspec(dllexport) 7 | #else 8 | #define LIBSPEC __declspec(dllimport) 9 | #endif 10 | #else 11 | #define LIBSPEC 12 | #endif 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /libminiupnpc/igd_desc_parse.c: -------------------------------------------------------------------------------- 1 | /* $Id: igd_desc_parse.c,v 1.14 2011/04/11 09:19:24 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * http://miniupnp.free.fr/ 4 | * Author : Thomas Bernard 5 | * Copyright (c) 2005-2010 Thomas Bernard 6 | * This software is subject to the conditions detailed in the 7 | * LICENCE file provided in this distribution. */ 8 | 9 | #include "igd_desc_parse.h" 10 | #include 11 | #include 12 | 13 | /* Start element handler : 14 | * update nesting level counter and copy element name */ 15 | void IGDstartelt(void * d, const char * name, int l) 16 | { 17 | struct IGDdatas * datas = (struct IGDdatas *)d; 18 | memcpy( datas->cureltname, name, l); 19 | datas->cureltname[l] = '\0'; 20 | datas->level++; 21 | if( (l==7) && !memcmp(name, "service", l) ) { 22 | datas->tmp.controlurl[0] = '\0'; 23 | datas->tmp.eventsuburl[0] = '\0'; 24 | datas->tmp.scpdurl[0] = '\0'; 25 | datas->tmp.servicetype[0] = '\0'; 26 | } 27 | } 28 | 29 | /* End element handler : 30 | * update nesting level counter and update parser state if 31 | * service element is parsed */ 32 | void IGDendelt(void * d, const char * name, int l) 33 | { 34 | struct IGDdatas * datas = (struct IGDdatas *)d; 35 | datas->level--; 36 | /*printf("endelt %2d %.*s\n", datas->level, l, name);*/ 37 | if( (l==7) && !memcmp(name, "service", l) ) 38 | { 39 | /* 40 | if( datas->state < 1 41 | && !strcmp(datas->servicetype, 42 | // "urn:schemas-upnp-org:service:WANIPConnection:1") ) 43 | "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) 44 | datas->state ++; 45 | */ 46 | if(0==strcmp(datas->tmp.servicetype, 47 | "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1")) { 48 | memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service)); 49 | } else if(0==strcmp(datas->tmp.servicetype, 50 | "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1")) { 51 | memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service)); 52 | } else if(0==strcmp(datas->tmp.servicetype, 53 | "urn:schemas-upnp-org:service:WANIPConnection:1") 54 | || 0==strcmp(datas->tmp.servicetype, 55 | "urn:schemas-upnp-org:service:WANPPPConnection:1") ) { 56 | if(datas->first.servicetype[0] == '\0') { 57 | memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service)); 58 | } else { 59 | memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service)); 60 | } 61 | } 62 | } 63 | } 64 | 65 | /* Data handler : 66 | * copy data depending on the current element name and state */ 67 | void IGDdata(void * d, const char * data, int l) 68 | { 69 | struct IGDdatas * datas = (struct IGDdatas *)d; 70 | char * dstmember = 0; 71 | /*printf("%2d %s : %.*s\n", 72 | datas->level, datas->cureltname, l, data); */ 73 | if( !strcmp(datas->cureltname, "URLBase") ) 74 | dstmember = datas->urlbase; 75 | else if( !strcmp(datas->cureltname, "presentationURL") ) 76 | dstmember = datas->presentationurl; 77 | else if( !strcmp(datas->cureltname, "serviceType") ) 78 | dstmember = datas->tmp.servicetype; 79 | else if( !strcmp(datas->cureltname, "controlURL") ) 80 | dstmember = datas->tmp.controlurl; 81 | else if( !strcmp(datas->cureltname, "eventSubURL") ) 82 | dstmember = datas->tmp.eventsuburl; 83 | else if( !strcmp(datas->cureltname, "SCPDURL") ) 84 | dstmember = datas->tmp.scpdurl; 85 | /* else if( !strcmp(datas->cureltname, "deviceType") ) 86 | dstmember = datas->devicetype_tmp;*/ 87 | if(dstmember) 88 | { 89 | if(l>=MINIUPNPC_URL_MAXSIZE) 90 | l = MINIUPNPC_URL_MAXSIZE-1; 91 | memcpy(dstmember, data, l); 92 | dstmember[l] = '\0'; 93 | } 94 | } 95 | 96 | void printIGD(struct IGDdatas * d) 97 | { 98 | printf("urlbase = '%s'\n", d->urlbase); 99 | printf("WAN Device (Common interface config) :\n"); 100 | /*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/ 101 | printf(" serviceType = '%s'\n", d->CIF.servicetype); 102 | printf(" controlURL = '%s'\n", d->CIF.controlurl); 103 | printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl); 104 | printf(" SCPDURL = '%s'\n", d->CIF.scpdurl); 105 | printf("primary WAN Connection Device (IP or PPP Connection):\n"); 106 | /*printf(" deviceType = '%s'\n", d->first.devicetype);*/ 107 | printf(" servicetype = '%s'\n", d->first.servicetype); 108 | printf(" controlURL = '%s'\n", d->first.controlurl); 109 | printf(" eventSubURL = '%s'\n", d->first.eventsuburl); 110 | printf(" SCPDURL = '%s'\n", d->first.scpdurl); 111 | printf("secondary WAN Connection Device (IP or PPP Connection):\n"); 112 | /*printf(" deviceType = '%s'\n", d->second.devicetype);*/ 113 | printf(" servicetype = '%s'\n", d->second.servicetype); 114 | printf(" controlURL = '%s'\n", d->second.controlurl); 115 | printf(" eventSubURL = '%s'\n", d->second.eventsuburl); 116 | printf(" SCPDURL = '%s'\n", d->second.scpdurl); 117 | printf("WAN IPv6 Firewall Control :\n"); 118 | /*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/ 119 | printf(" servicetype = '%s'\n", d->IPv6FC.servicetype); 120 | printf(" controlURL = '%s'\n", d->IPv6FC.controlurl); 121 | printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl); 122 | printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl); 123 | } 124 | 125 | 126 | -------------------------------------------------------------------------------- /libminiupnpc/igd_desc_parse.h: -------------------------------------------------------------------------------- 1 | /* $Id: igd_desc_parse.h,v 1.11 2012/10/16 16:49:02 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * http://miniupnp.free.fr/ 4 | * Author : Thomas Bernard 5 | * Copyright (c) 2005-2010 Thomas Bernard 6 | * This software is subject to the conditions detailed in the 7 | * LICENCE file provided in this distribution. 8 | * */ 9 | #ifndef IGD_DESC_PARSE_H_INCLUDED 10 | #define IGD_DESC_PARSE_H_INCLUDED 11 | 12 | /* Structure to store the result of the parsing of UPnP 13 | * descriptions of Internet Gateway Devices */ 14 | #define MINIUPNPC_URL_MAXSIZE (128) 15 | struct IGDdatas_service { 16 | char controlurl[MINIUPNPC_URL_MAXSIZE]; 17 | char eventsuburl[MINIUPNPC_URL_MAXSIZE]; 18 | char scpdurl[MINIUPNPC_URL_MAXSIZE]; 19 | char servicetype[MINIUPNPC_URL_MAXSIZE]; 20 | /*char devicetype[MINIUPNPC_URL_MAXSIZE];*/ 21 | }; 22 | 23 | struct IGDdatas { 24 | char cureltname[MINIUPNPC_URL_MAXSIZE]; 25 | char urlbase[MINIUPNPC_URL_MAXSIZE]; 26 | char presentationurl[MINIUPNPC_URL_MAXSIZE]; 27 | int level; 28 | /*int state;*/ 29 | /* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */ 30 | struct IGDdatas_service CIF; 31 | /* "urn:schemas-upnp-org:service:WANIPConnection:1" 32 | * "urn:schemas-upnp-org:service:WANPPPConnection:1" */ 33 | struct IGDdatas_service first; 34 | /* if both WANIPConnection and WANPPPConnection are present */ 35 | struct IGDdatas_service second; 36 | /* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */ 37 | struct IGDdatas_service IPv6FC; 38 | /* tmp */ 39 | struct IGDdatas_service tmp; 40 | }; 41 | 42 | void IGDstartelt(void *, const char *, int); 43 | void IGDendelt(void *, const char *, int); 44 | void IGDdata(void *, const char *, int); 45 | void printIGD(struct IGDdatas *); 46 | 47 | #endif 48 | 49 | -------------------------------------------------------------------------------- /libminiupnpc/minisoap.c: -------------------------------------------------------------------------------- 1 | /* $Id: minisoap.c,v 1.22 2012/01/21 13:30:31 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2005-2012 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided in this distribution. 7 | * 8 | * Minimal SOAP implementation for UPnP protocol. 9 | */ 10 | #include 11 | #include 12 | #ifdef _WIN32 13 | #include 14 | #include 15 | #define snprintf _snprintf 16 | #else 17 | #include 18 | #include 19 | #include 20 | #endif 21 | #include "minisoap.h" 22 | #include "miniupnpcstrings.h" 23 | 24 | /* only for malloc */ 25 | #include 26 | 27 | #ifdef _WIN32 28 | #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); 29 | #else 30 | #define PRINT_SOCKET_ERROR(x) perror(x) 31 | #endif 32 | 33 | /* httpWrite sends the headers and the body to the socket 34 | * and returns the number of bytes sent */ 35 | static int 36 | httpWrite(int fd, const char * body, int bodysize, 37 | const char * headers, int headerssize) 38 | { 39 | int n = 0; 40 | /*n = write(fd, headers, headerssize);*/ 41 | /*if(bodysize>0) 42 | n += write(fd, body, bodysize);*/ 43 | /* Note : my old linksys router only took into account 44 | * soap request that are sent into only one packet */ 45 | char * p; 46 | /* TODO: AVOID MALLOC */ 47 | p = malloc(headerssize+bodysize); 48 | if(!p) 49 | return 0; 50 | memcpy(p, headers, headerssize); 51 | memcpy(p+headerssize, body, bodysize); 52 | /*n = write(fd, p, headerssize+bodysize);*/ 53 | n = send(fd, p, headerssize+bodysize, 0); 54 | if(n<0) { 55 | PRINT_SOCKET_ERROR("send"); 56 | } 57 | /* disable send on the socket */ 58 | /* draytek routers dont seems to like that... */ 59 | #if 0 60 | #ifdef _WIN32 61 | if(shutdown(fd, SD_SEND)<0) { 62 | #else 63 | if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/ 64 | #endif 65 | PRINT_SOCKET_ERROR("shutdown"); 66 | } 67 | #endif 68 | free(p); 69 | return n; 70 | } 71 | 72 | /* self explanatory */ 73 | int soapPostSubmit(int fd, 74 | const char * url, 75 | const char * host, 76 | unsigned short port, 77 | const char * action, 78 | const char * body, 79 | const char * httpversion) 80 | { 81 | int bodysize; 82 | char headerbuf[512]; 83 | int headerssize; 84 | char portstr[8]; 85 | bodysize = (int)strlen(body); 86 | /* We are not using keep-alive HTTP connections. 87 | * HTTP/1.1 needs the header Connection: close to do that. 88 | * This is the default with HTTP/1.0 89 | * Using HTTP/1.1 means we need to support chunked transfer-encoding : 90 | * When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked 91 | * transfer encoding. */ 92 | /* Connection: Close is normally there only in HTTP/1.1 but who knows */ 93 | portstr[0] = '\0'; 94 | if(port != 80) 95 | snprintf(portstr, sizeof(portstr), ":%hu", port); 96 | headerssize = snprintf(headerbuf, sizeof(headerbuf), 97 | "POST %s HTTP/%s\r\n" 98 | "Host: %s%s\r\n" 99 | "User-Agent: " OS_STRING ", UPnP/1.0, MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n" 100 | "Content-Length: %d\r\n" 101 | "Content-Type: text/xml\r\n" 102 | "SOAPAction: \"%s\"\r\n" 103 | "Connection: Close\r\n" 104 | "Cache-Control: no-cache\r\n" /* ??? */ 105 | "Pragma: no-cache\r\n" 106 | "\r\n", 107 | url, httpversion, host, portstr, bodysize, action); 108 | #ifdef DEBUG 109 | /*printf("SOAP request : headersize=%d bodysize=%d\n", 110 | headerssize, bodysize); 111 | */ 112 | printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n", 113 | url, httpversion, host, portstr); 114 | printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize); 115 | printf("Headers :\n%s", headerbuf); 116 | printf("Body :\n%s\n", body); 117 | #endif 118 | return httpWrite(fd, body, bodysize, headerbuf, headerssize); 119 | } 120 | 121 | 122 | -------------------------------------------------------------------------------- /libminiupnpc/minisoap.h: -------------------------------------------------------------------------------- 1 | /* $Id: minisoap.h,v 1.5 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2005 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided in this distribution. */ 7 | #ifndef MINISOAP_H_INCLUDED 8 | #define MINISOAP_H_INCLUDED 9 | 10 | /*int httpWrite(int, const char *, int, const char *);*/ 11 | int soapPostSubmit(int, const char *, const char *, unsigned short, 12 | const char *, const char *, const char *); 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /libminiupnpc/minissdpc.c: -------------------------------------------------------------------------------- 1 | /* $Id: minissdpc.c,v 1.16 2012/03/05 19:42:46 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Web : http://miniupnp.free.fr/ 4 | * Author : Thomas BERNARD 5 | * copyright (c) 2005-2012 Thomas Bernard 6 | * This software is subjet to the conditions detailed in the 7 | * provided LICENCE file. */ 8 | /*#include */ 9 | #include 10 | #include 11 | #include 12 | 13 | #ifndef _WIN32 14 | #include 15 | #include 16 | #endif 17 | 18 | #if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) 19 | #ifdef _WIN32 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | typedef signed long ssize_t; 26 | #endif 27 | #if defined(__amigaos__) || defined(__amigaos4__) 28 | #include 29 | #endif 30 | #if defined(__amigaos__) 31 | #define uint16_t unsigned short 32 | #endif 33 | /* Hack */ 34 | #define UNIX_PATH_LEN 108 35 | struct sockaddr_un { 36 | uint16_t sun_family; 37 | char sun_path[UNIX_PATH_LEN]; 38 | }; 39 | #else 40 | #include 41 | #include 42 | #endif 43 | 44 | #include "minissdpc.h" 45 | #include "miniupnpc.h" 46 | 47 | #include "codelength.h" 48 | 49 | struct UPNPDev * 50 | getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath) 51 | { 52 | struct UPNPDev * tmp; 53 | struct UPNPDev * devlist = NULL; 54 | unsigned char buffer[2048]; 55 | ssize_t n; 56 | unsigned char * p; 57 | unsigned char * url; 58 | unsigned int i; 59 | unsigned int urlsize, stsize, usnsize, l; 60 | int s; 61 | struct sockaddr_un addr; 62 | 63 | // is socket path null-terminated? 64 | if (!memchr(socketpath, '\0', sizeof(addr.sun_path))) 65 | return NULL; 66 | s = socket(AF_UNIX, SOCK_STREAM, 0); 67 | if(s < 0) 68 | { 69 | /*syslog(LOG_ERR, "socket(unix): %m");*/ 70 | perror("socket(unix)"); 71 | return NULL; 72 | } 73 | addr.sun_family = AF_UNIX; 74 | strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path)); 75 | /* TODO : check if we need to handle the EINTR */ 76 | if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) 77 | { 78 | /*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/ 79 | close(s); 80 | return NULL; 81 | } 82 | stsize = strlen(devtype); 83 | buffer[0] = 1; /* request type 1 : request devices/services by type */ 84 | p = buffer + 1; 85 | l = stsize; CODELENGTH(l, p); 86 | if(p + stsize > buffer + sizeof(buffer)) 87 | { 88 | /* devtype is too long ! */ 89 | close(s); 90 | return NULL; 91 | } 92 | memcpy(p, devtype, stsize); 93 | p += stsize; 94 | if(write(s, buffer, p - buffer) < 0) 95 | { 96 | /*syslog(LOG_ERR, "write(): %m");*/ 97 | perror("minissdpc.c: write()"); 98 | close(s); 99 | return NULL; 100 | } 101 | n = read(s, buffer, sizeof(buffer)); 102 | if(n<=0) 103 | { 104 | perror("minissdpc.c: read()"); 105 | close(s); 106 | return NULL; 107 | } 108 | p = buffer + 1; 109 | for(i = 0; i < buffer[0]; i++) 110 | { 111 | if(p+2>=buffer+sizeof(buffer)) 112 | break; 113 | DECODELENGTH(urlsize, p); 114 | if(p+urlsize+2>=buffer+sizeof(buffer)) 115 | break; 116 | url = p; 117 | p += urlsize; 118 | DECODELENGTH(stsize, p); 119 | if(p+stsize+2>=buffer+sizeof(buffer)) 120 | break; 121 | tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize); 122 | tmp->pNext = devlist; 123 | tmp->descURL = tmp->buffer; 124 | tmp->st = tmp->buffer + 1 + urlsize; 125 | memcpy(tmp->buffer, url, urlsize); 126 | tmp->buffer[urlsize] = '\0'; 127 | memcpy(tmp->buffer + urlsize + 1, p, stsize); 128 | p += stsize; 129 | tmp->buffer[urlsize+1+stsize] = '\0'; 130 | devlist = tmp; 131 | /* added for compatibility with recent versions of MiniSSDPd 132 | * >= 2007/12/19 */ 133 | DECODELENGTH(usnsize, p); 134 | p += usnsize; 135 | if(p>buffer + sizeof(buffer)) 136 | break; 137 | } 138 | close(s); 139 | return devlist; 140 | } 141 | 142 | -------------------------------------------------------------------------------- /libminiupnpc/minissdpc.h: -------------------------------------------------------------------------------- 1 | /* $Id: minissdpc.h,v 1.2 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Project: miniupnp 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * Author: Thomas Bernard 5 | * Copyright (c) 2005-2007 Thomas Bernard 6 | * This software is subjects to the conditions detailed 7 | * in the LICENCE file provided within this distribution */ 8 | #ifndef MINISSDPC_H_INCLUDED 9 | #define MINISSDPC_H_INCLUDED 10 | 11 | struct UPNPDev * 12 | getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath); 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /libminiupnpc/miniupnpc.def: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | ; miniupnpc library 3 | 4 | EXPORTS 5 | ; miniupnpc 6 | upnpDiscover 7 | freeUPNPDevlist 8 | parserootdesc 9 | UPNP_GetValidIGD 10 | UPNP_GetIGDFromUrl 11 | GetUPNPUrls 12 | FreeUPNPUrls 13 | ; miniwget 14 | miniwget 15 | miniwget_getaddr 16 | ; upnpcommands 17 | UPNP_GetTotalBytesSent 18 | UPNP_GetTotalBytesReceived 19 | UPNP_GetTotalPacketsSent 20 | UPNP_GetTotalPacketsReceived 21 | UPNP_GetStatusInfo 22 | UPNP_GetConnectionTypeInfo 23 | UPNP_GetExternalIPAddress 24 | UPNP_GetLinkLayerMaxBitRates 25 | UPNP_AddPortMapping 26 | UPNP_DeletePortMapping 27 | UPNP_GetPortMappingNumberOfEntries 28 | UPNP_GetSpecificPortMappingEntry 29 | UPNP_GetGenericPortMappingEntry 30 | UPNP_GetListOfPortMappings 31 | UPNP_AddPinhole 32 | UPNP_CheckPinholeWorking 33 | UPNP_UpdatePinhole 34 | UPNP_GetPinholePackets 35 | UPNP_DeletePinhole 36 | UPNP_GetFirewallStatus 37 | UPNP_GetOutboundPinholeTimeout 38 | ; upnperrors 39 | strupnperror 40 | ; portlistingparse 41 | ParsePortListing 42 | FreePortListing 43 | -------------------------------------------------------------------------------- /libminiupnpc/miniupnpc.h: -------------------------------------------------------------------------------- 1 | /* $Id: miniupnpc.h,v 1.32 2013/02/06 14:44:42 nanard Exp $ */ 2 | /* Project: miniupnp 3 | * http://miniupnp.free.fr/ 4 | * Author: Thomas Bernard 5 | * Copyright (c) 2005-2012 Thomas Bernard 6 | * This software is subjects to the conditions detailed 7 | * in the LICENCE file provided within this distribution */ 8 | #ifndef MINIUPNPC_H_INCLUDED 9 | #define MINIUPNPC_H_INCLUDED 10 | 11 | #include "declspec.h" 12 | #include "igd_desc_parse.h" 13 | 14 | /* error codes : */ 15 | #define UPNPDISCOVER_SUCCESS (0) 16 | #define UPNPDISCOVER_UNKNOWN_ERROR (-1) 17 | #define UPNPDISCOVER_SOCKET_ERROR (-101) 18 | #define UPNPDISCOVER_MEMORY_ERROR (-102) 19 | 20 | /* versions : */ 21 | #define MINIUPNPC_VERSION "1.8.20130801" 22 | #define MINIUPNPC_API_VERSION 9 23 | 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | /* Structures definitions : */ 29 | struct UPNParg { const char * elt; const char * val; }; 30 | 31 | char * 32 | simpleUPnPcommand(int, const char *, const char *, 33 | const char *, struct UPNParg *, 34 | int *); 35 | 36 | struct UPNPDev { 37 | struct UPNPDev * pNext; 38 | char * descURL; 39 | char * st; 40 | unsigned int scope_id; 41 | char buffer[2]; 42 | }; 43 | 44 | /* upnpDiscover() 45 | * discover UPnP devices on the network. 46 | * The discovered devices are returned as a chained list. 47 | * It is up to the caller to free the list with freeUPNPDevlist(). 48 | * delay (in millisecond) is the maximum time for waiting any device 49 | * response. 50 | * If available, device list will be obtained from MiniSSDPd. 51 | * Default path for minissdpd socket will be used if minissdpdsock argument 52 | * is NULL. 53 | * If multicastif is not NULL, it will be used instead of the default 54 | * multicast interface for sending SSDP discover packets. 55 | * If sameport is not null, SSDP packets will be sent from the source port 56 | * 1900 (same as destination port) otherwise system assign a source port. */ 57 | LIBSPEC struct UPNPDev * 58 | upnpDiscover(int delay, const char * multicastif, 59 | const char * minissdpdsock, int sameport, 60 | int ipv6, 61 | int * error); 62 | /* freeUPNPDevlist() 63 | * free list returned by upnpDiscover() */ 64 | LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist); 65 | 66 | /* parserootdesc() : 67 | * parse root XML description of a UPnP device and fill the IGDdatas 68 | * structure. */ 69 | LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *); 70 | 71 | /* structure used to get fast access to urls 72 | * controlURL: controlURL of the WANIPConnection 73 | * ipcondescURL: url of the description of the WANIPConnection 74 | * controlURL_CIF: controlURL of the WANCommonInterfaceConfig 75 | * controlURL_6FC: controlURL of the WANIPv6FirewallControl 76 | */ 77 | struct UPNPUrls { 78 | char * controlURL; 79 | char * ipcondescURL; 80 | char * controlURL_CIF; 81 | char * controlURL_6FC; 82 | char * rootdescURL; 83 | }; 84 | 85 | /* UPNP_GetValidIGD() : 86 | * return values : 87 | * 0 = NO IGD found 88 | * 1 = A valid connected IGD has been found 89 | * 2 = A valid IGD has been found but it reported as 90 | * not connected 91 | * 3 = an UPnP device has been found but was not recognized as an IGD 92 | * 93 | * In any non zero return case, the urls and data structures 94 | * passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to 95 | * free allocated memory. 96 | */ 97 | LIBSPEC int 98 | UPNP_GetValidIGD(struct UPNPDev * devlist, 99 | struct UPNPUrls * urls, 100 | struct IGDdatas * data, 101 | char * lanaddr, int lanaddrlen); 102 | 103 | /* UPNP_GetIGDFromUrl() 104 | * Used when skipping the discovery process. 105 | * return value : 106 | * 0 - Not ok 107 | * 1 - OK */ 108 | LIBSPEC int 109 | UPNP_GetIGDFromUrl(const char * rootdescurl, 110 | struct UPNPUrls * urls, 111 | struct IGDdatas * data, 112 | char * lanaddr, int lanaddrlen); 113 | 114 | LIBSPEC void 115 | GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *, 116 | const char *, unsigned int); 117 | 118 | LIBSPEC void 119 | FreeUPNPUrls(struct UPNPUrls *); 120 | 121 | /* return 0 or 1 */ 122 | LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *); 123 | 124 | 125 | #ifdef __cplusplus 126 | } 127 | #endif 128 | 129 | #endif 130 | 131 | -------------------------------------------------------------------------------- /libminiupnpc/miniupnpcstrings.h: -------------------------------------------------------------------------------- 1 | /* $Id: miniupnpcstrings.h.in,v 1.5 2012/10/16 16:48:26 nanard Exp $ */ 2 | /* Project: miniupnp 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * Author: Thomas Bernard 5 | * Copyright (c) 2005-2011 Thomas Bernard 6 | * This software is subjects to the conditions detailed 7 | * in the LICENCE file provided within this distribution */ 8 | #ifndef MINIUPNPCSTRINGS_H_INCLUDED 9 | #define MINIUPNPCSTRINGS_H_INCLUDED 10 | 11 | #define OS_STRING "OS/version" 12 | #define MINIUPNPC_VERSION_STRING "version" 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /libminiupnpc/miniupnpctypes.h: -------------------------------------------------------------------------------- 1 | /* $Id: miniupnpctypes.h,v 1.2 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2011 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided within this distribution */ 7 | #ifndef MINIUPNPCTYPES_H_INCLUDED 8 | #define MINIUPNPCTYPES_H_INCLUDED 9 | 10 | #if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) 11 | #define UNSIGNED_INTEGER unsigned long long 12 | #define STRTOUI strtoull 13 | #else 14 | #define UNSIGNED_INTEGER unsigned int 15 | #define STRTOUI strtoul 16 | #endif 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /libminiupnpc/miniwget.h: -------------------------------------------------------------------------------- 1 | /* $Id: miniwget.h,v 1.8 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2005-2012 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided in this distribution. 7 | * */ 8 | #ifndef MINIWGET_H_INCLUDED 9 | #define MINIWGET_H_INCLUDED 10 | 11 | #include "declspec.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | LIBSPEC void * getHTTPResponse(int s, int * size); 18 | 19 | LIBSPEC void * miniwget(const char *, int *, unsigned int); 20 | 21 | LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int); 22 | 23 | int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /libminiupnpc/minixml.c: -------------------------------------------------------------------------------- 1 | /* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */ 2 | /* minixml.c : the minimum size a xml parser can be ! */ 3 | /* Project : miniupnp 4 | * webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 5 | * Author : Thomas Bernard 6 | 7 | Copyright (c) 2005-2011, Thomas BERNARD 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | * The name of the author may not be used to endorse or promote products 19 | derived from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 25 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | #include 34 | #include "minixml.h" 35 | 36 | /* parseatt : used to parse the argument list 37 | * return 0 (false) in case of success and -1 (true) if the end 38 | * of the xmlbuffer is reached. */ 39 | static int parseatt(struct xmlparser * p) 40 | { 41 | const char * attname; 42 | int attnamelen; 43 | const char * attvalue; 44 | int attvaluelen; 45 | while(p->xml < p->xmlend) 46 | { 47 | if(*p->xml=='/' || *p->xml=='>') 48 | return 0; 49 | if( !IS_WHITE_SPACE(*p->xml) ) 50 | { 51 | char sep; 52 | attname = p->xml; 53 | attnamelen = 0; 54 | while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) ) 55 | { 56 | attnamelen++; p->xml++; 57 | if(p->xml >= p->xmlend) 58 | return -1; 59 | } 60 | while(*(p->xml++) != '=') 61 | { 62 | if(p->xml >= p->xmlend) 63 | return -1; 64 | } 65 | while(IS_WHITE_SPACE(*p->xml)) 66 | { 67 | p->xml++; 68 | if(p->xml >= p->xmlend) 69 | return -1; 70 | } 71 | sep = *p->xml; 72 | if(sep=='\'' || sep=='\"') 73 | { 74 | p->xml++; 75 | if(p->xml >= p->xmlend) 76 | return -1; 77 | attvalue = p->xml; 78 | attvaluelen = 0; 79 | while(*p->xml != sep) 80 | { 81 | attvaluelen++; p->xml++; 82 | if(p->xml >= p->xmlend) 83 | return -1; 84 | } 85 | } 86 | else 87 | { 88 | attvalue = p->xml; 89 | attvaluelen = 0; 90 | while( !IS_WHITE_SPACE(*p->xml) 91 | && *p->xml != '>' && *p->xml != '/') 92 | { 93 | attvaluelen++; p->xml++; 94 | if(p->xml >= p->xmlend) 95 | return -1; 96 | } 97 | } 98 | /*printf("%.*s='%.*s'\n", 99 | attnamelen, attname, attvaluelen, attvalue);*/ 100 | if(p->attfunc) 101 | p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen); 102 | } 103 | p->xml++; 104 | } 105 | return -1; 106 | } 107 | 108 | /* parseelt parse the xml stream and 109 | * call the callback functions when needed... */ 110 | static void parseelt(struct xmlparser * p) 111 | { 112 | int i; 113 | const char * elementname; 114 | while(p->xml < (p->xmlend - 1)) 115 | { 116 | if((p->xml)[0]=='<' && (p->xml)[1]!='?') 117 | { 118 | i = 0; elementname = ++p->xml; 119 | while( !IS_WHITE_SPACE(*p->xml) 120 | && (*p->xml!='>') && (*p->xml!='/') 121 | ) 122 | { 123 | i++; p->xml++; 124 | if (p->xml >= p->xmlend) 125 | return; 126 | /* to ignore namespace : */ 127 | if(*p->xml==':') 128 | { 129 | i = 0; 130 | elementname = ++p->xml; 131 | } 132 | } 133 | if(i>0) 134 | { 135 | if(p->starteltfunc) 136 | p->starteltfunc(p->data, elementname, i); 137 | if(parseatt(p)) 138 | return; 139 | if(*p->xml!='/') 140 | { 141 | const char * data; 142 | i = 0; data = ++p->xml; 143 | if (p->xml >= p->xmlend) 144 | return; 145 | while( IS_WHITE_SPACE(*p->xml) ) 146 | { 147 | i++; p->xml++; 148 | if (p->xml >= p->xmlend) 149 | return; 150 | } 151 | if(memcmp(p->xml, "xml += 9; 155 | data = p->xml; 156 | i = 0; 157 | while(memcmp(p->xml, "]]>", 3) != 0) 158 | { 159 | i++; p->xml++; 160 | if ((p->xml + 3) >= p->xmlend) 161 | return; 162 | } 163 | if(i>0 && p->datafunc) 164 | p->datafunc(p->data, data, i); 165 | while(*p->xml!='<') 166 | { 167 | p->xml++; 168 | if (p->xml >= p->xmlend) 169 | return; 170 | } 171 | } 172 | else 173 | { 174 | while(*p->xml!='<') 175 | { 176 | i++; p->xml++; 177 | if ((p->xml + 1) >= p->xmlend) 178 | return; 179 | } 180 | if(i>0 && p->datafunc && *(p->xml + 1) == '/') 181 | p->datafunc(p->data, data, i); 182 | } 183 | } 184 | } 185 | else if(*p->xml == '/') 186 | { 187 | i = 0; elementname = ++p->xml; 188 | if (p->xml >= p->xmlend) 189 | return; 190 | while((*p->xml != '>')) 191 | { 192 | i++; p->xml++; 193 | if (p->xml >= p->xmlend) 194 | return; 195 | } 196 | if(p->endeltfunc) 197 | p->endeltfunc(p->data, elementname, i); 198 | p->xml++; 199 | } 200 | } 201 | else 202 | { 203 | p->xml++; 204 | } 205 | } 206 | } 207 | 208 | /* the parser must be initialized before calling this function */ 209 | void parsexml(struct xmlparser * parser) 210 | { 211 | parser->xml = parser->xmlstart; 212 | parser->xmlend = parser->xmlstart + parser->xmlsize; 213 | parseelt(parser); 214 | } 215 | 216 | 217 | -------------------------------------------------------------------------------- /libminiupnpc/minixml.h: -------------------------------------------------------------------------------- 1 | /* $Id: minixml.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* minimal xml parser 3 | * 4 | * Project : miniupnp 5 | * Website : http://miniupnp.free.fr/ 6 | * Author : Thomas Bernard 7 | * Copyright (c) 2005 Thomas Bernard 8 | * This software is subject to the conditions detailed in the 9 | * LICENCE file provided in this distribution. 10 | * */ 11 | #ifndef MINIXML_H_INCLUDED 12 | #define MINIXML_H_INCLUDED 13 | #define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n')) 14 | 15 | /* if a callback function pointer is set to NULL, 16 | * the function is not called */ 17 | struct xmlparser { 18 | const char *xmlstart; 19 | const char *xmlend; 20 | const char *xml; /* pointer to current character */ 21 | int xmlsize; 22 | void * data; 23 | void (*starteltfunc) (void *, const char *, int); 24 | void (*endeltfunc) (void *, const char *, int); 25 | void (*datafunc) (void *, const char *, int); 26 | void (*attfunc) (void *, const char *, int, const char *, int); 27 | }; 28 | 29 | /* parsexml() 30 | * the xmlparser structure must be initialized before the call 31 | * the following structure members have to be initialized : 32 | * xmlstart, xmlsize, data, *func 33 | * xml is for internal usage, xmlend is computed automatically */ 34 | void parsexml(struct xmlparser *); 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /libminiupnpc/minixmlvalid.c: -------------------------------------------------------------------------------- 1 | /* $Id: minixmlvalid.c,v 1.6 2012/05/01 16:24:07 nanard Exp $ */ 2 | /* MiniUPnP Project 3 | * http://miniupnp.tuxfamily.org/ or http://miniupnp.free.fr/ 4 | * minixmlvalid.c : 5 | * validation program for the minixml parser 6 | * 7 | * (c) 2006-2011 Thomas Bernard */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include "minixml.h" 13 | 14 | /* xml event structure */ 15 | struct event { 16 | enum { ELTSTART, ELTEND, ATT, CHARDATA } type; 17 | const char * data; 18 | int len; 19 | }; 20 | 21 | struct eventlist { 22 | int n; 23 | struct event * events; 24 | }; 25 | 26 | /* compare 2 xml event lists 27 | * return 0 if the two lists are equals */ 28 | int evtlistcmp(struct eventlist * a, struct eventlist * b) 29 | { 30 | int i; 31 | struct event * ae, * be; 32 | if(a->n != b->n) 33 | { 34 | printf("event number not matching : %d != %d\n", a->n, b->n); 35 | /*return 1;*/ 36 | } 37 | for(i=0; in; i++) 38 | { 39 | ae = a->events + i; 40 | be = b->events + i; 41 | if( (ae->type != be->type) 42 | ||(ae->len != be->len) 43 | ||memcmp(ae->data, be->data, ae->len)) 44 | { 45 | printf("Found a difference : %d '%.*s' != %d '%.*s'\n", 46 | ae->type, ae->len, ae->data, 47 | be->type, be->len, be->data); 48 | return 1; 49 | } 50 | } 51 | return 0; 52 | } 53 | 54 | /* Test data */ 55 | static const char xmldata[] = 56 | "\n" 57 | " " 58 | "character data" 59 | " \n \t" 60 | "" 61 | "\nstuff !\n ]]> \n\n" 62 | " \tchardata1 chardata2 " 63 | ""; 64 | 65 | static const struct event evtref[] = 66 | { 67 | {ELTSTART, "xmlroot", 7}, 68 | {ELTSTART, "elt1", 4}, 69 | /* attributes */ 70 | {CHARDATA, "character data", 14}, 71 | {ELTEND, "elt1", 4}, 72 | {ELTSTART, "elt1b", 5}, 73 | {ELTSTART, "elt1", 4}, 74 | {CHARDATA, " stuff !\n ", 16}, 75 | {ELTEND, "elt1", 4}, 76 | {ELTSTART, "elt2a", 5}, 77 | {ELTSTART, "elt2b", 5}, 78 | {CHARDATA, "chardata1", 9}, 79 | {ELTEND, "elt2b", 5}, 80 | {ELTSTART, "elt2b", 5}, 81 | {CHARDATA, " chardata2 ", 11}, 82 | {ELTEND, "elt2b", 5}, 83 | {ELTEND, "elt2a", 5}, 84 | {ELTEND, "xmlroot", 7} 85 | }; 86 | 87 | void startelt(void * data, const char * p, int l) 88 | { 89 | struct eventlist * evtlist = data; 90 | struct event * evt; 91 | evt = evtlist->events + evtlist->n; 92 | /*printf("startelt : %.*s\n", l, p);*/ 93 | evt->type = ELTSTART; 94 | evt->data = p; 95 | evt->len = l; 96 | evtlist->n++; 97 | } 98 | 99 | void endelt(void * data, const char * p, int l) 100 | { 101 | struct eventlist * evtlist = data; 102 | struct event * evt; 103 | evt = evtlist->events + evtlist->n; 104 | /*printf("endelt : %.*s\n", l, p);*/ 105 | evt->type = ELTEND; 106 | evt->data = p; 107 | evt->len = l; 108 | evtlist->n++; 109 | } 110 | 111 | void chardata(void * data, const char * p, int l) 112 | { 113 | struct eventlist * evtlist = data; 114 | struct event * evt; 115 | evt = evtlist->events + evtlist->n; 116 | /*printf("chardata : '%.*s'\n", l, p);*/ 117 | evt->type = CHARDATA; 118 | evt->data = p; 119 | evt->len = l; 120 | evtlist->n++; 121 | } 122 | 123 | int testxmlparser(const char * xml, int size) 124 | { 125 | int r; 126 | struct eventlist evtlist; 127 | struct eventlist evtlistref; 128 | struct xmlparser parser; 129 | evtlist.n = 0; 130 | evtlist.events = malloc(sizeof(struct event)*100); 131 | memset(&parser, 0, sizeof(parser)); 132 | parser.xmlstart = xml; 133 | parser.xmlsize = size; 134 | parser.data = &evtlist; 135 | parser.starteltfunc = startelt; 136 | parser.endeltfunc = endelt; 137 | parser.datafunc = chardata; 138 | parsexml(&parser); 139 | printf("%d events\n", evtlist.n); 140 | /* compare */ 141 | evtlistref.n = sizeof(evtref)/sizeof(struct event); 142 | evtlistref.events = (struct event *)evtref; 143 | r = evtlistcmp(&evtlistref, &evtlist); 144 | free(evtlist.events); 145 | return r; 146 | } 147 | 148 | int main(int argc, char * * argv) 149 | { 150 | int r; 151 | (void)argc; (void)argv; 152 | 153 | r = testxmlparser(xmldata, sizeof(xmldata)-1); 154 | if(r) 155 | printf("minixml validation test failed\n"); 156 | return r; 157 | } 158 | 159 | -------------------------------------------------------------------------------- /libminiupnpc/portlistingparse.c: -------------------------------------------------------------------------------- 1 | /* $Id: portlistingparse.c,v 1.6 2012/05/29 10:26:51 nanard Exp $ */ 2 | /* MiniUPnP project 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * (c) 2011 Thomas Bernard 5 | * This software is subject to the conditions detailed 6 | * in the LICENCE file provided within the distribution */ 7 | #include 8 | #include 9 | #include "portlistingparse.h" 10 | #include "minixml.h" 11 | 12 | /* list of the elements */ 13 | static const struct { 14 | const portMappingElt code; 15 | const char * const str; 16 | } elements[] = { 17 | { PortMappingEntry, "PortMappingEntry"}, 18 | { NewRemoteHost, "NewRemoteHost"}, 19 | { NewExternalPort, "NewExternalPort"}, 20 | { NewProtocol, "NewProtocol"}, 21 | { NewInternalPort, "NewInternalPort"}, 22 | { NewInternalClient, "NewInternalClient"}, 23 | { NewEnabled, "NewEnabled"}, 24 | { NewDescription, "NewDescription"}, 25 | { NewLeaseTime, "NewLeaseTime"}, 26 | { PortMappingEltNone, NULL} 27 | }; 28 | 29 | /* Helper function */ 30 | static UNSIGNED_INTEGER 31 | atoui(const char * p, int l) 32 | { 33 | UNSIGNED_INTEGER r = 0; 34 | while(l > 0 && *p) 35 | { 36 | if(*p >= '0' && *p <= '9') 37 | r = r*10 + (*p - '0'); 38 | else 39 | break; 40 | p++; 41 | l--; 42 | } 43 | return r; 44 | } 45 | 46 | /* Start element handler */ 47 | static void 48 | startelt(void * d, const char * name, int l) 49 | { 50 | int i; 51 | struct PortMappingParserData * pdata = (struct PortMappingParserData *)d; 52 | pdata->curelt = PortMappingEltNone; 53 | for(i = 0; elements[i].str; i++) 54 | { 55 | if(memcmp(name, elements[i].str, l) == 0) 56 | { 57 | pdata->curelt = elements[i].code; 58 | break; 59 | } 60 | } 61 | if(pdata->curelt == PortMappingEntry) 62 | { 63 | struct PortMapping * pm; 64 | pm = calloc(1, sizeof(struct PortMapping)); 65 | LIST_INSERT_HEAD( &(pdata->head), pm, entries); 66 | } 67 | } 68 | 69 | /* End element handler */ 70 | static void 71 | endelt(void * d, const char * name, int l) 72 | { 73 | struct PortMappingParserData * pdata = (struct PortMappingParserData *)d; 74 | (void)name; 75 | (void)l; 76 | pdata->curelt = PortMappingEltNone; 77 | } 78 | 79 | /* Data handler */ 80 | static void 81 | data(void * d, const char * data, int l) 82 | { 83 | struct PortMapping * pm; 84 | struct PortMappingParserData * pdata = (struct PortMappingParserData *)d; 85 | pm = pdata->head.lh_first; 86 | if(!pm) 87 | return; 88 | if(l > 63) 89 | l = 63; 90 | switch(pdata->curelt) 91 | { 92 | case NewRemoteHost: 93 | memcpy(pm->remoteHost, data, l); 94 | pm->remoteHost[l] = '\0'; 95 | break; 96 | case NewExternalPort: 97 | pm->externalPort = (unsigned short)atoui(data, l); 98 | break; 99 | case NewProtocol: 100 | if(l > 3) 101 | l = 3; 102 | memcpy(pm->protocol, data, l); 103 | pm->protocol[l] = '\0'; 104 | break; 105 | case NewInternalPort: 106 | pm->internalPort = (unsigned short)atoui(data, l); 107 | break; 108 | case NewInternalClient: 109 | memcpy(pm->internalClient, data, l); 110 | pm->internalClient[l] = '\0'; 111 | break; 112 | case NewEnabled: 113 | pm->enabled = (unsigned char)atoui(data, l); 114 | break; 115 | case NewDescription: 116 | memcpy(pm->description, data, l); 117 | pm->description[l] = '\0'; 118 | break; 119 | case NewLeaseTime: 120 | pm->leaseTime = atoui(data, l); 121 | break; 122 | default: 123 | break; 124 | } 125 | } 126 | 127 | 128 | /* Parse the PortMappingList XML document for IGD version 2 129 | */ 130 | void 131 | ParsePortListing(const char * buffer, int bufsize, 132 | struct PortMappingParserData * pdata) 133 | { 134 | struct xmlparser parser; 135 | 136 | memset(pdata, 0, sizeof(struct PortMappingParserData)); 137 | LIST_INIT(&(pdata->head)); 138 | /* init xmlparser */ 139 | parser.xmlstart = buffer; 140 | parser.xmlsize = bufsize; 141 | parser.data = pdata; 142 | parser.starteltfunc = startelt; 143 | parser.endeltfunc = endelt; 144 | parser.datafunc = data; 145 | parser.attfunc = 0; 146 | parsexml(&parser); 147 | } 148 | 149 | void 150 | FreePortListing(struct PortMappingParserData * pdata) 151 | { 152 | struct PortMapping * pm; 153 | while((pm = pdata->head.lh_first) != NULL) 154 | { 155 | LIST_REMOVE(pm, entries); 156 | free(pm); 157 | } 158 | } 159 | 160 | -------------------------------------------------------------------------------- /libminiupnpc/portlistingparse.h: -------------------------------------------------------------------------------- 1 | /* $Id: portlistingparse.h,v 1.7 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* MiniUPnP project 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * (c) 2011-2012 Thomas Bernard 5 | * This software is subject to the conditions detailed 6 | * in the LICENCE file provided within the distribution */ 7 | #ifndef PORTLISTINGPARSE_H_INCLUDED 8 | #define PORTLISTINGPARSE_H_INCLUDED 9 | 10 | #include "declspec.h" 11 | /* for the definition of UNSIGNED_INTEGER */ 12 | #include "miniupnpctypes.h" 13 | 14 | #if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__) 15 | #include "bsdqueue.h" 16 | #else 17 | #include 18 | #endif 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | /* sample of PortMappingEntry : 25 | 26 | 202.233.2.1 27 | 2345 28 | TCP 29 | 2345 30 | 192.168.1.137 31 | 1 32 | dooom 33 | 345 34 | 35 | */ 36 | typedef enum { PortMappingEltNone, 37 | PortMappingEntry, NewRemoteHost, 38 | NewExternalPort, NewProtocol, 39 | NewInternalPort, NewInternalClient, 40 | NewEnabled, NewDescription, 41 | NewLeaseTime } portMappingElt; 42 | 43 | struct PortMapping { 44 | LIST_ENTRY(PortMapping) entries; 45 | UNSIGNED_INTEGER leaseTime; 46 | unsigned short externalPort; 47 | unsigned short internalPort; 48 | char remoteHost[64]; 49 | char internalClient[64]; 50 | char description[64]; 51 | char protocol[4]; 52 | unsigned char enabled; 53 | }; 54 | 55 | struct PortMappingParserData { 56 | LIST_HEAD(portmappinglisthead, PortMapping) head; 57 | portMappingElt curelt; 58 | }; 59 | 60 | LIBSPEC void 61 | ParsePortListing(const char * buffer, int bufsize, 62 | struct PortMappingParserData * pdata); 63 | 64 | LIBSPEC void 65 | FreePortListing(struct PortMappingParserData * pdata); 66 | 67 | #ifdef __cplusplus 68 | } 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /libminiupnpc/receivedata.c: -------------------------------------------------------------------------------- 1 | /* $Id: receivedata.c,v 1.4 2012/06/23 22:34:47 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Website : http://miniupnp.free.fr/ 4 | * Author : Thomas Bernard 5 | * Copyright (c) 2011-2012 Thomas Bernard 6 | * This software is subject to the conditions detailed in the 7 | * LICENCE file provided in this distribution. */ 8 | 9 | #include 10 | #ifdef _WIN32 11 | #include 12 | #include 13 | #else 14 | #include 15 | #if defined(__amigaos__) && !defined(__amigaos4__) 16 | #define socklen_t int 17 | #else /* #if defined(__amigaos__) && !defined(__amigaos4__) */ 18 | #include 19 | #endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */ 20 | #include 21 | #include 22 | #if !defined(__amigaos__) && !defined(__amigaos4__) 23 | #include 24 | #endif 25 | #include 26 | #define MINIUPNPC_IGNORE_EINTR 27 | #endif 28 | 29 | #ifdef _WIN32 30 | #define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError()); 31 | #else 32 | #define PRINT_SOCKET_ERROR(x) perror(x) 33 | #endif 34 | 35 | #include "receivedata.h" 36 | 37 | int 38 | receivedata(int socket, 39 | char * data, int length, 40 | int timeout, unsigned int * scope_id) 41 | { 42 | #if MINIUPNPC_GET_SRC_ADDR 43 | struct sockaddr_storage src_addr; 44 | socklen_t src_addr_len = sizeof(src_addr); 45 | #endif 46 | int n; 47 | #if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) 48 | /* using poll */ 49 | struct pollfd fds[1]; /* for the poll */ 50 | #ifdef MINIUPNPC_IGNORE_EINTR 51 | do { 52 | #endif 53 | fds[0].fd = socket; 54 | fds[0].events = POLLIN; 55 | n = poll(fds, 1, timeout); 56 | #ifdef MINIUPNPC_IGNORE_EINTR 57 | } while(n < 0 && errno == EINTR); 58 | #endif 59 | if(n < 0) { 60 | PRINT_SOCKET_ERROR("poll"); 61 | return -1; 62 | } else if(n == 0) { 63 | /* timeout */ 64 | return 0; 65 | } 66 | #else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */ 67 | /* using select under _WIN32 and amigaos */ 68 | fd_set socketSet; 69 | TIMEVAL timeval; 70 | FD_ZERO(&socketSet); 71 | FD_SET(socket, &socketSet); 72 | timeval.tv_sec = timeout / 1000; 73 | timeval.tv_usec = (timeout % 1000) * 1000; 74 | n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval); 75 | if(n < 0) { 76 | PRINT_SOCKET_ERROR("select"); 77 | return -1; 78 | } else if(n == 0) { 79 | return 0; 80 | } 81 | #endif 82 | #if MINIUPNPC_GET_SRC_ADDR 83 | n = recvfrom(socket, data, length, 0, 84 | (struct sockaddr *)&src_addr, &src_addr_len); 85 | #else 86 | n = recv(socket, data, length, 0); 87 | #endif 88 | if(n<0) { 89 | PRINT_SOCKET_ERROR("recv"); 90 | } 91 | #if MINIUPNPC_GET_SRC_ADDR 92 | if (src_addr.ss_family == AF_INET6) { 93 | const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr; 94 | #ifdef DEBUG 95 | printf("scope_id=%u\n", src_addr6->sin6_scope_id); 96 | #endif 97 | if(scope_id) 98 | *scope_id = src_addr6->sin6_scope_id; 99 | } 100 | #endif 101 | return n; 102 | } 103 | 104 | 105 | -------------------------------------------------------------------------------- /libminiupnpc/receivedata.h: -------------------------------------------------------------------------------- 1 | /* $Id: receivedata.h,v 1.4 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Project: miniupnp 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * Author: Thomas Bernard 5 | * Copyright (c) 2011-2012 Thomas Bernard 6 | * This software is subjects to the conditions detailed 7 | * in the LICENCE file provided within this distribution */ 8 | #ifndef RECEIVEDATA_H_INCLUDED 9 | #define RECEIVEDATA_H_INCLUDED 10 | 11 | /* Reads data from the specified socket. 12 | * Returns the number of bytes read if successful, zero if no bytes were 13 | * read or if we timed out. Returns negative if there was an error. */ 14 | int receivedata(int socket, 15 | char * data, int length, 16 | int timeout, unsigned int * scope_id); 17 | 18 | #endif 19 | 20 | -------------------------------------------------------------------------------- /libminiupnpc/upnpcommands.h: -------------------------------------------------------------------------------- 1 | /* $Id: upnpcommands.h,v 1.25 2012/09/27 15:42:10 nanard Exp $ */ 2 | /* Miniupnp project : http://miniupnp.free.fr/ 3 | * Author : Thomas Bernard 4 | * Copyright (c) 2005-2011 Thomas Bernard 5 | * This software is subject to the conditions detailed in the 6 | * LICENCE file provided within this distribution */ 7 | #ifndef UPNPCOMMANDS_H_INCLUDED 8 | #define UPNPCOMMANDS_H_INCLUDED 9 | 10 | #include "upnpreplyparse.h" 11 | #include "portlistingparse.h" 12 | #include "declspec.h" 13 | #include "miniupnpctypes.h" 14 | 15 | /* MiniUPnPc return codes : */ 16 | #define UPNPCOMMAND_SUCCESS (0) 17 | #define UPNPCOMMAND_UNKNOWN_ERROR (-1) 18 | #define UPNPCOMMAND_INVALID_ARGS (-2) 19 | #define UPNPCOMMAND_HTTP_ERROR (-3) 20 | 21 | #ifdef __cplusplus 22 | extern "C" { 23 | #endif 24 | 25 | LIBSPEC UNSIGNED_INTEGER 26 | UPNP_GetTotalBytesSent(const char * controlURL, 27 | const char * servicetype); 28 | 29 | LIBSPEC UNSIGNED_INTEGER 30 | UPNP_GetTotalBytesReceived(const char * controlURL, 31 | const char * servicetype); 32 | 33 | LIBSPEC UNSIGNED_INTEGER 34 | UPNP_GetTotalPacketsSent(const char * controlURL, 35 | const char * servicetype); 36 | 37 | LIBSPEC UNSIGNED_INTEGER 38 | UPNP_GetTotalPacketsReceived(const char * controlURL, 39 | const char * servicetype); 40 | 41 | /* UPNP_GetStatusInfo() 42 | * status and lastconnerror are 64 byte buffers 43 | * Return values : 44 | * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR 45 | * or a UPnP Error code */ 46 | LIBSPEC int 47 | UPNP_GetStatusInfo(const char * controlURL, 48 | const char * servicetype, 49 | char * status, 50 | unsigned int * uptime, 51 | char * lastconnerror); 52 | 53 | /* UPNP_GetConnectionTypeInfo() 54 | * argument connectionType is a 64 character buffer 55 | * Return Values : 56 | * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR 57 | * or a UPnP Error code */ 58 | LIBSPEC int 59 | UPNP_GetConnectionTypeInfo(const char * controlURL, 60 | const char * servicetype, 61 | char * connectionType); 62 | 63 | /* UPNP_GetExternalIPAddress() call the corresponding UPNP method. 64 | * if the third arg is not null the value is copied to it. 65 | * at least 16 bytes must be available 66 | * 67 | * Return values : 68 | * 0 : SUCCESS 69 | * NON ZERO : ERROR Either an UPnP error code or an unknown error. 70 | * 71 | * possible UPnP Errors : 72 | * 402 Invalid Args - See UPnP Device Architecture section on Control. 73 | * 501 Action Failed - See UPnP Device Architecture section on Control. */ 74 | LIBSPEC int 75 | UPNP_GetExternalIPAddress(const char * controlURL, 76 | const char * servicetype, 77 | char * extIpAdd); 78 | 79 | /* UPNP_GetLinkLayerMaxBitRates() 80 | * call WANCommonInterfaceConfig:1#GetCommonLinkProperties 81 | * 82 | * return values : 83 | * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR 84 | * or a UPnP Error Code. */ 85 | LIBSPEC int 86 | UPNP_GetLinkLayerMaxBitRates(const char* controlURL, 87 | const char* servicetype, 88 | unsigned int * bitrateDown, 89 | unsigned int * bitrateUp); 90 | 91 | /* UPNP_AddPortMapping() 92 | * if desc is NULL, it will be defaulted to "libminiupnpc" 93 | * remoteHost is usually NULL because IGD don't support it. 94 | * 95 | * Return values : 96 | * 0 : SUCCESS 97 | * NON ZERO : ERROR. Either an UPnP error code or an unknown error. 98 | * 99 | * List of possible UPnP errors for AddPortMapping : 100 | * errorCode errorDescription (short) - Description (long) 101 | * 402 Invalid Args - See UPnP Device Architecture section on Control. 102 | * 501 Action Failed - See UPnP Device Architecture section on Control. 103 | * 715 WildCardNotPermittedInSrcIP - The source IP address cannot be 104 | * wild-carded 105 | * 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded 106 | * 718 ConflictInMappingEntry - The port mapping entry specified conflicts 107 | * with a mapping assigned previously to another client 108 | * 724 SamePortValuesRequired - Internal and External port values 109 | * must be the same 110 | * 725 OnlyPermanentLeasesSupported - The NAT implementation only supports 111 | * permanent lease times on port mappings 112 | * 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard 113 | * and cannot be a specific IP address or DNS name 114 | * 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and 115 | * cannot be a specific port value */ 116 | LIBSPEC int 117 | UPNP_AddPortMapping(const char * controlURL, const char * servicetype, 118 | const char * extPort, 119 | const char * inPort, 120 | const char * inClient, 121 | const char * desc, 122 | const char * proto, 123 | const char * remoteHost, 124 | const char * leaseDuration); 125 | 126 | /* UPNP_DeletePortMapping() 127 | * Use same argument values as what was used for AddPortMapping(). 128 | * remoteHost is usually NULL because IGD don't support it. 129 | * Return Values : 130 | * 0 : SUCCESS 131 | * NON ZERO : error. Either an UPnP error code or an undefined error. 132 | * 133 | * List of possible UPnP errors for DeletePortMapping : 134 | * 402 Invalid Args - See UPnP Device Architecture section on Control. 135 | * 714 NoSuchEntryInArray - The specified value does not exist in the array */ 136 | LIBSPEC int 137 | UPNP_DeletePortMapping(const char * controlURL, const char * servicetype, 138 | const char * extPort, const char * proto, 139 | const char * remoteHost); 140 | 141 | /* UPNP_GetPortMappingNumberOfEntries() 142 | * not supported by all routers */ 143 | LIBSPEC int 144 | UPNP_GetPortMappingNumberOfEntries(const char* controlURL, 145 | const char* servicetype, 146 | unsigned int * num); 147 | 148 | /* UPNP_GetSpecificPortMappingEntry() 149 | * retrieves an existing port mapping 150 | * params : 151 | * in extPort 152 | * in proto 153 | * out intClient (16 bytes) 154 | * out intPort (6 bytes) 155 | * out desc (80 bytes) 156 | * out enabled (4 bytes) 157 | * out leaseDuration (16 bytes) 158 | * 159 | * return value : 160 | * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR 161 | * or a UPnP Error Code. */ 162 | LIBSPEC int 163 | UPNP_GetSpecificPortMappingEntry(const char * controlURL, 164 | const char * servicetype, 165 | const char * extPort, 166 | const char * proto, 167 | char * intClient, 168 | char * intPort, 169 | char * desc, 170 | char * enabled, 171 | char * leaseDuration); 172 | 173 | /* UPNP_GetGenericPortMappingEntry() 174 | * params : 175 | * in index 176 | * out extPort (6 bytes) 177 | * out intClient (16 bytes) 178 | * out intPort (6 bytes) 179 | * out protocol (4 bytes) 180 | * out desc (80 bytes) 181 | * out enabled (4 bytes) 182 | * out rHost (64 bytes) 183 | * out duration (16 bytes) 184 | * 185 | * return value : 186 | * UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR 187 | * or a UPnP Error Code. 188 | * 189 | * Possible UPNP Error codes : 190 | * 402 Invalid Args - See UPnP Device Architecture section on Control. 191 | * 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds 192 | */ 193 | LIBSPEC int 194 | UPNP_GetGenericPortMappingEntry(const char * controlURL, 195 | const char * servicetype, 196 | const char * index, 197 | char * extPort, 198 | char * intClient, 199 | char * intPort, 200 | char * protocol, 201 | char * desc, 202 | char * enabled, 203 | char * rHost, 204 | char * duration); 205 | 206 | /* UPNP_GetListOfPortMappings() Available in IGD v2 207 | * 208 | * 209 | * Possible UPNP Error codes : 210 | * 606 Action not Authorized 211 | * 730 PortMappingNotFound - no port mapping is found in the specified range. 212 | * 733 InconsistantParameters - NewStartPort and NewEndPort values are not 213 | * consistent. 214 | */ 215 | LIBSPEC int 216 | UPNP_GetListOfPortMappings(const char * controlURL, 217 | const char * servicetype, 218 | const char * startPort, 219 | const char * endPort, 220 | const char * protocol, 221 | const char * numberOfPorts, 222 | struct PortMappingParserData * data); 223 | 224 | /* IGD:2, functions for service WANIPv6FirewallControl:1 */ 225 | LIBSPEC int 226 | UPNP_GetFirewallStatus(const char * controlURL, 227 | const char * servicetype, 228 | int * firewallEnabled, 229 | int * inboundPinholeAllowed); 230 | 231 | LIBSPEC int 232 | UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype, 233 | const char * remoteHost, 234 | const char * remotePort, 235 | const char * intClient, 236 | const char * intPort, 237 | const char * proto, 238 | int * opTimeout); 239 | 240 | LIBSPEC int 241 | UPNP_AddPinhole(const char * controlURL, const char * servicetype, 242 | const char * remoteHost, 243 | const char * remotePort, 244 | const char * intClient, 245 | const char * intPort, 246 | const char * proto, 247 | const char * leaseTime, 248 | char * uniqueID); 249 | 250 | LIBSPEC int 251 | UPNP_UpdatePinhole(const char * controlURL, const char * servicetype, 252 | const char * uniqueID, 253 | const char * leaseTime); 254 | 255 | LIBSPEC int 256 | UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID); 257 | 258 | LIBSPEC int 259 | UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype, 260 | const char * uniqueID, int * isWorking); 261 | 262 | LIBSPEC int 263 | UPNP_GetPinholePackets(const char * controlURL, const char * servicetype, 264 | const char * uniqueID, int * packets); 265 | 266 | #ifdef __cplusplus 267 | } 268 | #endif 269 | 270 | #endif 271 | 272 | -------------------------------------------------------------------------------- /libminiupnpc/upnperrors.c: -------------------------------------------------------------------------------- 1 | /* $Id: upnperrors.c,v 1.6 2012/03/15 01:02:03 nanard Exp $ */ 2 | /* Project : miniupnp 3 | * Author : Thomas BERNARD 4 | * copyright (c) 2007 Thomas Bernard 5 | * All Right reserved. 6 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 7 | * This software is subjet to the conditions detailed in the 8 | * provided LICENCE file. */ 9 | #include 10 | #include "upnperrors.h" 11 | #include "upnpcommands.h" 12 | #include "miniupnpc.h" 13 | 14 | const char * strupnperror(int err) 15 | { 16 | const char * s = NULL; 17 | switch(err) { 18 | case UPNPCOMMAND_SUCCESS: 19 | s = "Success"; 20 | break; 21 | case UPNPCOMMAND_UNKNOWN_ERROR: 22 | s = "Miniupnpc Unknown Error"; 23 | break; 24 | case UPNPCOMMAND_INVALID_ARGS: 25 | s = "Miniupnpc Invalid Arguments"; 26 | break; 27 | case UPNPDISCOVER_SOCKET_ERROR: 28 | s = "Miniupnpc Socket error"; 29 | break; 30 | case UPNPDISCOVER_MEMORY_ERROR: 31 | s = "Miniupnpc Memory allocation error"; 32 | break; 33 | case 401: 34 | s = "Invalid Action"; 35 | break; 36 | case 402: 37 | s = "Invalid Args"; 38 | break; 39 | case 501: 40 | s = "Action Failed"; 41 | break; 42 | case 606: 43 | s = "Action not authorized"; 44 | break; 45 | case 701: 46 | s = "PinholeSpaceExhausted"; 47 | break; 48 | case 702: 49 | s = "FirewallDisabled"; 50 | break; 51 | case 703: 52 | s = "InboundPinholeNotAllowed"; 53 | break; 54 | case 704: 55 | s = "NoSuchEntry"; 56 | break; 57 | case 705: 58 | s = "ProtocolNotSupported"; 59 | break; 60 | case 706: 61 | s = "InternalPortWildcardingNotAllowed"; 62 | break; 63 | case 707: 64 | s = "ProtocolWildcardingNotAllowed"; 65 | break; 66 | case 708: 67 | s = "WildcardNotPermittedInSrcIP"; 68 | break; 69 | case 709: 70 | s = "NoPacketSent"; 71 | break; 72 | case 713: 73 | s = "SpecifiedArrayIndexInvalid"; 74 | break; 75 | case 714: 76 | s = "NoSuchEntryInArray"; 77 | break; 78 | case 715: 79 | s = "WildCardNotPermittedInSrcIP"; 80 | break; 81 | case 716: 82 | s = "WildCardNotPermittedInExtPort"; 83 | break; 84 | case 718: 85 | s = "ConflictInMappingEntry"; 86 | break; 87 | case 724: 88 | s = "SamePortValuesRequired"; 89 | break; 90 | case 725: 91 | s = "OnlyPermanentLeasesSupported"; 92 | break; 93 | case 726: 94 | s = "RemoteHostOnlySupportsWildcard"; 95 | break; 96 | case 727: 97 | s = "ExternalPortOnlySupportsWildcard"; 98 | break; 99 | default: 100 | s = "UnknownError"; 101 | break; 102 | } 103 | return s; 104 | } 105 | -------------------------------------------------------------------------------- /libminiupnpc/upnperrors.h: -------------------------------------------------------------------------------- 1 | /* $Id: upnperrors.h,v 1.4 2012/09/27 15:42:11 nanard Exp $ */ 2 | /* (c) 2007 Thomas Bernard 3 | * All rights reserved. 4 | * MiniUPnP Project. 5 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 6 | * This software is subjet to the conditions detailed in the 7 | * provided LICENCE file. */ 8 | #ifndef UPNPERRORS_H_INCLUDED 9 | #define UPNPERRORS_H_INCLUDED 10 | 11 | #include "declspec.h" 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | /* strupnperror() 18 | * Return a string description of the UPnP error code 19 | * or NULL for undefinded errors */ 20 | LIBSPEC const char * strupnperror(int err); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /libminiupnpc/upnpreplyparse.c: -------------------------------------------------------------------------------- 1 | /* $Id: upnpreplyparse.c,v 1.15 2013/06/06 21:36:40 nanard Exp $ */ 2 | /* MiniUPnP project 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * (c) 2006-2013 Thomas Bernard 5 | * This software is subject to the conditions detailed 6 | * in the LICENCE file provided within the distribution */ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "upnpreplyparse.h" 13 | #include "minixml.h" 14 | 15 | static void 16 | NameValueParserStartElt(void * d, const char * name, int l) 17 | { 18 | struct NameValueParserData * data = (struct NameValueParserData *)d; 19 | data->topelt = 1; 20 | if(l>63) 21 | l = 63; 22 | memcpy(data->curelt, name, l); 23 | data->curelt[l] = '\0'; 24 | data->cdata = NULL; 25 | data->cdatalen = 0; 26 | } 27 | 28 | static void 29 | NameValueParserEndElt(void * d, const char * name, int l) 30 | { 31 | struct NameValueParserData * data = (struct NameValueParserData *)d; 32 | struct NameValue * nv; 33 | (void)name; 34 | (void)l; 35 | if(!data->topelt) 36 | return; 37 | if(strcmp(data->curelt, "NewPortListing") != 0) 38 | { 39 | int l; 40 | /* standard case. Limited to n chars strings */ 41 | l = data->cdatalen; 42 | nv = malloc(sizeof(struct NameValue)); 43 | if(l>=(int)sizeof(nv->value)) 44 | l = sizeof(nv->value) - 1; 45 | strncpy(nv->name, data->curelt, 64); 46 | nv->name[63] = '\0'; 47 | if(data->cdata != NULL) 48 | { 49 | memcpy(nv->value, data->cdata, l); 50 | nv->value[l] = '\0'; 51 | } 52 | else 53 | { 54 | nv->value[0] = '\0'; 55 | } 56 | LIST_INSERT_HEAD( &(data->head), nv, entries); 57 | } 58 | data->cdata = NULL; 59 | data->cdatalen = 0; 60 | data->topelt = 0; 61 | } 62 | 63 | static void 64 | NameValueParserGetData(void * d, const char * datas, int l) 65 | { 66 | struct NameValueParserData * data = (struct NameValueParserData *)d; 67 | if(strcmp(data->curelt, "NewPortListing") == 0) 68 | { 69 | /* specific case for NewPortListing which is a XML Document */ 70 | data->portListing = malloc(l + 1); 71 | if(!data->portListing) 72 | { 73 | /* malloc error */ 74 | return; 75 | } 76 | memcpy(data->portListing, datas, l); 77 | data->portListing[l] = '\0'; 78 | data->portListingLength = l; 79 | } 80 | else 81 | { 82 | /* standard case. */ 83 | data->cdata = datas; 84 | data->cdatalen = l; 85 | } 86 | } 87 | 88 | void 89 | ParseNameValue(const char * buffer, int bufsize, 90 | struct NameValueParserData * data) 91 | { 92 | struct xmlparser parser; 93 | LIST_INIT(&(data->head)); 94 | data->portListing = NULL; 95 | data->portListingLength = 0; 96 | /* init xmlparser object */ 97 | parser.xmlstart = buffer; 98 | parser.xmlsize = bufsize; 99 | parser.data = data; 100 | parser.starteltfunc = NameValueParserStartElt; 101 | parser.endeltfunc = NameValueParserEndElt; 102 | parser.datafunc = NameValueParserGetData; 103 | parser.attfunc = 0; 104 | parsexml(&parser); 105 | } 106 | 107 | void 108 | ClearNameValueList(struct NameValueParserData * pdata) 109 | { 110 | struct NameValue * nv; 111 | if(pdata->portListing) 112 | { 113 | free(pdata->portListing); 114 | pdata->portListing = NULL; 115 | pdata->portListingLength = 0; 116 | } 117 | while((nv = pdata->head.lh_first) != NULL) 118 | { 119 | LIST_REMOVE(nv, entries); 120 | free(nv); 121 | } 122 | } 123 | 124 | char * 125 | GetValueFromNameValueList(struct NameValueParserData * pdata, 126 | const char * Name) 127 | { 128 | struct NameValue * nv; 129 | char * p = NULL; 130 | for(nv = pdata->head.lh_first; 131 | (nv != NULL) && (p == NULL); 132 | nv = nv->entries.le_next) 133 | { 134 | if(strcmp(nv->name, Name) == 0) 135 | p = nv->value; 136 | } 137 | return p; 138 | } 139 | 140 | #if 0 141 | /* useless now that minixml ignores namespaces by itself */ 142 | char * 143 | GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, 144 | const char * Name) 145 | { 146 | struct NameValue * nv; 147 | char * p = NULL; 148 | char * pname; 149 | for(nv = pdata->head.lh_first; 150 | (nv != NULL) && (p == NULL); 151 | nv = nv->entries.le_next) 152 | { 153 | pname = strrchr(nv->name, ':'); 154 | if(pname) 155 | pname++; 156 | else 157 | pname = nv->name; 158 | if(strcmp(pname, Name)==0) 159 | p = nv->value; 160 | } 161 | return p; 162 | } 163 | #endif 164 | 165 | /* debug all-in-one function 166 | * do parsing then display to stdout */ 167 | #ifdef DEBUG 168 | void 169 | DisplayNameValueList(char * buffer, int bufsize) 170 | { 171 | struct NameValueParserData pdata; 172 | struct NameValue * nv; 173 | ParseNameValue(buffer, bufsize, &pdata); 174 | for(nv = pdata.head.lh_first; 175 | nv != NULL; 176 | nv = nv->entries.le_next) 177 | { 178 | printf("%s = %s\n", nv->name, nv->value); 179 | } 180 | ClearNameValueList(&pdata); 181 | } 182 | #endif 183 | 184 | -------------------------------------------------------------------------------- /libminiupnpc/upnpreplyparse.h: -------------------------------------------------------------------------------- 1 | /* $Id: upnpreplyparse.h,v 1.17 2013/06/06 21:36:40 nanard Exp $ */ 2 | /* MiniUPnP project 3 | * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/ 4 | * (c) 2006-2013 Thomas Bernard 5 | * This software is subject to the conditions detailed 6 | * in the LICENCE file provided within the distribution */ 7 | 8 | #ifndef UPNPREPLYPARSE_H_INCLUDED 9 | #define UPNPREPLYPARSE_H_INCLUDED 10 | 11 | #if defined(NO_SYS_QUEUE_H) || defined(_WIN32) || defined(__HAIKU__) 12 | #include "bsdqueue.h" 13 | #else 14 | #include 15 | #endif 16 | 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | struct NameValue { 22 | LIST_ENTRY(NameValue) entries; 23 | char name[64]; 24 | char value[128]; 25 | }; 26 | 27 | struct NameValueParserData { 28 | LIST_HEAD(listhead, NameValue) head; 29 | char curelt[64]; 30 | char * portListing; 31 | int portListingLength; 32 | int topelt; 33 | const char * cdata; 34 | int cdatalen; 35 | }; 36 | 37 | /* ParseNameValue() */ 38 | void 39 | ParseNameValue(const char * buffer, int bufsize, 40 | struct NameValueParserData * data); 41 | 42 | /* ClearNameValueList() */ 43 | void 44 | ClearNameValueList(struct NameValueParserData * pdata); 45 | 46 | /* GetValueFromNameValueList() */ 47 | char * 48 | GetValueFromNameValueList(struct NameValueParserData * pdata, 49 | const char * Name); 50 | 51 | #if 0 52 | /* GetValueFromNameValueListIgnoreNS() */ 53 | char * 54 | GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata, 55 | const char * Name); 56 | #endif 57 | 58 | /* DisplayNameValueList() */ 59 | #ifdef DEBUG 60 | void 61 | DisplayNameValueList(char * buffer, int bufsize); 62 | #endif 63 | 64 | #ifdef __cplusplus 65 | } 66 | #endif 67 | 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /libnatpmp/.gitignore: -------------------------------------------------------------------------------- 1 | Makefile.in 2 | /autom4te.cache 3 | /aclocal.m4 4 | /compile 5 | /configure 6 | /depcomp 7 | /install-sh 8 | /missing 9 | /m4 10 | /config.guess 11 | /config.sub -------------------------------------------------------------------------------- /libnatpmp/Changelog.txt: -------------------------------------------------------------------------------- 1 | $Id: Changelog.txt,v 1.32 2013/09/10 20:13:26 nanard Exp $ 2 | 3 | 2013/09/10: 4 | small patch for MSVC >= 16 5 | rename win32 implementation of gettimeofday() to natpmp_gettimeofday() 6 | 7 | 2012/08/21: 8 | Little change in Makefile 9 | removed warnings in testgetgateway.c 10 | Fixed bugs in command line argumentparsing in natpmpc.c 11 | 12 | 2011/08/07: 13 | Patch to build on debian/kFreeBSD. 14 | 15 | 2011/07/15: 16 | Put 3 clauses BSD licence at the top of source files. 17 | 18 | 2011/06/18: 19 | --no-undefined => -Wl,--no-undefined 20 | adding a natpmpc.1 man page 21 | 22 | 2011/05/19: 23 | Small fix in libnatpmpmodule.c thanks to Manuel Mausz 24 | 25 | 2011/01/03: 26 | Added an argument to initnatpmp() in order to force the gateway to be used 27 | 28 | 2011/01/01: 29 | fix in make install 30 | 31 | 2010/05/21: 32 | make install now working under MacOSX (and BSD) 33 | 34 | 2010/04/12: 35 | cplusplus stuff in natpmp.h 36 | 37 | 2010/02/02: 38 | Fixed compilation under Mac OS X 39 | 40 | 2009/12/19: 41 | improve and fix building under Windows. 42 | Project files for MS Visual Studio 2008 43 | More simple (and working) code for Win32. 44 | More checks in the /proc/net/route parsing. Add some comments. 45 | 46 | 2009/08/04: 47 | improving getgateway.c for windows 48 | 49 | 2009/07/13: 50 | Adding Haiku code in getgateway.c 51 | 52 | 2009/06/04: 53 | Adding Python module thanks to David Wu 54 | 55 | 2009/03/10: 56 | Trying to have windows get gateway working if not using DHCP 57 | 58 | 2009/02/27: 59 | dont include declspec.h if not under WIN32. 60 | 61 | 2009/01/23: 62 | Prefixed the libraries name with lib 63 | 64 | 2008/10/06: 65 | Fixed a memory leak in getdefaultgateway() (USE_SYSCTL_NET_ROUTE) 66 | 67 | 2008/07/03: 68 | Adding WIN32 code from Robbie Hanson 69 | 70 | 2008/06/30: 71 | added a Solaris implementation for getgateway(). 72 | added a LICENCE file to the distribution 73 | 74 | 2008/05/29: 75 | Anonymous unions are forbidden in ANSI C. That was causing problems with 76 | non-GCC compilers. 77 | 78 | 2008/04/28: 79 | introduced strnatpmperr() 80 | improved natpmpc.c sample 81 | make install now install the binary 82 | 83 | 2007/12/13: 84 | Fixed getgateway.c for working under OS X ;) 85 | Fixed values for NATPMP_PROTOCOL_TCP and NATPMP_PROTOCOL_UDP 86 | 87 | 2007/12/11: 88 | Fixed getgateway.c for compilation under Mac OS X 89 | 90 | 2007/12/01: 91 | added some comments in .h 92 | 93 | 2007/11/30: 94 | implemented almost everything 95 | 96 | -------------------------------------------------------------------------------- /libnatpmp/Jamfile: -------------------------------------------------------------------------------- 1 | 2 | lib natpmpc 3 | : # sources 4 | getgateway.c 5 | natpmp.c 6 | : # requirements 7 | . 8 | windows:wingettimeofday.c 9 | ENABLE_STRNATPMPERR 10 | windows:WIN32 11 | windows:STATICLIB 12 | : # default build 13 | static 14 | : # usage requirements 15 | . 16 | ENABLE_STRNATPMPERR 17 | windows:STATICLIB 18 | ; 19 | 20 | 21 | -------------------------------------------------------------------------------- /libnatpmp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2007-2011, Thomas BERNARD 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * The name of the author may not be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 | POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /libnatpmp/Makefile: -------------------------------------------------------------------------------- 1 | # $Id: Makefile,v 1.21 2013/09/10 20:15:06 nanard Exp $ 2 | # This Makefile is designed for use with GNU make 3 | # libnatpmp 4 | # (c) 2007-2013 Thomas Bernard 5 | # http://miniupnp.free.fr/libnatpmp.html 6 | 7 | OS = $(shell uname -s) 8 | CC = gcc 9 | INSTALL = install 10 | VERSION = $(shell cat VERSION) 11 | 12 | ifeq ($(OS), Darwin) 13 | JARSUFFIX=mac 14 | endif 15 | ifeq ($(OS), Linux) 16 | JARSUFFIX=linux 17 | endif 18 | ifneq (,$(findstring WIN,$(OS))) 19 | JARSUFFIX=win32 20 | endif 21 | 22 | # APIVERSION is used in soname 23 | APIVERSION = 1 24 | #LDFLAGS = -Wl,--no-undefined 25 | CFLAGS = -Os 26 | #CFLAGS = -g -O0 27 | CFLAGS += -fPIC 28 | CFLAGS += -Wall 29 | #CFLAGS += -Wextra 30 | CFLAGS += -DENABLE_STRNATPMPERR 31 | 32 | LIBOBJS = natpmp.o getgateway.o 33 | 34 | OBJS = $(LIBOBJS) testgetgateway.o natpmpc.o natpmp-jni.o 35 | 36 | STATICLIB = libnatpmp.a 37 | ifeq ($(OS), Darwin) 38 | SHAREDLIB = libnatpmp.dylib 39 | JNISHAREDLIB = libjninatpmp.dylib 40 | SONAME = $(basename $(SHAREDLIB)).$(APIVERSION).dylib 41 | CFLAGS := -DMACOSX -D_DARWIN_C_SOURCE $(CFLAGS) 42 | else 43 | ifneq (,$(findstring WIN,$(OS))) 44 | SHAREDLIB = natpmp.dll 45 | JNISHAREDLIB = jninatpmp.dll 46 | CC = i686-w64-mingw32-gcc 47 | EXTRA_LD = -lws2_32 -lIphlpapi -Wl,--no-undefined -Wl,--enable-runtime-pseudo-reloc --Wl,kill-at 48 | else 49 | SHAREDLIB = libnatpmp.so 50 | JNISHAREDLIB = libjninatpmp.so 51 | SONAME = $(SHAREDLIB).$(APIVERSION) 52 | endif 53 | endif 54 | 55 | HEADERS = natpmp.h 56 | 57 | EXECUTABLES = testgetgateway natpmpc-shared natpmpc-static 58 | 59 | INSTALLPREFIX ?= $(PREFIX)/usr 60 | INSTALLDIRINC = $(INSTALLPREFIX)/include 61 | INSTALLDIRLIB = $(INSTALLPREFIX)/lib 62 | INSTALLDIRBIN = $(INSTALLPREFIX)/bin 63 | 64 | JAVA = java 65 | JAVAPACKAGE = fr/free/miniupnp/libnatpmp 66 | JAVACLASSES = $(JAVAPACKAGE)/NatPmp.class $(JAVAPACKAGE)/NatPmpResponse.class $(JAVAPACKAGE)/LibraryExtractor.class $(JAVAPACKAGE)/URLUtils.class 67 | JNIHEADERS = fr_free_miniupnp_libnatpmp_NatPmp.h 68 | 69 | .PHONY: all clean depend install cleaninstall installpythonmodule 70 | 71 | all: $(STATICLIB) $(SHAREDLIB) $(EXECUTABLES) 72 | 73 | pythonmodule: $(STATICLIB) libnatpmpmodule.c setup.py 74 | python setup.py build 75 | touch $@ 76 | 77 | installpythonmodule: pythonmodule 78 | python setup.py install 79 | 80 | clean: 81 | $(RM) $(OBJS) $(EXECUTABLES) $(STATICLIB) $(SHAREDLIB) $(JAVACLASSES) $(JNISHAREDLIB) 82 | $(RM) pythonmodule 83 | $(RM) -r build/ dist/ libraries/ 84 | 85 | depend: 86 | makedepend -f$(MAKEFILE_LIST) -Y $(OBJS:.o=.c) 2>/dev/null 87 | 88 | install: $(HEADERS) $(STATICLIB) $(SHAREDLIB) natpmpc-shared 89 | $(INSTALL) -d $(INSTALLDIRINC) 90 | $(INSTALL) -m 644 $(HEADERS) $(INSTALLDIRINC) 91 | $(INSTALL) -d $(INSTALLDIRLIB) 92 | $(INSTALL) -m 644 $(STATICLIB) $(INSTALLDIRLIB) 93 | $(INSTALL) -m 644 $(SHAREDLIB) $(INSTALLDIRLIB)/$(SONAME) 94 | $(INSTALL) -d $(INSTALLDIRBIN) 95 | $(INSTALL) -m 755 natpmpc-shared $(INSTALLDIRBIN)/natpmpc 96 | ln -s -f $(SONAME) $(INSTALLDIRLIB)/$(SHAREDLIB) 97 | 98 | $(JNIHEADERS): fr/free/miniupnp/libnatpmp/NatPmp.class 99 | javah -jni fr.free.miniupnp.libnatpmp.NatPmp 100 | 101 | %.class: %.java 102 | javac -cp . $< 103 | 104 | $(JNISHAREDLIB): $(SHAREDLIB) $(JNIHEADERS) $(JAVACLASSES) 105 | ifneq (,$(findstring WIN,$(OS))) 106 | $(CC) -m32 -D_JNI_Implementation_ -Wl,--kill-at \ 107 | -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" \ 108 | natpmp-jni.c -shared \ 109 | -o $(JNISHAREDLIB) -L. -lnatpmp -lws2_32 -lIphlpapi 110 | else 111 | $(CC) $(CFLAGS) -c -I"$(JAVA_HOME)/include" -I"$(JAVA_HOME)/include/win32" natpmp-jni.c 112 | $(CC) $(CFLAGS) -o $(JNISHAREDLIB) -shared -Wl,-soname,$(JNISHAREDLIB) natpmp-jni.o -lc -L. -lnatpmp 113 | endif 114 | 115 | jar: $(JNISHAREDLIB) 116 | find fr -name '*.class' -print > classes.list 117 | $(eval JNISHAREDLIBPATH := $(shell java fr.free.miniupnp.libnatpmp.LibraryExtractor)) 118 | mkdir -p libraries/$(JNISHAREDLIBPATH) 119 | mv $(JNISHAREDLIB) libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB) 120 | jar cf natpmp_$(JARSUFFIX).jar @classes.list libraries/$(JNISHAREDLIBPATH)/$(JNISHAREDLIB) 121 | rm classes.list 122 | 123 | jnitest: $(JNISHAREDLIB) JavaTest.class 124 | $(RM) libjninatpmp.so 125 | java -Djna.nosys=true -cp . JavaTest 126 | 127 | mvn_install: 128 | mvn install:install-file -Dfile=java/natpmp_$(JARSUFFIX).jar \ 129 | -DgroupId=com.github \ 130 | -DartifactId=natpmp \ 131 | -Dversion=$(VERSION) \ 132 | -Dpackaging=jar \ 133 | -Dclassifier=$(JARSUFFIX) \ 134 | -DgeneratePom=true \ 135 | -DcreateChecksum=true 136 | 137 | cleaninstall: 138 | $(RM) $(addprefix $(INSTALLDIRINC), $(HEADERS)) 139 | $(RM) $(INSTALLDIRLIB)/$(SONAME) 140 | $(RM) $(INSTALLDIRLIB)/$(SHAREDLIB) 141 | $(RM) $(INSTALLDIRLIB)/$(STATICLIB) 142 | 143 | testgetgateway: testgetgateway.o getgateway.o 144 | $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) 145 | 146 | natpmpc-static: natpmpc.o $(STATICLIB) 147 | $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) 148 | 149 | natpmpc-shared: natpmpc.o $(SHAREDLIB) 150 | $(CC) $(LDFLAGS) -o $@ $^ $(EXTRA_LD) 151 | 152 | $(STATICLIB): $(LIBOBJS) 153 | $(AR) crs $@ $? 154 | 155 | $(SHAREDLIB): $(LIBOBJS) 156 | ifeq ($(OS), Darwin) 157 | $(CC) -dynamiclib -Wl,-install_name,$(SONAME) -o $@ $^ 158 | else 159 | $(CC) -shared -Wl,-soname,$(SONAME) -o $@ $^ $(EXTRA_LD) 160 | endif 161 | 162 | 163 | # DO NOT DELETE 164 | 165 | natpmp.o: natpmp.h getgateway.h declspec.h 166 | getgateway.o: getgateway.h declspec.h 167 | testgetgateway.o: getgateway.h declspec.h 168 | natpmpc.o: natpmp.h 169 | -------------------------------------------------------------------------------- /libnatpmp/README: -------------------------------------------------------------------------------- 1 | libnatpmp (c) 2007-2009 Thomas Bernard 2 | contact : miniupnp@free.fr 3 | 4 | see http://miniupnp.free.fr/libnatpmp.html 5 | or http://miniupnp.tuxfamily.org/libnatpmp.html 6 | for some documentation and code samples. 7 | 8 | -------------------------------------------------------------------------------- /libnatpmp/declspec.h: -------------------------------------------------------------------------------- 1 | #ifndef __DECLSPEC_H__ 2 | #define __DECLSPEC_H__ 3 | 4 | #if defined(WIN32) && !defined(STATICLIB) 5 | #ifdef NATPMP_EXPORTS 6 | #define LIBSPEC __declspec(dllexport) 7 | #else 8 | #define LIBSPEC __declspec(dllimport) 9 | #endif 10 | #else 11 | #define LIBSPEC 12 | #endif 13 | 14 | #endif 15 | 16 | -------------------------------------------------------------------------------- /libnatpmp/getgateway.h: -------------------------------------------------------------------------------- 1 | /* $Id: getgateway.h,v 1.7 2013/09/10 20:09:04 nanard Exp $ */ 2 | /* libnatpmp 3 | Copyright (c) 2007-2013, Thomas BERNARD 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * The name of the author may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef __GETGATEWAY_H__ 30 | #define __GETGATEWAY_H__ 31 | 32 | #ifdef WIN32 33 | #if !defined(_MSC_VER) || _MSC_VER >= 1600 34 | #include 35 | #else 36 | typedef unsigned long uint32_t; 37 | typedef unsigned short uint16_t; 38 | #endif 39 | #define in_addr_t uint32_t 40 | #endif 41 | #ifdef ANDROID 42 | #define in_addr_t uint32_t 43 | #endif 44 | #include "declspec.h" 45 | 46 | /* getdefaultgateway() : 47 | * return value : 48 | * 0 : success 49 | * -1 : failure */ 50 | LIBSPEC int getdefaultgateway(in_addr_t * addr); 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /libnatpmp/libnatpmpmodule.c: -------------------------------------------------------------------------------- 1 | /* $Id: libnatpmpmodule.c,v 1.7 2012/03/05 19:38:37 nanard Exp $ */ 2 | /* libnatpmp 3 | * http://miniupnp.free.fr/libnatpmp.html 4 | Copyright (c) 2007-2011, Thomas BERNARD 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | * The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 | POSSIBILITY OF SUCH DAMAGE. 29 | */ 30 | #include 31 | #ifdef WIN32 32 | #include 33 | #else 34 | #include 35 | #include 36 | #endif 37 | 38 | #define STATICLIB 39 | #include "structmember.h" 40 | #include "natpmp.h" 41 | 42 | /* for compatibility with Python < 2.4 */ 43 | #ifndef Py_RETURN_NONE 44 | #define Py_RETURN_NONE return Py_INCREF(Py_None), Py_None 45 | #endif 46 | 47 | #ifndef Py_RETURN_TRUE 48 | #define Py_RETURN_TRUE return Py_INCREF(Py_True), Py_True 49 | #endif 50 | 51 | #ifndef Py_RETURN_FALSE 52 | #define Py_RETURN_FALSE return Py_INCREF(Py_False), Py_False 53 | #endif 54 | 55 | typedef struct { 56 | PyObject_HEAD 57 | 58 | /* Type-specific fields go here. */ 59 | unsigned int discoverdelay; 60 | 61 | natpmp_t natpmp; 62 | } NATPMPObject; 63 | 64 | static PyMemberDef NATPMP_members[] = { 65 | {"discoverdelay", T_UINT, offsetof(NATPMPObject, discoverdelay), 66 | 0/*READWRITE*/, "value in ms used to wait for NATPMP responses" 67 | }, 68 | {NULL} 69 | }; 70 | 71 | static PyObject * 72 | NATPMPObject_new(PyTypeObject *type, PyObject *args, PyObject *kwds) 73 | { 74 | NATPMPObject *self; 75 | 76 | self = (NATPMPObject *)type->tp_alloc(type, 0); 77 | if (self) { 78 | initnatpmp(&self->natpmp, 0, 0); 79 | } 80 | 81 | return (PyObject *)self; 82 | } 83 | 84 | static void 85 | NATPMPObject_dealloc(NATPMPObject *self) 86 | { 87 | closenatpmp(&self->natpmp); 88 | self->ob_type->tp_free((PyObject*)self); 89 | } 90 | 91 | static PyObject * 92 | NATPMP_externalipaddress(NATPMPObject *self) 93 | { 94 | int r; 95 | struct timeval timeout; 96 | fd_set fds; 97 | natpmpresp_t response; 98 | 99 | r = sendpublicaddressrequest(&self->natpmp); 100 | 101 | if (r < 0) { 102 | #ifdef ENABLE_STRNATPMPERR 103 | PyErr_SetString(PyExc_Exception, strnatpmperr(r)); 104 | #endif 105 | return NULL; 106 | } 107 | 108 | do { 109 | FD_ZERO(&fds); 110 | FD_SET(self->natpmp.s, &fds); 111 | getnatpmprequesttimeout(&self->natpmp, &timeout); 112 | select(FD_SETSIZE, &fds, NULL, NULL, &timeout); 113 | r = readnatpmpresponseorretry(&self->natpmp, &response); 114 | if (r < 0 && r != NATPMP_TRYAGAIN) { 115 | #ifdef ENABLE_STRNATPMPERR 116 | PyErr_SetString(PyExc_Exception, strnatpmperr(r)); 117 | #endif 118 | return NULL; 119 | } 120 | } while (r == NATPMP_TRYAGAIN); 121 | 122 | return Py_BuildValue("s", inet_ntoa(response.pnu.publicaddress.addr)); 123 | } 124 | 125 | static PyObject * 126 | NATPMP_domapping(natpmp_t *n, unsigned short eport, unsigned short iport, 127 | const char *protocol, unsigned int lifetime) 128 | { 129 | int proto; 130 | struct timeval timeout; 131 | fd_set fds; 132 | natpmpresp_t response; 133 | int r; 134 | 135 | if (!strncasecmp("tcp", protocol, 3)) { 136 | proto = NATPMP_PROTOCOL_TCP; 137 | } else if (!strncasecmp("udp", protocol, 3)) { 138 | proto = NATPMP_PROTOCOL_UDP; 139 | } else { 140 | PyErr_SetString(PyExc_Exception, "Unknown protocol"); 141 | return NULL; 142 | } 143 | 144 | r = sendnewportmappingrequest(n, proto, iport, eport, 145 | lifetime); 146 | 147 | if (r < 0) { 148 | #ifdef ENABLE_STRNATPMPERR 149 | PyErr_SetString(PyExc_Exception, strnatpmperr(r)); 150 | #endif 151 | return NULL; 152 | } 153 | 154 | do { 155 | FD_ZERO(&fds); 156 | FD_SET(n->s, &fds); 157 | getnatpmprequesttimeout(n, &timeout); 158 | select(FD_SETSIZE, &fds, NULL, NULL, &timeout); 159 | r = readnatpmpresponseorretry(n, &response); 160 | if (r < 0 && r != NATPMP_TRYAGAIN) { 161 | #ifdef ENABLE_STRNATPMPERR 162 | PyErr_SetString(PyExc_Exception, strnatpmperr(r)); 163 | #endif 164 | return NULL; 165 | } 166 | } while (r == NATPMP_TRYAGAIN); 167 | 168 | return Py_BuildValue("H", response.pnu.newportmapping.mappedpublicport); 169 | } 170 | 171 | 172 | /* AddPortMapping(externalPort, protocol, internalPort, lifetime) 173 | * protocol is 'UDP' or 'TCP' */ 174 | static PyObject * 175 | NATPMP_addportmapping(NATPMPObject *self, PyObject *args) 176 | { 177 | unsigned short eport; 178 | unsigned short iport; 179 | unsigned int lifetime; 180 | const char *protocol; 181 | 182 | if (!PyArg_ParseTuple(args, "HsHI", &eport, &protocol, &iport, &lifetime)) 183 | return NULL; 184 | 185 | return NATPMP_domapping(&self->natpmp, eport, iport, protocol, lifetime); 186 | } 187 | 188 | /* DeletePortMapping(externalPort, protocol, internalPort) 189 | * protocol is 'UDP' or 'TCP' */ 190 | static PyObject * 191 | NATPMP_deleteportmapping(NATPMPObject *self, PyObject *args) 192 | { 193 | unsigned short eport; 194 | unsigned short iport; 195 | const char *protocol; 196 | 197 | if (!PyArg_ParseTuple(args, "HsH", &eport, &protocol, &iport)) 198 | return NULL; 199 | 200 | return NATPMP_domapping(&self->natpmp, eport, iport, protocol, 0); 201 | } 202 | 203 | /* natpmp.NATPMP object Method Table */ 204 | static PyMethodDef NATPMP_methods[] = { 205 | {"externalipaddress", (PyCFunction)NATPMP_externalipaddress, METH_NOARGS, 206 | "return external IP address" 207 | }, 208 | {"addportmapping", (PyCFunction)NATPMP_addportmapping, METH_VARARGS, 209 | "add a port mapping" 210 | }, 211 | {"deleteportmapping", (PyCFunction)NATPMP_deleteportmapping, METH_VARARGS, 212 | "delete a port mapping" 213 | }, 214 | {NULL} /* Sentinel */ 215 | }; 216 | 217 | static PyTypeObject NATPMPType = { 218 | PyObject_HEAD_INIT(NULL) 219 | 0, /*ob_size*/ 220 | "libnatpmp.NATPMP", /*tp_name*/ 221 | sizeof(NATPMPObject), /*tp_basicsize*/ 222 | 0, /*tp_itemsize*/ 223 | (destructor)NATPMPObject_dealloc, /*tp_dealloc*/ 224 | 0, /*tp_print*/ 225 | 0, /*tp_getattr*/ 226 | 0, /*tp_setattr*/ 227 | 0, /*tp_compare*/ 228 | 0, /*tp_repr*/ 229 | 0, /*tp_as_number*/ 230 | 0, /*tp_as_sequence*/ 231 | 0, /*tp_as_mapping*/ 232 | 0, /*tp_hash */ 233 | 0, /*tp_call*/ 234 | 0, /*tp_str*/ 235 | 0, /*tp_getattro*/ 236 | 0, /*tp_setattro*/ 237 | 0, /*tp_as_buffer*/ 238 | Py_TPFLAGS_DEFAULT, /*tp_flags*/ 239 | "NATPMP objects", /* tp_doc */ 240 | 0, /* tp_traverse */ 241 | 0, /* tp_clear */ 242 | 0, /* tp_richcompare */ 243 | 0, /* tp_weaklistoffset */ 244 | 0, /* tp_iter */ 245 | 0, /* tp_iternext */ 246 | NATPMP_methods, /* tp_methods */ 247 | NATPMP_members, /* tp_members */ 248 | 0, /* tp_getset */ 249 | 0, /* tp_base */ 250 | 0, /* tp_dict */ 251 | 0, /* tp_descr_get */ 252 | 0, /* tp_descr_set */ 253 | 0, /* tp_dictoffset */ 254 | 0, /* tp_init */ 255 | 0, /* tp_alloc */ 256 | NATPMPObject_new, /* tp_new */ 257 | }; 258 | 259 | /* module methods */ 260 | static PyMethodDef libnatpmp_methods[] = { 261 | {NULL} /* Sentinel */ 262 | }; 263 | 264 | #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ 265 | #define PyMODINIT_FUNC void 266 | #endif 267 | PyMODINIT_FUNC 268 | initlibnatpmp(void) 269 | { 270 | PyObject* m; 271 | 272 | if (PyType_Ready(&NATPMPType) < 0) 273 | return; 274 | 275 | m = Py_InitModule3("libnatpmp", libnatpmp_methods, 276 | "libnatpmp module."); 277 | 278 | Py_INCREF(&NATPMPType); 279 | PyModule_AddObject(m, "NATPMP", (PyObject *)&NATPMPType); 280 | } 281 | 282 | -------------------------------------------------------------------------------- /libnatpmp/natpmp.h: -------------------------------------------------------------------------------- 1 | /* $Id: natpmp.h,v 1.17 2013/09/10 20:09:04 nanard Exp $ */ 2 | /* libnatpmp 3 | Copyright (c) 2007-2013, Thomas BERNARD 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * The name of the author may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef __NATPMP_H__ 30 | #define __NATPMP_H__ 31 | 32 | /* NAT-PMP Port as defined by the NAT-PMP draft */ 33 | #define NATPMP_PORT (5351) 34 | 35 | #include 36 | #if !defined(_MSC_VER) 37 | #include 38 | #endif 39 | #ifdef WIN32 40 | #include 41 | #if !defined(_MSC_VER) || _MSC_VER >= 1600 42 | #include 43 | #else 44 | typedef unsigned long uint32_t; 45 | typedef unsigned short uint16_t; 46 | #endif 47 | #define in_addr_t uint32_t 48 | #include "declspec.h" 49 | #else 50 | #ifdef ANDROID 51 | #define in_addr_t uint32_t 52 | #endif 53 | #define LIBSPEC 54 | #include 55 | #endif 56 | 57 | typedef struct { 58 | int s; /* socket */ 59 | in_addr_t gateway; /* default gateway (IPv4) */ 60 | int has_pending_request; 61 | unsigned char pending_request[12]; 62 | int pending_request_len; 63 | int try_number; 64 | struct timeval retry_time; 65 | } natpmp_t; 66 | 67 | typedef struct { 68 | uint16_t type; /* NATPMP_RESPTYPE_* */ 69 | uint16_t resultcode; /* NAT-PMP response code */ 70 | uint32_t epoch; /* Seconds since start of epoch */ 71 | union { 72 | struct { 73 | //in_addr_t addr; 74 | struct in_addr addr; 75 | } publicaddress; 76 | struct { 77 | uint16_t privateport; 78 | uint16_t mappedpublicport; 79 | uint32_t lifetime; 80 | } newportmapping; 81 | } pnu; 82 | } natpmpresp_t; 83 | 84 | /* possible values for type field of natpmpresp_t */ 85 | #define NATPMP_RESPTYPE_PUBLICADDRESS (0) 86 | #define NATPMP_RESPTYPE_UDPPORTMAPPING (1) 87 | #define NATPMP_RESPTYPE_TCPPORTMAPPING (2) 88 | 89 | /* Values to pass to sendnewportmappingrequest() */ 90 | #define NATPMP_PROTOCOL_UDP (1) 91 | #define NATPMP_PROTOCOL_TCP (2) 92 | 93 | /* return values */ 94 | /* NATPMP_ERR_INVALIDARGS : invalid arguments passed to the function */ 95 | #define NATPMP_ERR_INVALIDARGS (-1) 96 | /* NATPMP_ERR_SOCKETERROR : socket() failed. check errno for details */ 97 | #define NATPMP_ERR_SOCKETERROR (-2) 98 | /* NATPMP_ERR_CANNOTGETGATEWAY : can't get default gateway IP */ 99 | #define NATPMP_ERR_CANNOTGETGATEWAY (-3) 100 | /* NATPMP_ERR_CLOSEERR : close() failed. check errno for details */ 101 | #define NATPMP_ERR_CLOSEERR (-4) 102 | /* NATPMP_ERR_RECVFROM : recvfrom() failed. check errno for details */ 103 | #define NATPMP_ERR_RECVFROM (-5) 104 | /* NATPMP_ERR_NOPENDINGREQ : readnatpmpresponseorretry() called while 105 | * no NAT-PMP request was pending */ 106 | #define NATPMP_ERR_NOPENDINGREQ (-6) 107 | /* NATPMP_ERR_NOGATEWAYSUPPORT : the gateway does not support NAT-PMP */ 108 | #define NATPMP_ERR_NOGATEWAYSUPPORT (-7) 109 | /* NATPMP_ERR_CONNECTERR : connect() failed. check errno for details */ 110 | #define NATPMP_ERR_CONNECTERR (-8) 111 | /* NATPMP_ERR_WRONGPACKETSOURCE : packet not received from the network gateway */ 112 | #define NATPMP_ERR_WRONGPACKETSOURCE (-9) 113 | /* NATPMP_ERR_SENDERR : send() failed. check errno for details */ 114 | #define NATPMP_ERR_SENDERR (-10) 115 | /* NATPMP_ERR_FCNTLERROR : fcntl() failed. check errno for details */ 116 | #define NATPMP_ERR_FCNTLERROR (-11) 117 | /* NATPMP_ERR_GETTIMEOFDAYERR : gettimeofday() failed. check errno for details */ 118 | #define NATPMP_ERR_GETTIMEOFDAYERR (-12) 119 | 120 | /* */ 121 | #define NATPMP_ERR_UNSUPPORTEDVERSION (-14) 122 | #define NATPMP_ERR_UNSUPPORTEDOPCODE (-15) 123 | 124 | /* Errors from the server : */ 125 | #define NATPMP_ERR_UNDEFINEDERROR (-49) 126 | #define NATPMP_ERR_NOTAUTHORIZED (-51) 127 | #define NATPMP_ERR_NETWORKFAILURE (-52) 128 | #define NATPMP_ERR_OUTOFRESOURCES (-53) 129 | 130 | /* NATPMP_TRYAGAIN : no data available for the moment. try again later */ 131 | #define NATPMP_TRYAGAIN (-100) 132 | 133 | #ifdef __cplusplus 134 | extern "C" { 135 | #endif 136 | 137 | /* initnatpmp() 138 | * initialize a natpmp_t object 139 | * With forcegw=1 the gateway is not detected automaticaly. 140 | * Return values : 141 | * 0 = OK 142 | * NATPMP_ERR_INVALIDARGS 143 | * NATPMP_ERR_SOCKETERROR 144 | * NATPMP_ERR_FCNTLERROR 145 | * NATPMP_ERR_CANNOTGETGATEWAY 146 | * NATPMP_ERR_CONNECTERR */ 147 | LIBSPEC int initnatpmp(natpmp_t * p, int forcegw, in_addr_t forcedgw); 148 | 149 | /* closenatpmp() 150 | * close resources associated with a natpmp_t object 151 | * Return values : 152 | * 0 = OK 153 | * NATPMP_ERR_INVALIDARGS 154 | * NATPMP_ERR_CLOSEERR */ 155 | LIBSPEC int closenatpmp(natpmp_t * p); 156 | 157 | /* sendpublicaddressrequest() 158 | * send a public address NAT-PMP request to the network gateway 159 | * Return values : 160 | * 2 = OK (size of the request) 161 | * NATPMP_ERR_INVALIDARGS 162 | * NATPMP_ERR_SENDERR */ 163 | LIBSPEC int sendpublicaddressrequest(natpmp_t * p); 164 | 165 | /* sendnewportmappingrequest() 166 | * send a new port mapping NAT-PMP request to the network gateway 167 | * Arguments : 168 | * protocol is either NATPMP_PROTOCOL_TCP or NATPMP_PROTOCOL_UDP, 169 | * lifetime is in seconds. 170 | * To remove a port mapping, set lifetime to zero. 171 | * To remove all port mappings to the host, set lifetime and both ports 172 | * to zero. 173 | * Return values : 174 | * 12 = OK (size of the request) 175 | * NATPMP_ERR_INVALIDARGS 176 | * NATPMP_ERR_SENDERR */ 177 | LIBSPEC int sendnewportmappingrequest(natpmp_t * p, int protocol, 178 | uint16_t privateport, uint16_t publicport, 179 | uint32_t lifetime); 180 | 181 | /* getnatpmprequesttimeout() 182 | * fills the timeval structure with the timeout duration of the 183 | * currently pending NAT-PMP request. 184 | * Return values : 185 | * 0 = OK 186 | * NATPMP_ERR_INVALIDARGS 187 | * NATPMP_ERR_GETTIMEOFDAYERR 188 | * NATPMP_ERR_NOPENDINGREQ */ 189 | LIBSPEC int getnatpmprequesttimeout(natpmp_t * p, struct timeval * timeout); 190 | 191 | /* readnatpmpresponseorretry() 192 | * fills the natpmpresp_t structure if possible 193 | * Return values : 194 | * 0 = OK 195 | * NATPMP_TRYAGAIN 196 | * NATPMP_ERR_INVALIDARGS 197 | * NATPMP_ERR_NOPENDINGREQ 198 | * NATPMP_ERR_NOGATEWAYSUPPORT 199 | * NATPMP_ERR_RECVFROM 200 | * NATPMP_ERR_WRONGPACKETSOURCE 201 | * NATPMP_ERR_UNSUPPORTEDVERSION 202 | * NATPMP_ERR_UNSUPPORTEDOPCODE 203 | * NATPMP_ERR_NOTAUTHORIZED 204 | * NATPMP_ERR_NETWORKFAILURE 205 | * NATPMP_ERR_OUTOFRESOURCES 206 | * NATPMP_ERR_UNSUPPORTEDOPCODE 207 | * NATPMP_ERR_UNDEFINEDERROR */ 208 | LIBSPEC int readnatpmpresponseorretry(natpmp_t * p, natpmpresp_t * response); 209 | 210 | #ifdef ENABLE_STRNATPMPERR 211 | LIBSPEC const char * strnatpmperr(int t); 212 | #endif 213 | 214 | #ifdef __cplusplus 215 | } 216 | #endif 217 | 218 | #endif 219 | -------------------------------------------------------------------------------- /libnatpmp/wingettimeofday.c: -------------------------------------------------------------------------------- 1 | /* $Id: wingettimeofday.c,v 1.6 2013/09/10 20:13:26 nanard Exp $ */ 2 | /* libnatpmp 3 | Copyright (c) 2007-2013, Thomas BERNARD 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * The name of the author may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifdef WIN32 30 | #if defined(_MSC_VER) 31 | struct timeval { 32 | long tv_sec; 33 | long tv_usec; 34 | }; 35 | #else 36 | #include 37 | #endif 38 | 39 | typedef struct _FILETIME { 40 | unsigned long dwLowDateTime; 41 | unsigned long dwHighDateTime; 42 | } FILETIME; 43 | 44 | void __stdcall GetSystemTimeAsFileTime(FILETIME*); 45 | 46 | int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */) { 47 | union { 48 | long long ns100; /*time since 1 Jan 1601 in 100ns units */ 49 | FILETIME ft; 50 | } _now; 51 | 52 | if(!p) 53 | return -1; 54 | GetSystemTimeAsFileTime( &(_now.ft) ); 55 | p->tv_usec =(long)((_now.ns100 / 10LL) % 1000000LL ); 56 | p->tv_sec = (long)((_now.ns100-(116444736000000000LL))/10000000LL); 57 | return 0; 58 | } 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /libnatpmp/wingettimeofday.h: -------------------------------------------------------------------------------- 1 | /* $Id: wingettimeofday.h,v 1.5 2013/09/11 07:22:25 nanard Exp $ */ 2 | /* libnatpmp 3 | Copyright (c) 2007-2013, Thomas BERNARD 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | * The name of the author may not be used to endorse or promote products 15 | derived from this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef __WINGETTIMEOFDAY_H__ 30 | #define __WINGETTIMEOFDAY_H__ 31 | #ifdef WIN32 32 | #if defined(_MSC_VER) 33 | #include 34 | #else 35 | #include 36 | #endif 37 | int natpmp_gettimeofday(struct timeval* p, void* tz /* IGNORED */); 38 | #endif 39 | #endif 40 | -------------------------------------------------------------------------------- /src/LoadLibraryList.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | //#include "StdAfxCore.h" 19 | #include "LoadLibraryList.h" 20 | 21 | // Helper function needed to load a lib 22 | bool __cdecl LoadLibraryList(void **proc, const char *dll, HMODULE module) 23 | { 24 | bool retVal = true; 25 | HMODULE lib = module; 26 | void *p; 27 | 28 | if (lib) goto load_procs; 29 | 30 | while (*dll) { 31 | lib = LoadLibraryA(dll); 32 | if (lib == NULL) { 33 | DWORD err = GetLastError(); 34 | retVal = false; 35 | break; 36 | } 37 | while (true) { 38 | while(*dll++); 39 | load_procs: 40 | if (!*dll) 41 | break; 42 | p = (void *) GetProcAddress(lib, dll); 43 | if (p == NULL) 44 | retVal = false; 45 | *proc++ = p; 46 | } 47 | dll++; 48 | } 49 | return retVal; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/LoadLibraryList.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #pragma once 18 | /// 19 | /// LoadLibraryList is a convenient shorthand on windows to load many 20 | /// functions from a specific DLL into a struct with function pointers. 21 | /// 22 | /// Define a struct like this: 23 | /// \code 24 | /// struct Kernel32_t { 25 | /// HANDLE (__stdcall *CreateToolhelp32Snapshot)(DWORD Flags, DWORD NotUsedZero); 26 | /// BOOL (__stdcall *Thread32First)(HANDLE ToolHelp, PTHREADENTRY32 ThreadEntry); 27 | /// BOOL (__stdcall *Thread32Next)(HANDLE ToolHelp, PTHREADENTRY32 ThreadEntry); 28 | /// HANDLE (__stdcall *OpenThread)(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwThreadId); 29 | /// } Kernel32; 30 | /// \endcode 31 | /// And a loading string like: 32 | /// \code 33 | /// const char *Kernel32Names = "kernel32.dll\0CreateToolhelp32Snapshot\0Thread32First\0Thread32Next\0OpenThread\0"; 34 | /// \endcode 35 | /// 36 | /// Note that the substrings are nul seperated and the whole thing is terminated by an additional nul character. 37 | /// The first subpart is the name of the dll to load. 38 | /// 39 | /// Use it like: 40 | /// \code 41 | /// Kernel32_t k32 = { }; 42 | /// if (!LoadLibraryList((void**)&k32, Kernel32Names)) 43 | /// return false; 44 | /// \endcode 45 | /// At this point, all function pointers in k32 are populated. 46 | /// 47 | bool __cdecl LoadLibraryList(void **proc, const char *dll, HMODULE module = 0); 48 | -------------------------------------------------------------------------------- /src/file.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "file.hpp" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "utils.hpp" // for log_error 23 | 24 | #if defined __APPLE__ 25 | #include "TargetConditionals.h" 26 | #endif 27 | 28 | #if TARGET_OS_IPHONE 29 | #include "file_cocoa.h" // for printing iOS file attributes 30 | #endif 31 | 32 | #ifdef _WIN32 33 | #include 34 | #include 35 | #else 36 | #include 37 | #include 38 | #include 39 | #endif 40 | 41 | 42 | #ifndef _WIN32 43 | std::mutex file::m_mutex; 44 | std::unordered_set file::m_locked_inodes; 45 | #endif 46 | 47 | #ifdef _WIN32 48 | #define posix_close _close 49 | #define posix_read _read 50 | #define posix_write _write 51 | #define posix_fstat _fstat 52 | #define posix_lseek _lseek 53 | #define posix_fsync _commit 54 | #define stat _stat 55 | #else 56 | #define posix_close close 57 | #define posix_read read 58 | #define posix_write write 59 | #define posix_fstat fstat 60 | #define posix_lseek lseek 61 | #define posix_fsync fsync 62 | #endif 63 | 64 | file::~file() 65 | { 66 | close(); 67 | } 68 | 69 | #ifndef _WIN32 70 | void file::unlock_inode(ino_t ino) 71 | { 72 | if (ino) { 73 | std::lock_guard l(m_mutex); 74 | m_locked_inodes.erase(ino); 75 | } 76 | } 77 | #endif 78 | 79 | void file::close() 80 | { 81 | #ifndef _WIN32 82 | if (m_inode_locked) { 83 | unlock_inode(m_inode_locked); 84 | m_inode_locked = 0; 85 | } 86 | #endif 87 | 88 | if (m_fd == -1) 89 | return; 90 | 91 | ::posix_close(m_fd); 92 | m_fd = -1; 93 | } 94 | 95 | file::file(char const* filename, int flags) 96 | : m_fd(-1) 97 | #ifndef _WIN32 98 | , m_inode_locked(0) 99 | #endif 100 | { 101 | open(filename, flags); 102 | } 103 | 104 | void file::open(char const* filename, int flags) 105 | { 106 | #ifdef _WIN32 107 | 108 | HANDLE h = INVALID_HANDLE_VALUE; 109 | 110 | DWORD access = GENERIC_READ; 111 | if (flags & read_write) 112 | access |= GENERIC_WRITE; 113 | if (flags & append) 114 | access = FILE_APPEND_DATA; 115 | 116 | DWORD share_mode = FILE_SHARE_WRITE | FILE_SHARE_READ; 117 | 118 | // share mode 0 means nobody else is allowed to open 119 | // this file while we have it open 120 | if (flags & exclusive) 121 | share_mode = 0; 122 | 123 | DWORD disposition = OPEN_EXISTING; 124 | if (flags & create) 125 | disposition = OPEN_ALWAYS; 126 | 127 | h = CreateFile(filename, access, share_mode, nullptr, disposition 128 | , FILE_ATTRIBUTE_NORMAL, nullptr); 129 | 130 | // if we want exclusive access and we fail, retry one second later 131 | int retry = 0; 132 | while (h == INVALID_HANDLE_VALUE 133 | && (flags & exclusive) 134 | && GetLastError() == ERROR_SHARING_VIOLATION 135 | && retry < 5) { 136 | 137 | std::this_thread::sleep_for(std::chrono::seconds(1)); 138 | 139 | // retry 140 | ++retry; 141 | h = CreateFile(filename, access, share_mode, nullptr, disposition 142 | , FILE_ATTRIBUTE_NORMAL, nullptr); 143 | } 144 | 145 | if (h == INVALID_HANDLE_VALUE) { 146 | throw boost::system::system_error(error_code(GetLastError() 147 | , boost::system::system_category())); 148 | } 149 | 150 | close(); 151 | m_fd = _open_osfhandle((intptr_t)h, 0); 152 | 153 | #else 154 | 155 | 156 | int oflags = 0; 157 | 158 | // you're not allowed to lock a file that's opened in read-only mode 159 | if ((flags & read_write) || (flags & exclusive)) 160 | oflags = O_RDWR; 161 | else 162 | oflags = O_RDONLY; 163 | 164 | if (flags & create) 165 | oflags |= O_CREAT; 166 | 167 | if (flags & exclusive) 168 | oflags |= O_EXCL; 169 | 170 | if (flags & append) 171 | oflags |= O_APPEND; 172 | 173 | int fd = ::open(filename, oflags, S_IRUSR | S_IWUSR); 174 | 175 | if (fd < 0 && (flags & exclusive)) { 176 | fd = ::open(filename, oflags & ~(O_EXCL | O_CREAT) 177 | , S_IRUSR | S_IWUSR); 178 | } 179 | 180 | if (fd < 0) { 181 | throw boost::system::system_error(error_code(errno 182 | , boost::system::system_category())); 183 | } 184 | 185 | #if TARGET_OS_IPHONE 186 | set_file_attributes(std::string(filename)); 187 | #endif 188 | 189 | // if we fail, we need to close fd 190 | auto guard = make_guard([=] { ::close(fd); }); 191 | 192 | ino_t locked_inode = 0; 193 | auto inode_guard = make_guard([&] { unlock_inode(locked_inode); }); 194 | 195 | if (flags & exclusive) { 196 | 197 | // file locks in posix are per-process, not per file descriptor. 198 | // two separate file descriptors can acquire the lock to the 199 | // same file, as long as they are both within the same process. 200 | // we want per-handle semantics in our locking, so we introduce a 201 | // concept of locking a file within a process. This is simply to 202 | // synchronize the handles within the same process, such that only 203 | // one handle then moves on to attempting to acquire the actual 204 | // file lock. 205 | 206 | // first we need to find the inode number for this file. This is the 207 | // unique identifier for the file we want to lock. 208 | struct stat st; 209 | int ret = fstat(fd, &st); 210 | if (ret != 0) { 211 | log_error("failed to stat file \"%s\" [%d]: (%d) %s\n" 212 | , filename, fd, errno, strerror(errno)); 213 | ::close(fd); 214 | throw boost::system::system_error(error_code(errno 215 | , boost::system::system_category())); 216 | } 217 | 218 | ino_t ino = st.st_ino; 219 | // there is an assumption that inode no. cannot be 0 220 | assert(ino != 0); 221 | 222 | // Attempt to lock the file, retry for a few seconds 223 | int retry = 0; 224 | 225 | { 226 | // now that we know the inode number, try to acquire the process-wide 227 | // lock for this file 228 | std::lock_guard l(m_mutex); 229 | while (m_locked_inodes.count(ino) > 0) { 230 | m_mutex.unlock(); 231 | ++retry; 232 | 233 | // if we fail to grab the lock for 5 seconds, give up 234 | if (retry >= 5) { 235 | ::close(fd); 236 | throw boost::system::system_error(error_code(EBADF 237 | , boost::system::system_category())); 238 | } 239 | 240 | std::this_thread::sleep_for(std::chrono::seconds(1)); 241 | m_mutex.lock(); 242 | } 243 | 244 | // we have the lock of this inode now 245 | locked_inode = ino; 246 | m_locked_inodes.insert(ino); 247 | } 248 | 249 | struct flock lock; 250 | 251 | lock.l_start = 0; 252 | lock.l_len = 0; 253 | lock.l_type = F_WRLCK; 254 | lock.l_whence = SEEK_SET; 255 | lock.l_pid = getpid(); 256 | 257 | int locked = fcntl(fd, F_SETLK, &lock); 258 | // fcntl returns -1 upon failure 259 | while (locked == -1 && retry < 5) 260 | { 261 | retry++; 262 | std::this_thread::sleep_for(std::chrono::seconds(1)); 263 | locked = fcntl(fd, F_SETLK, &lock); 264 | } 265 | if (locked == -1) { 266 | log_error("failed to lock file \"%s\" [%d]: (%d) %s\n" 267 | , filename, fd, errno, strerror(errno)); 268 | ::close(fd); 269 | throw boost::system::system_error(error_code(errno 270 | , boost::system::system_category())); 271 | } 272 | } 273 | 274 | close(); 275 | 276 | inode_guard.disarm(); 277 | m_inode_locked = locked_inode; 278 | 279 | // ignore errors here. This is just to make sure old existing files have 280 | // appropriate protection bits 281 | fchmod(fd, S_IRUSR | S_IWUSR); 282 | 283 | guard.disarm(); 284 | m_fd = fd; 285 | #endif 286 | } 287 | 288 | void file::truncate(int size) 289 | { 290 | #ifdef _WIN32 291 | HANDLE h = (HANDLE)_get_osfhandle(m_fd); 292 | FILE_END_OF_FILE_INFO eofi; 293 | eofi.EndOfFile.QuadPart = size; 294 | BOOL ret = SetFileInformationByHandle(h, FileEndOfFileInfo 295 | , &eofi, sizeof(eofi)); 296 | 297 | if (ret == FALSE) 298 | throw boost::system::system_error(error_code(GetLastError() 299 | , boost::system::system_category())); 300 | #else 301 | int ret = ftruncate(m_fd, size); 302 | if (ret != 0) 303 | throw boost::system::system_error(error_code(errno 304 | , boost::system::generic_category())); 305 | #endif 306 | } 307 | 308 | void file::seek(int pos) 309 | { 310 | int ret = ::posix_lseek(m_fd, pos, SEEK_SET); 311 | if (ret < 0) 312 | throw boost::system::system_error(error_code(errno 313 | , boost::system::system_category())); 314 | } 315 | 316 | int file::read(char* buf, int len) 317 | { 318 | if (len == 0) return 0; 319 | int ret = 0; 320 | do { 321 | int r = ::posix_read(m_fd, buf, len); 322 | 323 | // read() returning 0 means End Of File. 324 | // in that case we need to leave the loop 325 | if (r < 0) { 326 | throw boost::system::system_error(error_code(errno 327 | , boost::system::system_category())); 328 | } 329 | if (r == 0) break; 330 | 331 | ret += r; 332 | len -= r; 333 | } while (len > 0); 334 | return ret; 335 | } 336 | 337 | int file::write(char const* buf, int len) 338 | { 339 | if (len == 0) return 0; 340 | int length = len; 341 | do { 342 | int w = ::posix_write(m_fd, buf, len); 343 | 344 | // if we fail to write any bytes, but without getting 345 | // an error, we risk ending up in an infinite loop, 346 | // and we should leave. 347 | if (w <= 0) { 348 | throw boost::system::system_error(error_code(errno 349 | , boost::system::system_category())); 350 | } 351 | 352 | len -= w; 353 | } while (len > 0); 354 | return length; 355 | } 356 | 357 | int file::flush() 358 | { 359 | return posix_fsync(m_fd); 360 | } 361 | 362 | int64_t file::size() 363 | { 364 | // Figure out the size of the file, so that we can read entire file 365 | struct stat st; 366 | int ret = ::posix_fstat(m_fd, &st); 367 | 368 | // 0 means success 369 | if (ret < 0) { 370 | throw boost::system::system_error(error_code(errno 371 | , boost::system::system_category())); 372 | return 0; 373 | } 374 | 375 | return st.st_size; 376 | } 377 | 378 | -------------------------------------------------------------------------------- /src/file.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef FILE_HPP 18 | #define FILE_HPP 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | using boost::system::error_code; 25 | 26 | struct file 27 | { 28 | 29 | file() 30 | : m_fd(-1) 31 | #ifndef _WIN32 32 | , m_inode_locked(0) 33 | #endif 34 | {} 35 | ~file(); 36 | 37 | file(file const&) = delete; 38 | file(char const* filename, int flags); 39 | file& operator=(file const&) = delete; 40 | 41 | enum flags_t { read_only = 0, read_write = 1 42 | , create = 2, exclusive = 4, append = 8 }; 43 | void open(char const* filename, int flags); 44 | void seek(int pos); 45 | 46 | void close(); 47 | void truncate(int size); 48 | 49 | int read(char* buf, int len); 50 | int write(char const* buf, int len); 51 | 52 | int flush(); 53 | 54 | int64_t size(); 55 | 56 | bool is_open() const { return m_fd != 0; } 57 | 58 | int native_handle() { return m_fd; } 59 | 60 | private: 61 | 62 | int m_fd; 63 | 64 | #ifndef _WIN32 65 | // this is used to implement a per file descriptor lock of the underlying 66 | // inode number. This is to extend the posix file locking to have the 67 | // same semantics as on windows, where a file handle owns file locks, 68 | // not a process. 69 | static std::mutex m_mutex; 70 | static std::unordered_set m_locked_inodes; 71 | 72 | // if this handle holds a lock on its inode, this is set to a non-zero value 73 | ino_t m_inode_locked; 74 | void unlock_inode(ino_t ino); 75 | #endif 76 | 77 | }; 78 | #endif 79 | 80 | -------------------------------------------------------------------------------- /src/sockaddr.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "sockaddr.hpp" 18 | 19 | SockAddr endpoint_to_sockaddr(udp::endpoint const& ep) 20 | { 21 | SockAddr ret; 22 | if (ep.address().is_v4()) 23 | { 24 | ret.set_addr4(ntohl(ep.address().to_v4().to_ulong())); 25 | } 26 | else 27 | { 28 | auto b = ep.address().to_v6().to_bytes(); 29 | ret.from_compact(&b[0], b.size()); 30 | } 31 | ret.set_port(ep.port()); 32 | return ret; 33 | } 34 | 35 | udp::endpoint sockaddr_to_endpoint(SockAddr const& saddr) 36 | { 37 | udp::endpoint ret; 38 | if (saddr.isv4()) 39 | { 40 | ret = udp::endpoint(address_v4(saddr.get_addr4()), saddr.get_port()); 41 | } 42 | else 43 | { 44 | address_v6::bytes_type b; 45 | saddr.compact(&b[0], false); 46 | ret = udp::endpoint(address_v6(b), saddr.get_port()); 47 | } 48 | return ret; 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/sockaddr.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef SOCKADDR_HPP__ 18 | #define SOCKADDR_HPP__ 19 | 20 | #include "sockaddr.h" 21 | #include 22 | #include 23 | 24 | using boost::asio::ip::address; 25 | using boost::asio::ip::address_v4; 26 | using boost::asio::ip::address_v6; 27 | using boost::asio::ip::udp; 28 | 29 | SockAddr endpoint_to_sockaddr(udp::endpoint const& ep); 30 | udp::endpoint sockaddr_to_endpoint(SockAddr const& saddr); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /src/upnp-portmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef __CLIENTUPNP_H__ 18 | #define __CLIENTUPNP_H__ 19 | 20 | #include 21 | 22 | HRESULT InitializeCOM(); 23 | void UninitializeCOM(); 24 | 25 | class COM_stack_init 26 | { 27 | public: 28 | COM_stack_init() { result = InitializeCOM(); } 29 | ~COM_stack_init() { UninitializeCOM(); } 30 | operator bool() { return SUCCEEDED(result); } 31 | HRESULT result; 32 | }; 33 | 34 | bool UPnPMapPort(uint32 ip, uint16 internal_port, uint16* tcp_port, uint16* udp_port 35 | , std::wstring const& name, boost::asio::ip::address_v4& external_ip); 36 | bool RemoveNATPortMapping(WORD ExternalPort, bool tcp); 37 | 38 | #endif //__CLIENTUPNP_H__ 39 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "utils.hpp" 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | using namespace boost::uuids::detail; 26 | 27 | #ifdef _WIN32 28 | #include "winsock2.h" 29 | #else 30 | #include 31 | #endif 32 | 33 | 34 | sha1_hash sha1_fun(const byte* buf, int len) 35 | { 36 | sha1 hash; 37 | unsigned int digest[5]; 38 | hash.process_bytes(buf, len); 39 | hash.get_digest(digest); 40 | for (short i = 0; i < 5; i++) { 41 | digest[i] = htonl(digest[i]); 42 | } 43 | sha1_hash ret(reinterpret_cast(digest)); 44 | return ret; 45 | } 46 | 47 | 48 | // format the dht blob for a message in the linked list (message data and hash of the next message): 49 | std::vector message_dht_blob_write(gsl::span msg_data, chash_span next_msg_hash) 50 | { 51 | std::vector blob(msg_data.size() + sizeof(dht_msg_header)); 52 | 53 | gsl::span output = gsl::as_span(blob); 54 | 55 | // format the dht msg header: 56 | dht_msg_header header; 57 | std::copy(next_msg_hash.begin(), next_msg_hash.end(), header.next_hash.data()); 58 | header.msg_length = msg_data.size(); 59 | header.msg_offset = 0; 60 | // serialize the header: 61 | output = flatten(output, gsl::span(header)); 62 | // serialize the message: 63 | output = flatten(output, msg_data); 64 | 65 | return blob; 66 | } 67 | 68 | // this function takes in the dht buffer, parses it and returns a vector representing the message contents 69 | // and a hash pointing to the next message in the linked list: 70 | std::vector message_dht_blob_read(gsl::span dht_blob, hash& next_msg_hash) 71 | { 72 | // if the buffer is empty, return an empty vector: 73 | if (dht_blob.size() == 0) 74 | return{}; 75 | 76 | // extract the header: 77 | dht_msg_header header; 78 | dht_blob = extract(gsl::span(header), dht_blob); 79 | // get the next hash from the extracted header: 80 | std::copy(header.next_hash.begin(), header.next_hash.end(), next_msg_hash.data()); 81 | // return an empty vector if the buffer isn't long enough for the message length: 82 | if (dht_blob.size() < header.msg_offset + header.msg_length) 83 | return{}; 84 | 85 | // prepare a vector to receive the message contents: 86 | std::vector msg_contents(header.msg_length); 87 | // apply the offset (if any) and extract the actual message contents: 88 | dht_blob = extract(gsl::as_span(msg_contents), dht_blob.subspan(header.msg_offset, header.msg_length)); 89 | 90 | return msg_contents; 91 | } 92 | 93 | void log_debug(char const* fmt, ...) 94 | { 95 | va_list vl; 96 | va_start(vl, fmt); 97 | vfprintf(stderr, fmt, vl); 98 | va_end(vl); 99 | fprintf(stderr, "\n"); 100 | } 101 | 102 | void log_error(char const* fmt, ...) 103 | { 104 | va_list vl; 105 | va_start(vl, fmt); 106 | vfprintf(stderr, fmt, vl); 107 | va_end(vl); 108 | fprintf(stderr, "\n"); 109 | } 110 | 111 | std::vector decrypt_buffer(std::vector buffer, secret_key_span sk) 112 | { 113 | std::vector plaintext; 114 | 115 | if (buffer.size() <= crypto_secretbox_NONCEBYTES + crypto_secretbox_BOXZEROBYTES) 116 | return plaintext; 117 | 118 | // pull out the nonce bytes from the start of the buffer 119 | unsigned char nonce[crypto_secretbox_NONCEBYTES]; 120 | std::memcpy(nonce, &buffer[0], crypto_secretbox_NONCEBYTES); 121 | buffer.erase(buffer.begin(), buffer.begin() + crypto_secretbox_NONCEBYTES); 122 | 123 | // prepend the zero-padding space needed by 124 | // crypto_box_open() 125 | buffer.resize(buffer.size() + crypto_secretbox_BOXZEROBYTES); 126 | std::memmove(&buffer[crypto_secretbox_BOXZEROBYTES], &buffer[0] 127 | , buffer.size() - crypto_secretbox_BOXZEROBYTES); 128 | std::memset(&buffer[0], 0, crypto_secretbox_BOXZEROBYTES); 129 | plaintext.resize(buffer.size()); 130 | 131 | // crypto_secretbox_open(m,c,mlen,n,sk); 132 | // m: plain text message [out] 133 | // c: cipher text [in] 134 | // mlen: length of message [in] 135 | // n: nonce bytes [in] 136 | // sk: secret key [in] 137 | int ret = crypto_secretbox_open((unsigned char*)&plaintext[0] 138 | , (unsigned char*)&buffer[0] 139 | , buffer.size() 140 | , nonce 141 | , (const unsigned char*) sk.data()); 142 | 143 | if (ret != 0) { 144 | plaintext.clear(); 145 | return plaintext; 146 | } 147 | 148 | // now, strip the leading zeroes 149 | plaintext.erase(plaintext.begin(), plaintext.begin() + crypto_secretbox_ZEROBYTES); 150 | return plaintext; 151 | } 152 | 153 | std::vector encrypt_buffer(std::vector buffer, secret_key_span sk, const unsigned char* nonce_in) 154 | { 155 | // first, generate a nonce 156 | unsigned char nonce[crypto_secretbox_NONCEBYTES]; 157 | if (nonce_in) { 158 | std::copy(nonce_in, nonce_in + crypto_secretbox_NONCEBYTES, nonce); 159 | } 160 | else { 161 | randombytes(nonce, crypto_secretbox_NONCEBYTES); 162 | } 163 | 164 | // then prepend the zero padding 165 | buffer.resize(buffer.size() + crypto_secretbox_ZEROBYTES); 166 | std::memmove(&buffer[crypto_secretbox_ZEROBYTES], &buffer[0] 167 | , buffer.size() - crypto_secretbox_ZEROBYTES); 168 | std::memset(&buffer[0], 0, crypto_secretbox_ZEROBYTES); 169 | 170 | std::vector ciphertext; 171 | ciphertext.resize(buffer.size()); 172 | 173 | crypto_secretbox((unsigned char*)&ciphertext[0] // destination buffer 174 | , (const unsigned char*)&buffer[0] // source plaintext buffer 175 | , buffer.size() 176 | , nonce // nonce bytes 177 | , (const unsigned char*)sk.data()); 178 | 179 | // strip the remaining zero-padding 180 | ciphertext.erase(ciphertext.begin(), ciphertext.begin() 181 | + crypto_secretbox_BOXZEROBYTES); 182 | 183 | // prepend the nonce 184 | ciphertext.insert(ciphertext.begin(), nonce, nonce + crypto_secretbox_NONCEBYTES); 185 | return ciphertext; 186 | } 187 | -------------------------------------------------------------------------------- /test/Jamfile: -------------------------------------------------------------------------------- 1 | import testing ; 2 | import modules ; 3 | 4 | BOOST_ROOT = [ modules.peek : BOOST_ROOT ] ; 5 | use-project /boost : $(BOOST_ROOT) ; 6 | use-project /parent : .. ; 7 | use-project /btdht : ../btdht ; 8 | 9 | GTEST_ROOT = ../btdht/btutils/vendor/gtest-1.6.0 ; 10 | GMOCK_ROOT = ../btdht/btutils/vendor/gmock-1.6.0 ; 11 | 12 | lib test_lib 13 | : # sources 14 | $(GTEST_ROOT)/src/gtest-all.cc 15 | $(GMOCK_ROOT)/src/gmock-all.cc 16 | $(GMOCK_ROOT)/src/gmock_main.cc 17 | : # requirements 18 | /parent//scout/static 19 | BOOST_ALL_NO_LIB 20 | gcc:GTEST_USE_OWN_TR1_TUPLE=1 21 | darwin:GTEST_USE_OWN_TR1_TUPLE=1 22 | 23 | $(GTEST_ROOT)/include 24 | $(GMOCK_ROOT)/include 25 | $(GTEST_ROOT) 26 | $(GMOCK_ROOT) 27 | 28 | ../src 29 | /opt/local/include 30 | 31 | : # default build 32 | static 33 | : # usage requirements 34 | gcc:GTEST_USE_OWN_TR1_TUPLE=1 35 | darwin:GTEST_USE_OWN_TR1_TUPLE=1 36 | BOOST_ALL_NO_LIB 37 | 38 | $(GTEST_ROOT)/include 39 | $(GMOCK_ROOT)/include 40 | $(GTEST_ROOT) 41 | $(GMOCK_ROOT) 42 | ../include 43 | ../GSL/include 44 | ../src 45 | /opt/local/include 46 | $(BOOST_ROOT) 47 | 48 | ; 49 | 50 | project 51 | : requirements 52 | test_lib/static 53 | /parent//scout/static 54 | BOOST_ALL_NO_LIB 55 | ; 56 | 57 | test-suite communicator-tests : 58 | [ run test_serialization.cpp ] 59 | [ run test_scout_api.cpp ] 60 | ; 61 | 62 | # build-project ../btdht/unittests ; 63 | # build-project ../btdht/btutils/unittests ; 64 | -------------------------------------------------------------------------------- /test/fake_dht.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "dht.h" 18 | #include 19 | 20 | class FakeDhtImpl : public IDht 21 | { 22 | public: 23 | FakeDhtImpl() {} 24 | ~FakeDhtImpl() {} 25 | REFBASE; 26 | 27 | DhtSHACallback* _sha_callback; 28 | 29 | virtual bool handleReadEvent(UDPSocketInterface *socket, byte *buffer, size_t len, const SockAddr& addr) { return true; } 30 | virtual bool handleICMP(UDPSocketInterface *socket, byte *buffer, size_t len, const SockAddr& addr) { return true; } 31 | virtual void Tick() {} 32 | virtual void Vote(void *ctx, const sha1_hash* info_hash, int vote, DhtVoteCallback* callb) {} 33 | 34 | std::vector putDataCallbackBuffer; 35 | 36 | virtual void Put(const byte * pkey, const byte * skey, DhtPutCallback* put_callback, 37 | DhtPutCompletedCallback * put_completed_callback, DhtPutDataCallback* put_data_callback, 38 | void *ctx, int flags = 0, int64 seq = 0) 39 | { 40 | SockAddr src; 41 | // call the put data callback with the buffer we've previously filled: 42 | put_data_callback(ctx, putDataCallbackBuffer, seq, src); 43 | put_callback(ctx, putDataCallbackBuffer, seq, src); 44 | put_completed_callback(ctx); 45 | } 46 | 47 | std::vector immutableData; 48 | 49 | virtual sha1_hash ImmutablePut(const byte * data, size_t data_len, 50 | DhtPutCompletedCallback* put_completed_callback = nullptr, void *ctx = nullptr) 51 | { 52 | // save the data blob so we can check it later: 53 | immutableData.assign(data, data+data_len); 54 | put_completed_callback(ctx); 55 | sha1_hash hash; 56 | return hash; 57 | } 58 | 59 | virtual void ImmutableGet(sha1_hash target, DhtGetCallback* cb, void* ctx = nullptr) 60 | { 61 | // return the data blob: 62 | cb(ctx, immutableData); 63 | } 64 | 65 | virtual void AnnounceInfoHash(const byte *info_hash, DhtAddNodesCallback *addnodes_callback, 66 | DhtPortCallback* pcb, cstr file_name, void *ctx, int flags = 0) {} 67 | 68 | virtual void SetId(byte new_id_bytes[20]) {} 69 | virtual void Enable(bool enabled, int rate) {} 70 | virtual void SetVersion(char const* client, int major, int minor) {} 71 | virtual void SetRate(int bytes_per_second) {} 72 | virtual void SetExternalIPCounter(ExternalIPCounter* ip) {} 73 | virtual void SetPacketCallback(DhtPacketCallback* cb) {} 74 | virtual void SetAddNodeResponseCallback(DhtAddNodeResponseCallback* cb) {} 75 | virtual void SetSHACallback(DhtSHACallback* cb) { _sha_callback = cb; } 76 | virtual void SetEd25519VerifyCallback(Ed25519VerifyCallback* cb) {} 77 | virtual void SetEd25519SignCallback(Ed25519SignCallback* cb) {} 78 | virtual void AddBootstrapNode(SockAddr const& addr) {} 79 | virtual void AddNode(const SockAddr& addr, void* userdata, uint origin) {} 80 | virtual bool CanAnnounce() { return true; } 81 | virtual void Close() {} 82 | virtual void Shutdown() {} 83 | virtual void Initialize(UDPSocketInterface *, UDPSocketInterface *) {} 84 | virtual bool IsEnabled() { return true; } 85 | virtual void ForceRefresh() {} 86 | virtual void SetReadOnly(bool readOnly) {} 87 | virtual void SetPingFrequency(int seconds) {} 88 | virtual void SetPingBatching(int num_pings) {} 89 | virtual void EnableQuarantine(bool e) {} 90 | virtual bool ProcessIncoming(byte *buffer, size_t len, const SockAddr& addr) { return true; } 91 | virtual void DumpTracked() {} 92 | virtual void DumpBuckets() {} 93 | virtual int GetProbeQuota() { return 0; } 94 | virtual bool CanAddNode() { return true; } 95 | virtual int GetNumPeers() { return 0; } 96 | virtual bool IsBusy() { return true; } 97 | virtual int GetBootstrapState() { return 0; } 98 | virtual int GetRate() { return 0; } 99 | virtual int GetQuota() { return 0; } 100 | virtual int GetProbeRate() { return 0; } 101 | virtual int GetNumPeersTracked() { return 0; } 102 | virtual void Restart() {} 103 | virtual void GenerateId() {} 104 | }; -------------------------------------------------------------------------------- /test/test_scout_api.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "gtest/gtest.h" 18 | #include "gmock/gmock.h" 19 | 20 | #include 21 | #include 22 | #include "fake_dht.h" 23 | 24 | using namespace scout; 25 | using b = gsl::byte; 26 | 27 | namespace 28 | { 29 | void init(IDht &dht) 30 | { 31 | // set the DHT callback: 32 | dht.SetSHACallback(&sha1_fun); 33 | } 34 | } 35 | 36 | TEST(scout_api, put) 37 | { 38 | FakeDhtImpl fake_dht = FakeDhtImpl(); 39 | init(fake_dht); 40 | 41 | std::string test_msg = "test message"; 42 | auto test_msg_span = gsl::as_bytes(gsl::as_span(test_msg.c_str(), test_msg.size())); 43 | 44 | // create a message list: 45 | list_head msg_list; 46 | // add a message to the list: 47 | list_token msg_token = msg_list.push_front(test_msg_span); 48 | 49 | bool finished_cb_called = false; 50 | 51 | put_finished finished_cb = [&] { 52 | finished_cb_called = true; 53 | // check the blob that's been written to the DHT: 54 | auto dht_blob_span = gsl::as_bytes(gsl::as_span(fake_dht.immutableData)); 55 | hash parsed_hash; 56 | // parse the blob and extract the hash and message: 57 | auto parsed_msg = message_dht_blob_read(dht_blob_span, parsed_hash); 58 | // check that the parsed hash matches the message token's next hash: 59 | EXPECT_EQ(msg_token.next(), parsed_hash); 60 | // check that the parsed message matches: 61 | EXPECT_TRUE(std::equal(test_msg_span.begin(), test_msg_span.end(), parsed_msg.begin())); 62 | }; 63 | 64 | put(fake_dht, msg_token, test_msg_span, finished_cb); 65 | 66 | // check that ImmutablePut got called: 67 | EXPECT_TRUE(fake_dht.immutableData.size() > 0); 68 | // check that the finished callback got called: 69 | EXPECT_TRUE(finished_cb_called); 70 | } 71 | 72 | TEST(scout_api, get) 73 | { 74 | FakeDhtImpl fake_dht = FakeDhtImpl(); 75 | init(fake_dht); 76 | 77 | std::string test_msg = "test message"; 78 | auto test_msg_span = gsl::as_bytes(gsl::as_span(test_msg.c_str(), test_msg.size())); 79 | 80 | hash const test_hash = { b(0), b(1), b(2), b(3), b(4), b(5), b(6), b(7), b(8), b(9), 81 | b(10), b(11), b(12), b(13), b(14), b(15), b(16), b(17), b(18), b(19) }; 82 | 83 | chash_span test_hash_span = gsl::as_span(test_hash); 84 | 85 | // form a dht blob from the message and hash and save it in the fake DHT: 86 | auto dht_blob = message_dht_blob_write(test_msg_span, test_hash_span); 87 | std::string prefix = std::to_string(dht_blob.size()) + ":"; 88 | fake_dht.immutableData.assign(prefix.begin(), prefix.end()); 89 | fake_dht.immutableData.insert( 90 | fake_dht.immutableData.end() 91 | , (char*)dht_blob.data(), (char*)dht_blob.data() + dht_blob.size()); 92 | 93 | // create a message list: 94 | list_head msg_list; 95 | // add a message to the list: 96 | list_token msg_token = msg_list.push_front(test_msg_span); 97 | 98 | bool received_cb_called = false; 99 | 100 | item_received received_cb = [&](std::vector contents, hash const& next_hash) { 101 | received_cb_called = true; 102 | // check that the received hash matches: 103 | EXPECT_EQ(next_hash, test_hash); 104 | // check that the received message matches: 105 | EXPECT_TRUE(std::equal(test_msg_span.begin(), test_msg_span.end(), contents.begin())); 106 | }; 107 | 108 | hash target_hash; 109 | get(fake_dht, target_hash, received_cb); 110 | // check that the received callback was called: 111 | EXPECT_TRUE(received_cb_called); 112 | } 113 | 114 | TEST(scout_api, synchronize) 115 | { 116 | // initialize fake dht: 117 | FakeDhtImpl fake_dht = FakeDhtImpl(); 118 | init(fake_dht); 119 | 120 | std::array const test_content[] 121 | { { 0, 1, 2, 3, 4, 5, 6, 7 , 8, 9 }, 122 | { 10, 11, 12, 13, 14, 15, 16, 17 , 18, 19 }, 123 | { 20, 21, 22, 23, 24, 25, 26, 27 , 28, 29 } }; 124 | 125 | // create a vector of entries for testing: 126 | std::vector entries; 127 | 128 | int num_entries = 3; 129 | 130 | for (int i = 0; i < num_entries; ++i) 131 | { 132 | entries.emplace_back(i); 133 | entries.back().assign(gsl::as_span(test_content[i])); 134 | } 135 | 136 | // generate Alice's key pair (our keypair): 137 | std::pair aliceKeyPair = generate_keypair(); 138 | // generate Bob's key pair (remote contact keypair): 139 | std::pair bobKeyPair = generate_keypair(); 140 | // perform a DH exchange between our private key and the remote's public: 141 | secret_key shared_key = key_exchange(aliceKeyPair.first, bobKeyPair.second); 142 | 143 | bool entry_cb_called = false; 144 | bool finalize_cb_called = false; 145 | bool finished_cb_called = false; 146 | 147 | // create some fake modified entries from the initial vector of entries: 148 | std::vector entries_modified = entries; 149 | // add a new entry: 150 | entries_modified.emplace_back(num_entries + 1); 151 | char new_entry_content[] = { 30, 31, 32, 33, 34, 35, 36, 37, 38, 39 }; 152 | entries_modified.back().assign(gsl::as_span(new_entry_content)); 153 | // modify one of the existing entries: 154 | entries_modified[0].update_seq(entries_modified[0].seq() + 1); 155 | std::vector modified_entry_content = { b(6), b(6), b(6) }; 156 | entries_modified[0].update_contents(modified_entry_content); 157 | // format the entries into a dht blob and feed it into the fake dht in order 158 | // to simulate updated entries for the put data callback: 159 | std::vector buffer(1000); 160 | serialize(entries_modified, gsl::as_writeable_bytes(gsl::as_span(buffer))); 161 | buffer = encrypt_buffer(buffer, shared_key); 162 | std::string prefix = std::to_string(buffer.size()) + ":"; 163 | buffer.insert(buffer.begin(), prefix.begin(), prefix.end()); 164 | fake_dht.putDataCallbackBuffer = buffer; 165 | 166 | entry_updated entry_cb = [&](entry const& e) { 167 | entry_cb_called = true; 168 | // check that this callback is being called for the modified entries: 169 | if (e.seq() == 1) 170 | { // this is the newly added entry... 171 | // check that the new entry matches: 172 | EXPECT_TRUE(std::equal(e.value().begin(), e.value().end(), entries_modified.back().value().begin())); 173 | } 174 | else 175 | { // this is the updated entry: 176 | EXPECT_TRUE(e.seq() == 2); 177 | // check that the updated entry matches: 178 | EXPECT_TRUE(std::equal(e.value().begin(), e.value().end(), modified_entry_content.begin())); 179 | } 180 | }; 181 | 182 | finalize_entries finalize_cb = [&](std::vector& final_entries) { 183 | finalize_cb_called = true; 184 | // check that the number of entries match: 185 | EXPECT_TRUE(final_entries.size() == entries_modified.size()); 186 | // check that all the entries match: 187 | int i = 0; 188 | for (auto const &e : final_entries) 189 | { 190 | EXPECT_TRUE(e == entries_modified[i]); 191 | i++; 192 | } 193 | // modify an entry (we'll check in the finalize callback, that the modification has been made): 194 | final_entries.back().assign(modified_entry_content); 195 | // apply the same change to the original entries list so we can compare: 196 | entries_modified.back().assign(modified_entry_content); 197 | }; 198 | 199 | sync_finished finished_cb = [&] { 200 | finished_cb_called = true; 201 | // check that the final dht buffer contains the modification we've made in finalize_cb: 202 | 203 | // skip the length prefix 204 | int skip = 0; 205 | while (skip < int(fake_dht.putDataCallbackBuffer.size())) { 206 | ++skip; 207 | if (fake_dht.putDataCallbackBuffer[skip - 1] == ':') break; 208 | } 209 | std::vector buffer2(fake_dht.putDataCallbackBuffer.begin() + skip, fake_dht.putDataCallbackBuffer.end()); 210 | std::vector plaintext = decrypt_buffer(buffer2, shared_key); 211 | // parse the blob into a vector of entries: 212 | std::vector blob_entries; 213 | parse(gsl::as_bytes(gsl::as_span(plaintext)), blob_entries); 214 | 215 | // check that the number of entries match: 216 | EXPECT_TRUE(blob_entries.size() == entries_modified.size()); 217 | // check that all the entries match: 218 | int i = 0; 219 | for (auto const &e : blob_entries) 220 | { 221 | EXPECT_TRUE(e == entries_modified[i]); 222 | i++; 223 | } 224 | }; 225 | 226 | synchronize(fake_dht, shared_key, entries, entry_cb, finalize_cb, finished_cb); 227 | 228 | // check that the callbacks have been called: 229 | EXPECT_TRUE(entry_cb_called); 230 | EXPECT_TRUE(finalize_cb_called); 231 | EXPECT_TRUE(finished_cb_called); 232 | } 233 | -------------------------------------------------------------------------------- /test/test_serialization.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 BitTorrent Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include "gtest/gtest.h" 18 | 19 | #include 20 | #include 21 | 22 | using namespace scout; 23 | using b = gsl::byte; 24 | 25 | TEST(serialization, entry) 26 | { 27 | std::array const test_content 28 | { 0, 1, 2, 3, 4, 5, 6, 7 , 8, 9 }; 29 | std::array expected_buffer 30 | { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 111, 0, 0, 10, 0, 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9 }; 31 | //^--sequence number---^ ^----id----^ ^--^ ^^ ^----------content------------^ 32 | // reserved content length 33 | entry e(111); 34 | e.assign(gsl::as_span(test_content)); 35 | 36 | std::array output_buffer; 37 | auto remaining = e.serialize(output_buffer); 38 | auto serialized = gsl::as_span(output_buffer.data(), output_buffer.size() - remaining.size()); 39 | 40 | auto expected_span = gsl::as_bytes(gsl::as_span(expected_buffer)); 41 | EXPECT_TRUE(std::equal(expected_span.begin(), expected_span.end(), serialized.begin())); 42 | 43 | auto parsed = entry::parse(serialized); 44 | EXPECT_EQ(e, parsed.first); 45 | EXPECT_EQ(0, parsed.second.size_bytes()); 46 | } 47 | 48 | TEST(serialization, list_token) 49 | { 50 | hash const test_hash 51 | { b(0), b(1), b(2), b(3), b(4), b(5), b(6), b(7), b(8), b(9) 52 | , b(10), b(11), b(12), b(13), b(14), b(15), b(16), b(17), b(18), b(19) }; 53 | chash_span test_hash_span = gsl::as_span(test_hash); 54 | 55 | list_token parsed = list_token::parse(test_hash_span); 56 | 57 | EXPECT_EQ(test_hash, parsed.next()); 58 | 59 | std::array output_buffer; 60 | auto remaining = parsed.serialize(output_buffer); 61 | auto serialized = gsl::as_span(output_buffer.data(), output_buffer.size() - remaining.size()); 62 | 63 | EXPECT_TRUE(std::equal(serialized.begin(), serialized.end(), test_hash_span.begin())); 64 | } 65 | 66 | TEST(serialization, list_head) 67 | { 68 | hash const test_hash 69 | { b(0), b(1), b(2), b(3), b(4), b(5), b(6), b(7), b(8), b(9) 70 | , b(10), b(11), b(12), b(13), b(14), b(15), b(16), b(17), b(18), b(19) }; 71 | chash_span test_hash_span = gsl::as_span(test_hash); 72 | 73 | list_head parsed = list_head::parse(test_hash_span); 74 | 75 | EXPECT_EQ(test_hash, parsed.head()); 76 | 77 | std::array output_buffer; 78 | auto remaining = parsed.serialize(output_buffer); 79 | auto serialized = gsl::as_span(output_buffer.data(), output_buffer.size() - remaining.size()); 80 | 81 | EXPECT_TRUE(std::equal(serialized.begin(), serialized.end(), test_hash_span.begin())); 82 | } 83 | 84 | TEST(serialization, entries) 85 | { 86 | std::array const test_content[] 87 | { { 0, 1, 2, 3, 4, 5, 6, 7 , 8, 9 }, 88 | { 10, 11, 12, 13, 14, 15, 16, 17 , 18, 19 }, 89 | { 20, 21, 22, 23, 24, 25, 26, 27 , 28, 29 } }; 90 | 91 | std::vector test_vector; 92 | 93 | for (int i = 0; i < 3; ++i) 94 | { 95 | test_vector.emplace_back(i); 96 | test_vector.back().assign(gsl::as_span(test_content[i])); 97 | } 98 | 99 | std::array output_buffer; 100 | auto remaining = serialize(test_vector, output_buffer); 101 | 102 | std::vector parsed_vector; 103 | auto parsed = parse(gsl::as_bytes(gsl::as_span(output_buffer)), parsed_vector); 104 | 105 | EXPECT_EQ(gsl::as_bytes(remaining), parsed); 106 | EXPECT_EQ(test_vector, parsed_vector); 107 | } 108 | 109 | TEST(serialization, msg_dht_blob) 110 | { 111 | 112 | hash const test_hash = { b(0), b(1), b(2), b(3), b(4), b(5), b(6), b(7), b(8), b(9), 113 | b(10), b(11), b(12), b(13), b(14), b(15), b(16), b(17), b(18), b(19) }; 114 | 115 | chash_span test_hash_span = gsl::as_span(test_hash); 116 | 117 | std::string test_msg = "ta mere suce des schtroumpfs"; 118 | 119 | auto test_msg_span = gsl::as_bytes(gsl::as_span(test_msg.c_str(), test_msg.size())); 120 | 121 | // form a dht blob from the message and hash: 122 | auto dht_blob = message_dht_blob_write(test_msg_span, test_hash_span); 123 | 124 | // parse the blob and extract the hash and message: 125 | hash parsed_hash; 126 | auto parsed_msg = message_dht_blob_read(dht_blob, parsed_hash); 127 | // check that the parsed hash matches: 128 | EXPECT_EQ(test_hash, parsed_hash); 129 | // check that the parsed message matches: 130 | EXPECT_TRUE(std::equal(test_msg_span.begin(), test_msg_span.end(), parsed_msg.begin())); 131 | } --------------------------------------------------------------------------------