├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── LICENSE
├── README.md
├── async_fec.cpp
├── async_fec.h
├── build.sh
├── clear.sh
├── config.cpp
├── config.h
├── encoding.h
├── encrypt.cpp
├── encrypt.h
├── fec.cpp
├── fec.h
├── frame.h
├── galois.cpp
├── galois.h
├── galois_noasm.cpp
├── galois_noasm.h
├── galois_table.c
├── inversion_tree.cpp
├── inversion_tree.h
├── kcptun_client.cpp
├── kcptun_client.h
├── kcptun_client_main.cpp
├── kcptun_server.cpp
├── kcptun_server.h
├── kcptun_server_main.cpp
├── local.cpp
├── local.h
├── matrix.cpp
├── matrix.h
├── reedsolomon.cpp
├── reedsolomon.h
├── server.cpp
├── server.h
├── sess.cpp
├── sess.h
├── smux.cpp
├── smux.h
├── snappy_stream.cpp
├── snappy_stream.h
├── utils.cpp
└── utils.h
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files
2 | *.slo
3 | *.lo
4 | *.o
5 | *.obj
6 |
7 | # Precompiled Headers
8 | *.gch
9 | *.pch
10 |
11 | # Compiled Dynamic libraries
12 | *.so
13 | *.dylib
14 | *.dll
15 |
16 | # Fortran module files
17 | *.mod
18 | *.smod
19 |
20 | # Compiled Static libraries
21 | *.lai
22 | *.la
23 | *.a
24 | *.lib
25 |
26 | # Executables
27 | *.exe
28 | *.out
29 | *.app
30 | .vscode
31 | .idea
32 | *.json
33 | .idea
34 | .vscode
35 | CMakeFiles
36 | CMakeCache.txt
37 | Makefile
38 | cmake_install.cmake
39 | kcptun
40 | kcptun_client
41 | kcptun_server
42 | tags
43 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "snappy"]
2 | path = snappy
3 | url = https://github.com/google/snappy
4 | [submodule "cryptopp"]
5 | path = cryptopp
6 | url = https://github.com/weidai11/cryptopp
7 | [submodule "asio"]
8 | path = asio
9 | url = https://github.com/chriskohlhoff/asio
10 | [submodule "rapidjson"]
11 | path = rapidjson
12 | url = https://github.com/Tencent/rapidjson
13 | [submodule "gflags"]
14 | path = gflags
15 | url = https://github.com/gflags/gflags
16 | [submodule "glog"]
17 | path = glog
18 | url = https://github.com/google/glog
19 | [submodule "kcp"]
20 | path = kcp
21 | url = https://github.com/skywind3000/kcp
22 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.6)
2 | project(kcptun)
3 |
4 | set(CMAKE_CXX_STANDARD 11)
5 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O2 -DASIO_STANDALONE")
6 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
7 | include_directories("${CMAKE_SOURCE_DIR}/asio/asio/include")
8 | include_directories("${CMAKE_SOURCE_DIR}/rapidjson/include/rapidjson")
9 | include_directories("${CMAKE_SOURCE_DIR}/gflags/include")
10 | include_directories("${CMAKE_SOURCE_DIR}/snappy")
11 | include_directories("${CMAKE_SOURCE_DIR}/kcp")
12 |
13 | add_subdirectory("glog")
14 | add_subdirectory("gflags")
15 | add_subdirectory("snappy")
16 | add_subdirectory("kcp")
17 |
18 | set(SOURCE_FILES
19 | sess.cpp
20 | sess.h
21 | utils.h
22 | utils.cpp
23 | frame.h
24 | smux.h
25 | smux.cpp
26 | encrypt.h
27 | encrypt.cpp
28 | config.h
29 | config.cpp
30 | snappy_stream.cpp
31 | snappy_stream.h
32 | fec.cpp
33 | fec.h
34 | galois.cpp
35 | galois.h
36 | galois_noasm.cpp
37 | galois_noasm.h
38 | galois_table.c
39 | inversion_tree.cpp
40 | inversion_tree.h
41 | matrix.cpp
42 | matrix.h
43 | reedsolomon.cpp
44 | reedsolomon.h
45 | async_fec.cpp
46 | async_fec.h)
47 |
48 | set(KCPTUN_CLIENT_SOURCE_FILES ${SOURCE_FILES}
49 | kcptun_client_main.cpp
50 | kcptun_client.h
51 | kcptun_client.cpp
52 | local.cpp
53 | local.h)
54 |
55 | set(KCPTUN_SERVER_SOURCE_FILES ${SOURCE_FILES}
56 | kcptun_server_main.cpp
57 | kcptun_server.h
58 | kcptun_server.cpp
59 | server.cpp
60 | server.h)
61 |
62 | add_executable(kcptun_client ${KCPTUN_CLIENT_SOURCE_FILES})
63 | target_link_libraries(kcptun_client gflags)
64 | target_link_libraries(kcptun_client glog)
65 | target_link_libraries(kcptun_client snappy)
66 | target_link_libraries(kcptun_client kcp)
67 | target_link_libraries(kcptun_client z)
68 | if(UNIX)
69 | target_link_libraries(kcptun_client "${CMAKE_SOURCE_DIR}/cryptopp/libcryptopp.a")
70 | target_link_libraries(kcptun_client -static-libstdc++)
71 | target_link_libraries(kcptun_client pthread)
72 | else()
73 | target_link_libraries(kcptun_client "${CMAKE_SOURCE_DIR}/cryptopp/cryptlib.lib")
74 | endif()
75 |
76 | add_executable(kcptun_server ${KCPTUN_SERVER_SOURCE_FILES})
77 | target_link_libraries(kcptun_server gflags)
78 | target_link_libraries(kcptun_server glog)
79 | target_link_libraries(kcptun_server snappy)
80 | target_link_libraries(kcptun_server kcp)
81 | target_link_libraries(kcptun_server z)
82 | if(UNIX)
83 | target_link_libraries(kcptun_server "${CMAKE_SOURCE_DIR}/cryptopp/libcryptopp.a")
84 | target_link_libraries(kcptun_server -static-libstdc++)
85 | target_link_libraries(kcptun_server pthread)
86 | else()
87 | target_link_libraries(kcptun_server "${CMAKE_SOURCE_DIR}/cryptopp/cryptlib.lib")
88 | endif()
89 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Name
2 | ====
3 |
4 | kcptun-asio -- A Secure Tunnel Based On KCP with N:M Multiplexing
5 | kcptun-asio is based on C++11 and Asio, fully compatible with [kcptun(go)](https://github.com/xtaci/kcptun)
6 |
7 | Synopsis
8 | ========
9 |
10 | ```
11 | $ ./kcptun_client -l :6666 -r xx:xx:xx:xx:yy --key password --crypt aes --mtu 1200 --ds 20 --ps 10 --nocomp
12 | $ ./kcptun_server -l :7777 -t xx:xx:xx:xx:yy --key password --crypt aes --mtu 1200 --ds 20 --ps 10 --nocomp
13 | ```
14 |
15 | Features
16 | ========
17 |
18 | * reliable data transfering based on kcp protocol
19 | * support aes*/xor/xtea/none/cast5/blowfish/twofish/3des/salsa20 encryption
20 | * multiplexing
21 | * snappy streaming compression and decompression,based on [google/snappy](https://github.com/google/snappy).The data frame format is [frame_format](https://github.com/google/snappy/blob/master/framing_format.txt)
22 | * forward error correction
23 | * lower resource consumption
24 |
25 | Build
26 | =====
27 |
28 | Prerequisites
29 | -------------
30 |
31 | 1. [asio](https://github.com/chriskohlhoff/asio)
32 | 2. [cryptopp](https://github.com/weidai11/cryptopp)
33 | 3. [snappy](https://github.com/google/snappy)
34 |
35 | Unix-like system
36 | ----------------
37 | 1. Get the latest code
38 | ```
39 | $ git clone https://github.com/ccsexyz/kcptun-asio.git
40 | ```
41 | 2. Run build.sh
42 | ```
43 | $ ./build.sh
44 | ```
45 |
46 | odd Windows
47 | -----------
48 |
49 | Fuck MSVC.
50 |
51 | TODO
52 | ====
53 |
54 | * performance optimization(memory optimization & CPU optimization)
55 | * ~~improve smux~~
56 |
--------------------------------------------------------------------------------
/async_fec.cpp:
--------------------------------------------------------------------------------
1 | #include "async_fec.h"
2 | #include "config.h"
3 | #include "fec.h"
4 |
5 | AsyncFECInputer::AsyncFECInputer(OutputHandler o)
6 | : AsyncInOutputer(o),
7 | fec_(my_make_unique(
8 | FEC::New(3 * (FLAGS_datashard + FLAGS_parityshard), FLAGS_datashard, FLAGS_parityshard))) {}
9 |
10 | void AsyncFECInputer::output_recovered(
11 | std::size_t len, std::shared_ptr> recovered,
12 | Handler handler) {
13 | if (!recovered || recovered->size() == 0) {
14 | if (handler) {
15 | handler(std::error_code(0, std::generic_category()), len);
16 | }
17 | return;
18 | }
19 | auto first = recovered->begin();
20 | auto r = *first;
21 | recovered->erase(first);
22 | if (r->size() <= 2) {
23 | output_recovered(len, recovered, handler);
24 | return;
25 | }
26 | auto ptr = r->data();
27 | uint16_t sz;
28 | decode16u(ptr, &sz);
29 | if (sz < 2 || sz > r->size()) {
30 | output_recovered(len, recovered, handler);
31 | return;
32 | }
33 | output((char *)(ptr + 2), sz - 2,
34 | [this, len, recovered, handler](std::error_code ec, std::size_t) {
35 | if (ec) {
36 | if (handler) {
37 | handler(ec, len);
38 | }
39 | return;
40 | }
41 | output_recovered(len, recovered, handler);
42 | });
43 | }
44 |
45 | void AsyncFECInputer::async_input(char *buf, std::size_t len, Handler handler) {
46 | auto pkt = fec_->Decode((byte *)buf, len);
47 | if (pkt.flag != typeData && pkt.flag != typeFEC) {
48 | return;
49 | }
50 | auto f = [pkt, handler, len, this](std::error_code ec,
51 | std::size_t) mutable {
52 | if (ec) {
53 | if (handler) {
54 | handler(ec, len);
55 | }
56 | return;
57 | }
58 | auto recovered =
59 | std::make_shared>(fec_->Input(pkt));
60 | output_recovered(len, recovered, handler);
61 | };
62 | if (pkt.flag == typeData) {
63 | auto ptr = pkt.data->data();
64 | output((char *)(ptr + 2), pkt.data->size() - 2, f);
65 | } else {
66 | f(std::error_code(0, std::generic_category()), len);
67 | }
68 | }
69 |
70 | static Buffers fec_buffers(2048);
71 |
72 | static Buffers fec_header_buffers;
73 |
74 | static char *get_fec_header() {
75 | static ConstructCaller nopCaller([](){
76 | fec_header_buffers.reset(fecHeaderSize*FLAGS_parityshard);
77 | });
78 | return fec_header_buffers.get();
79 | }
80 |
81 | static void push_fec_header_back(char *buf) {
82 | fec_header_buffers.push_back(buf);
83 | }
84 |
85 |
86 |
87 | AsyncFECOutputer::AsyncFECOutputer(OutputHandler o)
88 | : AsyncInOutputer(o),
89 | fec_(my_make_unique(
90 | FEC::New(3 * (FLAGS_datashard + FLAGS_parityshard), FLAGS_datashard, FLAGS_parityshard))),
91 | shards_(my_make_unique>(FLAGS_datashard + FLAGS_parityshard,
92 | nullptr)) {}
93 |
94 | void AsyncFECOutputer::async_input(char *buf, std::size_t len,
95 | Handler handler) {
96 | memcpy(buf_ + fecHeaderSizePlus2, buf, len);
97 | fec_->MarkData(buf_, len + fecHeaderSizePlus2);
98 | auto slen = len + 2;
99 | assert(pkt_idx_ < (FLAGS_datashard + FLAGS_parityshard));
100 | (*shards_)[pkt_idx_] = std::make_shared>(
101 | &(buf_[fecHeaderSize]), &(buf_[fecHeaderSize + slen]));
102 | pkt_idx_++;
103 | if (pkt_idx_ < FLAGS_datashard) {
104 | output((char *)buf_, len + fecHeaderSizePlus2,
105 | [this, len, handler](std::error_code ec, std::size_t) {
106 | if (handler) {
107 | handler(ec, len);
108 | }
109 | return;
110 | });
111 | return;
112 | }
113 | pkt_idx_ = 0;
114 | fec_->Encode(*shards_);
115 | char *buffer = fec_buffers.get();
116 | std::vector shards(FLAGS_parityshard, nullptr);
117 | char *fec_headers = get_fec_header();
118 | for (int i = 0; i < FLAGS_parityshard; i++) {
119 | shards[i] = (*shards_)[FLAGS_datashard + i];
120 | (*shards_)[FLAGS_datashard + i] = nullptr;
121 | fec_->MarkFEC((byte *)(fec_headers + fecHeaderSize * i));
122 | }
123 | output((char *)buf_, len + fecHeaderSizePlus2,
124 | [this, len, handler, buffer, fec_headers, shards](std::error_code ec,
125 | std::size_t) {
126 | DeferCaller defer([buffer, fec_headers, ec, handler, len] {
127 | fec_buffers.push_back(buffer);
128 | push_fec_header_back(fec_headers);
129 | if (handler) {
130 | handler(ec, len);
131 | }
132 | });
133 | for (int i = 0; i < FLAGS_parityshard; i++) {
134 | memcpy(buffer, fec_headers + i * fecHeaderSize,
135 | fecHeaderSize);
136 | memcpy(buffer + fecHeaderSize, shards[i]->data(),
137 | shards[i]->size());
138 | output(buffer, shards[i]->size() + fecHeaderSize, nullptr);
139 | }
140 | });
141 | }
--------------------------------------------------------------------------------
/async_fec.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_ASYNC_FEC
2 | #define KCPTUN_ASYNC_FEC
3 |
4 | #include "utils.h"
5 | #include "fec.h"
6 |
7 | class AsyncFECInputer : public AsyncInOutputer {
8 | public:
9 | AsyncFECInputer(OutputHandler o = nullptr);
10 | void async_input(char *buf, std::size_t len, Handler handler) override;
11 | void output_recovered(std::size_t len, std::shared_ptr> recovered, Handler handler);
12 |
13 | private:
14 | std::unique_ptr fec_;
15 | };
16 |
17 | class AsyncFECOutputer : public AsyncInOutputer {
18 | public:
19 | AsyncFECOutputer(OutputHandler o = nullptr);
20 | void async_input(char *buf, std::size_t len, Handler handler) override;
21 |
22 | private:
23 | byte buf_[2048];
24 | uint32_t pkt_idx_ = 0;
25 | std::unique_ptr fec_;
26 | std::unique_ptr> shards_;
27 | };
28 |
29 | #endif
30 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | N=1
3 | if [ $# -gt 0 ]; then
4 | N=$1
5 | fi
6 | git submodule update --init --recursive
7 |
8 | cd cryptopp
9 | make clean
10 | make libcryptopp.a "-j$N"
11 | cd ..
12 |
13 | rm CMakeCache.txt
14 | cmake .
15 | make clean
16 | make kcptun_client "-j$N"
17 | make kcptun_server "-j$N"
18 |
19 |
--------------------------------------------------------------------------------
/clear.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd gflags
4 | git checkout . && git clean -xdf
5 | cd ..
6 |
7 | cd snappy
8 | git checkout . && git clean -xdf
9 | cd ..
10 |
11 | cd cryptopp
12 | git checkout . && git clean -xdf
13 | cd ..
14 |
15 | cd glog
16 | git checkout . && git clean -xdf
17 | cd ..
18 |
19 | cd kcp
20 | git checkout . && git clean -xdf
21 | cd ..
22 |
23 | make clean
24 | rm CMakeCache.txt
25 | rm *.cmake
26 |
--------------------------------------------------------------------------------
/config.cpp:
--------------------------------------------------------------------------------
1 | #include "config.h"
2 | #include "rapidjson.h"
3 | #include "document.h"
4 | #include "utils.h"
5 |
6 | DEFINE_string(localaddr, ":12948", "local listen address");
7 | DEFINE_string(remoteaddr, ":29900", "kcp server address");
8 | DEFINE_string(targetaddr, ":29900", "target server address");
9 | DEFINE_string(l, "", "alias for localaddr");
10 | DEFINE_string(r, "", "alias for remoteaddr");
11 | DEFINE_string(t, "", "alias for targetaddr");
12 | DEFINE_string(c, "", "config from json file, which will override the command from shell");
13 | DEFINE_string(key, "it's a secret", "pre-shared secret between client and server");
14 | DEFINE_string(crypt, "aes", "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none");
15 | DEFINE_string(mode, "fast", "profiles: fast3, fast2, fast, normal");
16 | DEFINE_string(logfile, "", "specify a log file to output, default goes to stdout");
17 |
18 | DEFINE_int32(conn, 1, "set num of UDP connections to server");
19 | DEFINE_int32(autoexpire, 0, "set auto expiration time(in seconds) for a single UDP connection, 0 to disable");
20 | DEFINE_int32(mtu, 1350, "set maximum transmission unit for UDP packets");
21 | DEFINE_int32(scavengettl, 600, "set how long an expired connection can live(in sec), -1 to disable");
22 | DEFINE_int32(sndwnd, 128, "set send window size(num of packets)");
23 | DEFINE_int32(rcvwnd, 512, "set receive window size(num of packets)");
24 | DEFINE_int32(datashard, 10, "set reed-solomon erasure coding - datashard");
25 | DEFINE_int32(parityshard, 3, "set reed-solomon erasure coding - parityshard");
26 | DEFINE_int32(ds, -1, "alias for datashard");
27 | DEFINE_int32(ps, -1, "alias for parityshard");
28 | DEFINE_int32(dscp, 0, "set dscp(6bit)");
29 | DEFINE_int32(nodelay, 1, "");
30 | DEFINE_int32(resend, 1, "");
31 | DEFINE_int32(nc, 0, "");
32 | DEFINE_int32(interval, 40, "");
33 | DEFINE_int32(sockbuf, 4194304, "socket buffer size");
34 | DEFINE_int32(keepalive, 10, "keepalive interval in seconds");
35 |
36 | DEFINE_bool(nocomp, false, "disable compression");
37 | DEFINE_bool(acknodelay, true, "flush ack immediately when a packet is received");
38 | DEFINE_bool(kvar, false, "run default kvar printer");
39 |
40 | using namespace rapidjson;
41 |
42 | void print_configs() {
43 | char buffer[1024];
44 | snprintf(buffer, 1024, "listening on: %s\n"
45 | "encryption: %s\n"
46 | "nodelay parameters: %d %d %d %d\n"
47 | "remote address: %s\n"
48 | "target address: %s\n"
49 | "sndwnd: %d rcvwnd: %d\n"
50 | "compression: %s\n"
51 | "mtu: %d\n"
52 | "datashard: %d parityshard: %d\n"
53 | "acknodelay: %s\n"
54 | "dscp: %d\n"
55 | "sockbuf: %d\n"
56 | "keepalive: %d\n"
57 | "conn: %d\n"
58 | "autoexpire: %d\n"
59 | "scavengettl: %d\n",
60 | FLAGS_localaddr.c_str(),
61 | FLAGS_crypt.c_str(),
62 | FLAGS_nodelay, FLAGS_interval, FLAGS_resend, FLAGS_nc,
63 | FLAGS_remoteaddr.c_str(),
64 | FLAGS_targetaddr.c_str(),
65 | FLAGS_sndwnd, FLAGS_rcvwnd, get_bool_str(!FLAGS_nocomp), FLAGS_mtu,
66 | FLAGS_datashard, FLAGS_parityshard, get_bool_str(FLAGS_acknodelay), FLAGS_dscp, FLAGS_sockbuf,
67 | FLAGS_keepalive, FLAGS_conn, FLAGS_autoexpire, FLAGS_scavengettl);
68 | LOG(INFO) << buffer;
69 | }
70 |
71 | static bool
72 | check_zero_cstr(const char *cstr)
73 | {
74 | if (cstr == NULL || strlen(cstr) == 0) {
75 | return true;
76 | }
77 | return false;
78 | }
79 |
80 | static void
81 | env_assign_bool(char *src, void *dst)
82 | {
83 | *(bool *)dst = true;
84 | }
85 |
86 | static void
87 | env_assign_int32(char *src, void *dst)
88 | {
89 | *(google::int32*)dst = std::atoi(src);
90 | }
91 |
92 | static void
93 | env_assign_string(char *src, void *dst)
94 | {
95 | *(std::string *)dst = src;
96 | }
97 |
98 | static std::unordered_map>> env_assigners = {
99 | {"localaddr", std::make_tuple(&FLAGS_localaddr, env_assign_bool)},
100 | {"remoteaddr", std::make_tuple(&FLAGS_remoteaddr, env_assign_string)},
101 | {"targetaddr", std::make_tuple(&FLAGS_targetaddr, env_assign_string)},
102 | {"key", std::make_tuple(&FLAGS_key, env_assign_string)},
103 | {"crypt", std::make_tuple(&FLAGS_crypt, env_assign_string)},
104 | {"logfile", std::make_tuple(&FLAGS_logfile, env_assign_string)},
105 | {"mode", std::make_tuple(&FLAGS_mode, env_assign_string)},
106 |
107 | {"conn", std::make_tuple(&FLAGS_conn, env_assign_int32)},
108 | {"autoexpire", std::make_tuple(&FLAGS_autoexpire, env_assign_int32)},
109 | {"mtu", std::make_tuple(&FLAGS_mtu, env_assign_int32)},
110 | {"sndwnd", std::make_tuple(&FLAGS_sndwnd, env_assign_int32)},
111 | {"rcvwnd", std::make_tuple(&FLAGS_rcvwnd, env_assign_int32)},
112 | {"scavengettl", std::make_tuple(&FLAGS_scavengettl, env_assign_int32)},
113 | {"datashard", std::make_tuple(&FLAGS_datashard, env_assign_int32)},
114 | {"parityshard", std::make_tuple(&FLAGS_parityshard, env_assign_int32)},
115 | {"nodelay", std::make_tuple(&FLAGS_nodelay, env_assign_int32)},
116 | {"resend", std::make_tuple(&FLAGS_resend, env_assign_int32)},
117 | {"nc", std::make_tuple(&FLAGS_nc, env_assign_int32)},
118 | {"interval", std::make_tuple(&FLAGS_interval, env_assign_int32)},
119 | {"sockbuf", std::make_tuple(&FLAGS_sockbuf, env_assign_int32)},
120 | {"keepalive", std::make_tuple(&FLAGS_keepalive, env_assign_int32)},
121 |
122 | {"nocomp", std::make_tuple(&FLAGS_nocomp, env_assign_bool)},
123 | {"acknodelay", std::make_tuple(&FLAGS_acknodelay, env_assign_bool)},
124 | {"kvar", std::make_tuple(&FLAGS_kvar, env_assign_bool)},
125 | };
126 |
127 | static void
128 | parse_plugin_option(char *plugin_option)
129 | {
130 | char *key = strtok(plugin_option, "=");
131 | if (check_zero_cstr(key)) {
132 | return;
133 | }
134 | char *value = strtok(NULL, "=");
135 |
136 | auto it = env_assigners.find(key);
137 | if (it == env_assigners.end()) {
138 | return;
139 | }
140 |
141 | void *dst = std::get<0>(it->second);
142 | auto &caller = std::get<1>(it->second);
143 | caller(value, dst);
144 | }
145 |
146 | static void
147 | parse_plugin_options(char *plugin_options)
148 | {
149 | char *str = NULL;
150 | std::vector option_strs;
151 | // note: strtok isn't thread-safe, this function shouldn't
152 | // be used in multi-threaded environment
153 | while ((str = strtok(plugin_options, ";")) != NULL) {
154 | plugin_options = NULL;
155 | option_strs.emplace_back(str);
156 | }
157 |
158 | for (auto str : option_strs) {
159 | parse_plugin_option(str);
160 | }
161 | }
162 |
163 | // for SIP003
164 | static void
165 | parse_config_from_env()
166 | {
167 | const char *remote_host = std::getenv("SS_REMOTE_HOST");
168 | const char *remote_port = std::getenv("SS_REMOTE_PORT");
169 | const char *local_host = std::getenv("SS_LOCAL_HOST");
170 | const char *local_port = std::getenv("SS_LOCAL_PORT");
171 | char *plugin_options = std::getenv("SS_PLUGIN_OPTIONS");
172 |
173 | bool has_zero_str = false;
174 | has_zero_str |= check_zero_cstr(remote_host);
175 | has_zero_str |= check_zero_cstr(remote_port);
176 | has_zero_str |= check_zero_cstr(local_host);
177 | has_zero_str |= check_zero_cstr(local_port);
178 |
179 | if (has_zero_str) {
180 | return;
181 | }
182 |
183 | FLAGS_remoteaddr = std::string(remote_host) + ":" + std::string(remote_port);
184 | FLAGS_localaddr = std::string(local_host) + ":" + std::string(local_port);
185 |
186 | if (check_zero_cstr(plugin_options)) {
187 | return;
188 | }
189 |
190 | plugin_options = strdup(plugin_options);
191 | parse_plugin_options(plugin_options);
192 | free(plugin_options);
193 | }
194 |
195 | void parse_command_lines(int argc, char **argv) {
196 | google::LogToStderr();
197 | DeferCaller defer([] {
198 | process_configs();
199 | google::SetLogDestination(0, FLAGS_logfile.c_str());
200 | print_configs();
201 | });
202 |
203 | gflags::ParseCommandLineFlags(&argc, &argv, true);
204 | FLAGS_colorlogtostderr = true;
205 | google::InitGoogleLogging(argv[0]);
206 |
207 | auto string_alias_check = [](std::string &var, const std::string &alias) {
208 | if (alias.empty()) {
209 | return;
210 | }
211 | var = alias;
212 | };
213 | auto integer_alias_check = [](int &var, int alias) {
214 | if (alias >= 0) {
215 | var = alias;
216 | }
217 | };
218 |
219 | string_alias_check(FLAGS_localaddr, FLAGS_l);
220 | string_alias_check(FLAGS_remoteaddr, FLAGS_r);
221 | string_alias_check(FLAGS_targetaddr, FLAGS_t);
222 |
223 | integer_alias_check(FLAGS_parityshard, FLAGS_ps);
224 | integer_alias_check(FLAGS_datashard, FLAGS_ds);
225 |
226 | parse_config_from_env();
227 |
228 | if (FLAGS_c.empty()) {
229 | return;
230 | }
231 |
232 | std::ifstream ifs(FLAGS_c);
233 | std::string jsonstr((std::istreambuf_iterator(ifs)), std::istreambuf_iterator());
234 | if (jsonstr.empty()) {
235 | return;
236 | }
237 |
238 | Document d;
239 | d.Parse(jsonstr.data());
240 |
241 | if (d.IsNull() || !d.IsObject()) {
242 | return;
243 | }
244 |
245 | std::unordered_map> boolean_handlers;
246 | std::unordered_map> integer_handlers;
247 | std::unordered_map> string_handlers;
248 |
249 | auto get_int_assigner = [&](const std::string &name, int *pi) {
250 | integer_handlers.insert(std::make_pair(name, [&, pi](int i) {
251 | *pi = i;
252 | }));
253 | };
254 | auto get_bool_assigner = [&](const std::string &name, bool *pb) {
255 | boolean_handlers.insert(std::make_pair(name, [&, pb](bool b) {
256 | *pb = b;
257 | }));
258 | };
259 | auto get_string_assigner = [&](const std::string &name, std::string *ps) {
260 | string_handlers.insert(std::make_pair(name, [&, ps](const std::string &s) {
261 | *ps = s;
262 | }));
263 | };
264 |
265 | get_string_assigner("localaddr", &FLAGS_localaddr);
266 | get_string_assigner("remoteaddr", &FLAGS_remoteaddr);
267 | get_string_assigner("targetaddr", &FLAGS_targetaddr);
268 | get_string_assigner("listen", &FLAGS_localaddr);
269 | get_string_assigner("target", &FLAGS_targetaddr);
270 | get_string_assigner("key", &FLAGS_key);
271 | get_string_assigner("crypt", &FLAGS_crypt);
272 | get_string_assigner("mode", &FLAGS_mode);
273 | get_string_assigner("logfile", &FLAGS_logfile);
274 |
275 | get_int_assigner("conn", &FLAGS_conn);
276 | get_int_assigner("autoexpire", &FLAGS_autoexpire);
277 | get_int_assigner("mtu", &FLAGS_mtu);
278 | get_int_assigner("scavengettl", &FLAGS_scavengettl);
279 | get_int_assigner("sndwnd", &FLAGS_sndwnd);
280 | get_int_assigner("rcvwnd", &FLAGS_rcvwnd);
281 | get_int_assigner("datashard", &FLAGS_datashard);
282 | get_int_assigner("parityshard", &FLAGS_parityshard);
283 | get_int_assigner("dscp", &FLAGS_dscp);
284 | get_int_assigner("nodelay", &FLAGS_nodelay);
285 | get_int_assigner("resend", &FLAGS_resend);
286 | get_int_assigner("nc", &FLAGS_nc);
287 | get_int_assigner("sockbuf", &FLAGS_sockbuf);
288 | get_int_assigner("keepalive", &FLAGS_keepalive);
289 | get_int_assigner("interval", &FLAGS_interval);
290 |
291 | get_bool_assigner("kvar", &FLAGS_kvar);
292 | get_bool_assigner("nocomp", &FLAGS_nocomp);
293 | get_bool_assigner("acknodelay", &FLAGS_acknodelay);
294 |
295 | for (auto &m : d.GetObject()) {
296 | if (!m.name.IsString()) {
297 | continue;
298 | }
299 | std::string key = m.name.GetString();
300 | auto &value = m.value;
301 | if (value.IsBool()) {
302 | auto it = boolean_handlers.find(key);
303 | if (it != boolean_handlers.end()) {
304 | (it->second)(value.GetBool());
305 | }
306 | } else if (value.IsString()) {
307 | auto it = string_handlers.find(key);
308 | if (it != string_handlers.end()) {
309 | (it->second)(value.GetString());
310 | }
311 | } else if (value.IsNumber()) {
312 | auto it = integer_handlers.find(key);
313 | if (it != integer_handlers.end()) {
314 | (it->second)(value.GetInt());
315 | }
316 | }
317 | }
318 | }
319 |
320 | std::string get_host(const std::string &addr) {
321 | auto pos = addr.find_last_of(':');
322 | if (pos == std::string::npos) {
323 | std::terminate();
324 | }
325 | auto host = addr.substr(0, pos);
326 | if (host == "") {
327 | host = "0.0.0.0";
328 | }
329 | return host;
330 | }
331 |
332 | std::string get_port(const std::string &addr) {
333 | auto pos = addr.find_last_of(':');
334 | if (pos == std::string::npos) {
335 | std::terminate();
336 | }
337 | return addr.substr(pos + 1);
338 | }
339 |
340 | void process_configs() {
341 | auto assigner = [](int nodelay, int interval, int resend, int nc) -> std::function {
342 | return [nodelay, interval, resend, nc]() {
343 | FLAGS_nodelay = nodelay;
344 | FLAGS_interval = interval;
345 | FLAGS_resend = resend;
346 | FLAGS_nc = nc;
347 | };
348 | };
349 | std::unordered_map> handlers = {
350 | {"normal", assigner(0, 40, 2, 1)},
351 | {"fast", assigner(0, 30, 2, 1)},
352 | {"fast2", assigner(1, 20, 2, 1)},
353 | {"fast3", assigner(1, 10, 2, 1)},
354 | };
355 | auto it = handlers.find(FLAGS_mode);
356 | if (it == handlers.end()) {
357 | return;
358 | }
359 | (it->second)();
360 | }
361 |
--------------------------------------------------------------------------------
/config.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_CONFIG_H
2 | #define KCPTUN_CONFIG_H
3 |
4 | #include "utils.h"
5 |
6 | DECLARE_string(localaddr);
7 | DECLARE_string(remoteaddr);
8 | DECLARE_string(targetaddr);
9 | DECLARE_string(key);
10 | DECLARE_string(crypt);
11 | DECLARE_string(mode);
12 | DECLARE_string(logfile);
13 |
14 | DECLARE_int32(conn);
15 | DECLARE_int32(autoexpire);
16 | DECLARE_int32(mtu);
17 | DECLARE_int32(scavengettl);
18 | DECLARE_int32(sndwnd);
19 | DECLARE_int32(rcvwnd);
20 | DECLARE_int32(datashard);
21 | DECLARE_int32(parityshard);
22 | DECLARE_int32(dscp);
23 | DECLARE_int32(nodelay);
24 | DECLARE_int32(resend);
25 | DECLARE_int32(nc);
26 | DECLARE_int32(sockbuf);
27 | DECLARE_int32(keepalive);
28 | DECLARE_int32(interval);
29 |
30 | DECLARE_bool(kvar);
31 | DECLARE_bool(nocomp);
32 | DECLARE_bool(acknodelay);
33 |
34 | void parse_command_lines(int argc, char **argv);
35 |
36 | std::string get_host(const std::string &addr);
37 |
38 | std::string get_port(const std::string &addr);
39 |
40 | void process_configs();
41 |
42 | #endif
43 |
--------------------------------------------------------------------------------
/encoding.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/6.
25 | //
26 |
27 | #ifndef KCP_ENCODING_H
28 | #define KCP_ENCODING_H
29 | #include
30 | typedef unsigned char byte;
31 | //---------------------------------------------------------------------
32 | // WORD ORDER
33 | //---------------------------------------------------------------------
34 | #ifndef IWORDS_BIG_ENDIAN
35 | #ifdef _BIG_ENDIAN_
36 | #if _BIG_ENDIAN_
37 | #define IWORDS_BIG_ENDIAN 1
38 | #endif
39 | #endif
40 | #ifndef IWORDS_BIG_ENDIAN
41 | #if defined(__hppa__) || defined(__m68k__) || defined(mc68000) || \
42 | defined(_M_M68K) || (defined(__MIPS__) && defined(__MISPEB__)) || \
43 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \
44 | defined(__sparc__) || defined(__powerpc__) || defined(__mc68000__) || \
45 | defined(__s390x__) || defined(__s390__)
46 | #define IWORDS_BIG_ENDIAN 1
47 | #endif
48 | #endif
49 | #ifndef IWORDS_BIG_ENDIAN
50 | #define IWORDS_BIG_ENDIAN 0
51 | #endif
52 | #endif
53 |
54 | /* encode 16 bits unsigned int (lsb) */
55 | inline byte *encode16u(byte *p, uint16_t w) {
56 | #if IWORDS_BIG_ENDIAN
57 | *(byte *)(p + 0) = (w & 255);
58 | *(byte *)(p + 1) = (w >> 8);
59 | #else
60 | *(uint16_t *)(p) = w;
61 | #endif
62 | p += 2;
63 | return p;
64 | }
65 |
66 | /* Decode 16 bits unsigned int (lsb) */
67 | inline byte *decode16u(byte *p, uint16_t *w) {
68 | #if IWORDS_BIG_ENDIAN
69 | *w = *(const unsigned char *)(p + 1);
70 | *w = *(const unsigned char *)(p + 0) + (*w << 8);
71 | #else
72 | *w = *(const unsigned short *)p;
73 | #endif
74 | p += 2;
75 | return p;
76 | }
77 |
78 | /* encode 32 bits unsigned int (lsb) */
79 | inline byte *encode32u(byte *p, uint32_t l) {
80 | #if IWORDS_BIG_ENDIAN
81 | *(unsigned char *)(p + 0) = (unsigned char)((l >> 0) & 0xff);
82 | *(unsigned char *)(p + 1) = (unsigned char)((l >> 8) & 0xff);
83 | *(unsigned char *)(p + 2) = (unsigned char)((l >> 16) & 0xff);
84 | *(unsigned char *)(p + 3) = (unsigned char)((l >> 24) & 0xff);
85 | #else
86 | *(uint32_t *)p = l;
87 | #endif
88 | p += 4;
89 | return p;
90 | }
91 |
92 | /* Decode 32 bits unsigned int (lsb) */
93 | inline byte *decode32u(byte *p, uint32_t *l) {
94 | #if IWORDS_BIG_ENDIAN
95 | *l = *(const unsigned char *)(p + 3);
96 | *l = *(const unsigned char *)(p + 2) + (*l << 8);
97 | *l = *(const unsigned char *)(p + 1) + (*l << 8);
98 | *l = *(const unsigned char *)(p + 0) + (*l << 8);
99 | #else
100 | *l = *(const uint32_t *)p;
101 | #endif
102 | p += 4;
103 | return p;
104 | }
105 |
106 | #endif // KCP_ENCODING_H
107 |
--------------------------------------------------------------------------------
/encrypt.cpp:
--------------------------------------------------------------------------------
1 | #include "encrypt.h"
2 | #include "cryptopp/aes.h"
3 | #include "cryptopp/blowfish.h"
4 | #include "cryptopp/cast.h"
5 | #include "cryptopp/chacha.h"
6 | #include "cryptopp/crc.h"
7 | #include "cryptopp/cryptlib.h"
8 | #include "cryptopp/des.h"
9 | #include "cryptopp/filters.h"
10 | #include "cryptopp/hex.h"
11 | #include "cryptopp/modes.h"
12 | #include "cryptopp/osrng.h"
13 | #include "cryptopp/pwdbased.h"
14 | #include "cryptopp/salsa.h"
15 | #include "cryptopp/tea.h"
16 | #include "cryptopp/twofish.h"
17 |
18 | using CryptoPP::PKCS5_PBKDF2_HMAC;
19 | using CryptoPP::SHA1;
20 | using CryptoPP::AutoSeededRandomPool;
21 | using CryptoPP::Exception;
22 | using CryptoPP::StringSink;
23 | using CryptoPP::StringSource;
24 | using CryptoPP::ArraySink;
25 | using CryptoPP::ArraySource;
26 | using CryptoPP::StreamTransformationFilter;
27 | using CryptoPP::HashFilter;
28 | using CryptoPP::AES;
29 | using CryptoPP::DES;
30 | using CryptoPP::Blowfish;
31 | using CryptoPP::CAST;
32 | using CryptoPP::Salsa20;
33 | using CryptoPP::CFB_Mode;
34 | using CryptoPP::CRC32;
35 | using CryptoPP::CRC32C;
36 | using CryptoPP::Twofish;
37 | using CryptoPP::DES_EDE3;
38 | using CryptoPP::XTEA;
39 | using CryptoPP::CAST128;
40 |
41 | const byte iv[] = {167, 115, 79, 156, 18, 172, 27, 1,
42 | 164, 21, 242, 193, 252, 120, 230, 107};
43 |
44 | std::string pbkdf2(std::string password) {
45 | byte salt[] = "kcp-go";
46 | size_t slen = strlen((const char *)salt);
47 | byte derived[32];
48 | PKCS5_PBKDF2_HMAC pbk;
49 | pbk.DeriveKey(derived, sizeof(derived), 0, (const byte *)(password.c_str()),
50 | password.length(), salt, slen, 4096);
51 | return std::string((const char *)derived, 32);
52 | }
53 |
54 | template
55 | class DecEncrypter : public BaseDecEncrypter {
56 | public:
57 | DecEncrypter(const std::string &pass) : pass_(pass) {
58 | assert(keyLen <= pass.length());
59 | assert(ivLen <= sizeof(iv));
60 | }
61 | void encrypt(char *dst, std::size_t dlen, char *src,
62 | std::size_t slen) override {
63 | enc_.SetKeyWithIV((const byte *)(pass_.c_str()), keyLen, iv, ivLen);
64 | ArraySource((byte *)dst, dlen, true,
65 | new StreamTransformationFilter(
66 | enc_, new ArraySink((byte *)src, slen)));
67 | return;
68 | }
69 |
70 | void decrypt(char *dst, std::size_t dlen, char *src,
71 | std::size_t slen) override {
72 | dec_.SetKeyWithIV((const byte *)(pass_.c_str()), keyLen, iv, ivLen);
73 | ArraySource((byte *)dst, dlen, true,
74 | new StreamTransformationFilter(
75 | dec_, new ArraySink((byte *)src, slen)));
76 | return;
77 | }
78 |
79 | private:
80 | std::string pass_;
81 | typename CFB_Mode::Encryption enc_;
82 | typename CFB_Mode::Decryption dec_;
83 |
84 | };
85 |
86 | void put_random_bytes(char *buffer, std::size_t length) {
87 | AutoSeededRandomPool prng;
88 | prng.GenerateBlock((byte *)buffer, length);
89 | }
90 |
91 | template
92 | class DecEncrypter : public BaseDecEncrypter {
93 | public:
94 | DecEncrypter(const std::string &pass) : pass_(pass) {
95 | assert(keyLen <= pass.length());
96 | }
97 | void encrypt(char *dst, std::size_t dlen, char *src,
98 | std::size_t slen) override {
99 | enc_.SetKeyWithIV((const byte *)(pass_.c_str()), keyLen,
100 | (const byte *)src, ivLen);
101 | memmove(dst, src, ivLen);
102 | dst += ivLen;
103 | src += ivLen;
104 | dlen -= ivLen;
105 | slen -= ivLen;
106 | ArraySource((byte *)dst, dlen, true,
107 | new StreamTransformationFilter(
108 | enc_, new ArraySink((byte *)src, slen)));
109 | return;
110 | }
111 |
112 | void decrypt(char *dst, std::size_t dlen, char *src,
113 | std::size_t slen) override {
114 | dec_.SetKeyWithIV((const byte *)(pass_.c_str()), keyLen,
115 | (const byte *)src, ivLen);
116 | memmove(dst, src, ivLen);
117 | dst += ivLen;
118 | src += ivLen;
119 | dlen -= ivLen;
120 | slen -= ivLen;
121 | ArraySource((byte *)dst, dlen, true,
122 | new StreamTransformationFilter(
123 | dec_, new ArraySink((byte *)src, slen)));
124 | return;
125 | }
126 |
127 | private:
128 | std::string pass_;
129 | Salsa20::Encryption enc_;
130 | Salsa20::Decryption dec_;
131 | };
132 |
133 | class NoneDecEncrypter final : public BaseDecEncrypter {
134 | public:
135 | void encrypt(char *dst, std::size_t dlen, char *src,
136 | std::size_t slen) override {
137 | memmove(dst, src, slen);
138 | }
139 | void decrypt(char *dst, std::size_t dlen, char *src,
140 | std::size_t slen) override {
141 | memmove(dst, src, slen);
142 | }
143 | };
144 |
145 | class XorBase {
146 | public:
147 | XorBase(const std::string &pwd) {
148 | xortbl = new char[mtu_limit];
149 | byte salt[] = "sH3CIVoF#rWLtJo6";
150 | size_t slen = strlen((const char *)salt);
151 | PKCS5_PBKDF2_HMAC pbk;
152 | pbk.DeriveKey((byte *)xortbl, mtu_limit, 0, (const byte *)(pwd.c_str()),
153 | pwd.length(), salt, slen, 32);
154 | }
155 | virtual ~XorBase() { delete[] xortbl; }
156 |
157 | public:
158 | char *xortbl = nullptr;
159 | };
160 |
161 | class SimpleXorDecEncrypter final : public BaseDecEncrypter, public XorBase {
162 | public:
163 | SimpleXorDecEncrypter(const std::string &pwd) : XorBase(pwd) {}
164 | void encrypt(char *dst, std::size_t dlen, char *src,
165 | std::size_t slen) override {
166 | for (auto i = 0; i < slen; i++) {
167 | dst[i] = src[i] ^ xortbl[i];
168 | }
169 | }
170 | void decrypt(char *dst, std::size_t dlen, char *src,
171 | std::size_t slen) override {
172 | for (auto i = 0; i < slen; i++) {
173 | dst[i] = src[i] ^ xortbl[i];
174 | }
175 | }
176 | };
177 |
178 | std::unique_ptr getDecEncrypter(const std::string &method,
179 | const std::string &pwd) {
180 | if (method == "aes-128") {
181 | return std::move(my_make_unique>(pwd));
182 | } else if (method == "aes-192") {
183 | return std::move(my_make_unique>(pwd));
184 | } else if (method == "none") {
185 | return std::move(my_make_unique());
186 | } else if (method == "xor") {
187 | return std::move(my_make_unique(pwd));
188 | } else if (method == "3des") {
189 | return std::move(my_make_unique>(pwd));
190 | } else if (method == "blowfish") {
191 | return std::move(my_make_unique>(pwd));
192 | } else if (method == "twofish") {
193 | return std::move(my_make_unique>(pwd));
194 | } else if (method == "salsa20") {
195 | return std::move(my_make_unique>(pwd));
196 | } else if (method == "xtea") {
197 | return std::move(my_make_unique>(pwd));
198 | } else if (method == "cast5") {
199 | return std::move(my_make_unique>(pwd));
200 | } else {
201 | return std::move(my_make_unique>(pwd));
202 | }
203 | }
204 |
205 | uint32_t
206 | crc32c_cast(const unsigned char *buf, size_t len)
207 | {
208 | CRC32C crc;
209 | uint32_t ret;
210 | crc.Update(buf, len);
211 | crc.Final((CryptoPP::byte *)&ret);
212 | return ret;
213 | }
--------------------------------------------------------------------------------
/encrypt.h:
--------------------------------------------------------------------------------
1 | #ifndef SHADOWSOCKS_ASIO_ENCRYPT_H
2 | #define SHADOWSOCKS_ASIO_ENCRYPT_H
3 |
4 | #include "utils.h"
5 |
6 | std::string pbkdf2(std::string password);
7 |
8 | class BaseDecEncrypter {
9 | public:
10 | virtual ~BaseDecEncrypter() = default;
11 | virtual void encrypt(char *dst, std::size_t dlen, char *src,
12 | std::size_t slen) = 0;
13 | virtual void decrypt(char *dst, std::size_t dlen, char *src,
14 | std::size_t slen) = 0;
15 | };
16 |
17 | class AsyncDecrypter : public AsyncInOutputer {
18 | public:
19 | AsyncDecrypter(std::unique_ptr &&dec, OutputHandler o = nullptr)
20 | : AsyncInOutputer(o), dec_(std::move(dec)) {}
21 | void async_input(char *buf, std::size_t len, Handler handler) override {
22 | dec_->decrypt(buf, len, buf, len);
23 | output(buf, len, handler);
24 | }
25 |
26 | private:
27 | std::unique_ptr dec_;
28 | };
29 |
30 | class AsyncEncrypter : public AsyncInOutputer {
31 | public:
32 | AsyncEncrypter(std::unique_ptr &&enc, OutputHandler o = nullptr)
33 | : AsyncInOutputer(o), enc_(std::move(enc)) {}
34 | void async_input(char *buf, std::size_t len, Handler handler) override {
35 | enc_->encrypt(buf, len, buf, len);
36 | output(buf, len, handler);
37 | }
38 |
39 | private:
40 | std::unique_ptr enc_;
41 | };
42 |
43 | std::unique_ptr getDecEncrypter(const std::string &method,
44 | const std::string &pwd);
45 |
46 | void put_random_bytes(char *buffer, std::size_t length);
47 |
48 | static inline std::shared_ptr
49 | getAsyncEncrypter(std::unique_ptr &&enc,
50 | OutputHandler handler) {
51 | return std::make_shared(std::move(enc), handler);
52 | }
53 |
54 | static inline std::shared_ptr
55 | getAsyncDecrypter(std::unique_ptr &&dec,
56 | OutputHandler handler) {
57 | return std::make_shared(std::move(dec), handler);
58 | }
59 |
60 | uint32_t crc32c_cast(const unsigned char *buf, size_t len);
61 |
62 | #endif // SHADOWSOCKS_ASIO_ENCRYPT_H
63 |
--------------------------------------------------------------------------------
/fec.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/2.
25 | //
26 |
27 | #include "fec.h"
28 | #include "encoding.h"
29 | //#include
30 | #include
31 | #include
32 | #include "utils.h"
33 |
34 | static inline uint32_t currentMs() {
35 | long sec, usec;
36 | itimeofday(&sec, &usec);
37 | return uint32_t((sec * 1000) + (usec / 1000));
38 | }
39 |
40 | FEC::FEC(ReedSolomon enc) : enc(enc) {}
41 |
42 | FEC FEC::New(int rxlimit, int dataShards, int parityShards) {
43 | if (dataShards <= 0 || parityShards <= 0) {
44 | throw std::invalid_argument("invalid arguments");
45 | }
46 |
47 | if (rxlimit < dataShards + parityShards) {
48 | throw std::invalid_argument("invalid arguments");
49 | }
50 |
51 | FEC fec(ReedSolomon::New(dataShards, parityShards));
52 | fec.rxlimit = rxlimit;
53 | fec.dataShards = dataShards;
54 | fec.parityShards = parityShards;
55 | fec.totalShards = dataShards + parityShards;
56 | fec.paws = (0xffffffff / uint32_t(fec.totalShards) - 1) *
57 | uint32_t(fec.totalShards);
58 |
59 | return fec;
60 | }
61 |
62 | fecPacket FEC::Decode(byte *data, size_t sz) {
63 | fecPacket pkt;
64 | data = decode32u(data, &pkt.seqid);
65 | data = decode16u(data, &pkt.flag);
66 | pkt.ts = currentMs();
67 | pkt.data =
68 | std::make_shared>(data, data + sz - fecHeaderSize);
69 | return pkt;
70 | }
71 |
72 | void FEC::MarkData(byte *data, uint16_t sz) {
73 | data = encode32u(data, this->next);
74 | data = encode16u(data, typeData);
75 | encode16u(data, static_cast(sz + 2)); // including size itself
76 | this->next++;
77 | }
78 |
79 | void FEC::MarkFEC(byte *data) {
80 | data = encode32u(data, this->next);
81 | encode16u(data, typeFEC);
82 | this->next++;
83 | if (this->next >= this->paws) { // paws would only occurs in MarkFEC
84 | this->next = 0;
85 | }
86 | }
87 |
88 | std::vector FEC::Input(fecPacket &pkt) {
89 | std::vector recovered;
90 |
91 | uint32_t now = currentMs();
92 | if (now - lastCheck >= fecExpire) {
93 | for (auto it = rx.begin(); it != rx.end();) {
94 | if (now - it->ts > fecExpire) {
95 | it = rx.erase(it);
96 | } else {
97 | it++;
98 | }
99 | }
100 | lastCheck = now;
101 | }
102 |
103 | // insertion
104 | auto n = this->rx.size() - 1;
105 | int insertIdx = 0;
106 | for (int i = n; i >= 0; i--) {
107 | if (pkt.seqid == rx[i].seqid) {
108 | return recovered;
109 | } else if (pkt.seqid > rx[i].seqid) {
110 | insertIdx = i + 1;
111 | break;
112 | }
113 | }
114 | // insert into ordered rx queue
115 | rx.insert(rx.begin() + insertIdx, pkt);
116 |
117 | // shard range for current packet
118 | auto shardBegin = pkt.seqid - pkt.seqid % totalShards;
119 | auto shardEnd = shardBegin + totalShards - 1;
120 |
121 | // max search range in ordered queue for current shard
122 | auto searchBegin = insertIdx - int(pkt.seqid % totalShards);
123 | if (searchBegin < 0) {
124 | searchBegin = 0;
125 | }
126 |
127 | auto searchEnd = searchBegin + totalShards - 1;
128 | if (searchEnd >= rx.size()) {
129 | searchEnd = rx.size() - 1;
130 | }
131 |
132 | if (searchEnd > searchBegin && searchEnd - searchBegin + 1 >= dataShards) {
133 | int numshard = 0;
134 | int numDataShard = 0;
135 | int first = 0;
136 | size_t maxlen = 0;
137 |
138 | std::vector shardVec(totalShards);
139 | std::vector shardflag(totalShards, false);
140 |
141 | for (auto i = searchBegin; i <= searchEnd; i++) {
142 | auto seqid = rx[i].seqid;
143 | if (seqid > shardEnd) {
144 | break;
145 | } else if (seqid >= shardBegin) {
146 | shardVec[seqid % totalShards] = rx[i].data;
147 | shardflag[seqid % totalShards] = true;
148 | numshard++;
149 | if (rx[i].flag == typeData) {
150 | numDataShard++;
151 | }
152 | if (numshard == 1) {
153 | first = i;
154 | }
155 | if (rx[i].data->size() > maxlen) {
156 | maxlen = rx[i].data->size();
157 | }
158 | }
159 | }
160 |
161 | if (numDataShard == dataShards) { // no lost
162 | rx.erase(rx.begin() + first, rx.begin() + first + numshard);
163 | } else if (numshard >= dataShards) { // recoverable
164 | // equally resized
165 | for (int i = 0; i < shardVec.size(); i++) {
166 | if (shardVec[i] != nullptr) {
167 | shardVec[i]->resize(maxlen, 0);
168 | }
169 | }
170 |
171 | // reconstruct shards
172 | enc.Reconstruct(shardVec);
173 | for (int k = 0; k < dataShards; k++) {
174 | if (!shardflag[k]) {
175 | recovered.push_back(shardVec[k]);
176 | }
177 | }
178 | rx.erase(rx.begin() + first, rx.begin() + first + numshard);
179 | }
180 | }
181 |
182 | // keep rxlimit
183 | if (rx.size() > rxlimit) {
184 | rx.erase(rx.begin());
185 | }
186 |
187 | return recovered;
188 | }
189 |
190 | void FEC::Encode(std::vector &shards) {
191 | // resize elements with 0 appending
192 | size_t max = 0;
193 | for (int i = 0; i < dataShards; i++) {
194 | if (shards[i]->size() > max) {
195 | max = shards[i]->size();
196 | }
197 | }
198 |
199 | for (auto &s : shards) {
200 | if (s == nullptr) {
201 | s = std::make_shared>(max);
202 | } else {
203 | s->resize(max, 0);
204 | }
205 | }
206 |
207 | enc.Encode(shards);
208 | }
209 |
--------------------------------------------------------------------------------
/fec.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/2.
25 | //
26 |
27 | #ifndef KCP_FEC_H
28 | #define KCP_FEC_H
29 |
30 | #include "reedsolomon.h"
31 | #include
32 | #include
33 | #include
34 |
35 | const size_t fecHeaderSize = 6;
36 | const size_t fecHeaderSizePlus2{fecHeaderSize + 2};
37 | const uint16_t typeData = 0xf1;
38 | const uint16_t typeFEC = 0xf2;
39 | const int fecExpire = 30000;
40 |
41 | class fecPacket {
42 | public:
43 | uint32_t seqid;
44 | uint16_t flag;
45 | row_type data;
46 | uint32_t ts;
47 | };
48 |
49 | class FEC {
50 | public:
51 | FEC() = default;
52 | FEC(ReedSolomon enc);
53 |
54 | static FEC New(int rxlimit, int dataShards, int parityShards);
55 |
56 | inline bool isEnabled() { return dataShards > 0 && parityShards > 0; }
57 |
58 | // Input a FEC packet, and return recovered data if possible.
59 | std::vector Input(fecPacket &pkt);
60 |
61 | // Calc Parity Shards
62 | void Encode(std::vector &shards);
63 |
64 | // Decode a raw array into fecPacket
65 | static fecPacket Decode(byte *data, size_t sz);
66 |
67 | // Mark raw array as typeData, and write correct size.
68 | void MarkData(byte *data, uint16_t sz);
69 |
70 | // Mark raw array as typeFEC
71 | void MarkFEC(byte *data);
72 |
73 | private:
74 | std::vector rx; // ordered receive queue
75 | int rxlimit; // queue empty limit
76 | int dataShards;
77 | int parityShards;
78 | int totalShards;
79 | uint32_t next{0}; // next seqid
80 | ReedSolomon enc;
81 | uint32_t paws; // Protect Against Wrapped Sequence numbers
82 | uint32_t lastCheck{0};
83 | };
84 |
85 | #endif // KCP_FEC_H
86 |
--------------------------------------------------------------------------------
/frame.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_FRAME_H
2 | #define KCPTUN_FRAME_H
3 |
4 | #include "utils.h"
5 |
6 | enum { cmdSyn = 0, cmdFin = 1, cmdPsh = 2, cmdNop = 3 };
7 |
8 | enum { VERSION = 1 };
9 |
10 | enum {
11 | sizeOfVer = 1,
12 | sizeOfCmd = 1,
13 | sizeOfLength = 2,
14 | sizeOfSid = 4,
15 | headerSize = sizeOfVer + sizeOfCmd + sizeOfSid + sizeOfLength
16 | };
17 |
18 | struct frame final {
19 | uint8_t version;
20 | uint8_t cmd;
21 | uint16_t length;
22 | uint32_t id;
23 | char *data;
24 |
25 | static frame unmarshal(const char *b) {
26 | frame f;
27 | f.version = b[0];
28 | f.cmd = b[1];
29 | decode16u((byte *)(b + 2), &(f.length));
30 | decode32u((byte *)(b + 4), &(f.id));
31 | return f;
32 | }
33 | void marshal(char *b) {
34 | b[0] = version;
35 | b[1] = cmd;
36 | encode16u((byte *)(b + 2), length);
37 | encode32u((byte *)(b + 4), id);
38 | }
39 | };
40 |
41 | #endif
42 |
--------------------------------------------------------------------------------
/galois.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #include "galois.h"
28 | #include
29 |
30 | extern const int fieldSize;
31 | extern "C" byte mulTable[256][256];
32 | extern "C" byte logTable[];
33 | extern "C" byte expTable[];
34 |
35 | byte galAdd(byte a, byte b) { return a ^ b; }
36 |
37 | byte galSub(byte a, byte b) { return a ^ b; }
38 |
39 | byte galMultiply(byte a, byte b) { return mulTable[a][b]; }
40 |
41 | byte galDivide(byte a, byte b) {
42 | if (a == 0) {
43 | return 0;
44 | }
45 |
46 | if (b == 0) {
47 | throw std::invalid_argument("Argument 'divisor' is 0");
48 | }
49 |
50 | int logA = logTable[a];
51 | int logB = logTable[b];
52 | int logResult = logA - logB;
53 | if (logResult < 0) {
54 | logResult += 255;
55 | }
56 | return expTable[logResult];
57 | }
58 |
59 | byte galExp(byte a, byte n) {
60 | if (n == 0) {
61 | return 1;
62 | }
63 | if (a == 0) {
64 | return 0;
65 | }
66 |
67 | int logA = logTable[a];
68 | int logResult = logA * n;
69 | while (logResult >= 255) {
70 | logResult -= 255;
71 | }
72 | return expTable[logResult];
73 | }
74 |
--------------------------------------------------------------------------------
/galois.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #ifndef KCP_GALOIS_H
28 | #define KCP_GALOIS_H
29 |
30 | typedef unsigned char byte;
31 |
32 | byte galAdd(byte a, byte b);
33 |
34 | byte galSub(byte a, byte b);
35 |
36 | // galMultiply multiplies to elements of the field.
37 | // Uses lookup table ~40% faster
38 | byte galMultiply(byte a, byte b);
39 |
40 | // galDivide is inverse of galMultiply.
41 | byte galDivide(byte a, byte b);
42 |
43 | // Computes a**n.
44 | //
45 | // The result will be the same as multiplying a times itself n times.
46 | byte galExp(byte a, byte b);
47 |
48 | #endif // KCP_GALOIS_H
49 |
--------------------------------------------------------------------------------
/galois_noasm.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #include "galois_noasm.h"
28 | #include "matrix.h"
29 |
30 | extern "C" byte mulTable[256][256];
31 |
32 | void galMulSlice(byte c, row_type in, row_type out) {
33 | for (int n = 0; n < in->size(); n++) {
34 | (*out)[n] = mulTable[c][(*in)[n]];
35 | }
36 | }
37 |
38 | void galMulSliceXor(byte c, row_type in, row_type out) {
39 | for (int n = 0; n < in->size(); n++) {
40 | (*out)[n] ^= mulTable[c][(*in)[n]];
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/galois_noasm.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #ifndef KCP_GALOIS_NOASM_H
28 | #define KCP_GALOIS_NOASM_H
29 |
30 | #include "galois.h"
31 | #include "matrix.h"
32 |
33 | #ifdef __cplusplus
34 | extern "C" {
35 | #endif
36 | void galMulSlice(byte c, row_type in, row_type out);
37 | void galMulSliceXor(byte c, row_type in, row_type out);
38 |
39 | #ifdef __cplusplus
40 | }
41 | #endif
42 |
43 | #endif // KCP_GALOIS_NOASM_H
44 |
--------------------------------------------------------------------------------
/inversion_tree.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/1.
25 | //
26 |
27 | #include "inversion_tree.h"
28 | #include
29 |
30 | inversionTree inversionTree::newInversionTree(int dataShards,
31 | int parityShards) {
32 | inversionTree tree;
33 | tree.m_root.m_children.resize(dataShards + parityShards, nullptr);
34 | tree.m_root.m_matrix = matrix::identityMatrix(dataShards);
35 | return tree;
36 | }
37 |
38 | matrix inversionTree::GetInvertedMatrix(std::vector &invalidIndices) {
39 | if (invalidIndices.size() == 0) {
40 | return m_root.m_matrix;
41 | }
42 |
43 | return m_root.getInvertedMatrix(invalidIndices, 0);
44 | }
45 |
46 | int inversionTree::InsertInvertedMatrix(std::vector &invalidIndices,
47 | matrix &matrix, int shards) {
48 | // If no invalid indices were given then we are done because the
49 | // m_root node is already set with the identity matrix.
50 | if (invalidIndices.size() == 0) {
51 | return -1;
52 | }
53 |
54 | if (!matrix.IsSquare()) {
55 | return -2;
56 | }
57 |
58 | // Recursively create nodes for the inverted matrix in the tree until
59 | // we reach the node to insert the matrix to. We start by passing in
60 | // 0 as the parent index as we start at the m_root of the tree.
61 | m_root.insertInvertedMatrix(invalidIndices, matrix, shards, 0);
62 |
63 | return 0;
64 | }
65 |
66 | matrix inversionNode::getInvertedMatrix(std::vector &invalidIndices,
67 | int parent) {
68 | // Get the child node to search next from the list of m_children. The
69 | // list of m_children starts relative to the parent index passed in
70 | // because the indices of invalid rows is sorted (by default). As we
71 | // search recursively, the first invalid index gets popped off the list,
72 | // so when searching through the list of m_children, use that first invalid
73 | // index to find the child node.
74 | int firstIndex = invalidIndices[0];
75 | auto node = m_children[firstIndex - parent];
76 |
77 | // If the child node doesn't exist in the list yet, fail fast by
78 | // returning, so we can construct and insert the proper inverted matrix.
79 | if (node == nullptr) {
80 | return matrix{};
81 | }
82 |
83 | // If there's more than one invalid index left in the list we should
84 | // keep searching recursively.
85 | if (invalidIndices.size() > 1) {
86 | // Search recursively on the child node by passing in the invalid
87 | // indices with the first index popped off the front. Also the parent
88 | // index to pass down is the first index plus one.
89 | std::vector v(invalidIndices.begin() + 1, invalidIndices.end());
90 | return node->getInvertedMatrix(v, firstIndex + 1);
91 | }
92 |
93 | // If there aren't any more invalid indices to search, we've found our
94 | // node. Return it, however keep in mind that the matrix could still be
95 | // nil because intermediary nodes in the tree are created sometimes with
96 | // their inversion matrices uninitialized.
97 | // std::cout << "return cached matrix:" << std::endl;
98 | return node->m_matrix;
99 | }
100 |
101 | void inversionNode::insertInvertedMatrix(std::vector &invalidIndices,
102 | struct matrix &matrix, int shards,
103 | int parent) {
104 | // As above, get the child node to search next from the list of m_children.
105 | // The list of m_children starts relative to the parent index passed in
106 | // because the indices of invalid rows is sorted (by default). As we
107 | // search recursively, the first invalid index gets popped off the list,
108 | // so when searching through the list of m_children, use that first invalid
109 | // index to find the child node.
110 | int firstIndex = invalidIndices[0];
111 | auto node = m_children[firstIndex - parent];
112 |
113 | // If the child node doesn't exist in the list yet, create a new
114 | // node because we have the writer lock and add it to the list
115 | // of m_children.
116 | if (node == nullptr) {
117 | // Make the length of the list of m_children equal to the number
118 | // of shards minus the first invalid index because the list of
119 | // invalid indices is sorted, so only this length of errors
120 | // are possible in the tree.
121 | node = std::make_shared();
122 | node->m_children.resize(shards - firstIndex, nullptr);
123 | m_children[firstIndex - parent] = node;
124 | }
125 |
126 | // If there's more than one invalid index left in the list we should
127 | // keep searching recursively in order to find the node to add our
128 | // matrix.
129 | if (invalidIndices.size() > 1) {
130 | // As above, search recursively on the child node by passing in
131 | // the invalid indices with the first index popped off the front.
132 | // Also the total number of shards and parent index are passed down
133 | // which is equal to the first index plus one.
134 | std::vector v(invalidIndices.begin() + 1, invalidIndices.end());
135 | node->insertInvertedMatrix(v, matrix, shards, firstIndex + 1);
136 | } else {
137 | node->m_matrix = matrix;
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/inversion_tree.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/1.
25 | //
26 |
27 | #ifndef KCP_INVERSION_TREE_H
28 | #define KCP_INVERSION_TREE_H
29 |
30 | #include "matrix.h"
31 | #include
32 |
33 | struct inversionNode {
34 | struct matrix m_matrix;
35 | std::vector> m_children;
36 | struct matrix getInvertedMatrix(std::vector &invalidIndices,
37 | int parent);
38 |
39 | void insertInvertedMatrix(std::vector &invalidIndices,
40 | struct matrix &matrix, int shards, int parent);
41 | };
42 |
43 | class inversionTree {
44 | public:
45 | // newInversionTree initializes a tree for storing inverted matrices.
46 | // Note that the m_root node is the identity matrix as it implies
47 | // there were no errors with the original data.
48 | static inversionTree newInversionTree(int dataShards, int parityShards);
49 |
50 | // GetInvertedMatrix returns the cached inverted matrix or nil if it
51 | // is not found in the tree keyed on the indices of invalid rows.
52 | matrix GetInvertedMatrix(std::vector &invalidIndices);
53 |
54 | // InsertInvertedMatrix inserts a new inverted matrix into the tree
55 | // keyed by the indices of invalid rows. The total number of shards
56 | // is required for creating the proper length lists of child nodes for
57 | // each node.
58 | int InsertInvertedMatrix(std::vector &invalidIndices,
59 | struct matrix &matrix, int shards);
60 |
61 | private:
62 | inversionNode m_root;
63 | };
64 |
65 | #endif // KCP_INVERSION_TREE_H
66 |
--------------------------------------------------------------------------------
/kcptun_client.cpp:
--------------------------------------------------------------------------------
1 | #include "kcptun_client.h"
2 | #include "snappy_stream.h"
3 |
4 | kcptun_client::kcptun_client(asio::io_service &io_service,
5 | asio::ip::tcp::endpoint local_endpoint,
6 | asio::ip::udp::endpoint target_endpoint)
7 | : service_(io_service), socket_(io_service),
8 | target_endpoint_(target_endpoint), acceptor_(io_service, local_endpoint){}
9 |
10 | void kcptun_client::run() {
11 | locals_.reserve(FLAGS_conn);
12 | for (int i = 0; i < FLAGS_conn; i++) {
13 | auto l = std::make_shared(service_, target_endpoint_);
14 | l->run();
15 | locals_.emplace_back(l);
16 | }
17 | acceptor_.set_option(asio::ip::tcp::acceptor::reuse_address(true));
18 | do_accept();
19 | }
20 |
21 | void kcptun_client::async_choose_local(
22 | std::function)> f) {
23 | auto i = rand() % FLAGS_conn;
24 | auto local = locals_[i].lock();
25 | if ((!local) || local->is_destroyed()) {
26 | local = std::make_shared(service_, target_endpoint_);
27 | local->run();
28 | locals_[i] = local;
29 | f(local);
30 | return;
31 | }
32 | f(local);
33 | return;
34 | }
35 |
36 | void kcptun_client::do_accept() {
37 | auto self = shared_from_this();
38 | acceptor_.async_accept(socket_, [this, self](std::error_code ec) {
39 | TRACE
40 | if (ec) {
41 | TRACE
42 | return;
43 | }
44 | auto sock = std::make_shared(std::move(socket_));
45 | async_choose_local([this, self, sock](std::shared_ptr local) {
46 | if (!local) {
47 | return;
48 | }
49 | local->async_connect([this, self,
50 | sock](std::shared_ptr sess) {
51 | if (!sess) {
52 | return;
53 | }
54 | std::make_shared(service_, sock, sess)
55 | ->run();
56 | });
57 | });
58 | do_accept();
59 | });
60 | }
61 |
62 | static kvar kcptun_client_session_kvar("kcptun_client_session");
63 |
64 | kcptun_client_session::kcptun_client_session(
65 | asio::io_service &io_service, std::shared_ptr sock,
66 | std::shared_ptr sess)
67 | : service_(io_service), sock_(sock), sess_(sess), kvar_(kcptun_client_session_kvar) {
68 | }
69 |
70 | kcptun_client_session::~kcptun_client_session() {
71 | LOG(INFO) << "stream closed!";
72 | }
73 |
74 | void kcptun_client_session::run() {
75 | LOG(INFO) << "stream opened!";
76 | do_pipe1();
77 | do_pipe2();
78 | }
79 |
80 | void kcptun_client_session::do_pipe1() {
81 | auto self = shared_from_this();
82 | sock_->async_read_some(
83 | asio::buffer(buf1_, sizeof(buf1_)),
84 | [this, self](std::error_code ec, std::size_t len) {
85 | if (ec) {
86 | destroy();
87 | return;
88 | }
89 | sess_->async_write(buf1_, len,
90 | [this, self](std::error_code ec, std::size_t) {
91 | if (ec) {
92 | destroy();
93 | return;
94 | }
95 | do_pipe1();
96 | });
97 | });
98 | }
99 |
100 | void kcptun_client_session::do_pipe2() {
101 | auto self = shared_from_this();
102 | sess_->async_read_some(buf2_, sizeof(buf2_), [this,
103 | self](std::error_code ec,
104 | std::size_t len) {
105 | if (ec) {
106 | destroy();
107 | return;
108 | }
109 | asio::async_write(*sock_, asio::buffer(buf2_, len),
110 | [this, self](std::error_code ec, std::size_t len) {
111 | if (ec) {
112 | destroy();
113 | return;
114 | }
115 | do_pipe2();
116 | });
117 | });
118 | }
119 |
120 | void kcptun_client_session::call_this_on_destroy() {
121 | auto self = shared_from_this();
122 |
123 | Destroy::call_this_on_destroy();
124 |
125 | if(sock_) {
126 | sock_->close();
127 | }
128 | if(sess_) {
129 | sess_->destroy();
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/kcptun_client.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_KCPTUN_CLIENT_H
2 | #define KCPTUN_KCPTUN_CLIENT_H
3 |
4 | #include "config.h"
5 | #include "encrypt.h"
6 | #include "local.h"
7 | #include "smux.h"
8 |
9 | class snappy_stream_writer;
10 | class snappy_stream_reader;
11 |
12 | class kcptun_client_session final
13 | : public std::enable_shared_from_this,
14 | public kvar_,
15 | public Destroy {
16 | public:
17 | kcptun_client_session(asio::io_service &io_service,
18 | std::shared_ptr sock,
19 | std::shared_ptr sess);
20 | ~kcptun_client_session();
21 | void run();
22 |
23 | private:
24 | void do_pipe1();
25 | void do_pipe2();
26 | void call_this_on_destroy() override;
27 |
28 | private:
29 | char buf1_[4096];
30 | char buf2_[4096];
31 | asio::io_service &service_;
32 | std::shared_ptr sock_;
33 | std::shared_ptr sess_;
34 | };
35 | class kcptun_client final : public std::enable_shared_from_this {
36 | public:
37 | kcptun_client(asio::io_service &io_service,
38 | asio::ip::tcp::endpoint local_endpoint,
39 | asio::ip::udp::endpoint target_endpoint);
40 | void run();
41 |
42 | private:
43 | void do_accept();
44 | void async_choose_local(std::function)> f);
45 |
46 | private:
47 | asio::io_service &service_;
48 | asio::ip::tcp::socket socket_;
49 | asio::ip::udp::endpoint target_endpoint_;
50 | asio::ip::tcp::acceptor acceptor_;
51 | std::vector> locals_;
52 | };
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/kcptun_client_main.cpp:
--------------------------------------------------------------------------------
1 | #include "encrypt.h"
2 | #include "kcptun_client.h"
3 | #include "local.h"
4 | #include "server.h"
5 |
6 | int main(int argc, char **argv) {
7 | gflags::SetUsageMessage("usage: kcptun_client");
8 | parse_command_lines(argc, argv);
9 | asio::io_service io_service;
10 | asio::ip::udp::endpoint remote_endpoint;
11 | asio::ip::tcp::endpoint local_endpoint;
12 | {
13 | asio::ip::udp::resolver resolver(io_service);
14 | remote_endpoint = asio::ip::udp::endpoint(*resolver.resolve(
15 | {get_host(FLAGS_remoteaddr), get_port(FLAGS_remoteaddr)}));
16 | }
17 | {
18 | asio::ip::tcp::resolver resolver(io_service);
19 | local_endpoint = asio::ip::tcp::endpoint(*resolver.resolve(
20 | {get_host(FLAGS_localaddr), get_port(FLAGS_localaddr)}));
21 | }
22 | std::make_shared(io_service, local_endpoint, remote_endpoint)
23 | ->run();
24 | if (FLAGS_kvar) {
25 | run_kvar_printer(io_service);
26 | }
27 | io_service.run();
28 | gflags::ShutDownCommandLineFlags();
29 | google::ShutdownGoogleLogging();
30 | return 0;
31 | }
32 |
--------------------------------------------------------------------------------
/kcptun_server.cpp:
--------------------------------------------------------------------------------
1 | #include "kcptun_server.h"
2 | #include "fec.h"
3 | #include "server.h"
4 | #include "sess.h"
5 | #include "smux.h"
6 |
7 | kcptun_server::kcptun_server(asio::io_service &io_service,
8 | asio::ip::udp::endpoint local_endpoint,
9 | asio::ip::tcp::endpoint target_endpoint)
10 | : service_(io_service), target_endpoint_(target_endpoint),
11 | usocket_(io_service, local_endpoint) {}
12 |
13 | void kcptun_server::run() {
14 | isfec_ = FLAGS_datashard > 0 && FLAGS_parityshard > 0;
15 | dec_or_enc_ = getDecEncrypter(FLAGS_crypt, pbkdf2(FLAGS_key));
16 | do_receive();
17 | }
18 |
19 | void kcptun_server::accept_handler(std::shared_ptr sess) {
20 | std::make_shared(service_, sess, target_endpoint_)
21 | ->run();
22 | }
23 |
24 | void kcptun_server::do_receive() {
25 | auto self = shared_from_this();
26 | usocket_.async_receive_from(
27 | asio::buffer(buf_, sizeof(buf_)), ep_,
28 | [this, self](std::error_code ec, std::size_t len) {
29 | if (ec) {
30 | return;
31 | }
32 | if (len <= nonce_size + crc_size) {
33 | do_receive();
34 | return;
35 | }
36 | dec_or_enc_->decrypt(buf_, len, buf_, len);
37 | char *buf = buf_ + (nonce_size + crc_size);
38 | len -= nonce_size + crc_size;
39 | auto it = servers_.find(ep_);
40 | std::shared_ptr server;
41 | if (it != servers_.end()) {
42 | server = it->second.lock();
43 | }
44 | if (!server) {
45 | uint32_t convid;
46 | if (isfec_) {
47 | uint16_t fec_type;
48 | decode16u((byte *)(buf + 4), &fec_type);
49 | if (fec_type != typeData) {
50 | do_receive();
51 | return;
52 | }
53 | decode32u((byte *)(buf + fecHeaderSizePlus2), &convid);
54 | } else {
55 | decode32u((byte *)buf, &convid);
56 | }
57 | asio::ip::udp::endpoint ep = ep_;
58 | server = std::make_shared(
59 | service_, [this, self, ep](char *buf, std::size_t len,
60 | Handler handler) {
61 | char *buffer = buffers_.get();
62 | memcpy(buffer + nonce_size + crc_size, buf, len);
63 | auto crc = crc32c_ieee(0, (byte *)buf, len);
64 | encode32u((byte *)(buffer + nonce_size), crc);
65 | dec_or_enc_->encrypt(
66 | buffer, len + nonce_size + crc_size, buffer,
67 | len + nonce_size + crc_size);
68 | usocket_.async_send_to(
69 | asio::buffer(buffer, len + nonce_size + crc_size),
70 | ep, [handler, this, self, len,
71 | buffer](std::error_code ec, std::size_t) {
72 | buffers_.push_back(buffer);
73 | if (handler) {
74 | handler(ec, len);
75 | }
76 | });
77 | });
78 | server->run(
79 | [this, self](std::shared_ptr sess) {
80 | accept_handler(sess);
81 | },
82 | convid);
83 | servers_.emplace(ep, server);
84 | }
85 | server->async_input(
86 | buf, len,
87 | [this, self](std::error_code, std::size_t) { do_receive(); });
88 | });
89 | }
90 |
91 | static kvar server_session_kvar("kcptun_server_session");
92 |
93 | kcptun_server_session::kcptun_server_session(
94 | asio::io_service &io_service, std::shared_ptr sess,
95 | asio::ip::tcp::endpoint target_endpoint)
96 | : service_(io_service), sess_(sess), socket_(io_service),
97 | target_endpoint_(target_endpoint), kvar_(server_session_kvar) {}
98 |
99 | void kcptun_server_session::run() {
100 | auto self = shared_from_this();
101 | socket_.async_connect(target_endpoint_, [this, self](std::error_code ec) {
102 | if (ec) {
103 | return;
104 | }
105 | do_pipe1();
106 | do_pipe2();
107 | });
108 | }
109 |
110 | void kcptun_server_session::do_pipe1() {
111 | auto self = shared_from_this();
112 | socket_.async_read_some(
113 | asio::buffer(buf1_, sizeof(buf1_)),
114 | [this, self](std::error_code ec, std::size_t len) {
115 | if (ec) {
116 | destroy();
117 | return;
118 | }
119 | sess_->async_write(
120 | buf1_, len, [this, self](std::error_code ec, std::size_t len) {
121 | if (ec) {
122 | destroy();
123 | return;
124 | }
125 | do_pipe1();
126 | });
127 | });
128 | }
129 |
130 | void kcptun_server_session::do_pipe2() {
131 | auto self = shared_from_this();
132 | sess_->async_read_some(buf2_, sizeof(buf2_), [this,
133 | self](std::error_code ec,
134 | std::size_t len) {
135 | if (ec) {
136 | destroy();
137 | return;
138 | }
139 | asio::async_write(socket_, asio::buffer(buf2_, len),
140 | [this, self](std::error_code ec, std::size_t len) {
141 | if (ec) {
142 | destroy();
143 | return;
144 | }
145 | do_pipe2();
146 | });
147 | });
148 | }
149 |
150 | void kcptun_server_session::call_this_on_destroy() {
151 | auto self = shared_from_this();
152 |
153 | Destroy::call_this_on_destroy();
154 |
155 | socket_.close();
156 | if(sess_) {
157 | sess_->destroy();
158 | }
159 | }
160 |
--------------------------------------------------------------------------------
/kcptun_server.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_KCPTUN_SERVER_H
2 | #define KCPTUN_KCPTUN_SERVER_H
3 |
4 | #include "config.h"
5 | #include "server.h"
6 |
7 | class kcptun_server_session final
8 | : public std::enable_shared_from_this,
9 | public kvar_,
10 | public Destroy {
11 | public:
12 | kcptun_server_session(asio::io_service &io_service,
13 | std::shared_ptr sess,
14 | asio::ip::tcp::endpoint target_endpoint);
15 | void run();
16 |
17 | private:
18 | void do_pipe1();
19 | void do_pipe2();
20 | void call_this_on_destroy() override;
21 |
22 | private:
23 | char buf1_[4096];
24 | char buf2_[4096];
25 | asio::io_service &service_;
26 | std::shared_ptr sess_;
27 | asio::ip::tcp::socket socket_;
28 | asio::ip::tcp::endpoint target_endpoint_;
29 | };
30 |
31 | class kcptun_server final : public std::enable_shared_from_this {
32 | public:
33 | kcptun_server(asio::io_service &io_service,
34 | asio::ip::udp::endpoint local_endpoint,
35 | asio::ip::tcp::endpoint target_point);
36 | void run();
37 | void do_receive();
38 |
39 | private:
40 | void accept_handler(std::shared_ptr sess);
41 |
42 | private:
43 | bool isfec_;
44 | char buf_[2048];
45 | asio::io_service &service_;
46 | asio::ip::udp::socket usocket_;
47 | asio::ip::udp::endpoint ep_;
48 | asio::ip::tcp::endpoint target_endpoint_;
49 | std::unique_ptr dec_or_enc_;
50 | std::map> servers_;
51 | Buffers buffers_;
52 | };
53 |
54 | #endif
55 |
--------------------------------------------------------------------------------
/kcptun_server_main.cpp:
--------------------------------------------------------------------------------
1 | #include "encrypt.h"
2 | #include "kcptun_server.h"
3 | #include "local.h"
4 | #include "server.h"
5 |
6 | int main(int argc, char **argv) {
7 | gflags::SetUsageMessage("usage: kcptun_server");
8 | parse_command_lines(argc, argv);
9 | asio::io_service io_service;
10 | asio::ip::udp::endpoint local_endpoint;
11 | asio::ip::tcp::endpoint target_endpoint;
12 | {
13 | asio::ip::udp::resolver resolver(io_service);
14 | local_endpoint = asio::ip::udp::endpoint(*resolver.resolve(
15 | {get_host(FLAGS_localaddr), get_port(FLAGS_localaddr)}));
16 | }
17 | {
18 | asio::ip::tcp::resolver resolver(io_service);
19 | target_endpoint = asio::ip::tcp::endpoint(*resolver.resolve(
20 | {get_host(FLAGS_targetaddr), get_port(FLAGS_targetaddr)}));
21 | }
22 | std::make_shared(io_service, local_endpoint, target_endpoint)
23 | ->run();
24 | if (FLAGS_kvar) {
25 | run_kvar_printer(io_service);
26 | }
27 | io_service.run();
28 | gflags::ShutDownCommandLineFlags();
29 | google::ShutdownGoogleLogging();
30 | return 0;
31 | }
32 |
--------------------------------------------------------------------------------
/local.cpp:
--------------------------------------------------------------------------------
1 | #include "local.h"
2 | #include "async_fec.h"
3 | #include "sess.h"
4 | #include "smux.h"
5 | #include "snappy_stream.h"
6 |
7 | static kvar local_kvar("Local");
8 |
9 | Local::Local(asio::io_service &io_service, asio::ip::udp::endpoint ep)
10 | : service_(io_service), ep_(ep), kvar_(local_kvar) {
11 | auto usocket = asio::ip::udp::socket(io_service);
12 | usocket.connect(ep_);
13 | usock_ = std::make_shared(std::move(usocket));
14 | }
15 |
16 | void Local::run() {
17 | auto self = shared_from_this();
18 | auto fec = FLAGS_datashard > 0 && FLAGS_parityshard > 0;
19 |
20 | in = [this](char *buf, std::size_t len, Handler handler) {
21 | sess_->async_input(buf, len, handler);
22 | };
23 | if (fec) {
24 | auto fec_in = std::make_shared(in);
25 | in = [this, fec_in](char *buf, std::size_t len, Handler handler) {
26 | fec_in->async_input(buf, len, handler);
27 | };
28 | }
29 | auto inb = in;
30 | auto dec = getAsyncDecrypter(
31 | getDecEncrypter(FLAGS_crypt, pbkdf2(FLAGS_key)),
32 | [inb](char *buf, std::size_t len, Handler handler) {
33 | auto n = nonce_size + crc_size;
34 | buf += n;
35 | len -= n;
36 | inb(buf, len, handler);
37 | });
38 | in = [this, dec](char *buf, std::size_t len, Handler handler) {
39 | dec->async_input(buf, len, handler);
40 | };
41 |
42 | out = [this](char *buf, std::size_t len, Handler handler) {
43 | usock_->async_write(buf, len, handler);
44 | };
45 | auto enc = getAsyncEncrypter(getDecEncrypter(FLAGS_crypt, pbkdf2(FLAGS_key)), out);
46 | out = [this, enc](char *buf, std::size_t len, Handler handler) {
47 | char *buffer = buffers_.get();
48 | auto n = nonce_size + crc_size;
49 | memcpy(buffer + n, buf, len);
50 | // info("capacity: %lu size: %lu\n", buffers_.capacity(), buffers_.size());
51 | auto crc = crc32c_ieee(0, (byte *)buf, len);
52 | encode32u((byte *)(buffer + nonce_size), crc);
53 | enc->async_input(buffer, len + n, [handler, buffer, len, this](
54 | std::error_code ec, std::size_t) {
55 | buffers_.push_back(buffer);
56 | // info("capacity: %lu size: %lu\n", buffers_.capacity(), buffers_.size());
57 | if (handler) {
58 | handler(ec, len);
59 | }
60 | });
61 | };
62 | if (fec) {
63 | auto fec_out = std::make_shared(out);
64 | out = [this, fec_out](char *buf, std::size_t len, Handler handler) {
65 | fec_out->async_input(buf, len, handler);
66 | };
67 | }
68 | sess_ = std::make_shared(service_, uint32_t(rand()), out);
69 | sess_->run();
70 |
71 | out2 = [this](char *buf, std::size_t len, Handler handler) {
72 | sess_->async_write(buf, len, handler);
73 | };
74 | if (!FLAGS_nocomp) {
75 | auto snappy_writer =
76 | std::make_shared(service_, out2);
77 | out2 = [this, snappy_writer](char *buf, std::size_t len,
78 | Handler handler) {
79 | snappy_writer->async_input(buf, len, handler);
80 | };
81 | }
82 | smux_ = std::make_shared(service_, out2);
83 | smux_->call_on_destroy([self, this]{
84 | destroy();
85 | });
86 | smux_->run();
87 |
88 | in2 = [this](char *buf, std::size_t len, Handler handler) {
89 | smux_->async_input(buf, len, handler);
90 | };
91 | if (!FLAGS_nocomp) {
92 | auto snappy_reader =
93 | std::make_shared(service_, in2);
94 | in2 = [this, snappy_reader](char *buf, std::size_t len,
95 | Handler handler) {
96 | snappy_reader->async_input(buf, len, handler);
97 | };
98 | }
99 |
100 | do_usocket_receive();
101 | do_sess_receive();
102 | }
103 |
104 | void Local::do_usocket_receive() {
105 | auto self = shared_from_this();
106 | usock_->async_read_some(
107 | buf_, sizeof(buf_), [this, self](std::error_code ec, std::size_t sz) {
108 | if (ec) {
109 | return;
110 | }
111 | in(buf_, sz, [this, self](std::error_code ec, std::size_t) {
112 | if (ec) {
113 | return;
114 | }
115 | do_usocket_receive();
116 | });
117 | });
118 | }
119 |
120 | void Local::do_sess_receive() {
121 | auto self = shared_from_this();
122 | sess_->async_read_some(
123 | sbuf_, sizeof(sbuf_), [this, self](std::error_code ec, std::size_t sz) {
124 | if (ec) {
125 | return;
126 | }
127 | in2(sbuf_, sz, [this, self](std::error_code ec, std::size_t) {
128 | if (ec) {
129 | return;
130 | }
131 | do_sess_receive();
132 | });
133 | });
134 | }
135 |
136 | void Local::async_connect(
137 | std::function)> handler) {
138 | smux_->async_connect(handler);
139 | }
140 |
141 | //bool Local::is_destroyed() const { return smux_->is_destroyed(); }
142 |
143 | void Local::run_scavenger() {
144 | if (FLAGS_scavengettl <= 0) {
145 | return;
146 | }
147 | auto self = shared_from_this();
148 | auto timer = std::make_shared(
149 | service_, std::chrono::seconds(FLAGS_scavengettl));
150 | timer->async_wait([this, timer, self](const std::error_code &) {
151 | if (smux_) {
152 | smux_->destroy();
153 | }
154 | });
155 | }
156 |
157 | void Local::call_this_on_destroy() {
158 | auto self = shared_from_this();
159 |
160 | Destroy::call_this_on_destroy();
161 |
162 | if (sess_) {
163 | auto sess = sess_;
164 | sess_ = nullptr;
165 | sess->destroy();
166 | }
167 |
168 | if (smux_) {
169 | auto smux = smux_;
170 | smux_ = nullptr;
171 | smux->destroy();
172 | }
173 |
174 | usock_ = nullptr;
175 |
176 | in = nullptr;
177 | out = nullptr;
178 | in2 = nullptr;
179 | out2 = nullptr;
180 | }
181 |
--------------------------------------------------------------------------------
/local.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_LOCAL_H
2 | #define KCPTUN_LOCAL_H
3 |
4 | #include "config.h"
5 | #include "sess.h"
6 |
7 | class smux_sess;
8 | class smux;
9 |
10 | class Local final :
11 | public std::enable_shared_from_this,
12 | public kvar_,
13 | public Destroy {
14 | public:
15 | Local(asio::io_service &io_service, asio::ip::udp::endpoint ep);
16 | void run();
17 | void async_connect(std::function)> handler);
18 | void run_scavenger();
19 |
20 | private:
21 | void do_usocket_receive();
22 | void do_sess_receive();
23 | void call_this_on_destroy() override;
24 |
25 | private:
26 | char buf_[2048];
27 | char sbuf_[2048];
28 | asio::io_service &service_;
29 | asio::ip::udp::endpoint ep_;
30 | std::shared_ptr sess_;
31 | std::shared_ptr smux_;
32 | std::shared_ptr usock_;
33 | OutputHandler in;
34 | OutputHandler out;
35 | OutputHandler in2;
36 | OutputHandler out2;
37 | Buffers buffers_;
38 | };
39 |
40 | #endif
41 |
--------------------------------------------------------------------------------
/matrix.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #include "matrix.h"
28 | #include "galois.h"
29 | #include
30 |
31 | matrix matrix::newMatrix(int rows, int cols) {
32 | if (rows <= 0 || cols <= 0) {
33 | throw std::invalid_argument("invalid arguments");
34 | }
35 |
36 | matrix m;
37 | m.rows = rows;
38 | m.cols = cols;
39 | m.data.resize(rows, nullptr);
40 | for (auto i = 0; i < rows; i++) {
41 | m.data[i] = std::make_shared>(cols);
42 | }
43 | return m;
44 | }
45 |
46 | matrix matrix::identityMatrix(int size) {
47 | matrix m = matrix::newMatrix(size, size);
48 | for (int i = 0; i < size; i++) {
49 | m.at(i, i) = 1;
50 | }
51 | return m;
52 | }
53 |
54 | matrix matrix::Multiply(matrix &right) {
55 | if (cols != right.rows) {
56 | return matrix{};
57 | }
58 |
59 | matrix result = matrix::newMatrix(rows, right.cols);
60 |
61 | for (int r = 0; r < result.rows; r++) {
62 | for (int c = 0; c < result.cols; c++) {
63 | byte value = 0;
64 | for (int i = 0; i < this->cols; i++) {
65 | value ^= galMultiply(at(r, i), right.at(i, c));
66 | }
67 | result.at(r, c) = value;
68 | }
69 | }
70 | return result;
71 | }
72 |
73 | matrix matrix::Augment(matrix &right) {
74 | matrix result = matrix::newMatrix(this->rows, this->cols + right.cols);
75 |
76 | for (int r = 0; r < this->rows; r++) {
77 | for (int c = 0; c < this->cols; c++) {
78 | result.at(r, c) = at(r, c);
79 | }
80 | auto cols = this->cols;
81 | for (int c = 0; c < right.cols; c++) {
82 | result.at(r, cols + c) = right.at(r, c);
83 | }
84 | }
85 | return result;
86 | }
87 |
88 | matrix matrix::SubMatrix(int rmin, int cmin, int rmax, int cmax) {
89 | matrix result = matrix::newMatrix(rmax - rmin, cmax - cmin);
90 | for (int r = rmin; r < rmax; r++) {
91 | for (int c = cmin; c < cmax; c++) {
92 | result.at(r - rmin, c - cmin) = at(r, c);
93 | }
94 | }
95 | return result;
96 | }
97 |
98 | // SwapRows Exchanges two rows in the matrix.
99 | int matrix::SwapRows(int r1, int r2) {
100 | if (r1 < 0 || rows <= r1 || r2 < 0 || rows <= r2) {
101 | return -1;
102 | }
103 |
104 | std::swap(data[r1], data[r2]);
105 | return 0;
106 | }
107 |
108 | bool matrix::IsSquare() { return this->rows == this->cols; }
109 |
110 | matrix matrix::Invert() {
111 | if (!IsSquare()) {
112 | return matrix{};
113 | }
114 | auto work = matrix::identityMatrix(rows);
115 | work = matrix::Augment(work);
116 |
117 | auto ret = work.gaussianElimination();
118 | if (ret != 0) {
119 | return matrix{};
120 | }
121 |
122 | return work.SubMatrix(0, rows, rows, rows * 2);
123 | }
124 |
125 | int matrix::gaussianElimination() {
126 | auto rows = this->rows;
127 | auto columns = this->cols;
128 | // Clear out the part below the main diagonal and scale the main
129 | // diagonal to be 1.
130 | for (int r = 0; r < rows; r++) {
131 | // If the element on the diagonal is 0, find a row below
132 | // that has a non-zero and swap them.
133 | if (at(r, r) == 0) {
134 | for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
135 | if (at(rowBelow, r) != 0) {
136 | this->SwapRows(r, rowBelow);
137 | break;
138 | }
139 | }
140 | }
141 |
142 | // If we couldn't find one, the matrix is singular.
143 | if (at(r, r) == 0) {
144 | return -1;
145 | }
146 |
147 | // Scale to 1.
148 | if (at(r, r) != 1) {
149 | byte scale = galDivide(1, at(r, r));
150 | for (int c = 0; c < columns; c++) {
151 | at(r, c) = galMultiply(at(r, c), scale);
152 | }
153 | }
154 |
155 | // Make everything below the 1 be a 0 by subtracting
156 | // a multiple of it. (Subtraction and addition are
157 | // both exclusive or in the Galois field.)
158 | for (int rowBelow = r + 1; rowBelow < rows; rowBelow++) {
159 | if (at(rowBelow, r) != 0) {
160 | byte scale = at(rowBelow, r);
161 | for (int c = 0; c < columns; c++) {
162 | at(rowBelow, c) ^= galMultiply(scale, at(r, c));
163 | }
164 | }
165 | }
166 | }
167 |
168 | // Now clear the part above the main diagonal.
169 | for (int d = 0; d < rows; d++) {
170 | for (int rowAbove = 0; rowAbove < d; rowAbove++) {
171 | if (at(rowAbove, d) != 0) {
172 | byte scale = at(rowAbove, d);
173 | for (int c = 0; c < columns; c++) {
174 | at(rowAbove, c) ^= galMultiply(scale, at(d, c));
175 | }
176 | }
177 | }
178 | }
179 | return 0;
180 | }
181 |
182 | matrix matrix::vandermonde(int rows, int cols) {
183 | matrix result = matrix::newMatrix(rows, cols);
184 | for (int r = 0; r < rows; r++) {
185 | for (int c = 0; c < cols; c++) {
186 | result.at(r, c) = galExp(byte(r), byte(c));
187 | }
188 | }
189 | return result;
190 | }
191 |
--------------------------------------------------------------------------------
/matrix.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2016/12/30.
25 | //
26 |
27 | #ifndef KCP_MATRIX_H
28 | #define KCP_MATRIX_H
29 |
30 | #include "galois.h"
31 | #include
32 | #include
33 |
34 | using row_type = std::shared_ptr>;
35 |
36 | struct matrix {
37 | // newMatrix returns a matrix of zeros.
38 | static matrix newMatrix(int rows, int cols);
39 |
40 | // IdentityMatrix returns an identity matrix of the given empty.
41 | static matrix identityMatrix(int size);
42 |
43 | // Create a Vandermonde matrix, which is guaranteed to have the
44 | // property that any subset of rows that forms a square matrix
45 | // is invertible.
46 | static matrix vandermonde(int rows, int cols);
47 |
48 | // Multiply multiplies this matrix (the one on the left) by another
49 | // matrix (the one on the right) and returns a new matrix with the result.
50 | matrix Multiply(matrix &right);
51 |
52 | // Augment returns the concatenation of this matrix and the matrix on the
53 | // right.
54 | matrix Augment(matrix &right);
55 |
56 | // Returns a part of this matrix. Data is copied.
57 | matrix SubMatrix(int rmin, int cmin, int rmax, int cmax);
58 |
59 | // IsSquare will return true if the matrix is square
60 | bool IsSquare();
61 |
62 | // SwapRows Exchanges two rows in the matrix.
63 | int SwapRows(int r1, int r2);
64 |
65 | // Invert returns the inverse of this matrix.
66 | // Returns ErrSingular when the matrix is singular and doesn't have an
67 | // inverse. The matrix must be square, otherwise ErrNotSquare is returned.
68 | matrix Invert();
69 |
70 | // Gaussian elimination (also known as row reduction)
71 | int gaussianElimination();
72 |
73 | std::vector data;
74 | int rows{0}, cols{0};
75 |
76 | inline byte &at(int row, int col) { return (*(data[row]))[col]; }
77 |
78 | inline bool empty() { return (rows == 0 || cols == 0); }
79 | };
80 |
81 | #endif // KCP_MATRIX_H
82 |
--------------------------------------------------------------------------------
/reedsolomon.cpp:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/1.
25 | //
26 |
27 | #include "reedsolomon.h"
28 | #include "galois_noasm.h"
29 | #include
30 | #include
31 | #include
32 |
33 | ReedSolomon::ReedSolomon(int dataShards, int parityShards)
34 | : m_dataShards(dataShards), m_parityShards(parityShards),
35 | m_totalShards(dataShards + parityShards) {
36 | tree = inversionTree::newInversionTree(dataShards, parityShards);
37 | }
38 |
39 | ReedSolomon ReedSolomon::New(int dataShards, int parityShards) {
40 | if (dataShards <= 0 || parityShards <= 0) {
41 | throw std::invalid_argument(
42 | "cannot create Encoder with zero or less data/parity shards");
43 | }
44 |
45 | if (dataShards + parityShards > 255) {
46 | throw std::invalid_argument(
47 | "cannot create Encoder with 255 or more data+parity shards");
48 | }
49 |
50 | ReedSolomon r(dataShards, parityShards);
51 |
52 | // Start with a Vandermonde matrix. This matrix would work,
53 | // in theory, but doesn't have the property that the data
54 | // shards are unchanged after encoding.
55 | matrix vm = matrix::vandermonde(r.m_totalShards, r.m_dataShards);
56 |
57 | // Multiply by the inverse of the top square of the matrix.
58 | // This will make the top square be the identity matrix, but
59 | // preserve the property that any square subset of rows is
60 | // invertible.
61 | auto top = vm.SubMatrix(0, 0, dataShards, dataShards);
62 | top = top.Invert();
63 | r.m = vm.Multiply(top);
64 |
65 | // Inverted matrices are cached in a tree keyed by the indices
66 | // of the invalid rows of the data to reconstruct.
67 | // The inversion m_root node will have the identity matrix as
68 | // its inversion matrix because it implies there are no errors
69 | // with the original data.
70 | r.tree = inversionTree::newInversionTree(dataShards, parityShards);
71 |
72 | r.parity = std::vector(parityShards);
73 | for (int i = 0; i < parityShards; i++) {
74 | r.parity[i] = r.m.data[dataShards + i];
75 | }
76 | return r;
77 | }
78 |
79 | void ReedSolomon::Encode(std::vector &shards) {
80 | if (shards.size() != m_totalShards) {
81 | throw std::invalid_argument("too few shards given");
82 | }
83 |
84 | checkShards(shards, false);
85 |
86 | // Get the slice of output buffers.
87 | std::vector output(shards.begin() + m_dataShards, shards.end());
88 |
89 | // Do the coding.
90 | std::vector input(shards.begin(), shards.begin() + m_dataShards);
91 | codeSomeShards(parity, input, output, m_parityShards);
92 | };
93 |
94 | void ReedSolomon::codeSomeShards(std::vector &matrixRows,
95 | std::vector &inputs,
96 | std::vector &outputs,
97 | int outputCount) {
98 | for (int c = 0; c < m_dataShards; c++) {
99 | auto in = inputs[c];
100 | for (int iRow = 0; iRow < outputCount; iRow++) {
101 | if (c == 0) {
102 | galMulSlice((*matrixRows[iRow])[c], in, outputs[iRow]);
103 | } else {
104 | galMulSliceXor((*matrixRows[iRow])[c], in, outputs[iRow]);
105 | }
106 | }
107 | }
108 | }
109 |
110 | void ReedSolomon::Reconstruct(std::vector &shards) {
111 | if (shards.size() != m_totalShards) {
112 | throw std::invalid_argument("too few shards given");
113 | }
114 |
115 | // Check arguments
116 | checkShards(shards, true);
117 |
118 | auto shardSize = this->shardSize(shards);
119 |
120 | // Quick check: are all of the shards present? If so, there's
121 | // nothing to do.
122 | int numberPresent = 0;
123 | for (int i = 0; i < m_totalShards; i++) {
124 | if (shards[i] != nullptr) {
125 | numberPresent++;
126 | }
127 | }
128 |
129 | if (numberPresent == m_totalShards) {
130 | // Cool. All of the shards data data. We don't
131 | // need to do anything.
132 | return;
133 | }
134 |
135 | // More complete sanity check
136 | if (numberPresent < m_dataShards) {
137 | throw std::invalid_argument("too few shards given");
138 | }
139 |
140 | // Pull out an array holding just the shards that
141 | // correspond to the rows of the submatrix. These shards
142 | // will be the Input to the decoding process that re-creates
143 | // the missing data shards.
144 | //
145 | // Also, create an array of indices of the valid rows we do have
146 | // and the invalid rows we don't have up until we have enough valid rows.
147 | std::vector subShards(m_dataShards);
148 | std::vector validIndices(m_dataShards, 0);
149 | std::vector invalidIndices;
150 | int subMatrixRow = 0;
151 |
152 | for (int matrixRow = 0;
153 | matrixRow < m_totalShards && subMatrixRow < m_dataShards;
154 | matrixRow++) {
155 | if (shards[matrixRow] != nullptr) {
156 | subShards[subMatrixRow] = shards[matrixRow];
157 | validIndices[subMatrixRow] = matrixRow;
158 | subMatrixRow++;
159 | } else {
160 | invalidIndices.push_back(matrixRow);
161 | }
162 | }
163 |
164 | // Attempt to get the cached inverted matrix out of the tree
165 | // based on the indices of the invalid rows.
166 | auto dataDecodeMatrix = tree.GetInvertedMatrix(invalidIndices);
167 |
168 | // If the inverted matrix isn't cached in the tree yet we must
169 | // construct it ourselves and insert it into the tree for the
170 | // future. In this way the inversion tree is lazily loaded.
171 | if (dataDecodeMatrix.empty()) {
172 | // Pull out the rows of the matrix that correspond to the
173 | // shards that we have and build a square matrix. This
174 | // matrix could be used to generate the shards that we have
175 | // from the original data.
176 | auto subMatrix = matrix::newMatrix(m_dataShards, m_dataShards);
177 | for (subMatrixRow = 0; subMatrixRow < validIndices.size();
178 | subMatrixRow++) {
179 | for (int c = 0; c < m_dataShards; c++) {
180 | subMatrix.at(subMatrixRow, c) =
181 | m.at(validIndices[subMatrixRow], c);
182 | };
183 | }
184 |
185 | // Invert the matrix, so we can go from the encoded shards
186 | // back to the original data. Then pull out the row that
187 | // generates the shard that we want to Decode. Note that
188 | // since this matrix maps back to the original data, it can
189 | // be used to create a data shard, but not a parity shard.
190 | dataDecodeMatrix = subMatrix.Invert();
191 | if (dataDecodeMatrix.empty()) {
192 | throw std::runtime_error("cannot get matrix invert");
193 | }
194 |
195 | // Cache the inverted matrix in the tree for future use keyed on the
196 | // indices of the invalid rows.
197 | int ret = tree.InsertInvertedMatrix(invalidIndices, dataDecodeMatrix,
198 | m_totalShards);
199 | if (ret != 0) {
200 | throw std::runtime_error("cannot insert matrix invert");
201 | }
202 | }
203 |
204 | // Re-create any data shards that were missing.
205 | //
206 | // The Input to the coding is all of the shards we actually
207 | // have, and the output is the missing data shards. The computation
208 | // is done using the special Decode matrix we just built.
209 | std::vector outputs(m_parityShards);
210 | std::vector matrixRows(m_parityShards);
211 | int outputCount = 0;
212 |
213 | for (int iShard = 0; iShard < m_dataShards; iShard++) {
214 | if (shards[iShard] == nullptr) {
215 | shards[iShard] = std::make_shared>(shardSize);
216 | outputs[outputCount] = shards[iShard];
217 | matrixRows[outputCount] = dataDecodeMatrix.data[iShard];
218 | outputCount++;
219 | }
220 | }
221 | codeSomeShards(matrixRows, subShards, outputs, outputCount);
222 |
223 | // Now that we have all of the data shards intact, we can
224 | // compute any of the parity that is missing.
225 | //
226 | // The Input to the coding is ALL of the data shards, including
227 | // any that we just calculated. The output is whichever of the
228 | // data shards were missing.
229 | outputCount = 0;
230 | for (int iShard = m_dataShards; iShard < m_totalShards; iShard++) {
231 | if (shards[iShard] == nullptr) {
232 | shards[iShard] = std::make_shared>(shardSize);
233 | outputs[outputCount] = shards[iShard];
234 | matrixRows[outputCount] = parity[iShard - m_dataShards];
235 | outputCount++;
236 | }
237 | }
238 | codeSomeShards(matrixRows, shards, outputs, outputCount);
239 | }
240 |
241 | void ReedSolomon::checkShards(std::vector &shards, bool nilok) {
242 | auto size = shardSize(shards);
243 | if (size == 0) {
244 | throw std::invalid_argument("no shard data");
245 | }
246 |
247 | for (int i = 0; i < shards.size(); i++) {
248 | if (shards[i] == nullptr) {
249 | if (!nilok) {
250 | throw std::invalid_argument("shard sizes does not match");
251 | }
252 | } else if (shards[i]->size() != size) {
253 | throw std::invalid_argument("shard sizes does not match");
254 | }
255 | }
256 | }
257 |
258 | int ReedSolomon::shardSize(std::vector &shards) {
259 | for (int i = 0; i < shards.size(); i++) {
260 | if (shards[i] != nullptr) {
261 | return shards[i]->size();
262 | }
263 | }
264 |
265 | return 0;
266 | }
267 |
--------------------------------------------------------------------------------
/reedsolomon.h:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 |
3 | // Copyright (c) 2016 Daniel Fu
4 |
5 | // Permission is hereby granted, free of charge, to any person obtaining a copy
6 | // of this software and associated documentation files (the "Software"), to deal
7 | // in the Software without restriction, including without limitation the rights
8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | // copies of the Software, and to permit persons to whom the Software is
10 | // furnished to do so, subject to the following conditions:
11 |
12 | // The above copyright notice and this permission notice shall be included in
13 | // all copies or substantial portions of the Software.
14 |
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | // SOFTWARE.
22 |
23 | //
24 | // Created by 理 傅 on 2017/1/1.
25 | //
26 |
27 | #ifndef KCP_REEDSOLOMON_H
28 | #define KCP_REEDSOLOMON_H
29 |
30 | #include "galois.h"
31 | #include "inversion_tree.h"
32 | #include "matrix.h"
33 |
34 | class ReedSolomon {
35 | public:
36 | ReedSolomon() = default;
37 |
38 | ReedSolomon(int dataShards, int parityShards);
39 |
40 | // New creates a new encoder and initializes it to
41 | // the number of data shards and parity shards that
42 | // you want to use. You can reuse this encoder.
43 | // Note that the maximum number of data shards is 256.
44 | static ReedSolomon New(int dataShards, int parityShards);
45 |
46 | // Encodes parity for a set of data shards.
47 | // An array 'shards' containing data shards followed by parity shards.
48 | // The number of shards must match the number given to New.
49 | // Each shard is a byte array, and they must all be the same empty.
50 | // The parity shards will always be overwritten and the data shards
51 | // will remain the same.
52 | void Encode(std::vector &shards);
53 |
54 | // Reconstruct will recreate the missing shards, if possible.
55 | //
56 | // Given a list of shards, some of which contain data, fills in the
57 | // ones that don't have data.
58 | //
59 | // The length of the array must be equal to Shards.
60 | // You indicate that a shard is missing by setting it to nil.
61 | //
62 | // If there are too few shards to reconstruct the missing
63 | // ones, ErrTooFewShards will be returned.
64 | //
65 | // The reconstructed shard set is complete, but integrity is not verified.
66 | // Use the Verify function to check if data set is ok.
67 | void Reconstruct(std::vector &shards);
68 |
69 | private:
70 | int m_dataShards; // Number of data shards, should not be modified.
71 | int m_parityShards; // Number of parity shards, should not be modified.
72 | int m_totalShards; // Total number of shards. Calculated, and should not be
73 | // modified.
74 |
75 | matrix m;
76 | inversionTree tree;
77 | std::vector parity;
78 |
79 | int shardSize(std::vector &shards);
80 |
81 | // Multiplies a subset of rows from a coding matrix by a full set of
82 | // Input shards to produce some output shards.
83 | // 'matrixRows' is The rows from the matrix to use.
84 | // 'inputs' An array of byte arrays, each of which is one Input shard.
85 | // The number of inputs used is determined by the length of each matrix row.
86 | // outputs Byte arrays where the computed shards are stored.
87 | // The number of outputs computed, and the
88 | // number of matrix rows used, is determined by
89 | // outputCount, which is the number of outputs to compute.
90 | void codeSomeShards(std::vector &matrixRows,
91 | std::vector &inputs,
92 | std::vector &outputs, int outputCount);
93 |
94 | // checkShards will check if shards are the same size
95 | // or 0, if allowed. An error is returned if this fails.
96 | // An error is also returned if all shards are size 0.
97 | void checkShards(std::vector &shards, bool nilok);
98 | };
99 |
100 | #endif // KCP_REEDSOLOMON_H
101 |
--------------------------------------------------------------------------------
/server.cpp:
--------------------------------------------------------------------------------
1 | #include "server.h"
2 | #include "async_fec.h"
3 | #include "sess.h"
4 | #include "smux.h"
5 | #include "snappy_stream.h"
6 |
7 | static kvar server_kvar("Server");
8 |
9 | Server::Server(asio::io_service &io_service, OutputHandler handler)
10 | : AsyncInOutputer(handler), service_(io_service), kvar_(server_kvar) {
11 | }
12 |
13 | void Server::run(AcceptHandler accept_handler, uint32_t convid) {
14 | auto self = shared_from_this();
15 | auto fec = FLAGS_datashard > 0 && FLAGS_parityshard > 0;
16 |
17 | in = [this](char *buf, std::size_t len, Handler handler) {
18 | sess_->async_input(buf, len, handler);
19 | };
20 | if (fec) {
21 | auto fec_in = std::make_shared(in);
22 | in = [this, fec_in](char *buf, std::size_t len, Handler handler) {
23 | fec_in->async_input(buf, len, handler);
24 | };
25 | }
26 |
27 | out = [this](char *buf, std::size_t len, Handler handler) {
28 | output(buf, len, handler);
29 | };
30 | if (fec) {
31 | auto fec_out = std::make_shared(out);
32 | out = [this, fec_out](char *buf, std::size_t len, Handler handler) {
33 | fec_out->async_input(buf, len, handler);
34 | };
35 | }
36 | sess_ = std::make_shared(service_, convid, out);
37 | sess_->run();
38 |
39 | out2 = [this](char *buf, std::size_t len, Handler handler) {
40 | sess_->async_write(buf, len, handler);
41 | };
42 | if (!FLAGS_nocomp) {
43 | auto snappy_writer =
44 | std::make_shared(service_, out2);
45 | out2 = [this, snappy_writer](char *buf, std::size_t len,
46 | Handler handler) {
47 | snappy_writer->async_input(buf, len, handler);
48 | };
49 | }
50 | smux_ = std::make_shared(service_, out2);
51 | smux_->set_accept_handler(accept_handler);
52 | smux_->call_on_destroy([this, self]{
53 | destroy();
54 | });
55 | smux_->run();
56 |
57 | in2 = [this](char *buf, std::size_t len, Handler handler) {
58 | smux_->async_input(buf, len, handler);
59 | };
60 | if (!FLAGS_nocomp) {
61 | auto snappy_reader =
62 | std::make_shared(service_, in2);
63 | in2 = [this, snappy_reader](char *buf, std::size_t len,
64 | Handler handler) {
65 | snappy_reader->async_input(buf, len, handler);
66 | };
67 | }
68 |
69 | do_sess_receive();
70 | }
71 |
72 | void Server::async_input(char *buf, std::size_t len, Handler handler) {
73 | in(buf, len, handler);
74 | }
75 |
76 | void Server::do_sess_receive() {
77 | auto self = shared_from_this();
78 | sess_->async_read_some(
79 | sbuf_, sizeof(sbuf_), [this, self](std::error_code ec, std::size_t sz) {
80 | if (ec) {
81 | return;
82 | }
83 | in2(sbuf_, sz, [this, self](std::error_code ec, std::size_t) {
84 | if (ec) {
85 | return;
86 | }
87 | do_sess_receive();
88 | });
89 | });
90 | }
91 |
92 | Server::~Server() {
93 | }
94 |
95 | void Server::call_this_on_destroy() {
96 | auto self = shared_from_this();
97 |
98 | Destroy::call_this_on_destroy();
99 |
100 | if (sess_) {
101 | auto sess = sess_;
102 | sess_ = nullptr;
103 | sess->destroy();
104 | }
105 |
106 | if (smux_) {
107 | auto smux = smux_;
108 | smux_ = nullptr;
109 | smux->destroy();
110 | }
111 |
112 | in = nullptr;
113 | out = nullptr;
114 | in2 = nullptr;
115 | out2 = nullptr;
116 | }
117 |
--------------------------------------------------------------------------------
/server.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_SERVER_H
2 | #define KCPTUN_SERVER_H
3 |
4 | #include "config.h"
5 | #include "sess.h"
6 |
7 | class smux;
8 | class smux_sess;
9 |
10 | using AcceptHandler = std::function)>;
11 |
12 | class Server final : public std::enable_shared_from_this,
13 | public AsyncInOutputer,
14 | public kvar_,
15 | public Destroy {
16 | public:
17 | Server(asio::io_service &io_service, OutputHandler handler);
18 | ~Server() override;
19 | void run(AcceptHandler handler, uint32_t convid);
20 | void async_input(char *buf, std::size_t len, Handler handler) override;
21 |
22 | private:
23 | void do_sess_receive();
24 | void call_this_on_destroy() override;
25 |
26 | private:
27 | char sbuf_[2048];
28 | asio::io_service &service_;
29 | std::shared_ptr sess_;
30 | std::shared_ptr smux_;
31 | OutputHandler in;
32 | OutputHandler out;
33 | OutputHandler in2;
34 | OutputHandler out2;
35 | };
36 |
37 | #endif
38 |
--------------------------------------------------------------------------------
/sess.cpp:
--------------------------------------------------------------------------------
1 | #include "sess.h"
2 | #include "encrypt.h"
3 | #include "fec.h"
4 |
5 | static kvar sess_kvar("Session");
6 |
7 | Session::Session(asio::io_service &service, uint32_t convid, OutputHandler o)
8 | : AsyncInOutputer(o), service_(service), convid_(convid), kvar_(sess_kvar) {
9 | }
10 |
11 | Session::~Session() {
12 | TRACE
13 | if (kcp_ != nullptr) {
14 | ikcp_release(kcp_);
15 | }
16 | }
17 |
18 | void Session::run() {
19 | kcp_ = ikcp_create(convid_, static_cast(this));
20 | kcp_->output = Session::output_wrapper;
21 | kcp_->stream = 1;
22 | ikcp_nodelay(kcp_, FLAGS_nodelay, FLAGS_interval, FLAGS_resend, FLAGS_nc);
23 | ikcp_wndsize(kcp_, FLAGS_sndwnd, FLAGS_rcvwnd);
24 | ikcp_setmtu(kcp_, FLAGS_mtu);
25 | timer_ = std::make_shared(service_);
26 | run_timer(std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(FLAGS_interval));
27 | // run_peeksize_checker();
28 | }
29 |
30 | void Session::run_timer(std::chrono::high_resolution_clock::time_point pt) {
31 | if(timer_->expires_at() <= pt && timer_->expires_from_now() >= std::chrono::milliseconds(0)) {
32 | return;
33 | } else {
34 | timer_->expires_at(pt);
35 | }
36 | std::weak_ptr ws = shared_from_this();
37 | timer_->async_wait([this, ws](const std::error_code &) {
38 | auto s = ws.lock();
39 | if (!s) {
40 | return;
41 | }
42 | update();
43 | });
44 | }
45 |
46 | void Session::run_peeksize_checker() {
47 | auto self = shared_from_this();
48 | auto timer = std::make_shared(
49 | service_, std::chrono::seconds(1));
50 | timer->async_wait([this, self, timer](const std::error_code &) {
51 | std::cout << ikcp_peeksize(kcp_) << std::endl;
52 | run_peeksize_checker();
53 | });
54 | }
55 |
56 | void Session::async_input(char *buffer, std::size_t len, Handler handler) {
57 | input(buffer, len);
58 | if (handler) {
59 | handler(errc(0), len);
60 | }
61 | }
62 |
63 | void Session::input(char *buffer, std::size_t len) {
64 | auto n = ikcp_input(kcp_, buffer, int(len));
65 | TRACE
66 | if (rtask_.check()) {
67 | update();
68 | }
69 | return;
70 | }
71 |
72 | void Session::async_read_some(char *buffer, std::size_t len, Handler handler) {
73 | if (streambufsiz_ > 0) {
74 | auto n = streambufsiz_;
75 | if (n > len) {
76 | n = len;
77 | }
78 | memcpy(buffer, stream_buf_, n);
79 | streambufsiz_ -= n;
80 | if (streambufsiz_ != 0) {
81 | memmove(stream_buf_, stream_buf_ + n, streambufsiz_);
82 | }
83 | if (handler) {
84 | handler(std::error_code(0, std::generic_category()),
85 | static_cast(n));
86 | }
87 | return;
88 | }
89 | auto psz = ikcp_peeksize(kcp_);
90 | if (psz <= 0) {
91 | rtask_.buf = buffer;
92 | rtask_.len = len;
93 | rtask_.handler = handler;
94 | return;
95 | }
96 | auto n = ikcp_recv(kcp_, buffer, int(len));
97 | // LOG(INFO) << "ikcp_recv return " << n;
98 | if (handler) {
99 | handler(std::error_code(0, std::generic_category()),
100 | static_cast(n));
101 | }
102 | if (psz > len) {
103 | n = ikcp_recv(kcp_, stream_buf_, sizeof(stream_buf_));
104 | // LOG(INFO) << "ikcp_recv2 return " << n;
105 | streambufsiz_ = n;
106 | }
107 | return;
108 | }
109 |
110 | void Session::async_write(char *buffer, std::size_t len, Handler handler) {
111 | auto waitsnd = ikcp_waitsnd(kcp_);
112 | if (waitsnd <= FLAGS_sndwnd * 2) {
113 | auto n = ikcp_send(kcp_, buffer, int(len));
114 | if (handler) {
115 | handler(std::error_code(0, std::generic_category()),
116 | static_cast(n));
117 | }
118 | // ikcp_flush(kcp_);
119 | } else {
120 | wtasks_.push_back(Task{buffer, len, handler});
121 | }
122 |
123 | update();
124 | }
125 |
126 | int Session::output_wrapper(const char *buffer, int len, struct IKCPCB *kcp,
127 | void *user)
128 | {
129 | assert(user != nullptr);
130 | Session *sess = static_cast(user);
131 | sess->output((char *)(buffer), static_cast(len), nullptr);
132 | sess->updateWrite();
133 | return 0;
134 | }
135 |
136 | void Session::updateRead() {
137 | ikcp_update(kcp_, iclock());
138 | if (!rtask_.check()) {
139 | return;
140 | }
141 | auto psz = ikcp_peeksize(kcp_);
142 | if (psz <= 0) {
143 | return;
144 | }
145 | auto len = rtask_.len;
146 | auto n = ikcp_recv(kcp_, rtask_.buf, int(len));
147 | // LOG(INFO) << "ikcp_recv return " << n;
148 | auto rtask_handler = rtask_.handler;
149 | rtask_.reset();
150 | if (rtask_handler) {
151 | rtask_handler(std::error_code(0, std::generic_category()),
152 | static_cast(n));
153 | }
154 | // LOG(INFO) << "psz = " << psz << " pick again is " << ikcp_peeksize(kcp_);
155 | if (psz > len) {
156 | n = ikcp_recv(kcp_, stream_buf_, sizeof(stream_buf_));
157 | // LOG(INFO) << "ikcp_recv2 return " << n;
158 | streambufsiz_ = n;
159 | }
160 | }
161 |
162 | void Session::updateWrite() {
163 | while (ikcp_waitsnd(kcp_) < FLAGS_sndwnd * 2 && !wtasks_.empty()) {
164 | auto task = wtasks_.front();
165 | wtasks_.pop_front();
166 | auto n = ikcp_send(kcp_, task.buf, int(task.len));
167 | auto &handler = task.handler;
168 | if (handler) {
169 | handler(std::error_code(0, std::generic_category()),
170 | static_cast(n));
171 | }
172 | }
173 | }
174 |
175 | void Session::updateTimer() {
176 | auto current = iclock();
177 | auto next = ikcp_check(kcp_, current);
178 | next -= current;
179 | // LOG(INFO) << "next = " << next;
180 | run_timer(std::chrono::high_resolution_clock::now()+std::chrono::milliseconds(next));
181 | }
182 |
183 | void Session::update() {
184 | updateRead();
185 | updateWrite();
186 | updateTimer();
187 | }
188 |
189 | void Session::call_this_on_destroy() {
190 | auto self = shared_from_this();
191 |
192 | Destroy::call_this_on_destroy();
193 |
194 | if (timer_) {
195 | timer_->cancel();
196 | timer_ = nullptr;
197 | }
198 |
199 | if (rtask_.check()) {
200 | auto rtask_handler = rtask_.handler;
201 | rtask_.reset();
202 | rtask_handler(std::error_code(1, std::generic_category()), 0);
203 | }
204 |
205 | if (wtask_.check()) {
206 | auto wtask_handler = wtask_.handler;
207 | wtask_.reset();
208 | wtask_handler(std::error_code(1, std::generic_category()), 0);
209 | }
210 | }
211 |
--------------------------------------------------------------------------------
/sess.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_SESS_H
2 | #define KCPTUN_SESS_H
3 |
4 | #include "config.h"
5 | #include "encrypt.h"
6 | #include "ikcp.h"
7 | #include "matrix.h"
8 | #include "utils.h"
9 |
10 | class FEC;
11 |
12 | class Session : public std::enable_shared_from_this,
13 | public AsyncReadWriter,
14 | public AsyncInOutputer,
15 | public kvar_,
16 | public Destroy {
17 | public:
18 | Session(asio::io_service &service, uint32_t convid, OutputHandler o);
19 | void run();
20 | ~Session();
21 |
22 | private:
23 | void run_timer(std::chrono::high_resolution_clock::time_point pt);
24 | static int output_wrapper(const char *buffer, int len, struct IKCPCB *kcp,
25 | void *user);
26 | void update();
27 | void updateRead();
28 | void updateWrite();
29 | void updateTimer();
30 | void run_peeksize_checker();
31 |
32 | public:
33 | void input(char *buffer, std::size_t len);
34 | void async_input(char *buffer, std::size_t len, Handler handler) override;
35 | void async_read_some(char *buffer, std::size_t len, Handler handler) override;
36 | void async_write(char *buffer, std::size_t len, Handler handler) override;
37 | void call_this_on_destroy() override;
38 |
39 | private:
40 | asio::io_service &service_;
41 | std::shared_ptr timer_;
42 | Task rtask_;
43 | Task wtask_;
44 | std::deque wtasks_;
45 |
46 | private:
47 | uint32_t convid_ = 0;
48 | ikcpcb *kcp_ = nullptr;
49 | char stream_buf_[65535];
50 | std::size_t streambufsiz_ = 0;
51 | };
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/smux.cpp:
--------------------------------------------------------------------------------
1 | #include "smux.h"
2 | #include "config.h"
3 |
4 | void smux::run() {
5 | do_receive_frame();
6 | do_keepalive_checker();
7 | do_keepalive_sender();
8 | }
9 |
10 | void smux::do_keepalive_checker() {
11 | std::weak_ptr weaksmux = shared_from_this();
12 | if (!keepalive_check_timer_) {
13 | keepalive_check_timer_ = std::make_shared(
14 | service_, std::chrono::seconds(FLAGS_keepalive * 3));
15 | } else {
16 | keepalive_check_timer_->expires_at(
17 | keepalive_check_timer_->expires_at() +
18 | std::chrono::seconds(FLAGS_keepalive * 3));
19 | }
20 | keepalive_check_timer_->async_wait(
21 | [this, weaksmux](const std::error_code &) {
22 | auto s = weaksmux.lock();
23 | if (!s || is_destroyed()) {
24 | return;
25 | }
26 | if (data_ready_) {
27 | data_ready_ = false;
28 | do_keepalive_checker();
29 | } else {
30 | s->destroy();
31 | }
32 | });
33 | }
34 |
35 | void smux::do_keepalive_sender() {
36 | std::weak_ptr weaksmux = shared_from_this();
37 | if (!keepalive_sender_timer_) {
38 | keepalive_sender_timer_ = std::make_shared(
39 | service_, std::chrono::seconds(FLAGS_keepalive));
40 | } else {
41 | keepalive_sender_timer_->expires_at(
42 | keepalive_sender_timer_->expires_at() +
43 | std::chrono::seconds(FLAGS_keepalive));
44 | }
45 | keepalive_sender_timer_->async_wait(
46 | [this, weaksmux](const std::error_code &) {
47 | auto s = weaksmux.lock();
48 | if (!s || is_destroyed()) {
49 | return;
50 | }
51 | async_write_frame(frame{VERSION, cmdNop, 0, 0}, nullptr);
52 | do_keepalive_sender();
53 | });
54 | }
55 |
56 | void smux::do_stat_checker() {
57 | auto self = shared_from_this();
58 | auto stat_timer = std::make_shared(
59 | service_, std::chrono::seconds(1));
60 | stat_timer->async_wait([this, self, stat_timer](const std::error_code &) {
61 | if (is_destroyed()) {
62 | return;
63 | }
64 | do_stat_checker();
65 | });
66 | }
67 |
68 | void smux::async_input(char *buf, std::size_t len, Handler handler) {
69 | data_ready_ = true;
70 | if (is_destroyed()) {
71 | if (handler) {
72 | handler(std::error_code(1, std::generic_category()), 0);
73 | }
74 | return;
75 | }
76 | input_task_.reset();
77 | if (len < read_task_.len) {
78 | memcpy(read_task_.buf, buf, len);
79 | read_task_.buf += len;
80 | read_task_.len -= len;
81 | if (handler) {
82 | handler(std::error_code(0, std::generic_category()), 0);
83 | }
84 | } else {
85 | TRACE
86 | memcpy(read_task_.buf, buf, read_task_.len);
87 | len -= read_task_.len;
88 | buf += read_task_.len;
89 | auto read_handler = read_task_.handler;
90 | read_task_.reset();
91 | if (len == 0) {
92 | TRACE
93 | if (handler) {
94 | handler(std::error_code(0, std::generic_category()), 0);
95 | }
96 | } else {
97 | TRACE
98 | input_task_.buf = buf;
99 | input_task_.len = len;
100 | input_task_.handler = handler;
101 | }
102 | if (read_handler) {
103 | read_handler(std::error_code(0, std::generic_category()), 0);
104 | }
105 | }
106 | return;
107 | }
108 |
109 | void smux::async_read_full(char *buf, std::size_t len, Handler handler) {
110 | read_task_.reset();
111 | if (is_destroyed()) {
112 | if (handler) {
113 | handler(std::error_code(1, std::generic_category()), 0);
114 | }
115 | return;
116 | }
117 | TRACE
118 | if (len < input_task_.len) {
119 | memcpy(buf, input_task_.buf, len);
120 | input_task_.len -= len;
121 | input_task_.buf += len;
122 | TRACE
123 | if (handler) {
124 | TRACE
125 | handler(std::error_code(0, std::generic_category()), 0);
126 | }
127 | } else if (len == input_task_.len) {
128 | memcpy(buf, input_task_.buf, len);
129 | auto input_handler = input_task_.handler;
130 | input_task_.reset();
131 | TRACE
132 | if (input_handler) {
133 | TRACE
134 | input_handler(std::error_code(0, std::generic_category()), 0);
135 | }
136 | if (handler) {
137 | handler(std::error_code(0, std::generic_category()), 0);
138 | }
139 | } else {
140 | TRACE
141 | memcpy(buf, input_task_.buf, input_task_.len);
142 | read_task_.len = len - input_task_.len;
143 | read_task_.buf = buf + input_task_.len;
144 | read_task_.handler = handler;
145 | auto input_handler = input_task_.handler;
146 | input_task_.reset();
147 | if (input_handler) {
148 | TRACE
149 | input_handler(std::error_code(0, std::generic_category()), 0);
150 | }
151 | }
152 | return;
153 | }
154 |
155 | void smux::do_receive_frame() {
156 | auto self = shared_from_this();
157 | frame_flag = true;
158 | async_read_full(frame_header_, headerSize, [this, self](std::error_code ec,
159 | std::size_t) {
160 | if (ec) {
161 | TRACE
162 | return;
163 | }
164 | frame f = frame::unmarshal(frame_header_);
165 | async_read_full(
166 | frame_data_, static_cast(f.length),
167 | [f, this, self](std::error_code ec, std::size_t) mutable {
168 | if (ec) {
169 | return;
170 | }
171 | f.data = frame_data_;
172 | frame_flag = false;
173 | handle_frame(f);
174 | });
175 | });
176 | }
177 |
178 | void smux::handle_frame(frame f) {
179 | auto it = sessions_.find(f.id);
180 | auto id = f.id;
181 | switch (f.cmd) {
182 | case cmdSyn:
183 | if (it == sessions_.end() && acceptHandler_) {
184 | auto s = std::make_shared(
185 | service_, id, VERSION, std::weak_ptr(shared_from_this()));
186 | acceptHandler_(s);
187 | sessions_.emplace(std::make_pair(id, std::weak_ptr(s)));
188 | }
189 | do_receive_frame();
190 | break;
191 | case cmdFin:
192 | if (it != sessions_.end()) {
193 | auto s = it->second.lock();
194 | if (s) {
195 | sessions_.erase(f.id);
196 | s->destroy();
197 | }
198 | }
199 | do_receive_frame();
200 | break;
201 | case cmdPsh:
202 | if (it != sessions_.end()) {
203 | auto s = it->second.lock();
204 | if (s) {
205 | auto self = shared_from_this();
206 | s->input(f.data, static_cast(f.length),
207 | [this, self](std::error_code ec, std::size_t) {
208 | TRACE
209 | do_receive_frame();
210 | });
211 | } else {
212 | do_receive_frame();
213 | }
214 | }
215 | break;
216 | case cmdNop:
217 | do_receive_frame();
218 | break;
219 | default:
220 | TRACE
221 | destroy();
222 | break;
223 | }
224 | return;
225 | }
226 |
227 | void smux::async_write(char *buf, std::size_t len, Handler handler) {
228 | if (is_destroyed()) {
229 | if (handler) {
230 | handler(std::error_code(1, std::generic_category()), 0);
231 | }
232 | return;
233 | }
234 | TRACE
235 | try_output(buf, len, handler);
236 | }
237 |
238 | static kvar smux_sess_kvar("smux_sess");
239 |
240 | smux_sess::smux_sess(asio::io_service &io_service, uint32_t id, uint8_t version,
241 | std::weak_ptr sm)
242 | : service_(io_service), id_(id), version_(version), sm_(sm), kvar_(smux_sess_kvar) {
243 | LOG(INFO) << "smux session created!";
244 | }
245 |
246 | void smux_sess::input(char *buf, std::size_t len, Handler handler) {
247 | if (destroy_) {
248 | if (handler) {
249 | handler(std::error_code(1, std::generic_category()), 0);
250 | }
251 | return;
252 | }
253 | if (read_task_.check()) {
254 | if (len <= read_task_.len) {
255 | memcpy(read_task_.buf, buf, len);
256 | auto read_handler = read_task_.handler;
257 | read_task_.reset();
258 | if (read_handler) {
259 | read_handler(std::error_code(0, std::generic_category()), len);
260 | }
261 | if (handler) {
262 | handler(std::error_code(0, std::generic_category()), len);
263 | }
264 | } else {
265 | memcpy(read_task_.buf, buf, read_task_.len);
266 | auto read_handler = read_task_.handler;
267 | auto read_len = read_task_.len;
268 | read_task_.reset();
269 | input_buffer_.append(buf+read_len, len-read_len);
270 | if (read_handler) {
271 | read_handler(std::error_code(0, std::generic_category()),
272 | read_len);
273 | }
274 | if (handler) {
275 | handler(std::error_code(0, std::generic_category()), len);
276 | }
277 | }
278 | } else {
279 | input_buffer_.append(buf, len);
280 | if (handler) {
281 | if (input_buffer_.size() > 4096 * 32) {
282 | input_handler_ = [len, handler](std::error_code ec, std::size_t) {
283 | if (ec) {
284 | handler(ec, 0);
285 | } else {
286 | handler(ec, len);
287 | }
288 | };
289 | } else {
290 | handler(std::error_code(0, std::generic_category()), len);
291 | }
292 | }
293 | }
294 | return;
295 | }
296 |
297 | void smux_sess::async_read_some(char *buf, std::size_t len, Handler handler) {
298 | if (destroy_) {
299 | if (handler) {
300 | handler(std::error_code(1, std::generic_category()), 0);
301 | }
302 | return;
303 | }
304 | auto input_buffer_size = input_buffer_.size();
305 | if (input_buffer_size == 0) {
306 | read_task_.buf = buf;
307 | read_task_.len = len;
308 | read_task_.handler = handler;
309 | } else {
310 | auto retrieve_size = input_buffer_size;
311 | if (len < retrieve_size) {
312 | retrieve_size = len;
313 | }
314 | input_buffer_.retrieve(buf, retrieve_size);
315 | assert(input_buffer_size-retrieve_size == input_buffer_.size());
316 | if (handler) {
317 | handler(std::error_code(0, std::generic_category()), retrieve_size);
318 | }
319 | }
320 | if (input_handler_ && input_buffer_.size() < 4096 * 4) {
321 | auto input_handler = input_handler_;
322 | input_handler_ = nullptr;
323 | input_handler(std::error_code(0, std::generic_category()), 0);
324 | }
325 | return;
326 | }
327 |
328 | static Buffers smux_sess_buffers(4120);
329 |
330 | void smux_sess::async_write(char *buf, std::size_t len, Handler handler) {
331 | if (destroy_) {
332 | if (handler) {
333 | handler(std::error_code(1, std::generic_category()), 0);
334 | }
335 | return;
336 | }
337 | auto s = sm_.lock();
338 | if (!s) {
339 | if (handler) {
340 | handler(std::error_code(1, std::generic_category()), 0);
341 | }
342 | return;
343 | }
344 | auto f = frame{version_, cmdPsh, static_cast(len), id_};
345 | auto data = smux_sess_buffers.get();
346 | f.marshal(data);
347 | memcpy(data + headerSize, buf, len);
348 | s->async_write(data, len + headerSize, [handler, data](std::error_code ec, std::size_t sz){
349 | smux_sess_buffers.push_back(data);
350 | if (handler) {
351 | handler(ec, sz);
352 | }
353 | });
354 | }
355 |
356 | smux_sess::~smux_sess() {
357 | auto s = sm_.lock();
358 | if (s) {
359 | s->async_write_frame(frame{version_, cmdFin, 0, id_}, nullptr);
360 | }
361 | LOG(INFO) << "smux session destroyed!";
362 | }
363 |
364 | void smux_sess::call_this_on_destroy() {
365 | auto self = shared_from_this();
366 |
367 | Destroy::call_this_on_destroy();
368 |
369 | destroy_ = true;
370 | auto read_handler = read_task_.handler;
371 | auto input_handler = input_handler_;
372 | read_task_.reset();
373 | input_handler_ = nullptr;
374 | if (read_handler) {
375 | read_handler(std::error_code(1, std::generic_category()), 0);
376 | }
377 | if (input_handler) {
378 | input_handler(std::error_code(1, std::generic_category()), 0);
379 | }
380 | }
381 |
382 | void smux::async_write_frame(frame f, Handler handler) {
383 | TRACE
384 | if (is_destroyed()) {
385 | TRACE
386 | if (handler) {
387 | handler(std::error_code(1, std::generic_category()), 0);
388 | }
389 | return;
390 | }
391 | char *header = new char[headerSize];
392 | f.marshal(header);
393 | async_write(header, headerSize,
394 | [handler, header](std::error_code ec, std::size_t) {
395 | if (ec) {
396 | TRACE
397 | }
398 | delete[] header;
399 | if (handler) {
400 | handler(ec, 0);
401 | }
402 | });
403 | }
404 |
405 | void smux::async_connect(
406 | std::function)> connectHandler) {
407 | TRACE
408 | auto self = shared_from_this();
409 | if (is_destroyed()) {
410 | if (connectHandler) {
411 | connectHandler(nullptr);
412 | }
413 | return;
414 | }
415 | nextStreamID_ += 2;
416 | auto sid = nextStreamID_;
417 | auto ss = std::make_shared(service_, sid, VERSION,
418 | std::weak_ptr(self));
419 | async_write_frame(
420 | frame{VERSION, cmdSyn, 0, sid},
421 | [this, self, ss, connectHandler, sid](std::error_code ec, std::size_t) {
422 | if (ec) {
423 | if (connectHandler) {
424 | connectHandler(nullptr);
425 | }
426 | return;
427 | }
428 | if (connectHandler) {
429 | sessions_.emplace(sid, std::weak_ptr(ss));
430 | connectHandler(ss);
431 | }
432 | });
433 | }
434 |
435 | void smux::try_output(char *buf, std::size_t len, Handler handler) {
436 | tasks_.push_back(Task{buf, len, handler});
437 | if (!writing_) {
438 | writing_ = true;
439 | try_write_task();
440 | }
441 | }
442 |
443 | void smux::try_write_task() {
444 | auto self = shared_from_this();
445 | if (tasks_.empty()) {
446 | writing_ = false;
447 | return;
448 | }
449 | auto task = tasks_.front();
450 | tasks_.pop_front();
451 | output(task.buf, task.len,
452 | [this, self, task](std::error_code ec, std::size_t) {
453 | if (ec) {
454 | return;
455 | }
456 | if (task.handler) {
457 | task.handler(ec, task.len);
458 | }
459 | try_write_task();
460 | });
461 | }
462 |
463 | void smux::call_this_on_destroy() {
464 | auto self = shared_from_this();
465 |
466 | Destroy::call_this_on_destroy();
467 |
468 | auto read_handler = read_task_.handler;
469 | auto input_handler = input_task_.handler;
470 | read_task_.reset();
471 | input_task_.reset();
472 | if (read_handler) {
473 | read_handler(std::error_code(1, std::generic_category()), 0);
474 | }
475 | if (input_handler) {
476 | input_handler(std::error_code(1, std::generic_category()), 0);
477 | }
478 | }
479 |
--------------------------------------------------------------------------------
/smux.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_SMUX_H
2 | #define KCPTUN_SMUX_H
3 |
4 | #include "frame.h"
5 | #include "utils.h"
6 |
7 | class smux;
8 |
9 | class smux_sess final : public std::enable_shared_from_this,
10 | public AsyncReadWriter,
11 | public kvar_,
12 | public Destroy {
13 | public:
14 | smux_sess(asio::io_service &io_service, uint32_t id, uint8_t version,
15 | std::weak_ptr sm);
16 | ~smux_sess();
17 | void input(char *buf, std::size_t len, Handler handler);
18 | void async_read_some(char *buf, std::size_t len, Handler handler) override;
19 | void async_write(char *buf, std::size_t len, Handler handler) override;
20 |
21 | private:
22 | void call_this_on_destroy() override;
23 |
24 | private:
25 | uint8_t version_;
26 | uint32_t id_;
27 | bool destroy_ = false;
28 | asio::io_service &service_;
29 | Task read_task_;
30 | Handler input_handler_;
31 | LinearBuffer input_buffer_;
32 | std::weak_ptr sm_;
33 | };
34 |
35 | class smux final : public std::enable_shared_from_this,
36 | public AsyncReadWriter,
37 | public AsyncInOutputer,
38 | public Destroy {
39 | public:
40 | smux(asio::io_service &io_service, OutputHandler handler = nullptr)
41 | : AsyncInOutputer(handler), service_(io_service) {}
42 |
43 | void run();
44 | void async_input(char *buf, std::size_t len, Handler handler) override;
45 | void set_accept_handler(
46 | std::function)> handler) {
47 | acceptHandler_ = handler;
48 | }
49 | void async_write(char *buf, std::size_t len, Handler handler) override;
50 | void async_connect(
51 | std::function)> connectHandler);
52 | void async_write_frame(frame f, Handler handler);
53 | void async_read_some(char *buf, std::size_t len, Handler handler) override {
54 | }
55 | void remove_sess_by_id(uint32_t id) { sessions_.erase(id); }
56 |
57 | private:
58 | void do_keepalive_checker();
59 | void do_keepalive_sender();
60 | void do_receive_frame();
61 | void do_stat_checker();
62 | void handle_frame(frame f);
63 | void async_read_full(char *buf, std::size_t len, Handler handler);
64 | void try_output(char *buf, std::size_t len, Handler handler);
65 | void try_write_task();
66 | void call_this_on_destroy() override;
67 |
68 | private:
69 | bool writing_ = false;
70 | bool data_ready_ = true;
71 | char frame_header_[headerSize];
72 | char frame_data_[65536];
73 | uint16_t nextStreamID_ = 1;
74 | asio::io_service &service_;
75 | Task read_task_;
76 | Task input_task_;
77 | std::deque tasks_;
78 | std::function)> acceptHandler_;
79 | std::unordered_map> sessions_;
80 | std::shared_ptr keepalive_check_timer_;
81 | std::shared_ptr keepalive_sender_timer_;
82 | bool frame_flag = false;
83 | };
84 |
85 | #endif
86 |
--------------------------------------------------------------------------------
/snappy_stream.cpp:
--------------------------------------------------------------------------------
1 | #include "snappy_stream.h"
2 | #include "snappy.h"
3 | #include "encrypt.h"
4 |
5 | static const unsigned char magic_head[] = {0xff, 0x06, 0x00, 0x00, 0x73,
6 | 0x4e, 0x61, 0x50, 0x70, 0x59};
7 |
8 | snappy_stream_writer::snappy_stream_writer(asio::io_service &io_service,
9 | OutputHandler handler)
10 | : service_(io_service), AsyncInOutputer(handler) {
11 | memcpy(buf_, magic_head, snappy_magic_head_len);
12 | off_ = snappy_magic_head_len;
13 | }
14 |
15 | void snappy_stream_writer::async_input(char *buf, std::size_t len,
16 | Handler handler) {
17 | auto self = shared_from_this();
18 | if (len > snappy_max_block_size) {
19 | task_.buf = buf + snappy_max_block_size;
20 | task_.len = len - snappy_max_block_size;
21 | len -= snappy_max_block_size;
22 | task_.handler = handler;
23 | handler = nullptr;
24 | }
25 | auto chksum = crc32c_cast((const unsigned char *)buf, len);
26 | encode32u((byte *)(buf_ + off_ + snappy_header_len), chksum);
27 | std::size_t compressed_length;
28 | char *payload = buf_ + off_ + snappy_header_len + snappy_checksum_size;
29 | snappy::RawCompress(buf, len, payload, &compressed_length);
30 | std::size_t length;
31 | if (len > compressed_length) {
32 | buf_[off_] = 0x0;
33 | length = compressed_length + snappy_checksum_size;
34 | } else {
35 | buf_[off_] = 0x1;
36 | length = len + snappy_checksum_size;
37 | memcpy(payload, buf, len);
38 | }
39 | buf_[off_ + 1] = length & 0xFF;
40 | buf_[off_ + 2] = (length >> 8) & 0xFF;
41 | buf_[off_ + 3] = (length >> 16) & 0xFF;
42 | off_ += snappy_header_len + length;
43 | output(buf_, off_, [this, self, handler](std::error_code ec, std::size_t) {
44 | off_ = 0;
45 | if (handler) {
46 | handler(ec, 0);
47 | }
48 | if (task_.check()) {
49 | auto b = task_.buf;
50 | auto l = task_.len;
51 | auto h = task_.handler;
52 | task_.reset();
53 | async_input(b, l, h);
54 | }
55 | });
56 | }
57 |
58 | void snappy_stream_reader::async_input(char *buf, std::size_t len,
59 | Handler handler) {
60 | auto self = shared_from_this();
61 | if (off_ < snappy_header_len) {
62 | if (len + off_ < snappy_header_len) {
63 | memcpy(chunk_ + off_, buf, len);
64 | off_ += len;
65 | if (handler) {
66 | handler(std::error_code(0, std::generic_category()), len);
67 | }
68 | return;
69 | }
70 | auto n = snappy_header_len - off_;
71 | memcpy(chunk_ + off_, buf, n);
72 | len -= n;
73 | buf += n;
74 | off_ += n;
75 | if (len == 0) {
76 | if (handler) {
77 | handler(std::error_code(0, std::generic_category()), len);
78 | }
79 | return;
80 | }
81 | }
82 | auto chunk_type = uint8_t(chunk_[0]);
83 | uint32_t chunk_len = uint32_t(uint8_t(chunk_[1])) |
84 | (uint32_t(uint8_t(chunk_[2])) << 8) |
85 | (uint32_t(uint8_t(chunk_[3])) << 16);
86 | if (!valid_) {
87 | if (chunk_type == chunk_type_stream_identifier &&
88 | chunk_len == snappy_magic_head_len - snappy_header_len &&
89 | off_ < snappy_magic_head_len) {
90 | if (len + off_ < snappy_magic_head_len) {
91 | memcpy(chunk_ + off_, buf, len);
92 | off_ += len;
93 | if (handler) {
94 | handler(std::error_code(0, std::generic_category()), len);
95 | }
96 | return;
97 | }
98 | auto n = snappy_magic_head_len - off_;
99 | memcpy(chunk_ + off_, buf, n);
100 | len -= n;
101 | buf += n;
102 | off_ += n;
103 | for (int i = 0; i < snappy_magic_head_len; i++) {
104 | if (magic_head[i] != uint8_t(chunk_[i])) {
105 | if (handler) {
106 | handler(std::error_code(1, std::generic_category()),
107 | len);
108 | }
109 | return;
110 | }
111 | }
112 | off_ = 0;
113 | valid_ = true;
114 | if (len == 0) {
115 | if (handler) {
116 | handler(std::error_code(0, std::generic_category()), len);
117 | }
118 | return;
119 | }
120 | async_input(buf, len, handler);
121 | return;
122 | } else {
123 | if (handler) {
124 | handler(std::error_code(0, std::generic_category()), len);
125 | }
126 | return;
127 | }
128 | }
129 | if (off_ > snappy_header_len + chunk_len) {
130 | std::terminate();
131 | }
132 | if (len + off_ < snappy_header_len + chunk_len) {
133 | memcpy(chunk_ + off_, buf, len);
134 | off_ += len;
135 | if (handler) {
136 | handler(std::error_code(0, std::generic_category()), len);
137 | }
138 | return;
139 | }
140 | auto n = snappy_header_len + chunk_len - off_;
141 | memcpy(chunk_ + off_, buf, n);
142 | len -= n;
143 | buf += n;
144 | off_ += n;
145 | uint32_t crc32_chksum_2;
146 | decode32u((byte *)(chunk_ + snappy_header_len), &crc32_chksum_2);
147 | if (chunk_type == chunk_type_uncompressed_data) {
148 | uint32_t crc32_chksum =
149 | crc32c_cast((unsigned char *)(chunk_ + snappy_header_len +
150 | snappy_checksum_size),
151 | chunk_len - 4);
152 | if (crc32_chksum != crc32_chksum_2) {
153 | if (handler) {
154 | handler(std::error_code(1, std::generic_category()), len);
155 | }
156 | return;
157 | }
158 | output(
159 | chunk_ + snappy_header_len + snappy_checksum_size,
160 | off_ - (snappy_header_len + snappy_checksum_size),
161 | [this, self, buf, len, handler](std::error_code ec, std::size_t) {
162 | off_ = 0;
163 | if (ec) {
164 | if (handler) {
165 | handler(ec, len);
166 | }
167 | return;
168 | }
169 | if (len == 0) {
170 | if (handler) {
171 | handler(std::error_code(0, std::generic_category()),
172 | len);
173 | }
174 | return;
175 | }
176 | async_input(buf, len, handler);
177 | });
178 | } else if (chunk_type == chunk_type_compressed_data) {
179 | char *compressed = chunk_ + snappy_header_len + snappy_checksum_size;
180 | std::size_t compressed_length =
181 | off_ - (snappy_header_len + snappy_checksum_size);
182 | std::size_t uncompressed_length;
183 | snappy::GetUncompressedLength(compressed, compressed_length,
184 | &uncompressed_length);
185 | snappy::RawUncompress(compressed, compressed_length, decode_buffer_);
186 | uint32_t crc32_chksum = crc32c_cast(
187 | (unsigned char *)(decode_buffer_), uncompressed_length);
188 | if (crc32_chksum != crc32_chksum_2) {
189 | if (handler) {
190 | handler(std::error_code(1, std::generic_category()), len);
191 | }
192 | return;
193 | }
194 | output(
195 | decode_buffer_, uncompressed_length,
196 | [this, self, buf, len, handler](std::error_code ec, std::size_t) {
197 | off_ = 0;
198 | if (ec) {
199 | if (handler) {
200 | handler(ec, len);
201 | }
202 | return;
203 | }
204 | if (len == 0) {
205 | if (handler) {
206 | handler(std::error_code(0, std::generic_category()),
207 | len);
208 | }
209 | return;
210 | }
211 | async_input(buf, len, handler);
212 | });
213 | }
214 | if (chunk_type <= 0x7f) {
215 | if (handler) {
216 | handler(std::error_code(1, std::generic_category()), len);
217 | }
218 | return;
219 | }
220 | if (len == 0) {
221 | if (handler) {
222 | handler(std::error_code(0, std::generic_category()), len);
223 | }
224 | return;
225 | }
226 | async_input(buf, len, handler);
227 | }
228 |
--------------------------------------------------------------------------------
/snappy_stream.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_SNAPPY_STREAM_H
2 | #define KCPTUN_SNAPPY_STREAM_H
3 |
4 | #include "utils.h"
5 |
6 | // snappy stream
7 |
8 | enum {
9 | snappy_max_block_size = 65536,
10 | snappy_max_encoded_len_of_max_block_size = 76490,
11 | snappy_checksum_size = 4,
12 | snappy_header_len = 4,
13 | snappy_magic_head_len = 10,
14 | chunk_type_compressed_data = 0x0,
15 | chunk_type_uncompressed_data = 0x1,
16 | chunk_type_padding = 0xfe,
17 | chunk_type_stream_identifier = 0xff
18 | };
19 |
20 | class snappy_stream_reader final
21 | : public std::enable_shared_from_this,
22 | public AsyncInOutputer {
23 | public:
24 | snappy_stream_reader(asio::io_service &io_service, OutputHandler handler)
25 | : service_(io_service), AsyncInOutputer(handler) {}
26 | void async_input(char *buf, std::size_t len, Handler handler) override;
27 |
28 | private:
29 | bool valid_ = false;
30 | Task task_;
31 | std::size_t off_ = 0;
32 | asio::io_service &service_;
33 | char chunk_[snappy_max_block_size + snappy_checksum_size +
34 | snappy_header_len];
35 | char decode_buffer_[snappy_max_block_size];
36 | };
37 |
38 | class snappy_stream_writer final
39 | : public std::enable_shared_from_this,
40 | public AsyncInOutputer {
41 | public:
42 | snappy_stream_writer(asio::io_service &io_service, OutputHandler handler);
43 | void async_input(char *buf, std::size_t len, Handler handler);
44 |
45 | private:
46 | asio::io_service &service_;
47 | char buf_[snappy_max_block_size + snappy_checksum_size + snappy_header_len +
48 | snappy_magic_head_len];
49 | std::size_t off_ = 0;
50 | Task task_;
51 | };
52 |
53 | #endif // KCPTUN_SNAPPY_STREAM_H
54 |
--------------------------------------------------------------------------------
/utils.cpp:
--------------------------------------------------------------------------------
1 | #include "utils.h"
2 |
3 | void Buffers::push_back(char *buf) {
4 | bufs_.insert(buf);
5 | auto s1 = bufs_.size();
6 | auto s2 = all_bufs_.size();
7 | // info("bufs: %ld, all_bufs: %ld", s1, s2);
8 | if (s1 * 4 > s2 * 3 && s2 > 16) {
9 | std::vector v;
10 | auto it = 0;
11 | for (auto buf : bufs_) {
12 | if (++it > s1 / 2) {
13 | break;
14 | }
15 | v.push_back(buf);
16 | }
17 | for (auto buf : v) {
18 | bufs_.erase(buf);
19 | all_bufs_.erase(buf);
20 | free(buf);
21 | }
22 | }
23 | }
24 |
25 | char *Buffers::get() {
26 | for (auto buf : bufs_) {
27 | bufs_.erase(buf);
28 | return buf;
29 | }
30 | char *buf = static_cast(malloc(n));
31 | all_bufs_.insert(buf);
32 | return buf;
33 | }
34 |
35 | static std::unique_ptr> kvars;
36 | static std::unique_ptr> kvarsRef;
37 |
38 | kvar::kvar(const std::string &name) {
39 | if (!kvars) {
40 | kvars = my_make_unique>();
41 | }
42 | if (!kvarsRef) {
43 | kvarsRef = my_make_unique>();
44 | }
45 | auto it = kvars->find(name);
46 | if (it == kvars->end()) {
47 | p = new int(0);
48 | kvars->insert(std::make_pair(name, p));
49 | kvarsRef->insert(std::make_pair(name, 1));
50 | } else {
51 | p = (*kvars)[name];
52 | (*kvarsRef)[name]++;
53 | }
54 | name_ = name;
55 | }
56 |
57 | kvar::~kvar() {}
58 |
59 | void printKvars() {
60 | if (!kvars) {
61 | return;
62 | }
63 | std::stringstream log_stream;
64 | for (auto &kvar : *kvars) {
65 | auto name = kvar.first;
66 | if (kvar.second == nullptr) {
67 | continue;
68 | }
69 | auto value = *(kvar.second);
70 | log_stream << name << ":" << value << "\t";
71 | }
72 | LOG(INFO) << log_stream.str();
73 | }
74 |
75 | void run_kvar_printer(asio::io_service &service) {
76 | auto timer = std::make_shared(service, std::chrono::seconds(1));
77 | timer->async_wait([&service, timer](const std::error_code &ec) {
78 | if (ec) {
79 | return;
80 | }
81 | printKvars();
82 | run_kvar_printer(service);
83 | });
84 | }
85 |
86 | static Buffers buffersCache(4096);
87 |
88 | buffer::buffer() {
89 | init();
90 | }
91 |
92 | buffer::buffer(buffer &&other) noexcept {
93 | buf = other.buf;
94 | off = other.off;
95 | len = other.len;
96 | cap = other.cap;
97 | other.buf = nullptr;
98 | other.off = 0;
99 | other.len = 0;
100 | other.cap = 0;
101 | }
102 |
103 | buffer::~buffer() {
104 | if (buf == nullptr) {
105 | return;
106 | }
107 | buffersCache.push_back(buf);
108 | }
109 |
110 | void buffer::init() {
111 | if (buf != nullptr) {
112 | buffersCache.push_back(buf);
113 | }
114 | buf = buffersCache.get();
115 | cap = 4096;
116 | off = 0;
117 | len = 0;
118 | }
119 |
120 | LinearBuffer::LinearBuffer() {}
121 |
122 | LinearBuffer::~LinearBuffer() {}
123 |
124 | std::size_t LinearBuffer::size() {
125 | auto bufsSize = bufs_.size();
126 | if (bufsSize == 0) {
127 | return 0;
128 | } else if (bufsSize == 1) {
129 | return bufs_.front().size();
130 | } else {
131 | std::size_t n = 0;
132 | n = bufs_.front().size();
133 | n += bufs_.back().size();
134 | n += (bufsSize - 2) * 4096;
135 | return n;
136 | }
137 | }
138 |
139 | void LinearBuffer::append(char *buf, std::size_t len) {
140 | if (len == 0 || buf == nullptr) {
141 | return;
142 | }
143 | if (bufs_.empty() || bufs_.back().aval() == 0) {
144 | bufs_.emplace_back(buffer());
145 | }
146 | while (len > 0) {
147 | auto aval = bufs_.back().aval();
148 | if (aval >= len) {
149 | bufs_.back().append(buf, len);
150 | return;
151 | }
152 | bufs_.back().append(buf, aval);
153 | len -= aval;
154 | buf += aval;
155 | bufs_.emplace_back(buffer());
156 | }
157 | }
158 |
159 | void LinearBuffer::retrieve(char *buf, std::size_t len) {
160 | while (len > 0 && !bufs_.empty()) {
161 | auto sz = bufs_.front().size();
162 | if (sz > len) {
163 | bufs_.front().retrieve(buf, len);
164 | return;
165 | }
166 | bufs_.front().retrieve(buf, sz);
167 | len -= sz;
168 | buf += sz;
169 | bufs_.pop_front();
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/utils.h:
--------------------------------------------------------------------------------
1 | #ifndef KCPTUN_UTILS_H
2 | #define KCPTUN_UTILS_H
3 |
4 | #include
5 | #include "asio.hpp"
6 | #include "asio/high_resolution_timer.hpp"
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include
29 | #include
30 | #include
31 | #include