├── .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 | }
--------------------------------------------------------------------------------