├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTOR ├── COPYING ├── Cargo.toml ├── LICENSE ├── README.md ├── appveyor.yml ├── examples ├── simple-tcp-server.rs ├── simple-udp-server.rs ├── tcp-hole-punch.rs └── udp-hole-punch.rs └── src ├── lib.rs ├── listener_message.rs ├── mapped_socket_addr.rs ├── mapped_tcp_socket.rs ├── mapped_udp_socket.rs ├── mapping_context.rs ├── punched_udp_socket.rs ├── rendezvous_info.rs ├── simple_tcp_hole_punch_server.rs ├── simple_udp_hole_punch_server.rs ├── socket_utils.rs └── utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | *.sublime* 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | secure: nhtNb8JlGf12L6SltSqY1T9X208b7sfdX85EDIvITzhKSPuVkziM9wCLf9wn+6PZvcU+RIUuEJgsB3OKA9pU0pcMvkc0Icu4Auv5890+7+Yp0xS7pB4Is3xcnzMXCMXk9cTQKZ8sy1IxHvGaFAMjERDN4YE9FP2BYSaw0xpV7sAhbx+yvsqkG+1QbOdhh51wfc2xA7Gdynn0EbS3nGcZSFfI2B7GAWreK2+XiDcBBgEqoDROYUiWxbHu+R2fwdFkcUOMeZ/AgwUjw4gJM4lURFbZxRyokfMRJ5cf74bB3Y7PmjvSoRRs/3d0KVYX9LSZ6ky9YIjTNhp6C4NrnSWpsmiS1n9s/stBMYTNrB4mT9DcRWeKLUHEskX4V+UZeomo1ItbX2Estbd2M9kP7l5h5/Ey2YEYaAfHt7wrwH+AEJM5bMc4PNAIKQ4+WNJMFIACrGnTz7QlnHnPFrRaO9J6SV10g/GZLDKCwKal5M/bq+KRv8o+czrX3zdjyzT6ztpqhNAWOPEbYlvLKQctx20Tc7tRtqh8aTbqkT8CFh9ozjswq+IErjY9sePsTzibU+1HIz9Heys3HPR3lzWlbXOq8ngQEtpqs04tqSRlCPLPW7VCmsZS4i6T7ISF3GaZhRDLDBaOqIO2gcmYliN3fxdzMAlVRTLZyTwGYWDwj4yXHK0= 4 | os: 5 | - linux 6 | - osx 7 | language: rust 8 | rust: 9 | - stable 10 | - nightly 11 | sudo: false 12 | branches: 13 | only: 14 | - master 15 | cache: 16 | cargo: true 17 | directories: 18 | - $HOME/libsodium 19 | - $HOME/elfutils 20 | install: 21 | - curl -sSLO https://github.com/maidsafe/QA/raw/master/Bash%20Scripts/Travis/install_libsodium.sh 22 | - . install_libsodium.sh 23 | script: 24 | - curl -sSL https://github.com/maidsafe/QA/raw/master/Bash%20Scripts/Travis/build_and_run_tests.sh | bash 25 | before_cache: 26 | - curl -sSLO https://github.com/maidsafe/QA/raw/master/Bash%20Scripts/Travis/install_elfutils.sh 27 | - . install_elfutils.sh 28 | after_success: 29 | - curl -sSL https://github.com/maidsafe/QA/raw/master/Bash%20Scripts/Travis/after_success.sh | bash 30 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # NAT Traversal - Change Log 2 | 3 | ## [0.4.1] 4 | - Exit MappedTcpSocket::map early after discovering two external addresses. 5 | 6 | ## [0.4.0] 7 | - All blocking methods take a deadline parameter 8 | 9 | ## [0.3.3] 10 | - Tcp mapping and punching functions time-out eagerly 11 | 12 | ## [0.3.2] 13 | - TCP rendezvous fixes 14 | - Windows fixes 15 | 16 | ## [0.3.1] 17 | - Export the following symbols: `new_reusably_bound_tcp_socket`, 18 | `MappedTcpSocketMapError`, `MappedTcpSocketMapWarning`, 19 | `MappedTcpSocketNewError`, `NewReusablyBoundTcpSocketError`, 20 | `TcpPunchHoleWarning`, `TcpPunchHoleError` 21 | 22 | ## [0.3.0] 23 | - Change Simple{Tcp,Udp}HolePunchServer to use `AsRef` 24 | 25 | ## [0.2.1] 26 | - Better interoperability with `io::Error` 27 | 28 | ## [0.2.0] 29 | - Implemented TCP hole punching 30 | - Replaced ip::IpAddr with std::IpAddr 31 | - Replaced CBOR with maidsafe_utilities::serialisation 32 | 33 | ## [0.1.0] 34 | - Richer error information 35 | - Smarter UDP hole punch logic 36 | - Initial TCP port mapping implementation 37 | 38 | ## [0.0.0 - 0.0.3] 39 | - Initial implementation 40 | -------------------------------------------------------------------------------- /CONTRIBUTOR: -------------------------------------------------------------------------------- 1 | MaidSafe Contributor Agreement version 1.1 2 | 3 | 1. Scope of this Agreement 4 | 5 | This MaidSafe Contributor Agreement ("MCA") applies to any accepted contribution (as defined below) 6 | that you make to any software or other product or project managed by us (the "project"), and sets 7 | out the intellectual property rights you assign and grant to us in the contributed materials. 8 | The term "us" shall mean MaidSafe.net Limited. The term "you" shall mean the person or entity 9 | identified below. 10 | 11 | 12 | 2. Acceptance 13 | 14 | You should read this agreement carefully before posting any contributions (as defined below) to the 15 | project. Your posting of any contribution to this project will be deemed to constitute acceptance 16 | of the terms of this MCA. This MCA is a binding legal agreement between you and us. 17 | 18 | 19 | 3. Intellectual property rights 20 | 21 | The term "contributions" or "contributed materials" means any source code, object code, patch, tool, 22 | sample, graphic, specification, manual, documentation, or any other material posted or submitted by 23 | you to the project. 24 | 25 | The term "intellectual property rights" means any patents, rights to inventions, copyrights and 26 | similar rights, rights in know-how and all other intellectual property rights anywhere in the world 27 | for the full term of those rights including all registrations and applications and the right to 28 | apply for registrations. 29 | 30 | In relation to intellectual property rights in your contributions: 31 | 32 | * you hereby assign to us with full title guarantee all the intellectual property rights 33 | (existing and future) in your contributions (the "rights"), and we hereby grant to you a 34 | perpetual, irrevocable, non-exclusive, worldwide, no-charge, royalty-free, unrestricted 35 | license to use the rights for any purpose. To the extent that such assignment is or becomes 36 | invalid, ineffective or unenforceable, you hereby grant to us a perpetual, irrevocable, 37 | non-exclusive, worldwide, no-charge, royalty-free, unrestricted license to use the rights for 38 | any purpose (including, at our option, the right to sublicense these same rights to third 39 | parties through multiple levels of sublicensees or other licensing arrangements. 40 | 41 | * you hereby irrevocably and unconditionally waive all moral rights which you may have in your 42 | contributions in whatever part of the world such rights may be enforceable; 43 | 44 | * you agree to, without charge, execute and do all such acts, documents, matters and things as 45 | may be necessary or reasonably required to obtain patent, copyright or other protection for 46 | any of your contributions (or software in which they are included) or improvements or 47 | developments to them and to vest title to the intellectual property rights in them in us and 48 | you irrevocably appoint us to be your attorney and in your name and on your behalf to execute 49 | and do any such acts described above for the same purpose if needed; and 50 | 51 | * you agree that neither of us has any duty to consult with, obtain the consent of, pay or 52 | render an accounting to the other for any use or distribution of your contribution. 53 | 54 | This MCA is effective on the date you first submitted a contribution to us. Any contribution we 55 | make available under any license will also be made available under GPL v3. 56 | 57 | You covenant, represent, warrant and agree that: 58 | 59 | * each contribution that you submit (1) is and shall be an original work of authorship and you 60 | can legally assign and grant the rights set out in this MCA, and (2) does not include any 61 | third party code or other materials; 62 | 63 | * no contribution will violate any third party's intellectual property rights; and 64 | 65 | * each contribution shall be in compliance with (and will not need a licence or permission 66 | under) applicable export or import laws. 67 | 68 | You agree to notify us if you become aware of any circumstance which would make any of the foregoing 69 | representations inaccurate in any respect. MaidSafe may publicly disclose your participation in the 70 | project, including the fact that you have signed the MCA. 71 | 72 | Any notice or other written communication to be given under or in connection with this agreement 73 | shall be in writing, no term shall be varied by statement, conduct or act of any party except that 74 | the parties may amend this agreement only by letter or written instrument signed by both parties. 75 | This agreement sets out the entire agreement and understanding between the parties relating to your 76 | contributions. 77 | 78 | This MCA is governed by Scottish law and subject to the jurisdiction of the Scottish courts save 79 | that we or you may bring action in other courts to the extent necessary to protect or enforce our 80 | intellectual property rights. 81 | 82 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["MaidSafe Developers "] 3 | description = "NAT traversal utilities." 4 | documentation = "http://docs.maidsafe.net/nat_traversal/latest" 5 | homepage = "http://maidsafe.net" 6 | license = "GPL-3.0/MaidSafe.net Commercial License 1.0" 7 | name = "nat_traversal" 8 | readme = "README.md" 9 | repository = "https://github.com/maidsafe/nat_traversal" 10 | version = "0.4.1" 11 | 12 | [dependencies] 13 | clippy = {version = "~0.0.44", optional = true} 14 | crossbeam = "~0.2.8" 15 | get_if_addrs = "~0.4.0" 16 | igd = "~0.4.2" 17 | libc = "~0.2.7" 18 | log = "~0.3.5" 19 | maidsafe_utilities = "~0.4.0" 20 | net2 = "~0.2.22" 21 | quick-error = "1.0.0" 22 | rand = "~0.3.14" 23 | rustc-serialize = "~0.3.18" 24 | socket_addr = "~0.1.0" 25 | sodiumoxide = "~0.0.9" 26 | void = "1.0.1" 27 | w_result = "~0.1.1" 28 | byteorder = "~0.5.0" 29 | 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MaidSafe.net Commercial Licence 1.0 2 | 3 | DATE [insert date] 4 | 5 | 6 | PARTIES 7 | 8 | (1) MaidSafe.next Ltd registered company number SC297540 of 72 Templehill, Troon, Scotland, 9 | KA10 6BE; and 10 | 11 | (2) [INSERT FULL COMPANY NAME OF CUSTOMER] (registered company number [number]) of [address] 12 | (the "Customer"). 13 | 14 | 15 | INTRODUCTION 16 | 17 | MaidSafe has agreed to supply and license, and the Customer has agreed to use and pay for, 18 | MaidSafe's proprietary software on the terms set out in this agreement. 19 | 20 | 21 | 1. Definitions 22 | 23 | In this agreement: 24 | 25 | "Affiliate" means in relation to any company, any body corporate which is from time to time a 26 | holding company of that company, a subsidiary of that company or a subsidiary of a holding 27 | company of that company ("holding company" and "subsidiary" having the meanings attributed to 28 | them by s.1159 Companies Act 2006); 29 | 30 | "Intellectual Property Rights" means any patents, rights to inventions, copyrights and similar 31 | rights, rights in know-how and all other intellectual property rights anywhere in the world 32 | for the full term of those rights including all registrations and applications and the right 33 | to apply for registrations; 34 | 35 | "Licence" shall mean the licence in clause 3 of this agreement; 36 | 37 | "Object" form shall mean any form resulting from mechanical transformation or translation of a 38 | Source form, including but not limited to compiled object code, generated documentation, and 39 | conversions to other media types; 40 | 41 | "Software" means the source or binary form of any MaidSafe.net limited developed code; and 42 | 43 | "Source" form shall mean software source code. 44 | 45 | 46 | 2. Supply of the Software 47 | 48 | MaidSafe will make available to the Customer the Software for download. 49 | 50 | 51 | 3. Licence 52 | 53 | 3.1 In consideration of, and subject to payment of, the charges payable under this agreement, 54 | MaidSafe grants the Customer a perpetual non-exclusive licence to use reproduce, sublicense, 55 | and distribute the Software in Source or Object form, in each case in accordance with this 56 | clause 3, for the duration of this agreement. This right includes the right to use the 57 | Intellectual Property Rights in the Software, including patent rights, and the Customer 58 | acknowledges that its use of the Software or any similar software (or any modified version of 59 | the Software or any software that is based on or includes any part of the Software) other than 60 | in accordance with the terms of this agreement (including the payment of the charges when due) 61 | would infringe MaidSafe's Intellectual Property Rights, including patents rights, and in such 62 | cases MaidSafe may terminate this Agreement without prejudice to its rights to claim damages, 63 | account of profits and/or injunctive relief. 64 | 65 | 3.2 Customer may reproduce and distribute copies of the Software in any medium, with or without 66 | modifications, and in Source or Object form, provided that Customer meets the following 67 | conditions: 68 | 69 | (a) Customer must ensure that any permitted user is required to enter into a written 70 | software licence and is not allowed to use the Software (including any modified version 71 | of the Software or any software that is based on or includes any part of the Software) 72 | in any manner that would not be permitted by this Licence; 73 | 74 | (b) Customer must cause any modified files to carry prominent notices stating that Customer 75 | changed the files; 76 | 77 | (c) Customer must retain, in the Source form of any copies of the Software (including any 78 | modified version of the Software or any software that is based on or includes any part 79 | of the Software) or any part thereof that Customer distributes, all copyright, patent, 80 | trademark, and attribution notices from the Source form of the Software; and 81 | 82 | (d) if the Software includes a "NOTICE" text file as part of its distribution, then any 83 | copies of the Software (including any modified version of the Software or any software 84 | that is based on or includes any part of the Software) or any part that Customer 85 | distributes must include a readable copy of the attribution notices contained within 86 | such NOTICE file in at least one of the following places: 87 | 88 | (i) within a NOTICE text file distributed as part of the Software; 89 | 90 | (ii) within the Source form or documentation, if provided along with the Software; or 91 | 92 | (iii) within a display generated by the Software, if and wherever such third-party 93 | notices normally appear. The contents of the NOTICE file are for informational 94 | purposes only and do not modify the Licence. 95 | 96 | 3.3 Except as expressly permitted otherwise by any term of this agreement, only the Customer is 97 | permitted to use the Software. Use by the Customer includes use by the Customer's employees 98 | and contractors provided that such use is solely on behalf of the Customer and for the 99 | purposes of the Customer's business. 100 | 101 | 3.4 The Customer may make such backup copies of the Software as are reasonably necessary to 102 | support the Customer's use of the Software in accordance with this agreement. MaidSafe will 103 | own the Intellectual Property Rights in any such backup copies. 104 | 105 | 3.5 The Customer may reverse engineer or decompile the Software but only to the extent allowed 106 | under applicable law and on the basis that Customer will request interoperability information 107 | from MaidSafe. 108 | 109 | 3.6 The Customer will comply with any reasonable instructions which MaidSafe gives the Customer 110 | relating to the use of the Software (or any modified version of the Software or any software 111 | that is based on or includes any part of the Software). The Customer will allow MaidSafe 112 | access to any premises controlled by the Customer in order to allow MaidSafe to check that the 113 | Software (or any modified version of the Software or any software that is based on or includes 114 | any part of the Software) is being used only as permitted. 115 | 116 | 3.7 This agreement does not grant permission to use the trade names, trademarks, service marks, or 117 | product names of the Licensor, except as required for reasonable and customary use in 118 | describing the origin of the Software and complying with this agreement. 119 | 120 | 121 | 4. Limited warranty 122 | 123 | 4.1 MaidSafe warrants that it will perform its obligations under this agreement with reasonable 124 | care and skill. 125 | 126 | 4.2 If the warranty in clause 4.1 is breached, the Customer must tell MaidSafe as soon as 127 | possible. The Customer must give MaidSafe a reasonable time to fix the problem or to 128 | re-perform any relevant services. This will be done without any additional charge to the 129 | Customer. If MaidSafe is able to do this within a reasonable time, MaidSafe will have no 130 | other obligations or liability in relation to that breach. If MaidSafe is unable to do this 131 | within a reasonable time or MaidSafe does not think that it is a sensible way to deal with the 132 | problem, then MaidSafe may if it wishes elect to take back the Software and to refund to the 133 | Customer all of the money which the Customer has paid to MaidSafe under this agreement. Where 134 | the problem relates to a portion of the Software and other elements supplied which are capable 135 | of use separately without material detriment to the Customer, MaidSafe may take back (and 136 | refund in respect of) affected portions only. 137 | 138 | 4.3 Apart from the terms set out in this agreement, no conditions, warranties or other terms apply 139 | to the Software or its supply or Licence under this agreement. In particular, no implied 140 | conditions, warranties or other terms relating to satisfactory quality or fitness for any 141 | purpose will apply to anything supplied under this agreement. MaidSafe does not warrant or 142 | enter into any terms to the effect that the Software: 143 | 144 | (a) will perform any particular function or purpose; or 145 | 146 | (b) be entirely free from defects or that its operation will be entirely error free. 147 | 148 | 4.4 MaidSafe will not be liable for breach of any of the warranties or any other terms in this 149 | agreement to the extent that the breach arises from: 150 | 151 | (a) use of the Software other than in accordance with normal operating procedures or as 152 | otherwise notified to the Customer by MaidSafe; 153 | 154 | (b) any alterations to the Software made by anyone other than MaidSafe or someone authorised 155 | by MaidSafe; 156 | 157 | (c) any problem with the computer on which the Software is installed, any equipment 158 | connected to that computer or any other software which is installed on that computer; 159 | 160 | (d) any abnormal or incorrect operating conditions; or 161 | 162 | (e) use of the Software in combination with any other hardware or software, unless this use 163 | has been approved by MaidSafe in writing. 164 | 165 | 166 | 5. Limitation of Liability 167 | 168 | 5.1 Neither party's liability: 169 | 170 | (a) for death or personal injury caused by its negligence or the negligence of its employees 171 | or agents; 172 | 173 | (b) for breach of clause 7 (Confidentiality); or 174 | 175 | (c) for fraudulent misrepresentation, is excluded or limited by this agreement, even if any 176 | other term of this agreement would otherwise suggest that this might be the case. 177 | 178 | 5.2 Other than as set out in clause 5.1, neither party shall be liable to the other (whether for 179 | breach of contract, negligence or for any other reason) for any: 180 | 181 | (a) loss of profits; 182 | 183 | (b) loss of sales; 184 | 185 | (c) loss of revenue; 186 | 187 | (d) loss of any software or data; 188 | 189 | (e) loss of use of hardware, software or data; 190 | 191 | (f) indirect, consequential or special loss. 192 | 193 | 5.3 Subject to clauses 5.1 and 5.2, MaidSafe's total aggregate liability under this agreement and 194 | in relation to anything which MaidSafe has done or not done in connection with this agreement 195 | (and whether the liability arises because of breach of contract, negligence or for any other 196 | reason) shall be limited to: 197 | 198 | (a) an amount equal to 125% of the total amount payable by the Customer under this agreement 199 | in the preceding 12 months; or 200 | 201 | (b) if the amount referred to in (a) cannot be calculated accurately at the time the 202 | relevant liability is to be assessed, or if it is less than £5,000, to £5,000 203 | 204 | 205 | 6. Charges 206 | 207 | 6.1 Schedule 1 sets out the licence fees and other charges payable by the Customer under this 208 | agreement. The charges are due on the dates (or on the happening of the events) specified in 209 | schedule 1. 210 | 211 | 6.2 MaidSafe may invoice the Customer for the charges as soon as they become due. The Customer 212 | must pay the invoices within 30 days of receiving them (and if MaidSafe posts them to the 213 | Customer, the Customer will be treated as having received them two working days later unless 214 | the Customer can show that this was not the case). 215 | 216 | 6.3 Where any charges are based on the Customer's revenues the Customer shall keep all accounts 217 | and documents necessary to evidence such revenues and to support any calculation of the 218 | relevant revenue share, and shall provide copies to MaidSafe on request. Customer shall allow 219 | MaidSafe and its agents to enter into Customer's premises and to have access to all such 220 | accounts and documents upon reasonable request. Where Customer's accounts and documents 221 | illustrate that Customer has underpaid any charges (or MaidSafe can otherwise demonstrate 222 | this) Customer shall immediately pay the balance due to MaidSafe plus MaidSafe's reasonable 223 | costs of audit. This clause 6.3 shall survive termination or expiry of this agreement for 6 224 | years. 225 | 226 | 6.4 MaidSafe may charge interest on all sums outstanding beyond the date on which they are due for 227 | payment under this agreement. Interest may be charged on that basis from the date payment was 228 | due until the date of payment (including after any judgement has been obtained) at the rate of 229 | 3% per calendar month or part thereof. 230 | 231 | 6.5 The amounts specified in schedule 1 do not include VAT or any other taxes on supplies and the 232 | Customer will pay these to MaidSafe as well as the amounts concerned. 233 | 234 | 235 | 7. Confidentiality 236 | 237 | 7.1 Each party will keep confidential any information which the other supplies to it in connection 238 | with this agreement. Confidential information will include the Software and any related 239 | documentation; all information marked as being confidential; and any other information which 240 | might reasonably be assumed to be confidential. The obligations as to confidentiality in this 241 | agreement will not apply to any information which: 242 | 243 | (a) is available to the public other than because of any breach of this agreement; 244 | 245 | (b) is, when it is supplied, already known to whomever it is disclosed to in circumstances 246 | in which they are not prevented from disclosing it to others; 247 | 248 | (c) is independently obtained by whomever it is disclosed to in circumstances in which they 249 | are not prevented from disclosing it to others; or 250 | 251 | (d) is required to be disclosed by law or by any court or tribunal with proper authority to 252 | order its disclosure (but only to the extent of such requirements). 253 | 254 | 255 | 8. Term and termination 256 | 257 | 8.1 This agreement will commence on the date set out on page 1 and will continue indefinitely 258 | until terminated in accordance with this clause 8. 259 | 260 | 8.2 Either party may terminate this agreement if: 261 | 262 | (a) the other materially breaches any term of this agreement and it is not possible to 263 | remedy that breach or it is possible to remedy that breach, but the other fails to do so 264 | within 30 days of being asked to do so; or 265 | 266 | (b) the other suffers any of the following event: 267 | 268 | (i) a meeting of creditors of that party being held or an arrangement or composition 269 | with or for the benefit of its creditors (including a voluntary arrangement as 270 | defined in the Insolvency Act 1986) being proposed by or in relation to that 271 | party; 272 | 273 | (ii) a chargeholder, receiver, administrative receiver or other similar party taking 274 | possession of or being appointed over or any distress, execution or other process 275 | being levied or enforced (and not being discharged within seven days) on the whole 276 | or a material part of the assets of that party; 277 | 278 | (iii) that party ceasing to carry on business or being deemed to be unable to pay its 279 | debts within the meaning of section 123 Insolvency Act 1986; 280 | 281 | (iv) that party or its directors or the holder of a qualifying floating charge or any 282 | of its creditors giving notice of their intention to appoint, appointing or making 283 | an application to the court for the appointment of, an administrator; 284 | 285 | (v) a petition being advertised or a resolution being passed or an order being made 286 | for the administration or the winding-up, bankruptcy or dissolution of that party; 287 | and/or 288 | 289 | (vi) the happening in relation to that party of an event analogous to any of the above 290 | in any jurisdiction in which it is incorporated or resident or in which it carries 291 | on business or has assets. 292 | 293 | 8.3 MaidSafe may terminate this agreement if: 294 | 295 | (a) Customer fails to pay any charges within 60 days of their due date; or 296 | 297 | (b) should the Software become, or in MaidSafe's reasonable opinion is likely to become, the 298 | subject of a claim of intellectual property infringement claim. 299 | 300 | 8.4 Apart from any other rights which MaidSafe might have, if the Customer breaches this agreement 301 | MaidSafe may suspend performance of any of its obligations or any of the Customer's rights 302 | under this agreement until the Customer remedies the breach to the reasonable satisfaction of 303 | MaidSafe. 304 | 305 | 306 | 9. Consequences of termination 307 | 308 | 9.1 If this agreement is terminated (regardless of who terminates it and regardless of the reason) 309 | the Customer will immediately on termination: 310 | 311 | (a) cease using the Software (including any modified version of the Software or any software 312 | that is based on or includes any part of the Software); 313 | 314 | (b) return all copies of the Software to MaidSafe or (if the copies are on media which is 315 | non-removable and forms part of equipment belonging to the Customer) delete all copies 316 | in such a way that they cannot be recovered; and 317 | 318 | (c) confirm to MaidSafe in writing that both of the above things have been done. 319 | 320 | 9.2 Termination of this agreement will not affect any accrued rights or liabilities which either 321 | MaidSafe or the Customer may have by the time termination takes effect. Clauses 5, 6 (for 322 | unpaid charges), 7 and 9 shall survive termination or expiry of this agreement and any other 323 | clause shall survive termination or expiry if expressly stated. 324 | 325 | 326 | 10. Other terms 327 | 328 | 10.1 The Customer may not assign any of the Customer's rights or obligations under this agreement. 329 | MaidSafe may assign this agreement or any of MaidSafe's rights or obligations under this 330 | agreement to someone else, provided MaidSafe tells the Customer in writing if it does so. 331 | 332 | 10.2 Neither party has any authority to enter into a contract for or on behalf of the other party, 333 | to assume a liability on behalf of the other party or to pledge the credit of the other party, 334 | unless such authority is expressly granted in writing by the other party. Neither party may 335 | act as if it has such authority and must not represent (expressly or by implying it) that it 336 | has such authority. 337 | 338 | 10.3 MaidSafe will not be liable to the Customer for any breach of this agreement which arises 339 | because of any circumstances which MaidSafe cannot reasonably be expected to control. 340 | 341 | 10.4 All notices and consents relating to this agreement must be in writing. All variations to 342 | this agreement must be agreed, set out in writing and signed on behalf of both MaidSafe and 343 | the Customer before they take effect. 344 | 345 | 10.5 In this agreement, unless it says otherwise: 346 | 347 | (a) reference to a person includes a legal person (such as a limited company) as well as a 348 | natural person; 349 | 350 | (b) reference to this agreement includes reference to the schedules and appendices and other 351 | documents attached to it or incorporated by reference into it (all as amended or added 352 | to from time to time); 353 | 354 | (c) reference to "including" in this agreement shall be treated as being by way of example 355 | and shall not limit the general applicability of any preceding words; 356 | 357 | (d) reference to any legislation shall be to that legislation as amended, extended or 358 | re-enacted from time to time and to any subordinate provision made under that 359 | legislation; 360 | 361 | (e) references to clauses or schedules shall be to those in this agreement; 362 | 363 | (f) reference to this agreement shall include reference to it after it has been amended, 364 | added to or replaced by a new agreement. 365 | 366 | 10.6 Except to the extent that this agreement expressly says otherwise, nothing in this agreement 367 | shall create a partnership between the parties or give the rights of a partner to either 368 | party. 369 | 370 | 10.7 Any software supplied or Licenced under this agreement will not be treated as goods within the 371 | meaning of the Sale of Goods Act 1979. Firmware will be treated as part of the goods in which 372 | it is installed. 373 | 374 | 10.8 This agreement sets out all of the terms that have been agreed between MaidSafe and the 375 | Customer in relation to the subjects covered by it. Subject to clause 5.1, no other 376 | representations or terms shall apply or form part of this agreement. The Customer 377 | acknowledges that it has not been influenced to enter this agreement by anything MaidSafe has 378 | said or done or committed to do, except as expressly recorded herein. 379 | 380 | 10.9 No term of this agreement is enforceable under the Contracts (Rights of Third Parties) Act 381 | 1999 by a person who is not a party to this agreement. 382 | 383 | 10.10 This agreement is governed by Scottish law. Both MaidSafe and the Customer submit to the 384 | exclusive jurisdiction of the Scottish courts in relation to any dispute concerning this 385 | agreement but MaidSafe is also entitled to apply to any court worldwide for injunctive and 386 | other remedies in order to protect or enforce its Intellectual Property Rights. 387 | 388 | 389 | SCHEDULE 1 390 | 391 | CHARGES 392 | 393 | 394 | 1. Customer shall pay to MaidSafe 1% of Qualifying Revenue. 395 | 396 | 2 "Qualifying Revenue" shall mean any revenue generated directly or indirectly by Customer or 397 | any Affiliate of Customer through: 398 | 399 | (a) use of the Software (including any modified version of the Software or any software that 400 | is based on or includes any part of the Software); or 401 | 402 | (b) the provision of services directly or indirectly to any person using the Software 403 | (including any modified version of the Software or any software that is based on or 404 | includes any part of the Software), 405 | 406 | ("Qualifying Activities") less any VAT charged on such Qualifying Activities. 407 | 408 | 3. Where any Qualifying Activity is discounted or provided for free (whether through bundling or 409 | otherwise) it will be deemed to be provided at market rate and the relevant Qualifying Revenue 410 | shall be calculated accordingly. 411 | 412 | 4. The charges will be payable quarterly in arrears. 413 | 414 | 5. Within 5 days of the end of each month Customer will provide to MaidSafe a statement setting 415 | out the Qualifying Revenue for the month. MaidSafe shall invoice Customer within 5 days of 416 | the end of each third month. Where no statement is provided or MaidSafe has cause to believe 417 | it to be inaccurate it may invoke its audit rights under this agreement. 418 | 419 | 420 | SIGNED on behalf of both parties on the date set out on page 1 of this agreement: 421 | 422 | 423 | 424 | SIGNED: ..................................................................... 425 | for and on behalf of MaidSafe 426 | 427 | 428 | 429 | ..................................................................... 430 | Name/status 431 | 432 | 433 | 434 | 435 | SIGNED: ..................................................................... 436 | for and on behalf of the Customer 437 | 438 | 439 | 440 | ..................................................................... 441 | Name/status 442 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ***This repository is no longer maintained*** 2 | # It has been moved to the maidsafe-archive organisation for reference only 3 | # 4 | # 5 | # 6 | # 7 | # nat_traversal 8 | 9 | **Maintainer:** Spandan Sharma (spandan.sharma@maidsafe.net) 10 | 11 | |Crate|Linux/OS X|Windows|Coverage|Issues| 12 | |:---:|:--------:|:-----:|:------:|:----:| 13 | |[![](http://meritbadge.herokuapp.com/nat_traversal)](https://crates.io/crates/nat_traversal)|[![Build Status](https://travis-ci.org/maidsafe/nat_traversal.svg?branch=master)](https://travis-ci.org/maidsafe/nat_traversal)|[![Build status](https://ci.appveyor.com/api/projects/status/ngrcqkvae91r0uvj/branch/master?svg=true)](https://ci.appveyor.com/project/MaidSafe-QA/nat-traversal/branch/master)|[![Coverage Status](https://coveralls.io/repos/maidsafe/nat_traversal/badge.svg?branch=master&service=github)](https://coveralls.io/github/maidsafe/nat_traversal?branch=master)|[![Stories in Ready](https://badge.waffle.io/maidsafe/nat_traversal.png?label=ready&title=Ready)](https://waffle.io/maidsafe/nat_traversal)| 14 | 15 | | [API Documentation - master branch](http://docs.maidsafe.net/nat_traversal/master) | [MaidSafe website](http://maidsafe.net) | [SAFE Network Forum](https://forum.safenetwork.io) | 16 | |:------:|:-------:|:-------:|:-------:| 17 | 18 | ## Overview 19 | 20 | NAT-traversal utilities. 21 | 22 | ## License 23 | 24 | Licensed under either of 25 | 26 | * the MaidSafe.net Commercial License, version 1.0 or later ([LICENSE](LICENSE)) 27 | * the General Public License (GPL), version 3 ([COPYING](COPYING) or http://www.gnu.org/licenses/gpl-3.0.en.html) 28 | 29 | at your option. 30 | 31 | ## Contribution 32 | 33 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the 34 | work by you, as defined in the MaidSafe Contributor Agreement, version 1.1 ([CONTRIBUTOR] 35 | (CONTRIBUTOR)), shall be dual licensed as above, and you agree to be bound by the terms of the 36 | MaidSafe Contributor Agreement, version 1.1. 37 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | global: 3 | RUST_TEST_THREADS: 1 4 | RUST_BACKTRACE: 1 5 | matrix: 6 | - RUST_VERSION: stable 7 | # - RUST_VERSION: beta 8 | # - RUST_VERSION: nightly 9 | branches: 10 | only: 11 | - master 12 | 13 | clone_depth: 50 14 | 15 | install: 16 | - ps: | 17 | $url = "https://github.com/maidsafe/QA/raw/master/Powershell%20Scripts/AppVeyor" 18 | Start-FileDownload "$url/Install%20Rust.ps1" -FileName "Install Rust.ps1" 19 | Start-FileDownload "$url/Install%20libsodium.ps1" -FileName "Install libsodium.ps1" 20 | Start-FileDownload "$url/Build.ps1" -FileName "Build.ps1" 21 | Start-FileDownload "$url/Run%20Tests.ps1" -FileName "Run Tests.ps1" 22 | . ".\Install Rust.ps1" 23 | . ".\Install libsodium.ps1" 24 | 25 | platform: 26 | - x86 27 | - x64 28 | 29 | configuration: 30 | # - Debug 31 | - Release 32 | 33 | build_script: 34 | - ps: . ".\Build.ps1" 35 | 36 | test_script: 37 | - ps: . ".\Run Tests.ps1" 38 | -------------------------------------------------------------------------------- /examples/simple-tcp-server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! Simple service example. 19 | 20 | // For explanation of lint checks, run `rustc -W help` or see 21 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 22 | #![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, 23 | unknown_crate_types, warnings)] 24 | #![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs, 25 | non_shorthand_field_patterns, overflowing_literals, plugin_as_library, 26 | private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion, 27 | unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes, 28 | unused_comparisons, unused_features, unused_parens, while_true)] 29 | #![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, 30 | unused_qualifications, unused_results)] 31 | #![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations, 32 | missing_debug_implementations, variant_size_differences)] 33 | 34 | #![cfg_attr(feature="clippy", feature(plugin))] 35 | #![cfg_attr(feature="clippy", plugin(clippy))] 36 | #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] 37 | 38 | extern crate nat_traversal; 39 | extern crate w_result; 40 | 41 | use std::time::{Instant, Duration}; 42 | 43 | use nat_traversal::{MappingContext, SimpleTcpHolePunchServer}; 44 | use w_result::{WOk, WErr}; 45 | 46 | fn main() { 47 | println!("The example runs a simple rendezvous server that peers can use to connect to each other with"); 48 | 49 | // First, we must create a mapping context. 50 | let mapping_context = match MappingContext::new() { 51 | WOk(mapping_context, warnings) => { 52 | for warning in warnings { 53 | println!("Warning when creating mapping context: {}", warning); 54 | } 55 | mapping_context 56 | } 57 | WErr(e) => { 58 | println!("Error creating mapping context: {}", e); 59 | println!("Exiting."); 60 | return; 61 | } 62 | }; 63 | 64 | // Now we create the server. 65 | let deadline = Instant::now() + Duration::from_secs(3); 66 | let simple_server = match SimpleTcpHolePunchServer::new(Box::new(mapping_context), deadline) { 67 | WOk(simple_server, warnings) => { 68 | for warning in warnings { 69 | println!("Warning when creating simple server: {}", warning); 70 | } 71 | simple_server 72 | }, 73 | WErr(e) => { 74 | println!("Error creating simple server: {}", e); 75 | println!("Exiting."); 76 | return; 77 | } 78 | }; 79 | 80 | // Now we print the servers known addresses 81 | let addresses = simple_server.addresses(); 82 | println!("Server addresses: {:#?}", addresses); 83 | 84 | std::thread::park(); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /examples/simple-udp-server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! Simple service example. 19 | 20 | // For explanation of lint checks, run `rustc -W help` or see 21 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 22 | #![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, 23 | unknown_crate_types, warnings)] 24 | #![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs, 25 | non_shorthand_field_patterns, overflowing_literals, plugin_as_library, 26 | private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion, 27 | unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes, 28 | unused_comparisons, unused_features, unused_parens, while_true)] 29 | #![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, 30 | unused_qualifications, unused_results)] 31 | #![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations, 32 | missing_debug_implementations, variant_size_differences)] 33 | 34 | #![cfg_attr(feature="clippy", feature(plugin))] 35 | #![cfg_attr(feature="clippy", plugin(clippy))] 36 | #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] 37 | 38 | extern crate nat_traversal; 39 | extern crate w_result; 40 | 41 | use std::time::{Instant, Duration}; 42 | 43 | use nat_traversal::{MappingContext, SimpleUdpHolePunchServer}; 44 | use w_result::{WOk, WErr}; 45 | 46 | fn main() { 47 | println!("The example runs a simple rendezvous server that peers can use to connect to each other with"); 48 | 49 | // First, we must create a mapping context. 50 | let mapping_context = match MappingContext::new() { 51 | WOk(mapping_context, warnings) => { 52 | for warning in warnings { 53 | println!("Warning when creating mapping context: {}", warning); 54 | } 55 | mapping_context 56 | } 57 | WErr(e) => { 58 | println!("Error creating mapping context: {}", e); 59 | println!("Exiting."); 60 | return; 61 | } 62 | }; 63 | 64 | // Now we create the server. 65 | let deadline = Instant::now() + Duration::from_secs(3); 66 | let simple_server = match SimpleUdpHolePunchServer::new(Box::new(mapping_context), deadline) { 67 | WOk(simple_server, warnings) => { 68 | for warning in warnings { 69 | println!("Warning when creating simple server: {}", warning); 70 | } 71 | simple_server 72 | }, 73 | WErr(e) => { 74 | println!("Error creating simple server: {}", e); 75 | println!("Exiting."); 76 | return; 77 | } 78 | }; 79 | 80 | // Now we print the servers known addresses 81 | let addresses = simple_server.addresses(); 82 | println!("Server addresses: {:#?}", addresses); 83 | 84 | std::thread::park(); 85 | } 86 | 87 | -------------------------------------------------------------------------------- /examples/tcp-hole-punch.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! TCP hole punch example. 19 | 20 | // For explanation of lint checks, run `rustc -W help` or see 21 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 22 | #![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, 23 | unknown_crate_types, warnings)] 24 | #![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs, 25 | non_shorthand_field_patterns, overflowing_literals, plugin_as_library, 26 | private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion, 27 | unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes, 28 | unused_comparisons, unused_features, unused_parens, while_true)] 29 | #![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, 30 | unused_qualifications, unused_results)] 31 | #![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations, 32 | missing_debug_implementations, variant_size_differences)] 33 | 34 | #![cfg_attr(feature="clippy", feature(plugin))] 35 | #![cfg_attr(feature="clippy", plugin(clippy))] 36 | #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] 37 | 38 | #[allow(unused_extern_crates)] 39 | #[macro_use] 40 | extern crate maidsafe_utilities; 41 | extern crate nat_traversal; 42 | extern crate w_result; 43 | extern crate rustc_serialize; 44 | extern crate socket_addr; 45 | 46 | use std::net::ToSocketAddrs; 47 | use std::io::{Read, Write}; 48 | use std::time::{Instant, Duration}; 49 | 50 | use socket_addr::SocketAddr; 51 | use nat_traversal::{MappingContext, gen_rendezvous_info, MappedTcpSocket, tcp_punch_hole}; 52 | use w_result::{WOk, WErr}; 53 | 54 | fn main() { 55 | println!("This example allows you to connect to two hosts over TCP through NATs and firewalls."); 56 | 57 | // First, we must create a mapping context. 58 | let mapping_context = match MappingContext::new() { 59 | WOk(mapping_context, warnings) => { 60 | for warning in warnings { 61 | println!("Warning when creating mapping context: {}", warning); 62 | } 63 | mapping_context 64 | } 65 | WErr(e) => { 66 | println!("Error creating mapping context: {}", e); 67 | println!("Exiting."); 68 | return; 69 | } 70 | }; 71 | 72 | // Now we can register a set of external hole punching servers that may be needed to complete 73 | // the hole punching. 74 | loop { 75 | println!(""); 76 | println!("Enter the socket addresses of a simple hole punching server or hit return for none."); 77 | println!(""); 78 | let mut addr_str = String::new(); 79 | match std::io::stdin().read_line(&mut addr_str) { 80 | Ok(_) => (), 81 | Err(e) => { 82 | if e.kind() == std::io::ErrorKind::UnexpectedEof { 83 | println!("Exiting."); 84 | return; 85 | } 86 | println!("IO error reading stdin: {}", e); 87 | return; 88 | }, 89 | }; 90 | let addr_str = addr_str.trim(); 91 | if addr_str == "" { 92 | break; 93 | } 94 | let mut addrs = match addr_str.to_socket_addrs() { 95 | Ok(addrs) => addrs, 96 | Err(e) => { 97 | println!("Error parsing socket address: {}", e); 98 | continue; 99 | }, 100 | }; 101 | let addr = match addrs.next() { 102 | Some(addr) => SocketAddr(addr), 103 | None => { 104 | println!("Invalid value"); 105 | continue; 106 | } 107 | }; 108 | println!("Registering address: {:#?}", addr); 109 | mapping_context.add_simple_tcp_servers(vec![addr]); 110 | } 111 | 112 | // Now we use our context to create a mapped tcp socket. 113 | let deadline = Instant::now() + Duration::from_secs(5); 114 | let mapped_socket = match MappedTcpSocket::new(&mapping_context, deadline) { 115 | WOk(mapped_socket, warnings) => { 116 | for warning in warnings { 117 | println!("Warning when mapping socket: {}", warning); 118 | } 119 | mapped_socket 120 | }, 121 | WErr(e) => { 122 | println!("IO error mapping socket: {}", e); 123 | println!("Exiting."); 124 | return; 125 | } 126 | }; 127 | 128 | // A MappedTcpSocket is just a socket and set of known endpoints of the socket; 129 | let MappedTcpSocket { socket, endpoints } = mapped_socket; 130 | println!("Created a socket. It's endpoints are: {:#?}", endpoints); 131 | 132 | // Now we use the endpoints to create a rendezvous info pair 133 | let (our_priv_info, our_pub_info) = gen_rendezvous_info(endpoints); 134 | 135 | // Now we exchange our public rendezvous info with the remote peer out-of-band somehow. Yes, to 136 | // connect to the peer you already need to be able to communicate with them. Yes, network 137 | // address translation sucks. 138 | println!("Your public rendezvous info is:"); 139 | println!(""); 140 | println!("{}", unwrap_result!(rustc_serialize::json::encode(&our_pub_info))); 141 | println!(""); 142 | 143 | let their_pub_info; 144 | loop { 145 | println!("Paste the peer's pub rendezvous info below and when you are ready to initiate"); 146 | println!("the connection hit return. The peer must initiate their side of the connection"); 147 | println!("at the same time."); 148 | println!(""); 149 | 150 | let mut info_str = String::new(); 151 | match std::io::stdin().read_line(&mut info_str) { 152 | Ok(_) => (), 153 | Err(e) => { 154 | if e.kind() == std::io::ErrorKind::UnexpectedEof { 155 | println!("Exiting."); 156 | return; 157 | } 158 | println!("IO error reading stdin: {}", e); 159 | return; 160 | }, 161 | }; 162 | match rustc_serialize::json::decode(&info_str) { 163 | Ok(info) => { 164 | their_pub_info = info; 165 | break; 166 | }, 167 | Err(e) => { 168 | println!("Error decoding their public rendezvous info: {}", e); 169 | println!("Push sure to paste their complete info all in one line."); 170 | } 171 | } 172 | }; 173 | 174 | // Now we use the socket, our private rendezvous info and their public rendezvous info to 175 | // complete the connection. 176 | let mut stream = match tcp_punch_hole( 177 | socket, our_priv_info, their_pub_info, 178 | Instant::now() + Duration::from_secs(5) 179 | ) { 180 | WOk(punched_socket, warnings) => { 181 | for warning in warnings { 182 | println!("Warning when punching hole: {}", warning); 183 | } 184 | punched_socket 185 | }, 186 | WErr(e) => { 187 | println!("Error punching tcp socket: {}", e); 188 | println!("Exiting."); 189 | return; 190 | }, 191 | }; 192 | 193 | let mut recv_stream = match stream.try_clone() { 194 | Ok(recv_stream) => recv_stream, 195 | Err(e) => { 196 | println!("Failed to clone tcp stream: {}", e); 197 | println!("Exiting."); 198 | return; 199 | } 200 | }; 201 | 202 | // Now we can chat to the peer! 203 | println!("Connected! You can now chat to your buddy. ^D to exit."); 204 | 205 | let _ = thread!("recv and print", move || { 206 | let mut buf = [0u8; 1024]; 207 | loop { 208 | let n = match recv_stream.read(&mut buf[..]) { 209 | Ok(n) => n, 210 | Err(e) => { 211 | println!("IO error receiving from tcp socket: {}", e); 212 | //return; 213 | continue; 214 | } 215 | }; 216 | if n == 0 { 217 | println!("Disconnected."); 218 | return; 219 | } 220 | match std::str::from_utf8(&buf[..n]) { 221 | Ok(s) => println!("{}", s), 222 | Err(e) => println!("Peer sent invalid utf8 data. Error: {}", e), 223 | }; 224 | } 225 | }); 226 | 227 | let mut line; 228 | loop { 229 | line = String::new(); 230 | match std::io::stdin().read_line(&mut line) { 231 | Ok(_) => (), 232 | Err(e) => { 233 | if e.kind() != std::io::ErrorKind::UnexpectedEof { 234 | println!("Error reading from stdin: {}", e); 235 | } 236 | println!("Exiting."); 237 | return; 238 | } 239 | }; 240 | match stream.write_all(line.as_bytes()) { 241 | Ok(()) => (), 242 | Err(e) => { 243 | println!("Error writing to tcp stream: {}", e); 244 | println!("Exiting."); 245 | return; 246 | } 247 | }; 248 | } 249 | } 250 | 251 | -------------------------------------------------------------------------------- /examples/udp-hole-punch.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! UDP hole punch example. 19 | 20 | // For explanation of lint checks, run `rustc -W help` or see 21 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 22 | #![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, 23 | unknown_crate_types, warnings)] 24 | #![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs, 25 | non_shorthand_field_patterns, overflowing_literals, plugin_as_library, 26 | private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion, 27 | unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes, 28 | unused_comparisons, unused_features, unused_parens, while_true)] 29 | #![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, 30 | unused_qualifications, unused_results)] 31 | #![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations, 32 | missing_debug_implementations, variant_size_differences)] 33 | 34 | #![cfg_attr(feature="clippy", feature(plugin))] 35 | #![cfg_attr(feature="clippy", plugin(clippy))] 36 | #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] 37 | 38 | #[allow(unused_extern_crates)] 39 | #[macro_use] 40 | extern crate maidsafe_utilities; 41 | extern crate nat_traversal; 42 | extern crate w_result; 43 | extern crate rustc_serialize; 44 | extern crate socket_addr; 45 | 46 | use std::net::ToSocketAddrs; 47 | use std::time::{Instant, Duration}; 48 | 49 | use socket_addr::SocketAddr; 50 | use nat_traversal::{MappingContext, gen_rendezvous_info, MappedUdpSocket, PunchedUdpSocket}; 51 | use w_result::{WOk, WErr}; 52 | 53 | fn main() { 54 | println!("This example allows you to connect to two hosts over UDP through NATs and firewalls."); 55 | 56 | // First, we must create a mapping context. 57 | let mapping_context = match MappingContext::new() { 58 | WOk(mapping_context, warnings) => { 59 | for warning in warnings { 60 | println!("Warning when creating mapping context: {}", warning); 61 | } 62 | mapping_context 63 | } 64 | WErr(e) => { 65 | println!("Error creating mapping context: {}", e); 66 | println!("Exiting."); 67 | return; 68 | } 69 | }; 70 | 71 | // Now we can register a set of external hole punching servers that may be needed to complete 72 | // the hole punching. 73 | loop { 74 | println!(""); 75 | println!("Enter the socket addresses of a simple hole punching server or hit return for none."); 76 | println!(""); 77 | let mut addr_str = String::new(); 78 | match std::io::stdin().read_line(&mut addr_str) { 79 | Ok(_) => (), 80 | Err(e) => { 81 | if e.kind() == std::io::ErrorKind::UnexpectedEof { 82 | println!("Exiting."); 83 | return; 84 | } 85 | println!("IO error reading stdin: {}", e); 86 | return; 87 | }, 88 | }; 89 | let addr_str = addr_str.trim(); 90 | if addr_str == "" { 91 | break; 92 | } 93 | let mut addrs = match addr_str.to_socket_addrs() { 94 | Ok(addrs) => addrs, 95 | Err(e) => { 96 | println!("Error parsing socket address: {}", e); 97 | continue; 98 | }, 99 | }; 100 | let addr = match addrs.next() { 101 | Some(addr) => SocketAddr(addr), 102 | None => { 103 | println!("Invalid value"); 104 | continue; 105 | } 106 | }; 107 | println!("Registering address: {:#?}", addr); 108 | mapping_context.add_simple_udp_servers(vec![addr]); 109 | } 110 | 111 | // Now we use our context to create a mapped udp socket. 112 | let mapped_socket = match MappedUdpSocket::new( 113 | &mapping_context, 114 | Instant::now() + Duration::from_secs(5) 115 | ) { 116 | WOk(mapped_socket, warnings) => { 117 | for warning in warnings { 118 | println!("Warning when mapping socket: {}", warning); 119 | } 120 | mapped_socket 121 | }, 122 | WErr(e) => { 123 | println!("IO error mapping socket: {}", e); 124 | println!("Exiting."); 125 | return; 126 | } 127 | }; 128 | 129 | // A MappedUdpSocket is just a socket and set of known endpoints of the socket; 130 | let MappedUdpSocket { socket, endpoints } = mapped_socket; 131 | println!("Created a socket. It's endpoints are: {:#?}", endpoints); 132 | 133 | // Now we use the endpoints to create a rendezvous info pair 134 | let (our_priv_info, our_pub_info) = gen_rendezvous_info(endpoints); 135 | 136 | // Now we exchange our public rendezvous info with the remote peer out-of-band somehow. Yes, to 137 | // connect to the peer you already need to be able to communicate with them. Yes, network 138 | // address translation sucks. 139 | println!("Your public rendezvous info is:"); 140 | println!(""); 141 | println!("{}", unwrap_result!(rustc_serialize::json::encode(&our_pub_info))); 142 | println!(""); 143 | 144 | let their_pub_info; 145 | loop { 146 | println!("Paste the peer's pub rendezvous info below and when you are ready to initiate"); 147 | println!("the connection hit return. The peer must initiate their side of the connection"); 148 | println!("at the same time."); 149 | println!(""); 150 | 151 | let mut info_str = String::new(); 152 | match std::io::stdin().read_line(&mut info_str) { 153 | Ok(_) => (), 154 | Err(e) => { 155 | if e.kind() == std::io::ErrorKind::UnexpectedEof { 156 | println!("Exiting."); 157 | return; 158 | } 159 | println!("IO error reading stdin: {}", e); 160 | return; 161 | }, 162 | }; 163 | match rustc_serialize::json::decode(&info_str) { 164 | Ok(info) => { 165 | their_pub_info = info; 166 | break; 167 | }, 168 | Err(e) => { 169 | println!("Error decoding their public rendezvous info: {}", e); 170 | println!("Push sure to paste their complete info all in one line."); 171 | } 172 | } 173 | }; 174 | 175 | // Now we use the socket, our private rendezvous info and their public rendezvous info to 176 | // complete the connection. 177 | let deadline = Instant::now() + Duration::from_secs(5); 178 | let punched_socket = match PunchedUdpSocket::punch_hole(socket, our_priv_info, 179 | their_pub_info, deadline) 180 | { 181 | WOk(punched_socket, warnings) => { 182 | for warning in warnings { 183 | println!("Warning when punching hole: {}", warning); 184 | } 185 | punched_socket 186 | }, 187 | WErr(e) => { 188 | println!("IO error punching udp socket: {}", e); 189 | println!("Exiting."); 190 | return; 191 | }, 192 | }; 193 | 194 | // A PunchedUdpSocket is just a socket and an address that we should have unrestricted 195 | // communication to. 196 | let PunchedUdpSocket { socket, peer_addr } = punched_socket; 197 | 198 | let recv_socket = match socket.try_clone() { 199 | Ok(recv_socket) => recv_socket, 200 | Err(e) => { 201 | println!("Failed to clone udp socket: {}", e); 202 | println!("Exiting."); 203 | return; 204 | } 205 | }; 206 | 207 | // Now we can chat to the peer! 208 | println!("Connected! You can now chat to your buddy. ^D to exit."); 209 | 210 | let _ = thread!("recv and print", move || { 211 | let mut buf = [0u8; 1024]; 212 | loop { 213 | let (n, addr) = match recv_socket.recv_from(&mut buf[..]) { 214 | Ok(x) => x, 215 | Err(e) => { 216 | println!("IO error receiving from udp socket: {}", e); 217 | //return; 218 | continue; 219 | } 220 | }; 221 | if addr != peer_addr.0 { 222 | continue; 223 | } 224 | match std::str::from_utf8(&buf[..n]) { 225 | Ok(s) => println!("{}", s), 226 | Err(e) => println!("Peer sent invalid utf8 data. Error: {}", e), 227 | }; 228 | } 229 | }); 230 | 231 | let mut line; 232 | loop { 233 | line = String::new(); 234 | match std::io::stdin().read_line(&mut line) { 235 | Ok(_) => (), 236 | Err(e) => { 237 | if e.kind() != std::io::ErrorKind::UnexpectedEof { 238 | println!("Error reading from stdin: {}", e); 239 | } 240 | println!("Exiting."); 241 | return; 242 | } 243 | }; 244 | match socket.send_to(line.as_bytes(), peer_addr.0) { 245 | Ok(_) => (), 246 | Err(e) => { 247 | println!("Error writing to udp socket: {}", e); 248 | println!("Exiting."); 249 | return; 250 | } 251 | }; 252 | } 253 | } 254 | 255 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | #![doc(html_logo_url = 22 | "https://raw.githubusercontent.com/maidsafe/QA/master/Images/maidsafe_logo.png", 23 | html_favicon_url = "http://maidsafe.net/img/favicon.ico", 24 | html_root_url = "http://maidsafe.github.io/nat_traversal/")] 25 | 26 | // For explanation of lint checks, run `rustc -W help` or see 27 | // https://github.com/maidsafe/QA/blob/master/Documentation/Rust%20Lint%20Checks.md 28 | #![forbid(bad_style, exceeding_bitshifts, mutable_transmutes, no_mangle_const_items, 29 | unknown_crate_types, warnings)] 30 | #![deny(deprecated, drop_with_repr_extern, improper_ctypes, missing_docs, 31 | non_shorthand_field_patterns, overflowing_literals, plugin_as_library, 32 | private_no_mangle_fns, private_no_mangle_statics, stable_features, unconditional_recursion, 33 | unknown_lints, unsafe_code, unused, unused_allocation, unused_attributes, 34 | unused_comparisons, unused_features, unused_parens, while_true)] 35 | #![warn(trivial_casts, trivial_numeric_casts, unused_extern_crates, unused_import_braces, 36 | unused_qualifications, unused_results)] 37 | #![allow(box_pointers, fat_ptr_transmutes, missing_copy_implementations, 38 | missing_debug_implementations, variant_size_differences)] 39 | 40 | #![cfg_attr(feature="clippy", feature(plugin))] 41 | #![cfg_attr(feature="clippy", plugin(clippy))] 42 | #![cfg_attr(feature="clippy", deny(clippy, clippy_pedantic))] 43 | 44 | // TODO(canndrew): Remove this once this: https://github.com/tailhook/quick-error/issues/18 45 | // is fixed. 46 | #![allow(missing_docs)] 47 | 48 | extern crate byteorder; 49 | extern crate net2; 50 | extern crate rand; 51 | extern crate rustc_serialize; 52 | extern crate void; 53 | #[macro_use] 54 | extern crate maidsafe_utilities; 55 | extern crate igd; 56 | extern crate socket_addr; 57 | extern crate get_if_addrs; 58 | extern crate w_result; 59 | #[allow(unused_extern_crates)] // Needed because the crate is only used for macros 60 | #[macro_use] 61 | extern crate quick_error; 62 | 63 | pub use mapping_context::{MappingContext, MappingContextNewError, MappingContextNewWarning}; 64 | pub use mapped_socket_addr::MappedSocketAddr; 65 | pub use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo, 66 | gen_rendezvous_info}; 67 | pub use mapped_udp_socket::{MappedUdpSocket, MappedUdpSocketMapError, 68 | MappedUdpSocketMapWarning, MappedUdpSocketNewError}; 69 | pub use punched_udp_socket::{PunchedUdpSocket, filter_udp_hole_punch_packet}; 70 | pub use mapped_tcp_socket::{new_reusably_bound_tcp_socket, MappedTcpSocket, tcp_punch_hole, 71 | MappedTcpSocketMapError, MappedTcpSocketMapWarning, 72 | MappedTcpSocketNewError, NewReusablyBoundTcpSocketError, 73 | TcpPunchHoleWarning, TcpPunchHoleError}; 74 | pub use simple_udp_hole_punch_server::{SimpleUdpHolePunchServer, SimpleUdpHolePunchServerNewError}; 75 | pub use simple_tcp_hole_punch_server::{SimpleTcpHolePunchServer, SimpleTcpHolePunchServerNewError}; 76 | 77 | mod mapping_context; 78 | mod mapped_socket_addr; 79 | mod rendezvous_info; 80 | mod mapped_udp_socket; 81 | mod punched_udp_socket; 82 | mod mapped_tcp_socket; 83 | mod simple_udp_hole_punch_server; 84 | mod simple_tcp_hole_punch_server; 85 | mod socket_utils; 86 | mod listener_message; 87 | mod utils; 88 | 89 | -------------------------------------------------------------------------------- /src/listener_message.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | use socket_addr::SocketAddr; 19 | 20 | pub const REQUEST_MAGIC_CONSTANT: [u8; 4] = ['E' as u8, 'C' as u8, 'H' as u8, 'O' as u8]; 21 | 22 | #[derive(RustcEncodable, RustcDecodable)] 23 | pub struct EchoExternalAddr { 24 | pub external_addr: SocketAddr, 25 | } 26 | -------------------------------------------------------------------------------- /src/mapped_socket_addr.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use socket_addr::SocketAddr; 22 | 23 | /// A socket address obtained through some mapping technique. 24 | #[derive(Debug, PartialEq, Eq, Clone, RustcEncodable, RustcDecodable)] 25 | pub struct MappedSocketAddr { 26 | /// The mapped address 27 | pub addr: SocketAddr, 28 | 29 | /// Indicated that hole punching needs to be used for an external client to connect to this 30 | /// address. `nat_restricted` will not be set if this is a fully mapped address such as the 31 | /// external address of a full-cone NAT or one obtained through UPnP. 32 | pub nat_restricted: bool, 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/mapped_tcp_socket.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use std::net; 22 | use std::net::{IpAddr, Ipv4Addr, TcpStream}; 23 | use std::io; 24 | use std::io::{Read, Write}; 25 | use std::time::{Instant, Duration}; 26 | use std::thread; 27 | use std::str; 28 | use std::sync::mpsc; 29 | use std::fmt; 30 | use std::sync::atomic::{AtomicBool, Ordering}; 31 | use std::sync::Arc; 32 | 33 | use igd; 34 | use net2; 35 | use socket_addr::SocketAddr; 36 | use w_result::{WResult, WErr, WOk}; 37 | use maidsafe_utilities::serialisation::{deserialise, SerialisationError}; 38 | use rand::random; 39 | use byteorder::{ReadBytesExt, WriteBytesExt, BigEndian}; 40 | 41 | use mapping_context::MappingContext; 42 | use mapped_socket_addr::MappedSocketAddr; 43 | use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo}; 44 | use rendezvous_info; 45 | use socket_utils; 46 | use mapping_context; 47 | use listener_message; 48 | use utils::DisplaySlice; 49 | 50 | /// A tcp socket for which we know our external endpoints. 51 | pub struct MappedTcpSocket { 52 | /// A bound, but neither listening or connected tcp socket. The socket is 53 | /// bound to be reuseable (ie. SO_REUSEADDR is set as is SO_REUSEPORT on 54 | /// unix). 55 | pub socket: net2::TcpBuilder, 56 | /// The known endpoints of this socket. 57 | pub endpoints: Vec, 58 | } 59 | 60 | quick_error! { 61 | /// Errors returned by MappedTcpSocket::map 62 | #[derive(Debug)] 63 | pub enum MappedTcpSocketMapError { 64 | /// Error getting local address of the socket. Most likely, bind() has not been called on 65 | /// the socket. 66 | SocketLocalAddr { err: io::Error } { 67 | description("Error getting local address of socket \ 68 | (have you called bind() on the socket?)") 69 | display("Error getting local address of socket. \ 70 | TcpBuilder::local_addr returned an error: {} \ 71 | (have you called bind() on the socket?).", 72 | err) 73 | cause(err) 74 | } 75 | } 76 | } 77 | 78 | impl From for io::Error { 79 | fn from(e: MappedTcpSocketMapError) -> io::Error { 80 | let err_str = format!("{}", e); 81 | let kind = match e { 82 | MappedTcpSocketMapError::SocketLocalAddr { err } => err.kind(), 83 | }; 84 | io::Error::new(kind, err_str) 85 | } 86 | } 87 | 88 | quick_error! { 89 | /// Warnings raised by MappedTcpSocket::map 90 | #[derive(Debug)] 91 | pub enum MappedTcpSocketMapWarning { 92 | /// Error searching for an IGD gateway. 93 | FindGateway { 94 | err: igd::SearchError 95 | } { 96 | description("Error searching for IGD gateway") 97 | display("Error searching for IGD gateway. \ 98 | igd::search_gateway_from_timeout returned an error: {}", 99 | err) 100 | cause(err) 101 | } 102 | /// Error mapping external address and port through IGD gateway. 103 | GetExternalPort { 104 | gateway_addr: net::SocketAddrV4, 105 | err: igd::AddAnyPortError, 106 | } { 107 | description("Error mapping external address and port through IGD \ 108 | gateway") 109 | display("Error mapping external address and port through IGD \ 110 | gateway at address {}. igd::Gateway::get_any_address \ 111 | returned an error: {}", gateway_addr, err) 112 | cause(err) 113 | } 114 | /// Error creating a reusably bound temporary socket for mapping. 115 | NewReusablyBoundTcpSocket { err: NewReusablyBoundTcpSocketError } { 116 | description("Error creating a reusably bound temporary socket for mapping.") 117 | display("Error creating a reusably bound temporary socket for mapping: {}", err) 118 | cause(err) 119 | } 120 | /// Error connecting to a mapping server. 121 | MappingSocketConnect { 122 | addr: SocketAddr, 123 | err: io::Error 124 | } { 125 | description("Error connecting to a mapping server.") 126 | display("Error connecting to mapping server at address {}. connect() returned an \ 127 | error: {}", addr, err) 128 | cause(err) 129 | } 130 | /// Error writing to temporary socket. 131 | MappingSocketWrite { err: io::Error } { 132 | description("Error writing to temporary socket.") 133 | display("Error writing to temporary socket: {}", err) 134 | cause(err) 135 | } 136 | /// Error reading from temporary socket. 137 | MappingSocketRead { err: io::Error } { 138 | description("Error reading from temporary socket.") 139 | display("Error reading from temporary socket: {}", err) 140 | cause(err) 141 | } 142 | /// Error deserialising a response from a mapping server. 143 | Deserialise { addr: SocketAddr, err: SerialisationError, response: Vec } { 144 | description("Error deserialising a response from a mapping server. Are you sure \ 145 | you've connected to a mapping server?") 146 | display("Error deserialising a response from mapping server at address {}: {}. \ 147 | Response: \"{}\". Are you sure you've connected to a mapping server?", 148 | addr, err, { 149 | match str::from_utf8(response) { 150 | Ok(r) => r, 151 | Err(e) => "", 152 | } 153 | } 154 | ) 155 | } 156 | } 157 | } 158 | 159 | quick_error! { 160 | /// Errors returned by MappedTcpSocket::new 161 | #[derive(Debug)] 162 | pub enum MappedTcpSocketNewError { 163 | /// Error creating a reusably bound tcp socket. 164 | NewReusablyBoundTcpSocket { err: NewReusablyBoundTcpSocketError } { 165 | description("Error creating a reusably bound tcp socket.") 166 | display("Error creating a reusably bound tcp socket: {}", err) 167 | cause(err) 168 | } 169 | /// Error mapping new socket. 170 | Map { err: MappedTcpSocketMapError } { 171 | description("Error mapping new socket") 172 | display("Error mapping new socket: {}", err) 173 | cause(err) 174 | } 175 | } 176 | } 177 | 178 | impl From for io::Error { 179 | fn from(e: MappedTcpSocketNewError) -> io::Error { 180 | let err_str = format!("{}", e); 181 | let kind = match e { 182 | MappedTcpSocketNewError::NewReusablyBoundTcpSocket { err } => { 183 | let err: io::Error = From::from(err); 184 | err.kind() 185 | }, 186 | MappedTcpSocketNewError::Map { err } => { 187 | let err: io::Error = From::from(err); 188 | err.kind() 189 | }, 190 | }; 191 | io::Error::new(kind, err_str) 192 | } 193 | } 194 | 195 | quick_error! { 196 | /// Errors returned by new_reusably_bound_tcp_socket 197 | #[derive(Debug)] 198 | pub enum NewReusablyBoundTcpSocketError { 199 | /// Error creating socket. 200 | Create { err: io::Error } { 201 | description("Error creating socket.") 202 | display("Error creating socket: {}", err) 203 | cause(err) 204 | } 205 | /// Error setting SO_REUSEADDR on socket. 206 | EnableReuseAddr { err: io::Error } { 207 | description("Error setting SO_REUSEADDR on socket.") 208 | display("Error setting SO_REUSEADDR on socket. \ 209 | Got IO error: {}", err) 210 | cause(err) 211 | } 212 | /// Error setting SO_REUSEPORT (or equivalent) on socket. 213 | EnableReusePort { err: io::Error } { 214 | description("Error setting SO_REUSEPORT (or equivalent) on socket.") 215 | display("Error setting SO_REUSEPORT (or equivalent) on socket. \ 216 | Got IO error: {}", err) 217 | cause(err) 218 | } 219 | /// Error binding new socket to the provided address. Likely a socket was already bound to 220 | /// this address without SO_REUSEPORT and SO_REUSEADDR being set. 221 | Bind { err: io::Error } { 222 | description("Error binding new socket to the provided address. Likely a socket was \ 223 | already bound to this address without SO_REUSEPORT and SO_REUSEADDR \ 224 | being set") 225 | display("Error binding new socket to the provided address: {}. Likely a socket was \ 226 | already bound to this address without SO_REUSEPORT and SO_REUSEADDR being \ 227 | set", err) 228 | cause(err) 229 | } 230 | } 231 | } 232 | 233 | impl From for io::Error { 234 | fn from(e: NewReusablyBoundTcpSocketError) -> io::Error { 235 | let err_str = format!("{}", e); 236 | let kind = match e { 237 | NewReusablyBoundTcpSocketError::Create { err } => err.kind(), 238 | NewReusablyBoundTcpSocketError::EnableReuseAddr { err } => err.kind(), 239 | NewReusablyBoundTcpSocketError::EnableReusePort { err } => err.kind(), 240 | NewReusablyBoundTcpSocketError::Bind { err } => err.kind(), 241 | }; 242 | io::Error::new(kind, err_str) 243 | } 244 | } 245 | 246 | pub fn new_reusably_bound_tcp_socket(local_addr: &net::SocketAddr) -> Result { 247 | let socket_res = match local_addr.ip() { 248 | IpAddr::V4(..) => net2::TcpBuilder::new_v4(), 249 | IpAddr::V6(..) => net2::TcpBuilder::new_v6(), 250 | }; 251 | let socket = match socket_res { 252 | Ok(socket) => socket, 253 | Err(e) => return Err(NewReusablyBoundTcpSocketError::Create { err: e }), 254 | }; 255 | match socket.reuse_address(true) { 256 | Ok(_) => (), 257 | Err(e) => return Err(NewReusablyBoundTcpSocketError::EnableReuseAddr { err: e }), 258 | }; 259 | match socket_utils::enable_so_reuseport(&socket) { 260 | Ok(()) => (), 261 | Err(e) => return Err(NewReusablyBoundTcpSocketError::EnableReusePort { err: e }), 262 | }; 263 | match socket.bind(local_addr) { 264 | Ok(..) => (), 265 | Err(e) => return Err(NewReusablyBoundTcpSocketError::Bind { err: e }), 266 | }; 267 | Ok(socket) 268 | } 269 | 270 | impl MappedTcpSocket { 271 | /// Map an existing tcp socket. The socket must bound but not connected. It must have been 272 | /// bound with SO_REUSEADDR and SO_REUSEPORT options (or equivalent) set. 273 | pub fn map(socket: net2::TcpBuilder, mc: &MappingContext, deadline: Instant) 274 | -> WResult 275 | { 276 | let mut endpoints = Vec::new(); 277 | let mut warnings = Vec::new(); 278 | 279 | let local_addr = match socket_utils::tcp_builder_local_addr(&socket) { 280 | Ok(local_addr) => local_addr, 281 | Err(e) => return WErr(MappedTcpSocketMapError::SocketLocalAddr { err: e }), 282 | }; 283 | match local_addr.ip() { 284 | IpAddr::V4(ipv4_addr) => { 285 | if socket_utils::ipv4_is_unspecified(&ipv4_addr) { 286 | // If the socket address is unspecified we add an address for every local 287 | // interface. We also ask the interface's IGD gateway (if there is one) for 288 | // an address. 289 | for iface_v4 in mapping_context::interfaces_v4(&mc) { 290 | let local_iface_addr = net::SocketAddrV4::new(iface_v4.addr, local_addr.port()); 291 | endpoints.push(MappedSocketAddr { 292 | addr: SocketAddr(net::SocketAddr::V4(local_iface_addr)), 293 | nat_restricted: false, 294 | }); 295 | if let Some(gateway) = iface_v4.gateway { 296 | match gateway.get_any_address(igd::PortMappingProtocol::TCP, 297 | local_iface_addr, 0, 298 | "rust nat_traversal") 299 | { 300 | Ok(external_addr) => { 301 | endpoints.push(MappedSocketAddr { 302 | addr: SocketAddr(net::SocketAddr::V4(external_addr)), 303 | nat_restricted: false, 304 | }); 305 | }, 306 | Err(e) => { 307 | warnings.push(MappedTcpSocketMapWarning::GetExternalPort { 308 | gateway_addr: gateway.addr, 309 | err: e, 310 | }); 311 | } 312 | } 313 | }; 314 | }; 315 | } 316 | else { 317 | let local_addr_v4 = net::SocketAddrV4::new(ipv4_addr, local_addr.port()); 318 | endpoints.push(MappedSocketAddr { 319 | addr: SocketAddr(net::SocketAddr::V4(local_addr_v4)), 320 | nat_restricted: false, 321 | }); 322 | 323 | // If the local address is the address of an interface then we can avoid 324 | // searching for an IGD gateway, just reuse the search result from when we 325 | // found this interface. 326 | let mut gateway_opt_opt = None; 327 | for iface_v4 in mapping_context::interfaces_v4(&mc) { 328 | if iface_v4.addr == ipv4_addr { 329 | gateway_opt_opt = Some(iface_v4.gateway); 330 | break; 331 | } 332 | }; 333 | let gateway_opt = match gateway_opt_opt { 334 | Some(gateway_opt) => gateway_opt, 335 | // We don't where this local address came from so search for an IGD gateway 336 | // at it. 337 | None => { 338 | match igd::search_gateway_from_timeout(ipv4_addr, Duration::from_secs(1)) { 339 | Ok(gateway) => Some(gateway), 340 | Err(e) => { 341 | warnings.push(MappedTcpSocketMapWarning::FindGateway { 342 | err: e 343 | }); 344 | None 345 | } 346 | } 347 | } 348 | }; 349 | // If we have a gateway, ask it for an external address. 350 | if let Some(gateway) = gateway_opt { 351 | match gateway.get_any_address(igd::PortMappingProtocol::TCP, 352 | local_addr_v4, 0, 353 | "rust nat_traversal") 354 | { 355 | Ok(external_addr) => { 356 | endpoints.push(MappedSocketAddr { 357 | addr: SocketAddr(net::SocketAddr::V4(external_addr)), 358 | nat_restricted: false, 359 | }); 360 | }, 361 | Err(e) => { 362 | warnings.push(MappedTcpSocketMapWarning::GetExternalPort { 363 | gateway_addr: gateway.addr, 364 | err: e, 365 | }); 366 | } 367 | } 368 | }; 369 | }; 370 | }, 371 | IpAddr::V6(ipv6_addr) => { 372 | if socket_utils::ipv6_is_unspecified(&ipv6_addr) { 373 | // If the socket address is unspecified add an address for every interface. 374 | for iface_v6 in mapping_context::interfaces_v6(&mc) { 375 | let local_iface_addr = net::SocketAddr::V6(net::SocketAddrV6::new(iface_v6.addr, local_addr.port(), 0, 0)); 376 | endpoints.push(MappedSocketAddr { 377 | addr: SocketAddr(local_iface_addr), 378 | nat_restricted: false, 379 | }); 380 | }; 381 | } 382 | else { 383 | endpoints.push(MappedSocketAddr { 384 | addr: SocketAddr(net::SocketAddr::V6(net::SocketAddrV6::new(ipv6_addr, local_addr.port(), 0, 0))), 385 | nat_restricted: false, 386 | }); 387 | } 388 | }, 389 | }; 390 | 391 | let (results_tx, results_rx) = mpsc::channel(); 392 | let mut mapping_threads = Vec::new(); 393 | let simple_servers = mapping_context::simple_tcp_servers(&mc); 394 | for simple_server in simple_servers { 395 | // TODO(canndrew): Remove this. Ideally we should use servers that are on private 396 | // networks in case we're behind multiple private networks. This will require using 397 | // non-blocking IO however. 398 | match simple_server.ip() { 399 | IpAddr::V4(ipv4_addr) => { 400 | if ipv4_addr.is_private() || ipv4_addr.is_loopback() { 401 | continue; 402 | }; 403 | }, 404 | IpAddr::V6(ipv6_addr) => { 405 | if ipv6_addr.is_loopback() { 406 | continue; 407 | }; 408 | }, 409 | }; 410 | let results_tx = results_tx.clone(); 411 | mapping_threads.push(thread::spawn(move || { 412 | let map = move || { 413 | let mapping_socket = match new_reusably_bound_tcp_socket(&local_addr) { 414 | Ok(mapping_socket) => mapping_socket, 415 | Err(e) => return Err(MappedTcpSocketMapWarning::NewReusablyBoundTcpSocket { err: e }), 416 | }; 417 | let mut stream = match mapping_socket.connect(&*simple_server) { 418 | Ok(stream) => stream, 419 | Err(e) => return Err(MappedTcpSocketMapWarning::MappingSocketConnect { 420 | addr: simple_server, 421 | err: e 422 | }), 423 | }; 424 | let send_data = listener_message::REQUEST_MAGIC_CONSTANT; 425 | // TODO(canndrew): What should we do if we get a partial write? 426 | let _ = match stream.write(&send_data[..]) { 427 | Ok(n) => n, 428 | Err(e) => return Err(MappedTcpSocketMapWarning::MappingSocketWrite { err: e }), 429 | }; 430 | 431 | const MAX_DATAGRAM_SIZE: usize = 256; 432 | let mut recv_data = [0u8; MAX_DATAGRAM_SIZE]; 433 | let n = match stream.read(&mut recv_data[..]) { 434 | Ok(n) => n, 435 | Err(e) => return Err(MappedTcpSocketMapWarning::MappingSocketRead { err: e }), 436 | }; 437 | let listener_message::EchoExternalAddr { external_addr } = match deserialise::(&recv_data[..n]) { 438 | Ok(msg) => msg, 439 | Err(e) => return Err(MappedTcpSocketMapWarning::Deserialise { 440 | addr: simple_server, 441 | err: e, 442 | response: recv_data[..n].to_vec(), 443 | }), 444 | }; 445 | Ok(external_addr) 446 | }; 447 | let _ = results_tx.send(Some(map())); 448 | })); 449 | } 450 | let timeout_thread = thread!("MappedTcpSocket::map timeout", move || { 451 | let now = Instant::now(); 452 | if deadline > now { 453 | let timeout = deadline - now; 454 | thread::park_timeout(timeout); 455 | } 456 | let _ = results_tx.send(None); 457 | }); 458 | 459 | let mut num_results = 0; 460 | for result in results_rx { 461 | match result { 462 | Some(Ok(external_addr)) => { 463 | endpoints.push(MappedSocketAddr { 464 | addr: external_addr, 465 | nat_restricted: true, 466 | }); 467 | num_results += 1; 468 | if num_results >= 2 { 469 | break; 470 | } 471 | }, 472 | Some(Err(e)) => { 473 | warnings.push(e); 474 | }, 475 | None => { 476 | break; 477 | }, 478 | } 479 | } 480 | 481 | timeout_thread.thread().unpark(); 482 | WOk(MappedTcpSocket { 483 | socket: socket, 484 | endpoints: endpoints, 485 | }, warnings) 486 | } 487 | 488 | /// Create a new `MappedTcpSocket` 489 | pub fn new(mc: &MappingContext, deadline: Instant) 490 | -> WResult 491 | { 492 | let unspec_addr = net::SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 0); 493 | let socket = match new_reusably_bound_tcp_socket(&unspec_addr) { 494 | Ok(socket) => socket, 495 | Err(e) => return WErr(MappedTcpSocketNewError::NewReusablyBoundTcpSocket { err: e }), 496 | }; 497 | 498 | MappedTcpSocket::map(socket, mc, deadline).map_err(|e| MappedTcpSocketNewError::Map { err: e }) 499 | } 500 | } 501 | 502 | quick_error! { 503 | #[derive(Debug)] 504 | pub enum TcpPunchHoleWarning { 505 | /// Connecting to endpoint failed. 506 | Connect { peer_addr: SocketAddr, err: io::Error } { 507 | description("Connecting to endpoint failed.") 508 | display("Connecting to endpoint {} failed: {}", peer_addr, err) 509 | cause(err) 510 | } 511 | /// Error accepting an incoming connection. 512 | Accept { err: io::Error } { 513 | description("Error accepting an incoming connection.") 514 | display("Error accepting an incoming connection: {}", err) 515 | cause(err) 516 | } 517 | /// Error setting the timeout on a connected stream. 518 | StreamSetTimeout { err: io::Error } { 519 | description("Error setting the timeout on a connected stream.") 520 | display("Error setting the timeout on a connected stream: {}", err) 521 | cause(err) 522 | } 523 | /// IO error communicating with a connected host. 524 | StreamIo { peer_addr: SocketAddr, err: io::Error } { 525 | description("IO error communicating with a connected host.") 526 | display("IO error communicating with connected host at {}: {}", peer_addr, err) 527 | cause(err) 528 | } 529 | /// A connected host provided an invalid response to the handshake. 530 | InvalidResponse { peer_addr: SocketAddr, data: [u8; 4] } { 531 | description("A connected host provided an invalid response to the handshake.") 532 | display("The connected host at {} provided an invalid response to the handshake: {:?}", peer_addr, data) 533 | } 534 | } 535 | } 536 | 537 | #[derive(Debug)] 538 | pub struct TcpPunchHoleBrokenStream { 539 | peer_addr: SocketAddr, 540 | error: io::Error, 541 | } 542 | 543 | impl fmt::Display for TcpPunchHoleBrokenStream { 544 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 545 | write!(f, "Broken stream with peer addr {} errored: {}", self.peer_addr, self.error) 546 | } 547 | } 548 | 549 | quick_error! { 550 | #[derive(Debug)] 551 | pub enum TcpPunchHoleError { 552 | /// Error getting the local address of the provided socket. 553 | SocketLocalAddr { err: io::Error } { 554 | description("Error getting the local address of the provided socket.") 555 | display("Error getting the local address of the provided socket: {}", err) 556 | cause(err) 557 | } 558 | /// Error binding another socket to the same local address as the provided socket. 559 | NewReusablyBoundTcpSocket { err: NewReusablyBoundTcpSocketError } { 560 | description("Error binding another socket to the same local address as the provided socket.") 561 | display("Error binding another socket to the same local address as the provided socket: {}", err) 562 | cause(err) 563 | } 564 | /// Error listening on the provided socket. 565 | Listen { err: io::Error } { 566 | description("Error listening on the provided socket.") 567 | display("Error listening on the provided socket: {}", err) 568 | cause(err) 569 | } 570 | /// Tcp hole punching timed out without making a successful connection. 571 | TimedOut { warnings: Vec } { 572 | description("Tcp hole punching timed out without making a successful connection.") 573 | display("Tcp hole punching timed out without making a successful connection. The \ 574 | following warnings were raised during hole punching: {}", DisplaySlice("warning", &warnings)) 575 | } 576 | /// Multiple streams were successfully punched to the peer but all of them died. 577 | DecideStream { errors: Vec } { 578 | description("Multiple streams were successfully punched to the peer but all of them died.") 579 | display("Multiple streams were successfully punched to the peer but all of them died. {}", DisplaySlice("broken stream", &errors)) 580 | } 581 | } 582 | } 583 | 584 | impl From for io::Error { 585 | fn from(e: TcpPunchHoleError) -> io::Error { 586 | let err_str = format!("{}", e); 587 | let kind = match e { 588 | TcpPunchHoleError::SocketLocalAddr { err } => err.kind(), 589 | TcpPunchHoleError::NewReusablyBoundTcpSocket { err } => { 590 | let err: io::Error = From::from(err); 591 | err.kind() 592 | }, 593 | TcpPunchHoleError::Listen { err } => err.kind(), 594 | TcpPunchHoleError::TimedOut { .. } => io::ErrorKind::TimedOut, 595 | TcpPunchHoleError::DecideStream { errors } 596 | => errors.first().map(|bs| bs.error.kind()).unwrap_or(io::ErrorKind::Other), 597 | }; 598 | io::Error::new(kind, err_str) 599 | } 600 | } 601 | 602 | /// Perform a tcp rendezvous connect. `socket` should have been obtained from a 603 | /// `MappedTcpSocket`. 604 | pub fn tcp_punch_hole(socket: net2::TcpBuilder, 605 | our_priv_rendezvous_info: PrivRendezvousInfo, 606 | their_pub_rendezvous_info: PubRendezvousInfo, 607 | deadline: Instant) 608 | -> WResult { 609 | // In order to do tcp hole punching we connect to all of their endpoints in parallel while 610 | // simultaneously listening. All the sockets we use must be bound to the same local address. As 611 | // soon as we successfully connect and exchange secrets, or accept and exchange secrets, we 612 | // return. 613 | // 614 | // It would be way better to implement this using non-blocking sockets but at the moment mio 615 | // doesn't provide any way to convert a non-blocking socket back into a blocking socket. So 616 | // we're stuck with spawning (and then detaching) loads of threads. Setting the read/write 617 | // timeouts should prevent the detached threads from leaking indefinitely. 618 | 619 | let mut warnings = Vec::new(); 620 | 621 | let shutdown = Arc::new(AtomicBool::new(false)); 622 | 623 | // The channel we will use to collect the results from the many worker threads. 624 | let (results_tx, results_rx) = mpsc::channel::>>(); 625 | 626 | let our_secret = rendezvous_info::get_priv_secret(our_priv_rendezvous_info); 627 | let (their_endpoints, their_secret) = rendezvous_info::decompose(their_pub_rendezvous_info); 628 | 629 | let local_addr = match socket_utils::tcp_builder_local_addr(&socket) { 630 | Ok(local_addr) => local_addr, 631 | Err(e) => return WErr(TcpPunchHoleError::SocketLocalAddr { err: e }), 632 | }; 633 | 634 | // Try connecting to every potential endpoint in a seperate thread. 635 | for endpoint in their_endpoints { 636 | let addr = endpoint.addr; 637 | // Important to call new_reusably_bound_tcp_socket outside the inner thread so that it's called 638 | // before the listen() call below. 639 | let mapping_socket = match new_reusably_bound_tcp_socket(&local_addr) { 640 | Ok(mapping_socket) => mapping_socket, 641 | Err(e) => return WErr(TcpPunchHoleError::NewReusablyBoundTcpSocket { err: e }), 642 | }; 643 | let results_tx_clone = results_tx.clone(); 644 | let shutdown_clone = shutdown.clone(); 645 | let _ = thread!("tcp_punch_hole connect", move || { 646 | let f = |timeout| { 647 | let mut stream = match mapping_socket.connect(&*addr) { 648 | Ok(stream) => stream, 649 | Err(e) => return Err(TcpPunchHoleWarning::Connect { 650 | peer_addr: addr, 651 | err: e, 652 | }), 653 | }; 654 | match stream.set_write_timeout(Some(timeout)) { 655 | Ok(()) => (), 656 | Err(e) => return Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }), 657 | }; 658 | match stream.set_read_timeout(Some(timeout)) { 659 | Ok(()) => (), 660 | Err(e) => return Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }), 661 | }; 662 | match stream.write_all(&our_secret[..]) { 663 | Ok(()) => (), 664 | Err(e) => return Err(TcpPunchHoleWarning::StreamIo { 665 | peer_addr: addr, 666 | err: e, 667 | }), 668 | }; 669 | let mut recv_data = [0u8; 4]; 670 | match stream.read_exact(&mut recv_data[..]) { 671 | Ok(()) => (), 672 | Err(e) => return Err(TcpPunchHoleWarning::StreamIo { 673 | peer_addr: addr, 674 | err: e, 675 | }), 676 | }; 677 | if recv_data != their_secret { 678 | return Err(TcpPunchHoleWarning::InvalidResponse { 679 | peer_addr: addr, 680 | data: recv_data, 681 | }); 682 | }; 683 | match stream.set_write_timeout(None) { 684 | Ok(()) => (), 685 | Err(e) => return Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }), 686 | }; 687 | match stream.set_read_timeout(None) { 688 | Ok(()) => (), 689 | Err(e) => return Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }), 690 | }; 691 | Ok(stream) 692 | }; 693 | loop { 694 | let now = Instant::now(); 695 | if now >= deadline || shutdown_clone.load(Ordering::SeqCst) { 696 | break; 697 | } 698 | else { 699 | let timeout = deadline - now; 700 | match f(timeout) { 701 | Ok(stream) => { 702 | let _ = results_tx_clone.send(Some(Ok((stream, addr)))); 703 | break; 704 | }, 705 | Err(e) => { 706 | let _ = results_tx_clone.send(Some(Err(e))); 707 | // So we don't continuously hammer an address we can't connect to. 708 | thread::sleep(Duration::from_secs(1)); 709 | continue; 710 | }, 711 | } 712 | } 713 | } 714 | }); 715 | }; 716 | 717 | // Listen for incoming connections. 718 | let listener = match socket.listen(128) { 719 | Ok(listener) => listener, 720 | Err(e) => return WErr(TcpPunchHoleError::Listen { err: e }), 721 | }; 722 | let results_tx_clone = results_tx.clone(); 723 | let shutdown_clone = shutdown.clone(); 724 | let _ = thread!("tcp_punch_hole listen", move || { 725 | loop { 726 | let res = listener.accept(); 727 | // First, check if we should shutdown. 728 | if shutdown_clone.load(Ordering::SeqCst) { 729 | break; 730 | }; 731 | let (mut stream, addr) = match res { 732 | Ok(x) => x, 733 | Err(e) => { 734 | match results_tx_clone.send(Some(Err(TcpPunchHoleWarning::Accept { err: e }))) { 735 | Ok(()) => (), 736 | Err(_) => { 737 | break; 738 | }, 739 | } 740 | continue; 741 | }, 742 | }; 743 | 744 | // Spawn a new thread here to prevent someone from connecting then not sending any data 745 | // and preventing us from accepting any more connections. 746 | let results_tx_clone = results_tx_clone.clone(); 747 | let now = Instant::now(); 748 | if now >= deadline { 749 | break; 750 | }; 751 | let _ = thread!("tcp_punch_hole listen handshake", move || { 752 | let timeout = deadline - now; 753 | match stream.set_write_timeout(Some(timeout)) { 754 | Ok(()) => (), 755 | Err(e) => { 756 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }))); 757 | return; 758 | }, 759 | }; 760 | match stream.set_read_timeout(Some(timeout)) { 761 | Ok(()) => (), 762 | Err(e) => { 763 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }))); 764 | return; 765 | }, 766 | }; 767 | match stream.write_all(&our_secret[..]) { 768 | Ok(()) => (), 769 | Err(e) => { 770 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamIo { 771 | peer_addr: SocketAddr(addr), 772 | err: e, 773 | }))); 774 | return; 775 | }, 776 | }; 777 | let mut recv_data = [0u8; 4]; 778 | match stream.read_exact(&mut recv_data[..]) { 779 | Ok(()) => (), 780 | Err(e) => { 781 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamIo { 782 | peer_addr: SocketAddr(addr), 783 | err: e, 784 | }))); 785 | return; 786 | }, 787 | }; 788 | if recv_data != their_secret { 789 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::InvalidResponse { 790 | peer_addr: SocketAddr(addr), 791 | data: recv_data, 792 | }))); 793 | return; 794 | } 795 | match stream.set_write_timeout(None) { 796 | Ok(()) => (), 797 | Err(e) => { 798 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }))); 799 | return; 800 | }, 801 | }; 802 | match stream.set_read_timeout(None) { 803 | Ok(()) => (), 804 | Err(e) => { 805 | let _ = results_tx_clone.send(Some(Err(TcpPunchHoleWarning::StreamSetTimeout { err: e }))); 806 | return; 807 | }, 808 | }; 809 | let _ = results_tx_clone.send(Some(Ok((stream, SocketAddr(addr))))); 810 | }); 811 | } 812 | }); 813 | 814 | // Create a separate thread for timing out. 815 | // TODO(canndrew): We won't need to do this one this is fixed: https://github.com/rust-lang/rfcs/issues/962 816 | let results_tx_clone = results_tx.clone(); 817 | let timeout_thread = thread!("tcp_punch_hole timeout", move || { 818 | let now = Instant::now(); 819 | if deadline > now { 820 | let timeout = deadline - now; 821 | thread::park_timeout(timeout); 822 | } 823 | let _ = results_tx_clone.send(None); 824 | }); 825 | let timeout_thread_handle = timeout_thread.thread(); 826 | 827 | drop(results_tx); 828 | let acceptor_addr = net::SocketAddr::new(socket_utils::ip_unspecified_to_loopback(local_addr.ip()), 829 | local_addr.port()); 830 | // Process the results that the worker threads send us. 831 | loop { 832 | match results_rx.recv() { 833 | // All the senders have closed. This could only happen if all of the worker threads 834 | // panicked. Propogate the panic. 835 | Err(_) => panic!("In tcp_punch_hole results_rx.recv() returned Err"), 836 | 837 | // We timed out. 838 | Ok(None) => { 839 | timeout_thread_handle.unpark(); 840 | shutdown.store(true, Ordering::SeqCst); 841 | let _ = TcpStream::connect(&acceptor_addr); 842 | return WErr(TcpPunchHoleError::TimedOut { warnings: warnings }); 843 | }, 844 | 845 | // One of the worker threads raised a warning. 846 | Ok(Some(Err(e))) => { 847 | warnings.push(e); 848 | }, 849 | 850 | // Success! 851 | Ok(Some(Ok((stream, stream_addr)))) => { 852 | shutdown.store(true, Ordering::SeqCst); 853 | // Cause the acceptor to shut down. 854 | let _ = TcpStream::connect(&acceptor_addr); 855 | 856 | let mut other_streams = Vec::new(); 857 | // Collect any other successful connections that may have occured simultaneously to 858 | // the one we've got. 859 | for stream_res_opt in results_rx { 860 | match stream_res_opt { 861 | // Timed out. Assume all other connections failed. 862 | None => break, 863 | // A worker thread had an error. But we don't care because we've already 864 | // got a successful connection. 865 | Some(Err(_)) => (), 866 | // We made another connection. 867 | Some(Ok((stream, stream_addr))) => other_streams.push((stream, stream_addr)), 868 | }; 869 | } 870 | 871 | timeout_thread_handle.unpark(); 872 | 873 | if other_streams.len() == 0 { 874 | return WOk(stream, warnings); 875 | } 876 | else { 877 | // We have more than one stream. Both sides need to agree on which stream to 878 | // keep and which streams to discard. To decide, we write and read a random u64 879 | // to each stream, sum the read and written values, and take the stream with 880 | // the highest sum. 881 | 882 | let mut errors = Vec::new(); 883 | other_streams.push((stream, stream_addr)); 884 | 885 | // Write the random u64 to each stream. 886 | let streams: Vec<(TcpStream, SocketAddr, u64)> = other_streams.into_iter().filter_map(|(mut stream, stream_addr)| { 887 | let w = random(); 888 | match stream.write_u64::(w) { 889 | Ok(()) => (), 890 | Err(e) => { 891 | errors.push(TcpPunchHoleBrokenStream { 892 | peer_addr: stream_addr, 893 | error: e, 894 | }); 895 | return None; 896 | }, 897 | }; 898 | Some((stream, stream_addr, w)) 899 | }).collect(); 900 | 901 | // Read the random u64 from each stream while keeping hold of the stream with 902 | // the highest sum so far. 903 | let stream_opt = streams.into_iter().fold(None, |opt, (mut this_stream, this_stream_addr, w)| { 904 | // Calculate the sum for this stream. 905 | let this_sum = match this_stream.read_u64::() { 906 | Ok(r) => r.wrapping_add(w), 907 | Err(e) => { 908 | errors.push(TcpPunchHoleBrokenStream { 909 | peer_addr: this_stream_addr, 910 | error: e, 911 | }); 912 | return opt; 913 | }, 914 | }; 915 | // If the sum is greater than the current highest (or we don't have a 916 | // current highest yet), replace the highest stream and sum with this 917 | // stream and sum. 918 | match opt { 919 | Some((top_stream, top_sum)) => { 920 | if this_sum > top_sum { 921 | Some((this_stream, this_sum)) 922 | } 923 | else { 924 | Some((top_stream, top_sum)) 925 | } 926 | }, 927 | None => Some((this_stream, this_sum)) 928 | } 929 | }); 930 | 931 | match stream_opt { 932 | // Return the chosen stream. 933 | Some((stream, _sum)) => { 934 | warnings.extend(errors.into_iter().map(|bs| { 935 | TcpPunchHoleWarning::StreamIo { 936 | peer_addr: bs.peer_addr, 937 | err: bs.error, 938 | } 939 | })); 940 | return WOk(stream, warnings); 941 | }, 942 | // Every stream died while deciding which stream to use. 943 | None => { 944 | return WErr(TcpPunchHoleError::DecideStream { 945 | errors: errors, 946 | }); 947 | } 948 | } 949 | } 950 | }, 951 | } 952 | } 953 | } 954 | 955 | #[cfg(test)] 956 | mod test { 957 | use super::*; 958 | 959 | use std::io::{Read, Write}; 960 | use std::time::{Instant, Duration}; 961 | 962 | use mapping_context::MappingContext; 963 | use rendezvous_info::gen_rendezvous_info; 964 | 965 | #[test] 966 | fn two_peers_tcp_hole_punch_over_loopback() { 967 | let deadline = Instant::now() + Duration::from_secs(5); 968 | let mapping_context = unwrap_result!(MappingContext::new().result_log()); 969 | 970 | let mapped_socket_0 = unwrap_result!(MappedTcpSocket::new(&mapping_context, deadline).result_log()); 971 | let socket_0 = mapped_socket_0.socket; 972 | let endpoints_0 = mapped_socket_0.endpoints; 973 | let (priv_info_0, pub_info_0) = gen_rendezvous_info(endpoints_0); 974 | 975 | let mapped_socket_1 = unwrap_result!(MappedTcpSocket::new(&mapping_context, deadline).result_log()); 976 | let socket_1 = mapped_socket_1.socket; 977 | let endpoints_1 = mapped_socket_1.endpoints; 978 | let (priv_info_1, pub_info_1) = gen_rendezvous_info(endpoints_1); 979 | 980 | let deadline = Instant::now() + Duration::from_secs(5); 981 | let thread_0 = thread!("two_peers_tcp_hole_punch_over_loopback_0", move || { 982 | let mut stream = unwrap_result!(tcp_punch_hole(socket_0, priv_info_0, pub_info_1, deadline).result_log()); 983 | let mut data = [0u8; 4]; 984 | let n = unwrap_result!(stream.write(&data)); 985 | assert_eq!(n, 4); 986 | let n = unwrap_result!(stream.read(&mut data)); 987 | assert_eq!(n, 4); 988 | assert_eq!(data, [1u8; 4]); 989 | }); 990 | 991 | let thread_1 = thread!("two_peers_tcp_hole_punch_over_loopback_1", move || { 992 | let mut stream = unwrap_result!(tcp_punch_hole(socket_1, priv_info_1, pub_info_0, deadline).result_log()); 993 | let mut data = [1u8; 4]; 994 | let n = unwrap_result!(stream.write(&data)); 995 | assert_eq!(n, 4); 996 | let n = unwrap_result!(stream.read(&mut data)); 997 | assert_eq!(n, 4); 998 | assert_eq!(data, [0u8; 4]); 999 | }); 1000 | 1001 | unwrap_result!(thread_0.join()); 1002 | unwrap_result!(thread_1.join()); 1003 | } 1004 | } 1005 | 1006 | -------------------------------------------------------------------------------- /src/mapped_udp_socket.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use std::io; 22 | use std::net::UdpSocket; 23 | use std::net; 24 | use std::net::IpAddr; 25 | use std::time::{Instant, Duration}; 26 | use std::collections::HashSet; 27 | 28 | use igd; 29 | use maidsafe_utilities::serialisation::deserialise; 30 | use socket_addr::SocketAddr; 31 | use w_result::{WResult, WOk, WErr}; 32 | 33 | use listener_message; 34 | use mapping_context; 35 | use mapping_context::MappingContext; 36 | use mapped_socket_addr::MappedSocketAddr; 37 | use socket_utils; 38 | use socket_utils::RecvUntil; 39 | 40 | /// A bound udp socket for which we know our external endpoints. 41 | pub struct MappedUdpSocket { 42 | /// The socket. 43 | pub socket: UdpSocket, 44 | /// The known endpoints of this socket. 45 | pub endpoints: Vec 46 | } 47 | 48 | quick_error! { 49 | /// Errors returned by MappedUdpSocket::map 50 | #[derive(Debug)] 51 | pub enum MappedUdpSocketMapError { 52 | /// Error getting the local address of the socket. 53 | SocketLocalAddr { 54 | err: io::Error 55 | } { 56 | description("Error getting local address of socket \ 57 | (have you called bind() on the socket?)") 58 | display("Error getting local address of socket. \ 59 | UdpSocket::local_addr returned an error: {} 60 | (have you called bind() on the socket?).", 61 | err) 62 | cause(err) 63 | } 64 | /// IO error receiving data on the socket. 65 | RecvError { 66 | err: io::Error 67 | } { 68 | description("IO error receiving data on socket") 69 | display("IO error receiving data on socket: {}", err) 70 | cause(err) 71 | } 72 | /// IO error sending data on the socket. 73 | SendError { 74 | err: io::Error 75 | } { 76 | description("IO error sending data on socket") 77 | display("IO error sending data on socket: {}", err) 78 | cause(err) 79 | } 80 | } 81 | } 82 | 83 | impl From for io::Error { 84 | fn from(e: MappedUdpSocketMapError) -> io::Error { 85 | let err_str = format!("{}", e); 86 | let kind = match e { 87 | MappedUdpSocketMapError::SocketLocalAddr { err } => err.kind(), 88 | MappedUdpSocketMapError::RecvError { err } => err.kind(), 89 | MappedUdpSocketMapError::SendError { err } => err.kind(), 90 | }; 91 | io::Error::new(kind, err_str) 92 | } 93 | } 94 | 95 | quick_error! { 96 | /// Warnings raised by MappedUdpSocket::map 97 | #[derive(Debug)] 98 | pub enum MappedUdpSocketMapWarning { 99 | /// Error searching for IGD gateway 100 | FindGateway { 101 | err: igd::SearchError 102 | } { 103 | description("Error searching for IGD gateway") 104 | display("Error searching for IGD gateway. \ 105 | igd::search_gateway_from_timeout returned an error: {}", 106 | err) 107 | cause(err) 108 | } 109 | /// Error mapping external address and port through IGD gateway. `gateway_addr` is the 110 | /// address of the IGD gateway that we requested a port mapping from. 111 | GetExternalPort { 112 | gateway_addr: net::SocketAddrV4, 113 | err: igd::AddAnyPortError, 114 | } { 115 | description("Error mapping external address and port through IGD \ 116 | gateway") 117 | display("Error mapping external address and port through IGD \ 118 | gateway at address {}. igd::Gateway::get_any_address \ 119 | returned an error: {}", gateway_addr, err) 120 | cause(err) 121 | } 122 | } 123 | } 124 | 125 | quick_error! { 126 | /// Errors returned by MappedUdpSocket::new 127 | #[derive(Debug)] 128 | pub enum MappedUdpSocketNewError { 129 | /// Error creating new udp socket bound to 0.0.0.0:0 130 | CreateSocket { 131 | err: io::Error 132 | } { 133 | description("Error creating a new udp socket bound to 0.0.0.0:0") 134 | display("Error creating a new udp socket bound to 0.0.0.0:0. \ 135 | UdpSocket::bind returned an IO error: {}", err) 136 | cause(err) 137 | } 138 | /// Error mapping udp socket. 139 | MapSocket { 140 | err: MappedUdpSocketMapError 141 | } { 142 | description("Error mapping udp socket") 143 | display("Error mapping udp socket. MappedUdpSocket::map returned \ 144 | an error: {}", err) 145 | cause(err) 146 | } 147 | } 148 | } 149 | 150 | impl From for io::Error { 151 | fn from(e: MappedUdpSocketNewError) -> io::Error { 152 | let err_str = format!("{}", e); 153 | let kind = match e { 154 | MappedUdpSocketNewError::CreateSocket { err } => err.kind(), 155 | MappedUdpSocketNewError::MapSocket { err } => { 156 | let err: io::Error = From::from(err); 157 | err.kind() 158 | }, 159 | }; 160 | io::Error::new(kind, err_str) 161 | } 162 | } 163 | 164 | impl MappedUdpSocket { 165 | /// Map an existing `UdpSocket`. 166 | pub fn map(socket: UdpSocket, mc: &MappingContext, deadline: Instant) 167 | -> WResult 168 | { 169 | let mut endpoints = Vec::new(); 170 | let mut warnings = Vec::new(); 171 | 172 | // Add the local addresses of this socket for the sake of peers on the name machine or 173 | // same local network as us. 174 | let local_addr = match socket.local_addr() { 175 | Ok(local_addr) => local_addr, 176 | Err(e) => return WErr(MappedUdpSocketMapError::SocketLocalAddr { err: e }) 177 | }; 178 | match local_addr.ip() { 179 | IpAddr::V4(ipv4_addr) => { 180 | if socket_utils::ipv4_is_unspecified(&ipv4_addr) { 181 | // If the socket address is unspecified we add an address for every local 182 | // interface. We also ask the interface's IGD gateway (if there is one) for 183 | // an address. 184 | for iface_v4 in mapping_context::interfaces_v4(&mc) { 185 | let local_iface_addr = net::SocketAddrV4::new(iface_v4.addr, local_addr.port()); 186 | endpoints.push(MappedSocketAddr { 187 | addr: SocketAddr(net::SocketAddr::V4(local_iface_addr)), 188 | nat_restricted: false, 189 | }); 190 | if let Some(gateway) = iface_v4.gateway { 191 | match gateway.get_any_address(igd::PortMappingProtocol::UDP, 192 | local_iface_addr, 0, 193 | "rust nat_traversal") 194 | { 195 | Ok(external_addr) => { 196 | endpoints.push(MappedSocketAddr { 197 | addr: SocketAddr(net::SocketAddr::V4(external_addr)), 198 | nat_restricted: false, 199 | }); 200 | }, 201 | Err(e) => { 202 | warnings.push(MappedUdpSocketMapWarning::GetExternalPort { 203 | gateway_addr: gateway.addr, 204 | err: e, 205 | }); 206 | } 207 | } 208 | }; 209 | }; 210 | } 211 | else { 212 | let local_addr_v4 = net::SocketAddrV4::new(ipv4_addr, local_addr.port()); 213 | endpoints.push(MappedSocketAddr { 214 | addr: SocketAddr(net::SocketAddr::V4(local_addr_v4)), 215 | nat_restricted: false, 216 | }); 217 | 218 | // If the local address is the address of an interface then we can avoid 219 | // searching for an IGD gateway, just reuse the search result from when we 220 | // found this interface. 221 | let mut gateway_opt_opt = None; 222 | for iface_v4 in mapping_context::interfaces_v4(&mc) { 223 | if iface_v4.addr == ipv4_addr { 224 | gateway_opt_opt = Some(iface_v4.gateway); 225 | break; 226 | } 227 | }; 228 | let gateway_opt = match gateway_opt_opt { 229 | Some(gateway_opt) => gateway_opt, 230 | // We don't where this local address came from so search for an IGD gateway 231 | // at it. 232 | None => { 233 | match igd::search_gateway_from_timeout(ipv4_addr, Duration::from_secs(1)) { 234 | Ok(gateway) => Some(gateway), 235 | Err(e) => { 236 | warnings.push(MappedUdpSocketMapWarning::FindGateway { 237 | err: e 238 | }); 239 | None 240 | } 241 | } 242 | } 243 | }; 244 | // If we have a gateway, ask it for an external address. 245 | if let Some(gateway) = gateway_opt { 246 | match gateway.get_any_address(igd::PortMappingProtocol::UDP, 247 | local_addr_v4, 0, 248 | "rust nat_traversal") 249 | { 250 | Ok(external_addr) => { 251 | endpoints.push(MappedSocketAddr { 252 | addr: SocketAddr(net::SocketAddr::V4(external_addr)), 253 | nat_restricted: false, 254 | }); 255 | }, 256 | Err(e) => { 257 | warnings.push(MappedUdpSocketMapWarning::GetExternalPort { 258 | gateway_addr: gateway.addr, 259 | err: e, 260 | }); 261 | } 262 | } 263 | }; 264 | }; 265 | }, 266 | IpAddr::V6(ipv6_addr) => { 267 | if socket_utils::ipv6_is_unspecified(&ipv6_addr) { 268 | // If the socket address is unspecified add an address for every interface. 269 | for iface_v6 in mapping_context::interfaces_v6(&mc) { 270 | let local_iface_addr = net::SocketAddr::V6(net::SocketAddrV6::new(iface_v6.addr, local_addr.port(), 0, 0)); 271 | endpoints.push(MappedSocketAddr { 272 | addr: SocketAddr(local_iface_addr), 273 | nat_restricted: false, 274 | }); 275 | }; 276 | } 277 | else { 278 | endpoints.push(MappedSocketAddr { 279 | addr: SocketAddr(net::SocketAddr::V6(net::SocketAddrV6::new(ipv6_addr, local_addr.port(), 0, 0))), 280 | nat_restricted: false, 281 | }); 282 | } 283 | }, 284 | }; 285 | 286 | const MAX_DATAGRAM_SIZE: usize = 256; 287 | 288 | let send_data = listener_message::REQUEST_MAGIC_CONSTANT; 289 | let mut simple_servers: HashSet = mapping_context::simple_udp_servers(&mc) 290 | .into_iter().collect(); 291 | 292 | // Ping all the simple servers and waiting for a response. 293 | let start_time = Instant::now(); 294 | let mut recv_deadline = start_time; 295 | let mut deadline = deadline; 296 | while recv_deadline < deadline && simple_servers.len() > 0 { 297 | recv_deadline = recv_deadline + Duration::from_millis(250); 298 | 299 | // TODO(canndrew): We should limit the number of servers that we send to. If the user 300 | // has added two thousand servers we really don't want to be pinging all of them. We 301 | // should be smart about it though and try to ping servers that are on different 302 | // networks, not just the first ten in the list or something. 303 | for simple_server in &simple_servers { 304 | // TODO(canndrew): What should we do if we get a partial write? 305 | let _ = match socket.send_to(&send_data[..], &**simple_server) { 306 | Ok(n) => n, 307 | Err(e) => return WErr(MappedUdpSocketMapError::SendError { err: e }), 308 | }; 309 | }; 310 | let mut recv_data = [0u8; MAX_DATAGRAM_SIZE]; 311 | loop { 312 | let (read_size, recv_addr) = match socket.recv_until(&mut recv_data[..], recv_deadline) { 313 | Ok(Some(res)) => res, 314 | Ok(None) => break, 315 | Err(e) => return WErr(MappedUdpSocketMapError::RecvError { err: e }), 316 | }; 317 | if let Ok(listener_message::EchoExternalAddr { external_addr }) = 318 | deserialise::(&recv_data[..read_size]) { 319 | // Don't ping this simple server again while mapping this socket. 320 | simple_servers.remove(&recv_addr); 321 | 322 | // If the address that responded to us is global then drop max_attempts to exit 323 | // the loop more quickly. The logic here is that global addresses are the ones 324 | // that are likely to take the longest to respond and they're all likely to 325 | // give us the same address. By contrast, servers on the same subnet as us or 326 | // behind the same carrier-level NAT are likely to respond in under a second. 327 | // So once we have one global address drop the timeout. 328 | 329 | // TODO(canndrew): Use IpAddr::is_global when it's available 330 | // let is_global = recv_addr.is_global(); 331 | let is_global = false; 332 | if is_global { 333 | let now = Instant::now(); 334 | if deadline > now { 335 | deadline = now + (now - deadline) / 2; 336 | } 337 | }; 338 | 339 | // Add this endpoint if we don't already know about it. We may have found it 340 | // through IGD or it may be a local interface. 341 | if endpoints.iter().all(|e| e.addr != external_addr) { 342 | endpoints.push(MappedSocketAddr { 343 | addr: external_addr, 344 | // TODO(canndrew): We should consider ways to determine whether this is 345 | // actually an restricted port. For now, just assume it's restricted. It 346 | // usually will be. 347 | nat_restricted: true, 348 | }); 349 | } 350 | } 351 | } 352 | } 353 | 354 | WOk(MappedUdpSocket { 355 | socket: socket, 356 | endpoints: endpoints, 357 | }, warnings) 358 | } 359 | 360 | /// Create a new `MappedUdpSocket` 361 | pub fn new(mc: &MappingContext, deadline: Instant) 362 | -> WResult 363 | { 364 | // Sometimes we might bind a socket to a random port then find that we have an IGD gateway 365 | // that could give us an unrestricted external port but that it can't map the random port 366 | // number we got. Hence we might need to try several times with different port numbers. 367 | let mut attempt = 0; 368 | 'attempt: loop { 369 | attempt += 1; 370 | let socket = match UdpSocket::bind("0.0.0.0:0") { 371 | Ok(socket) => socket, 372 | Err(e) => return WErr(MappedUdpSocketNewError::CreateSocket { err: e }), 373 | }; 374 | let (socket, warnings) = match Self::map(socket, mc, deadline) { 375 | WOk(s, ws) => (s, ws), 376 | WErr(e) => return WErr(MappedUdpSocketNewError::MapSocket { err: e }), 377 | }; 378 | if attempt < 3 { 379 | for warning in &warnings { 380 | match *warning { 381 | // If we bound to a port that the IGD gateway can't map, rebind and try again. 382 | MappedUdpSocketMapWarning::GetExternalPort { 383 | err: igd::AddAnyPortError::ExternalPortInUse, 384 | .. 385 | } => continue 'attempt, 386 | _ => (), 387 | } 388 | } 389 | } 390 | return WOk(socket, warnings); 391 | } 392 | } 393 | } 394 | 395 | -------------------------------------------------------------------------------- /src/mapping_context.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use std::sync::RwLock; 22 | use std::io; 23 | use std::net::{Ipv4Addr, Ipv6Addr}; 24 | use std::thread; 25 | use std::time::Duration; 26 | 27 | use igd; 28 | use socket_addr::SocketAddr; 29 | use w_result::{WResult, WOk, WErr}; 30 | use get_if_addrs; 31 | use void::Void; 32 | 33 | use socket_utils; 34 | 35 | /// You need to create a `MappingContext` before doing any socket mapping. This 36 | /// `MappingContext` should ideally be kept throughout the lifetime of the 37 | /// program. Internally it caches a addresses of UPnP servers and hole punching 38 | /// servers. 39 | pub struct MappingContext { 40 | interfaces_v4: RwLock>, 41 | interfaces_v6: RwLock>, 42 | simple_udp_servers: RwLock>, 43 | simple_tcp_servers: RwLock>, 44 | } 45 | 46 | #[derive(Clone)] 47 | pub struct InterfaceV4 { 48 | pub gateway: Option, 49 | pub addr: Ipv4Addr, 50 | } 51 | 52 | // TODO(canndrew): Can we support IGD on ipv6? 53 | #[derive(Clone)] 54 | pub struct InterfaceV6 { 55 | pub addr: Ipv6Addr, 56 | } 57 | 58 | quick_error! { 59 | #[derive(Debug)] 60 | pub enum MappingContextNewError { 61 | /// Failed to list the local machine's network interfaces. 62 | ListInterfaces { 63 | err:io::Error, 64 | } { 65 | description("Failed to list the local machine's network interfaces") 66 | display("Failed to list the local machines's network interfaces \ 67 | get_if_addrs returned an error: {}", err) 68 | cause(err) 69 | } 70 | /// Failed to spawn a thread. 71 | SpawnThread { 72 | err: io::Error 73 | } { 74 | description("Failed to spawn a thread") 75 | display("Failed to spawn a thread. \ 76 | thread::spawn returned an error: {}", err) 77 | cause(err) 78 | } 79 | } 80 | } 81 | 82 | impl From for io::Error { 83 | fn from(e: MappingContextNewError) -> io::Error { 84 | let err_str = format!("{}", e); 85 | let kind = match e { 86 | MappingContextNewError::ListInterfaces { err } => err.kind(), 87 | MappingContextNewError::SpawnThread { err } => err.kind(), 88 | }; 89 | io::Error::new(kind, err_str) 90 | } 91 | } 92 | 93 | quick_error! { 94 | #[derive(Debug)] 95 | pub enum MappingContextNewWarning { 96 | /// Error finding IGD gateway. `if_name` and `if_addr` indicate the network interface being 97 | /// searched from when this error was raised. 98 | SearchGateway { 99 | if_name: String, 100 | if_addr: Ipv4Addr, 101 | err: igd::SearchError 102 | } { 103 | description("Failed to find IGD gateway") 104 | display("Failed to find an IGD gateway on network interface {} {}. \ 105 | igd::search_gateway_from_timeout returned an error: {}", 106 | if_name, if_addr, err) 107 | cause(err) 108 | } 109 | } 110 | } 111 | 112 | impl MappingContext { 113 | /// Create a new mapping context. This will block breifly while it searches 114 | /// the network for UPnP servers. 115 | pub fn new() -> WResult { 116 | let interfaces = match get_if_addrs::get_if_addrs() { 117 | Ok(if_addrs) => if_addrs, 118 | Err(e) => return WErr(MappingContextNewError::ListInterfaces { err: e }), 119 | }; 120 | let mut interfaces_v4 = Vec::new(); 121 | let mut interfaces_v6 = Vec::new(); 122 | let mut warnings = Vec::new(); 123 | let mut search_threads = Vec::new(); 124 | for interface in interfaces { 125 | let addr_v4 = match interface.addr { 126 | get_if_addrs::IfAddr::V4(v4_addr) => { 127 | v4_addr.ip 128 | }, 129 | get_if_addrs::IfAddr::V6(v6_addr) => { 130 | interfaces_v6.push(InterfaceV6 { 131 | addr: v6_addr.ip, 132 | }); 133 | continue; 134 | }, 135 | }; 136 | if socket_utils::ipv4_is_loopback(&addr_v4) { 137 | interfaces_v4.push(InterfaceV4 { 138 | gateway: None, 139 | addr: addr_v4, 140 | }); 141 | continue; 142 | }; 143 | let if_name = interface.name; 144 | search_threads.push(thread::Builder::new() 145 | .name(From::from("IGD search")) 146 | .spawn(move || -> WResult<_, _, Void> { 147 | let mut warnings = Vec::new(); 148 | let gateway = match igd::search_gateway_from_timeout(addr_v4, Duration::from_secs(1)) { 149 | Ok(gateway) => Some(gateway), 150 | Err(e) => { 151 | warnings.push(MappingContextNewWarning::SearchGateway { 152 | if_name: if_name, 153 | if_addr: addr_v4, 154 | err: e, 155 | }); 156 | None 157 | }, 158 | }; 159 | WOk(InterfaceV4 { 160 | gateway: gateway, 161 | addr: addr_v4, 162 | }, warnings) 163 | })); 164 | }; 165 | 166 | for search_thread in search_threads { 167 | match search_thread { 168 | Err(e) => return WErr(MappingContextNewError::SpawnThread { err: e }), 169 | Ok(jh) => { 170 | // If the child thread panicked, propogate the panic. 171 | let res = unwrap_result!(jh.join()); 172 | match res { 173 | WErr(e) => match e {}, 174 | WOk(interface, ws) => { 175 | interfaces_v4.push(interface); 176 | warnings.extend(ws); 177 | } 178 | } 179 | } 180 | } 181 | } 182 | let mc = MappingContext { 183 | interfaces_v4: RwLock::new(interfaces_v4), 184 | interfaces_v6: RwLock::new(interfaces_v6), 185 | simple_udp_servers: RwLock::new(Vec::new()), 186 | simple_tcp_servers: RwLock::new(Vec::new()), 187 | }; 188 | WOk(mc, warnings) 189 | } 190 | 191 | /// Inform the context about external servers that speak the UDP simple hole punch server 192 | /// protocol. 193 | pub fn add_simple_udp_servers(&self, servers: S) 194 | where S: IntoIterator 195 | { 196 | let mut s = unwrap_result!(self.simple_udp_servers.write()); 197 | s.extend(servers) 198 | } 199 | 200 | /// Inform the context about external servers that speak the TCP simple hole punch server 201 | /// protocol. 202 | pub fn add_simple_tcp_servers(&self, servers: S) 203 | where S: IntoIterator 204 | { 205 | let mut s = unwrap_result!(self.simple_tcp_servers.write()); 206 | s.extend(servers) 207 | } 208 | } 209 | 210 | pub fn interfaces_v4(mc: &MappingContext) -> Vec { 211 | unwrap_result!(mc.interfaces_v4.read()).clone() 212 | } 213 | 214 | pub fn interfaces_v6(mc: &MappingContext) -> Vec { 215 | unwrap_result!(mc.interfaces_v6.read()).clone() 216 | } 217 | 218 | pub fn simple_udp_servers(mc: &MappingContext) -> Vec { 219 | unwrap_result!(mc.simple_udp_servers.read()).clone() 220 | } 221 | 222 | pub fn simple_tcp_servers(mc: &MappingContext) -> Vec { 223 | unwrap_result!(mc.simple_tcp_servers.read()).clone() 224 | } 225 | 226 | #[cfg(test)] 227 | mod tests { 228 | use super::*; 229 | 230 | #[test] 231 | fn create_mapping_context() { 232 | let _ = unwrap_result!(MappingContext::new().result_discard()); 233 | } 234 | } 235 | 236 | -------------------------------------------------------------------------------- /src/punched_udp_socket.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use maidsafe_utilities::serialisation::{deserialise, SerialisationError, serialise}; 22 | use std::io; 23 | use std::net::UdpSocket; 24 | use std::time::{Instant, Duration}; 25 | use std::thread; 26 | 27 | use socket_addr::SocketAddr; 28 | use w_result::{WResult, WOk, WErr}; 29 | 30 | use rendezvous_info::{PrivRendezvousInfo, PubRendezvousInfo}; 31 | use rendezvous_info; 32 | use socket_utils::RecvUntil; 33 | use mapped_socket_addr::MappedSocketAddr; 34 | 35 | #[derive(Debug, RustcEncodable, RustcDecodable)] 36 | struct HolePunch { 37 | pub secret: [u8; 4], 38 | pub ack: bool, 39 | } 40 | 41 | /// Used for reporting warnings inside `UdpPunchHoleWarning` 42 | #[derive(Debug)] 43 | pub struct HolePunchPacketData { 44 | data: HolePunch, 45 | } 46 | 47 | /// A udp socket that has been hole punched. 48 | pub struct PunchedUdpSocket { 49 | /// The UDP socket. 50 | pub socket: UdpSocket, 51 | /// The remote address that this socket is able to send messages to and receive messages from. 52 | pub peer_addr: SocketAddr, 53 | } 54 | 55 | quick_error! { 56 | /// Warnings raise by `PunchedUdpSocket::punch_hole` 57 | #[derive(Debug)] 58 | #[allow(variant_size_differences)] 59 | pub enum UdpPunchHoleWarning { 60 | /// Received a hole punch packet that does correspond to the connection we are trying to 61 | /// make. Possibly, hole punch packets from an unrelated connection or arriving on this socket. 62 | UnexpectedHolePunchPacket { 63 | hole_punch: HolePunchPacketData, 64 | } { 65 | description("Received a hole punch packet that does correspond to the \ 66 | connection we are trying to make. Possibly, hole punch packets \ 67 | from an unrelated connection or arriving on this socket.") 68 | display("Received a hole punch packet that does correspond to the \ 69 | connection we are trying to make. Possibly, hole punch packets \ 70 | from an unrelated connection or arriving on this socket. Debug \ 71 | info: {:#?}", hole_punch) 72 | } 73 | /// Received invalid data on the udp socket while hole punching. 74 | InvalidHolePunchPacket { 75 | err: SerialisationError, 76 | } { 77 | description("Received invalid data on the udp socket while hole punching") 78 | display("Received invalid data on the udp socket while hole punching. \ 79 | deserialisation produced the error: {}", err) 80 | cause(err) 81 | } 82 | /// There was an IO error trying to send a message to one of the peer's potential endpoints. 83 | MsgEndpoint { 84 | endpoint: MappedSocketAddr, 85 | err: io::Error, 86 | } { 87 | description("IO error trying to send a message to one of the peer's potential endpoints.") 88 | display("IO error trying to send a message to endpoint {:?}. {}", endpoint, err) 89 | cause(err) 90 | } 91 | } 92 | } 93 | 94 | quick_error! { 95 | /// Error returned by PunchedUdpSocket::punch_hole 96 | #[derive(Debug)] 97 | pub enum UdpPunchHoleError { 98 | /// Timed out waiting for a response from the peer. 99 | TimedOut { 100 | description("Timed out waiting for a response from the peer.") 101 | } 102 | /// IO error when using socket 103 | Io { 104 | err: io::Error, 105 | } { 106 | description("IO error when using socket") 107 | display("IO error when using socket: {}", err) 108 | cause(err) 109 | } 110 | SendCompleteAck { 111 | description("Error sending ACK to peer. Kept getting partial writes.") 112 | display("Error sending ACK to peer. Kept getting partial writes.") 113 | } 114 | } 115 | } 116 | 117 | impl From for io::Error { 118 | fn from(e: UdpPunchHoleError) -> io::Error { 119 | let err_str = format!("{}", e); 120 | let kind = match e { 121 | UdpPunchHoleError::TimedOut => io::ErrorKind::TimedOut, 122 | UdpPunchHoleError::Io { err } => err.kind(), 123 | UdpPunchHoleError::SendCompleteAck => io::ErrorKind::Other, 124 | }; 125 | io::Error::new(kind, err_str) 126 | } 127 | } 128 | 129 | impl PunchedUdpSocket { 130 | /// Punch a udp socket using a mapped socket and the peer's rendezvous info. 131 | pub fn punch_hole(socket: UdpSocket, 132 | our_priv_rendezvous_info: PrivRendezvousInfo, 133 | their_pub_rendezvous_info: PubRendezvousInfo, 134 | deadline: Instant) 135 | -> WResult 136 | { 137 | let mut warnings = Vec::new(); 138 | 139 | let (mut endpoints, their_secret) 140 | = rendezvous_info::decompose(their_pub_rendezvous_info); 141 | let our_secret 142 | = rendezvous_info::get_priv_secret(our_priv_rendezvous_info); 143 | 144 | // Cbor seems to serialize into bytes of different sizes and 145 | // it sometimes exceeded 16 bytes, let's be safe and use 128. 146 | const MAX_DATAGRAM_SIZE: usize = 128; 147 | 148 | let send_data = { 149 | let hole_punch = HolePunch { 150 | secret: our_secret, 151 | ack: false, 152 | }; 153 | 154 | serialise(&hole_punch).unwrap() 155 | }; 156 | 157 | assert!(send_data.len() <= MAX_DATAGRAM_SIZE, 158 | format!("Data exceed MAX_DATAGRAM_SIZE in blocking_udp_punch_hole: {} > {}", 159 | send_data.len(), 160 | MAX_DATAGRAM_SIZE)); 161 | 162 | let mut recv_data = [0u8; MAX_DATAGRAM_SIZE]; 163 | 164 | // TODO(canndrew): Have a hard think about whether this is the best possible algorithm for 165 | // doing this. 166 | // 167 | // As far as I can see, the desired properties are: 168 | // (a) We shouldn't read from the socket if the peer might have already returned their 169 | // socket to the caller and started sending us real data. Otherwise we have to either. 170 | // drop that data or return it in the PunchedUdpSocket struct. 171 | // (b) We should only return the socket once there are no more hole-punch messages to 172 | // receive. Otherwise the caller will start reading from the socket and find crap on the wire. 173 | // (c) We should try to return as soon as possible after establishing a connection. 174 | // (d) We should account for the fact that UDP is unreliable by resending messages. 175 | // 176 | // The problem is none of these requirements are possible to fulfill 100% of the time and 177 | // they all conflict with each other. So we need to decide how bad these problems are 178 | // relative to each other. In the case of (a) sending back data inside PunchedUdpSocket 179 | // wouldn't be the end of the world but it would be pretty annoying for the user as they'd 180 | // need to process that data and couldn't just start using their socket whereever it's 181 | // needed. Applications that use UDP should account for the fact that data can dissapear 182 | // anyway but it might be problematic for some apps if the very first chunk of data very 183 | // often dissapears. In the case of (b) applications need to account for the fact that 184 | // random data can sometimes appear on a UDP socket but they're probably not expecting to 185 | // get random data from the peer they're talking to. We should at the very least make sure 186 | // our hole punch packets are easily recognizable and give the user a facility to identify 187 | // them and throw them away. (c) is important but it conflicts with (b) and (d). In the 188 | // case of (d) it would helpful to have some idea of the probability of a given packet 189 | // being lost and balance that against (c). 190 | // 191 | // Assuming we successfully punch a hole there's four ways this can happen: (0) we get one 192 | // of their hole punching messages and it's from an address we were sending to. In this 193 | // case they likely got our hole punch message(s) aswell although it's possible the packet 194 | // got dropped. (1) We get one of their hole punching messages from an address that we 195 | // weren't sending to. In this case they haven't received any of our messages and we'll 196 | // definitely need to send an ack to their address. (2) We receive an ack to one of our 197 | // messages and it's from an address we weren't sending to. I don't think this should ever 198 | // happen. (3) We receive an ack to one of our packets and it's from an address we were 199 | // sending to. In this case they likely initially didn't have an address they could contact 200 | // us on. 201 | // 202 | // For now we keep the algorithm simple: If we get a hole punch message we send back two 203 | // acks with a delay in between before returning. If we get an ack we return immediately. 204 | 205 | // Spend TOTAL_TIMEOUT_MS trying to get their actual address that we can 206 | // communicate with. 207 | 208 | const DELAY_BETWEEN_RESENDS_MS: u64 = 600; 209 | 210 | let mut recv_deadline = Instant::now(); 211 | while recv_deadline < deadline { 212 | recv_deadline = recv_deadline + Duration::from_millis(DELAY_BETWEEN_RESENDS_MS); 213 | let mut i = 0; 214 | while i < endpoints.len() { 215 | // TODO(canndrew): How should we handle partial write? 216 | let _ = match socket.send_to(&send_data[..], &*endpoints[i].addr) { 217 | Ok(n) => n, 218 | Err(e) => { 219 | warnings.push(UdpPunchHoleWarning::MsgEndpoint { 220 | endpoint: endpoints.swap_remove(i), 221 | err: e, 222 | }); 223 | continue; 224 | } 225 | }; 226 | i += 1; 227 | } 228 | // Keep reading until it's time to send to all endpoints again. 229 | loop { 230 | let (read_size, addr) = match socket.recv_until(&mut recv_data[..], recv_deadline) { 231 | Ok(Some(x)) => x, 232 | Ok(None) => break, 233 | Err(e) => return WErr(UdpPunchHoleError::Io { err: e }), 234 | }; 235 | match deserialise::(&recv_data[..read_size]) { 236 | Ok(hp) => { 237 | if hp.secret == our_secret && hp.ack { 238 | return WOk(PunchedUdpSocket { 239 | socket: socket, 240 | peer_addr: addr, 241 | }, warnings); 242 | } 243 | if hp.secret == their_secret { 244 | let send_data = { 245 | let hole_punch = HolePunch { 246 | secret: their_secret, 247 | ack: true, 248 | }; 249 | 250 | serialise(&hole_punch).unwrap() 251 | }; 252 | 253 | assert!(send_data.len() <= MAX_DATAGRAM_SIZE, 254 | format!("Data exceed MAX_DATAGRAM_SIZE in blocking_udp_punch_hole: {} > {}", 255 | send_data.len(), 256 | MAX_DATAGRAM_SIZE)); 257 | 258 | let mut attempts = 0; 259 | let mut successful_attempts = 0; 260 | let mut error = None; 261 | while attempts < 2 || Instant::now() < deadline { 262 | attempts += 1; 263 | match socket.send_to(&send_data[..], &*addr) { 264 | Ok(n) => { 265 | if n == send_data.len() { 266 | successful_attempts += 1; 267 | if successful_attempts == 2 { 268 | break; 269 | } 270 | } 271 | } 272 | Err(e) => { 273 | if error.is_none() { 274 | error = Some(e); 275 | } 276 | } 277 | }; 278 | thread::sleep(Duration::from_millis(100)); 279 | } 280 | if successful_attempts == 0 { 281 | let ret = match error { 282 | Some(e) => UdpPunchHoleError::Io { err: e }, 283 | None => UdpPunchHoleError::SendCompleteAck, 284 | }; 285 | return WErr(ret); 286 | } 287 | else { 288 | return WOk(PunchedUdpSocket { 289 | socket: socket, 290 | peer_addr: addr, 291 | }, warnings); 292 | } 293 | } 294 | // Protect against a malicious peer sending us loads of spurious data. 295 | if warnings.len() < 10 { 296 | warnings.push(UdpPunchHoleWarning::UnexpectedHolePunchPacket { 297 | hole_punch: HolePunchPacketData { 298 | data: hp, 299 | }, 300 | }); 301 | } 302 | } 303 | Err(e) => { 304 | // Protect against a malicious peer sending us loads of spurious data. 305 | if warnings.len() < 10 { 306 | warnings.push(UdpPunchHoleWarning::InvalidHolePunchPacket { 307 | err: e, 308 | }); 309 | } 310 | } 311 | }; 312 | } 313 | } 314 | WErr(UdpPunchHoleError::TimedOut) 315 | } 316 | } 317 | 318 | /// Returns `None` if `data` looks like a hole punching message. Otherwise returns the data it was 319 | /// given. 320 | /// 321 | /// Punching a hole with a udp socket involves packets being sent and received on the socket. After 322 | /// hole punching succeeds it's possible that more hole punching packets sent by the remote peer 323 | /// may yet arrive on the socket. This function can be used to filter out those packets. 324 | pub fn filter_udp_hole_punch_packet(data: &[u8]) -> Option<&[u8]> { 325 | match deserialise::(data){ 326 | Ok(_) => None, 327 | _ => Some(data), 328 | } 329 | } 330 | 331 | #[cfg(test)] 332 | mod tests { 333 | use std::sync::mpsc; 334 | use std::thread; 335 | use std::time::{Instant, Duration}; 336 | use rand; 337 | 338 | use mapping_context::MappingContext; 339 | use mapped_udp_socket::MappedUdpSocket; 340 | use punched_udp_socket::{PunchedUdpSocket, filter_udp_hole_punch_packet}; 341 | use rendezvous_info::gen_rendezvous_info; 342 | 343 | #[test] 344 | fn two_peers_udp_hole_punch_over_loopback() { 345 | let deadline = Instant::now() + Duration::from_secs(3); 346 | let mapping_context = unwrap_result!(MappingContext::new().result_discard()); 347 | let mapped_socket_0 = unwrap_result!(MappedUdpSocket::new(&mapping_context, deadline).result_discard()); 348 | let mapped_socket_1 = unwrap_result!(MappedUdpSocket::new(&mapping_context, deadline).result_discard()); 349 | 350 | let socket_0 = mapped_socket_0.socket; 351 | let socket_1 = mapped_socket_1.socket; 352 | let (priv_info_0, pub_info_0) = gen_rendezvous_info(mapped_socket_0.endpoints); 353 | let (priv_info_1, pub_info_1) = gen_rendezvous_info(mapped_socket_1.endpoints); 354 | 355 | let (tx_0, rx_0) = mpsc::channel(); 356 | let (tx_1, rx_1) = mpsc::channel(); 357 | 358 | let deadline = Instant::now() + Duration::from_secs(3); 359 | let jh_0 = thread!("two_peers_hole_punch_over_loopback punch socket 0", move || { 360 | let res = PunchedUdpSocket::punch_hole(socket_0, 361 | priv_info_0, 362 | pub_info_1, 363 | deadline); 364 | unwrap_result!(tx_0.send(res)); 365 | }); 366 | let jh_1 = thread!("two_peers_hole_punch_over_loopback punch socket 1", move || { 367 | let res = PunchedUdpSocket::punch_hole(socket_1, 368 | priv_info_1, 369 | pub_info_0, 370 | deadline); 371 | unwrap_result!(tx_1.send(res)); 372 | }); 373 | 374 | thread::sleep(Duration::from_millis(500)); 375 | let punched_socket_0 = unwrap_result!(unwrap_result!(rx_0.try_recv()).result_discard()); 376 | let punched_socket_1 = unwrap_result!(unwrap_result!(rx_1.try_recv()).result_discard()); 377 | 378 | const DATA_LEN: usize = 8; 379 | let data_send: [u8; DATA_LEN] = rand::random(); 380 | let mut data_recv; 381 | 382 | // Send data from 0 to 1 383 | data_recv = [0u8; 1024]; 384 | let n = unwrap_result!(punched_socket_0.socket.send_to(&data_send[..], &*punched_socket_0.peer_addr)); 385 | assert_eq!(n, DATA_LEN); 386 | loop { 387 | let (n, _) = unwrap_result!(punched_socket_1.socket.recv_from(&mut data_recv[..])); 388 | match filter_udp_hole_punch_packet(&data_recv[..n]) { 389 | Some(d) => { 390 | assert_eq!(data_send, d); 391 | break; 392 | }, 393 | None => continue, 394 | } 395 | } 396 | 397 | // Send data from 1 to 0 398 | data_recv = [0u8; 1024]; 399 | let n = unwrap_result!(punched_socket_1.socket.send_to(&data_send[..], &*punched_socket_1.peer_addr)); 400 | assert_eq!(n, DATA_LEN); 401 | loop { 402 | let (n, _) = unwrap_result!(punched_socket_0.socket.recv_from(&mut data_recv[..])); 403 | match filter_udp_hole_punch_packet(&data_recv[..n]) { 404 | Some(d) => { 405 | assert_eq!(data_send, d); 406 | break; 407 | }, 408 | None => continue, 409 | } 410 | } 411 | 412 | unwrap_result!(jh_0.join()); 413 | unwrap_result!(jh_1.join()); 414 | } 415 | } 416 | 417 | -------------------------------------------------------------------------------- /src/rendezvous_info.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use rand; 22 | 23 | use mapped_socket_addr::MappedSocketAddr; 24 | 25 | /// Info exchanged by both parties before performing a rendezvous connection. 26 | #[derive(Debug, Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] 27 | pub struct PubRendezvousInfo { 28 | /// A vector of all the mapped addresses that the peer can try connecting to. 29 | endpoints: Vec, 30 | /// Used to identify the peer. 31 | secret: [u8; 4], 32 | } 33 | 34 | /// The local half of a `PubRendezvousInfo`. 35 | #[derive(Debug, Clone, PartialEq, Eq)] 36 | pub struct PrivRendezvousInfo { 37 | secret: [u8; 4], 38 | } 39 | 40 | /// Create a `(PrivRendezvousInfo, PubRendezvousInfo)` pair from a list of 41 | /// mapped socket addresses. 42 | pub fn gen_rendezvous_info(endpoints: Vec) 43 | -> (PrivRendezvousInfo, PubRendezvousInfo) { 44 | let secret = rand::random(); 45 | let priv_info = PrivRendezvousInfo { 46 | secret: secret, 47 | }; 48 | let pub_info = PubRendezvousInfo { 49 | endpoints: endpoints, 50 | secret: secret, 51 | }; 52 | (priv_info, pub_info) 53 | } 54 | 55 | pub fn decompose(info: PubRendezvousInfo) -> (Vec, [u8; 4]) { 56 | let PubRendezvousInfo { endpoints, secret } = info; 57 | (endpoints, secret) 58 | } 59 | 60 | pub fn get_priv_secret(info: PrivRendezvousInfo) -> [u8; 4] { 61 | info.secret 62 | } 63 | -------------------------------------------------------------------------------- /src/simple_tcp_hole_punch_server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use std::io; 22 | use std::io::{Read, Write}; 23 | use std::net::{TcpStream, TcpListener}; 24 | use std::time::{Instant, Duration}; 25 | use std::sync::Arc; 26 | use std::sync::atomic::{AtomicBool, Ordering}; 27 | use std::net; 28 | 29 | use maidsafe_utilities::serialisation::serialise; 30 | use maidsafe_utilities::thread::RaiiThreadJoiner; 31 | use w_result::{WResult, WOk, WErr}; 32 | use socket_addr::SocketAddr; 33 | 34 | use listener_message; 35 | use socket_utils; 36 | use mapping_context::MappingContext; 37 | use mapped_tcp_socket::{MappedTcpSocket, MappedTcpSocketNewError, MappedTcpSocketMapWarning}; 38 | 39 | const TCP_RW_TIMEOUT: u64 = 20; 40 | 41 | /// RAII type for a hole punch server which speaks the simple hole punching protocol. 42 | pub struct SimpleTcpHolePunchServer> { 43 | // TODO(canndrew): Use this to refresh our external addrs. 44 | _mapping_context: T, 45 | stop_flag: Arc, 46 | local_addr: net::SocketAddr, 47 | _raii_joiner: RaiiThreadJoiner, 48 | known_endpoints: Vec, 49 | } 50 | 51 | quick_error! { 52 | #[derive(Debug)] 53 | /// Errors returned by SimpleTcpHolePunchServer::new 54 | pub enum SimpleTcpHolePunchServerNewError { 55 | /// Error creating a mapped tcp socket to listen on. 56 | CreateMappedSocket { 57 | err: MappedTcpSocketNewError } { 58 | description("Error creating a mapped tcp socket to listen on.") 59 | display("Error creating a mapped tcp socket to listen on: {}", err) 60 | cause(err) 61 | } 62 | Listen { err: io::Error } { 63 | description("Error listening on socket.") 64 | display("Error listening on socket: {}", err) 65 | cause(err) 66 | } 67 | SocketLocalAddr { err: io::Error} { 68 | description("Error getting local address of listening socket.") 69 | display("Error getting local address of listening socket: {}", err) 70 | cause(err) 71 | } 72 | } 73 | } 74 | 75 | impl From for io::Error { 76 | fn from(e: SimpleTcpHolePunchServerNewError) -> io::Error { 77 | let err_str = format!("{}", e); 78 | let kind = match e { 79 | SimpleTcpHolePunchServerNewError::CreateMappedSocket { err } => { 80 | let err: io::Error = From::from(err); 81 | err.kind() 82 | }, 83 | SimpleTcpHolePunchServerNewError::Listen { err } => err.kind(), 84 | SimpleTcpHolePunchServerNewError::SocketLocalAddr { err } => err.kind(), 85 | }; 86 | io::Error::new(kind, err_str) 87 | } 88 | } 89 | 90 | impl> SimpleTcpHolePunchServer { 91 | /// Create a new server. This will spawn a background thread which will serve requests until 92 | /// the server is dropped. 93 | pub fn new(mapping_context: T, deadline: Instant) 94 | -> WResult, 95 | MappedTcpSocketMapWarning, 96 | SimpleTcpHolePunchServerNewError> 97 | { 98 | let (mapped_socket, warnings) = match MappedTcpSocket::new(mapping_context.as_ref(), deadline) { 99 | WOk(mapped_socket, warnings) => (mapped_socket, warnings), 100 | WErr(e) => { 101 | return WErr(SimpleTcpHolePunchServerNewError::CreateMappedSocket { err: e }); 102 | } 103 | }; 104 | 105 | let tcp_socket = mapped_socket.socket; 106 | let stop_flag = Arc::new(AtomicBool::new(false)); 107 | let cloned_stop_flag = stop_flag.clone(); 108 | 109 | let tcp_listener = match tcp_socket.listen(128) { 110 | Ok(tcp_listener) => tcp_listener, 111 | Err(e) => return WErr(SimpleTcpHolePunchServerNewError::Listen { err: e }), 112 | }; 113 | 114 | let mut local_addr = None; 115 | let unrestricted_endpoints = mapped_socket.endpoints.into_iter().filter_map(|msa| { 116 | let addr = msa.addr; 117 | if socket_utils::is_loopback(&addr.ip()) { 118 | local_addr = Some(addr); 119 | return None; 120 | }; 121 | match msa.nat_restricted { 122 | false => Some(addr), 123 | true => None, 124 | } 125 | }).collect(); 126 | let local_addr = match local_addr { 127 | Some(local_addr) => *local_addr, 128 | None => { 129 | match tcp_listener.local_addr() { 130 | Ok(local_addr) => local_addr, 131 | Err(e) => return WErr(SimpleTcpHolePunchServerNewError::SocketLocalAddr { err: e }), 132 | } 133 | }, 134 | }; 135 | 136 | let raii_joiner = RaiiThreadJoiner::new(thread!("SimpleTcpHolePunchServer", move || { 137 | Self::run(tcp_listener, cloned_stop_flag); 138 | })); 139 | 140 | WOk(SimpleTcpHolePunchServer { 141 | _mapping_context: mapping_context, 142 | stop_flag: stop_flag, 143 | _raii_joiner: raii_joiner, 144 | local_addr: local_addr, 145 | known_endpoints: unrestricted_endpoints, 146 | }, warnings) 147 | } 148 | 149 | fn run(tcp_listener: TcpListener, 150 | stop_flag: Arc) { 151 | 152 | while !stop_flag.load(Ordering::SeqCst) { 153 | if let Ok((mut stream, peer_addr)) = tcp_listener.accept() { 154 | let _ = thread!("SimpleTcpHolePunchServer::run", move || { 155 | match stream.set_write_timeout(Some(Duration::from_secs(TCP_RW_TIMEOUT))) { 156 | Ok(()) => (), 157 | Err(_) => return, 158 | }; 159 | match stream.set_read_timeout(Some(Duration::from_secs(TCP_RW_TIMEOUT))) { 160 | Ok(()) => (), 161 | Err(_) => return, 162 | }; 163 | let mut read_buf = [0; 1024]; 164 | let bytes_read = match stream.read(&mut read_buf) { 165 | Ok(n) => n, 166 | Err(_) => return, 167 | }; 168 | if read_buf[..bytes_read] != listener_message::REQUEST_MAGIC_CONSTANT { 169 | return; 170 | } 171 | 172 | let resp = listener_message::EchoExternalAddr { 173 | external_addr: SocketAddr(peer_addr), 174 | }; 175 | 176 | let _ = stream.write(&unwrap_result!(serialise(&resp))); 177 | }); 178 | } 179 | } 180 | } 181 | 182 | /// Get the external addresses of this server to be shared with peers. 183 | pub fn addresses(&self) -> Vec { 184 | self.known_endpoints.clone() 185 | } 186 | } 187 | 188 | impl> Drop for SimpleTcpHolePunchServer { 189 | fn drop(&mut self) { 190 | self.stop_flag.store(true, Ordering::SeqCst); 191 | // Unblock the acceptor. 192 | let _ = TcpStream::connect(self.local_addr); 193 | } 194 | } 195 | 196 | -------------------------------------------------------------------------------- /src/simple_udp_hole_punch_server.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | //! # `nat_traversal` 19 | //! NAT traversal utilities. 20 | 21 | use std::io; 22 | use std::net::UdpSocket; 23 | use std::time::{Instant, Duration}; 24 | use std::sync::Arc; 25 | use std::sync::atomic::{AtomicBool, Ordering}; 26 | 27 | use maidsafe_utilities::serialisation::serialise; 28 | use maidsafe_utilities::thread::RaiiThreadJoiner; 29 | use w_result::{WResult, WOk, WErr}; 30 | 31 | use socket_addr::SocketAddr; 32 | use listener_message; 33 | 34 | use mapping_context::MappingContext; 35 | use mapped_udp_socket::{MappedUdpSocket, MappedUdpSocketNewError, MappedUdpSocketMapWarning}; 36 | 37 | const UDP_READ_TIMEOUT_SECS: u64 = 2; 38 | 39 | /// RAII type for a hole punch server which speaks the simple hole punching protocol. 40 | pub struct SimpleUdpHolePunchServer> { 41 | // TODO(canndrew): Use this to refresh our external addrs. 42 | _mapping_context: T, 43 | stop_flag: Arc, 44 | _raii_joiner: RaiiThreadJoiner, 45 | known_endpoints: Vec, 46 | } 47 | 48 | quick_error! { 49 | #[derive(Debug)] 50 | /// Errors returned by SimpleUdpHolePunchServer::new 51 | pub enum SimpleUdpHolePunchServerNewError { 52 | /// Error creating a mapped udp socket to listen on. 53 | CreateMappedSocket { 54 | err: MappedUdpSocketNewError } { 55 | description("Error creating a mapped udp socket to listen on.") 56 | display("Error creating a mapped udp socket to listen on: {}", err) 57 | cause(err) 58 | } 59 | /// Error setting the timeout on the server's listening socket. 60 | SetSocketTimeout { 61 | err: io::Error 62 | } { 63 | description("Error setting the timeout on the server's listening socket.") 64 | display("Error setting the timeout on the server's listening socket: {}.", err) 65 | cause(err) 66 | } 67 | } 68 | } 69 | 70 | impl From for io::Error { 71 | fn from(e: SimpleUdpHolePunchServerNewError) -> io::Error { 72 | let err_str = format!("{}", e); 73 | let kind = match e { 74 | SimpleUdpHolePunchServerNewError::CreateMappedSocket { err } => { 75 | let err: io::Error = From::from(err); 76 | err.kind() 77 | }, 78 | SimpleUdpHolePunchServerNewError::SetSocketTimeout { err } => err.kind(), 79 | }; 80 | io::Error::new(kind, err_str) 81 | } 82 | } 83 | 84 | impl> SimpleUdpHolePunchServer { 85 | /// Create a new server. This will spawn a background thread which will serve requests until 86 | /// the server is dropped. 87 | pub fn new(mapping_context: T, deadline: Instant) 88 | -> WResult, 89 | MappedUdpSocketMapWarning, 90 | SimpleUdpHolePunchServerNewError> 91 | { 92 | let (mapped_socket, warnings) = match MappedUdpSocket::new(mapping_context.as_ref(), deadline) { 93 | WOk(mapped_socket, warnings) => (mapped_socket, warnings), 94 | WErr(e) => { 95 | return WErr(SimpleUdpHolePunchServerNewError::CreateMappedSocket { err: e }); 96 | } 97 | }; 98 | 99 | let udp_socket = mapped_socket.socket; 100 | let stop_flag = Arc::new(AtomicBool::new(false)); 101 | let cloned_stop_flag = stop_flag.clone(); 102 | 103 | match udp_socket.set_read_timeout(Some(Duration::from_secs(UDP_READ_TIMEOUT_SECS))) { 104 | Ok(()) => (), 105 | Err(e) => { 106 | return WErr(SimpleUdpHolePunchServerNewError::SetSocketTimeout { err: e }) 107 | } 108 | }; 109 | 110 | let raii_joiner = RaiiThreadJoiner::new(thread!("SimpleUdpHolePunchServer", move || { 111 | Self::run(udp_socket, cloned_stop_flag); 112 | })); 113 | 114 | let unrestricted_endpoints = mapped_socket.endpoints.into_iter().filter_map(|msa| { 115 | match msa.nat_restricted { 116 | false => Some(msa.addr), 117 | true => None, 118 | } 119 | }).collect(); 120 | WOk(SimpleUdpHolePunchServer { 121 | _mapping_context: mapping_context, 122 | stop_flag: stop_flag, 123 | _raii_joiner: raii_joiner, 124 | known_endpoints: unrestricted_endpoints, 125 | }, warnings) 126 | } 127 | 128 | fn run(udp_socket: UdpSocket, 129 | stop_flag: Arc) { 130 | let mut read_buf = [0; 1024]; 131 | 132 | while !stop_flag.load(Ordering::SeqCst) { 133 | if let Ok((bytes_read, peer_addr)) = udp_socket.recv_from(&mut read_buf) { 134 | if read_buf[..bytes_read] != listener_message::REQUEST_MAGIC_CONSTANT { 135 | continue; 136 | } 137 | 138 | let resp = listener_message::EchoExternalAddr { 139 | external_addr: SocketAddr(peer_addr.clone()), 140 | }; 141 | 142 | let _ = udp_socket.send_to(&unwrap_result!(serialise(&resp)), 143 | peer_addr); 144 | } 145 | } 146 | } 147 | 148 | /// Get the external addresses of this server to be shared with peers. 149 | pub fn addresses(&self) -> Vec { 150 | self.known_endpoints.clone() 151 | } 152 | } 153 | 154 | impl> Drop for SimpleUdpHolePunchServer { 155 | fn drop(&mut self) { 156 | self.stop_flag.store(true, Ordering::SeqCst); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/socket_utils.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 MaidSafe.net limited. 2 | // 3 | // This SAFE Network Software is licensed to you under (1) the MaidSafe.net Commercial License, 4 | // version 1.0 or later, or (2) The General Public License (GPL), version 3, depending on which 5 | // licence you accepted on initial access to the Software (the "Licences"). 6 | // 7 | // By contributing code to the SAFE Network Software, or to this project generally, you agree to be 8 | // bound by the terms of the MaidSafe Contributor Agreement, version 1.0. This, along with the 9 | // Licenses can be found in the root directory of this project at LICENSE, COPYING and CONTRIBUTOR. 10 | // 11 | // Unless required by applicable law or agreed to in writing, the SAFE Network Software distributed 12 | // under the GPL Licence is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | // KIND, either express or implied. 14 | // 15 | // Please review the Licences for the specific language governing permissions and limitations 16 | // relating to use of the SAFE Network Software. 17 | 18 | use std::io; 19 | use std::net::{TcpStream, UdpSocket, IpAddr, Ipv4Addr, Ipv6Addr}; 20 | use std::net; 21 | use std::time::Instant; 22 | #[cfg(target_family = "windows")] 23 | use std::mem; 24 | use socket_addr::SocketAddr; 25 | use std::io::ErrorKind; 26 | use net2; 27 | 28 | /// A self interruptable receive trait that allows a timed-out period to be defined 29 | pub trait RecvUntil { 30 | /// After specified timed-out period, the blocking receive method shall return with an error 31 | fn recv_until(&self, 32 | buf: &mut [u8], 33 | deadline: Instant) 34 | -> io::Result>; 35 | } 36 | 37 | impl RecvUntil for UdpSocket { 38 | fn recv_until(&self, 39 | buf: &mut [u8], 40 | deadline: Instant) 41 | -> io::Result> { 42 | let old_timeout = try!(self.read_timeout()); 43 | loop { 44 | let current_time = Instant::now(); 45 | if current_time >= deadline { 46 | try!(self.set_read_timeout(old_timeout)); 47 | return Ok(None); 48 | } 49 | { 50 | let timeout = deadline - current_time; 51 | try!(self.set_read_timeout(Some(timeout))); 52 | } 53 | 54 | match self.recv_from(buf) { 55 | Ok((bytes_len, addr)) => { 56 | try!(self.set_read_timeout(old_timeout)); 57 | return Ok(Some((bytes_len, SocketAddr(addr)))); 58 | }, 59 | Err(e) => { 60 | match e.kind() { 61 | ErrorKind::TimedOut | ErrorKind::WouldBlock => { 62 | try!(self.set_read_timeout(old_timeout)); 63 | return Ok(None); 64 | }, 65 | ErrorKind::Interrupted => (), 66 | // On Windows, when we send a packet to an endpoint 67 | // which is not being listened on, the system responds 68 | // with an ICMP packet "ICMP port unreachable". 69 | // We do not care about this silly behavior, so we just 70 | // ignore it. 71 | // See here for more info: 72 | // https://bobobobo.wordpress.com/2009/05/17/udp-an-existing-connection-was-forcibly-closed-by-the-remote-host/ 73 | ErrorKind::ConnectionReset => (), 74 | _ => { 75 | try!(self.set_read_timeout(old_timeout)); 76 | return Err(e); 77 | }, 78 | } 79 | } 80 | } 81 | } 82 | } 83 | } 84 | 85 | // TODO(canndrew): Remove this once #[feature(ip)] is stable 86 | pub fn ipv4_is_unspecified(addr: &Ipv4Addr) -> bool { 87 | addr.octets() == [0, 0, 0, 0] 88 | } 89 | 90 | // TODO(canndrew): Remove this once #[feature(ip)] is stable 91 | pub fn ipv6_is_unspecified(addr: &Ipv6Addr) -> bool { 92 | addr.segments() == [0, 0, 0, 0, 0, 0, 0, 0] 93 | } 94 | 95 | pub fn ipv4_unspecified_to_loopback(addr: Ipv4Addr) -> Ipv4Addr { 96 | if ipv4_is_unspecified(&addr) { 97 | Ipv4Addr::new(127, 0, 0, 1) 98 | } 99 | else { 100 | addr 101 | } 102 | } 103 | 104 | pub fn ipv6_unspecified_to_loopback(addr: Ipv6Addr) -> Ipv6Addr { 105 | if ipv6_is_unspecified(&addr) { 106 | Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1) 107 | } 108 | else { 109 | addr 110 | } 111 | } 112 | 113 | pub fn ip_unspecified_to_loopback(addr: IpAddr) -> IpAddr { 114 | match addr { 115 | IpAddr::V4(addr_v4) => IpAddr::V4(ipv4_unspecified_to_loopback(addr_v4)), 116 | IpAddr::V6(addr_v6) => IpAddr::V6(ipv6_unspecified_to_loopback(addr_v6)), 117 | } 118 | } 119 | 120 | // TODO(canndrew): Remove this once #[feature(ip)] is stable 121 | pub fn ipv4_is_loopback(addr: &Ipv4Addr) -> bool { 122 | addr.octets()[0] == 127 123 | } 124 | 125 | // TODO(canndrew): Remove this once #[feature(ip)] is stable 126 | pub fn ipv6_is_loopback(addr: &Ipv6Addr) -> bool { 127 | addr.segments() == [0, 0, 0, 0, 0, 0, 0, 1] 128 | } 129 | 130 | pub fn is_loopback(addr: &IpAddr) -> bool { 131 | match *addr { 132 | IpAddr::V4(ref addr_v4) => ipv4_is_loopback(addr_v4), 133 | IpAddr::V6(ref addr_v6) => ipv6_is_loopback(addr_v6), 134 | } 135 | } 136 | 137 | #[cfg(target_family = "unix")] 138 | pub fn enable_so_reuseport(sock: &net2::TcpBuilder) -> io::Result<()> { 139 | use net2::unix::UnixTcpBuilderExt; 140 | let _ = try!(sock.reuse_port(true)); 141 | Ok(()) 142 | } 143 | 144 | #[cfg(target_family = "windows")] 145 | pub fn enable_so_reuseport(_sock: &net2::TcpBuilder) -> io::Result<()> { 146 | Ok(()) 147 | } 148 | 149 | // TODO(canndrew): This function should be deprecated once this issue 150 | // (https://github.com/rust-lang-nursery/net2-rs/issues/26) is resolved. 151 | #[cfg(target_family = "unix")] 152 | #[allow(unsafe_code)] 153 | pub fn tcp_builder_local_addr(sock: &net2::TcpBuilder) -> io::Result { 154 | use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; 155 | let fd = sock.as_raw_fd(); 156 | let stream = unsafe { TcpStream::from_raw_fd(fd) }; 157 | let ret = stream.local_addr(); 158 | let _ = stream.into_raw_fd(); 159 | ret 160 | } 161 | 162 | #[cfg(target_family = "windows")] 163 | #[allow(unsafe_code)] 164 | pub fn tcp_builder_local_addr(sock: &net2::TcpBuilder) -> io::Result { 165 | use std::os::windows::io::{AsRawSocket, FromRawSocket}; 166 | let fd = sock.as_raw_socket(); 167 | let stream = unsafe { TcpStream::from_raw_socket(fd) }; 168 | let ret = stream.local_addr(); 169 | mem::forget(stream); // TODO(canndrew): Is this completely safe? 170 | ret 171 | } 172 | 173 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | pub struct DisplaySlice<'a, T: 'a>(pub &'static str, pub &'a [T]); 4 | 5 | impl<'a, T> fmt::Display for DisplaySlice<'a, T> 6 | where T: fmt::Display 7 | { 8 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 9 | let DisplaySlice(description, slice) = *self; 10 | let len = slice.len(); 11 | try!(write!(f, "{} {}(s):", len, description)); 12 | for (i, t) in slice.iter().enumerate() { 13 | try!(write!(f, " ({} of {}) {}", i + 1, len, t)); 14 | } 15 | Ok(()) 16 | } 17 | } 18 | 19 | --------------------------------------------------------------------------------