├── .editorconfig ├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── CONTRIBUTING.md ├── CONTRIBUTING_CN.md ├── LICENSE ├── README.md ├── bbr_http_server.png ├── bbr_http_server_clan.png ├── client.json ├── commandline.c ├── commandline.h ├── contributors.md ├── debug.c ├── debug.h ├── ikcp.c ├── ikcp.h ├── json.c ├── json.h ├── jwHash.c ├── jwHash.h ├── kcp_http_server.png ├── kcp_http_server_clan.png ├── kcptun.png ├── logo-big.png ├── logo.png ├── server.json ├── tcp_client.c ├── tcp_client.h ├── tcp_proxy.c ├── tcp_proxy.h ├── version.h ├── xkcp_client.c ├── xkcp_client.h ├── xkcp_config.c ├── xkcp_config.h ├── xkcp_mon.c ├── xkcp_mon.h ├── xkcp_server.c ├── xkcp_server.h ├── xkcp_spy.c ├── xkcp_util.c ├── xkcp_util.h └── xkcptun.service /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = LF 6 | [*.[ch]] 7 | indent_style = tab 8 | indent_size = 4 9 | max_line_length = 80 10 | [README.md] 11 | max_line_length = 80 12 | [CMakeLists.txt] 13 | indent_style = tab 14 | indent_size = 4 15 | max_line_length = 80 16 | [*.sh] 17 | indent_style = tab 18 | indent_size = 2 19 | [*.init] 20 | indent_style = tab 21 | indent_size = 4 22 | [*.conf] 23 | indent_style = tab 24 | indent_size = 4 25 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: https://paypal.me/liudf0716 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | 35 | # Cmake middle files 36 | CMakeFiles/ 37 | CMakeCache.txt 38 | Makefile 39 | cmake_install.cmake 40 | 41 | # bin generated 42 | xkcp_client 43 | xkcp_server 44 | xkcp_spy 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | sudo: false 3 | language: c 4 | 5 | compiler: 6 | - gcc 7 | 8 | addons: 9 | apt: 10 | sources: 11 | - sourceline: 'ppa:linuxjedi/ppa' 12 | packages: 13 | - build-essential 14 | - libjson-c-dev 15 | - libevent-dev 16 | 17 | script: 18 | - cmake . 19 | - make 20 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(xkcptun C) 4 | 5 | set(src_xkcp_spy 6 | xkcp_spy.c) 7 | 8 | set(src_xkcp_server 9 | xkcp_server.c 10 | tcp_client.c 11 | debug.c 12 | ikcp.c 13 | jwHash.c 14 | commandline.c 15 | xkcp_util.c 16 | xkcp_config.c 17 | json.c 18 | xkcp_mon.c) 19 | 20 | set(src_xkcp_client 21 | tcp_proxy.c 22 | xkcp_client.c 23 | xkcp_config.c 24 | xkcp_util.c 25 | commandline.c 26 | debug.c 27 | ikcp.c 28 | jwHash.c 29 | json.c 30 | xkcp_mon.c) 31 | 32 | set(libs 33 | m 34 | event) 35 | 36 | ADD_DEFINITIONS(-Wall -O2 --std=gnu99 -Wmissing-declarations) 37 | 38 | 39 | add_executable(xkcp_spy ${src_xkcp_spy}) 40 | target_link_libraries(xkcp_spy ${link_flag} ${libs}) 41 | 42 | add_executable(xkcp_client ${src_xkcp_client}) 43 | target_link_libraries(xkcp_client ${link_flag} ${libs}) 44 | 45 | add_executable(xkcp_server ${src_xkcp_server}) 46 | target_link_libraries(xkcp_server ${link_flag} ${libs}) 47 | 48 | install(TARGETS xkcp_client xkcp_server 49 | RUNTIME DESTINATION bin 50 | ) 51 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ================================================================================ 3 | 4 | If you want to contribute to [xkcptun](https://github.com/liudf0716/xkcptun), please follow these simple rules: 5 | 6 | 1. Press the fork button: 7 | 8 | ![fork](http://oi58.tinypic.com/jj2trm.jpg) 9 | 10 | 2. Clone the repository from your account with: 11 | 12 | ``` 13 | git clone git@github.com:your_github_username/xkcptun.git 14 | ``` 15 | 16 | 3. Create a new branch with: 17 | 18 | ``` 19 | git checkout -b "xkcptun-1-fix" 20 | ``` 21 | You can name it however you want. 22 | 23 | 4. Make your changes 24 | 25 | 5. Don't forget to add yourself in `contributors.md`. 26 | 27 | 6. Commit and push your changes, then make a pull request from Github. 28 | 29 | git commit --signoff 30 | git push -f 31 | 32 | 7. Awaiting review, if accepted, merged! 33 | 34 | 35 | 36 | **IMPORTANT** 37 | 38 | Please, don't forget to update your fork. While you made your changes, the content of the `master` branch can change because other pull requests were merged and it can create conflicts. This is why you have to rebase on `master` every time before pushing your changes and check that your branch doesn't have any conflicts with `master`. 39 | 40 | Thank you. 41 | -------------------------------------------------------------------------------- /CONTRIBUTING_CN.md: -------------------------------------------------------------------------------- 1 | 如果您想要对 [xkcptun](https://github.com/liudf0716/xkcptun)项目做贡献, 请参考如下步骤和规则: 2 | 3 | 1. fork本项目先: 4 | 5 | ![fork](http://oi58.tinypic.com/jj2trm.jpg) 6 | 7 | 2. Clone 您fork完的项目: 8 | 9 | ``` 10 | git clone git@github.com:your_github_username/xkcptun.git 11 | ``` 12 | 13 | 3. clone后请创建一个branch分支: 14 | 15 | ``` 16 | git checkout -b "xkcptun-1-fix" 17 | ``` 18 | 名称请任意定 19 | 20 | 4. 在该分支下修改您要提交的代码(提交前请测试通过) 21 | 22 | 5. 完成修改后不要忘记在 [contributor](https://github.com/liudf0716/xkcptun/blob/master/contributors.md)中添加您的大名. 23 | 24 | 6. Commit and push 您的更改, 在github上创建一个pull request. 25 | 26 | git commit --signoff 27 | git push -f 28 | 29 | 7. Awaiting review, if accepted, merged! 30 | 31 | 32 | 33 | **IMPORTANT** 34 | 35 | 切记,不要忘记更新您fork的xkcptun项目 36 | 37 | Please, don't forget to update your fork. While you made your changes, the content of the `master` branch can change because other pull requests were merged and it can create conflicts. This is why you have to rebase on `master` every time before pushing your changes and check that your branch doesn't have any conflicts with `master`. 38 | -------------------------------------------------------------------------------- /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 | ![xkcptun](https://github.com/liudf0716/xkcptun/blob/master/logo-big.png) 2 | 3 | [![Build Status][1]][2] 4 | [![Powered][3]][4] 5 | [![license][5]][6] 6 | [![PRs Welcome][7]][8] 7 | [![Issue Welcome][9]][10] 8 | [![OpenWRT][11]][12] 9 | [![KunTeng][13]][14] 10 | 11 | [1]: https://img.shields.io/travis/liudf0716/xkcptun.svg?style=plastic 12 | [2]: https://travis-ci.org/liudf0716/xkcptun 13 | [3]: https://img.shields.io/badge/KCP-Powered-blue.svg?style=plastic 14 | [4]: https://github.com/skywind3000/kcp 15 | [5]: https://img.shields.io/badge/license-GPLV3-brightgreen.svg?style=plastic 16 | [6]: https://github.com/KunTengRom/xfrp/blob/master/LICENSE 17 | [7]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=plastic 18 | [8]: https://github.com/liudf0716/xkcptun/pulls 19 | [9]: https://img.shields.io/badge/Issues-welcome-brightgreen.svg?style=plastic 20 | [10]: https://github.com/liudf0716/xkcptung/issues/new 21 | [11]: https://img.shields.io/badge/Platform-%20OpenWRT%20%7CLEDE%20%7CCentOS%20-brightgreen.svg?style=plastic 22 | [12]: https://github.com/gigibox/openwrt-xkcptun 23 | [13]: https://img.shields.io/badge/KunTeng-Inside-blue.svg?style=plastic 24 | [14]: http://rom.kunteng.org 25 | 26 | # xkcptun 基于kcp和libevent2库,用c语言实现的kcptun 27 | 28 | xkcptun主要应用于LEDE,openwrt中,其原理如图: 29 | 30 | kcptun 31 | 32 | ### Compile 33 | 34 | xkcptun依赖[libevent2](https://github.com/libevent/libevent) 35 | 36 | 安装libevent2库后 (apt-get install libevent-dev) 37 | 38 | git clone https://github.com/liudf0716/xkcptun.git 39 | 40 | cd xkcptun 41 | 42 | mkdir build && cd build 43 | 44 | cmake .. (camke -DBUILD_STATIC_LINK=yes .. //静态链接) 45 | 46 | make 47 | 48 | 49 | 生成xkcp_client, xkcp_server, xkcp_spy 50 | 51 | #### 参考文档 52 | 53 | 1, [安装libjson c的问题](https://github.com/liudf0716/xkcptun/wiki/%E5%AE%89%E8%A3%85libjson-c%E7%9A%84%E9%97%AE%E9%A2%98) 54 | 55 | 2, [bbr vs kcp 优化http下载性能对比报告](https://github.com/liudf0716/xkcptun/wiki/bbr-vs-kcp-%E4%BC%98%E5%8C%96http%E4%B8%8B%E8%BD%BD%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94%E6%8A%A5%E5%91%8A) 56 | 57 | 3, [如何在centos上部署xkcptun server](https://github.com/liudf0716/xkcptun/pull/11) 58 | 59 | ### OpenWrt 60 | 编译及安装请参考 [openwrt-xkcptun](https://github.com/gigibox/openwrt-xkcptun) 61 | 62 | ### QuickStart 63 | 64 | 为方便理解和使用,我们将使用场景放在同一台pc上,pc使用ubuntu系统,我们通过xkcptun来访问本机的http server 65 | 66 | 假设pc的 eth0 ip 为 192.168.199.18, http server的监听端口为80端口,xkcptun的server和client配置分别如下: 67 | 68 | server.json 如下: 69 | ``` 70 | { 71 | "localinterface": "eth0", 72 | "localport": 9089, 73 | "remoteaddr": "192.168.199.18", 74 | "remoteport": 80, 75 | "key": "14789632a", 76 | "crypt": "none", 77 | "mode": "fast3", 78 | "mtu": 1350, 79 | "sndwnd": 1024, 80 | "rcvwnd": 1024, 81 | "datashard": 10, 82 | "parityshard": 3, 83 | "dscp": 0, 84 | "nocomp": true, 85 | "acknodelay": false, 86 | "nodelay": 0, 87 | "interval": 20, 88 | "resend": 2, 89 | "nc": 1, 90 | "sockbuf": 4194304, 91 | "keepalive": 10 92 | } 93 | ``` 94 | 95 | client.json如下: 96 | ``` 97 | { 98 | "localinterface": "eth0", 99 | "localport": 9088, 100 | "remoteaddr": "192.168.199.18", 101 | "remoteport": 9089, 102 | "key": "14789632a", 103 | "crypt": "none", 104 | "mode": "fast3", 105 | "mtu": 1350, 106 | "sndwnd": 1024, 107 | "rcvwnd": 1024, 108 | "datashard": 10, 109 | "parityshard": 3, 110 | "dscp": 0, 111 | "nocomp": true, 112 | "acknodelay": false, 113 | "nodelay": 0, 114 | "interval": 20, 115 | "resend": 2, 116 | "nc": 1, 117 | "sockbuf": 4194304, 118 | "keepalive": 10 119 | } 120 | ``` 121 | 122 | 分别运行: 123 | 124 | xkcp_server -c server.json -f -d 7 125 | 126 | xkcp_client -c client.json -f -d 7 127 | 128 | 129 | [注] 以上命令都是运行在debug和前台运行模式,正式部署的时候要把 -f 去掉, -d 0 如: xkcp_server -c server.json -d 0 130 | 131 | curl http://192.168.199.18:9088 132 | 133 | 其执行效果与curl http://192.168.199.18 等同 134 | 135 | 136 | xkcp_spy -h 192.168.199.18 -s -t status 137 | 138 | 查看服务器端的情况 139 | 140 | xkcp_spy -h 192.168.199.18 -c -t status 141 | 142 | 查看客户端的情况 143 | 144 | ### Todo 145 | 146 | Compatible with [kcptun](https://github.com/xtaci/kcptun) kcptun 147 | 148 | 149 | 150 | ### How to contribute our project(给本项目做贡献) 151 | 152 | 153 | 欢迎大家给本项目提供意见和贡献,提供意见的方法可以在本项目的[Issues](https://github.com/liudf0716/xkcptun/issues/new)提,更加欢迎给项目提PULL REQUEST,具体提交PR的方法请参考[CONTRIBUTING](https://github.com/liudf0716/xkcptun/blob/master/CONTRIBUTING.md) 154 | 155 | 156 | ### Contact me 157 | 158 | QQ群 : [331230369](https://jq.qq.com/?_wv=1027&k=47QGEhL) 159 | 160 | -------------------------------------------------------------------------------- /bbr_http_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/bbr_http_server.png -------------------------------------------------------------------------------- /bbr_http_server_clan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/bbr_http_server_clan.png -------------------------------------------------------------------------------- /client.json: -------------------------------------------------------------------------------- 1 | { 2 | "localinterface": "br-lan", 3 | "localport": 9088, 4 | "remoteaddr": "xkcpserverip", 5 | "remoteport": 9089, 6 | "key": "14789632a", 7 | "crypt": "none", 8 | "mode": "fast3", 9 | "mtu": 1350, 10 | "sndwnd": 512, 11 | "rcvwnd": 4096, 12 | "datashard": 10, 13 | "parityshard": 3, 14 | "dscp": 0, 15 | "nocomp": true, 16 | "acknodelay": false, 17 | "nodelay": 0, 18 | "interval": 20, 19 | "resend": 2, 20 | "nc": 1, 21 | "sockbuf": 4194304, 22 | "keepalive": 10 23 | } 24 | -------------------------------------------------------------------------------- /commandline.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | /** @file commandline.c 22 | @brief Command line argument handling 23 | @author Copyright (C) 2004 Philippe April 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "xkcp_config.h" 34 | #include "commandline.h" 35 | #include "debug.h" 36 | #include "version.h" 37 | 38 | static struct option long_options[] = { 39 | { "key", required_argument, NULL, GETOPT_VAL_KEY}, 40 | { "crypt", required_argument, NULL, GETOPT_VAL_CRYPT}, 41 | { "mode", required_argument, NULL, GETOPT_VAL_MODE}, 42 | { "mtu", required_argument, NULL, GETOPT_VAL_MTU}, 43 | { "sndwnd", required_argument, NULL, GETOPT_VAL_SNDWND}, 44 | { "rcvwnd", required_argument, NULL, GETOPT_VAL_RCVWND}, 45 | { "dshard", required_argument, NULL, GETOPT_VAL_DATASHARD}, 46 | { "pshard", required_argument, NULL, GETOPT_VAL_PARITYSHARD}, 47 | { "dscp", required_argument, NULL, GETOPT_VAL_DSCP}, 48 | { "nocomp", no_argument, NULL, GETOPT_VAL_NOCOMP}, 49 | { "acknodelay", no_argument, NULL, GETOPT_VAL_ACKNODELAY}, 50 | { "nodelay", no_argument, NULL, GETOPT_VAL_NODELAY}, 51 | { "interval", required_argument, NULL, GETOPT_VAL_INTERVAL}, 52 | { "resend", required_argument, NULL, GETOPT_VAL_RESEND}, 53 | { "nc", required_argument, NULL, GETOPT_VAL_NC}, 54 | { "sockbuf", required_argument, NULL, GETOPT_VAL_SOCKBUF}, 55 | { "keepalive", required_argument, NULL, GETOPT_VAL_KEEPALIVE}, 56 | { "syslog", no_argument, NULL, GETOPT_VAL_SYSLOG}, 57 | { "help", no_argument, NULL, GETOPT_VAL_HELP}, 58 | { "version", no_argument, NULL, GETOPT_VAL_VERSION}, 59 | { NULL, 0, NULL, 0 } 60 | }; 61 | 62 | /** @internal 63 | * @brief Print usage 64 | * 65 | * Prints usage 66 | */ 67 | void 68 | usage(const char *appname) 69 | { 70 | fprintf(stdout, "Usage: %s [options]\n", appname); 71 | fprintf(stdout, "\n"); 72 | fprintf(stdout, "options:\n"); 73 | fprintf(stdout, " -c Use this config file\n"); 74 | fprintf(stdout, " -f Run in foreground\n"); 75 | fprintf(stdout, " -h --help Print usage\n"); 76 | fprintf(stdout, " -v --version Print version information\n"); 77 | fprintf(stdout, " -d Debug level\n"); 78 | fprintf(stdout, " --syslog Log to syslog\n\n"); 79 | 80 | fprintf(stdout, " -i Interface to use (default br-lan)\n"); 81 | fprintf(stdout, " -l Port number of your local server (default: 9088)\n"); 82 | fprintf(stdout, " -s Host name or IP address of your remote server \n"); 83 | fprintf(stdout, " -p Port number of your remote server (default: 9089)\n"); 84 | fprintf(stdout, " -k Pre-shared secret between client and server\n"); 85 | fprintf(stdout, " -e Encrypt method: none\n"); 86 | fprintf(stdout, " -m Profiles: fast3, fast2, fast, normal (default: \"fast\")\n"); 87 | fprintf(stdout, " -M --mtu MTU of your network interface\n"); 88 | fprintf(stdout, " -S --sndwnd Send window size(num of packets) (default: 512)\n"); 89 | fprintf(stdout, " -R --rcvwnd Receive window size(num of packets) (default: 512)\n"); 90 | fprintf(stdout, " -D --dshard Reed-solomon erasure coding - datashard (default: 10)\n"); 91 | fprintf(stdout, " -P --pshard Reed-solomon erasure coding - parityshard (default: 3)\n"); 92 | fprintf(stdout, " -N --nocomp Disable compression\n"); 93 | fprintf(stdout, " -A --acknodelay Ack no delay\n"); 94 | fprintf(stdout, " -L --nodelay No delay\n"); 95 | fprintf(stdout, " -T --interval \n"); 96 | fprintf(stdout, " -K --keepalive \n"); 97 | 98 | fprintf(stdout, "\n"); 99 | } 100 | 101 | /** Uses getopt() to parse the command line and set configuration values 102 | * also populates restartargv 103 | */ 104 | void 105 | parse_commandline(int argc, char **argv) 106 | { 107 | int c; 108 | struct xkcp_config *config = xkcp_get_config(); 109 | struct xkcp_param *param = &config->param; 110 | 111 | while (-1 != (c = getopt_long(argc, argv, "Ac:D:d:e:fhi:K:k:L:l:M:m:NP:p:R:S:s:T:v", 112 | long_options, NULL))) 113 | switch (c) { 114 | 115 | case GETOPT_VAL_HELP: 116 | case 'h': 117 | usage(argv[0]); 118 | exit(1); 119 | break; 120 | 121 | case 'c': 122 | if (optarg) { 123 | free(config->config_file); 124 | config->config_file = strdup(optarg); 125 | } 126 | break; 127 | 128 | case 'f': 129 | config->daemon = 0; 130 | debugconf.log_stderr = 1; 131 | break; 132 | 133 | case 'd': 134 | if (optarg) { 135 | debugconf.debuglevel = atoi(optarg); 136 | } 137 | break; 138 | 139 | case 'i': 140 | free(param->local_interface); 141 | param->local_interface = strdup(optarg); 142 | break; 143 | 144 | case 'l': 145 | param->local_port = atoi(optarg); 146 | break; 147 | 148 | case 's': 149 | free(param->remote_addr); 150 | param->remote_addr = strdup(optarg); 151 | break; 152 | 153 | case 'p': 154 | param->remote_port = atoi(optarg); 155 | break; 156 | 157 | case 'k': 158 | free(param->key); 159 | param->key = strdup(optarg); 160 | break; 161 | 162 | case 'e': 163 | free(param->crypt); 164 | param->crypt = strdup(optarg); 165 | break; 166 | 167 | case 'm': 168 | free(param->mode); 169 | param->mode = strdup(optarg); 170 | break; 171 | 172 | case GETOPT_VAL_MTU: 173 | case 'M': 174 | param->mtu= atoi(optarg); 175 | break; 176 | 177 | case GETOPT_VAL_SNDWND: 178 | case 'S': 179 | param->sndwnd= atoi(optarg); 180 | break; 181 | 182 | case GETOPT_VAL_RCVWND: 183 | case 'R': 184 | param->rcvwnd= atoi(optarg); 185 | break; 186 | 187 | case GETOPT_VAL_DATASHARD: 188 | case 'D': 189 | param->data_shard= atoi(optarg); 190 | break; 191 | 192 | case GETOPT_VAL_PARITYSHARD: 193 | case 'P': 194 | param->parity_shard= atoi(optarg); 195 | break; 196 | 197 | case GETOPT_VAL_NOCOMP: 198 | case 'N': 199 | param->nocomp = 1; 200 | break; 201 | 202 | case GETOPT_VAL_ACKNODELAY: 203 | case 'A': 204 | param->ack_nodelay = 1; 205 | break; 206 | 207 | case GETOPT_VAL_NODELAY: 208 | case 'L': 209 | param->nodelay = 1; 210 | break; 211 | 212 | case GETOPT_VAL_INTERVAL: 213 | param->interval = atoi(optarg); 214 | break; 215 | 216 | case GETOPT_VAL_KEEPALIVE: 217 | param->keepalive = atoi(optarg); 218 | break; 219 | 220 | case GETOPT_VAL_SYSLOG: 221 | debugconf.log_syslog = 1; 222 | debugconf.log_stderr = 0; 223 | break; 224 | 225 | case GETOPT_VAL_VERSION: 226 | case 'v': 227 | fprintf(stdout, "This is %s version " VERSION "\n", argv[0]); 228 | exit(1); 229 | break; 230 | 231 | default: 232 | usage(argv[0]); 233 | exit(1); 234 | break; 235 | } 236 | 237 | if (NULL != config->config_file) 238 | if (xkcp_parse_param(config->config_file)) { 239 | debug(LOG_ERR, "xkcp_parse_param failed \n"); 240 | usage(argv[0]); 241 | exit(0); 242 | } 243 | 244 | if (!param->remote_addr) { 245 | usage(argv[0]); 246 | exit(0); 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /commandline.h: -------------------------------------------------------------------------------- 1 | /* vim: set et sw=4 ts=4 sts=4 : */ 2 | /********************************************************************\ 3 | * This program is free software; you can redistribute it and/or * 4 | * modify it under the terms of the GNU General Public License as * 5 | * published by the Free Software Foundation; either version 2 of * 6 | * the License, or (at your option) any later version. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU General Public License* 14 | * along with this program; if not, contact: * 15 | * * 16 | * Free Software Foundation Voice: +1-617-542-5942 * 17 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 18 | * Boston, MA 02111-1307, USA gnu@gnu.org * 19 | * * 20 | \********************************************************************/ 21 | 22 | /* $Id$ */ 23 | /** @file commandline.h 24 | @brief Command line argument handling 25 | @author Copyright (C) 2004 Philippe April 26 | */ 27 | 28 | #ifndef _COMMANDLINE_H_ 29 | #define _COMMANDLINE_H_ 30 | 31 | enum { 32 | GETOPT_VAL_NC = 257, 33 | GETOPT_VAL_KEY, 34 | GETOPT_VAL_CRYPT, 35 | GETOPT_VAL_MODE, 36 | GETOPT_VAL_MTU, 37 | GETOPT_VAL_SNDWND, 38 | GETOPT_VAL_RCVWND, 39 | GETOPT_VAL_DATASHARD, 40 | GETOPT_VAL_PARITYSHARD, 41 | GETOPT_VAL_DSCP, 42 | GETOPT_VAL_NOCOMP, 43 | GETOPT_VAL_ACKNODELAY, 44 | GETOPT_VAL_NODELAY, 45 | GETOPT_VAL_INTERVAL, 46 | GETOPT_VAL_RESEND, 47 | GETOPT_VAL_SOCKBUF, 48 | GETOPT_VAL_KEEPALIVE, 49 | GETOPT_VAL_SYSLOG, 50 | GETOPT_VAL_HELP, 51 | GETOPT_VAL_VERSION 52 | }; 53 | 54 | /** @brief Parses the command line and set the config accordingly */ 55 | void parse_commandline(int, char **); 56 | 57 | void usage(); 58 | 59 | #endif /* _COMMANDLINE_H_ */ 60 | -------------------------------------------------------------------------------- /contributors.md: -------------------------------------------------------------------------------- 1 | Thank you to all contributors: 2 | ------------------------------ 3 | [liudengfeng](https://github.com/liudf0716) 4 | 5 | [zhangzengfei](https://github.com/gigibox) 6 | 7 | [HuangYeWuDeng](https://github.com/ihacklog) 8 | 9 | [qihongwei](https://github.com/joycechu) 10 | -------------------------------------------------------------------------------- /debug.c: -------------------------------------------------------------------------------- 1 | /* vim: set et ts=4 sts=4 sw=4 : */ 2 | /********************************************************************\ 3 | * This program is free software; you can redistribute it and/or * 4 | * modify it under the terms of the GNU General Public License as * 5 | * published by the Free Software Foundation; either version 2 of * 6 | * the License, or (at your option) any later version. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU General Public License* 14 | * along with this program; if not, contact: * 15 | * * 16 | * Free Software Foundation Voice: +1-617-542-5942 * 17 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 18 | * Boston, MA 02111-1307, USA gnu@gnu.org * 19 | * * 20 | \********************************************************************/ 21 | 22 | /** @file debug.c 23 | @brief Debug output routines 24 | @author Copyright (C) 2004 Philippe April 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "debug.h" 36 | 37 | debugconf_t debugconf = { 38 | .debuglevel = LOG_INFO, 39 | .log_stderr = 1, 40 | .log_syslog = 0, 41 | .syslog_facility = LOG_USER 42 | }; 43 | 44 | /** @internal 45 | Do not use directly, use the debug macro */ 46 | void 47 | _debug(const char *filename, int line, int level, const char *format, ...) 48 | { 49 | char buf[28]; 50 | va_list vlist; 51 | time_t ts; 52 | sigset_t block_chld; 53 | 54 | time(&ts); 55 | 56 | if (debugconf.debuglevel >= level) { 57 | sigemptyset(&block_chld); 58 | sigaddset(&block_chld, SIGCHLD); 59 | sigprocmask(SIG_BLOCK, &block_chld, NULL); 60 | 61 | if (level <= LOG_WARNING) { 62 | fprintf(stderr, "[%d][%.24s][%u](%s:%d) ", level, ctime_r(&ts, buf), getpid(), 63 | filename, line); 64 | va_start(vlist, format); 65 | vfprintf(stderr, format, vlist); 66 | va_end(vlist); 67 | fputc('\n', stderr); 68 | } else if (debugconf.log_stderr) { 69 | fprintf(stderr, "[%d][%.24s][%u](%s:%d) ", level, ctime_r(&ts, buf), getpid(), 70 | filename, line); 71 | va_start(vlist, format); 72 | vfprintf(stderr, format, vlist); 73 | va_end(vlist); 74 | fputc('\n', stderr); 75 | } 76 | 77 | if (debugconf.log_syslog) { 78 | openlog("xkcptun", LOG_PID, debugconf.syslog_facility); 79 | va_start(vlist, format); 80 | vsyslog(level, format, vlist); 81 | va_end(vlist); 82 | closelog(); 83 | } 84 | 85 | sigprocmask(SIG_UNBLOCK, &block_chld, NULL); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /debug.h: -------------------------------------------------------------------------------- 1 | /* vim: set et ts=4 sts=4 sw=4 : */ 2 | /********************************************************************\ 3 | * This program is free software; you can redistribute it and/or * 4 | * modify it under the terms of the GNU General Public License as * 5 | * published by the Free Software Foundation; either version 2 of * 6 | * the License, or (at your option) any later version. * 7 | * * 8 | * This program is distributed in the hope that it will be useful, * 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 11 | * GNU General Public License for more details. * 12 | * * 13 | * You should have received a copy of the GNU General Public License* 14 | * along with this program; if not, contact: * 15 | * * 16 | * Free Software Foundation Voice: +1-617-542-5942 * 17 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 18 | * Boston, MA 02111-1307, USA gnu@gnu.org * 19 | * * 20 | \********************************************************************/ 21 | 22 | /** @file debug.h 23 | @brief Debug output routines 24 | @author Copyright (C) 2004 Philippe April 25 | @author Copyright (C) 2016 Dengfeng Liu 26 | */ 27 | 28 | #ifndef _WIFIDOG_DEBUG_H_ 29 | #define _WIFIDOG_DEBUG_H_ 30 | 31 | #include 32 | 33 | #define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) 34 | 35 | typedef struct _debug_conf { 36 | int debuglevel; /**< @brief Debug information verbosity */ 37 | int log_stderr; /**< @brief Output log to stdout */ 38 | int log_syslog; /**< @brief Output log to syslog */ 39 | int syslog_facility; /**< @brief facility to use when using syslog for logging */ 40 | } debugconf_t; 41 | 42 | extern debugconf_t debugconf; 43 | 44 | /** Used to output messages. 45 | * The messages will include the filename and line number, and will be sent to syslog if so configured in the config file 46 | * @param level Debug level 47 | * @param format... sprintf like format string 48 | */ 49 | #define debug(level, format...) _debug(__FILENAME__, __LINE__, level, format) 50 | 51 | /** @internal */ 52 | void _debug(const char *, int, int, const char *, ...); 53 | 54 | #endif /* _DEBUG_H_ */ 55 | -------------------------------------------------------------------------------- /ikcp.h: -------------------------------------------------------------------------------- 1 | //===================================================================== 2 | // 3 | // KCP - A Better ARQ Protocol Implementation 4 | // skywind3000 (at) gmail.com, 2010-2011 5 | // 6 | // Features: 7 | // + Average RTT reduce 30% - 40% vs traditional ARQ like tcp. 8 | // + Maximum RTT reduce three times vs tcp. 9 | // + Lightweight, distributed as a single source file. 10 | // 11 | //===================================================================== 12 | #ifndef __IKCP_H__ 13 | #define __IKCP_H__ 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | //===================================================================== 21 | // 32BIT INTEGER DEFINITION 22 | //===================================================================== 23 | #ifndef __INTEGER_32_BITS__ 24 | #define __INTEGER_32_BITS__ 25 | #if defined(_WIN64) || defined(WIN64) || defined(__amd64__) || \ 26 | defined(__x86_64) || defined(__x86_64__) || defined(_M_IA64) || \ 27 | defined(_M_AMD64) 28 | typedef unsigned int ISTDUINT32; 29 | typedef int ISTDINT32; 30 | #elif defined(_WIN32) || defined(WIN32) || defined(__i386__) || \ 31 | defined(__i386) || defined(_M_X86) 32 | typedef unsigned long ISTDUINT32; 33 | typedef long ISTDINT32; 34 | #elif defined(__MACOS__) 35 | typedef UInt32 ISTDUINT32; 36 | typedef SInt32 ISTDINT32; 37 | #elif defined(__APPLE__) && defined(__MACH__) 38 | #include 39 | typedef u_int32_t ISTDUINT32; 40 | typedef int32_t ISTDINT32; 41 | #elif defined(__BEOS__) 42 | #include 43 | typedef u_int32_t ISTDUINT32; 44 | typedef int32_t ISTDINT32; 45 | #elif (defined(_MSC_VER) || defined(__BORLANDC__)) && (!defined(__MSDOS__)) 46 | typedef unsigned __int32 ISTDUINT32; 47 | typedef __int32 ISTDINT32; 48 | #elif defined(__GNUC__) 49 | #include 50 | typedef uint32_t ISTDUINT32; 51 | typedef int32_t ISTDINT32; 52 | #else 53 | typedef unsigned long ISTDUINT32; 54 | typedef long ISTDINT32; 55 | #endif 56 | #endif 57 | 58 | 59 | //===================================================================== 60 | // Integer Definition 61 | //===================================================================== 62 | #ifndef __IINT8_DEFINED 63 | #define __IINT8_DEFINED 64 | typedef char IINT8; 65 | #endif 66 | 67 | #ifndef __IUINT8_DEFINED 68 | #define __IUINT8_DEFINED 69 | typedef unsigned char IUINT8; 70 | #endif 71 | 72 | #ifndef __IUINT16_DEFINED 73 | #define __IUINT16_DEFINED 74 | typedef unsigned short IUINT16; 75 | #endif 76 | 77 | #ifndef __IINT16_DEFINED 78 | #define __IINT16_DEFINED 79 | typedef short IINT16; 80 | #endif 81 | 82 | #ifndef __IINT32_DEFINED 83 | #define __IINT32_DEFINED 84 | typedef ISTDINT32 IINT32; 85 | #endif 86 | 87 | #ifndef __IUINT32_DEFINED 88 | #define __IUINT32_DEFINED 89 | typedef ISTDUINT32 IUINT32; 90 | #endif 91 | 92 | #ifndef __IINT64_DEFINED 93 | #define __IINT64_DEFINED 94 | #if defined(_MSC_VER) || defined(__BORLANDC__) 95 | typedef __int64 IINT64; 96 | #else 97 | typedef long long IINT64; 98 | #endif 99 | #endif 100 | 101 | #ifndef __IUINT64_DEFINED 102 | #define __IUINT64_DEFINED 103 | #if defined(_MSC_VER) || defined(__BORLANDC__) 104 | typedef unsigned __int64 IUINT64; 105 | #else 106 | typedef unsigned long long IUINT64; 107 | #endif 108 | #endif 109 | 110 | #ifndef INLINE 111 | #if defined(__GNUC__) 112 | 113 | #if (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1)) 114 | #define INLINE __inline__ __attribute__((always_inline)) 115 | #else 116 | #define INLINE __inline__ 117 | #endif 118 | 119 | #elif (defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__)) 120 | #define INLINE __inline 121 | #else 122 | #define INLINE 123 | #endif 124 | #endif 125 | 126 | #if (!defined(__cplusplus)) && (!defined(inline)) 127 | #define inline INLINE 128 | #endif 129 | 130 | 131 | //===================================================================== 132 | // QUEUE DEFINITION 133 | //===================================================================== 134 | #ifndef __IQUEUE_DEF__ 135 | #define __IQUEUE_DEF__ 136 | 137 | struct IQUEUEHEAD { 138 | struct IQUEUEHEAD *next, *prev; 139 | }; 140 | 141 | typedef struct IQUEUEHEAD iqueue_head; 142 | 143 | 144 | //--------------------------------------------------------------------- 145 | // queue init 146 | //--------------------------------------------------------------------- 147 | #define IQUEUE_HEAD_INIT(name) { &(name), &(name) } 148 | #define IQUEUE_HEAD(name) \ 149 | struct IQUEUEHEAD name = IQUEUE_HEAD_INIT(name) 150 | 151 | #define IQUEUE_INIT(ptr) ( \ 152 | (ptr)->next = (ptr), (ptr)->prev = (ptr)) 153 | 154 | #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 155 | 156 | #define ICONTAINEROF(ptr, type, member) ( \ 157 | (type*)( ((char*)((type*)ptr)) - IOFFSETOF(type, member)) ) 158 | 159 | #define IQUEUE_ENTRY(ptr, type, member) ICONTAINEROF(ptr, type, member) 160 | 161 | 162 | //--------------------------------------------------------------------- 163 | // queue operation 164 | //--------------------------------------------------------------------- 165 | #define IQUEUE_ADD(node, head) ( \ 166 | (node)->prev = (head), (node)->next = (head)->next, \ 167 | (head)->next->prev = (node), (head)->next = (node)) 168 | 169 | #define IQUEUE_ADD_TAIL(node, head) ( \ 170 | (node)->prev = (head)->prev, (node)->next = (head), \ 171 | (head)->prev->next = (node), (head)->prev = (node)) 172 | 173 | #define IQUEUE_DEL_BETWEEN(p, n) ((n)->prev = (p), (p)->next = (n)) 174 | 175 | #define IQUEUE_DEL(entry) (\ 176 | (entry)->next->prev = (entry)->prev, \ 177 | (entry)->prev->next = (entry)->next, \ 178 | (entry)->next = 0, (entry)->prev = 0) 179 | 180 | #define IQUEUE_DEL_INIT(entry) do { \ 181 | IQUEUE_DEL(entry); IQUEUE_INIT(entry); } while (0) 182 | 183 | #define IQUEUE_IS_EMPTY(entry) ((entry) == (entry)->next) 184 | 185 | #define iqueue_init IQUEUE_INIT 186 | #define iqueue_entry IQUEUE_ENTRY 187 | #define iqueue_add IQUEUE_ADD 188 | #define iqueue_add_tail IQUEUE_ADD_TAIL 189 | #define iqueue_del IQUEUE_DEL 190 | #define iqueue_del_init IQUEUE_DEL_INIT 191 | #define iqueue_is_empty IQUEUE_IS_EMPTY 192 | 193 | #define IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) \ 194 | for ((iterator) = iqueue_entry((head)->next, TYPE, MEMBER); \ 195 | &((iterator)->MEMBER) != (head); \ 196 | (iterator) = iqueue_entry((iterator)->MEMBER.next, TYPE, MEMBER)) 197 | 198 | #define iqueue_foreach(iterator, head, TYPE, MEMBER) \ 199 | IQUEUE_FOREACH(iterator, head, TYPE, MEMBER) 200 | 201 | #define iqueue_foreach_entry(pos, head) \ 202 | for( (pos) = (head)->next; (pos) != (head) ; (pos) = (pos)->next ) 203 | 204 | 205 | #define __iqueue_splice(list, head) do { \ 206 | iqueue_head *first = (list)->next, *last = (list)->prev; \ 207 | iqueue_head *at = (head)->next; \ 208 | (first)->prev = (head), (head)->next = (first); \ 209 | (last)->next = (at), (at)->prev = (last); } while (0) 210 | 211 | #define iqueue_splice(list, head) do { \ 212 | if (!iqueue_is_empty(list)) __iqueue_splice(list, head); } while (0) 213 | 214 | #define iqueue_splice_init(list, head) do { \ 215 | iqueue_splice(list, head); iqueue_init(list); } while (0) 216 | 217 | 218 | #ifdef _MSC_VER 219 | #pragma warning(disable:4311) 220 | #pragma warning(disable:4312) 221 | #pragma warning(disable:4996) 222 | #endif 223 | 224 | #endif 225 | 226 | 227 | //--------------------------------------------------------------------- 228 | // WORD ORDER 229 | //--------------------------------------------------------------------- 230 | #ifndef IWORDS_BIG_ENDIAN 231 | #ifdef _BIG_ENDIAN_ 232 | #if _BIG_ENDIAN_ 233 | #define IWORDS_BIG_ENDIAN 1 234 | #endif 235 | #endif 236 | #ifndef IWORDS_BIG_ENDIAN 237 | #if defined(__hppa__) || \ 238 | defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ 239 | (defined(__mips__) && defined(__MIPSEB__)) || \ 240 | defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ 241 | defined(__sparc__) || defined(__powerpc__) || \ 242 | defined(__mc68000__) || defined(__s390x__) || defined(__s390__) 243 | #define IWORDS_BIG_ENDIAN 1 244 | #endif 245 | #endif 246 | #ifndef IWORDS_BIG_ENDIAN 247 | #define IWORDS_BIG_ENDIAN 0 248 | #endif 249 | #endif 250 | 251 | 252 | 253 | //===================================================================== 254 | // SEGMENT 255 | //===================================================================== 256 | struct IKCPSEG 257 | { 258 | struct IQUEUEHEAD node; 259 | IUINT32 conv; 260 | IUINT32 cmd; 261 | IUINT32 frg; 262 | IUINT32 wnd; 263 | IUINT32 ts; 264 | IUINT32 sn; 265 | IUINT32 una; 266 | IUINT32 len; 267 | IUINT32 resendts; 268 | IUINT32 rto; 269 | IUINT32 fastack; 270 | IUINT32 xmit; 271 | char data[1]; 272 | }; 273 | 274 | 275 | //--------------------------------------------------------------------- 276 | // IKCPCB 277 | //--------------------------------------------------------------------- 278 | struct IKCPCB 279 | { 280 | IUINT32 conv, mtu, mss, state; 281 | IUINT32 snd_una, snd_nxt, rcv_nxt; 282 | IUINT32 ts_recent, ts_lastack, ssthresh; 283 | IINT32 rx_rttval, rx_srtt, rx_rto, rx_minrto; 284 | IUINT32 snd_wnd, rcv_wnd, rmt_wnd, cwnd, probe; 285 | IUINT32 current, interval, ts_flush, xmit; 286 | IUINT32 nrcv_buf, nsnd_buf; 287 | IUINT32 nrcv_que, nsnd_que; 288 | IUINT32 nodelay, updated; 289 | IUINT32 ts_probe, probe_wait; 290 | IUINT32 dead_link, incr; 291 | struct IQUEUEHEAD snd_queue; 292 | struct IQUEUEHEAD rcv_queue; 293 | struct IQUEUEHEAD snd_buf; 294 | struct IQUEUEHEAD rcv_buf; 295 | IUINT32 *acklist; 296 | IUINT32 ackcount; 297 | IUINT32 ackblock; 298 | void *user; 299 | char *buffer; 300 | int fastresend; 301 | int nocwnd, stream; 302 | int logmask; 303 | int (*output)(const char *buf, int len, struct IKCPCB *kcp, void *user); 304 | void (*writelog)(const char *log, struct IKCPCB *kcp, void *user); 305 | }; 306 | 307 | 308 | typedef struct IKCPCB ikcpcb; 309 | 310 | #define IKCP_LOG_OUTPUT 1 311 | #define IKCP_LOG_INPUT 2 312 | #define IKCP_LOG_SEND 4 313 | #define IKCP_LOG_RECV 8 314 | #define IKCP_LOG_IN_DATA 16 315 | #define IKCP_LOG_IN_ACK 32 316 | #define IKCP_LOG_IN_PROBE 64 317 | #define IKCP_LOG_IN_WINS 128 318 | #define IKCP_LOG_OUT_DATA 256 319 | #define IKCP_LOG_OUT_ACK 512 320 | #define IKCP_LOG_OUT_PROBE 1024 321 | #define IKCP_LOG_OUT_WINS 2048 322 | 323 | #ifdef __cplusplus 324 | extern "C" { 325 | #endif 326 | 327 | const char *ikcp_decode32u(const char *p, IUINT32 *l); 328 | typedef struct IKCPSEG IKCPSEG; 329 | 330 | //--------------------------------------------------------------------- 331 | // interface 332 | //--------------------------------------------------------------------- 333 | 334 | // create a new kcp control object, 'conv' must equal in two endpoint 335 | // from the same connection. 'user' will be passed to the output callback 336 | // output callback can be setup like this: 'kcp->output = my_udp_output' 337 | ikcpcb* ikcp_create(IUINT32 conv, void *user); 338 | 339 | // release kcp control object 340 | void ikcp_release(ikcpcb *kcp); 341 | 342 | // set output callback, which will be invoked by kcp 343 | void ikcp_setoutput(ikcpcb *kcp, int (*output)(const char *buf, int len, 344 | ikcpcb *kcp, void *user)); 345 | 346 | // user/upper level recv: returns size, returns below zero for EAGAIN 347 | int ikcp_recv(ikcpcb *kcp, char *buffer, int len); 348 | 349 | // user/upper level send, returns below zero for error 350 | int ikcp_send(ikcpcb *kcp, const char *buffer, int len); 351 | 352 | // update state (call it repeatedly, every 10ms-100ms), or you can ask 353 | // ikcp_check when to call it again (without ikcp_input/_send calling). 354 | // 'current' - current timestamp in millisec. 355 | void ikcp_update(ikcpcb *kcp, IUINT32 current); 356 | 357 | // Determine when should you invoke ikcp_update: 358 | // returns when you should invoke ikcp_update in millisec, if there 359 | // is no ikcp_input/_send calling. you can call ikcp_update in that 360 | // time, instead of call update repeatly. 361 | // Important to reduce unnacessary ikcp_update invoking. use it to 362 | // schedule ikcp_update (eg. implementing an epoll-like mechanism, 363 | // or optimize ikcp_update when handling massive kcp connections) 364 | IUINT32 ikcp_check(const ikcpcb *kcp, IUINT32 current); 365 | 366 | // when you received a low level packet (eg. UDP packet), call it 367 | int ikcp_input(ikcpcb *kcp, const char *data, long size); 368 | 369 | // flush pending data 370 | void ikcp_flush(ikcpcb *kcp); 371 | 372 | // check the size of next message in the recv queue 373 | int ikcp_peeksize(const ikcpcb *kcp); 374 | 375 | // change MTU size, default is 1400 376 | int ikcp_setmtu(ikcpcb *kcp, int mtu); 377 | 378 | // set maximum window size: sndwnd=32, rcvwnd=32 by default 379 | int ikcp_wndsize(ikcpcb *kcp, int sndwnd, int rcvwnd); 380 | 381 | // get how many packet is waiting to be sent 382 | int ikcp_waitsnd(const ikcpcb *kcp); 383 | 384 | // fastest: ikcp_nodelay(kcp, 1, 20, 2, 1) 385 | // nodelay: 0:disable(default), 1:enable 386 | // interval: internal update timer interval in millisec, default is 100ms 387 | // resend: 0:disable fast resend(default), 1:enable fast resend 388 | // nc: 0:normal congestion control(default), 1:disable congestion control 389 | int ikcp_nodelay(ikcpcb *kcp, int nodelay, int interval, int resend, int nc); 390 | 391 | int ikcp_rcvbuf_count(const ikcpcb *kcp); 392 | int ikcp_sndbuf_count(const ikcpcb *kcp); 393 | 394 | void ikcp_log(ikcpcb *kcp, int mask, const char *fmt, ...); 395 | 396 | // setup allocator 397 | void ikcp_allocator(void* (*new_malloc)(size_t), void (*new_free)(void*)); 398 | void ikcp_qprint(const char *name, const struct IQUEUEHEAD *head); 399 | void ikcp_parse_data(ikcpcb *kcp, IKCPSEG *newseg); 400 | int ikcp_interval(ikcpcb *kcp, int interval); 401 | 402 | // read conv 403 | IUINT32 ikcp_getconv(const void *ptr); 404 | 405 | 406 | #ifdef __cplusplus 407 | } 408 | #endif 409 | 410 | #endif 411 | 412 | 413 | -------------------------------------------------------------------------------- /json.c: -------------------------------------------------------------------------------- 1 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 2 | * 3 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 4 | * https://github.com/udp/json-parser 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | */ 29 | 30 | #include "json.h" 31 | 32 | #ifdef _MSC_VER 33 | #ifndef _CRT_SECURE_NO_WARNINGS 34 | #define _CRT_SECURE_NO_WARNINGS 35 | #endif 36 | #endif 37 | 38 | const struct _json_value json_value_none; 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | typedef unsigned int json_uchar; 46 | 47 | static unsigned char hex_value (json_char c) 48 | { 49 | if (isdigit(c)) 50 | return c - '0'; 51 | 52 | switch (c) { 53 | case 'a': case 'A': return 0x0A; 54 | case 'b': case 'B': return 0x0B; 55 | case 'c': case 'C': return 0x0C; 56 | case 'd': case 'D': return 0x0D; 57 | case 'e': case 'E': return 0x0E; 58 | case 'f': case 'F': return 0x0F; 59 | default: return 0xFF; 60 | } 61 | } 62 | 63 | typedef struct 64 | { 65 | unsigned long used_memory; 66 | 67 | unsigned int uint_max; 68 | unsigned long ulong_max; 69 | 70 | json_settings settings; 71 | int first_pass; 72 | 73 | const json_char * ptr; 74 | unsigned int cur_line, cur_col; 75 | 76 | } json_state; 77 | 78 | static void * default_alloc (size_t size, int zero, void * user_data) 79 | { 80 | return zero ? calloc (1, size) : malloc (size); 81 | } 82 | 83 | static void default_free (void * ptr, void * user_data) 84 | { 85 | free (ptr); 86 | } 87 | 88 | static void * json_alloc (json_state * state, unsigned long size, int zero) 89 | { 90 | if ((state->ulong_max - state->used_memory) < size) 91 | return 0; 92 | 93 | if (state->settings.max_memory 94 | && (state->used_memory += size) > state->settings.max_memory) 95 | { 96 | return 0; 97 | } 98 | 99 | return state->settings.mem_alloc (size, zero, state->settings.user_data); 100 | } 101 | 102 | static int new_value (json_state * state, 103 | json_value ** top, json_value ** root, json_value ** alloc, 104 | json_type type) 105 | { 106 | json_value * value; 107 | int values_size; 108 | 109 | if (!state->first_pass) 110 | { 111 | value = *top = *alloc; 112 | *alloc = (*alloc)->_reserved.next_alloc; 113 | 114 | if (!*root) 115 | *root = value; 116 | 117 | switch (value->type) 118 | { 119 | case json_array: 120 | 121 | if (value->u.array.length == 0) 122 | break; 123 | 124 | if (! (value->u.array.values = (json_value **) json_alloc 125 | (state, value->u.array.length * sizeof (json_value *), 0)) ) 126 | { 127 | return 0; 128 | } 129 | 130 | value->u.array.length = 0; 131 | break; 132 | 133 | case json_object: 134 | 135 | if (value->u.object.length == 0) 136 | break; 137 | 138 | values_size = sizeof (*value->u.object.values) * value->u.object.length; 139 | 140 | if (! (value->u.object.values = (json_object_entry *) json_alloc 141 | (state, values_size + ((unsigned long) value->u.object.values), 0)) ) 142 | { 143 | return 0; 144 | } 145 | 146 | value->_reserved.object_mem = (*(char **) &value->u.object.values) + values_size; 147 | 148 | value->u.object.length = 0; 149 | break; 150 | 151 | case json_string: 152 | 153 | if (! (value->u.string.ptr = (json_char *) json_alloc 154 | (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) 155 | { 156 | return 0; 157 | } 158 | 159 | value->u.string.length = 0; 160 | break; 161 | 162 | default: 163 | break; 164 | }; 165 | 166 | return 1; 167 | } 168 | 169 | if (! (value = (json_value *) json_alloc 170 | (state, sizeof (json_value) + state->settings.value_extra, 1))) 171 | { 172 | return 0; 173 | } 174 | 175 | if (!*root) 176 | *root = value; 177 | 178 | value->type = type; 179 | value->parent = *top; 180 | 181 | #ifdef JSON_TRACK_SOURCE 182 | value->line = state->cur_line; 183 | value->col = state->cur_col; 184 | #endif 185 | 186 | if (*alloc) 187 | (*alloc)->_reserved.next_alloc = value; 188 | 189 | *alloc = *top = value; 190 | 191 | return 1; 192 | } 193 | 194 | #define whitespace \ 195 | case '\n': ++ state.cur_line; state.cur_col = 0; \ 196 | case ' ': case '\t': case '\r' 197 | 198 | #define string_add(b) \ 199 | do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); 200 | 201 | #define line_and_col \ 202 | state.cur_line, state.cur_col 203 | 204 | static const long 205 | flag_next = 1 << 0, 206 | flag_reproc = 1 << 1, 207 | flag_need_comma = 1 << 2, 208 | flag_seek_value = 1 << 3, 209 | flag_escaped = 1 << 4, 210 | flag_string = 1 << 5, 211 | flag_need_colon = 1 << 6, 212 | flag_done = 1 << 7, 213 | flag_num_negative = 1 << 8, 214 | flag_num_zero = 1 << 9, 215 | flag_num_e = 1 << 10, 216 | flag_num_e_got_sign = 1 << 11, 217 | flag_num_e_negative = 1 << 12, 218 | flag_line_comment = 1 << 13, 219 | flag_block_comment = 1 << 14; 220 | 221 | json_value * json_parse_ex (json_settings * settings, 222 | const json_char * json, 223 | size_t length, 224 | char * error_buf) 225 | { 226 | json_char error [json_error_max]; 227 | const json_char * end; 228 | json_value * top, * root, * alloc = 0; 229 | json_state state = { 0 }; 230 | long flags; 231 | long num_digits = 0, num_e = 0; 232 | json_int_t num_fraction = 0; 233 | 234 | /* Skip UTF-8 BOM 235 | */ 236 | if (length >= 3 && ((unsigned char) json [0]) == 0xEF 237 | && ((unsigned char) json [1]) == 0xBB 238 | && ((unsigned char) json [2]) == 0xBF) 239 | { 240 | json += 3; 241 | length -= 3; 242 | } 243 | 244 | error[0] = '\0'; 245 | end = (json + length); 246 | 247 | memcpy (&state.settings, settings, sizeof (json_settings)); 248 | 249 | if (!state.settings.mem_alloc) 250 | state.settings.mem_alloc = default_alloc; 251 | 252 | if (!state.settings.mem_free) 253 | state.settings.mem_free = default_free; 254 | 255 | memset (&state.uint_max, 0xFF, sizeof (state.uint_max)); 256 | memset (&state.ulong_max, 0xFF, sizeof (state.ulong_max)); 257 | 258 | state.uint_max -= 8; /* limit of how much can be added before next check */ 259 | state.ulong_max -= 8; 260 | 261 | for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) 262 | { 263 | json_uchar uchar; 264 | unsigned char uc_b1, uc_b2, uc_b3, uc_b4; 265 | json_char * string = 0; 266 | unsigned int string_length = 0; 267 | 268 | top = root = 0; 269 | flags = flag_seek_value; 270 | 271 | state.cur_line = 1; 272 | 273 | for (state.ptr = json ;; ++ state.ptr) 274 | { 275 | json_char b = (state.ptr == end ? 0 : *state.ptr); 276 | 277 | if (flags & flag_string) 278 | { 279 | if (!b) 280 | { sprintf (error, "Unexpected EOF in string (at %d:%d)", line_and_col); 281 | goto e_failed; 282 | } 283 | 284 | if (string_length > state.uint_max) 285 | goto e_overflow; 286 | 287 | if (flags & flag_escaped) 288 | { 289 | flags &= ~ flag_escaped; 290 | 291 | switch (b) 292 | { 293 | case 'b': string_add ('\b'); break; 294 | case 'f': string_add ('\f'); break; 295 | case 'n': string_add ('\n'); break; 296 | case 'r': string_add ('\r'); break; 297 | case 't': string_add ('\t'); break; 298 | case 'u': 299 | 300 | if (end - state.ptr < 4 || 301 | (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || 302 | (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || 303 | (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || 304 | (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) 305 | { 306 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); 307 | goto e_failed; 308 | } 309 | 310 | uc_b1 = (uc_b1 << 4) | uc_b2; 311 | uc_b2 = (uc_b3 << 4) | uc_b4; 312 | uchar = (uc_b1 << 8) | uc_b2; 313 | 314 | if ((uchar & 0xF800) == 0xD800) { 315 | json_uchar uchar2; 316 | 317 | if (end - state.ptr < 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' || 318 | (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || 319 | (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || 320 | (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || 321 | (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) 322 | { 323 | sprintf (error, "Invalid character value `%c` (at %d:%d)", b, line_and_col); 324 | goto e_failed; 325 | } 326 | 327 | uc_b1 = (uc_b1 << 4) | uc_b2; 328 | uc_b2 = (uc_b3 << 4) | uc_b4; 329 | uchar2 = (uc_b1 << 8) | uc_b2; 330 | 331 | uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); 332 | } 333 | 334 | if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F)) 335 | { 336 | string_add ((json_char) uchar); 337 | break; 338 | } 339 | 340 | if (uchar <= 0x7FF) 341 | { 342 | if (state.first_pass) 343 | string_length += 2; 344 | else 345 | { string [string_length ++] = 0xC0 | (uchar >> 6); 346 | string [string_length ++] = 0x80 | (uchar & 0x3F); 347 | } 348 | 349 | break; 350 | } 351 | 352 | if (uchar <= 0xFFFF) { 353 | if (state.first_pass) 354 | string_length += 3; 355 | else 356 | { string [string_length ++] = 0xE0 | (uchar >> 12); 357 | string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); 358 | string [string_length ++] = 0x80 | (uchar & 0x3F); 359 | } 360 | 361 | break; 362 | } 363 | 364 | if (state.first_pass) 365 | string_length += 4; 366 | else 367 | { string [string_length ++] = 0xF0 | (uchar >> 18); 368 | string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F); 369 | string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); 370 | string [string_length ++] = 0x80 | (uchar & 0x3F); 371 | } 372 | 373 | break; 374 | 375 | default: 376 | string_add (b); 377 | }; 378 | 379 | continue; 380 | } 381 | 382 | if (b == '\\') 383 | { 384 | flags |= flag_escaped; 385 | continue; 386 | } 387 | 388 | if (b == '"') 389 | { 390 | if (!state.first_pass) 391 | string [string_length] = 0; 392 | 393 | flags &= ~ flag_string; 394 | string = 0; 395 | 396 | switch (top->type) 397 | { 398 | case json_string: 399 | 400 | top->u.string.length = string_length; 401 | flags |= flag_next; 402 | 403 | break; 404 | 405 | case json_object: 406 | 407 | if (state.first_pass) 408 | (*(json_char **) &top->u.object.values) += string_length + 1; 409 | else 410 | { 411 | top->u.object.values [top->u.object.length].name 412 | = (json_char *) top->_reserved.object_mem; 413 | 414 | top->u.object.values [top->u.object.length].name_length 415 | = string_length; 416 | 417 | (*(json_char **) &top->_reserved.object_mem) += string_length + 1; 418 | } 419 | 420 | flags |= flag_seek_value | flag_need_colon; 421 | continue; 422 | 423 | default: 424 | break; 425 | }; 426 | } 427 | else 428 | { 429 | string_add (b); 430 | continue; 431 | } 432 | } 433 | 434 | if (state.settings.settings & json_enable_comments) 435 | { 436 | if (flags & (flag_line_comment | flag_block_comment)) 437 | { 438 | if (flags & flag_line_comment) 439 | { 440 | if (b == '\r' || b == '\n' || !b) 441 | { 442 | flags &= ~ flag_line_comment; 443 | -- state.ptr; /* so null can be reproc'd */ 444 | } 445 | 446 | continue; 447 | } 448 | 449 | if (flags & flag_block_comment) 450 | { 451 | if (!b) 452 | { sprintf (error, "%d:%d: Unexpected EOF in block comment", line_and_col); 453 | goto e_failed; 454 | } 455 | 456 | if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/') 457 | { 458 | flags &= ~ flag_block_comment; 459 | ++ state.ptr; /* skip closing sequence */ 460 | } 461 | 462 | continue; 463 | } 464 | } 465 | else if (b == '/') 466 | { 467 | if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) 468 | { sprintf (error, "%d:%d: Comment not allowed here", line_and_col); 469 | goto e_failed; 470 | } 471 | 472 | if (++ state.ptr == end) 473 | { sprintf (error, "%d:%d: EOF unexpected", line_and_col); 474 | goto e_failed; 475 | } 476 | 477 | switch (b = *state.ptr) 478 | { 479 | case '/': 480 | flags |= flag_line_comment; 481 | continue; 482 | 483 | case '*': 484 | flags |= flag_block_comment; 485 | continue; 486 | 487 | default: 488 | sprintf (error, "%d:%d: Unexpected `%c` in comment opening sequence", line_and_col, b); 489 | goto e_failed; 490 | }; 491 | } 492 | } 493 | 494 | if (flags & flag_done) 495 | { 496 | if (!b) 497 | break; 498 | 499 | switch (b) 500 | { 501 | whitespace: 502 | continue; 503 | 504 | default: 505 | 506 | sprintf (error, "%d:%d: Trailing garbage: `%c`", 507 | state.cur_line, state.cur_col, b); 508 | 509 | goto e_failed; 510 | }; 511 | } 512 | 513 | if (flags & flag_seek_value) 514 | { 515 | switch (b) 516 | { 517 | whitespace: 518 | continue; 519 | 520 | case ']': 521 | 522 | if (top && top->type == json_array) 523 | flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; 524 | else 525 | { sprintf (error, "%d:%d: Unexpected ]", line_and_col); 526 | goto e_failed; 527 | } 528 | 529 | break; 530 | 531 | default: 532 | 533 | if (flags & flag_need_comma) 534 | { 535 | if (b == ',') 536 | { flags &= ~ flag_need_comma; 537 | continue; 538 | } 539 | else 540 | { 541 | sprintf (error, "%d:%d: Expected , before %c", 542 | state.cur_line, state.cur_col, b); 543 | 544 | goto e_failed; 545 | } 546 | } 547 | 548 | if (flags & flag_need_colon) 549 | { 550 | if (b == ':') 551 | { flags &= ~ flag_need_colon; 552 | continue; 553 | } 554 | else 555 | { 556 | sprintf (error, "%d:%d: Expected : before %c", 557 | state.cur_line, state.cur_col, b); 558 | 559 | goto e_failed; 560 | } 561 | } 562 | 563 | flags &= ~ flag_seek_value; 564 | 565 | switch (b) 566 | { 567 | case '{': 568 | 569 | if (!new_value (&state, &top, &root, &alloc, json_object)) 570 | goto e_alloc_failure; 571 | 572 | continue; 573 | 574 | case '[': 575 | 576 | if (!new_value (&state, &top, &root, &alloc, json_array)) 577 | goto e_alloc_failure; 578 | 579 | flags |= flag_seek_value; 580 | continue; 581 | 582 | case '"': 583 | 584 | if (!new_value (&state, &top, &root, &alloc, json_string)) 585 | goto e_alloc_failure; 586 | 587 | flags |= flag_string; 588 | 589 | string = top->u.string.ptr; 590 | string_length = 0; 591 | 592 | continue; 593 | 594 | case 't': 595 | 596 | if ((end - state.ptr) < 3 || *(++ state.ptr) != 'r' || 597 | *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e') 598 | { 599 | goto e_unknown_value; 600 | } 601 | 602 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 603 | goto e_alloc_failure; 604 | 605 | top->u.boolean = 1; 606 | 607 | flags |= flag_next; 608 | break; 609 | 610 | case 'f': 611 | 612 | if ((end - state.ptr) < 4 || *(++ state.ptr) != 'a' || 613 | *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' || 614 | *(++ state.ptr) != 'e') 615 | { 616 | goto e_unknown_value; 617 | } 618 | 619 | if (!new_value (&state, &top, &root, &alloc, json_boolean)) 620 | goto e_alloc_failure; 621 | 622 | flags |= flag_next; 623 | break; 624 | 625 | case 'n': 626 | 627 | if ((end - state.ptr) < 3 || *(++ state.ptr) != 'u' || 628 | *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l') 629 | { 630 | goto e_unknown_value; 631 | } 632 | 633 | if (!new_value (&state, &top, &root, &alloc, json_null)) 634 | goto e_alloc_failure; 635 | 636 | flags |= flag_next; 637 | break; 638 | 639 | default: 640 | 641 | if (isdigit (b) || b == '-') 642 | { 643 | if (!new_value (&state, &top, &root, &alloc, json_integer)) 644 | goto e_alloc_failure; 645 | 646 | if (!state.first_pass) 647 | { 648 | while (isdigit (b) || b == '+' || b == '-' 649 | || b == 'e' || b == 'E' || b == '.') 650 | { 651 | if ( (++ state.ptr) == end) 652 | { 653 | b = 0; 654 | break; 655 | } 656 | 657 | b = *state.ptr; 658 | } 659 | 660 | flags |= flag_next | flag_reproc; 661 | break; 662 | } 663 | 664 | flags &= ~ (flag_num_negative | flag_num_e | 665 | flag_num_e_got_sign | flag_num_e_negative | 666 | flag_num_zero); 667 | 668 | num_digits = 0; 669 | num_fraction = 0; 670 | num_e = 0; 671 | 672 | if (b != '-') 673 | { 674 | flags |= flag_reproc; 675 | break; 676 | } 677 | 678 | flags |= flag_num_negative; 679 | continue; 680 | } 681 | else 682 | { sprintf (error, "%d:%d: Unexpected %c when seeking value", line_and_col, b); 683 | goto e_failed; 684 | } 685 | }; 686 | }; 687 | } 688 | else 689 | { 690 | switch (top->type) 691 | { 692 | case json_object: 693 | 694 | switch (b) 695 | { 696 | whitespace: 697 | continue; 698 | 699 | case '"': 700 | 701 | if (flags & flag_need_comma) 702 | { sprintf (error, "%d:%d: Expected , before \"", line_and_col); 703 | goto e_failed; 704 | } 705 | 706 | flags |= flag_string; 707 | 708 | string = (json_char *) top->_reserved.object_mem; 709 | string_length = 0; 710 | 711 | break; 712 | 713 | case '}': 714 | 715 | flags = (flags & ~ flag_need_comma) | flag_next; 716 | break; 717 | 718 | case ',': 719 | 720 | if (flags & flag_need_comma) 721 | { 722 | flags &= ~ flag_need_comma; 723 | break; 724 | } 725 | 726 | default: 727 | sprintf (error, "%d:%d: Unexpected `%c` in object", line_and_col, b); 728 | goto e_failed; 729 | }; 730 | 731 | break; 732 | 733 | case json_integer: 734 | case json_double: 735 | 736 | if (isdigit (b)) 737 | { 738 | ++ num_digits; 739 | 740 | if (top->type == json_integer || flags & flag_num_e) 741 | { 742 | if (! (flags & flag_num_e)) 743 | { 744 | if (flags & flag_num_zero) 745 | { sprintf (error, "%d:%d: Unexpected `0` before `%c`", line_and_col, b); 746 | goto e_failed; 747 | } 748 | 749 | if (num_digits == 1 && b == '0') 750 | flags |= flag_num_zero; 751 | } 752 | else 753 | { 754 | flags |= flag_num_e_got_sign; 755 | num_e = (num_e * 10) + (b - '0'); 756 | continue; 757 | } 758 | 759 | top->u.integer = (top->u.integer * 10) + (b - '0'); 760 | continue; 761 | } 762 | 763 | num_fraction = (num_fraction * 10) + (b - '0'); 764 | continue; 765 | } 766 | 767 | if (b == '+' || b == '-') 768 | { 769 | if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) 770 | { 771 | flags |= flag_num_e_got_sign; 772 | 773 | if (b == '-') 774 | flags |= flag_num_e_negative; 775 | 776 | continue; 777 | } 778 | } 779 | else if (b == '.' && top->type == json_integer) 780 | { 781 | if (!num_digits) 782 | { sprintf (error, "%d:%d: Expected digit before `.`", line_and_col); 783 | goto e_failed; 784 | } 785 | 786 | top->type = json_double; 787 | top->u.dbl = (double) top->u.integer; 788 | 789 | num_digits = 0; 790 | continue; 791 | } 792 | 793 | if (! (flags & flag_num_e)) 794 | { 795 | if (top->type == json_double) 796 | { 797 | if (!num_digits) 798 | { sprintf (error, "%d:%d: Expected digit after `.`", line_and_col); 799 | goto e_failed; 800 | } 801 | 802 | top->u.dbl += ((double) num_fraction) / (pow (10.0, (double) num_digits)); 803 | } 804 | 805 | if (b == 'e' || b == 'E') 806 | { 807 | flags |= flag_num_e; 808 | 809 | if (top->type == json_integer) 810 | { 811 | top->type = json_double; 812 | top->u.dbl = (double) top->u.integer; 813 | } 814 | 815 | num_digits = 0; 816 | flags &= ~ flag_num_zero; 817 | 818 | continue; 819 | } 820 | } 821 | else 822 | { 823 | if (!num_digits) 824 | { sprintf (error, "%d:%d: Expected digit after `e`", line_and_col); 825 | goto e_failed; 826 | } 827 | 828 | top->u.dbl *= pow (10.0, (double) 829 | (flags & flag_num_e_negative ? - num_e : num_e)); 830 | } 831 | 832 | if (flags & flag_num_negative) 833 | { 834 | if (top->type == json_integer) 835 | top->u.integer = - top->u.integer; 836 | else 837 | top->u.dbl = - top->u.dbl; 838 | } 839 | 840 | flags |= flag_next | flag_reproc; 841 | break; 842 | 843 | default: 844 | break; 845 | }; 846 | } 847 | 848 | if (flags & flag_reproc) 849 | { 850 | flags &= ~ flag_reproc; 851 | -- state.ptr; 852 | } 853 | 854 | if (flags & flag_next) 855 | { 856 | flags = (flags & ~ flag_next) | flag_need_comma; 857 | 858 | if (!top->parent) 859 | { 860 | /* root value done */ 861 | 862 | flags |= flag_done; 863 | continue; 864 | } 865 | 866 | if (top->parent->type == json_array) 867 | flags |= flag_seek_value; 868 | 869 | if (!state.first_pass) 870 | { 871 | json_value * parent = top->parent; 872 | 873 | switch (parent->type) 874 | { 875 | case json_object: 876 | 877 | parent->u.object.values 878 | [parent->u.object.length].value = top; 879 | 880 | break; 881 | 882 | case json_array: 883 | 884 | parent->u.array.values 885 | [parent->u.array.length] = top; 886 | 887 | break; 888 | 889 | default: 890 | break; 891 | }; 892 | } 893 | 894 | if ( (++ top->parent->u.array.length) > state.uint_max) 895 | goto e_overflow; 896 | 897 | top = top->parent; 898 | 899 | continue; 900 | } 901 | } 902 | 903 | alloc = root; 904 | } 905 | 906 | return root; 907 | 908 | e_unknown_value: 909 | 910 | sprintf (error, "%d:%d: Unknown value", line_and_col); 911 | goto e_failed; 912 | 913 | e_alloc_failure: 914 | 915 | strcpy (error, "Memory allocation failure"); 916 | goto e_failed; 917 | 918 | e_overflow: 919 | 920 | sprintf (error, "%d:%d: Too long (caught overflow)", line_and_col); 921 | goto e_failed; 922 | 923 | e_failed: 924 | 925 | if (error_buf) 926 | { 927 | if (*error) 928 | strcpy (error_buf, error); 929 | else 930 | strcpy (error_buf, "Unknown error"); 931 | } 932 | 933 | if (state.first_pass) 934 | alloc = root; 935 | 936 | while (alloc) 937 | { 938 | top = alloc->_reserved.next_alloc; 939 | state.settings.mem_free (alloc, state.settings.user_data); 940 | alloc = top; 941 | } 942 | 943 | if (!state.first_pass) 944 | json_value_free_ex (&state.settings, root); 945 | 946 | return 0; 947 | } 948 | 949 | json_value * json_parse (const json_char * json, size_t length) 950 | { 951 | json_settings settings = { 0 }; 952 | return json_parse_ex (&settings, json, length, 0); 953 | } 954 | 955 | void json_value_free_ex (json_settings * settings, json_value * value) 956 | { 957 | json_value * cur_value; 958 | 959 | if (!value) 960 | return; 961 | 962 | value->parent = 0; 963 | 964 | while (value) 965 | { 966 | switch (value->type) 967 | { 968 | case json_array: 969 | 970 | if (!value->u.array.length) 971 | { 972 | settings->mem_free (value->u.array.values, settings->user_data); 973 | break; 974 | } 975 | 976 | value = value->u.array.values [-- value->u.array.length]; 977 | continue; 978 | 979 | case json_object: 980 | 981 | if (!value->u.object.length) 982 | { 983 | settings->mem_free (value->u.object.values, settings->user_data); 984 | break; 985 | } 986 | 987 | value = value->u.object.values [-- value->u.object.length].value; 988 | continue; 989 | 990 | case json_string: 991 | 992 | settings->mem_free (value->u.string.ptr, settings->user_data); 993 | break; 994 | 995 | default: 996 | break; 997 | }; 998 | 999 | cur_value = value; 1000 | value = value->parent; 1001 | settings->mem_free (cur_value, settings->user_data); 1002 | } 1003 | } 1004 | 1005 | void json_value_free (json_value * value) 1006 | { 1007 | json_settings settings = { 0 }; 1008 | settings.mem_free = default_free; 1009 | json_value_free_ex (&settings, value); 1010 | } 1011 | 1012 | -------------------------------------------------------------------------------- /json.h: -------------------------------------------------------------------------------- 1 | 2 | /* vim: set et ts=3 sw=3 sts=3 ft=c: 3 | * 4 | * Copyright (C) 2012, 2013, 2014 James McLaughlin et al. All rights reserved. 5 | * https://github.com/udp/json-parser 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | #ifndef _JSON_H 32 | #define _JSON_H 33 | 34 | #ifndef json_char 35 | #define json_char char 36 | #endif 37 | 38 | #ifndef json_int_t 39 | #ifndef _MSC_VER 40 | #include 41 | #define json_int_t int64_t 42 | #else 43 | #define json_int_t __int64 44 | #endif 45 | #endif 46 | 47 | #include 48 | 49 | #ifdef __cplusplus 50 | 51 | #include 52 | 53 | extern "C" 54 | { 55 | 56 | #endif 57 | 58 | typedef struct 59 | { 60 | unsigned long max_memory; 61 | int settings; 62 | 63 | /* Custom allocator support (leave null to use malloc/free) 64 | */ 65 | 66 | void * (* mem_alloc) (size_t, int zero, void * user_data); 67 | void (* mem_free) (void *, void * user_data); 68 | 69 | void * user_data; /* will be passed to mem_alloc and mem_free */ 70 | 71 | size_t value_extra; /* how much extra space to allocate for values? */ 72 | 73 | } json_settings; 74 | 75 | #define json_enable_comments 0x01 76 | 77 | typedef enum 78 | { 79 | json_none, 80 | json_object, 81 | json_array, 82 | json_integer, 83 | json_double, 84 | json_string, 85 | json_boolean, 86 | json_null 87 | 88 | } json_type; 89 | 90 | extern const struct _json_value json_value_none; 91 | 92 | typedef struct _json_object_entry 93 | { 94 | json_char * name; 95 | unsigned int name_length; 96 | 97 | struct _json_value * value; 98 | 99 | } json_object_entry; 100 | 101 | typedef struct _json_value 102 | { 103 | struct _json_value * parent; 104 | 105 | json_type type; 106 | 107 | union 108 | { 109 | int boolean; 110 | json_int_t integer; 111 | double dbl; 112 | 113 | struct 114 | { 115 | unsigned int length; 116 | json_char * ptr; /* null terminated */ 117 | 118 | } string; 119 | 120 | struct 121 | { 122 | unsigned int length; 123 | 124 | json_object_entry * values; 125 | 126 | #if defined(__cplusplus) && __cplusplus >= 201103L 127 | decltype(values) begin () const 128 | { return values; 129 | } 130 | decltype(values) end () const 131 | { return values + length; 132 | } 133 | #endif 134 | 135 | } object; 136 | 137 | struct 138 | { 139 | unsigned int length; 140 | struct _json_value ** values; 141 | 142 | #if defined(__cplusplus) && __cplusplus >= 201103L 143 | decltype(values) begin () const 144 | { return values; 145 | } 146 | decltype(values) end () const 147 | { return values + length; 148 | } 149 | #endif 150 | 151 | } array; 152 | 153 | } u; 154 | 155 | union 156 | { 157 | struct _json_value * next_alloc; 158 | void * object_mem; 159 | 160 | } _reserved; 161 | 162 | #ifdef JSON_TRACK_SOURCE 163 | 164 | /* Location of the value in the source JSON 165 | */ 166 | unsigned int line, col; 167 | 168 | #endif 169 | 170 | 171 | /* Some C++ operator sugar */ 172 | 173 | #ifdef __cplusplus 174 | 175 | public: 176 | 177 | inline _json_value () 178 | { memset (this, 0, sizeof (_json_value)); 179 | } 180 | 181 | inline const struct _json_value &operator [] (int index) const 182 | { 183 | if (type != json_array || index < 0 184 | || ((unsigned int) index) >= u.array.length) 185 | { 186 | return json_value_none; 187 | } 188 | 189 | return *u.array.values [index]; 190 | } 191 | 192 | inline const struct _json_value &operator [] (const char * index) const 193 | { 194 | if (type != json_object) 195 | return json_value_none; 196 | 197 | for (unsigned int i = 0; i < u.object.length; ++ i) 198 | if (!strcmp (u.object.values [i].name, index)) 199 | return *u.object.values [i].value; 200 | 201 | return json_value_none; 202 | } 203 | 204 | inline operator const char * () const 205 | { 206 | switch (type) 207 | { 208 | case json_string: 209 | return u.string.ptr; 210 | 211 | default: 212 | return ""; 213 | }; 214 | } 215 | 216 | inline operator json_int_t () const 217 | { 218 | switch (type) 219 | { 220 | case json_integer: 221 | return u.integer; 222 | 223 | case json_double: 224 | return (json_int_t) u.dbl; 225 | 226 | default: 227 | return 0; 228 | }; 229 | } 230 | 231 | inline operator bool () const 232 | { 233 | if (type != json_boolean) 234 | return false; 235 | 236 | return u.boolean != 0; 237 | } 238 | 239 | inline operator double () const 240 | { 241 | switch (type) 242 | { 243 | case json_integer: 244 | return (double) u.integer; 245 | 246 | case json_double: 247 | return u.dbl; 248 | 249 | default: 250 | return 0; 251 | }; 252 | } 253 | 254 | #endif 255 | 256 | } json_value; 257 | 258 | json_value * json_parse (const json_char * json, 259 | size_t length); 260 | 261 | #define json_error_max 128 262 | json_value * json_parse_ex (json_settings * settings, 263 | const json_char * json, 264 | size_t length, 265 | char * error); 266 | 267 | void json_value_free (json_value *); 268 | 269 | 270 | /* Not usually necessary, unless you used a custom mem_alloc and now want to 271 | * use a custom mem_free. 272 | */ 273 | void json_value_free_ex (json_settings * settings, 274 | json_value *); 275 | 276 | 277 | #ifdef __cplusplus 278 | } /* extern "C" */ 279 | #endif 280 | 281 | #endif 282 | 283 | 284 | -------------------------------------------------------------------------------- /jwHash.c: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2015 Jonathan Watmough 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include "jwHash.h" 20 | 21 | #ifdef HASHTEST 22 | #include 23 | #endif 24 | 25 | #ifdef HASHTHREADED 26 | #include 27 | #include 28 | #endif 29 | 30 | //////////////////////////////////////////////////////////////////////////////// 31 | // STATIC HELPER FUNCTIONS 32 | 33 | // Spin-locking 34 | // http://stackoverflow.com/questions/1383363/is-my-spin-lock-implementation-correct-and-optimal 35 | 36 | // http://stackoverflow.com/a/12996028 37 | // hash function for int keys 38 | static inline long int hashInt(long int x) 39 | { 40 | x = ((x >> 16) ^ x) * 0x45d9f3b; 41 | x = ((x >> 16) ^ x) * 0x45d9f3b; 42 | x = ((x >> 16) ^ x); 43 | return x; 44 | } 45 | 46 | // http://www.cse.yorku.ca/~oz/hash.html 47 | // hash function for string keys djb2 48 | static inline long int hashString(char * str) 49 | { 50 | unsigned long hash = 5381; 51 | int c; 52 | 53 | while ((c = *str++)) 54 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 55 | return hash; 56 | } 57 | 58 | // helper for copying string keys and values 59 | static inline char * copystring(char * value) 60 | { 61 | char * copy = (char *)malloc(strlen(value)+1); 62 | if(!copy) { 63 | printf("Unable to allocate string value %s\n",value); 64 | abort(); 65 | } 66 | strcpy(copy,value); 67 | return copy; 68 | } 69 | 70 | 71 | //////////////////////////////////////////////////////////////////////////////// 72 | // CREATING A NEW HASH TABLE 73 | 74 | // Create hash table 75 | jwHashTable *create_hash( size_t buckets ) 76 | { 77 | // allocate space 78 | jwHashTable *table= (jwHashTable *)malloc(sizeof(jwHashTable)); 79 | if(!table) { 80 | // unable to allocate 81 | return NULL; 82 | } 83 | // locks 84 | #ifdef HASHTHREADED 85 | table->lock = 0; 86 | table->locks = (int *)malloc(buckets * sizeof(int)); 87 | if( !table->locks ) { 88 | free(table); 89 | return NULL; 90 | } 91 | memset((int *)&table->locks[0],0,buckets*sizeof(int)); 92 | #endif 93 | // setup 94 | table->bucket = (jwHashEntry **)malloc(buckets*sizeof(void*)); 95 | if( !table->bucket ) { 96 | free(table); 97 | return NULL; 98 | } 99 | memset(table->bucket,0,buckets*sizeof(void*)); 100 | table->buckets = table->bucketsinitial = buckets; 101 | HASH_DEBUG("table: %x bucket: %x\n",table,table->bucket); 102 | return table; 103 | } 104 | 105 | void delete_hash( jwHashTable *table, hashtable_free_item_callback free_cb, HASHVALTAG ktype, HASHVALTAG vtype) 106 | { 107 | int i = 0; 108 | for(; i < table->buckets; i++) { 109 | jwHashEntry *entry = table->bucket[i]; 110 | while(entry) { 111 | switch(vtype) { 112 | case HASHPTR: 113 | free_cb(entry->value.ptrValue); 114 | break; 115 | case HASHSTRING: 116 | free_cb(entry->value.strValue); 117 | break; 118 | default: 119 | break; 120 | } 121 | 122 | switch(ktype) { 123 | case HASHSTRING: 124 | free(entry->key.strValue); 125 | break; 126 | default: 127 | break; 128 | } 129 | jwHashEntry *next = entry->next; 130 | free(entry); 131 | entry = next; 132 | } 133 | } 134 | 135 | free(table->bucket); 136 | } 137 | 138 | //////////////////////////////////////////////////////////////////////////////// 139 | // ADDING / DELETING / GETTING BY STRING KEY 140 | 141 | // Add str to table - keyed by string 142 | HASHRESULT add_str_by_str( jwHashTable *table, char *key, char *value ) 143 | { 144 | // compute hash on key 145 | size_t hash = hashString(key) % table->buckets; 146 | HASH_DEBUG("adding %s -> %s hash: %ld\n",key,value,hash); 147 | 148 | // add entry 149 | jwHashEntry *entry = table->bucket[hash]; 150 | 151 | // already an entry 152 | HASH_DEBUG("entry: %x\n",entry); 153 | while(entry!=0) 154 | { 155 | HASH_DEBUG("checking entry: %x\n",entry); 156 | // check for already indexed 157 | if(0==strcmp(entry->key.strValue,key) && 0==strcmp(value,entry->value.strValue)) 158 | return HASHALREADYADDED; 159 | // check for replacing entry 160 | if(0==strcmp(entry->key.strValue,key) && 0!=strcmp(value,entry->value.strValue)) 161 | { 162 | free(entry->value.strValue); 163 | entry->value.strValue = copystring(value); 164 | return HASHREPLACEDVALUE; 165 | } 166 | // move to next entry 167 | entry = entry->next; 168 | } 169 | 170 | // create a new entry and add at head of bucket 171 | HASH_DEBUG("creating new entry\n"); 172 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 173 | HASH_DEBUG("new entry: %x\n",entry); 174 | entry->key.strValue = copystring(key); 175 | entry->valtag = HASHSTRING; 176 | entry->value.strValue = copystring(value); 177 | entry->next = table->bucket[hash]; 178 | table->bucket[hash] = entry; 179 | HASH_DEBUG("added entry\n"); 180 | return HASHOK; 181 | } 182 | 183 | HASHRESULT add_dbl_by_str( jwHashTable *table, char *key, double value ) 184 | { 185 | // compute hash on key 186 | size_t hash = hashString(key) % table->buckets; 187 | HASH_DEBUG("adding %s -> %f hash: %ld\n",key,value,hash); 188 | 189 | // add entry 190 | jwHashEntry *entry = table->bucket[hash]; 191 | 192 | // already an entry 193 | HASH_DEBUG("entry: %x\n",entry); 194 | while(entry!=0) 195 | { 196 | HASH_DEBUG("checking entry: %x\n",entry); 197 | // check for already indexed 198 | if(0==strcmp(entry->key.strValue,key) && value==entry->value.dblValue) 199 | return HASHALREADYADDED; 200 | // check for replacing entry 201 | if(0==strcmp(entry->key.strValue,key) && value!=entry->value.dblValue) 202 | { 203 | entry->value.dblValue = value; 204 | return HASHREPLACEDVALUE; 205 | } 206 | // move to next entry 207 | entry = entry->next; 208 | } 209 | 210 | // create a new entry and add at head of bucket 211 | HASH_DEBUG("creating new entry\n"); 212 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 213 | HASH_DEBUG("new entry: %x\n",entry); 214 | entry->key.strValue = copystring(key); 215 | entry->valtag = HASHNUMERIC; 216 | entry->value.dblValue = value; 217 | entry->next = table->bucket[hash]; 218 | table->bucket[hash] = entry; 219 | HASH_DEBUG("added entry\n"); 220 | return HASHOK; 221 | } 222 | 223 | HASHRESULT add_int_by_str( jwHashTable *table, char *key, long int value ) 224 | { 225 | // compute hash on key 226 | size_t hash = hashString(key); 227 | hash %= table->buckets; 228 | HASH_DEBUG("adding %s -> %d hash: %ld\n",key,value,hash); 229 | 230 | #ifdef HASHTHREADED 231 | // lock this bucket against changes 232 | while (__sync_lock_test_and_set(&table->locks[hash], 1)) { 233 | printf("."); 234 | // Do nothing. This GCC builtin instruction 235 | // ensures memory barrier. 236 | } 237 | #endif 238 | 239 | // check entry 240 | jwHashEntry *entry = table->bucket[hash]; 241 | 242 | // already an entry 243 | HASH_DEBUG("entry: %x\n",entry); 244 | while(entry!=0) 245 | { 246 | HASH_DEBUG("checking entry: %x\n",entry); 247 | // check for already indexed 248 | if(0==strcmp(entry->key.strValue,key) && value==entry->value.intValue) 249 | return HASHALREADYADDED; 250 | // check for replacing entry 251 | if(0==strcmp(entry->key.strValue,key) && value!=entry->value.intValue) 252 | { 253 | entry->value.intValue = value; 254 | return HASHREPLACEDVALUE; 255 | } 256 | // move to next entry 257 | entry = entry->next; 258 | } 259 | 260 | // create a new entry and add at head of bucket 261 | HASH_DEBUG("creating new entry\n"); 262 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 263 | HASH_DEBUG("new entry: %x\n",entry); 264 | entry->key.strValue = copystring(key); 265 | entry->valtag = HASHNUMERIC; 266 | entry->value.intValue = value; 267 | entry->next = table->bucket[hash]; 268 | table->bucket[hash] = entry; 269 | HASH_DEBUG("added entry\n"); 270 | #ifdef HASHTHREADED 271 | unlock: 272 | __sync_synchronize(); // memory barrier 273 | table->locks[hash] = 0; 274 | #endif 275 | return HASHOK; 276 | } 277 | 278 | 279 | HASHRESULT hash_iterator(jwHashTable *table, process_hash_value_callback process_cb, HASHVALTAG type) 280 | { 281 | for(int i = 0; i < table->buckets; i++) { 282 | jwHashEntry *entry = table->bucket[i]; 283 | while(entry) { 284 | switch(type) { 285 | case HASHPTR: 286 | process_cb(entry->value.ptrValue); 287 | break; 288 | case HASHNUMERIC: 289 | process_cb(&entry->value.intValue); 290 | break; 291 | case HASHSTRING: 292 | process_cb(entry->value.strValue); 293 | break; 294 | } 295 | 296 | // move to next entry 297 | entry = entry->next; 298 | } 299 | } 300 | 301 | return HASHOK; 302 | } 303 | 304 | HASHRESULT get_ptr_by_str(jwHashTable *table, char *key, void **ptr) 305 | { 306 | // compute hash on key 307 | size_t hash = hashString(key) % table->buckets; 308 | HASH_DEBUG("adding %s -> %x hash: %ld\n",key,ptr,hash); 309 | 310 | // add entry 311 | jwHashEntry *entry = table->bucket[hash]; 312 | 313 | // already an entry 314 | HASH_DEBUG("entry: %x\n",entry); 315 | while(entry!=0) 316 | { 317 | HASH_DEBUG("checking entry: %x\n",entry); 318 | // check for already indexed 319 | if(0==strcmp(entry->key.strValue,key)) { 320 | *ptr = entry->value.ptrValue; 321 | return HASHOK; 322 | } 323 | // move to next entry 324 | entry = entry->next; 325 | } 326 | 327 | return HASHNOTFOUND; 328 | } 329 | 330 | HASHRESULT add_ptr_by_str( jwHashTable *table, char *key, void *ptr ) 331 | { 332 | // compute hash on key 333 | size_t hash = hashString(key) % table->buckets; 334 | HASH_DEBUG("adding %s -> %x hash: %ld\n",key,ptr,hash); 335 | 336 | // add entry 337 | jwHashEntry *entry = table->bucket[hash]; 338 | 339 | // already an entry 340 | HASH_DEBUG("entry: %x\n",entry); 341 | while(entry!=0) 342 | { 343 | HASH_DEBUG("checking entry: %x\n",entry); 344 | // check for already indexed 345 | if(0==strcmp(entry->key.strValue,key) && ptr==entry->value.ptrValue) 346 | return HASHALREADYADDED; 347 | // check for replacing entry 348 | if(0==strcmp(entry->key.strValue,key) && ptr!=entry->value.ptrValue) 349 | { 350 | entry->value.ptrValue = ptr; 351 | return HASHREPLACEDVALUE; 352 | } 353 | // move to next entry 354 | entry = entry->next; 355 | } 356 | 357 | // create a new entry and add at head of bucket 358 | HASH_DEBUG("creating new entry\n"); 359 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 360 | HASH_DEBUG("new entry: %x\n",entry); 361 | entry->key.strValue = copystring(key); 362 | entry->valtag = HASHPTR; 363 | entry->value.ptrValue = ptr; 364 | entry->next = table->bucket[hash]; 365 | table->bucket[hash] = entry; 366 | HASH_DEBUG("added entry\n"); 367 | return HASHOK; 368 | } 369 | 370 | // Delete by string 371 | HASHRESULT del_by_str( jwHashTable *table, char *key ) 372 | { 373 | // compute hash on key 374 | size_t hash = hashString(key) % table->buckets; 375 | HASH_DEBUG("deleting: %s hash: %ld\n",key,hash); 376 | 377 | // add entry 378 | jwHashEntry *entry = table->bucket[hash]; 379 | jwHashEntry *previous = NULL; 380 | 381 | // found an entry 382 | HASH_DEBUG("entry: %x\n",entry); 383 | while(entry!=0) 384 | { 385 | HASH_DEBUG("checking entry: %x\n",entry); 386 | // check for already indexed 387 | if(0==strcmp(entry->key.strValue,key)) 388 | { 389 | // skip first record, or one in the chain 390 | if(!previous) 391 | table->bucket[hash] = entry->next; 392 | else 393 | previous->next = entry->next; 394 | // delete string value if needed 395 | if( entry->valtag==HASHSTRING ) 396 | free(entry->value.strValue); 397 | free(entry->key.strValue); 398 | free(entry); 399 | return HASHDELETED; 400 | } 401 | // move to next entry 402 | previous = entry; 403 | entry = entry->next; 404 | } 405 | return HASHNOTFOUND; 406 | } 407 | 408 | // Lookup str - keyed by str 409 | HASHRESULT get_str_by_str( jwHashTable *table, char *key, char **value ) 410 | { 411 | // compute hash on key 412 | size_t hash = hashString(key) % table->buckets; 413 | HASH_DEBUG("fetching %s -> ?? hash: %d\n",key,hash); 414 | 415 | // get entry 416 | jwHashEntry *entry = table->bucket[hash]; 417 | 418 | // already an entry 419 | while(entry) 420 | { 421 | // check for key 422 | HASH_DEBUG("found entry key: %d value: %s\n",entry->key.intValue,entry->value.strValue); 423 | if(0==strcmp(entry->key.strValue,key)) { 424 | *value = entry->value.strValue; 425 | return HASHOK; 426 | } 427 | // move to next entry 428 | entry = entry->next; 429 | } 430 | 431 | // not found 432 | return HASHNOTFOUND; 433 | } 434 | 435 | // Lookup int - keyed by str 436 | HASHRESULT get_int_by_str( jwHashTable *table, char *key, int *i ) 437 | { 438 | // compute hash on key 439 | size_t hash = hashString(key) % table->buckets; 440 | HASH_DEBUG("fetching %s -> ?? hash: %d\n",key,hash); 441 | 442 | // get entry 443 | jwHashEntry *entry = table->bucket[hash]; 444 | 445 | // already an entry 446 | while(entry) 447 | { 448 | // check for key 449 | HASH_DEBUG("found entry key: %s value: %ld\n",entry->key.strValue,entry->value.intValue); 450 | if(0==strcmp(entry->key.strValue,key)) { 451 | *i = entry->value.intValue; 452 | return HASHOK; 453 | } 454 | // move to next entry 455 | entry = entry->next; 456 | } 457 | 458 | // not found 459 | return HASHNOTFOUND; 460 | } 461 | 462 | // Lookup dbl - keyed by str 463 | HASHRESULT get_dbl_by_str( jwHashTable *table, char *key, double *val ) 464 | { 465 | // compute hash on key 466 | size_t hash = hashString(key) % table->buckets; 467 | HASH_DEBUG("fetching %s -> ?? hash: %d\n",key,hash); 468 | 469 | // get entry 470 | jwHashEntry *entry = table->bucket[hash]; 471 | 472 | // already an entry 473 | while(entry) 474 | { 475 | // check for key 476 | HASH_DEBUG("found entry key: %s value: %f\n",entry->key.strValue,entry->value.dblValue); 477 | if(0==strcmp(entry->key.strValue,key)) { 478 | *val = entry->value.dblValue; 479 | return HASHOK; 480 | } 481 | // move to next entry 482 | entry = entry->next; 483 | } 484 | 485 | // not found 486 | return HASHNOTFOUND; 487 | } 488 | 489 | //////////////////////////////////////////////////////////////////////////////// 490 | // ADDING / DELETING / GETTING BY LONG INT KEY 491 | 492 | // Add to table - keyed by int 493 | HASHRESULT add_str_by_int( jwHashTable *table, long int key, char *value ) 494 | { 495 | // compute hash on key 496 | size_t hash = hashInt(key) % table->buckets; 497 | HASH_DEBUG("adding %d -> %s hash: %d\n",key,value,hash); 498 | 499 | // add entry 500 | jwHashEntry *entry = table->bucket[hash]; 501 | 502 | // already an entry 503 | HASH_DEBUG("entry: %x\n",entry); 504 | while(entry!=0) 505 | { 506 | HASH_DEBUG("checking entry: %x\n",entry); 507 | // check for already indexed 508 | if(entry->key.intValue==key && 0==strcmp(value,entry->value.strValue)) 509 | return HASHALREADYADDED; 510 | // check for replacing entry 511 | if(entry->key.intValue==key && 0!=strcmp(value,entry->value.strValue)) 512 | { 513 | free(entry->value.strValue); 514 | entry->value.strValue = copystring(value); 515 | return HASHREPLACEDVALUE; 516 | } 517 | // move to next entry 518 | entry = entry->next; 519 | } 520 | 521 | // create a new entry and add at head of bucket 522 | HASH_DEBUG("creating new entry\n"); 523 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 524 | HASH_DEBUG("new entry: %x\n",entry); 525 | entry->key.intValue = key; 526 | entry->valtag = HASHSTRING; 527 | entry->value.strValue = copystring(value); 528 | entry->next = table->bucket[hash]; 529 | table->bucket[hash] = entry; 530 | HASH_DEBUG("added entry\n"); 531 | return HASHOK; 532 | } 533 | 534 | // Add dbl to table - keyed by int 535 | HASHRESULT add_dbl_by_int( jwHashTable* table, long int key, double value ) 536 | { 537 | // compute hash on key 538 | size_t hash = hashInt(key) % table->buckets; 539 | HASH_DEBUG("adding %d -> %f hash: %d\n",key,value,hash); 540 | 541 | // add entry 542 | jwHashEntry *entry = table->bucket[hash]; 543 | 544 | // already an entry 545 | HASH_DEBUG("entry: %x\n",entry); 546 | while(entry!=0) 547 | { 548 | HASH_DEBUG("checking entry: %x\n",entry); 549 | // check for already indexed 550 | if(entry->key.intValue==key && value==entry->value.dblValue) 551 | return HASHALREADYADDED; 552 | // check for replacing entry 553 | if(entry->key.intValue==key && value!=entry->value.dblValue) 554 | { 555 | entry->value.dblValue = value; 556 | return HASHREPLACEDVALUE; 557 | } 558 | // move to next entry 559 | entry = entry->next; 560 | } 561 | 562 | // create a new entry and add at head of bucket 563 | HASH_DEBUG("creating new entry\n"); 564 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 565 | HASH_DEBUG("new entry: %x\n",entry); 566 | entry->key.intValue = key; 567 | entry->valtag = HASHNUMERIC; 568 | entry->value.dblValue = value; 569 | entry->next = table->bucket[hash]; 570 | table->bucket[hash] = entry; 571 | HASH_DEBUG("added entry\n"); 572 | return HASHOK; 573 | } 574 | 575 | HASHRESULT add_int_by_int( jwHashTable* table, long int key, long int value ) 576 | { 577 | // compute hash on key 578 | size_t hash = hashInt(key) % table->buckets; 579 | HASH_DEBUG("adding %d -> %d hash: %d\n",key,value,hash); 580 | 581 | // add entry 582 | jwHashEntry *entry = table->bucket[hash]; 583 | 584 | // already an entry 585 | HASH_DEBUG("entry: %x\n",entry); 586 | while(entry!=0) 587 | { 588 | HASH_DEBUG("checking entry: %x\n",entry); 589 | // check for already indexed 590 | if(entry->key.intValue==key && value==entry->value.intValue) 591 | return HASHALREADYADDED; 592 | // check for replacing entry 593 | if(entry->key.intValue==key && value!=entry->value.intValue) 594 | { 595 | entry->value.intValue = value; 596 | return HASHREPLACEDVALUE; 597 | } 598 | // move to next entry 599 | entry = entry->next; 600 | } 601 | 602 | // create a new entry and add at head of bucket 603 | HASH_DEBUG("creating new entry\n"); 604 | entry = (jwHashEntry *)malloc(sizeof(jwHashEntry)); 605 | HASH_DEBUG("new entry: %x\n",entry); 606 | entry->key.intValue = key; 607 | entry->valtag = HASHNUMERIC; 608 | entry->value.intValue = value; 609 | entry->next = table->bucket[hash]; 610 | table->bucket[hash] = entry; 611 | HASH_DEBUG("added entry\n"); 612 | return HASHOK; 613 | } 614 | 615 | 616 | // Delete by int 617 | HASHRESULT del_by_int( jwHashTable* table, long int key ) 618 | { 619 | // compute hash on key 620 | size_t hash = hashInt(key) % table->buckets; 621 | HASH_DEBUG("deleting: %d hash: %d\n",key,hash); 622 | 623 | // add entry 624 | jwHashEntry *entry = table->bucket[hash]; 625 | jwHashEntry *prev = NULL; 626 | 627 | // found an entry 628 | HASH_DEBUG("entry: %x\n",entry); 629 | while(entry!=0) 630 | { 631 | HASH_DEBUG("checking entry: %x\n",entry); 632 | // check for entry to delete 633 | if(entry->key.intValue==key) 634 | { 635 | // skip first record, or one in the chain 636 | if(!prev) 637 | table->bucket[hash] = entry->next; 638 | else 639 | prev->next = entry->next; 640 | // delete string value if needed 641 | if( entry->valtag==HASHSTRING ) 642 | free(entry->value.strValue); 643 | free(entry); 644 | return HASHDELETED; 645 | } 646 | // move to next entry 647 | prev = entry; 648 | entry = entry->next; 649 | } 650 | return HASHNOTFOUND; 651 | } 652 | 653 | // Lookup str - keyed by int 654 | HASHRESULT get_str_by_int( jwHashTable *table, long int key, char **value ) 655 | { 656 | // compute hash on key 657 | size_t hash = hashInt(key) % table->buckets; 658 | HASH_DEBUG("fetching %d -> ?? hash: %d\n",key,hash); 659 | 660 | // get entry 661 | jwHashEntry *entry = table->bucket[hash]; 662 | 663 | // already an entry 664 | while(entry) 665 | { 666 | // check for key 667 | HASH_DEBUG("found entry key: %d value: %s\n",entry->key.intValue,entry->value.strValue); 668 | if(entry->key.intValue==key) { 669 | *value = entry->value.strValue; 670 | return HASHOK; 671 | } 672 | // move to next entry 673 | entry = entry->next; 674 | } 675 | 676 | // not found 677 | return HASHNOTFOUND; 678 | } 679 | 680 | 681 | 682 | 683 | 684 | 685 | -------------------------------------------------------------------------------- /jwHash.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2015 Jonathan Watmough 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | 14 | */ 15 | 16 | //#define HASHTHREADED 1 17 | //#define HASHTEST 1 18 | //#define HASHDEBUG 1 19 | 20 | // guards! guards! 21 | #ifndef jwhash_h 22 | #define jwhash_h 23 | 24 | // needed for size_t 25 | #include 26 | 27 | //#define HASHDEBUG 28 | #ifdef HASHDEBUG 29 | # define HASH_DEBUG(fmt,args...) printf(fmt, ## args) 30 | #else 31 | # define HASH_DEBUG(fmt,args...) do {} while (0); 32 | #endif 33 | 34 | 35 | // resuts codes 36 | typedef enum 37 | { 38 | HASHOK, 39 | HASHADDED, 40 | HASHREPLACEDVALUE, 41 | HASHALREADYADDED, 42 | HASHDELETED, 43 | HASHNOTFOUND, 44 | } HASHRESULT; 45 | 46 | typedef enum 47 | { 48 | HASHPTR, 49 | HASHNUMERIC, 50 | HASHSTRING, 51 | } HASHVALTAG; 52 | 53 | 54 | typedef struct jwHashEntry jwHashEntry; 55 | struct jwHashEntry 56 | { 57 | union 58 | { 59 | char *strValue; 60 | double dblValue; 61 | int intValue; 62 | } key; 63 | HASHVALTAG valtag; 64 | union 65 | { 66 | char *strValue; 67 | double dblValue; 68 | int intValue; 69 | void *ptrValue; 70 | } value; 71 | jwHashEntry *next; 72 | }; 73 | 74 | typedef struct jwHashTable jwHashTable; 75 | struct jwHashTable 76 | { 77 | jwHashEntry **bucket; // pointer to array of buckets 78 | size_t buckets; 79 | size_t bucketsinitial; // if we resize, may need to hash multiple times 80 | HASHRESULT lastError; 81 | #ifdef HASHTHREADED 82 | volatile int *locks; // array of locks 83 | volatile int lock; // lock for entire table 84 | #endif 85 | }; 86 | 87 | typedef void (*hashtable_free_item_callback)(void *value); 88 | 89 | // Create/delete hash table 90 | jwHashTable *create_hash( size_t buckets ); 91 | void delete_hash( jwHashTable *table, hashtable_free_item_callback free_cb, HASHVALTAG ktype, HASHVALTAG vtype); // clean up all memory 92 | 93 | typedef void (*process_hash_value_callback)(void *value); 94 | 95 | 96 | //<<< liudengfeng added here 97 | HASHRESULT hash_iterator(jwHashTable *table, process_hash_value_callback process_cb, HASHVALTAG type); 98 | HASHRESULT get_ptr_by_str(jwHashTable *table, char *key, void **ptr); 99 | //>>> 100 | 101 | // Add to table - keyed by string 102 | HASHRESULT add_str_by_str( jwHashTable*, char *key, char *value ); 103 | HASHRESULT add_dbl_by_str( jwHashTable*, char *key, double value ); 104 | HASHRESULT add_int_by_str( jwHashTable*, char *key, long int value ); 105 | HASHRESULT add_ptr_by_str( jwHashTable*, char *key, void *value ); 106 | 107 | // Delete by string 108 | HASHRESULT del_by_str( jwHashTable*, char *key ); 109 | 110 | // Get by string 111 | HASHRESULT get_str_by_str( jwHashTable *table, char *key, char **value ); 112 | HASHRESULT get_int_by_str( jwHashTable *table, char *key, int *i ); 113 | HASHRESULT get_dbl_by_str( jwHashTable *table, char *key, double *val ); 114 | 115 | 116 | // Add to table - keyed by int 117 | HASHRESULT add_str_by_int( jwHashTable*, long int key, char *value ); 118 | HASHRESULT add_dbl_by_int( jwHashTable*, long int key, double value ); 119 | HASHRESULT add_int_by_int( jwHashTable*, long int key, long int value ); 120 | HASHRESULT add_ptr_by_int( jwHashTable*, long int key, void *value ); 121 | 122 | // Delete by int 123 | HASHRESULT del_by_int( jwHashTable*, long int key ); 124 | 125 | // Get by int 126 | HASHRESULT get_str_by_int( jwHashTable *table, long int key, char **value ); 127 | HASHRESULT get_int_by_int( jwHashTable *table, long int key, int *i ); 128 | HASHRESULT get_dbl_by_int( jwHashTable *table, long int key, double *val ); 129 | 130 | #endif 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /kcp_http_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/kcp_http_server.png -------------------------------------------------------------------------------- /kcp_http_server_clan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/kcp_http_server_clan.png -------------------------------------------------------------------------------- /kcptun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/kcptun.png -------------------------------------------------------------------------------- /logo-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/logo-big.png -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liudf0716/xkcptun/fd47967453f8bc23efa416c9bb10137e6929cdaa/logo.png -------------------------------------------------------------------------------- /server.json: -------------------------------------------------------------------------------- 1 | { 2 | "localinterface": "eth0", 3 | "localport": 9089, 4 | "remoteaddr": "127.0.0.1", 5 | "remoteport": 443, 6 | "key": "14789632a", 7 | "crypt": "none", 8 | "mode": "fast3", 9 | "mtu": 1350, 10 | "sndwnd": 4096, 11 | "rcvwnd": 512, 12 | "datashard": 10, 13 | "parityshard": 3, 14 | "dscp": 0, 15 | "nocomp": true, 16 | "acknodelay": false, 17 | "nodelay": 0, 18 | "interval": 20, 19 | "resend": 2, 20 | "nc": 1, 21 | "sockbuf": 4194304, 22 | "keepalive": 10 23 | } 24 | -------------------------------------------------------------------------------- /tcp_client.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include "xkcp_server.h" 39 | #include "xkcp_util.h" 40 | #include "tcp_client.h" 41 | #include "debug.h" 42 | #include "ikcp.h" 43 | #include "jwHash.h" 44 | 45 | static void clean_useless_client() 46 | { 47 | jwHashTable *table = get_xkcp_hash(); 48 | for(int i = 0; i < table->buckets; i++) { 49 | jwHashEntry *entry = table->bucket[i]; 50 | jwHashEntry *previous = NULL; 51 | while(entry) { 52 | iqueue_head *list = entry->value.ptrValue; 53 | if (list && iqueue_is_empty(list)) { 54 | if(!previous) 55 | table->bucket[i] = entry->next; 56 | else 57 | previous->next = entry->next; 58 | free(entry->value.strValue); 59 | free(entry->key.strValue); 60 | free(entry); 61 | } 62 | previous = entry; 63 | entry = entry->next; 64 | } 65 | } 66 | } 67 | 68 | void tcp_client_event_cb(struct bufferevent *bev, short what, void *ctx) 69 | { 70 | void *puser = xkcp_tcp_event_cb(bev, what, ctx); 71 | if (puser) 72 | free(puser); 73 | 74 | if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 75 | clean_useless_client(); 76 | } 77 | } 78 | 79 | void tcp_client_read_cb(struct bufferevent *bev, void *ctx) 80 | { 81 | struct xkcp_task *task = ctx; 82 | ikcpcb *kcp = task->kcp; 83 | xkcp_tcp_read_cb(bev, kcp); 84 | } 85 | -------------------------------------------------------------------------------- /tcp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _TCP_CLIENT_ 2 | #define _TCP_CLIENT_ 3 | 4 | struct evconnlistener; 5 | struct bufferevent; 6 | struct sockaddr; 7 | 8 | void tcp_client_event_cb(struct bufferevent *bev, short what, void *ctx); 9 | 10 | void tcp_client_read_cb(struct bufferevent *bev, void *ctx); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /tcp_proxy.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | 39 | #include "xkcp_util.h" 40 | #include "tcp_proxy.h" 41 | #include "xkcp_client.h" 42 | #include "debug.h" 43 | 44 | extern iqueue_head xkcp_task_list; 45 | 46 | static void 47 | tcp_proxy_read_cb(struct bufferevent *bev, void *ctx) 48 | { 49 | struct xkcp_task *task = ctx; 50 | ikcpcb *kcp = task->kcp; 51 | xkcp_tcp_read_cb(bev, kcp); 52 | xkcp_forward_all_data(&xkcp_task_list); 53 | } 54 | 55 | static void 56 | tcp_proxy_event_cb(struct bufferevent *bev, short what, void *ctx) 57 | { 58 | xkcp_tcp_event_cb(bev, what, ctx); 59 | } 60 | 61 | void 62 | tcp_proxy_accept_cb(struct evconnlistener *listener, evutil_socket_t fd, 63 | struct sockaddr *a, int slen, void *param) 64 | { 65 | struct xkcp_proxy_param *p = param; 66 | struct bufferevent *b_in = NULL; 67 | struct event_base *base = evconnlistener_get_base(listener); 68 | 69 | b_in = bufferevent_socket_new(base, fd, 70 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 71 | assert(b_in); 72 | 73 | static int conv = 1; 74 | ikcpcb *kcp_client = ikcp_create(conv, param); 75 | xkcp_set_config_param(kcp_client); 76 | conv++; 77 | 78 | debug(LOG_INFO, "accept new client [%d] in, conv [%d]", fd, conv-1); 79 | 80 | struct xkcp_task *task = malloc(sizeof(struct xkcp_task)); 81 | assert(task); 82 | task->kcp = kcp_client; 83 | task->bev = b_in; 84 | task->sockaddr = &p->sockaddr; 85 | add_task_tail(task, &xkcp_task_list); 86 | 87 | bufferevent_setcb(b_in, tcp_proxy_read_cb, NULL, tcp_proxy_event_cb, task); 88 | bufferevent_enable(b_in, EV_READ | EV_WRITE ); 89 | } 90 | -------------------------------------------------------------------------------- /tcp_proxy.h: -------------------------------------------------------------------------------- 1 | #ifndef _TCP_PROXY_ 2 | #define _TCP_PROXY_ 3 | 4 | #include "ikcp.h" 5 | 6 | void tcp_proxy_accept_cb(struct evconnlistener *listener, evutil_socket_t fd, 7 | struct sockaddr *a, int slen, void *param); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /version.h: -------------------------------------------------------------------------------- 1 | #ifndef _VERSION_ 2 | #define _VERSION_ 3 | 4 | #define VERSION "0.03" 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /xkcp_client.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | #include "ikcp.h" 47 | #include "xkcp_util.h" 48 | #include "tcp_proxy.h" 49 | #include "xkcp_config.h" 50 | #include "commandline.h" 51 | #include "xkcp_client.h" 52 | #include "xkcp_mon.h" 53 | #include "debug.h" 54 | 55 | IQUEUE_HEAD(xkcp_task_list); 56 | 57 | static short mport = 9086; 58 | 59 | void 60 | timer_event_cb(evutil_socket_t fd, short event, void *arg) 61 | { 62 | xkcp_timer_event_cb(arg, &xkcp_task_list); 63 | } 64 | 65 | void 66 | xkcp_rcv_cb(const int sock, short int which, void *arg) 67 | { 68 | struct xkcp_proxy_param *ptr = arg; 69 | char buf[XKCP_RECV_BUF_LEN] = {0}; 70 | int nrecv = 0; 71 | 72 | int index = 0; 73 | if ((nrecv = recvfrom(sock, buf, sizeof(buf)-1, 0, (struct sockaddr *) &ptr->sockaddr, (socklen_t*)&ptr->addr_len)) > 0) { 74 | int conv = ikcp_getconv(buf); 75 | ikcpcb *kcp = get_kcp_from_conv(conv, &xkcp_task_list); 76 | debug(LOG_DEBUG, "[%d] xkcp_rcv_cb [%d] len [%d] conv [%d] kcp is [%d]", 77 | index++, sock, nrecv, conv, kcp?1:0); 78 | if (kcp) { 79 | int nret = ikcp_input(kcp, buf, nrecv); 80 | if (nret < 0) { 81 | debug(LOG_INFO, "conv [%d] ikcp_input failed [%d]", conv, nret); 82 | } 83 | } else { 84 | debug(LOG_ERR, "xkcp_rcv_cb -- cant get kcp from peer data!!!!!!"); 85 | } 86 | 87 | xkcp_forward_all_data( &xkcp_task_list); 88 | } 89 | } 90 | 91 | static struct evconnlistener *set_tcp_proxy_listener(struct event_base *base, void *ptr) 92 | { 93 | short lport = xkcp_get_param()->local_port; 94 | struct sockaddr_in sin; 95 | char *addr = get_iface_ip(xkcp_get_param()->local_interface); 96 | if (!addr) { 97 | debug(LOG_ERR, "get_iface_ip [%s] failed", xkcp_get_param()->local_interface); 98 | exit(0); 99 | } 100 | 101 | memset(&sin, 0, sizeof(sin)); 102 | sin.sin_family = AF_INET; 103 | sin.sin_addr.s_addr = inet_addr(addr); 104 | sin.sin_port = htons(lport); 105 | 106 | struct evconnlistener * listener = evconnlistener_new_bind(base, tcp_proxy_accept_cb, ptr, 107 | LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE, 108 | -1, (struct sockaddr*)&sin, sizeof(sin)); 109 | if (!listener) { 110 | debug(LOG_ERR, "Couldn't create listener: [%s]", strerror(errno)); 111 | exit(0); 112 | } 113 | 114 | return listener; 115 | } 116 | 117 | int client_main_loop(void) 118 | { 119 | struct event_base *base = NULL; 120 | struct evconnlistener *listener = NULL, *mlistener = NULL; 121 | int xkcp_fd = socket(AF_INET, SOCK_DGRAM, 0); 122 | struct event timer_event, *xkcp_event; 123 | 124 | if (xkcp_fd < 0) { 125 | debug(LOG_ERR, "ERROR, open udp socket"); 126 | exit(0); 127 | } 128 | 129 | if (fcntl(xkcp_fd, F_SETFL, O_NONBLOCK) == -1) { 130 | debug(LOG_ERR, "ERROR, fcntl error: %s", strerror(errno)); 131 | exit(0); 132 | } 133 | 134 | 135 | struct hostent *server = gethostbyname(xkcp_get_param()->remote_addr); 136 | if (!server) { 137 | debug(LOG_ERR, "ERROR, no such host as %s", xkcp_get_param()->remote_addr); 138 | exit(0); 139 | } 140 | 141 | base = event_base_new(); 142 | if (!base) { 143 | debug(LOG_ERR, "event_base_new()"); 144 | exit(0); 145 | } 146 | 147 | struct xkcp_proxy_param proxy_param; 148 | memset(&proxy_param, 0, sizeof(proxy_param)); 149 | proxy_param.base = base; 150 | proxy_param.xkcpfd = xkcp_fd; 151 | proxy_param.sockaddr.sin_family = AF_INET; 152 | proxy_param.sockaddr.sin_port = htons(xkcp_get_param()->remote_port); 153 | memcpy((char *)&proxy_param.sockaddr.sin_addr.s_addr, (char *)server->h_addr, server->h_length); 154 | listener = set_tcp_proxy_listener(base, &proxy_param); 155 | 156 | mlistener = set_xkcp_mon_listener(base, mport, &xkcp_task_list); 157 | 158 | event_assign(&timer_event, base, -1, EV_PERSIST, timer_event_cb, &timer_event); 159 | set_timer_interval(&timer_event); 160 | 161 | xkcp_event = event_new(base, xkcp_fd, EV_READ|EV_PERSIST, xkcp_rcv_cb, &proxy_param); 162 | event_add(xkcp_event, NULL); 163 | 164 | event_base_dispatch(base); 165 | evconnlistener_free(mlistener); 166 | evconnlistener_free(listener); 167 | close(xkcp_fd); 168 | event_base_free(base); 169 | 170 | return 0; 171 | } 172 | 173 | int main(int argc, char **argv) 174 | { 175 | struct xkcp_config *config = xkcp_get_config(); 176 | config->main_loop = client_main_loop; 177 | 178 | return xkcp_main(argc, argv); 179 | } 180 | -------------------------------------------------------------------------------- /xkcp_client.h: -------------------------------------------------------------------------------- 1 | #ifndef _XKCP_CLIENT_ 2 | #define _XKCP_CLIENT_ 3 | 4 | #include 5 | #include "ikcp.h" 6 | 7 | struct xkcp_task; 8 | 9 | 10 | #define XKCP_RECV_BUF_LEN 1500 11 | #define XKCP_SEND_BUF_LEN 10*15000 12 | 13 | void xkcp_rcv_cb(const int sock, short int which, void *arg); 14 | 15 | void timer_event_cb(evutil_socket_t fd, short event, void *arg); 16 | 17 | int xkcp_main(int argc, char **argv); 18 | 19 | int client_main_loop(void); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /xkcp_config.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "json.h" 28 | #include "debug.h" 29 | #include "xkcp_config.h" 30 | 31 | static struct xkcp_config config; 32 | 33 | struct xkcp_config * xkcp_get_config(void) 34 | { 35 | return &config; 36 | } 37 | 38 | struct xkcp_param *xkcp_get_param(void) 39 | { 40 | return &config.param; 41 | } 42 | 43 | static void xkcp_param_init(struct xkcp_param *param) 44 | { 45 | memset(&config.param, 0, sizeof(struct xkcp_param)); 46 | param->local_interface = NULL; 47 | param->remote_addr = NULL; 48 | param->key = NULL; 49 | param->crypt = NULL; 50 | param->mode = NULL; 51 | 52 | param->mtu = 1350; 53 | param->sndwnd = 512; 54 | param->rcvwnd = 512; 55 | param->data_shard = 10; 56 | param->parity_shard = 3; 57 | param->dscp = 0; 58 | param->nocomp = 1; 59 | param->ack_nodelay = 0; 60 | param->nodelay = 1; 61 | param->interval = 20; 62 | param->resend = 2; 63 | param->nc = 0; 64 | param->sock_buf = 4194304; 65 | param->keepalive = 10; 66 | } 67 | 68 | void config_init() 69 | { 70 | config.daemon = 1; 71 | config.is_server = 0; 72 | 73 | xkcp_param_init(&config.param); 74 | } 75 | 76 | #if 0 77 | void xkcp_param_free(struct xkcp_param *param) 78 | { 79 | if (param->local_interface) 80 | free(param->local_interface); 81 | 82 | if (param->remote_addr) 83 | free(param->remote_addr); 84 | 85 | if (param->key) 86 | free(param->key); 87 | 88 | if (param->crypt) 89 | free(param->crypt); 90 | 91 | if (param->mode) 92 | free(param->mode); 93 | } 94 | #endif 95 | 96 | static int parse_json_int(const json_value *value) 97 | { 98 | if (value->type == json_integer) { 99 | return value->u.integer; 100 | } else if (value->type == json_boolean) { 101 | return value->u.boolean; 102 | } else { 103 | debug(LOG_ERR, "Need type :%d or %d, now get wrong type: %d", json_integer, json_boolean, value->type); 104 | debug(LOG_ERR, "Invalid config format."); 105 | } 106 | return 0; 107 | } 108 | 109 | static char * parse_json_string(const json_value *value) 110 | { 111 | if (value->type == json_string) { 112 | return strdup(value->u.string.ptr); 113 | } else if (value->type == json_null) { 114 | return NULL; 115 | } else { 116 | debug(LOG_ERR, "Need type :%d or %d, now get wrong type: %d", json_string, json_null, value->type); 117 | debug(LOG_ERR, "Invalid config format."); 118 | } 119 | return 0; 120 | } 121 | 122 | int xkcp_parse_param(const char *filename) 123 | { 124 | return xkcp_parse_json_param(&config.param, filename); 125 | } 126 | 127 | // 1: error; 0, success 128 | int xkcp_parse_json_param(struct xkcp_param *param, const char *filename) 129 | { 130 | if (!param) 131 | return 1; 132 | 133 | char * buf; 134 | json_value *obj; 135 | 136 | FILE *f = fopen(filename, "rb"); 137 | if (f == NULL) { 138 | debug(LOG_ERR, "Invalid config path."); 139 | return 1; 140 | } 141 | 142 | fseek(f, 0, SEEK_END); 143 | long pos = ftell(f); 144 | fseek(f, 0, SEEK_SET); 145 | 146 | buf = malloc(pos + 1); 147 | if (buf == NULL) { 148 | debug(LOG_ERR, "No enough memory."); 149 | return 1; 150 | } 151 | 152 | int nread = fread(buf, pos, 1, f); 153 | if (!nread) { 154 | debug(LOG_ERR, "Failed to read the config file."); 155 | return 1; 156 | } 157 | fclose(f); 158 | 159 | buf[pos] = '\0'; // end of string 160 | 161 | json_settings settings = { 0UL, 0, NULL, NULL, NULL }; 162 | char error_buf[512]; 163 | obj = json_parse_ex(&settings, buf, pos, error_buf); 164 | 165 | if (obj == NULL) { 166 | debug(LOG_ERR, "%s", error_buf); 167 | return 1; 168 | } 169 | 170 | if (obj->type == json_object) { 171 | unsigned int i; 172 | for (i = 0; i < obj->u.object.length; i++) { 173 | char *name = obj->u.object.values[i].name; 174 | json_value *value = obj->u.object.values[i].value; 175 | if (strcmp(name, "localinterface") == 0) { 176 | param->local_interface = parse_json_string(value); 177 | debug(LOG_DEBUG, "local_interface is %s", param->local_interface); 178 | } else if (strcmp(name, "localport") == 0) { 179 | param->local_port = parse_json_int(value);; 180 | debug(LOG_DEBUG, "local_port is %d", param->local_port); 181 | } else if (strcmp(name, "remoteaddr") == 0) { 182 | param->remote_addr = parse_json_string(value); 183 | debug(LOG_DEBUG, "remote_addr is %s", param->remote_addr); 184 | } else if (strcmp(name, "remoteport") == 0) { 185 | param->remote_port = parse_json_int(value);; 186 | debug(LOG_DEBUG, "remote_port is %d", param->remote_port); 187 | } else if (strcmp(name, "key") == 0) { 188 | param->key = parse_json_string(value); 189 | } else if (strcmp(name, "crypt") == 0) { 190 | param->crypt = parse_json_string(value); 191 | } else if (strcmp(name, "mode") == 0) { 192 | param->mode = parse_json_string(value); 193 | } else if (strcmp(name, "conn") == 0) { 194 | param->conn = parse_json_int(value);; 195 | } else if (strcmp(name, "autoexpire") == 0) { 196 | param->auto_expire = parse_json_int(value);; 197 | } else if (strcmp(name, "scavengettl") == 0) { 198 | param->scavenge_ttl = parse_json_int(value);; 199 | } else if (strcmp(name, "mtu") == 0) { 200 | param->mtu = parse_json_int(value);; 201 | } else if (strcmp(name, "sndwnd") == 0) { 202 | param->sndwnd = parse_json_int(value);; 203 | } else if (strcmp(name, "rcvwnd") == 0) { 204 | param->rcvwnd = parse_json_int(value);; 205 | } else if (strcmp(name, "datashard") == 0) { 206 | param->data_shard = parse_json_int(value);; 207 | } else if (strcmp(name, "parity_shard") == 0) { 208 | param->parity_shard = parse_json_int(value);; 209 | } else if (strcmp(name, "dscp") == 0) { 210 | param->dscp = parse_json_int(value);; 211 | } else if (strcmp(name, "nocomp") == 0) { 212 | param->nocomp = parse_json_int(value);; 213 | } else if (strcmp(name, "acknodelay") == 0) { 214 | param->ack_nodelay = parse_json_int(value);; 215 | } else if (strcmp(name, "nodelay") == 0) { 216 | param->nodelay = parse_json_int(value);; 217 | } else if (strcmp(name, "interval") == 0) { 218 | param->interval = parse_json_int(value);; 219 | } else if (strcmp(name, "resend") == 0) { 220 | param->resend = parse_json_int(value);; 221 | } else if (strcmp(name, "nc") == 0) { 222 | param->nc = parse_json_int(value);; 223 | } else if (strcmp(name, "keepalive") == 0) { 224 | param->keepalive = parse_json_int(value);; 225 | } 226 | } 227 | } else { 228 | debug(LOG_DEBUG, "Invalid config file"); 229 | return 1; 230 | } 231 | 232 | free(buf); 233 | json_value_free(obj); 234 | 235 | return 0; 236 | } 237 | -------------------------------------------------------------------------------- /xkcp_config.h: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #ifndef _XKCP_CONFIG_ 23 | #define _XKCP_CONFIG_ 24 | 25 | struct xkcp_param { 26 | char *local_interface; // localaddr 27 | char *remote_addr; // remoteaddr 28 | char *key; // key 29 | char *crypt; // crypt 30 | char *mode; // mode 31 | int local_port; // local tcp listen port 32 | int remote_port; // remote udp connect port 33 | int conn; // conn 34 | int auto_expire; // autoexpire 35 | int scavenge_ttl; // scavengettl 36 | int mtu; // mtu 37 | int sndwnd; // sndwnd 38 | int rcvwnd; // rcvwnd 39 | int data_shard; // datashard 40 | int parity_shard; // parityshard 41 | int dscp; // dscp 42 | int nocomp; // nocomp 43 | int ack_nodelay; // acknodelay 44 | int nodelay; // nodelay 45 | int interval; // interval 46 | int resend; // resend 47 | int nc; // no congestion 48 | int sock_buf; // sockbuf 49 | int keepalive; // keepalive 50 | }; 51 | 52 | struct xkcp_config { 53 | char *config_file; 54 | int daemon; 55 | int is_server; 56 | int (*main_loop)(); 57 | 58 | struct xkcp_param param; 59 | }; 60 | 61 | void config_init(void); 62 | 63 | struct xkcp_config *xkcp_get_config(void); 64 | 65 | int xkcp_param_validate(struct xkcp_param * param); 66 | 67 | int xkcp_parse_param(const char *filename); 68 | 69 | int xkcp_parse_json_param(struct xkcp_param *config, const char *filename); 70 | 71 | struct xkcp_param *xkcp_get_param(void); 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /xkcp_mon.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | 37 | #include "xkcp_mon.h" 38 | #include "xkcp_util.h" 39 | #include "xkcp_config.h" 40 | #include "debug.h" 41 | #include "jwHash.h" 42 | 43 | static int xkcp_server_flag = 0; 44 | 45 | typedef void (*spy_cmd_process)(struct bufferevent *bev, void *ctx); 46 | 47 | struct user_spy_cmd { 48 | char *command; 49 | spy_cmd_process cmd_process; 50 | }; 51 | 52 | static void get_client_status(struct bufferevent *bev, void *ctx); 53 | static void get_server_status(struct bufferevent *bev, void *ctx); 54 | static void get_client_info(struct bufferevent *bev, void *ctx); 55 | 56 | struct user_spy_cmd client_cmd[] = { 57 | {"status", get_client_status}, 58 | {NULL, NULL} 59 | }; 60 | 61 | struct user_spy_cmd server_cmd[] = { 62 | {"status", get_server_status}, 63 | {"client", get_client_info}, 64 | {NULL, NULL} 65 | }; 66 | 67 | int set_xkcp_server_flag(int flag) 68 | { 69 | xkcp_server_flag = flag; 70 | return 0; 71 | } 72 | 73 | static void get_client_status(struct bufferevent *bev, void *ctx) 74 | { 75 | dump_task_list(ctx, bev); 76 | } 77 | 78 | static void get_client_info(struct bufferevent *bev, void *ctx) 79 | { 80 | debug(LOG_DEBUG, "get_client_info "); 81 | jwHashTable *xkcp_hash = ctx; 82 | struct evbuffer *output = bufferevent_get_output(bev); 83 | evbuffer_add_printf(output, "client list:\n"); 84 | for(int i = 0; i < xkcp_hash->buckets; i++) { 85 | jwHashEntry *entry = xkcp_hash->bucket[i]; 86 | while (entry) { 87 | evbuffer_add_printf(output, "hash_id [%d] connected client [%s] connection [%d] \n", 88 | i, entry->key.strValue, get_task_list_size(entry->value.ptrValue)); 89 | entry = entry->next; 90 | } 91 | } 92 | } 93 | 94 | static void get_server_status(struct bufferevent *bev, void *ctx) 95 | { 96 | debug(LOG_DEBUG, "get_server_status "); 97 | jwHashTable *xkcp_hash = ctx; 98 | struct evbuffer *output = bufferevent_get_output(bev); 99 | evbuffer_add_printf(output, "client detail list:\n"); 100 | for(int i = 0; i < xkcp_hash->buckets; i++) { 101 | jwHashEntry *entry = xkcp_hash->bucket[i]; 102 | while (entry) { 103 | evbuffer_add_printf(output, "hash_id [%d] connected client [%s]: \n", i, entry->key.strValue); 104 | dump_task_list(entry->value.ptrValue, bev); 105 | entry = entry->next; 106 | } 107 | } 108 | } 109 | 110 | static void process_user_cmd(struct bufferevent *bev, const char *cmd, void *ctx) 111 | { 112 | debug(LOG_DEBUG, "cmd is %s", cmd); 113 | if (xkcp_server_flag) { 114 | for(int i = 0; server_cmd[i].command != NULL; i++) { 115 | if (strcmp(cmd, server_cmd[i].command) == 0) 116 | server_cmd[i].cmd_process(bev, ctx); 117 | } 118 | } else { 119 | for(int i = 0; client_cmd[i].command != NULL; i++) { 120 | if (strcmp(cmd, client_cmd[i].command) == 0) 121 | client_cmd[i].cmd_process(bev, ctx); 122 | } 123 | } 124 | } 125 | 126 | static void xkcp_mon_event_cb(struct bufferevent *bev, short what, void *ctx) 127 | { 128 | if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 129 | bufferevent_free(bev); 130 | } 131 | } 132 | 133 | static void xkcp_mon_write_cb(struct bufferevent *bev, void *ctx) 134 | { 135 | struct evbuffer *output = bufferevent_get_output(bev); 136 | if (evbuffer_get_length(output) == 0) { 137 | debug(LOG_INFO, "xkcp_mon_write_cb flush"); 138 | bufferevent_free(bev); 139 | } 140 | 141 | } 142 | 143 | static void xkcp_mon_read_cb(struct bufferevent *bev, void *ctx) 144 | { 145 | struct evbuffer *input = bufferevent_get_input(bev); 146 | int len = evbuffer_get_length(input); 147 | 148 | debug(LOG_DEBUG, "xkcp_mon_read_cb [%d]", len); 149 | 150 | if ( len > 0) { 151 | char *buf = malloc(len+1); 152 | memset(buf, 0, len+1); 153 | if (evbuffer_remove(input, buf, len) > 0) 154 | process_user_cmd(bev, buf, ctx); 155 | } 156 | } 157 | 158 | void xkcp_mon_accept_cb(struct evconnlistener *listener, evutil_socket_t fd, 159 | struct sockaddr *a, int slen, void *ptr) 160 | { 161 | struct bufferevent *b_in = NULL; 162 | struct event_base *base = evconnlistener_get_base(listener); 163 | 164 | b_in = bufferevent_socket_new(base, fd, 165 | BEV_OPT_CLOSE_ON_FREE|BEV_OPT_DEFER_CALLBACKS); 166 | assert(b_in); 167 | 168 | debug(LOG_INFO, "accept new mon client in"); 169 | 170 | bufferevent_setcb(b_in, xkcp_mon_read_cb, xkcp_mon_write_cb, xkcp_mon_event_cb, ptr); 171 | bufferevent_enable(b_in, EV_READ | EV_WRITE); 172 | } 173 | 174 | struct evconnlistener *set_xkcp_mon_listener(struct event_base *base, short port, void *ptr) 175 | { 176 | struct sockaddr_in sin; 177 | char *addr = get_iface_ip(xkcp_get_param()->local_interface); 178 | if (!addr) { 179 | debug(LOG_ERR, "get_iface_ip [%s] failed", xkcp_get_param()->local_interface); 180 | exit(0); 181 | } 182 | 183 | memset(&sin, 0, sizeof(sin)); 184 | sin.sin_family = AF_INET; 185 | sin.sin_addr.s_addr = inet_addr(addr); 186 | sin.sin_port = htons(port); 187 | 188 | struct evconnlistener * listener = evconnlistener_new_bind(base, xkcp_mon_accept_cb, ptr, 189 | LEV_OPT_CLOSE_ON_FREE|LEV_OPT_CLOSE_ON_EXEC|LEV_OPT_REUSEABLE, 190 | -1, (struct sockaddr*)&sin, sizeof(sin)); 191 | if (!listener) { 192 | debug(LOG_ERR, "Couldn't create listener: [%s]", strerror(errno)); 193 | exit(0); 194 | } 195 | 196 | return listener; 197 | } 198 | -------------------------------------------------------------------------------- /xkcp_mon.h: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #ifndef _XKCP_MON_ 23 | #define _XKCP_MON_ 24 | 25 | void xkcp_mon_accept_cb(struct evconnlistener *listener, evutil_socket_t fd, 26 | struct sockaddr *a, int slen, void *ptr); 27 | 28 | struct evconnlistener *set_xkcp_mon_listener(struct event_base *base, short port, void *ptr); 29 | 30 | int set_xkcp_server_flag(int flag); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /xkcp_server.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "ikcp.h" 45 | #include "debug.h" 46 | #include "jwHash.h" 47 | #include "xkcp_server.h" 48 | #include "xkcp_config.h" 49 | #include "xkcp_util.h" 50 | #include "xkcp_mon.h" 51 | #include "tcp_client.h" 52 | 53 | #ifndef NI_MAXHOST 54 | #define NI_MAXHOST 1025 55 | #endif 56 | #ifndef NI_MAXSERV 57 | #define NI_MAXSERV 32 58 | #endif 59 | 60 | static short mport = 9087; 61 | static jwHashTable *xkcp_hash = NULL; 62 | 63 | jwHashTable * get_xkcp_hash() 64 | { 65 | return xkcp_hash; 66 | } 67 | 68 | static void timer_event_cb(evutil_socket_t fd, short event, void *arg) 69 | { 70 | hash_iterator(xkcp_hash, (void*)xkcp_update_task_list, HASHPTR); 71 | 72 | set_timer_interval(arg); 73 | } 74 | 75 | static struct xkcp_task *create_new_tcp_connection(const int xkcpfd, struct event_base *base, 76 | struct sockaddr_in *from, int from_len, int conv, iqueue_head *task_list) 77 | { 78 | struct xkcp_proxy_param *param = malloc(sizeof(struct xkcp_proxy_param)); 79 | assert(param); 80 | memset(param, 0, sizeof(struct xkcp_proxy_param)); 81 | memcpy(¶m->sockaddr, from, from_len); 82 | param->xkcpfd = xkcpfd; 83 | param->addr_len = from_len; 84 | 85 | ikcpcb *kcp_server = ikcp_create(conv, param); 86 | xkcp_set_config_param(kcp_server); 87 | 88 | struct xkcp_task *task = malloc(sizeof(struct xkcp_task)); 89 | assert(task); 90 | task->kcp = kcp_server; 91 | task->sockaddr = ¶m->sockaddr; 92 | 93 | struct bufferevent *bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 94 | if (!bev) { 95 | debug(LOG_ERR, "bufferevent_socket_new failed [%s]", strerror(errno)); 96 | goto err; 97 | } 98 | 99 | task->bev = bev; 100 | bufferevent_setcb(bev, tcp_client_read_cb, NULL, tcp_client_event_cb, task); 101 | bufferevent_enable(bev, EV_READ); 102 | if (bufferevent_socket_connect_hostname(bev, NULL, AF_INET, 103 | xkcp_get_param()->remote_addr, 104 | xkcp_get_param()->remote_port) < 0) { 105 | bufferevent_free(bev); 106 | debug(LOG_ERR, "bufferevent_socket_connect failed [%s]", strerror(errno)); 107 | goto err; 108 | } 109 | add_task_tail(task, task_list); 110 | debug(LOG_INFO, "tcp client [%d] connect to [%s]:[%d] success", bufferevent_getfd(bev), 111 | xkcp_get_param()->remote_addr, xkcp_get_param()->remote_port); 112 | return task; 113 | err: 114 | free(param); 115 | free(task); 116 | return NULL; 117 | } 118 | 119 | static void accept_client_data(const int xkcpfd, struct event_base *base, 120 | struct sockaddr_in *from, int from_len, char *data, int len) 121 | { 122 | char host[NI_MAXHOST] = {0}; 123 | char serv[NI_MAXSERV] = {0}; 124 | char key[NI_MAXHOST+NI_MAXSERV+1] = {0}; 125 | 126 | int nret = getnameinfo((struct sockaddr *) from, from_len, 127 | host, sizeof(host), serv, sizeof(serv), 128 | NI_NUMERICHOST | NI_DGRAM); 129 | if (nret) { 130 | debug(LOG_INFO, "accept_new_client: getnameinfo error %s", strerror(errno)); 131 | return ; 132 | } 133 | 134 | iqueue_head *task_list = NULL; 135 | snprintf(key, NI_MAXHOST+NI_MAXSERV+1, "%s:%s", host, serv); 136 | struct xkcp_task *task = NULL; 137 | int conv = ikcp_getconv(data); 138 | debug(LOG_DEBUG, "accept_new_client: [%s:%s] conv [%d] len [%d]", host, serv, conv, len); 139 | if (get_ptr_by_str(xkcp_hash, key, (void*)&task_list) == HASHOK) { 140 | //old client 141 | task = get_task_from_conv(conv, task_list); 142 | debug(LOG_DEBUG, "old client, task is %d", task!=NULL?1:0); 143 | if (!task) { 144 | // new tcp connection 145 | task = create_new_tcp_connection(xkcpfd, base, from, from_len, conv, task_list); 146 | } 147 | } else { 148 | // new client 149 | if (!task_list) { 150 | debug(LOG_DEBUG, "new client"); 151 | task_list = malloc(sizeof(iqueue_head)); 152 | iqueue_init(task_list); 153 | } 154 | add_ptr_by_str(xkcp_hash, key, task_list); 155 | task = create_new_tcp_connection(xkcpfd, base, from, from_len, conv, task_list); 156 | } 157 | 158 | if (task && task->kcp) { 159 | int nret = ikcp_input(task->kcp, data, len); 160 | if (nret < 0) { 161 | debug(LOG_INFO, "[%d] ikcp_input failed [%d]", task->kcp->conv, nret); 162 | } 163 | } 164 | 165 | if (task_list) { 166 | debug(LOG_DEBUG, "accept_client_data: xkcp_forward_all_data ..."); 167 | xkcp_forward_all_data(task_list); 168 | } 169 | } 170 | 171 | static void xkcp_rcv_cb(const int sock, short int which, void *arg) 172 | { 173 | struct event_base *base = arg; 174 | struct sockaddr_in clientaddr; 175 | int clientlen = sizeof(clientaddr); 176 | memset(&clientaddr, 0, clientlen); 177 | 178 | char buf[BUF_RECV_LEN] = {0}; 179 | int len = recvfrom(sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *) &clientaddr, (socklen_t*)&clientlen); 180 | if (len > 0) { 181 | accept_client_data(sock, base, &clientaddr, clientlen, buf, len); 182 | } 183 | } 184 | 185 | static int set_xkcp_listener() 186 | { 187 | short lport = xkcp_get_param()->local_port; 188 | struct sockaddr_in sin; 189 | char *addr = get_iface_ip(xkcp_get_param()->local_interface); 190 | if (!addr) { 191 | debug(LOG_ERR, "get_iface_ip [%s] failed", xkcp_get_param()->local_interface); 192 | exit(0); 193 | } 194 | 195 | memset(&sin, 0, sizeof(sin)); 196 | sin.sin_family = AF_INET; 197 | sin.sin_addr.s_addr = inet_addr(addr); 198 | sin.sin_port = htons(lport); 199 | 200 | int xkcp_fd = socket(AF_INET, SOCK_DGRAM, 0); 201 | 202 | if (bind(xkcp_fd, (struct sockaddr *) &sin, sizeof(sin))) { 203 | debug(LOG_ERR, "xkcp_fd bind() failed %s ", strerror(errno)); 204 | exit(EXIT_FAILURE); 205 | } 206 | 207 | return xkcp_fd; 208 | } 209 | 210 | static void task_list_free(iqueue_head *task_list) 211 | { 212 | // delete task_list 213 | } 214 | 215 | int server_main_loop() 216 | { 217 | struct event timer_event, 218 | *xkcp_event = NULL; 219 | struct event_base *base = NULL; 220 | struct evconnlistener *mon_listener = NULL; 221 | 222 | base = event_base_new(); 223 | if (!base) { 224 | debug(LOG_ERR, "event_base_new()"); 225 | exit(0); 226 | } 227 | 228 | xkcp_hash = create_hash(100); 229 | 230 | int xkcp_fd = set_xkcp_listener(); 231 | 232 | mon_listener = set_xkcp_mon_listener(base, mport, xkcp_hash); 233 | set_xkcp_server_flag(1); // set it's xkcp server 234 | 235 | xkcp_event = event_new(base, xkcp_fd, EV_READ|EV_PERSIST, xkcp_rcv_cb, base); 236 | event_add(xkcp_event, NULL); 237 | 238 | event_assign(&timer_event, base, -1, EV_PERSIST, timer_event_cb, &timer_event); 239 | set_timer_interval(&timer_event); 240 | 241 | event_base_dispatch(base); 242 | 243 | evconnlistener_free(mon_listener); 244 | close(xkcp_fd); 245 | event_base_free(base); 246 | delete_hash(xkcp_hash, (void*)task_list_free, HASHPTR/*value*/, HASHSTRING/*key*/); 247 | 248 | return 0; 249 | } 250 | 251 | int main(int argc, char **argv) 252 | { 253 | struct xkcp_config *config = xkcp_get_config(); 254 | config->main_loop = server_main_loop; 255 | 256 | return xkcp_main(argc, argv); 257 | } 258 | 259 | -------------------------------------------------------------------------------- /xkcp_server.h: -------------------------------------------------------------------------------- 1 | #ifndef _XKCP_SERVER_ 2 | #define _XKCP_SERVER_ 3 | 4 | #include "ikcp.h" 5 | 6 | int server_main_loop(); 7 | 8 | struct jwHashTable; 9 | 10 | struct jwHashTable * get_xkcp_hash(); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /xkcp_spy.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include /* See NOTES */ 31 | #include 32 | #include 33 | #include 34 | 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | static void timeoutcb(evutil_socket_t fd, short what, void *arg) 44 | { 45 | struct event_base *base = arg; 46 | printf("timeout\n"); 47 | 48 | event_base_loopexit(base, NULL); 49 | } 50 | 51 | static void eventcb(struct bufferevent *bev, short what, void *ctx) 52 | { 53 | struct event_base *base = ctx; 54 | if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 55 | event_base_loopexit(base, NULL); 56 | } 57 | } 58 | 59 | static void readcb(struct bufferevent *bev, void *ctx) 60 | { 61 | struct evbuffer *input = bufferevent_get_input(bev); 62 | int len = evbuffer_get_length(input); 63 | 64 | if (len > 0) { 65 | char *buf = malloc(len+1); 66 | memset(buf, 0, len+1); 67 | evbuffer_remove(input, buf, len); 68 | printf("%s", buf); 69 | } 70 | } 71 | 72 | static void usage() 73 | { 74 | printf("xkcp_spy -h address -s|c -t cmd [ -m param]\n"); 75 | } 76 | 77 | int main(int argc, char **argv) 78 | { 79 | struct event *evtimeout; 80 | struct timeval timeout; 81 | struct event_base *base; 82 | struct bufferevent *bev; 83 | char *cmd = NULL, *addr = NULL, *param = NULL; 84 | int port = 0, opt, flag = 1; 85 | 86 | while((opt = getopt(argc, argv, "h:sct:m:")) != -1) { 87 | flag = 0; 88 | 89 | switch(opt) { 90 | case 'h': 91 | addr = strdup(optarg); 92 | break; 93 | case 's': 94 | port = 9087; 95 | break; 96 | case 'c': 97 | port = 9086; 98 | break; 99 | case 't': 100 | cmd = strdup(optarg); 101 | break; 102 | case 'm': 103 | param = strdup(optarg); 104 | break; 105 | default: 106 | usage(); 107 | exit(EXIT_FAILURE); 108 | } 109 | } 110 | 111 | if (flag || (!cmd || !addr || !port)) { 112 | usage(); 113 | exit(EXIT_FAILURE); 114 | } 115 | 116 | base = event_base_new(); 117 | if (!base) { 118 | puts("Couldn't open event base"); 119 | exit(EXIT_FAILURE); 120 | } 121 | 122 | timeout.tv_sec = 2; 123 | timeout.tv_usec = 0; 124 | 125 | evtimeout = evtimer_new(base, timeoutcb, base); 126 | evtimer_add(evtimeout, &timeout); 127 | 128 | bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); 129 | bufferevent_setcb(bev, readcb, NULL, eventcb, base); 130 | bufferevent_enable(bev, EV_READ|EV_WRITE); 131 | 132 | if (bufferevent_socket_connect_hostname(bev, NULL, AF_INET, addr, port) < 0) { 133 | bufferevent_free(bev); 134 | printf("bufferevent_socket_connect failed [%s]", strerror(errno)); 135 | exit(EXIT_FAILURE); 136 | } 137 | free(addr); 138 | 139 | if (!param) { 140 | bufferevent_write(bev, cmd, strlen(cmd)); 141 | free(cmd); 142 | } else { 143 | int size = strlen(cmd) + strlen(param) + 2; 144 | char *input = malloc(size); 145 | memset(input, 0, size); 146 | snprintf(input, size, "%s %s", cmd, param); 147 | bufferevent_write(bev, cmd, strlen(cmd)); 148 | free(input); 149 | free(cmd); 150 | free(param); 151 | } 152 | 153 | 154 | event_base_dispatch(base); 155 | 156 | bufferevent_free(bev); 157 | event_free(evtimeout); 158 | event_base_free(base); 159 | } 160 | -------------------------------------------------------------------------------- /xkcp_util.c: -------------------------------------------------------------------------------- 1 | /********************************************************************\ 2 | * This program is free software; you can redistribute it and/or * 3 | * modify it under the terms of the GNU General Public License as * 4 | * published by the Free Software Foundation; either version 2 of * 5 | * the License, or (at your option) any later version. * 6 | * * 7 | * This program is distributed in the hope that it will be useful, * 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 10 | * GNU General Public License for more details. * 11 | * * 12 | * You should have received a copy of the GNU General Public License* 13 | * along with this program; if not, contact: * 14 | * * 15 | * Free Software Foundation Voice: +1-617-542-5942 * 16 | * 59 Temple Place - Suite 330 Fax: +1-617-542-2652 * 17 | * Boston, MA 02111-1307, USA gnu@gnu.org * 18 | * * 19 | \********************************************************************/ 20 | 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include /* See NOTES */ 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include 45 | 46 | #include "ikcp.h" 47 | #include "xkcp_util.h" 48 | #include "xkcp_config.h" 49 | #include "commandline.h" 50 | #include "debug.h" 51 | 52 | static int task_list_count = 0; 53 | 54 | int get_task_list_count() 55 | { 56 | return task_list_count; 57 | } 58 | 59 | void itimeofday(long *sec, long *usec) 60 | { 61 | struct timeval time; 62 | gettimeofday(&time, NULL); 63 | if (sec) *sec = time.tv_sec; 64 | if (usec) *usec = time.tv_usec; 65 | } 66 | 67 | /* get clock in millisecond 64 */ 68 | IINT64 iclock64(void) 69 | { 70 | long s, u; 71 | IINT64 value; 72 | itimeofday(&s, &u); 73 | value = ((IINT64)s) * 1000 + (u / 1000); 74 | return value; 75 | } 76 | 77 | IUINT32 iclock() 78 | { 79 | return (IUINT32)(iclock64() & 0xfffffffful); 80 | } 81 | 82 | char *get_iface_ip(const char *ifname) 83 | { 84 | struct ifreq if_data; 85 | struct in_addr in; 86 | char *ip_str; 87 | int sockd; 88 | u_int32_t ip; 89 | 90 | /* Create a socket */ 91 | if ((sockd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 92 | debug(LOG_ERR, "socket(): %s", strerror(errno)); 93 | return NULL; 94 | } 95 | 96 | /* Get IP of internal interface */ 97 | strncpy(if_data.ifr_name, ifname, 15); 98 | if_data.ifr_name[15] = '\0'; 99 | 100 | /* Get the IP address */ 101 | if (ioctl(sockd, SIOCGIFADDR, &if_data) < 0) { 102 | debug(LOG_ERR, "ioctl(): SIOCGIFADDR %s", strerror(errno)); 103 | close(sockd); 104 | return NULL; 105 | } 106 | memcpy((void *)&ip, (void *)&if_data.ifr_addr.sa_data + 2, 4); 107 | in.s_addr = ip; 108 | 109 | close(sockd); 110 | ip_str = malloc(HTTP_IP_ADDR_LEN); 111 | memset(ip_str, 0, HTTP_IP_ADDR_LEN); 112 | if(ip_str&&inet_ntop(AF_INET, &in, ip_str, HTTP_IP_ADDR_LEN)) 113 | return ip_str; 114 | 115 | if (ip_str) free(ip_str); 116 | return NULL; 117 | } 118 | 119 | static void 120 | __list_add(iqueue_head *entry, iqueue_head *prev, iqueue_head *next) 121 | { 122 | next->prev = entry; 123 | entry->next = next; 124 | entry->prev = prev; 125 | prev->next = entry; 126 | } 127 | 128 | static void 129 | __list_del(iqueue_head *prev, iqueue_head *next) 130 | { 131 | next->prev = prev; 132 | prev->next = next; 133 | } 134 | 135 | void 136 | add_task_tail(struct xkcp_task *task, iqueue_head *head) { 137 | iqueue_head *entry = &task->head; 138 | 139 | __list_add(entry, head->prev, head); 140 | } 141 | 142 | void 143 | del_task(struct xkcp_task *task) { 144 | iqueue_head *entry = &task->head; 145 | 146 | __list_del(entry->prev, entry->next); 147 | } 148 | 149 | static int xkcp_output(const char *buf, int len, ikcpcb *kcp, void *user) 150 | { 151 | struct xkcp_proxy_param *ptr = user; 152 | int nret = sendto(ptr->xkcpfd, buf, len, 0, (struct sockaddr *)&ptr->sockaddr, sizeof(ptr->sockaddr)); 153 | if (nret > 0) 154 | debug(LOG_DEBUG, "xkcp_output conv [%d] fd [%d] len [%d], send datagram from %d", 155 | kcp->conv, ptr->xkcpfd, len, nret); 156 | else 157 | debug(LOG_INFO, "xkcp_output conv [%d] fd [%d] send datagram error: (%s)", 158 | kcp->conv, ptr->xkcpfd, strerror(errno)); 159 | 160 | return nret; 161 | } 162 | 163 | void xkcp_set_config_param(ikcpcb *kcp) 164 | { 165 | struct xkcp_param *param = xkcp_get_param(); 166 | kcp->output = xkcp_output; 167 | ikcp_wndsize(kcp, param->sndwnd, param->rcvwnd); 168 | ikcp_nodelay(kcp, param->nodelay, param->interval, param->resend, param->nc); 169 | debug(LOG_DEBUG, "sndwnd [%d] rcvwnd [%d] nodelay [%d] interval [%d] resend [%d] nc [%d]", 170 | param->sndwnd, param->rcvwnd, param->nodelay, param->interval, param->resend, param->nc); 171 | } 172 | 173 | static void set_tcp_no_delay(evutil_socket_t fd) 174 | { 175 | int one = 1; 176 | setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one)); 177 | } 178 | 179 | 180 | void *xkcp_tcp_event_cb(struct bufferevent *bev, short what, struct xkcp_task *task) 181 | { 182 | void *puser = NULL; 183 | if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { 184 | if (task) { 185 | puser = task->kcp->user; 186 | debug(LOG_INFO, "tcp_client_event_cb what is [%d] socket [%d]", 187 | what, bufferevent_getfd(bev)); 188 | if (task->bev != bev) { 189 | bufferevent_free(task->bev); 190 | debug(LOG_ERR, "impossible here\n"); 191 | } 192 | ikcp_flush(task->kcp); 193 | ikcp_release(task->kcp); 194 | del_task(task); 195 | free(task); 196 | } 197 | bufferevent_free(bev); 198 | } else if (what & BEV_EVENT_CONNECTED) { 199 | set_tcp_no_delay(bufferevent_getfd(bev)); 200 | } 201 | 202 | return puser; 203 | } 204 | 205 | void xkcp_tcp_read_cb(struct bufferevent *bev, ikcpcb *kcp) 206 | { 207 | char buf[1400] = {0}; 208 | int len, nret; 209 | struct evbuffer *input = bufferevent_get_input(bev); 210 | while ((len = evbuffer_remove(input, buf, sizeof(buf))) > 0) { 211 | nret = ikcp_send(kcp, buf, len); 212 | debug(LOG_INFO, "xkcp_tcp_read_cb : conv [%d] read data from client [%d] len [%d] ikcp_send [%d]", 213 | kcp->conv, bufferevent_getfd(bev), len, nret); 214 | memset(buf, 0, 1400); 215 | } 216 | 217 | } 218 | 219 | static void dump_task(struct xkcp_task *task, struct bufferevent *bev, int index) { 220 | struct evbuffer *output = bufferevent_get_output(bev); 221 | evbuffer_add_printf(output, 222 | "[%d]\t connection [%d]\t conv [%d]:\n --->state [%d] nrcv_buf [%d] " 223 | "nsnd_buf [%d] nrcv_que [%d] nsnd_que [%d] rcv_nxt [%d] probe [%d] " 224 | "peek [%d] stream [%d]\n", 225 | index, bufferevent_getfd(task->bev), task->kcp->conv, task->kcp->state, 226 | task->kcp->nrcv_buf, task->kcp->nsnd_buf, task->kcp->nrcv_que, 227 | task->kcp->nsnd_que, task->kcp->rcv_nxt, task->kcp->probe, 228 | ikcp_peeksize(task->kcp), task->kcp->stream); 229 | } 230 | 231 | int get_task_list_size(iqueue_head *task_list) 232 | { 233 | struct xkcp_task *task; 234 | int num = 0; 235 | iqueue_foreach(task, task_list, xkcp_task_type, head) { 236 | if (task->kcp) { 237 | num++; 238 | } 239 | } 240 | 241 | return num; 242 | } 243 | 244 | void dump_task_list(iqueue_head *task_list, struct bufferevent *bev) { 245 | struct xkcp_task *task; 246 | task_list_count = 0; 247 | iqueue_foreach(task, task_list, xkcp_task_type, head) { 248 | if (task->kcp) { 249 | dump_task(task, bev, ++task_list_count); 250 | } 251 | } 252 | debug(LOG_DEBUG, "dump_task_list number [%d]", task_list_count); 253 | } 254 | 255 | void xkcp_forward_all_data(iqueue_head *task_list) 256 | { 257 | struct xkcp_task *task; 258 | iqueue_foreach(task, task_list, xkcp_task_type, head) { 259 | if (task->kcp) { 260 | xkcp_forward_data(task); 261 | } 262 | } 263 | } 264 | 265 | void xkcp_forward_data(struct xkcp_task *task) 266 | { 267 | while(1) { 268 | char obuf[OBUF_SIZE] = {0}; 269 | int nrecv = ikcp_recv(task->kcp, obuf, OBUF_SIZE); 270 | if (nrecv < 0) { 271 | if (nrecv == -3) 272 | debug(LOG_INFO, "obuf is small, need to extend it"); 273 | break; 274 | } 275 | 276 | debug(LOG_INFO, "xkcp_forward_data conv [%d] client[%d] send [%d]", 277 | task->kcp->conv, bufferevent_getfd(task->bev), nrecv); 278 | if (task->bev) 279 | evbuffer_add(bufferevent_get_output(task->bev), obuf, nrecv); 280 | else 281 | debug(LOG_INFO, "this task has finished"); 282 | } 283 | } 284 | 285 | struct xkcp_task * 286 | get_task_from_conv(int conv, iqueue_head *task_list) 287 | { 288 | struct xkcp_task *task; 289 | iqueue_foreach(task, task_list, xkcp_task_type, head) 290 | if (task->kcp && task->kcp->conv == conv) { 291 | debug(LOG_DEBUG, "get_task_from_conv [%d]", task->kcp->conv); 292 | return task; 293 | } 294 | 295 | return NULL; 296 | } 297 | 298 | ikcpcb * 299 | get_kcp_from_conv(int conv, iqueue_head *task_list) 300 | { 301 | struct xkcp_task *task; 302 | iqueue_foreach(task, task_list, xkcp_task_type, head) 303 | if (task->kcp && task->kcp->conv == conv) { 304 | return task->kcp; 305 | } 306 | 307 | return NULL; 308 | } 309 | 310 | int xkcp_main(int argc, char **argv) 311 | { 312 | struct xkcp_config *config = xkcp_get_config(); 313 | 314 | config_init(); 315 | 316 | parse_commandline(argc, argv); 317 | 318 | if (config->main_loop == NULL) { 319 | debug(LOG_ERR, "should set main_loop firstly"); 320 | exit(0); 321 | } 322 | 323 | if (config->daemon) { 324 | 325 | debug(LOG_INFO, "Forking into background"); 326 | 327 | switch (fork()) { 328 | case 0: /* child */ 329 | setsid(); 330 | config->main_loop(); 331 | break; 332 | 333 | default: /* parent */ 334 | exit(0); 335 | break; 336 | } 337 | } else { 338 | config->main_loop(); 339 | } 340 | 341 | return (0); /* never reached */ 342 | } 343 | 344 | void 345 | set_timer_interval(struct event *timeout) 346 | { 347 | struct timeval tv; 348 | evutil_timerclear(&tv); 349 | tv.tv_usec = xkcp_get_param()->interval; 350 | event_add(timeout, &tv); 351 | } 352 | 353 | void xkcp_update_task_list(iqueue_head *task_list) 354 | { 355 | struct xkcp_task *task; 356 | iqueue_foreach(task, task_list, xkcp_task_type, head) { 357 | if (task->kcp) { 358 | ikcp_update(task->kcp, iclock()); 359 | } 360 | } 361 | } 362 | 363 | void xkcp_timer_event_cb(struct event *timeout, iqueue_head *task_list) 364 | { 365 | xkcp_update_task_list(task_list); 366 | set_timer_interval(timeout); 367 | } 368 | -------------------------------------------------------------------------------- /xkcp_util.h: -------------------------------------------------------------------------------- 1 | #ifndef _XKCP_UTIL_ 2 | #define _XKCP_UTIL_ 3 | 4 | #include "ikcp.h" 5 | 6 | #define HTTP_IP_ADDR_LEN 16 7 | #define OBUF_SIZE 4096 8 | #define BUF_RECV_LEN 1500 9 | 10 | struct event; 11 | struct eventbase; 12 | struct sockaddr_in; 13 | struct bufferevent; 14 | 15 | struct xkcp_proxy_param { 16 | struct event_base *base; 17 | int xkcpfd; 18 | struct sockaddr_in sockaddr; 19 | int addr_len; 20 | }; 21 | 22 | struct xkcp_task { 23 | iqueue_head head; 24 | ikcpcb *kcp; 25 | struct bufferevent *bev; 26 | struct sockaddr_in *sockaddr; 27 | }; 28 | 29 | typedef struct xkcp_task xkcp_task_type; 30 | 31 | void itimeofday(long *sec, long *usec); 32 | 33 | IINT64 iclock64(void); 34 | 35 | IUINT32 iclock(); 36 | 37 | int get_task_list_count(); 38 | 39 | char *get_iface_ip(const char *ifname); 40 | 41 | void add_task_tail(struct xkcp_task *task, iqueue_head *head); 42 | 43 | void del_task(struct xkcp_task *task); 44 | 45 | int get_task_list_size(iqueue_head *task_list); 46 | 47 | void dump_task_list(iqueue_head *task_list, struct bufferevent *bev); 48 | 49 | void xkcp_set_config_param(ikcpcb *kcp); 50 | 51 | void *xkcp_tcp_event_cb(struct bufferevent *bev, short what, struct xkcp_task *task); 52 | 53 | void xkcp_tcp_read_cb(struct bufferevent *bev, ikcpcb *kcp); 54 | 55 | void xkcp_forward_all_data(iqueue_head *task_list); 56 | 57 | void xkcp_forward_data(struct xkcp_task *task); 58 | 59 | void xkcp_update_task_list(iqueue_head *task_list); 60 | 61 | void set_timer_interval(struct event *timeout); 62 | 63 | void xkcp_timer_event_cb(struct event *timeout, iqueue_head *task_list); 64 | 65 | ikcpcb *get_kcp_from_conv(int conv, iqueue_head *task_list); 66 | 67 | struct xkcp_task *get_task_from_conv(int conv, iqueue_head *task_list); 68 | 69 | int xkcp_main(int argc, char **argv); 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /xkcptun.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=C implement of kcptun based on kcp and libevent2 3 | After=syslog.target network.target 4 | 5 | [Service] 6 | Type=forking 7 | ExecStart=/usr/local/bin/xkcp_server -c /etc/xkcptun/server.json -d 2 8 | ExecReload=/bin/kill -USR2 $MAINPID 9 | PrivateTmp=true 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | --------------------------------------------------------------------------------