├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── binding.gyp ├── lib ├── index.d.ts └── index.js ├── package-lock.json ├── package.json ├── src ├── arg_parser.h ├── get_value.h ├── netlink │ ├── config.h │ ├── core.cc │ ├── core.h │ ├── exception.code.inc │ ├── exception.h │ ├── netlink.h │ ├── release_manager.h │ ├── release_manager.inline.h │ ├── smart_buffer.cc │ ├── smart_buffer.h │ ├── smart_buffer.inline.h │ ├── socket.cc │ ├── socket.h │ ├── socket.inline.h │ ├── socket_group.cc │ ├── socket_group.h │ ├── socket_group.inline.h │ ├── util.cc │ ├── util.h │ └── util.inline.h ├── netlinksocket.cc ├── netlinkwrapper.cc └── netlinkwrapper.h ├── test ├── .eslintrc.js ├── .mocharc.js ├── base.test.ts ├── client.worker.ts ├── clients.test.ts ├── module.test.ts ├── sanity.test.ts ├── tcp.client.test.ts ├── tcp.server.test.ts ├── udp.test.ts └── utils │ ├── bad-arg.ts │ ├── echo-socket.ts │ ├── index.ts │ ├── permutations.ts │ ├── tester.tcp-client.ts │ ├── tester.tcp-server.ts │ ├── tester.ts │ └── tester.udp.ts ├── tsconfig.eslint.json └── tsconfig.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /* eslint-env node */ 3 | 4 | const { resolve } = require("path"); 5 | 6 | process.env.ESLINT_PATH_TSCONFIG = resolve("./tsconfig.eslint.json"); 7 | 8 | /** @type {import("eslint").Linter.Config} */ 9 | const baseEslintConfig = { 10 | extends: ["jacobfischer"], 11 | ignorePatterns: ["lib/**/*.js", "docs/**"], 12 | }; 13 | 14 | module.exports = baseEslintConfig; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | build 25 | 26 | # Dependency directory 27 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 28 | node_modules 29 | 30 | # Compiled source 31 | *.com 32 | *.class 33 | *.dll 34 | *.exe 35 | *.o 36 | *.so 37 | 38 | # Packages 39 | *.7z 40 | *.dmg 41 | *.gz 42 | *.iso 43 | *.jar 44 | *.rar 45 | *.tar 46 | *.zip 47 | 48 | # Logs and databases 49 | *.log 50 | *.sql 51 | *.sqlite 52 | 53 | # OS generated files 54 | .DS_Store 55 | .DS_Store? 56 | ._* 57 | .Spotlight-V100 58 | .Trashes 59 | ehthumbs.db 60 | Thumbs.db 61 | 62 | # Build artifact directories 63 | dist/ 64 | build/ 65 | temp/ 66 | 67 | # vscode 68 | .vscode/ 69 | 70 | # generated output 71 | out/ 72 | docs/ 73 | test-build/ 74 | netlinkwrapper-*.tgz 75 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | src/ 3 | dist/ 4 | docs/ 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = require("eslint-config-jacobfischer/prettier.config.js"); 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | os: osx # travis linux lacks IPv6 support 3 | node_js: 4 | - "10" 5 | - "12" 6 | - "14" 7 | 8 | script: 9 | # this builds the module, if it fails the build did not pass 10 | - npm install 11 | - npm run prettier:check 12 | - npm run lint 13 | - npm run ts:check 14 | - npm run build 15 | - npm test 16 | 17 | before_deploy: 18 | - npm run docs 19 | - npm run docs:predeploy 20 | - npm pack 21 | 22 | deploy: 23 | - provider: pages 24 | skip_cleanup: true 25 | local_dir: docs/ 26 | github_token: $GH_TOKEN 27 | on: 28 | tags: true 29 | branch: master 30 | node_js: "14" 31 | 32 | - provider: releases 33 | api_key: $GH_TOKEN 34 | skip_cleanup: true 35 | file_glob: true 36 | file: "netlinkwrapper-*.tgz" 37 | on: 38 | tags: true 39 | branch: master 40 | node_js: "14" 41 | 42 | - provider: npm 43 | skip_cleanup: true 44 | email: "jacob.t.fischer@gmail.com" 45 | api_key: $NPM_TOKEN 46 | on: 47 | tags: true 48 | branch: master 49 | node_js: "14" 50 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog] 5 | and this project adheres to [Semantic Versioning]. 6 | 7 | ## [2.0.2] - 2020-08-15 8 | ### Fixed 9 | - Fix invalid arguments to constructors not throwing when omitted [#16] 10 | 11 | ## [2.0.1] - 2020-08-09 12 | ### Fixed 13 | - Fix missing binding.gyp file in package deployed to NPM 14 | 15 | ## [2.0.0] - 2020-08-09 16 | ### Changes 17 | - **Breaking**: The entire shape of this package has been modified and expanded 18 | - The `NetLinkWrapper` constructor is removed, and is no longer the only 19 | export 20 | - `SocketClientTCP` functionally replaces `NetLinkWrapper` 21 | - It is a named export of the same name of this module now 22 | - `.connect` no longer exists. Instead connections are attempted to form 23 | during the constructor call 24 | - All constructors must be invoked with the `new` keyword 25 | - Failure to do so will result in an Error being thrown 26 | - `.blocking()` now is now a property `.isBlocking` 27 | - Setting it to a boolean will change the blocking nature of the socket 28 | - `.write()` renamed to `.send()` 29 | - Will now accept a `Buffer`, `string`, or `Uint8Array` typed value to send, 30 | instead of only a `string` [#15] 31 | - `.read()` renamed to `.receive()` 32 | - No longer requires (or accepts) a buffer size argument 33 | - Now returns a `Buffer` instance instead of a `string` 34 | - **Important**: The entire middleware component of this module has been 35 | re-written 36 | - It is recommended that you review the docs to see what has changed and been 37 | added 38 | 39 | ### Added 40 | - **New**: `SocketUDP` added for UDP usage 41 | - Can send and/or receive from other UDP sockets 42 | - **New**: `SocketServerTCP` added for TCP server usage 43 | - Can bind and listen to an address for new TCP clients 44 | - All socket classes can be manually specified to `IPv4` or `IPv6` 45 | - Defaults to `IPv4` 46 | - After constructed this can be checked via the `isIPv4` and `isIPv6` flags 47 | - Note: These cannot be set/changed these after construction, and attempting 48 | to do so will result in an Error being thrown 49 | - Sockets expose their `hostFrom`, `portFrom` (TCP Server/UDP), and `hostTo` 50 | , `portTo` (TCP Client) as properties 51 | - Note: These cannot be changed/set after constructed, and attempting to do so 52 | will result in an Error being thrown 53 | - See the [documentation] for full details on these new classes and 54 | functionality 55 | - Once `.disconnect()` is called, the new `isDestroyed` flag will be set from 56 | `false` to `true` 57 | - Note: This cannot be manually set, and attempting to do so will result in an 58 | Error being thrown 59 | 60 | ### Fixed 61 | - node-gyp C++ build warnings on Windows systems resolved 62 | 63 | ## [1.2.1] - 2020-01-07 64 | ### Security 65 | - Updated dependencies to latest to resolve security concerns 66 | 67 | ## [1.2.0] - 2020-01-01 68 | ### Fixed 69 | - Support for Node v13 builds [#13], [#14] 70 | - Fixes build warnings for unused variable `value_` 71 | ### Changed 72 | - Node v6 is no longer LTS thus dropped from support here 73 | 74 | ## [1.1.2] - 2019-07-03 75 | ### Fixed 76 | - Fixes for node supporting versions 6 through 12 (current) [#9] 77 | 78 | ## [1.1.1] - 2018-09-26 79 | ### Fixed 80 | - Fix for building against musl libc [#6] 81 | 82 | ## [1.1.0] - 2018-09-01 83 | ### Fixed 84 | - Build now works with Node.js v10 [#4] 85 | - Updated dependencies to latest to resolve security concerns 86 | 87 | ### Added 88 | - TypeScript definitions 89 | - This changelog! 90 | - TravisCI integration 91 | 92 | ## [1.0.0] - 2016-11-22 93 | ### Fixed 94 | - OSX builds working [#2] 95 | 96 | ### Added 97 | - Documentation in JSDoc format 98 | 99 | ## [0.0.1] - 2015-11-23 100 | - Initial release 101 | 102 | [#16]: https://github.com/JacobFischer/netlinkwrapper/issues/16 103 | [#15]: https://github.com/JacobFischer/netlinkwrapper/issues/15 104 | [#14]: https://github.com/JacobFischer/netlinkwrapper/pull/14 105 | [#13]: https://github.com/JacobFischer/netlinkwrapper/pull/13 106 | [#9]: https://github.com/JacobFischer/netlinkwrapper/pull/9 107 | [#6]: https://github.com/JacobFischer/netlinkwrapper/pull/6 108 | [#4]: https://github.com/JacobFischer/netlinkwrapper/pull/4 109 | [#2]: https://github.com/JacobFischer/netlinkwrapper/pull/2 110 | 111 | [2.0.2]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v2.0.2 112 | [2.0.1]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v2.0.1 113 | [2.0.0]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v2.0.0 114 | [1.2.1]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.2.1 115 | [1.2.0]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.2.0 116 | [1.1.2]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.1.2 117 | [1.1.1]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.1.1 118 | [1.1.0]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.1.0 119 | [1.0.0]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v1.0.0 120 | [0.0.1]: https://github.com/JacobFischer/netlinkwrapper/releases/tag/v0.0.1 121 | 122 | [documentation]: https://jacobfischer.github.io/netlinkwrapper/ 123 | 124 | [Keep a Changelog]: http://keepachangelog.com/en/1.0.0/ 125 | [Semantic Versioning]: http://semver.org/spec/v2.0.0.html 126 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # netlinkwrapper 2 | 3 | NetLinkWrapper allows access to TCP and UDP sockets via **synchronous** calls 4 | safety across platforms. 5 | 6 | This is a simple node-gyp module wrapper for the C++ library 7 | [NetLink Sockets][netlink]. Meaning this is a simple wrapper for a simple 8 | TCP/UDP socket. Also, and perhaps most importantly, this is an 9 | implementation that **only** works synchronously, as opposed to Node's 10 | asynchronous paradigm. 11 | 12 | ## Purpose 13 | 14 | Obviously node's [net]/[dgram] modules are far more suited for most TCP/UDP 15 | communications than this module. However, those modules can only work 16 | asynchronously, as is Node's design. To contrast, that is the opposite of the 17 | intention of this module. If you are in the **very** odd situation where you 18 | need synchronous usage of sockets across platforms, then this module may suit 19 | you. 20 | 21 | ## How to use 22 | 23 | As with most modules, use npm or your preferred package manager to install it 24 | for you. 25 | 26 | ``` 27 | npm install netlinkwrapper 28 | ``` 29 | 30 | **Note** [node-gyp] is a dependency. Ensure your setup can build C++ modules. 31 | Follow their documentation for installing the appropriate C++ build tools based 32 | on your environment. 33 | 34 | ## Examples 35 | 36 | ### TCP 37 | 38 | ```js 39 | const { SocketClientTCP, SocketServerTCP } = require('netlinkwrapper'); 40 | 41 | const port = 33333; 42 | const server = new SocketServerTCP(port); 43 | const client = new SocketClientTCP(port, 'localhost'); 44 | 45 | const serverSends = 'hello world!'; 46 | const serversClient = server.accept(); 47 | serversClient.send(serverSends); 48 | 49 | const clientReceived = client.receive(); 50 | 51 | const identical = serverSends === clientReceived.toString(); // should be true 52 | 53 | console.log('the two strings are identical?', identical); 54 | 55 | client.disconnect(); 56 | server.disconnect(); 57 | 58 | ``` 59 | 60 | ### UDP 61 | 62 | ```js 63 | const { SocketUDP } = require('netlinkwrapper'); 64 | 65 | const portA = 54321; 66 | const portB = 12345; 67 | 68 | const socketA = new SocketUDP(portA, 'localhost'); 69 | const socketB = new SocketUDP(portB, 'localhost'); 70 | 71 | socketA.sendTo('localhost', portB, 'Hello from socketA'); 72 | const got = socketB.receiveFrom(); 73 | 74 | const identical = got.port === portA; 75 | console.log('identical?', identical); // should be: true 76 | 77 | // should be: 'got: 'Hello from socketA' from localhost:54321' 78 | console.log(`got: '${got.data.toString()}' from ${got.host}:${got.port}`); 79 | 80 | socketA.disconnect(); 81 | socketB.disconnect(); 82 | 83 | ``` 84 | 85 | ## Other Notes 86 | 87 | Due to the connection-less nature of UDP, the same constructor can be used 88 | to make a "client" or "server" esc UDP socket. 89 | 90 | After calling `disconnect` on a socket it is considered "destroyed", and cannot 91 | be re-used. In addition, attempting to call any functions off it will result in 92 | Errors being thrown. 93 | 94 | TypeScript types are included in this package. They are highly encouraged to 95 | use. Attempting to do anything outside the scope of the included types will 96 | probably result in Errors being thrown. 97 | 98 | ## Docs 99 | 100 | Official documentation can be found online at [GitHub][docs]. 101 | 102 | ## Alternatives 103 | 104 | If you are looking for similar functionality, but **without** the node-gyp 105 | dependency I have made a similar (but **much** slower) module, [SyncSocket]. 106 | 107 | [netlink]: http://netlinksockets.sourceforge.net/ 108 | [net]: https://nodejs.org/api/net.html 109 | [dragm]: https://nodejs.org/api/dgram.html 110 | [node-gyp]: https://github.com/nodejs/node-gyp 111 | [docs]: https://jacobfischer.github.io/netlinkwrapper/ 112 | [SyncSocket]: https://github.com/JacobFischer/sync-socket 113 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "netlinksocket", 5 | "sources": [ 6 | "src/netlinksocket.cc", 7 | "src/netlinkwrapper.cc", 8 | "src/netlink/core.cc", 9 | "src/netlink/smart_buffer.cc", 10 | "src/netlink/socket.cc", 11 | "src/netlink/socket_group.cc", 12 | "src/netlink/util.cc" 13 | ], 14 | "cflags": [ "-fexceptions" ], 15 | "cflags_cc": [ "-fexceptions" ], 16 | "cflags!": [ "-fno-exceptions" ], 17 | "cflags_cc!": [ "-fno-exceptions" ], 18 | "conditions": [ 19 | [ 20 | 'OS=="win"', { 21 | "libraries": [ "ws2_32.lib" ] 22 | }, 23 | 'OS=="mac"', { 24 | "xcode_settings": { 25 | "GCC_ENABLE_CPP_EXCEPTIONS": "YES" 26 | } 27 | } 28 | ] 29 | ], 30 | "include_dirs" : [ 31 | " 2 | 3 | /** 4 | * The base socket all netlinkwrapper Socket instances inherit from. 5 | * No instances will ever, or can ever, be directly created from this class. 6 | * Instead this acts as a base class to check for `instanceOf` against all 7 | * the different socket classes. **Note:**: Attempting to use `new` against this 8 | * class will result in an exception being thrown. 9 | */ 10 | export declare abstract class SocketBase { 11 | /** 12 | * Disconnects this so. Once this is called the socket is considered 13 | * "destroyed" and no no longer be used for any form of communication. 14 | */ 15 | disconnect(): void; 16 | 17 | /** 18 | * The local port the socket is bound to. 19 | */ 20 | readonly portFrom: number; 21 | 22 | /** 23 | * Gets/sets the blocking nature of the Socket. True if to block, false 24 | * otherwise. 25 | * 26 | * When a socket is blocking, calls such as receive() and accept() will 27 | * synchronously wait until there is data to return from those calls. 28 | * When a socket is not set to block, they will check and immediately return 29 | * undefined when there is no data from those calls. 30 | */ 31 | isBlocking: boolean; 32 | 33 | /** 34 | * Flag if this socket has been manually disconnected and thus destroyed. 35 | * Destroyed sockets cannot be re-used. True if this socket has been 36 | * destroyed and disconnected. False otherwise. 37 | * 38 | * **NOTE**: On unexpected socket errors this may not be set correctly. This 39 | * check can only ensure this socket disconnected in an expected fashion. 40 | */ 41 | readonly isDestroyed: boolean; 42 | 43 | /** 44 | * Flag if the socket is Internet Protocol Version 4 (IPv4). 45 | */ 46 | readonly isIPv4: boolean; 47 | 48 | /** 49 | * Flag if the socket is Internet Protocol Version 6 (IPv6). 50 | */ 51 | readonly isIPv6: boolean; 52 | } 53 | 54 | /** 55 | * Represents a TCP Client connection. 56 | */ 57 | export declare class SocketClientTCP extends SocketBase { 58 | /** 59 | * Creates, and then attempts to connect to a remote server given an 60 | * address. If no connection can be made, an Error is thrown. 61 | * 62 | * @param portTo - The host of the address to connect this TCP client to. 63 | * @param hostTo - The host of the address to connect this TCP client to. 64 | * @param ipVersion - An optional specific IP version to use. Defaults to 65 | * IPv4. 66 | */ 67 | constructor(portTo: number, hostTo: string, ipVersion?: "IPv4" | "IPv6"); 68 | 69 | /** 70 | * The target host of the socket. 71 | */ 72 | readonly hostTo: string; 73 | 74 | /** 75 | * The port this socket is connected/sends to. 76 | */ 77 | readonly portTo: number; 78 | 79 | /** 80 | * Attempts to Receive data from the server and return it as a Buffer. 81 | * 82 | * @returns A Buffer instance with the data read from the connected server. 83 | * If set to blocking this call will synchronously block until some data 84 | * is received. Otherwise if there is no data to receive, this will return 85 | * undefined immediately and not block. 86 | */ 87 | receive(): Buffer | undefined; 88 | 89 | /** 90 | * Sends the data to the connected server. 91 | * 92 | * @param data - The data you want to send, as a string, Buffer, or 93 | * Uint8Array. 94 | */ 95 | send(data: string | Buffer | Uint8Array): void; 96 | } 97 | 98 | /** 99 | * Represents a TCP Server connection. 100 | */ 101 | export declare class SocketServerTCP extends SocketBase { 102 | /** 103 | * Creates a TCP Server listening on a given port (an optional host) for 104 | * new TCP Client connections. 105 | * 106 | * @param portFrom - The local port the socket will be bound to. 107 | * @param hostFrom - The local address to be bound to 108 | * (example: "localhost" or "127.0.0.1"). 109 | * Empty/undefined (by default) or "*" means all variable addresses. 110 | * @param ipVersion - The IP version to be used. IPv4 by default. 111 | */ 112 | constructor( 113 | portFrom: number, 114 | hostFrom?: string, 115 | ipVersion?: "IPv4" | "IPv6", 116 | ); 117 | 118 | /** 119 | * Listens for a new client connection, and accepts them, returning a new 120 | * `SocketClientTCP` instance as an interface to send and receive 121 | * data from their connection. 122 | * 123 | * @returns When a new connection can be accepted, a new 124 | * `SocketClientTCP` instance. If set to blocking this call will 125 | * synchronously block until a connect is made to accept and return. 126 | * Otherwise when not blocking and there is no connection to accept, 127 | * `undefined` is returned. 128 | */ 129 | accept(): SocketClientTCP | undefined; 130 | 131 | /** 132 | * Gets the socket local address. Empty string means any bound host. 133 | */ 134 | readonly hostFrom: string; 135 | } 136 | 137 | /** 138 | * Represents a UDP Datagram. 139 | */ 140 | export declare class SocketUDP extends SocketBase { 141 | /** 142 | * Creates a UDP socket datagram with an address to use as the default 143 | * socket to send/receive from. Because UDP is connection-less unlike TCP, 144 | * no Error is thrown on construction if the host/port combo do no listen. 145 | * 146 | * @param portFrom - An optional local port to bind to. If left undefined 147 | * then the local port of the socket is chosen by operating system. 148 | * @param hostFrom - An optional address to bind to. If left undefined, 149 | * empty string, or "*", then the operating system attempts to bind 150 | * to all local addresses. 151 | * @param ipVersion - The IP version to be used. IPv4 by default. 152 | */ 153 | constructor( 154 | portFrom?: number, 155 | hostFrom?: string, 156 | ipVersion?: "IPv4" | "IPv6", 157 | ); 158 | 159 | /** 160 | * The socket local address. Empty string means any bound host. 161 | */ 162 | readonly hostFrom: string; 163 | 164 | /** 165 | * Receive data from datagrams and returns the data and their address. 166 | * 167 | * @returns An object, containing the key `data` as a Buffer of the received 168 | * data. The address is present as key `host` and key `port`. 169 | */ 170 | receiveFrom(): { host: string; port: number; data: Buffer } | undefined; 171 | 172 | /** 173 | * Sends to a specific datagram address some data. 174 | * 175 | * @param hostTo - The host string to send data to. 176 | * @param portTo - The port number to send data to. 177 | * @param data - The actual data payload to send. Can be a `string`, 178 | * `Buffer`, or `Uint8Array`. 179 | */ 180 | sendTo( 181 | hostTo: string, 182 | portTo: number, 183 | data: string | Buffer | Uint8Array, 184 | ): void; 185 | } 186 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var netlinkwrapper = require("bindings")("netlinksocket"); 2 | netlinkwrapper.__esModule = true; 3 | netlinkwrapper.default = void 0; 4 | 5 | module.exports = netlinkwrapper; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "netlinkwrapper", 3 | "version": "2.0.2", 4 | "description": "Synchronous TCP/UDP sockets via NetLink Sockets", 5 | "author": "JacobFischer ", 6 | "license": "GNU GPL V2.0", 7 | "main": "lib/index.js", 8 | "types": "lib/index.d.ts", 9 | "gypfile": true, 10 | "bugs": { 11 | "url": "https://github.com/JacobFischer/netlinkwrapper/issues" 12 | }, 13 | "homepage": "https://jacobfischer.github.io/netlinkwrapper/", 14 | "keywords": [ 15 | "net", 16 | "sync", 17 | "synchronous", 18 | "tcp", 19 | "socket", 20 | "network", 21 | "addon" 22 | ], 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/JacobFischer/netlinkwrapper.git" 26 | }, 27 | "dependencies": { 28 | "bindings": "1.5.0", 29 | "nan": "2.14.1" 30 | }, 31 | "devDependencies": { 32 | "@types/bindings": "1.3.0", 33 | "@types/chai": "4.2.12", 34 | "@types/eslint": "7.2.0", 35 | "@types/mocha": "8.0.1", 36 | "@types/node": "14.0.27", 37 | "@typescript-eslint/eslint-plugin": "3.8.0", 38 | "chai": "4.2.0", 39 | "eslint": "7.6.0", 40 | "eslint-config-jacobfischer": "git://github.com/JacobFischer/eslint-config.git#94bffc94da2336b08dd5159a1ab4c93525c46d4b", 41 | "eslint-plugin-import": "2.22.0", 42 | "eslint-plugin-jsdoc": "30.2.1", 43 | "eslint-plugin-jsx-a11y": "6.3.1", 44 | "eslint-plugin-mocha": "8.0.0", 45 | "eslint-plugin-prettier": "3.1.4", 46 | "mocha": "8.1.1", 47 | "npm-check-updates": "7.0.3", 48 | "prettier": "2.0.5", 49 | "shx": "0.3.2", 50 | "ts-mocha": "7.0.0", 51 | "ts-node": "8.10.2", 52 | "ts-typed-events": "2.0.0", 53 | "typedoc": "0.18.0", 54 | "typescript": "3.9.7" 55 | }, 56 | "scripts": { 57 | "clean": "npm run clean:build && npm run clean:docs", 58 | "clean:build": "shx rm -rf build/ dist/", 59 | "clean:docs": "shx rm -rf docs/", 60 | "purge": "npm run clean && shx rm -rf node_modules/ && rm -rf package-lock.json", 61 | "docs": "typedoc --module commonjs --includeDeclarations --mode file --excludeNotExported --excludeExternals --out docs lib", 62 | "docs:predeploy": "shx touch docs/.nojekyll", 63 | "build": "node-gyp rebuild", 64 | "lint": "eslint ./", 65 | "prettier:base": "prettier **/*.{js,ts}", 66 | "prettier": "npm run prettier:base -- --write", 67 | "prettier:check": "npm run prettier:base -- --check", 68 | "ts:check": "tsc --noEmit", 69 | "test": "ts-mocha --paths test/**/*.test.ts --config test/.mocharc.js", 70 | "ncu": "ncu -u" 71 | }, 72 | "files": [ 73 | "binding.gyp", 74 | "lib/", 75 | "src/" 76 | ] 77 | } -------------------------------------------------------------------------------- /src/arg_parser.h: -------------------------------------------------------------------------------- 1 | #ifndef ARG_PARSER_H 2 | #define ARG_PARSER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "netlinkwrapper.h" 9 | #include "get_value.h" 10 | 11 | class ArgParser 12 | { 13 | private: 14 | bool valid = true; 15 | int position = -1; 16 | bool optional = false; 17 | const v8::FunctionCallbackInfo *v8_args; 18 | 19 | std::string named_position() 20 | { 21 | switch (this->position) 22 | { 23 | case 0: 24 | return "First"; 25 | case 1: 26 | return "Second"; 27 | case 2: 28 | return "Third"; 29 | case 3: 30 | return "Fourth"; 31 | case 4: 32 | return "Fifth"; 33 | case 5: 34 | return "Sixth"; 35 | case 6: 36 | return "Seventh"; 37 | case 7: 38 | return "Eighth"; 39 | case 8: 40 | return "Ninth"; 41 | case 9: 42 | return "Tenth"; 43 | // would there really be any more positional args past here? 44 | default: 45 | return "Some"; 46 | } 47 | } 48 | 49 | void invalidate(const char *arg_name, std::string reason) 50 | { 51 | this->valid = false; 52 | 53 | auto isolate = v8::Isolate::GetCurrent(); 54 | std::stringstream ss; 55 | 56 | ss << named_position(); 57 | ss << " argument \"" << arg_name << "\" "; 58 | ss << reason; 59 | auto error = Nan::New(ss.str()).ToLocalChecked(); 60 | isolate->ThrowException(v8::Exception::TypeError(error)); 61 | } 62 | 63 | public: 64 | ArgParser(const v8::FunctionCallbackInfo &args) 65 | { 66 | this->v8_args = &args; 67 | } 68 | 69 | bool isInvalid() 70 | { 71 | return !this->valid; 72 | } 73 | 74 | template 75 | ArgParser &opt( 76 | const char *arg_name, 77 | T &&value, 78 | GetValue::SubType sub_type = GetValue::SubType::None) 79 | { 80 | this->optional = true; 81 | return this->arg(arg_name, value, sub_type); 82 | } 83 | 84 | template 85 | ArgParser &arg( 86 | const char *arg_name, 87 | T &&value, 88 | GetValue::SubType sub_type = GetValue::SubType::None) 89 | { 90 | this->position += 1; 91 | 92 | if (!this->valid) 93 | { 94 | return *this; // no reason to keep parsing 95 | } 96 | 97 | auto args_length = this->v8_args->Length(); 98 | if (this->position >= args_length) 99 | { 100 | if (!this->optional) 101 | { 102 | std::stringstream ss; 103 | ss << "is required, but not enough arguments passed " 104 | << "(" << args_length << ")."; 105 | this->invalidate(arg_name, ss.str()); 106 | } 107 | 108 | return *this; 109 | } 110 | 111 | auto arg = (*this->v8_args)[this->position]; 112 | 113 | if (this->optional && arg->IsUndefined()) 114 | { 115 | return *this; 116 | } 117 | 118 | std::string error_message = GetValue::get_value(value, arg, sub_type); 119 | 120 | if (error_message.length() > 0) 121 | { 122 | this->invalidate(arg_name, error_message); 123 | } 124 | 125 | return *this; 126 | } 127 | }; 128 | 129 | #endif 130 | -------------------------------------------------------------------------------- /src/get_value.h: -------------------------------------------------------------------------------- 1 | #ifndef GET_VALUE_H 2 | #define GET_VALUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "netlinkwrapper.h" 9 | 10 | namespace GetValue 11 | { 12 | enum SubType 13 | { 14 | None, 15 | SendableData, 16 | }; 17 | 18 | std::string get_typeof_str(const v8::Local &arg) 19 | { 20 | auto isolate = v8::Isolate::GetCurrent(); 21 | auto type_of = arg->TypeOf(isolate); 22 | 23 | Nan::Utf8String utf8_str(type_of); 24 | std::stringstream ss; 25 | ss << "Got type \"" << *utf8_str << "\"."; 26 | return ss.str(); 27 | } 28 | 29 | template 30 | std::string get_value( 31 | T &&value, 32 | const v8::Local &arg, 33 | SubType sub_type) 34 | { 35 | return "Cannot handle unknown type"; 36 | } 37 | 38 | template <> 39 | std::string get_value( 40 | std::uint16_t &value, 41 | const v8::Local &arg, 42 | SubType sub_type) 43 | { 44 | if (!arg->IsNumber()) 45 | { 46 | return "must be a number. " + get_typeof_str(arg); 47 | } 48 | 49 | auto isolate = v8::Isolate::GetCurrent(); 50 | auto as_number = arg->IntegerValue(isolate->GetCurrentContext()).FromJust(); 51 | 52 | if (as_number <= 0) 53 | { 54 | std::stringstream ss; 55 | ss << as_number << " must be greater than 0."; 56 | return ss.str(); 57 | } 58 | 59 | if (as_number > UINT16_MAX) 60 | { 61 | std::stringstream ss; 62 | ss << as_number << " beyond max port range of " 63 | << UINT16_MAX << "."; 64 | return ss.str(); 65 | } 66 | 67 | value = static_cast(as_number); 68 | return ""; 69 | } 70 | 71 | template <> 72 | std::string get_value( 73 | bool &value, 74 | const v8::Local &arg, 75 | SubType sub_type) 76 | { 77 | if (!arg->IsBoolean()) 78 | { 79 | return "must be a boolean. " + get_typeof_str(arg); 80 | } 81 | 82 | auto boolean = arg->IsTrue(); 83 | value = boolean; 84 | return ""; 85 | } 86 | 87 | template <> 88 | std::string get_value( 89 | NL::IPVer &value, 90 | const v8::Local &arg, 91 | SubType sub_type) 92 | { 93 | std::string invalid_string("must be an ip version string either 'IPv4' or 'IPv6'."); 94 | if (!arg->IsString()) 95 | { 96 | std::stringstream ss; 97 | ss << invalid_string << " " << get_typeof_str(arg); 98 | return ss.str(); 99 | } 100 | 101 | Nan::Utf8String utf8_string(arg); 102 | std::string str(*utf8_string); 103 | 104 | if (str.compare("IPv6") == 0) 105 | { 106 | value = NL::IPVer::IP6; 107 | } 108 | else if (str.compare("IPv4") == 0) 109 | { 110 | value = NL::IPVer::IP4; 111 | } 112 | else 113 | { 114 | std::stringstream ss; 115 | ss << invalid_string << " Got: '" << str << "'."; 116 | return ss.str(); 117 | } 118 | 119 | return ""; 120 | } 121 | 122 | template <> 123 | std::string get_value( 124 | std::string &value, 125 | const v8::Local &arg, 126 | SubType sub_type) 127 | { 128 | auto is_string = arg->IsString(); 129 | if (sub_type != SubType::SendableData && !is_string) 130 | { 131 | return "must be a string. " + get_typeof_str(arg); 132 | } 133 | 134 | if (is_string) 135 | { 136 | Nan::Utf8String utf8_str(arg); 137 | value = std::string(*utf8_str); 138 | } 139 | else if (arg->IsUint8Array()) 140 | { 141 | auto typed_array = arg.As(); 142 | Nan::TypedArrayContents contents(typed_array); 143 | value = std::string(*contents, contents.length()); 144 | } 145 | else if (node::Buffer::HasInstance(arg)) 146 | { 147 | auto buffer = node::Buffer::Data(arg); 148 | auto length = node::Buffer::Length(arg); 149 | value = std::string(buffer, length); 150 | } 151 | else 152 | { 153 | return "must be a string, Buffer, or Uint8Array"; 154 | } 155 | 156 | return ""; 157 | } 158 | } // namespace GetValue 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /src/netlink/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_CONFIG 23 | #define __NL_CONFIG 24 | 25 | #include 26 | 27 | 28 | const size_t DEFAULT_LISTEN_QUEUE = 50; 29 | 30 | const size_t DEFAULT_SMARTBUFFER_SIZE = 1024; 31 | const double DEFAULT_SMARTBUFFER_REALLOC_RATIO = 1.5; 32 | 33 | 34 | 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/netlink/core.cc: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #include "core.h" 23 | 24 | 25 | NL_NAMESPACE 26 | 27 | /** 28 | * \fn void init() 29 | * Library initialization function 30 | * 31 | * @warning Must be called before using the library 32 | */ 33 | 34 | 35 | void init() { 36 | 37 | #ifdef OS_WIN32 38 | 39 | WSADATA wsaData; 40 | if (WSAStartup(MAKEWORD(2,0), &wsaData) != 0) 41 | throw Exception(Exception::ERROR_INIT, "Library inicialization failed"); 42 | 43 | #endif 44 | } 45 | 46 | NL_NAMESPACE_END 47 | 48 | -------------------------------------------------------------------------------- /src/netlink/core.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | /** 23 | * @file core.h 24 | * NetLink core types and init 25 | */ 26 | 27 | 28 | #ifndef __NL_CORE 29 | #define __NL_CORE 30 | 31 | #include "config.h" 32 | 33 | #define NL_NAMESPACE_NAME NL 34 | 35 | #define NL_NAMESPACE namespace NL_NAMESPACE_NAME { 36 | #define NL_NAMESPACE_END }; 37 | #define NL_NAMESPACE_USE using namespace NL_NAMESPACE_NAME; 38 | 39 | 40 | #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) 41 | 42 | #define OS_WIN32 43 | // #define _WIN32_WINNT 0x501 44 | 45 | 46 | #include 47 | #include 48 | 49 | // Requires Win7 or Vista 50 | // Link to Ws2_32.lib library 51 | 52 | # if defined(_MSC_VER) && _MSC_VER < 1900 53 | # define snprintf _snprintf_s 54 | # endif 55 | 56 | #else 57 | 58 | #define OS_LINUX 59 | 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #include 66 | #include 67 | #include 68 | #include 69 | #include 70 | 71 | #endif 72 | 73 | 74 | #include 75 | 76 | 77 | NL_NAMESPACE 78 | 79 | using std::string; 80 | 81 | 82 | void init(); 83 | 84 | 85 | /** 86 | * @enum Protocol 87 | * 88 | * Defines protocol type. 89 | */ 90 | 91 | enum Protocol { 92 | 93 | TCP, /**< TCP Protocol*/ 94 | UDP /**< UDP Protocol*/ 95 | }; 96 | 97 | 98 | /** 99 | * @enum IPVer 100 | * 101 | * Defines the version of IP. 102 | */ 103 | 104 | enum IPVer { 105 | 106 | IP4, /**< IP version 4*/ 107 | IP6, /**< IP version 6*/ 108 | ANY /**< Any IP version*/ 109 | }; 110 | 111 | 112 | /** 113 | * @enum SocketType 114 | * 115 | * Defines the nature of the socket. 116 | */ 117 | 118 | enum SocketType { 119 | 120 | CLIENT, /**< TCP or UDP socket connected or directed to a target host*/ 121 | SERVER /**< TCP socket which listens for connections or UDP socket without target host*/ 122 | }; 123 | 124 | NL_NAMESPACE_END 125 | 126 | 127 | #include "exception.h" 128 | #include "release_manager.h" 129 | #include "util.h" 130 | 131 | #endif 132 | -------------------------------------------------------------------------------- /src/netlink/exception.code.inc: -------------------------------------------------------------------------------- 1 | BAD_PROTOCOL, 2 | BAD_IP_VER, 3 | ERROR_INIT, 4 | ERROR_SET_ADDR_INFO, 5 | ERROR_GET_ADDR_INFO, 6 | ERROR_SET_SOCK_OPT, 7 | ERROR_CAN_NOT_LISTEN, 8 | ERROR_CONNECT_SOCKET, 9 | ERROR_SEND, 10 | ERROR_READ, 11 | ERROR_IOCTL, 12 | ERROR_SELECT, 13 | ERROR_ALLOC, 14 | EXPECTED_TCP_SOCKET, 15 | EXPECTED_UDP_SOCKET, 16 | EXPECTED_CLIENT_SOCKET, 17 | EXPECTED_SERVER_SOCKET, 18 | EXPECTED_HOST_TO, 19 | OUT_OF_RANGE 20 | -------------------------------------------------------------------------------- /src/netlink/exception.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_EXCEPTION 23 | #define __NL_EXCEPTION 24 | 25 | #include "core.h" 26 | 27 | NL_NAMESPACE 28 | 29 | /** 30 | * @class Exception exception.h netlink/exception.h 31 | * 32 | * Exception Class 33 | */ 34 | 35 | 36 | class Exception { 37 | 38 | public: 39 | 40 | enum CODE { 41 | #include "exception.code.inc" 42 | }; 43 | 44 | private: 45 | 46 | CODE _code; 47 | string _msg; 48 | int _nativeErrorCode; 49 | 50 | public: 51 | 52 | Exception(CODE code, const string& msg, int nativeErrorCode = 0): _code(code), _msg(msg), _nativeErrorCode(nativeErrorCode) {} 53 | 54 | /** 55 | * Returns the error code: 56 | * @htmlinclude "exception.code.inc" 57 | * @return Error code 58 | * 59 | * \note Defined Error Codes: 60 | * \li BAD_PROTOCOL 61 | * \li BAD_IP_VER 62 | * \li ERROR_INIT 63 | * \li ERROR_SET_ADDR_INFO 64 | * \li ERROR_GET_ADDR_INFO 65 | * \li ERROR_SET_SOCK_OPT 66 | * \li ERROR_CAN_NOT_LISTEN 67 | * \li ERROR_CONNECT_SOCKET 68 | * \li ERROR_SEND 69 | * \li ERROR_READ 70 | * \li ERROR_IOCTL 71 | * \li ERROR_SELECT 72 | * \li ERROR_ALLOC 73 | * \li EXPECTED_TCP_SOCKET 74 | * \li EXPECTED_UDP_SOCKET 75 | * \li EXPECTED_CLIENT_SOCKET 76 | * \li EXPECTED_SERVER_SOCKET 77 | * \li EXPECTED_HOST_TO 78 | * \li OUT_OF_RANGE 79 | */ 80 | 81 | CODE code() const { return _code; } 82 | 83 | 84 | /** 85 | * Returns the error text message 86 | * 87 | * @return A string with a human readable error description 88 | */ 89 | 90 | const string& msg() const { return _msg; } 91 | 92 | /** 93 | * Returns the error text message 94 | * 95 | * @return A C-string with a human readable error description 96 | */ 97 | 98 | const char* what() const { return _msg.c_str(); } 99 | 100 | 101 | /** 102 | * Returns the native error code received. 103 | * 104 | * @return Native error code set by OS 105 | * @warning Native error codes differ depending of the OS 106 | */ 107 | 108 | int nativeErrorCode() const { return _nativeErrorCode; } 109 | }; 110 | 111 | NL_NAMESPACE_END 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /src/netlink/netlink.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012-2014 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NET_LINK 23 | #define __NET_LINK 24 | 25 | 26 | /** 27 | * @example clientEcho.cc 28 | * Example of TCP CLIENT Socket 29 | * 30 | * @example serverEcho.cc 31 | * Example of TCP SERVER Socket 32 | * 33 | * @example udpDirectChat.cc 34 | * Example of UDP non-blocking Socket 35 | * 36 | * @example chatClient.cc 37 | * Example of SocketGroup 38 | * 39 | * @example chatServer.cc 40 | * Example of SocketGroup 41 | * 42 | * @example webGet.cc 43 | * Example of SmartBuffer and SocketGroup. 44 | * Retrieves a web page and prints it. 45 | */ 46 | 47 | /** 48 | * @namespace NL 49 | * NetLink Sockets Namespace 50 | */ 51 | 52 | 53 | /** 54 | * @mainpage NetLink Sockets C++ Library 55 | * @author Pedro Fco. Pareja Ruiz ( PedroPareja [at] Gmail.com ) 56 | * @version 1.0.0-pre-6 57 | * 58 | * This is a C++ socket library designed to enable easy and fast development of socket related functionality. 59 | * 60 | * @warning Do not forget to call NL::init() in first place for the library initialization. This is 61 | * only necessary in windows, but in the others OS will not do any harm. 62 | * 63 | * @note Since 1.0.0, NetLink Sockets C++ can be used in Windows XP (earlier versions require at least Windows Vista to be used in Windows OS) 64 | * 65 | * All the components of NetLink Sockets are in NL namespace. 66 | * 67 | * Download the latest version of the library at http://sourceforge.net/projects/netlinksockets/ 68 | * 69 | * 70 | * @par Linking: 71 | * 72 | * @li Linux and OSX: no need to link anything. 73 | * @li Windows: Link against WS2_32.lib (system lib) in Visual C++ or libws2_32.a in MingW. 74 | * 75 | * @n 76 | * @par CHANGELOG 77 | *
78 | * 79 | * 1.0.0-pre-6 80 | * @li Fixed: getTime() return type changed to unsigned long long 81 | * 82 | * 1.0.0-pre-5 83 | * @li Compilers: now \b MinGW compatible. 84 | * @li Added copy constructor and copy operator to SmartBuffer class. 85 | * @li Fixed: (remotely) possible memory leaks in NL::Socket::accept and NL::SmartBuffer::read 86 | * 87 | * 1.0.0-pre-4 88 | * @li Fixed: memory leak in NL::Socket::initSocket(): some blocks of addrinfo were not completely freed 89 | * 90 | * 1.0.0-pre-3 91 | * @li Fixed example: NL::init() was missing in udpDirectChat.cc 92 | * @li Added sourceForge logo to documentation 93 | * 94 | * 1.0.0-pre-2 95 | * @li Added SmartBuffer class 96 | * @li Added webGet.cc example 97 | * @li Documentation improvements 98 | * 99 | * 1.0.0-pre-1 100 | * @li First v1 release 101 | */ 102 | 103 | 104 | 105 | #include 106 | #include 107 | 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/netlink/release_manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_RM 23 | #define __NL_RM 24 | 25 | #include "core.h" 26 | 27 | #include 28 | 29 | NL_NAMESPACE 30 | 31 | using std::vector; 32 | 33 | /** 34 | * @class ReleaseManager release_manager.h netlink/release_manager.h 35 | * 36 | * Release Manager Class 37 | * 38 | * Private. For internal use 39 | */ 40 | 41 | template 42 | class ReleaseManager { 43 | 44 | protected: 45 | 46 | vector _releaseQueue; 47 | vector _releaseAddressQueue; 48 | 49 | void (*_releaseFunction) (T*); 50 | 51 | public: 52 | 53 | ReleaseManager(void (*releaseFunction)(T*) = NULL); 54 | virtual ~ReleaseManager(); 55 | 56 | void add(T** var); 57 | void add(T* address); 58 | }; 59 | 60 | 61 | #include "release_manager.inline.h" 62 | 63 | NL_NAMESPACE_END 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/netlink/release_manager.inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | template 24 | ReleaseManager::ReleaseManager(void (*releaseFunction)(T*)): 25 | _releaseFunction(releaseFunction) {} 26 | 27 | template 28 | ReleaseManager::~ReleaseManager() { 29 | 30 | for(unsigned i=0; i < (unsigned) _releaseQueue.size(); ++i) 31 | 32 | if(*_releaseQueue.at(i)) { 33 | 34 | if(_releaseFunction) 35 | _releaseFunction(*_releaseQueue.at(i)); 36 | else 37 | delete *_releaseQueue.at(i); 38 | } 39 | 40 | for(unsigned i=0; i < (unsigned) _releaseAddressQueue.size(); ++i) 41 | 42 | if(_releaseAddressQueue.at(i)) { 43 | 44 | if(_releaseFunction) 45 | _releaseFunction(_releaseAddressQueue.at(i)); 46 | else 47 | delete _releaseAddressQueue.at(i); 48 | 49 | } 50 | } 51 | 52 | template 53 | void ReleaseManager::add(T** var) { 54 | 55 | _releaseQueue.push_back(var); 56 | } 57 | 58 | template 59 | void ReleaseManager::add(T* address) { 60 | 61 | _releaseAddressQueue.push_back(address); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /src/netlink/smart_buffer.cc: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | #include "smart_buffer.h" 24 | 25 | NL_NAMESPACE_USE 26 | 27 | ; 28 | 29 | /** 30 | * SmartBuffer Constructor 31 | * 32 | * @param allocSize Size in bytes of the initial reserved memory. 33 | * @param reallocRatio Growing ratio for memory reallocs. For example 1.5 means that 34 | * each time the buffer is out of memory it reserves the previous size 1.5 times (150%). 35 | * 36 | * @throw Exception ERROR_ALLOC 37 | */ 38 | 39 | SmartBuffer::SmartBuffer(size_t allocSize, double reallocRatio): _usedSize(0), 40 | _allocSize(allocSize), _reallocRatio(reallocRatio) 41 | { 42 | 43 | _buffer = malloc(allocSize); 44 | 45 | if(!_buffer) 46 | throw Exception(Exception::ERROR_ALLOC, "SmartBuffer::SmartBuffer: memory alloc error"); 47 | 48 | } 49 | 50 | /** 51 | * SmartBuffer Copy Constructor 52 | * 53 | * @param s SmartBuffer source of data. 54 | * 55 | * @throw Exception ERROR_ALLOC 56 | */ 57 | 58 | 59 | SmartBuffer::SmartBuffer(SmartBuffer& s) : _usedSize(s._usedSize), _allocSize(s._allocSize), _reallocRatio(s._reallocRatio) 60 | { 61 | _buffer = malloc(_allocSize); 62 | 63 | if(!_buffer) 64 | throw Exception(Exception::ERROR_ALLOC, "SmartBuffer::SmartBuffer: memory alloc error"); 65 | 66 | memcpy(_buffer, s._buffer, _usedSize); 67 | } 68 | 69 | 70 | /** 71 | * SmartBuffer Destructor 72 | */ 73 | 74 | SmartBuffer::~SmartBuffer() { 75 | 76 | if(_buffer != NULL) 77 | free(_buffer); 78 | } 79 | 80 | /** 81 | * Inserts the data read from a socket in the buffer 82 | * 83 | * @param socket Socket to be used as source 84 | * 85 | * @throw Exception ERROR_ALLOC, ERROR_READ* 86 | */ 87 | 88 | 89 | void SmartBuffer::read(Socket* socket) { 90 | 91 | size_t incomingBytes = socket->nextReadSize(); 92 | 93 | do { 94 | 95 | size_t freeBytes = _allocSize - _usedSize; 96 | 97 | if(incomingBytes > freeBytes || !freeBytes) { 98 | 99 | size_t newAllocSize = uMax((unsigned)(_allocSize * _reallocRatio), _usedSize + incomingBytes); 100 | 101 | void* newBuffer = realloc(_buffer, newAllocSize); 102 | 103 | if(!newBuffer) 104 | throw Exception(Exception::ERROR_ALLOC, "SmartBuffer::Read: memory alloc error"); 105 | 106 | _buffer = newBuffer; 107 | 108 | _allocSize = newAllocSize; 109 | 110 | freeBytes = _allocSize - _usedSize; 111 | } 112 | 113 | _usedSize += socket->read((char*)_buffer + _usedSize, freeBytes); 114 | 115 | } while ((incomingBytes = socket->nextReadSize())); 116 | 117 | } 118 | 119 | /** 120 | * Copy Operator. 121 | * 122 | * @param s SmartBuffer source of data. 123 | * 124 | * @throw Exception ERROR_ALLOC 125 | */ 126 | 127 | SmartBuffer& SmartBuffer::operator=(SmartBuffer& s) 128 | { 129 | _usedSize = s._usedSize; 130 | 131 | if(_allocSize < s._usedSize) 132 | { 133 | if(_buffer != NULL) 134 | free(_buffer); 135 | 136 | _allocSize = s._usedSize; 137 | 138 | _buffer = malloc(_allocSize); 139 | 140 | if(!_buffer) 141 | throw Exception(Exception::ERROR_ALLOC, "SmartBuffer::operator=: memory alloc error"); 142 | } 143 | 144 | memcpy(_buffer, s._buffer, s._usedSize); 145 | 146 | return *this; 147 | } 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/netlink/smart_buffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | #ifndef __NL_SMART_BUFFER 24 | #define __NL_SMART_BUFFER 25 | 26 | #include 27 | #include 28 | 29 | #include "core.h" 30 | #include "util.h" 31 | #include "socket.h" 32 | 33 | 34 | NL_NAMESPACE 35 | 36 | 37 | /** 38 | * @class SmartBuffer smart_buffer.h netlink/smart_buffer.h 39 | * 40 | * Smart Buffer Class 41 | * 42 | * Buffer class to retrieve data of unknown size easily from a Socket 43 | */ 44 | 45 | 46 | class SmartBuffer { 47 | 48 | private: 49 | 50 | void* _buffer; 51 | size_t _usedSize; 52 | size_t _allocSize; 53 | double _reallocRatio; 54 | 55 | public: 56 | 57 | SmartBuffer(size_t allocSize = DEFAULT_SMARTBUFFER_SIZE, double reallocRatio = DEFAULT_SMARTBUFFER_REALLOC_RATIO); 58 | SmartBuffer(SmartBuffer& s); 59 | ~SmartBuffer(); 60 | 61 | const void* operator*() const; 62 | const char* operator[](size_t index) const; 63 | 64 | const void* buffer() const; 65 | size_t size() const; 66 | 67 | void read(Socket* socket); 68 | 69 | void clear(); 70 | 71 | SmartBuffer& operator=(SmartBuffer& s); 72 | }; 73 | 74 | #include "smart_buffer.inline.h" 75 | 76 | NL_NAMESPACE_END 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /src/netlink/smart_buffer.inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | #ifdef DOXYGEN 24 | #include "smart_buffer.h" 25 | NL_NAMESPACE 26 | #endif 27 | 28 | 29 | /** 30 | * Returns the buffer data address. Same of buffer(). 31 | * 32 | * @return A pointer to data 33 | */ 34 | 35 | inline const void* SmartBuffer::operator*() const { 36 | 37 | return _buffer; 38 | } 39 | 40 | 41 | /** 42 | * Returns the byte in position index of the buffer 43 | * 44 | * @param index Position of the buffer we want to retrieve 45 | * @return Char with the content of the given position 46 | * 47 | * @throw Exception OUT_OF_RANGE 48 | */ 49 | 50 | inline const char* SmartBuffer::operator[](size_t index) const { 51 | 52 | if(index >= _usedSize) 53 | throw Exception(Exception::OUT_OF_RANGE, "SmartBuffer::operator[]: index out of range"); 54 | 55 | return (char*) _buffer + index; 56 | } 57 | 58 | /** 59 | * Returns the buffer data address. Same of operator*() 60 | * 61 | * @return A pointer to data 62 | */ 63 | 64 | inline const void* SmartBuffer::buffer() const { 65 | 66 | return _buffer; 67 | } 68 | 69 | /** 70 | * Returns the amount of bytes of data stored in the buffer 71 | * 72 | * @return Size of buffer data 73 | */ 74 | 75 | inline size_t SmartBuffer::size() const { 76 | 77 | return _usedSize; 78 | } 79 | 80 | /** 81 | * Clears the buffer. 82 | * 83 | * Resets the buffer erasing all its content. 84 | */ 85 | 86 | inline void SmartBuffer::clear() { 87 | 88 | _usedSize = 0; 89 | } 90 | 91 | #ifdef DOXYGEN 92 | NL_NAMESPACE_END 93 | #endif 94 | 95 | -------------------------------------------------------------------------------- /src/netlink/socket.cc: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | #include "socket.h" 24 | 25 | #include 26 | #include 27 | 28 | 29 | NL_NAMESPACE 30 | 31 | 32 | #ifdef OS_WIN32 33 | 34 | static void close(int socketHandler) { 35 | closesocket(socketHandler); 36 | } 37 | 38 | 39 | static void freeaddrinfo(struct addrinfo* addrInfo) { 40 | (void)::freeaddrinfo(addrInfo); 41 | } 42 | 43 | 44 | static const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) 45 | { 46 | if (af == AF_INET) 47 | { 48 | struct sockaddr_in in; 49 | memset(&in, 0, sizeof(in)); 50 | in.sin_family = AF_INET; 51 | memcpy(&in.sin_addr, src, sizeof(struct in_addr)); 52 | getnameinfo((struct sockaddr *)&in, sizeof(struct 53 | sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); 54 | return dst; 55 | } 56 | else if (af == AF_INET6) 57 | { 58 | struct sockaddr_in6 in; 59 | memset(&in, 0, sizeof(in)); 60 | in.sin6_family = AF_INET6; 61 | memcpy(&in.sin6_addr, src, sizeof(struct in_addr6)); 62 | getnameinfo((struct sockaddr *)&in, sizeof(struct 63 | sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); 64 | return dst; 65 | } 66 | return NULL; 67 | } 68 | 69 | #endif 70 | 71 | 72 | static unsigned getInPort(struct sockaddr* sa) { 73 | 74 | if (sa->sa_family == AF_INET) 75 | return ntohs(((struct sockaddr_in*)sa)->sin_port); 76 | 77 | return ntohs(((struct sockaddr_in6*)sa)->sin6_port); 78 | } 79 | 80 | 81 | static int getSocketErrorCode() { 82 | 83 | #ifdef OS_WIN32 84 | return WSAGetLastError(); 85 | #else 86 | return errno; 87 | #endif 88 | } 89 | 90 | 91 | static unsigned getLocalPort(int socketHandler) { 92 | 93 | struct sockaddr_storage sin; 94 | 95 | #ifdef OS_WIN32 96 | int size; 97 | #else 98 | socklen_t size; 99 | #endif 100 | 101 | size = sizeof(sin); 102 | if(getsockname(socketHandler, (struct sockaddr*)&sin, &size) == 0) 103 | return getInPort((struct sockaddr*)&sin); 104 | else 105 | throw Exception(Exception::ERROR_GET_ADDR_INFO, "Socket::(static)getLocalPort: error getting socket info", getSocketErrorCode()); 106 | } 107 | 108 | 109 | static void checkReadError(const string& functionName) { 110 | 111 | #ifdef OS_WIN32 112 | if(WSAGetLastError() != WSAEWOULDBLOCK) 113 | throw Exception(Exception::ERROR_READ, string("Socket::") + functionName + ": error detected", getSocketErrorCode()); 114 | #else 115 | if(errno != EAGAIN && errno != EWOULDBLOCK) 116 | throw Exception(Exception::ERROR_READ, string("Socket::") + functionName + ": error detected", getSocketErrorCode()); 117 | #endif 118 | } 119 | 120 | 121 | void Socket::initSocket() { 122 | 123 | struct addrinfo conf, *res = NULL; 124 | memset(&conf, 0, sizeof(conf)); 125 | 126 | if(_type == SERVER || _protocol == UDP) 127 | conf.ai_flags = AI_PASSIVE; 128 | 129 | 130 | switch(_protocol) { 131 | 132 | case TCP: 133 | conf.ai_socktype = SOCK_STREAM; 134 | break; 135 | 136 | case UDP: 137 | conf.ai_socktype = SOCK_DGRAM; 138 | break; 139 | 140 | default: 141 | throw Exception(Exception::BAD_PROTOCOL, "Socket::initSocket: bad protocol"); 142 | } 143 | 144 | switch(_ipVer) { 145 | 146 | case IP4: 147 | conf.ai_family = AF_INET; 148 | break; 149 | 150 | case IP6: 151 | conf.ai_family = AF_INET6; 152 | break; 153 | 154 | case ANY: 155 | conf.ai_family = AF_UNSPEC; 156 | break; 157 | 158 | default: 159 | throw Exception(Exception::BAD_IP_VER, "Socket::initSocket: bad ip version parameter"); 160 | } 161 | 162 | 163 | char portStr[10]; 164 | 165 | const char* host; 166 | 167 | if(_type == CLIENT && _protocol == TCP) { 168 | host = _hostTo.c_str(); 169 | snprintf(portStr, 10, "%u", _portTo); 170 | } 171 | else { 172 | if(!_hostFrom.compare("") || !_hostFrom.compare("*")) 173 | host = NULL; 174 | else 175 | host = _hostFrom.c_str(); 176 | 177 | snprintf(portStr, 10, "%u", _portFrom); 178 | } 179 | 180 | int status = getaddrinfo(host, portStr, &conf, &res); 181 | 182 | ReleaseManager addrInfoReleaser(freeaddrinfo); 183 | addrInfoReleaser.add(res); 184 | 185 | if(status != 0) { 186 | 187 | string errorMsg = "Socket::initSocket: Error setting addrInfo: "; 188 | 189 | #ifndef _MSC_VER 190 | errorMsg += gai_strerror(status); 191 | #endif 192 | 193 | throw Exception(Exception::ERROR_SET_ADDR_INFO, errorMsg, getSocketErrorCode()); 194 | } 195 | 196 | bool connected = false; 197 | 198 | while(!connected && res) { 199 | 200 | _socketHandler = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 201 | 202 | if(_socketHandler != -1) 203 | 204 | switch(_type) { 205 | 206 | case CLIENT: 207 | if(_protocol == UDP) { 208 | 209 | if (bind(_socketHandler, res->ai_addr, res->ai_addrlen) == -1) 210 | close(_socketHandler); 211 | else 212 | connected = true; 213 | } 214 | else { 215 | status = connect(_socketHandler, res->ai_addr, res->ai_addrlen); 216 | if(status != -1) 217 | connected = true; 218 | else 219 | close(_socketHandler); 220 | } 221 | 222 | break; 223 | 224 | case SERVER: 225 | #ifdef OS_WIN32 226 | char yes = 1; 227 | #else 228 | int yes = 1; 229 | #endif 230 | 231 | if (setsockopt(_socketHandler, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) 232 | throw Exception(Exception::ERROR_SET_SOCK_OPT, "Socket::initSocket: Error establishing socket options"); 233 | 234 | if (bind(_socketHandler, res->ai_addr, res->ai_addrlen) == -1) 235 | close(_socketHandler); 236 | else 237 | connected = true; 238 | 239 | if (_protocol == TCP && listen(_socketHandler, _listenQueue) == -1) 240 | throw Exception(Exception::ERROR_CAN_NOT_LISTEN, "Socket::initSocket: could not start listening", getSocketErrorCode()); 241 | 242 | break; 243 | 244 | } 245 | 246 | if(connected && _ipVer == ANY) 247 | switch(res->ai_family) { 248 | case AF_INET: 249 | _ipVer = IP4; 250 | break; 251 | 252 | case AF_INET6: 253 | _ipVer = IP6; 254 | break; 255 | } 256 | 257 | res = res->ai_next; 258 | 259 | } 260 | 261 | if(!connected) 262 | throw Exception(Exception::ERROR_CONNECT_SOCKET, "Socket::initSocket: error in socket connection/bind", getSocketErrorCode()); 263 | 264 | 265 | if(!_portFrom) 266 | _portFrom = getLocalPort(_socketHandler); 267 | 268 | //freeaddrinfo(res); 269 | } 270 | 271 | /** 272 | * CLIENT Socket constructor 273 | * 274 | * Creates a socket, connect it (if TCP) and sets it ready to send to hostTo:portTo. 275 | * The local port of the socket is choosen by OS. 276 | * 277 | * @param hostTo the target/remote host 278 | * @param portTo the target/remote port 279 | * @param protocol the protocol to be used (TCP or UDP). TCP by default. 280 | * @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default. 281 | * @throw Exception BAD_PROTOCOL, BAD_IP_VER, ERROR_SET_ADDR_INFO*, ERROR_CONNECT_SOCKET*, 282 | * ERROR_GET_ADDR_INFO* 283 | */ 284 | 285 | 286 | Socket::Socket(const string& hostTo, unsigned portTo, Protocol protocol, IPVer ipVer) : 287 | _hostTo(hostTo), _portTo(portTo), _portFrom(0), _protocol(protocol), 288 | _ipVer(ipVer), _type(CLIENT), _blocking(true), _listenQueue(0) 289 | { 290 | initSocket(); 291 | } 292 | 293 | 294 | /** 295 | * SERVER Socket constructor 296 | * 297 | * Creates a socket, binds it to portFrom port and listens for connections (if TCP). 298 | * 299 | * @param portFrom the local port the socket will be bound to 300 | * @param protocol the protocol to be used (TCP or UDP). TCP by default. 301 | * @param ipVer the IP version to be used (IP4, IP6 or ANY). IP4 by default. 302 | * @param hostFrom the local address to be binded to (example: "localhost" or "127.0.0.1"). Empty (by default) or "*" means all avariable addresses. 303 | * @param listenQueue the size of the internal buffer of the SERVER TCP socket where the connection requests are stored until accepted 304 | * @throw Exception BAD_PROTOCOL, BAD_IP_VER, ERROR_SET_ADDR_INFO*, ERROR_SET_SOCK_OPT*, 305 | * ERROR_CAN_NOT_LISTEN*, ERROR_CONNECT_SOCKET* 306 | */ 307 | 308 | Socket::Socket(unsigned portFrom, Protocol protocol, IPVer ipVer, const string& hostFrom, unsigned listenQueue): 309 | _hostFrom(hostFrom), _portTo(0), _portFrom(portFrom), _protocol(protocol), 310 | _ipVer(ipVer), _type(SERVER), _blocking(true), _listenQueue(listenQueue) 311 | { 312 | initSocket(); 313 | } 314 | 315 | /** 316 | * UDP CLIENT Socket Constructor 317 | * 318 | * This client constructor for UDP Sockets allows to expecify the local port the socket 319 | * will be bound to. It sets the socket ready to send data to hostTo:portTo. 320 | * 321 | * @param hostTo the target/remote host 322 | * @param portTo the target/remote port 323 | * @param portFrom the local port the socket will be bound to 324 | * @param ipVer the IP version to be used (IP4, IP6 or ANY). ANY by default. 325 | * @throw Exception BAD_PROTOCOL, BAD_IP_VER, ERROR_SET_ADDR_INFO*, ERROR_SET_SOCK_OPT*, 326 | * ERROR_CAN_NOT_LISTEN*, ERROR_CONNECT_SOCKET* 327 | */ 328 | 329 | Socket::Socket(const string& hostTo, unsigned portTo, unsigned portFrom, IPVer ipVer): 330 | _hostTo(hostTo), _portTo(portTo), _portFrom(portFrom), _protocol(UDP), 331 | _ipVer(ipVer), _type(CLIENT), _blocking(true), _listenQueue(0) 332 | { 333 | 334 | initSocket(); 335 | } 336 | 337 | 338 | Socket::Socket() : _blocking(true), _socketHandler(-1) {}; 339 | 340 | 341 | /** 342 | * Socket Destructor 343 | * 344 | * Closes (disconnects) the socket 345 | */ 346 | 347 | Socket::~Socket() { 348 | 349 | if(_socketHandler != -1) 350 | close(_socketHandler); 351 | 352 | } 353 | 354 | 355 | // get sockaddr, IPv4 or IPv6: 356 | // This function is from Brian “Beej Jorgensen” Hall: Beej's Guide to Network Programming. 357 | static void *get_in_addr(struct sockaddr *sa) 358 | { 359 | if (sa->sa_family == AF_INET) { 360 | return &(((struct sockaddr_in*)sa)->sin_addr); 361 | } 362 | return &(((struct sockaddr_in6*)sa)->sin6_addr); 363 | } 364 | 365 | 366 | /** 367 | * Accepts a new incoming connection (SERVER Socket). 368 | * 369 | * Creates a new CLIENT socket to handle the communication (send/recieve data) of 370 | * this accepted connection. Requires the socket to be a SERVER TCP socket. Throws an 371 | * exception otherwise. 372 | * 373 | * @pre Socket must be SERVER 374 | * @return A CLIENT socket that handles the new connection 375 | * @throw Exception EXPECTED_TCP_SOCKET, EXPECTED_SERVER_SOCKET 376 | */ 377 | 378 | Socket* Socket::accept() { 379 | 380 | if(_protocol != TCP) 381 | throw Exception(Exception::EXPECTED_TCP_SOCKET, "Socket::accept: non-tcp socket can not accept connections"); 382 | 383 | if(_type != SERVER) 384 | throw Exception(Exception::EXPECTED_SERVER_SOCKET, "Socket::accept: non-server socket can not accept connections"); 385 | 386 | struct sockaddr_storage incoming_addr; 387 | 388 | #ifdef OS_WIN32 389 | int addrSize = sizeof(incoming_addr); 390 | #else 391 | unsigned addrSize = sizeof(incoming_addr); 392 | #endif 393 | 394 | int new_handler = ::accept(_socketHandler, (struct sockaddr *)&incoming_addr, &addrSize); 395 | 396 | if(new_handler == -1) 397 | return NULL; 398 | 399 | char hostChar[INET6_ADDRSTRLEN]; 400 | inet_ntop(incoming_addr.ss_family, get_in_addr((struct sockaddr *)&incoming_addr), hostChar, sizeof hostChar); 401 | 402 | int localPort = getLocalPort(new_handler); 403 | 404 | Socket* acceptSocket = new Socket(); 405 | acceptSocket->_socketHandler = new_handler; 406 | acceptSocket->_hostTo = hostChar; 407 | acceptSocket->_portTo = getInPort((struct sockaddr *)&incoming_addr); 408 | acceptSocket->_portFrom = localPort; 409 | 410 | acceptSocket->_protocol = _protocol; 411 | acceptSocket->_ipVer = _ipVer; 412 | acceptSocket->_type = CLIENT; 413 | acceptSocket->_listenQueue = 0; 414 | acceptSocket->blocking(_blocking); 415 | 416 | return acceptSocket; 417 | } 418 | 419 | 420 | /** 421 | * Sends data to an expecific host:port 422 | * 423 | * Sends the data contained in buffer to a given host:port. Requires the socket to be an UDP socket, 424 | * throws an exception otherwise. 425 | * 426 | * @pre Socket must be UDP 427 | * @param buffer A pointer to the data we want to send 428 | * @param size Size of the data to send (bytes) 429 | * @param hostTo Target/remote host 430 | * @param portTo Target/remote port 431 | * @throw Exception EXPECTED_UDP_SOCKET, BAD_IP_VER, ERROR_SET_ADDR_INFO*, ERROR_SEND* 432 | */ 433 | 434 | 435 | void Socket::sendTo(const void* buffer, size_t size, const string& hostTo, unsigned portTo) { 436 | 437 | if(_protocol != UDP) 438 | throw Exception(Exception::EXPECTED_UDP_SOCKET, "Socket::sendTo: non-UDP socket can not 'sendTo'"); 439 | 440 | struct addrinfo conf, *res; 441 | memset(&conf, 0, sizeof(conf)); 442 | 443 | conf.ai_socktype = SOCK_DGRAM; 444 | 445 | switch(_ipVer) { 446 | 447 | case IP4: 448 | conf.ai_family = AF_INET; 449 | break; 450 | 451 | case IP6: 452 | conf.ai_family = AF_INET6; 453 | break; 454 | 455 | default: 456 | throw Exception(Exception::BAD_IP_VER, "Socket::sendTo: bad ip version."); 457 | } 458 | 459 | char portStr[10]; 460 | snprintf(portStr, 10, "%u", portTo); 461 | 462 | int status = getaddrinfo(hostTo.c_str(), portStr, &conf, &res); 463 | 464 | ReleaseManager addrInfoReleaser(freeaddrinfo); 465 | addrInfoReleaser.add(&res); 466 | 467 | 468 | if(status != 0) { 469 | string errorMsg = "Socket::sendTo: error setting addrInfo: "; 470 | #ifndef _MSC_VER 471 | errorMsg += gai_strerror(status); 472 | #endif 473 | throw Exception(Exception::ERROR_SET_ADDR_INFO, "Socket::sendTo: error setting addr info", getSocketErrorCode()); 474 | } 475 | 476 | size_t sentBytes = 0; 477 | 478 | while(sentBytes < size) { 479 | 480 | int status = ::sendto(_socketHandler, (const char*)buffer + sentBytes, size - sentBytes, 0, res->ai_addr, res->ai_addrlen); 481 | 482 | if(status == -1) 483 | throw Exception(Exception::ERROR_SEND, "Socket::sendTo: could not send the data", getSocketErrorCode()); 484 | 485 | sentBytes += status; 486 | } 487 | } 488 | 489 | 490 | 491 | 492 | /** 493 | * Receive data and get the source host and port 494 | * 495 | * Requires the socket to be UDP. Source host address and port are returned in hostFrom and 496 | * portFrom parameters. Data recieved is written in buffer address up to bufferSize. 497 | * 498 | * @pre Socket must be UDP 499 | * @param buffer Pointer to a buffer where received data will be stored 500 | * @param bufferSize Size of the buffer 501 | * @param[out] hostFrom Here the function will store the address of the remote host 502 | * @param[out] portFrom Here the function will store the remote port 503 | * @return the length of the data recieved 504 | * @throw Exception EXPECTED_UDP_SOCKET, ERROR_READ* 505 | */ 506 | 507 | 508 | int Socket::readFrom(void* buffer, size_t bufferSize, string* hostFrom, unsigned* portFrom) { 509 | 510 | if(_protocol != UDP) 511 | throw Exception(Exception::EXPECTED_UDP_SOCKET, "Socket::readFrom: non-UDP socket can not 'readFrom'"); 512 | 513 | struct sockaddr_storage addr; 514 | socklen_t addrSize = sizeof(addr); 515 | int status = recvfrom(_socketHandler, (char*)buffer, bufferSize, 0, (struct sockaddr *)&addr, &addrSize); 516 | 517 | if(status == -1) { 518 | checkReadError("readFrom"); 519 | if(hostFrom) 520 | *hostFrom = ""; 521 | if(portFrom) 522 | *portFrom = 0; 523 | } 524 | 525 | else { 526 | 527 | if(portFrom) 528 | *portFrom = getInPort((struct sockaddr*)&addr); 529 | 530 | if(hostFrom) { 531 | char hostChar[INET6_ADDRSTRLEN]; 532 | inet_ntop(addr.ss_family, get_in_addr((struct sockaddr *)&addr), hostChar, sizeof hostChar); 533 | 534 | *hostFrom = hostChar; 535 | } 536 | } 537 | 538 | return status; 539 | } 540 | 541 | 542 | /** 543 | * Sends data 544 | * 545 | * Sends the data contained in buffer. Requires the Socket to be a CLIENT socket. 546 | * 547 | * @pre Socket must be CLIENT 548 | * @param buffer A pointer to the data we want to send 549 | * @param size Length of the data to be sent (bytes) 550 | * @throw Exception EXPECTED_CLIENT_SOCKET, ERROR_SEND* 551 | */ 552 | 553 | 554 | void Socket::send(const void* buffer, size_t size) { 555 | 556 | if(_type != CLIENT) 557 | throw Exception(Exception::EXPECTED_CLIENT_SOCKET, "Socket::send: Expected client socket (socket with host and port target)"); 558 | 559 | if(_protocol == UDP) 560 | return sendTo(buffer, size, _hostTo, _portTo); 561 | 562 | size_t sentData = 0; 563 | 564 | while (sentData < size) { 565 | 566 | int status = ::send(_socketHandler, (const char*)buffer + sentData, size - sentData, 0); 567 | 568 | if(status == -1) 569 | throw Exception(Exception::ERROR_SEND, "Error sending data", getSocketErrorCode()); 570 | 571 | sentData += status; 572 | } 573 | } 574 | 575 | /** 576 | * Receives data 577 | * 578 | * Receives data and stores it in buffer until bufferSize reached. 579 | * 580 | * @param buffer A pointer to a buffer where received data will be stored 581 | * @param bufferSize Size of the buffer 582 | * @return Size of received data or (-1) if Socket is non-blocking and there's no data received. 583 | * @throw Exception ERROR_READ* 584 | */ 585 | 586 | 587 | int Socket::read(void* buffer, size_t bufferSize) { 588 | 589 | int status = recv(_socketHandler, (char*)buffer, bufferSize, 0); 590 | 591 | if(status == -1) 592 | checkReadError("read"); 593 | 594 | return status; 595 | } 596 | 597 | 598 | /** 599 | * Get next read() data size 600 | * 601 | * Get the size of the data (bytes) a call to read() or readFrom() can process 602 | * 603 | * @return size of data the next call to read/readFrom will receive 604 | * @throw Exception ERROR_IOCTL* 605 | */ 606 | 607 | 608 | int Socket::nextReadSize() const { 609 | 610 | #ifdef OS_WIN32 611 | u_long result = -1; 612 | #else 613 | long int result = -1; 614 | #endif 615 | 616 | int status; 617 | 618 | #ifdef OS_WIN32 619 | status = ioctlsocket(_socketHandler, FIONREAD, &result); 620 | #else 621 | status = ioctl(_socketHandler, FIONREAD, &result); 622 | #endif 623 | 624 | if(status) 625 | throw Exception(Exception::ERROR_IOCTL, "Socket::nextReadSize: error ioctl", getSocketErrorCode()); 626 | 627 | return result; 628 | } 629 | 630 | 631 | /** 632 | * Sets the blocking nature of the Socket 633 | * 634 | * Sets the Socket as blocking (if blocking is true) or as non-blocking (otherwise) 635 | * 636 | * @param blocking true to set the Socket as blocking; false to set the Socket as non-blocking 637 | * @throw Exception ERROR_IOCTL* 638 | */ 639 | 640 | void Socket::blocking(bool blocking) { 641 | 642 | _blocking = blocking; 643 | 644 | int result = -1; 645 | 646 | #ifdef OS_WIN32 647 | 648 | u_long non_blocking = !blocking; 649 | result = ioctlsocket(_socketHandler, FIONBIO, &non_blocking); 650 | if(result!=0) 651 | result = -1; 652 | #else 653 | 654 | int flags = fcntl(_socketHandler, F_GETFL); 655 | 656 | if(blocking) 657 | result = fcntl(_socketHandler, F_SETFL, flags & ~O_NONBLOCK); 658 | else 659 | result = fcntl(_socketHandler, F_SETFL, flags | O_NONBLOCK); 660 | #endif 661 | 662 | if (result == -1) 663 | throw Exception(Exception::ERROR_IOCTL, "Socket::blocking: ioctl error", getSocketErrorCode()); 664 | } 665 | 666 | 667 | /** 668 | * Closes (disconnects) the socket. After this call the socket can not be used. 669 | * 670 | * @warning Any use of the Socket after disconnection leads to undefined behaviour. 671 | */ 672 | 673 | 674 | void Socket::disconnect() { 675 | 676 | close(_socketHandler); 677 | 678 | _socketHandler = -1; 679 | 680 | } 681 | 682 | /** 683 | * @include socket.inline.h 684 | */ 685 | 686 | 687 | NL_NAMESPACE_END 688 | -------------------------------------------------------------------------------- /src/netlink/socket.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_SOCKET 23 | #define __NL_SOCKET 24 | 25 | #include "core.h" 26 | 27 | 28 | NL_NAMESPACE 29 | 30 | /** 31 | * @class Socket socket.h netlink/socket.h 32 | * 33 | * Socket class 34 | * 35 | * @note The Exceptions with asterisk(*) includes the native error code (Exception::nativeErrorCode()) when thrown 36 | * @warning Remember to call init() before using Socket 37 | */ 38 | 39 | class Socket { 40 | 41 | private: 42 | 43 | string _hostTo; 44 | string _hostFrom; 45 | unsigned _portTo; 46 | unsigned _portFrom; 47 | Protocol _protocol; 48 | IPVer _ipVer; 49 | SocketType _type; 50 | bool _blocking; 51 | unsigned _listenQueue; 52 | 53 | int _socketHandler; 54 | 55 | 56 | public: 57 | 58 | Socket(const string& hostTo, unsigned portTo, Protocol protocol = TCP, IPVer ipVer = ANY); 59 | 60 | Socket(unsigned portFrom, Protocol protocol = TCP, IPVer ipVer = IP4, const string& hostFrom = "", unsigned listenQueue = DEFAULT_LISTEN_QUEUE); 61 | 62 | Socket(const string& hostTo, unsigned portTo, unsigned portFrom, IPVer ipVer = ANY); 63 | 64 | ~Socket(); 65 | 66 | 67 | Socket* accept(); 68 | 69 | int read(void* buffer, size_t bufferSize); 70 | void send(const void* buffer, size_t size); 71 | 72 | int readFrom(void* buffer, size_t bufferSize, string* HostFrom, unsigned* portFrom = NULL); 73 | void sendTo(const void* buffer, size_t size, const string& hostTo, unsigned portTo); 74 | 75 | int nextReadSize() const; 76 | 77 | void disconnect(); 78 | 79 | const string& hostTo() const; 80 | const string& hostFrom() const; 81 | unsigned portTo() const; 82 | unsigned portFrom() const; 83 | Protocol protocol() const; 84 | IPVer ipVer() const; 85 | SocketType type() const; 86 | bool blocking() const; 87 | unsigned listenQueue() const; 88 | int socketHandler() const; 89 | 90 | 91 | void blocking(bool blocking); 92 | 93 | 94 | private: 95 | 96 | void initSocket(); 97 | Socket(); 98 | 99 | }; 100 | 101 | #include "socket.inline.h" 102 | 103 | NL_NAMESPACE_END 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /src/netlink/socket.inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifdef DOXYGEN 23 | #include "socket.h" 24 | NL_NAMESPACE 25 | #endif 26 | 27 | /** 28 | * Returns the target host of the socket 29 | * 30 | * @return the host this socket is connected to (in TCP 31 | * case) or the host it sends data to (UDP case) 32 | */ 33 | 34 | inline const string& Socket::hostTo() const { 35 | 36 | return _hostTo; 37 | } 38 | 39 | /** 40 | * Returns the socket local address 41 | * 42 | * @return socket local address 43 | */ 44 | 45 | inline const string& Socket::hostFrom() const { 46 | 47 | return _hostFrom; 48 | } 49 | 50 | /** 51 | * Returns the port this socket is connected/sends to 52 | * 53 | * @return target/remote port 54 | */ 55 | 56 | inline unsigned Socket::portTo() const { 57 | 58 | return _portTo; 59 | } 60 | 61 | /** 62 | * Returns the local port of the socket 63 | * 64 | * @return local port 65 | */ 66 | 67 | inline unsigned Socket::portFrom() const { 68 | 69 | return _portFrom; 70 | } 71 | 72 | /** 73 | * Returns the socket protocol 74 | * @return Protocol 75 | */ 76 | 77 | inline Protocol Socket::protocol() const { 78 | 79 | return _protocol; 80 | } 81 | 82 | /** 83 | * Returns the socket IP version 84 | * @return IPVer 85 | */ 86 | 87 | inline IPVer Socket::ipVer() const { 88 | 89 | return _ipVer; 90 | } 91 | 92 | /** 93 | * Returns the socket type 94 | * @return SocketType (SERVER or CLIENT) 95 | */ 96 | 97 | inline SocketType Socket::type() const { 98 | 99 | return _type; 100 | } 101 | 102 | /** 103 | * Returns the size of the listen queue. 104 | * 105 | * @pre The Socket must be SERVER, otherwise this call has no sense and returns 0 106 | * @return the size of the internal buffer of the SERVER TCP socket where the connection 107 | * requests are stored until accepted 108 | */ 109 | 110 | inline unsigned Socket::listenQueue() const { 111 | 112 | return _listenQueue; 113 | } 114 | 115 | /** 116 | * Returns whether the socket is blocking (true) or not (false) 117 | * 118 | * @return socket blocking status 119 | */ 120 | 121 | inline bool Socket::blocking() const { 122 | 123 | return _blocking; 124 | } 125 | 126 | 127 | /** 128 | * Returns the socket handler (file/socket descriptor) 129 | * 130 | * @return File descritor to handle the socket 131 | */ 132 | 133 | 134 | inline int Socket::socketHandler() const { 135 | 136 | return _socketHandler; 137 | } 138 | 139 | #ifdef DOXYGEN 140 | NL_NAMESPACE_END 141 | #endif 142 | 143 | 144 | -------------------------------------------------------------------------------- /src/netlink/socket_group.cc: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #include "socket_group.h" 23 | 24 | NL_NAMESPACE_USE 25 | 26 | 27 | ; // <-- this is for doxygen not to get confused by NL_NAMESPACE_USE 28 | /** 29 | * SocketGroup constructor 30 | */ 31 | 32 | SocketGroup::SocketGroup(): _cmdOnAccept(NULL), _cmdOnRead(NULL), _cmdOnDisconnect(NULL) {} 33 | 34 | 35 | /** 36 | * Removes a socket of the group 37 | * 38 | * @param socket The socket we want to be removed 39 | * @warning This removes only one reference to the socket: if the socket was added to the group more 40 | * than once (you shouldn't) there will be references left 41 | */ 42 | 43 | void SocketGroup::remove(Socket* socket) { 44 | 45 | vector::iterator it = _vSocket.begin(); 46 | 47 | while(it != _vSocket.end()) 48 | if(*it == socket) { 49 | _vSocket.erase(it); 50 | return; 51 | } 52 | else 53 | ++it; 54 | } 55 | 56 | 57 | /** 58 | * Listens for incoming data/connections 59 | * 60 | * Listens during milisecs time for incoming data/connections in any socket of the group calling 61 | * the appropriate callback (Accept, Read or Disconnect) if assigned. 62 | * 63 | * @note UDP sockets only uses Read callback as they don not establish connections (can not accept) nor 64 | * they register disconnections 65 | * 66 | * @param milisec minimum time spent listening. By defaul 0 67 | * @param reference A pointer which can be passed to the callback functions so they have a context. 68 | * By default NULL 69 | * @return false if there were no incoming data, true otherwise 70 | * @throw Exception ERROR_SELECT 71 | */ 72 | 73 | 74 | bool SocketGroup::listen(unsigned milisec, void* reference) { 75 | 76 | unsigned long long finTime = getTime() + milisec; 77 | bool executedOnce = false; 78 | bool result = false; 79 | 80 | while(getTime() < finTime || !executedOnce) { 81 | 82 | executedOnce = true; 83 | 84 | fd_set setSockets; 85 | int maxHandle = 0; 86 | 87 | FD_ZERO(&setSockets); 88 | 89 | for(unsigned i=0; i < _vSocket.size(); i++) { 90 | FD_SET(_vSocket[i]->socketHandler(), &setSockets); 91 | maxHandle = iMax(maxHandle, _vSocket[i]->socketHandler()); 92 | } 93 | 94 | unsigned long long milisecLeft = finTime - getTime(); 95 | struct timeval timeout; 96 | 97 | timeout.tv_sec = milisecLeft / 1000; 98 | timeout.tv_usec = (milisecLeft % 1000) * 1000; 99 | 100 | int status = select(maxHandle + 1, &setSockets, NULL, NULL, &timeout); 101 | 102 | if (status == -1) 103 | throw Exception(Exception::ERROR_SELECT, "SocketGroup::listen: could not perform socket select"); 104 | 105 | unsigned i=0; 106 | int launchSockets = 0; 107 | 108 | while(launchSockets < status && i < _vSocket.size()) { 109 | 110 | if(FD_ISSET(_vSocket[i]->socketHandler(), &setSockets)) { 111 | 112 | launchSockets++; 113 | 114 | if(_vSocket[i]->type() == SERVER && _vSocket[i]->protocol() == TCP) { 115 | if(_cmdOnAccept) 116 | _cmdOnAccept->exec(_vSocket[i], this, reference); 117 | } //if 118 | else { 119 | if(_vSocket[i]->protocol() == TCP && !_vSocket[i]->nextReadSize()) { 120 | if(_cmdOnDisconnect) 121 | _cmdOnDisconnect->exec(_vSocket[i], this, reference); 122 | } 123 | 124 | else if(_cmdOnRead) 125 | _cmdOnRead->exec(_vSocket[i], this, reference); 126 | } 127 | 128 | } 129 | 130 | ++i; 131 | 132 | } //while 133 | 134 | if(launchSockets) 135 | result = true; 136 | 137 | } //while 138 | 139 | return result; 140 | 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/netlink/socket_group.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_SOCKET_GROUP 23 | #define __NL_SOCKET_GROUP 24 | 25 | #include "core.h" 26 | #include "socket.h" 27 | 28 | NL_NAMESPACE 29 | 30 | using std::vector; 31 | 32 | 33 | class SocketGroup; 34 | 35 | /** 36 | * @class SocketGroupCmd socket_group.h netlink/socket_group.h 37 | * 38 | * Class to be used as base for SocketGroup callback function implementing classes 39 | */ 40 | 41 | class SocketGroupCmd { 42 | 43 | public: 44 | 45 | /** 46 | * Function to be implemented for the callback. The parameters will be provided by the 47 | * SocketGroup::listen() function when calling 48 | * 49 | * @param socket Socket which triggered the callback 50 | * @param group SocketGroup which triggered the callback 51 | * @param reference Pointer passed to listen function to be used here 52 | */ 53 | 54 | virtual void exec(Socket* socket, SocketGroup* group, void* reference)=0; 55 | }; 56 | 57 | 58 | /** 59 | * @class SocketGroup socket_group.h netlink/socket_group.h 60 | * 61 | * To manage sockets and connections 62 | */ 63 | 64 | class SocketGroup { 65 | 66 | private: 67 | 68 | vector _vSocket; 69 | 70 | SocketGroupCmd* _cmdOnAccept; 71 | SocketGroupCmd* _cmdOnRead; 72 | SocketGroupCmd* _cmdOnDisconnect; 73 | 74 | public: 75 | 76 | SocketGroup(); 77 | 78 | void add(Socket* socket); 79 | Socket* get(unsigned index) const; 80 | void remove(unsigned index); 81 | void remove(Socket* socket); 82 | 83 | size_t size() const; 84 | 85 | void setCmdOnAccept(SocketGroupCmd* cmd); 86 | void setCmdOnRead(SocketGroupCmd* cmd); 87 | void setCmdOnDisconnect(SocketGroupCmd* cmd); 88 | 89 | bool listen(unsigned milisec=0, void* reference = NULL); 90 | }; 91 | 92 | #include "socket_group.inline.h" 93 | 94 | NL_NAMESPACE_END 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /src/netlink/socket_group.inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifdef DOXYGEN 23 | #include "socket_group.h" 24 | NL_NAMESPACE 25 | #endif 26 | 27 | 28 | /** 29 | * Adds the Socket to the SocketGroup 30 | * 31 | * @param socket Socket to be added 32 | */ 33 | 34 | 35 | inline void SocketGroup::add(Socket* socket) { 36 | 37 | _vSocket.push_back(socket); 38 | } 39 | 40 | /** 41 | * Gets the pointer to the Socket of position index in the SocketGroup 42 | * 43 | * @param index Socket position 44 | * @return A pointer to the Socket 45 | * 46 | * @throw Exception OUT_OF_RANGE 47 | */ 48 | 49 | inline Socket* SocketGroup::get(unsigned index) const { 50 | 51 | if(index >= _vSocket.size()) 52 | throw Exception(Exception::OUT_OF_RANGE, "SocketGroup::get: index out of range"); 53 | 54 | return _vSocket[index]; 55 | } 56 | 57 | /** 58 | * Removes from the group the Socket of position index 59 | * 60 | * @param index Socket position 61 | * 62 | * @throw Exception OUT_OF_RANGE 63 | */ 64 | 65 | inline void SocketGroup::remove(unsigned index) { 66 | 67 | if(index >= _vSocket.size()) 68 | throw Exception(Exception::OUT_OF_RANGE, "SocketGroup::remove: index out of range"); 69 | 70 | _vSocket.erase(_vSocket.begin() + index); 71 | } 72 | 73 | /** 74 | * Returns the size of the group 75 | * 76 | * @return The number of Sockets contained in the SocketGroup 77 | */ 78 | 79 | inline size_t SocketGroup::size() const { 80 | 81 | return _vSocket.size(); 82 | } 83 | 84 | /** 85 | * Sets the onAcceptReady callback 86 | * 87 | * This callback will be call for any incoming connection waiting to be accepted 88 | * @param cmd SocketGroupCmd implementing the desired callback in exec() function 89 | * @warning Only used for TCP protocol SERVER sockets 90 | */ 91 | 92 | inline void SocketGroup::setCmdOnAccept(SocketGroupCmd* cmd) { 93 | 94 | _cmdOnAccept = cmd; 95 | } 96 | 97 | /** 98 | * Sets the onReadReady callback 99 | * 100 | * This callback will be call for any incoming data waiting to be read 101 | * @param cmd SocketGroupCmd implementing the desired callback in exec() function 102 | */ 103 | 104 | 105 | inline void SocketGroup::setCmdOnRead(SocketGroupCmd* cmd) { 106 | 107 | _cmdOnRead = cmd; 108 | } 109 | 110 | /** 111 | * Sets the onDisconnect callback 112 | * 113 | * This callback will be call for any disconnection detected 114 | * @param cmd SocketGroupCmd implementing the desired callback in exec() function 115 | * @warning Only used for TCP protocol CLIENT sockets 116 | */ 117 | 118 | 119 | inline void SocketGroup::setCmdOnDisconnect(SocketGroupCmd* cmd) { 120 | 121 | _cmdOnDisconnect = cmd; 122 | } 123 | 124 | #ifdef DOXYGEN 125 | NL_NAMESPACE_END 126 | #endif 127 | 128 | -------------------------------------------------------------------------------- /src/netlink/util.cc: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | #include "util.h" 24 | 25 | 26 | unsigned long long NL_NAMESPACE_NAME::getTime() { 27 | 28 | #ifdef OS_WIN32 29 | 30 | SYSTEMTIME now; 31 | GetSystemTime(&now); 32 | unsigned long long milisec = now.wHour *3600*1000 + now.wMinute *60*1000 + now.wSecond *1000 + now.wMilliseconds; 33 | return(milisec); 34 | 35 | #else 36 | 37 | struct timeval now; 38 | gettimeofday(&now, NULL); 39 | unsigned long long milisec = now.tv_sec * 1000 + now.tv_usec / 1000.0; 40 | return(milisec); 41 | 42 | #endif 43 | 44 | } 45 | 46 | -------------------------------------------------------------------------------- /src/netlink/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | #ifndef __NL_UTIL 23 | #define __NL_UTIL 24 | 25 | #include "core.h" 26 | 27 | NL_NAMESPACE 28 | 29 | int iMax(int a, int b); 30 | unsigned uMax(unsigned a, unsigned b); 31 | 32 | unsigned long long getTime(); 33 | 34 | NL_NAMESPACE_END 35 | 36 | #include "util.inline.h" 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/netlink/util.inline.h: -------------------------------------------------------------------------------- 1 | /* 2 | NetLink Sockets: Networking C++ library 3 | Copyright 2012 Pedro Francisco Pareja Ruiz (PedroPareja@Gmail.com) 4 | 5 | This file is part of NetLink Sockets. 6 | 7 | NetLink Sockets is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | NetLink Sockets is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with NetLink Sockets. If not, see . 19 | 20 | */ 21 | 22 | 23 | 24 | inline int NL_NAMESPACE_NAME::iMax(int a, int b) { 25 | 26 | return (a>b)?a:b; 27 | } 28 | 29 | inline unsigned NL_NAMESPACE_NAME::uMax(unsigned a, unsigned b) { 30 | 31 | return (a>b)?a:b; 32 | } 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/netlinksocket.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "netlinkwrapper.h" 3 | 4 | void init_all(v8::Local exports) 5 | { 6 | NL::init(); 7 | NetLinkWrapper::init(exports); 8 | } 9 | 10 | NODE_MODULE(netlinksocket, init_all) 11 | -------------------------------------------------------------------------------- /src/netlinkwrapper.cc: -------------------------------------------------------------------------------- 1 | #define NOMINMAX 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "arg_parser.h" 10 | #include "get_value.h" 11 | #include "netlinkwrapper.h" 12 | #include "netlink/exception.h" 13 | 14 | #define READ_SIZE 255 15 | 16 | v8::Persistent NetLinkWrapper::class_socket_base; 17 | v8::Persistent NetLinkWrapper::class_socket_tcp_client; 18 | v8::Persistent NetLinkWrapper::class_socket_tcp_server; 19 | v8::Persistent NetLinkWrapper::class_socket_udp; 20 | 21 | v8::Local v8_str(const char *str) 22 | { 23 | if (str) 24 | { 25 | return Nan::New(str).ToLocalChecked(); 26 | } 27 | 28 | return Nan::New("No string provided").ToLocalChecked(); 29 | } 30 | 31 | v8::Local v8_str(const std::string &str) 32 | { 33 | return Nan::New(str).ToLocalChecked(); 34 | } 35 | 36 | void throw_js_error(NL::Exception &err) 37 | { 38 | auto isolate = v8::Isolate::GetCurrent(); 39 | std::stringstream ss; 40 | ss << "[NetLinkSocket Error " << err.code() << "]: " << err.msg(); 41 | 42 | auto v8_val = v8_str(ss.str()); 43 | isolate->ThrowException(v8::Exception::Error(v8_val)); 44 | } 45 | 46 | NetLinkWrapper::NetLinkWrapper(NL::Socket *socket) 47 | { 48 | this->socket = socket; 49 | 50 | this->blocking = this->socket->blocking(); 51 | this->ip_version = this->socket->ipVer(); 52 | 53 | this->port_from = this->socket->portFrom(); 54 | this->host_from = this->socket->hostFrom(); 55 | this->port_to = this->socket->portTo(); 56 | this->host_to = this->socket->hostTo(); 57 | } 58 | 59 | NetLinkWrapper::~NetLinkWrapper() 60 | { 61 | if (this->socket != nullptr) 62 | { 63 | this->socket->disconnect(); 64 | delete this->socket; 65 | this->socket = nullptr; 66 | } 67 | } 68 | 69 | bool NetLinkWrapper::throw_if_destroyed() 70 | { 71 | if (this->socket != nullptr) 72 | { 73 | return false; 74 | } 75 | 76 | auto isolate = v8::Isolate::GetCurrent(); 77 | auto v8_val = v8_str("Cannot use NetLinkSocket that has already been destroyed."); 78 | isolate->ThrowException(v8::Exception::Error(v8_val)); 79 | return true; 80 | } 81 | 82 | void NetLinkWrapper::init(v8::Local exports) 83 | { 84 | auto isolate = v8::Isolate::GetCurrent(); 85 | 86 | /* -- Base -- */ 87 | auto name_base = v8_str("SocketBase"); 88 | auto base_template = v8::FunctionTemplate::New(isolate, new_base); 89 | base_template->SetClassName(name_base); 90 | auto base_instance_template = base_template->InstanceTemplate(); 91 | base_instance_template->SetInternalFieldCount(1); 92 | 93 | base_instance_template->SetAccessor( 94 | v8_str("isBlocking"), 95 | getter_is_blocking, 96 | setter_is_blocking); 97 | 98 | base_instance_template->SetAccessor( 99 | v8_str("isDestroyed"), 100 | getter_is_destroyed, 101 | setter_throw_exception); 102 | 103 | base_instance_template->SetAccessor( 104 | v8_str("isIPv4"), 105 | getter_is_ipv4, 106 | setter_throw_exception); 107 | 108 | base_instance_template->SetAccessor( 109 | v8_str("isIPv6"), 110 | getter_is_ipv6, 111 | setter_throw_exception); 112 | 113 | base_instance_template->SetAccessor( 114 | v8_str("portFrom"), 115 | getter_port_from, 116 | setter_throw_exception); 117 | 118 | NODE_SET_PROTOTYPE_METHOD(base_template, "disconnect", disconnect); 119 | 120 | /* -- TCP Client -- */ 121 | auto name_tcp_client = v8_str("SocketClientTCP"); 122 | auto tcp_client_template = v8::FunctionTemplate::New(isolate, new_tcp_client); 123 | tcp_client_template->SetClassName(name_tcp_client); 124 | tcp_client_template->Inherit(base_template); 125 | auto tcp_client_instance_template = tcp_client_template->InstanceTemplate(); 126 | tcp_client_instance_template->SetInternalFieldCount(1); 127 | 128 | tcp_client_instance_template->SetAccessor( 129 | v8_str("hostTo"), 130 | getter_host_to, 131 | setter_throw_exception); 132 | tcp_client_instance_template->SetAccessor( 133 | v8_str("portTo"), 134 | getter_port_to, 135 | setter_throw_exception); 136 | 137 | NODE_SET_PROTOTYPE_METHOD(tcp_client_template, "receive", receive); 138 | NODE_SET_PROTOTYPE_METHOD(tcp_client_template, "send", send); 139 | 140 | /* -- TCP Server -- */ 141 | auto name_tcp_server = v8_str("SocketServerTCP"); 142 | auto tcp_server_template = v8::FunctionTemplate::New(isolate, new_tcp_server); 143 | tcp_server_template->SetClassName(name_tcp_server); 144 | tcp_server_template->Inherit(base_template); 145 | auto tcp_server_instance_template = tcp_server_template->InstanceTemplate(); 146 | tcp_server_instance_template->SetInternalFieldCount(1); 147 | 148 | tcp_server_instance_template->SetAccessor( 149 | v8_str("hostFrom"), 150 | getter_host_from, 151 | setter_throw_exception); 152 | 153 | NODE_SET_PROTOTYPE_METHOD(tcp_server_template, "accept", accept); 154 | 155 | /* -- UDP -- */ 156 | auto name_udp = v8_str("SocketUDP"); 157 | auto udp_template = v8::FunctionTemplate::New(isolate, new_udp); 158 | udp_template->SetClassName(name_udp); 159 | udp_template->Inherit(base_template); 160 | 161 | auto udp_instance_template = udp_template->InstanceTemplate(); 162 | udp_instance_template->SetInternalFieldCount(1); 163 | 164 | udp_instance_template->SetAccessor( 165 | v8_str("hostFrom"), 166 | getter_host_from, 167 | setter_throw_exception); 168 | 169 | NODE_SET_PROTOTYPE_METHOD(udp_template, "receiveFrom", receive_from); 170 | NODE_SET_PROTOTYPE_METHOD(udp_template, "sendTo", send_to); 171 | 172 | // Actually expose them to our module's exports 173 | Nan::Set(exports, name_base, Nan::GetFunction(base_template).ToLocalChecked()); 174 | Nan::Set(exports, name_tcp_client, Nan::GetFunction(tcp_client_template).ToLocalChecked()); 175 | Nan::Set(exports, name_tcp_server, Nan::GetFunction(tcp_server_template).ToLocalChecked()); 176 | Nan::Set(exports, name_udp, Nan::GetFunction(udp_template).ToLocalChecked()); 177 | 178 | class_socket_base.Reset(isolate, v8::Persistent(isolate, base_template)); 179 | class_socket_tcp_client.Reset(isolate, v8::Persistent(isolate, tcp_client_template)); 180 | class_socket_tcp_server.Reset(isolate, v8::Persistent(isolate, tcp_server_template)); 181 | class_socket_udp.Reset(isolate, v8::Persistent(isolate, udp_template)); 182 | } 183 | 184 | /* -- JS Constructors -- */ 185 | 186 | void NetLinkWrapper::new_base(const v8::FunctionCallbackInfo &args) 187 | { 188 | auto isolate = v8::Isolate::GetCurrent(); 189 | isolate->ThrowException(v8::Exception::Error(v8_str("SocketBase should not be directly constructed"))); 190 | } 191 | 192 | void NetLinkWrapper::new_tcp_client(const v8::FunctionCallbackInfo &args) 193 | { 194 | if (!args.IsConstructCall()) 195 | { 196 | auto isolate = v8::Isolate::GetCurrent(); 197 | isolate->ThrowException(v8::Exception::Error(v8_str("SocketClientTCP constructor must be invoked via 'new'."))); 198 | return; 199 | } 200 | // else Invoked as constructor: `new NetLinkWrapper(...)` 201 | 202 | std::string host; 203 | std::uint16_t port = 0; 204 | NL::IPVer ip_version = NL::IPVer::IP4; 205 | 206 | if (ArgParser(args) 207 | .arg("port", port) 208 | .arg("host", host) 209 | .opt("ipVersion", ip_version) 210 | .isInvalid()) 211 | { 212 | return; 213 | } 214 | 215 | NL::Socket *socket; 216 | try 217 | { 218 | socket = new NL::Socket(host, port, NL::Protocol::TCP, ip_version); 219 | } 220 | catch (NL::Exception &err) 221 | { 222 | throw_js_error(err); 223 | return; 224 | } 225 | 226 | NetLinkWrapper *obj = new NetLinkWrapper(socket); 227 | obj->Wrap(args.This()); 228 | args.GetReturnValue().Set(args.This()); 229 | } 230 | 231 | void NetLinkWrapper::new_udp(const v8::FunctionCallbackInfo &args) 232 | { 233 | if (!args.IsConstructCall()) 234 | { 235 | auto isolate = v8::Isolate::GetCurrent(); 236 | isolate->ThrowException(v8::Exception::Error(v8_str("SocketUDP constructor must be invoked via 'new'."))); 237 | return; 238 | } 239 | // else Invoked as constructor: `new NetLinkWrapper(...)` 240 | 241 | // expected args, in order 242 | std::uint16_t port_from = 0; 243 | std::string host_from; 244 | NL::IPVer ip_version = NL::IPVer::IP4; 245 | 246 | if (ArgParser(args) 247 | .opt("portFrom", port_from) 248 | .opt("hostFrom", host_from) 249 | .opt("ipVersion", ip_version) 250 | .isInvalid()) 251 | { 252 | return; 253 | } 254 | 255 | NL::Socket *socket; 256 | try 257 | { 258 | socket = new NL::Socket(port_from, NL::Protocol::UDP, ip_version, host_from); 259 | } 260 | catch (NL::Exception &err) 261 | { 262 | throw_js_error(err); 263 | return; 264 | } 265 | 266 | NetLinkWrapper *obj = new NetLinkWrapper(socket); 267 | obj->Wrap(args.This()); 268 | args.GetReturnValue().Set(args.This()); 269 | } 270 | 271 | void NetLinkWrapper::new_tcp_server(const v8::FunctionCallbackInfo &args) 272 | { 273 | if (!args.IsConstructCall()) 274 | { 275 | auto isolate = v8::Isolate::GetCurrent(); 276 | isolate->ThrowException(v8::Exception::Error(v8_str("SocketServerTCP constructor must be invoked via 'new'."))); 277 | return; 278 | } 279 | 280 | std::uint16_t port_from = 0; 281 | std::string host_from; 282 | NL::IPVer ip_version = NL::IPVer::IP4; 283 | 284 | if (ArgParser(args) 285 | .arg("portFrom", port_from) 286 | .opt("hostFrom", host_from) 287 | .opt("ipVersion", ip_version) 288 | .isInvalid()) 289 | { 290 | return; 291 | } 292 | 293 | NL::Socket *socket; 294 | try 295 | { 296 | socket = new NL::Socket(port_from, NL::Protocol::TCP, ip_version, host_from); 297 | } 298 | catch (NL::Exception &err) 299 | { 300 | throw_js_error(err); 301 | return; 302 | } 303 | 304 | NetLinkWrapper *obj = new NetLinkWrapper(socket); 305 | obj->Wrap(args.This()); 306 | args.GetReturnValue().Set(args.This()); 307 | } 308 | 309 | /* -- JS methods -- */ 310 | 311 | void NetLinkWrapper::accept(const v8::FunctionCallbackInfo &args) 312 | { 313 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 314 | if (obj->throw_if_destroyed()) 315 | { 316 | return; 317 | } 318 | 319 | NL::Socket *accepted = NULL; 320 | try 321 | { 322 | accepted = obj->socket->accept(); 323 | } 324 | catch (NL::Exception &err) 325 | { 326 | throw_js_error(err); 327 | return; 328 | } 329 | 330 | if (accepted != NULL) 331 | { 332 | auto new_wrapper = new NetLinkWrapper(accepted); 333 | // accept() only works on TCP servers, 334 | // So we know for certain wrapped instances always must be TCP clients 335 | auto isolate = v8::Isolate::GetCurrent(); 336 | auto function_template = NetLinkWrapper::class_socket_tcp_client.Get(isolate); 337 | auto object_template = function_template->InstanceTemplate(); 338 | auto instance = Nan::NewInstance(object_template).ToLocalChecked(); 339 | new_wrapper->Wrap(instance); 340 | 341 | args.GetReturnValue().Set(instance); 342 | } 343 | } 344 | 345 | void NetLinkWrapper::disconnect(const v8::FunctionCallbackInfo &args) 346 | { 347 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 348 | if (obj->throw_if_destroyed()) 349 | { 350 | return; 351 | } 352 | 353 | try 354 | { 355 | // TCP clients need to be drained 356 | // on Linux will others will throw an exception. Windows ignores it. 357 | if (obj->socket->protocol() == NL::Protocol::TCP && obj->socket->type() == NL::SocketType::CLIENT) 358 | { 359 | auto size = obj->socket->nextReadSize(); 360 | if (size > 0) 361 | { 362 | // we need to drain the socket. Otherwise it will hang on closing the 363 | // socket if there is still data in the buffer. 364 | char *buffer = new char[size + 1]; 365 | obj->socket->read(buffer, size); 366 | delete[] buffer; 367 | } 368 | } 369 | } 370 | catch (NL::Exception &err) 371 | { 372 | throw_js_error(err); 373 | return; 374 | } 375 | 376 | try 377 | { 378 | obj->socket->disconnect(); 379 | delete obj->socket; 380 | } 381 | catch (NL::Exception &err) 382 | { 383 | throw_js_error(err); 384 | return; 385 | } 386 | 387 | obj->socket = nullptr; 388 | } 389 | 390 | void NetLinkWrapper::receive(const v8::FunctionCallbackInfo &args) 391 | { 392 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 393 | if (obj->throw_if_destroyed()) 394 | { 395 | return; 396 | } 397 | 398 | int next_read_size = 0; 399 | bool blocking = false; 400 | try 401 | { 402 | next_read_size = obj->socket->nextReadSize(); 403 | blocking = obj->socket->blocking(); 404 | } 405 | catch (NL::Exception &err) 406 | { 407 | throw_js_error(err); 408 | return; 409 | } 410 | 411 | if (next_read_size < 1 && !blocking) 412 | { 413 | // we're not blocking and there is nothing to read, returning here 414 | // will return undefined to the js function, as there was nothing 415 | // to read. 416 | return; 417 | } 418 | 419 | std::stringstream ss; 420 | try 421 | { 422 | bool keep_reading = true; 423 | while (keep_reading) 424 | { 425 | auto buffer = std::array(); 426 | auto buffer_read = obj->socket->read(buffer.data(), READ_SIZE); 427 | if (buffer_read > 0) 428 | { 429 | ss << std::string(buffer.data(), buffer_read); 430 | } 431 | if (buffer_read != READ_SIZE) 432 | { 433 | keep_reading = false; 434 | } 435 | } 436 | } 437 | catch (NL::Exception &err) 438 | { 439 | throw_js_error(err); 440 | return; 441 | } 442 | 443 | auto str = ss.str(); 444 | if (str.length()) // range check 445 | { 446 | args.GetReturnValue().Set(Nan::CopyBuffer(str.c_str(), str.length()).ToLocalChecked()); 447 | } 448 | // else it did not read any data, so this will return undefined 449 | } 450 | 451 | void NetLinkWrapper::receive_from(const v8::FunctionCallbackInfo &args) 452 | { 453 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 454 | if (obj->throw_if_destroyed()) 455 | { 456 | return; 457 | } 458 | 459 | std::stringstream read_ss; 460 | std::string host_from = ""; 461 | unsigned int port_from = 0; 462 | try 463 | { 464 | bool keep_reading = true; 465 | while (keep_reading) 466 | { 467 | auto buffer = std::array(); 468 | auto buffer_read = obj->socket->readFrom(buffer.data(), READ_SIZE, &host_from, &port_from); 469 | if (buffer_read > 0) 470 | { 471 | read_ss << std::string(buffer.data(), buffer_read); 472 | } 473 | if (buffer_read != READ_SIZE) 474 | { 475 | keep_reading = false; 476 | } 477 | } 478 | } 479 | catch (NL::Exception &err) 480 | { 481 | throw_js_error(err); 482 | return; 483 | } 484 | 485 | auto read = read_ss.str(); 486 | if (host_from.length() || port_from || read.length()) 487 | { 488 | auto return_object = Nan::New(); 489 | 490 | auto host_key = v8_str("host"); 491 | auto host_value = v8_str(host_from); 492 | Nan::Set(return_object, host_key, host_value); 493 | 494 | auto port_key = v8_str("port"); 495 | auto port_value = Nan::New(port_from); 496 | Nan::Set(return_object, port_key, port_value); 497 | 498 | auto data_key = v8_str("data"); 499 | auto data_value = Nan::CopyBuffer(read.c_str(), read.length()).ToLocalChecked(); 500 | Nan::Set(return_object, data_key, data_value); 501 | 502 | args.GetReturnValue().Set(return_object); 503 | } 504 | // else it did not read any data, so this will return undefined 505 | } 506 | 507 | void NetLinkWrapper::set_blocking(const v8::FunctionCallbackInfo &args) 508 | { 509 | bool blocking = true; 510 | if (ArgParser(args) 511 | .arg("blocking", blocking) 512 | .isInvalid()) 513 | { 514 | return; 515 | } 516 | 517 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 518 | if (obj->throw_if_destroyed()) 519 | { 520 | return; 521 | } 522 | 523 | try 524 | { 525 | 526 | obj->socket->blocking(blocking); 527 | } 528 | catch (NL::Exception &err) 529 | { 530 | throw_js_error(err); 531 | return; 532 | } 533 | } 534 | 535 | void NetLinkWrapper::send(const v8::FunctionCallbackInfo &args) 536 | { 537 | std::string data; 538 | if (ArgParser(args) 539 | .arg("data", data, GetValue::SubType::SendableData) 540 | .isInvalid()) 541 | { 542 | return; 543 | } 544 | 545 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 546 | if (obj->throw_if_destroyed()) 547 | { 548 | return; 549 | } 550 | 551 | try 552 | { 553 | 554 | obj->socket->send(data.c_str(), data.length()); 555 | } 556 | catch (NL::Exception &err) 557 | { 558 | throw_js_error(err); 559 | return; 560 | } 561 | } 562 | 563 | void NetLinkWrapper::send_to(const v8::FunctionCallbackInfo &args) 564 | { 565 | 566 | std::string host; 567 | std::uint16_t port = 0; 568 | std::string data; 569 | if (ArgParser(args) 570 | .arg("host", host) 571 | .arg("port", port) 572 | .arg("data", data, GetValue::SubType::SendableData) 573 | .isInvalid()) 574 | { 575 | return; 576 | } 577 | 578 | auto obj = node::ObjectWrap::Unwrap(args.Holder()); 579 | if (obj->throw_if_destroyed()) 580 | { 581 | return; 582 | } 583 | 584 | try 585 | { 586 | 587 | obj->socket->sendTo(data.c_str(), data.length(), host, port); 588 | } 589 | catch (NL::Exception &err) 590 | { 591 | throw_js_error(err); 592 | return; 593 | } 594 | } 595 | 596 | /* -- Getters -- */ 597 | 598 | void NetLinkWrapper::getter_is_blocking( 599 | v8::Local, 600 | const v8::PropertyCallbackInfo &info) 601 | { 602 | 603 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 604 | info.GetReturnValue().Set(Nan::New(obj->blocking)); 605 | }; 606 | 607 | void NetLinkWrapper::getter_is_destroyed( 608 | v8::Local, 609 | const v8::PropertyCallbackInfo &info) 610 | { 611 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 612 | info.GetReturnValue().Set(Nan::New(obj->socket == nullptr)); 613 | }; 614 | 615 | void NetLinkWrapper::getter_is_ipv4( 616 | v8::Local, 617 | const v8::PropertyCallbackInfo &info) 618 | { 619 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 620 | bool is_ipv4 = obj->ip_version == NL::IPVer::IP4; 621 | info.GetReturnValue().Set(Nan::New(is_ipv4)); 622 | }; 623 | 624 | void NetLinkWrapper::getter_is_ipv6( 625 | v8::Local, 626 | const v8::PropertyCallbackInfo &info) 627 | { 628 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 629 | bool is_ipv6 = obj->ip_version == NL::IPVer::IP6; 630 | info.GetReturnValue().Set(Nan::New(is_ipv6)); 631 | }; 632 | 633 | void NetLinkWrapper::getter_host_from( 634 | v8::Local, 635 | const v8::PropertyCallbackInfo &info) 636 | { 637 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 638 | info.GetReturnValue().Set(v8_str(obj->host_from)); 639 | }; 640 | 641 | void NetLinkWrapper::getter_host_to( 642 | v8::Local, 643 | const v8::PropertyCallbackInfo &info) 644 | { 645 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 646 | info.GetReturnValue().Set(v8_str(obj->host_to)); 647 | }; 648 | 649 | void NetLinkWrapper::getter_port_from( 650 | v8::Local, 651 | const v8::PropertyCallbackInfo &info) 652 | { 653 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 654 | info.GetReturnValue().Set(Nan::New(obj->port_from)); 655 | }; 656 | 657 | void NetLinkWrapper::getter_port_to( 658 | v8::Local, 659 | const v8::PropertyCallbackInfo &info) 660 | { 661 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 662 | info.GetReturnValue().Set(Nan::New(obj->port_to)); 663 | }; 664 | 665 | /* -- Setters -- */ 666 | 667 | void NetLinkWrapper::setter_throw_exception( 668 | v8::Local property, 669 | v8::Local value, 670 | const v8::PropertyCallbackInfo &info) 671 | { 672 | Nan::Utf8String utf8_property_name(property); 673 | 674 | auto constructor_name = info.This()->GetConstructorName(); 675 | Nan::Utf8String utf8_constructor_name(constructor_name); 676 | 677 | std::stringstream ss; 678 | ss << "Property \"" << *utf8_property_name << "\" on " 679 | << *utf8_constructor_name 680 | << " instance cannot be set as it is a readonly property."; 681 | 682 | auto isolate = v8::Isolate::GetCurrent(); 683 | isolate->ThrowException(v8::Exception::Error(v8_str(ss.str()))); 684 | } 685 | 686 | void NetLinkWrapper::setter_is_blocking( 687 | v8::Local, 688 | v8::Local value, 689 | const v8::PropertyCallbackInfo &info) 690 | { 691 | if (!value->IsBoolean()) 692 | { 693 | auto isolate = v8::Isolate::GetCurrent(); 694 | isolate->ThrowException(v8::Exception::Error(v8_str("Value to set \"isBlocking\" to must be a boolean."))); 695 | return; 696 | } 697 | 698 | auto obj = node::ObjectWrap::Unwrap(info.Holder()); 699 | if (obj->throw_if_destroyed()) 700 | { 701 | return; 702 | } 703 | 704 | const auto blocking = value->IsTrue(); 705 | try 706 | { 707 | 708 | obj->socket->blocking(blocking); 709 | } 710 | catch (NL::Exception &err) 711 | { 712 | throw_js_error(err); 713 | return; 714 | } 715 | 716 | obj->blocking = blocking; 717 | } 718 | -------------------------------------------------------------------------------- /src/netlinkwrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef NETLINKOBJECT_H 2 | #define NETLINKOBJECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "netlink/socket.h" 9 | 10 | class NetLinkWrapper : public node::ObjectWrap 11 | { 12 | public: 13 | static void init(v8::Local exports); 14 | 15 | private: 16 | NL::Socket *socket; 17 | 18 | // accessed via getters, so we cache them here 19 | bool blocking = true; 20 | NL::IPVer ip_version; 21 | 22 | std::uint16_t port_from; 23 | std::string host_from; 24 | std::uint16_t port_to; 25 | std::string host_to; 26 | 27 | explicit NetLinkWrapper(NL::Socket *socket); 28 | ~NetLinkWrapper(); 29 | 30 | bool throw_if_destroyed(); 31 | 32 | static v8::Persistent class_socket_base; 33 | static v8::Persistent class_socket_tcp_client; 34 | static v8::Persistent class_socket_tcp_server; 35 | static v8::Persistent class_socket_udp; 36 | 37 | /* -- Class Constructors -- */ 38 | static void new_base(const v8::FunctionCallbackInfo &args); 39 | static void new_tcp_client(const v8::FunctionCallbackInfo &args); 40 | static void new_tcp_server(const v8::FunctionCallbackInfo &args); 41 | static void new_udp(const v8::FunctionCallbackInfo &args); 42 | 43 | /* -- Methods -- */ 44 | static void accept(const v8::FunctionCallbackInfo &args); 45 | static void disconnect(const v8::FunctionCallbackInfo &args); 46 | static void receive(const v8::FunctionCallbackInfo &args); 47 | static void receive_from(const v8::FunctionCallbackInfo &args); 48 | static void set_blocking(const v8::FunctionCallbackInfo &args); 49 | static void send(const v8::FunctionCallbackInfo &args); 50 | static void send_to(const v8::FunctionCallbackInfo &args); 51 | 52 | /* -- Getters -- */ 53 | static void getter_host_from( 54 | v8::Local, 55 | const v8::PropertyCallbackInfo &info); 56 | static void getter_host_to( 57 | v8::Local, 58 | const v8::PropertyCallbackInfo &info); 59 | static void getter_port_from( 60 | v8::Local, 61 | const v8::PropertyCallbackInfo &info); 62 | static void getter_port_to( 63 | v8::Local, 64 | const v8::PropertyCallbackInfo &info); 65 | 66 | static void getter_is_blocking( 67 | v8::Local, 68 | const v8::PropertyCallbackInfo &info); 69 | static void getter_is_destroyed( 70 | v8::Local, 71 | const v8::PropertyCallbackInfo &info); 72 | static void getter_is_ipv4( 73 | v8::Local, 74 | const v8::PropertyCallbackInfo &info); 75 | static void getter_is_ipv6( 76 | v8::Local, 77 | const v8::PropertyCallbackInfo &info); 78 | 79 | /* -- Setters -- */ 80 | static void setter_throw_exception( 81 | v8::Local, 82 | v8::Local, 83 | const v8::PropertyCallbackInfo &); 84 | static void setter_is_blocking( 85 | v8::Local, 86 | v8::Local value, 87 | const v8::PropertyCallbackInfo &info); 88 | }; 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /test/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /* eslint-env node */ 3 | 4 | /** @type {import("eslint").Linter.Config} */ 5 | const baseEslintConfig = { 6 | extends: ["../.eslintrc.js", "plugin:mocha/recommended"], 7 | plugins: ["mocha"], 8 | env: { 9 | mocha: true, 10 | }, 11 | rules: { 12 | "mocha/no-setup-in-describe": "off", 13 | }, 14 | }; 15 | 16 | module.exports = baseEslintConfig; 17 | -------------------------------------------------------------------------------- /test/.mocharc.js: -------------------------------------------------------------------------------- 1 | /** @type import("mocha").MochaOptions */ 2 | const mochaConfig = { 3 | timeout: 30000, 4 | }; 5 | 6 | module.exports = mochaConfig; 7 | -------------------------------------------------------------------------------- /test/base.test.ts: -------------------------------------------------------------------------------- 1 | import { SocketBase } from "../lib"; 2 | import { 3 | badArg, 4 | BadConstructor, 5 | EchoSocket, 6 | tcpClientTester, 7 | tcpServerTester, 8 | udpTester, 9 | } from "./utils"; 10 | import { expect } from "chai"; 11 | 12 | describe("base functionality", function () { 13 | for (const tester of [tcpClientTester, tcpServerTester, udpTester]) { 14 | tester.testPermutations((testing: typeof tester.testing) => { 15 | it("exists", function () { 16 | expect(testing.netLink).to.exist; 17 | }); 18 | 19 | it("sets up testing data", function () { 20 | expect(testing).to.exist; 21 | expect(testing.echo).to.be.instanceOf(EchoSocket); 22 | expect(testing.netLink).to.be.instanceOf(SocketBase); 23 | expect(typeof testing.host).to.equal("string"); 24 | expect(typeof testing.port).to.equal("number"); 25 | expect(typeof testing.str).to.equal("string"); 26 | }); 27 | 28 | it("extends SocketBase", function () { 29 | expect(testing.netLink).to.be.instanceof(SocketBase); 30 | }); 31 | 32 | it("can set blocking state", function () { 33 | testing.netLink.isBlocking = false; 34 | expect(testing.netLink.isBlocking).to.be.false; 35 | testing.netLink.isBlocking = true; 36 | expect(testing.netLink.isBlocking).to.be.true; 37 | }); 38 | 39 | it("can get blocking state", function () { 40 | expect(testing.netLink.isBlocking).to.be.true; 41 | testing.netLink.isBlocking = false; 42 | expect(testing.netLink.isBlocking).to.be.false; 43 | testing.netLink.isBlocking = true; 44 | expect(testing.netLink.isBlocking).to.be.true; 45 | }); 46 | 47 | it("cannot set isBlocking to an invalid arg", function () { 48 | expect(() => { 49 | testing.netLink.isBlocking = badArg(); 50 | }).to.throw(); 51 | }); 52 | 53 | it("cannot set isBlocking after disconnecting", function () { 54 | testing.netLink.disconnect(); 55 | expect(testing.netLink.isDestroyed).to.be.true; 56 | expect(() => { 57 | testing.netLink.isBlocking = true; 58 | }).to.throw(); 59 | }); 60 | 61 | it("can get portFrom", function () { 62 | const portFrom = testing.netLink.portFrom; 63 | expect(typeof portFrom).to.equal("number"); 64 | // can't guarantee portFrom is bound to a specific number for 65 | }); 66 | 67 | it("cannot set portFrom", function () { 68 | expect(() => { 69 | testing.settableNetLink.portFrom = badArg(); 70 | }).to.throw(); 71 | }); 72 | 73 | it("can disconnect", function () { 74 | expect(() => testing.netLink.disconnect()).not.to.throw; 75 | }); 76 | 77 | it("can get isDestroyed", function () { 78 | expect(testing.netLink.isDestroyed).to.be.false; 79 | testing.netLink.disconnect(); 80 | expect(testing.netLink.isDestroyed).to.be.true; 81 | }); 82 | 83 | it("cannot set isDestroyed", function () { 84 | expect(() => { 85 | testing.settableNetLink.isDestroyed = badArg(); 86 | }).to.throw(); 87 | }); 88 | 89 | it("cannot disconnect after disconnecting", function () { 90 | testing.netLink.disconnect(); 91 | expect(testing.netLink.isDestroyed).to.be.true; 92 | expect(() => testing.netLink.disconnect()).to.throw(); 93 | }); 94 | 95 | it("can get isIPv4", function () { 96 | expect(typeof testing.netLink.isIPv4).to.equal("boolean"); 97 | }); 98 | 99 | it("cannot set isIPv4", function () { 100 | expect(() => { 101 | testing.settableNetLink.isIPv4 = badArg(); 102 | }).to.throw(); 103 | }); 104 | 105 | it("can get isIPv6", function () { 106 | expect(typeof testing.netLink.isIPv6).to.equal("boolean"); 107 | }); 108 | 109 | it("cannot set isIPv6", function () { 110 | expect(() => { 111 | testing.settableNetLink.isIPv6 = badArg(); 112 | }).to.throw(); 113 | }); 114 | 115 | it("is the expected IP version", function () { 116 | const { ipVersion } = testing.constructorArgs; 117 | const expectIPv6 = ipVersion === "IPv6"; 118 | expect(testing.netLink.isIPv6).to.equal(expectIPv6); 119 | expect(testing.netLink.isIPv4).to.equal(!expectIPv6); 120 | }); 121 | }); 122 | 123 | tester.testInvalidPermutations((...args) => { 124 | it("should throw when constructed with invalid args", function () { 125 | expect(() => { 126 | new (tester.NetLinkClass as BadConstructor)(...args); 127 | }).to.throw(TypeError); 128 | }); 129 | }); 130 | } 131 | }); 132 | -------------------------------------------------------------------------------- /test/client.worker.ts: -------------------------------------------------------------------------------- 1 | import { SocketClientTCP, SocketUDP } from "../lib"; 2 | 3 | const { testString, testPort, testType } = process.env; 4 | 5 | if (!testString || !testPort || !testType) { 6 | throw new Error("env not set properly for test worker!"); 7 | } 8 | 9 | const host = "127.0.0.1"; 10 | const port = Number(testPort); 11 | 12 | const netLink = 13 | testType === "UDP" ? new SocketUDP() : new SocketClientTCP(port, host); 14 | 15 | netLink.isBlocking = true; 16 | 17 | let echoedString: string | -1 = -1; 18 | if (netLink instanceof SocketClientTCP) { 19 | netLink.send(testString); 20 | const echoed = netLink.receive(); 21 | echoedString = echoed?.toString() || -1; 22 | } else { 23 | netLink.sendTo(host, port, testString); 24 | const echoedFrom = netLink.receiveFrom(); 25 | if (echoedFrom?.port !== port) { 26 | throw new Error(`UDP Echo not from expected port '${port}'.`); 27 | } 28 | echoedString = echoedFrom.data.toString(); 29 | } 30 | 31 | if (echoedString !== testString) { 32 | throw new Error( 33 | `Echo server failed! ("${testString}" !== "${echoedString}").`, 34 | ); 35 | } 36 | 37 | netLink.disconnect(); 38 | // all looks good! process should exit with 0 here 39 | -------------------------------------------------------------------------------- /test/clients.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { fork } from "child_process"; 3 | import { join, resolve } from "path"; 4 | import { TextEncoder } from "util"; 5 | import { badArg, tcpClientTester, udpTester, EchoUDP } from "./utils"; 6 | import { SocketUDP } from "../lib"; 7 | 8 | describe("client shared functionality", function () { 9 | for (const tester of [tcpClientTester, udpTester]) { 10 | tester.testPermutations((testing: typeof tester.testing) => { 11 | const echoPort = () => 12 | testing.echo instanceof EchoUDP 13 | ? testing.echo.getPort() 14 | : testing.port; 15 | 16 | const send = ( 17 | data: string | Buffer | Uint8Array = testing.str, 18 | client = testing.netLink, 19 | ) => { 20 | if (client instanceof SocketUDP) { 21 | client.sendTo(testing.host, echoPort(), data); 22 | } else { 23 | client.send(data); 24 | } 25 | }; 26 | 27 | const receive = () => { 28 | if (testing.netLink instanceof SocketUDP) { 29 | const got = testing.netLink.receiveFrom(); 30 | return got?.data; 31 | } else { 32 | return testing.netLink.receive(); 33 | } 34 | }; 35 | 36 | it("exists", function () { 37 | expect(testing.netLink).to.exist; 38 | }); 39 | 40 | it("can read and write strings", async function () { 41 | const dataPromise = testing.echo.events.sentData.once(); 42 | send(testing.str); 43 | const sent = await dataPromise; 44 | expect(sent.str).to.equal(testing.str); // should be echoed back 45 | 46 | const read = receive(); 47 | expect(read).to.be.instanceOf(Buffer); 48 | expect(read?.compare(sent.buffer)).to.equal(0); 49 | }); 50 | 51 | it("can send Buffers", async function () { 52 | const dataPromise = testing.echo.events.sentData.once(); 53 | 54 | const buffer = Buffer.from(testing.str); 55 | send(buffer); 56 | const sent = await dataPromise; 57 | // should be echoed back 58 | expect(sent.buffer.compare(buffer)).to.equal(0); 59 | 60 | const read = receive(); 61 | expect(read?.compare(buffer)).to.equal(0); 62 | }); 63 | 64 | it("can send Uint8Arrays", async function () { 65 | const dataPromise = testing.echo.events.sentData.once(); 66 | 67 | const uint8array = new TextEncoder().encode(testing.str); 68 | send(uint8array); 69 | const sent = await dataPromise; 70 | expect(sent.str).to.equal(testing.str); // should be echoed back 71 | 72 | const read = receive(); 73 | expect(read?.toString()).to.equal(testing.str); 74 | }); 75 | 76 | it("cannot send invalid date", function () { 77 | expect(() => send(badArg())).to.throw(); 78 | }); 79 | 80 | it("can do blocking reads", async function () { 81 | testing.netLink.isBlocking = true; 82 | expect(testing.netLink.isBlocking).to.be.true; 83 | 84 | // shouldn't block here, nothing to read, returns undefined 85 | const sentPromise = testing.echo.events.sentData.once(); 86 | send(testing.str); 87 | const sent = await sentPromise; // now it should be echoed back 88 | expect(sent.buffer.toString()).to.equal(testing.str); 89 | const read = receive(); 90 | expect(read?.toString()).to.equal(testing.str); 91 | }); 92 | 93 | it("can do non blocking reads", function () { 94 | testing.netLink.isBlocking = false; 95 | expect(testing.netLink.isBlocking).to.be.false; 96 | 97 | // shouldn't block here, nothing to read, returns undefined 98 | const read = receive(); 99 | expect(read).to.be.undefined; 100 | }); 101 | 102 | it("can do truly blocking read", async function () { 103 | // Note: Slow because child process needs to transpile TS code 104 | const testString = "Hello worker thread!"; 105 | const newConnection = testing.echo.events.newConnection.once(); 106 | const sentDataPromise = testing.echo.events.sentData.once(); 107 | const closed = testing.echo.events.closedConnection.once(); 108 | // unlike other tests, for this test, the netLink tests are all 109 | // in the worker code 110 | const workerPath = resolve( 111 | join(__dirname, "./client.worker.ts"), 112 | ); 113 | 114 | const worker = fork(workerPath, [], { 115 | env: { 116 | testPort: String(echoPort()), 117 | testString, 118 | testType: 119 | testing.netLink instanceof SocketUDP 120 | ? "UDP" 121 | : "TCP", 122 | }, 123 | execArgv: ["-r", "ts-node/register"], 124 | }); 125 | 126 | await newConnection; 127 | const sent = await sentDataPromise; 128 | 129 | expect(sent.str).to.exist; 130 | expect(sent.str).to.equal(testString); 131 | 132 | await closed; 133 | 134 | const code = await new Promise((resolve, reject) => 135 | worker.on("exit", (code) => { 136 | if (code) { 137 | reject( 138 | new Error( 139 | `Worker process exited with code ${code}`, 140 | ), 141 | ); 142 | } else { 143 | resolve(code); 144 | } 145 | }), 146 | ); 147 | expect(code).to.equal(0); 148 | }); 149 | 150 | it("cannot receive once disconnected", function () { 151 | testing.netLink.disconnect(); 152 | expect(() => receive()).to.throw(); 153 | }); 154 | 155 | it("cannot send once disconnected", function () { 156 | testing.netLink.disconnect(); 157 | expect(() => send()).to.throw(); 158 | }); 159 | }); 160 | } 161 | }); 162 | -------------------------------------------------------------------------------- /test/module.test.ts: -------------------------------------------------------------------------------- 1 | import * as module from "../lib"; 2 | import { expect } from "chai"; 3 | 4 | describe("module", function () { 5 | it("has named exports", function () { 6 | expect(module.SocketBase).to.exist; 7 | expect(module.SocketClientTCP).to.exist; 8 | expect(module.SocketServerTCP).to.exist; 9 | expect(module.SocketUDP).to.exist; 10 | }); 11 | 12 | it("cannot be constructed as a base class.", function () { 13 | expect(() => { 14 | new (module.SocketBase as { 15 | new (): module.SocketBase; 16 | })(); 17 | }).to.throw(); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/sanity.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | 3 | describe("sanity tests", function () { 4 | it("knows true is true", function () { 5 | expect(typeof true).to.equal("boolean"); 6 | expect(true).to.equal(true); 7 | expect(true).to.equal(true); 8 | expect(true).to.be.true; 9 | }); 10 | 11 | it("knows true is not false", function () { 12 | expect(true).to.equal(!false); 13 | }); 14 | 15 | it("can compare numbers", function () { 16 | expect(1337).to.be.lessThan(9000); 17 | expect(9000).to.be.greaterThan(1337); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /test/tcp.client.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { Socket } from "net"; 3 | import { SocketClientTCP } from "../lib"; 4 | import { 5 | badArg, 6 | BadConstructor, 7 | badIPAddress, 8 | EchoClientTCP, 9 | getNextTestingPort, 10 | tcpClientTester, 11 | } from "./utils"; 12 | 13 | describe("TCP Client", function () { 14 | it("should throw without a port passed", function () { 15 | expect(() => { 16 | new (SocketClientTCP as BadConstructor)(); 17 | }).to.throw(TypeError); 18 | }); 19 | 20 | it("should throw without a host passed", function () { 21 | expect(() => { 22 | new (SocketClientTCP as BadConstructor)(12345); 23 | }).to.throw(TypeError); 24 | }); 25 | 26 | tcpClientTester.permutations("standalone", ({ ipVersion }) => { 27 | it("can register as a TCP listener", async function () { 28 | const echoServer = new EchoClientTCP(); 29 | const port = getNextTestingPort(); 30 | await echoServer.start({ port }); 31 | 32 | const connected = echoServer.events.newConnection.once(); 33 | const preConnectionCount = await echoServer.countConnections(); 34 | expect(preConnectionCount).to.equal(0); 35 | 36 | const tcp = new SocketClientTCP(port, "localhost", ipVersion); 37 | const listener = await connected; 38 | expect(listener).to.be.instanceOf(Socket); 39 | 40 | const postConnectionCount = await echoServer.countConnections(); 41 | expect(postConnectionCount).to.equal(1); 42 | 43 | const disconnected = echoServer.events.closedConnection.once(); 44 | tcp.disconnect(); 45 | const data = await disconnected; 46 | expect(data.from).to.equal(listener); 47 | expect(data.hadError).to.be.false; 48 | 49 | await echoServer.stop(); 50 | }); 51 | 52 | it("throws when it cannot connect to a server", function () { 53 | // extremely long timeout so all operating systems can run this 54 | this.timeout(2 * 60 * 1_000); // 2 min 55 | 56 | expect( 57 | () => new SocketClientTCP(1234, badIPAddress, ipVersion), 58 | ).to.throw(); 59 | }); 60 | }); 61 | 62 | tcpClientTester.testPermutations((testing) => { 63 | it("can get hostTo", function () { 64 | const { hostTo } = testing.netLink; 65 | 66 | expect(typeof hostTo).to.equal("string"); 67 | expect(hostTo).to.equal(testing.host); 68 | }); 69 | 70 | it("cannot set hostTo", function () { 71 | expect(() => { 72 | testing.settableNetLink.hostTo = badArg(); 73 | }).to.throw(); 74 | }); 75 | 76 | it("can get portTo", function () { 77 | const { portTo } = testing.netLink; 78 | 79 | expect(typeof portTo).to.equal("number"); 80 | expect(portTo).to.equal(testing.port); 81 | }); 82 | 83 | it("cannot set portTo", function () { 84 | expect(() => { 85 | testing.settableNetLink.portTo = badArg(); 86 | }).to.throw(); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/tcp.server.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { badArg, BadConstructor, tcpServerTester } from "./utils"; 3 | import { SocketClientTCP, SocketServerTCP } from "../lib"; 4 | 5 | describe("TCP Server", function () { 6 | it("should throw without a port passed", function () { 7 | expect(() => { 8 | new (SocketServerTCP as BadConstructor)(); 9 | }).to.throw(TypeError); 10 | }); 11 | 12 | tcpServerTester.testPermutations((testing) => { 13 | it("exists", function () { 14 | expect(testing.netLink).to.exist; 15 | }); 16 | 17 | it(`is IP Version "${testing.ipVersion}"`, function () { 18 | expect(testing.netLink.isIPv4).to.equal( 19 | testing.ipVersion !== "IPv6", 20 | ); 21 | expect(testing.netLink.isIPv6).to.equal( 22 | testing.ipVersion === "IPv6", 23 | ); 24 | }); 25 | 26 | it("can get hostFrom", function () { 27 | expect(testing.netLink.hostFrom).to.equal( 28 | testing.constructorArgs.host ? testing.host : "", 29 | ); 30 | }); 31 | 32 | it("cannot set hostFrom", function () { 33 | expect(() => { 34 | testing.settableNetLink.hostFrom = badArg(); 35 | }).to.throw(); 36 | }); 37 | 38 | it("can accept clients", function () { 39 | const client = testing.netLink.accept(); 40 | 41 | expect(client).to.exist; 42 | expect(client).to.be.an.instanceOf(SocketClientTCP); 43 | 44 | expect(client?.portFrom).to.equal(testing.port); 45 | 46 | client?.disconnect(); 47 | }); 48 | 49 | it("can accept with not blocking", function () { 50 | testing.netLink.isBlocking = false; 51 | const firstClient = testing.netLink.accept(); 52 | 53 | expect(firstClient).to.be.an.instanceOf(SocketClientTCP); 54 | expect(firstClient?.portFrom).to.equal(testing.port); 55 | firstClient?.disconnect(); 56 | 57 | const secondClient = testing.netLink.accept(); 58 | expect(secondClient).to.be.undefined; 59 | }); 60 | 61 | it("cannot accept clients once disconnected", function () { 62 | testing.netLink.disconnect(); 63 | 64 | expect(() => testing.netLink.accept()).to.throw(); 65 | }); 66 | 67 | it("can send and receive data to the client", async function () { 68 | const client = testing.netLink.accept(); 69 | 70 | expect(client).to.exist; 71 | if (!client) { 72 | throw new Error("client should exist"); 73 | } 74 | 75 | const sentData = testing.echo.events.sentData.once(); 76 | client.isBlocking = false; 77 | expect(client.receive()).to.be.undefined; 78 | client.send(testing.str); 79 | const sent = await sentData; 80 | expect(sent.str).to.equal(testing.str); 81 | const echoed = client.receive(); 82 | expect(echoed?.toString()).to.equal(testing.str); 83 | 84 | client.disconnect(); 85 | }); 86 | 87 | it("can attempt to accept when no clients connect", function () { 88 | // We don't want it to block forever waiting for a client that 89 | // will never exist 90 | testing.netLink.isBlocking = false; 91 | 92 | // first echo client that always connects 93 | const client = testing.netLink.accept(); 94 | expect(client).to.exist; 95 | 96 | // we never made anything else connect, so expect nothing to accept 97 | const noClient = testing.netLink.accept(); 98 | expect(noClient).to.be.undefined; 99 | 100 | client?.disconnect(); 101 | }); 102 | }); 103 | }); 104 | -------------------------------------------------------------------------------- /test/udp.test.ts: -------------------------------------------------------------------------------- 1 | import { TextEncoder } from "util"; 2 | import { expect } from "chai"; 3 | import { udpTester, getNextTestingPort } from "./utils"; 4 | 5 | describe("UDP specific tests", function () { 6 | udpTester.testPermutations((testing) => { 7 | it("can receiveFrom other UDP sockets", async function () { 8 | const sentPromise = testing.echo.events.sentData.once(); 9 | testing.netLink.sendTo( 10 | testing.host, 11 | testing.echo.getPort(), 12 | testing.str, 13 | ); 14 | void (await sentPromise); 15 | const read = testing.netLink.receiveFrom(); 16 | 17 | expect(read).to.exist; 18 | const isIPv4 = testing.ipVersion === "IPv4"; 19 | expect(read?.host).to.equal(isIPv4 ? "127.0.0.1" : "::1"); 20 | expect(read?.port).to.equal(testing.echo.getPort()); 21 | expect(read?.data.toString()).to.equal(testing.str); 22 | }); 23 | 24 | it("can receiveFrom nothing", function () { 25 | testing.netLink.isBlocking = false; 26 | const readFromNothing = testing.netLink.receiveFrom(); 27 | expect(readFromNothing).to.be.undefined; 28 | }); 29 | 30 | it("can sendTo other UDP sockets", async function () { 31 | const sentPromise = testing.echo.events.sentData.once(); 32 | testing.netLink.sendTo( 33 | testing.host, 34 | testing.echo.getPort(), 35 | testing.str, 36 | ); 37 | const sent = await sentPromise; 38 | 39 | expect(sent.from.port).to.equal(testing.netLink.portFrom); 40 | expect(sent.str).to.equal(testing.str); 41 | }); 42 | 43 | it("can sendTo with Buffers", async function () { 44 | const sentPromise = testing.echo.events.sentData.once(); 45 | const buffer = Buffer.from(testing.str); 46 | testing.netLink.sendTo( 47 | testing.host, 48 | testing.echo.getPort(), 49 | buffer, 50 | ); 51 | const sent = await sentPromise; 52 | 53 | expect(sent.buffer.compare(buffer)).to.equal(0); 54 | }); 55 | 56 | it("can sendTo with Uint8Arrays", async function () { 57 | const sentPromise = testing.echo.events.sentData.once(); 58 | const array = new TextEncoder().encode(testing.str); 59 | testing.netLink.sendTo( 60 | testing.host, 61 | testing.echo.getPort(), 62 | array, 63 | ); 64 | const sent = await sentPromise; 65 | 66 | expect(sent.str).to.equal(testing.str); 67 | }); 68 | 69 | it("can sendTo nothing", function () { 70 | expect(() => 71 | testing.netLink.sendTo( 72 | "localhost", 73 | getNextTestingPort(), // no one should be listening here 74 | "I scream into the void", 75 | ), 76 | ).not.to.throw(); 77 | }); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /test/utils/bad-arg.ts: -------------------------------------------------------------------------------- 1 | const noArg = Symbol("invalid arg"); 2 | 3 | /** 4 | * Gives back a bad argument for testing. 5 | * 6 | * @param arg - An argument override to unsafely type cast to T. 7 | * @returns A bad argument. Do not trust it! 8 | */ 9 | export function badArg(arg: unknown = noArg): T { 10 | return arg as T; 11 | } 12 | 13 | export type BadConstructor = { 14 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 15 | new (...args: any[]): unknown; 16 | }; 17 | 18 | export const badIPAddress = "192.0.2.0"; // invalid via RFC 5737 19 | -------------------------------------------------------------------------------- /test/utils/echo-socket.ts: -------------------------------------------------------------------------------- 1 | import { Event, events } from "ts-typed-events"; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 4 | export abstract class EchoSocket { 5 | public readonly events = events({ 6 | newConnection: new Event(), 7 | closedConnection: new Event<{ 8 | from: T; 9 | hadError: boolean; 10 | }>(), 11 | sentData: new Event<{ 12 | from: T; 13 | buffer: Buffer; 14 | str: string; 15 | }>(), 16 | }); 17 | 18 | public abstract start(data: { host: string; port: number }): Promise; 19 | public abstract stop(): Promise; 20 | } 21 | -------------------------------------------------------------------------------- /test/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bad-arg"; 2 | export * from "./echo-socket"; 3 | export * from "./permutations"; 4 | export * from "./tester"; 5 | export * from "./tester.tcp-client"; 6 | export * from "./tester.tcp-server"; 7 | export * from "./tester.udp"; 8 | -------------------------------------------------------------------------------- /test/utils/permutations.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable @typescript-eslint/no-unsafe-call */ 3 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 4 | 5 | export type ToUnion = T[number]; 6 | 7 | // Variadic types will simplify this greatly 8 | export function permutations(): []; 9 | export function permutations( 10 | a: A, 11 | ): [ToUnion][]; 12 | export function permutations< 13 | A extends readonly unknown[], 14 | B extends readonly unknown[] 15 | >(a: A, b: B): [ToUnion, ToUnion][]; 16 | export function permutations< 17 | A extends readonly unknown[], 18 | B extends readonly unknown[], 19 | C extends readonly unknown[] 20 | >(a: A, b: B, c: C): [ToUnion, ToUnion, ToUnion][]; 21 | export function permutations< 22 | A extends readonly unknown[], 23 | B extends readonly unknown[], 24 | C extends readonly unknown[], 25 | D extends readonly unknown[] 26 | >(a: A, b: B, c: C, d: D): [ToUnion, ToUnion, ToUnion, ToUnion][]; 27 | export function permutations< 28 | A extends readonly unknown[], 29 | B extends readonly unknown[], 30 | C extends readonly unknown[], 31 | D extends readonly unknown[], 32 | E extends readonly unknown[] 33 | >( 34 | a: A, 35 | b: B, 36 | c: C, 37 | d: D, 38 | e: E, 39 | ): [ToUnion, ToUnion, ToUnion, ToUnion, ToUnion][]; 40 | 41 | /** 42 | * Builds permutations of arrays, with generic type building. 43 | * 44 | * @param args - Arrays to build permutations from. 45 | * @returns An array of arrays, each sub array containing one of the possible 46 | * permutations from the passed in array values. 47 | */ 48 | export function permutations(...args: unknown[][]): unknown[][] { 49 | if (args.length === 0) { 50 | return []; 51 | } 52 | if (args.length === 1) { 53 | return args[0].map((a) => [a]); 54 | } 55 | 56 | const result = [] as unknown[][]; 57 | const [items, ...rest] = args; 58 | const sub: unknown[][] = (permutations as any)(...rest); 59 | for (const item of items) { 60 | for (const s of sub) { 61 | result.push([item, ...s]); 62 | } 63 | } 64 | 65 | return result; 66 | } 67 | -------------------------------------------------------------------------------- /test/utils/tester.tcp-client.ts: -------------------------------------------------------------------------------- 1 | import { Server, Socket as SocketTCP } from "net"; 2 | import { EchoSocket } from "./echo-socket"; 3 | import { SocketClientTCP } from "../../lib"; 4 | import { Tester } from "./tester"; 5 | 6 | /** 7 | * A simple Echo Server for testing. 8 | * Basically async/await syntax for clearer testing code. 9 | */ 10 | export class EchoClientTCP extends EchoSocket { 11 | private readonly server: Server; 12 | 13 | constructor() { 14 | super(); 15 | this.server = new Server((socket) => { 16 | socket.on("close", (hadError) => { 17 | this.events.closedConnection.emit({ from: socket, hadError }); 18 | }); 19 | 20 | // echo all data back 21 | socket.on("data", (buffer) => { 22 | const str = buffer.toString(); 23 | this.events.sentData.emit({ from: socket, buffer, str }); 24 | socket.write(buffer); 25 | }); 26 | 27 | this.events.newConnection.emit(socket); 28 | }); 29 | } 30 | 31 | public start(s: { port: number }): Promise { 32 | return new Promise((resolve) => this.server.listen(s.port, resolve)); 33 | } 34 | 35 | public stop(): Promise { 36 | return new Promise((resolve, reject) => 37 | this.server.close((err) => { 38 | if (err) { 39 | reject(err); 40 | } else { 41 | resolve(); 42 | } 43 | }), 44 | ); 45 | } 46 | 47 | public countConnections(): Promise { 48 | return new Promise((resolve, reject) => { 49 | this.server.getConnections((err, count) => { 50 | if (err) { 51 | reject(err); 52 | } else { 53 | resolve(count); 54 | } 55 | }); 56 | }); 57 | } 58 | } 59 | 60 | export const tcpClientTester = new Tester(SocketClientTCP, EchoClientTCP); 61 | -------------------------------------------------------------------------------- /test/utils/tester.tcp-server.ts: -------------------------------------------------------------------------------- 1 | import { AddressInfo, Socket as SocketTCP } from "net"; 2 | import { EchoSocket } from "./echo-socket"; 3 | import { Tester } from "./tester"; 4 | import { SocketServerTCP } from "../../lib"; 5 | 6 | /** 7 | * A simple Echo Server for testing. 8 | * Basically async/await syntax for clearer testing code. 9 | */ 10 | export class EchoServerTCP extends EchoSocket { 11 | private socket!: SocketTCP; 12 | 13 | public start(data: { 14 | host: string; 15 | port: number; 16 | ipVersion?: "IPv4" | "IPv6"; 17 | }): Promise { 18 | return new Promise((resolve) => { 19 | this.socket = new SocketTCP(); 20 | this.socket.on("data", (buffer) => { 21 | const address = this.socket.address(); 22 | if (typeof address === "string") { 23 | throw new Error("Running on too old a Node version!"); 24 | } 25 | this.events.newConnection.emit(address); 26 | this.socket.write(buffer, () => { 27 | this.events.sentData.emit({ 28 | from: address, 29 | buffer, 30 | str: buffer.toString(), 31 | }); 32 | this.events.closedConnection.emit({ 33 | from: address, 34 | hadError: false, 35 | }); 36 | }); 37 | }); 38 | this.socket.connect( 39 | { 40 | host: data.host, 41 | port: data.port, 42 | family: data.ipVersion === "IPv6" ? 6 : 4, 43 | }, 44 | resolve, 45 | ); 46 | }); 47 | } 48 | 49 | public stop(): Promise { 50 | return new Promise((resolve) => { 51 | this.socket.on("close", resolve); 52 | this.socket.destroy(); 53 | }); 54 | } 55 | } 56 | 57 | export const tcpServerTester = new Tester(SocketServerTCP, EchoServerTCP, { 58 | startEchoAfterNetLink: true, 59 | newPermute: ["host"], 60 | }); 61 | -------------------------------------------------------------------------------- /test/utils/tester.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable mocha/no-exports */ 2 | import { EchoSocket } from "./echo-socket"; 3 | import { SocketBase } from "../../lib"; 4 | import { permutations } from "./permutations"; 5 | import { badArg } from "./bad-arg"; 6 | 7 | type Writeable = { -readonly [P in keyof T]: T[P] }; 8 | 9 | const MAX_PORT = 65535; 10 | let nextPort = 50_000; 11 | 12 | /** 13 | * Gets the next testing port. Use this to ensure each test has a unique port. 14 | * 15 | * @returns A number representing a port to test on. 16 | */ 17 | export function getNextTestingPort(): number { 18 | const result = nextPort; 19 | nextPort += 1; 20 | if (nextPort > 60_000) { 21 | // something went really wrong 22 | throw new Error("Too many ports used!"); 23 | } 24 | 25 | return result; 26 | } 27 | 28 | const seenIds = new Set(); 29 | 30 | const getTestingString = (context: Mocha.Context): string => { 31 | const id = context.currentTest?.fullTitle(); 32 | if (!id) { 33 | throw new Error(`Cannot get full title for ${String(context)}`); 34 | } 35 | 36 | if (seenIds.has(id)) { 37 | throw new Error(`Duplicate test id detected: '${id}'`); 38 | } else { 39 | seenIds.add(id); 40 | } 41 | 42 | return id; 43 | }; 44 | 45 | const prettyString = (arg: unknown) => 46 | typeof arg === "string" 47 | ? arg === "number" || arg === "string" 48 | ? arg 49 | : `"${arg}"` 50 | : String(arg); 51 | 52 | /** 53 | * Prettifies with args for human reading. 54 | * 55 | * @param port - The port being used. 56 | * @param host - The host being used. 57 | * @param ipVersion - The ipVersion being used. 58 | * @returns A string for use in Mocha descriptions about what with. 59 | */ 60 | const prettyWithDescription = ( 61 | port: unknown, 62 | host: unknown, 63 | ipVersion: unknown, 64 | ): string => 65 | `with 66 | port: ${prettyString(port)}, 67 | host: ${prettyString(host)}, 68 | ip: ${prettyString(ipVersion)} 69 | `.replace(/\n/g, ""); 70 | 71 | export type BaseTesting< 72 | TNetLink extends SocketBase, 73 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 74 | TEchoSocket extends EchoSocket 75 | > = Readonly<{ 76 | host: string; 77 | port: number; 78 | ipVersion: "IPv4" | "IPv6"; 79 | constructorArgs: { 80 | host: string | undefined; 81 | port: number | undefined; 82 | ipVersion: "IPv4" | "IPv6" | undefined; 83 | }; 84 | str: string; 85 | netLink: TNetLink; 86 | echo: TEchoSocket; 87 | settableNetLink: Writeable; 88 | }>; 89 | 90 | export class Tester< 91 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 92 | TNetLinkClass extends { new (...args: any[]): SocketBase }, 93 | TEchoSocketClass extends { 94 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 95 | new (...args: any[]): TEchoSocket; 96 | }, 97 | TNetLink extends SocketBase = InstanceType, 98 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 99 | TEchoSocket extends EchoSocket = InstanceType, 100 | TTesting extends BaseTesting = BaseTesting< 101 | TNetLink, 102 | TEchoSocket 103 | > 104 | > { 105 | private readonly constructorArgs: [ 106 | boolean, 107 | boolean, 108 | "IPv4" | "IPv6" | undefined, 109 | ][]; 110 | private readonly invalidConstructorArgs: [unknown, unknown, unknown][]; 111 | private readonly startEchoAfterNetLink: boolean; 112 | private readonly alsoBeforeEach?: (testing: TTesting) => Promise; 113 | public readonly testing = badArg(); 114 | 115 | constructor( 116 | public readonly NetLinkClass: TNetLinkClass, 117 | private readonly EchoSocketClass: TEchoSocketClass, 118 | options?: { 119 | newPermute?: ("port" | "host")[]; 120 | startEchoAfterNetLink?: boolean; 121 | alsoBeforeEach?: (testing: TTesting) => Promise; 122 | }, 123 | ) { 124 | this.startEchoAfterNetLink = Boolean(options?.startEchoAfterNetLink); 125 | this.alsoBeforeEach = options?.alsoBeforeEach; 126 | 127 | const permutePort = options?.newPermute?.includes("port"); 128 | const permuteHost = options?.newPermute?.includes("host"); 129 | this.constructorArgs = permutations( 130 | ...([ 131 | permutePort ? [true, false] : [true], 132 | permuteHost ? [true, false] : [true], 133 | ["IPv4", "IPv6", undefined] as const, 134 | ] as const), 135 | ); 136 | 137 | const validPort = 12345; 138 | const isValid = new Set( 139 | this.constructorArgs.map(([usePort, useHost, ipVersion]) => 140 | JSON.stringify( 141 | [ 142 | usePort ? validPort : undefined, 143 | useHost ? "localhost" : undefined, 144 | ipVersion, 145 | ].map(String), 146 | ), 147 | ), 148 | ); 149 | 150 | this.invalidConstructorArgs = permutations( 151 | [validPort, -1337, 0, MAX_PORT + 1, undefined, badArg()], 152 | ["localhost", undefined, badArg()], 153 | ["IPv4", "IPv6", undefined, "some string", badArg()], 154 | ).filter((args) => !isValid.has(JSON.stringify(args.map(String)))); 155 | } 156 | 157 | public permutations( 158 | descriptionPrefix: string, 159 | callback: ( 160 | args: { 161 | usePort: boolean; 162 | useHost: boolean; 163 | ipVersion: "IPv4" | "IPv6" | undefined; 164 | }, 165 | suite: Mocha.Suite, 166 | ) => void, 167 | ): void { 168 | const { constructorArgs } = this; 169 | const description = [ 170 | descriptionPrefix, 171 | this.NetLinkClass.name, 172 | "permutations", 173 | ] 174 | .filter(Boolean) 175 | .join(" "); 176 | describe(description, function () { 177 | for (const [usePort, useHost, ipVersion] of constructorArgs) { 178 | const withDescription = prettyWithDescription( 179 | usePort ? "number" : undefined, 180 | useHost ? "string" : undefined, 181 | ipVersion, 182 | ); 183 | describe(withDescription, function () { 184 | callback({ usePort, useHost, ipVersion }, this); 185 | }); 186 | } 187 | }); 188 | } 189 | 190 | public testPermutations(callback: (testing: TTesting) => void): void { 191 | const { alsoBeforeEach, NetLinkClass, startEchoAfterNetLink } = this; 192 | this.permutations("", ({ usePort, useHost, ipVersion }, suite) => { 193 | const testing = { 194 | host: "localhost", 195 | port: 1, 196 | ipVersion: ipVersion || "IPv4", 197 | constructorArgs: { 198 | host: useHost ? "localhost" : undefined, 199 | port: usePort ? 1 : undefined, 200 | ipVersion, 201 | }, 202 | str: "", 203 | netLink: (null as unknown) as TNetLink, 204 | echo: new this.EchoSocketClass(), 205 | settableNetLink: (null as unknown) as Writeable, 206 | } as Writeable; 207 | 208 | suite.beforeEach(async function () { 209 | testing.str = getTestingString(this); 210 | testing.port = getNextTestingPort(); 211 | if (testing.constructorArgs.port) { 212 | testing.constructorArgs.port = testing.port; 213 | } 214 | 215 | if (!startEchoAfterNetLink) { 216 | await testing.echo.start(testing); 217 | } 218 | testing.netLink = new NetLinkClass( 219 | testing.constructorArgs.port, 220 | testing.constructorArgs.host, 221 | testing.constructorArgs.ipVersion, 222 | ) as TNetLink; 223 | 224 | testing.settableNetLink = testing.netLink; 225 | if (startEchoAfterNetLink) { 226 | await testing.echo.start(testing); 227 | } 228 | 229 | if (alsoBeforeEach) { 230 | await alsoBeforeEach(testing); 231 | } 232 | }); 233 | 234 | suite.afterEach(async () => { 235 | if (testing.netLink && !testing.netLink.isDestroyed) { 236 | testing.netLink.disconnect(); 237 | } 238 | await testing.echo.stop(); 239 | }); 240 | 241 | callback(testing); 242 | }); 243 | } 244 | 245 | public testInvalidPermutations( 246 | callback: (port: unknown, host: unknown, ipVersion: unknown) => void, 247 | ): void { 248 | const desc = `for ${this.NetLinkClass.name} invalid constructor args`; 249 | const { invalidConstructorArgs } = this; 250 | describe(desc, function () { 251 | for (const args of invalidConstructorArgs) { 252 | describe(prettyWithDescription(...args), function () { 253 | callback(...args); 254 | }); 255 | } 256 | }); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /test/utils/tester.udp.ts: -------------------------------------------------------------------------------- 1 | import { createSocket, RemoteInfo } from "dgram"; 2 | import { EchoSocket } from "./echo-socket"; 3 | import { SocketUDP } from "../../lib"; 4 | import { Tester, getNextTestingPort } from "./tester"; 5 | 6 | // upd6 will accept IPv4/6 connections so it is ideal for testing with 7 | const newUDP = () => createSocket({ type: "udp6" }); 8 | 9 | export class EchoUDP extends EchoSocket { 10 | private socket = newUDP(); 11 | 12 | private port = 1; 13 | public getPort(): number { 14 | return this.port; 15 | } 16 | 17 | public start(): Promise { 18 | return new Promise((resolve) => { 19 | // eslint-disable-next-line @typescript-eslint/no-misused-promises 20 | this.socket.on("message", async (buffer, remote) => { 21 | // UDP does not form true connections, so we will "fake" it 22 | this.events.newConnection.emit(remote); 23 | 24 | // echo it back 25 | await new Promise((res, rej) => 26 | this.socket.send( 27 | buffer, 28 | remote.port, 29 | remote.address, 30 | (err, val) => { 31 | if (err) { 32 | rej(err); 33 | } else { 34 | res(val); 35 | } 36 | }, 37 | ), 38 | ); 39 | 40 | this.events.sentData.emit({ 41 | from: remote, 42 | buffer, 43 | str: buffer.toString(), 44 | }); 45 | 46 | this.events.closedConnection.emit({ 47 | from: remote, 48 | hadError: false, 49 | }); 50 | }); 51 | 52 | this.port = getNextTestingPort(); 53 | this.socket.bind(this.port, resolve); 54 | }); 55 | } 56 | 57 | public stop(): Promise { 58 | return new Promise((resolve) => { 59 | this.socket.close(() => { 60 | this.socket = newUDP(); // current socket is done, need a new one for next use 61 | resolve(); 62 | }); 63 | }); 64 | } 65 | } 66 | 67 | export const udpTester = new Tester(SocketUDP, EchoUDP, { 68 | newPermute: ["host", "port"], 69 | }); 70 | -------------------------------------------------------------------------------- /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": [ 4 | ".eslintrc.js", 5 | "test/**/*.ts", 6 | "test/**/*.js", 7 | "test/**/.eslintrc.js", 8 | "lib/**/*.ts", 9 | ], 10 | "exclude": [ 11 | "lib/**/*.js", 12 | "docs/**", 13 | ], 14 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "module": "commonjs", 5 | "strict": true, 6 | "lib": [], 7 | }, 8 | } --------------------------------------------------------------------------------