├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── MIT-LICENSE.txt ├── Makefile ├── README.md ├── appveyor.yml ├── conf ├── client.toml └── server.toml ├── docs └── protocol.txt ├── src ├── bin │ ├── client.rs │ └── server.rs ├── client.rs ├── config.rs ├── crypto.rs ├── lib.rs ├── server.rs ├── socks5.rs ├── transfer.rs └── util.rs └── tools ├── before_deploy.sh ├── com.serholiu.fakio.plist ├── fakio.service └── supervisord.conf /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | examples/ 3 | target/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | cache: cargo 3 | matrix: 4 | allow_failures: 5 | - rust: nightly 6 | include: 7 | # Stable channel. 8 | - os: linux 9 | rust: stable 10 | env: TARGET=x86_64-unknown-linux-gnu 11 | - os: linux 12 | rust: stable 13 | env: TARGET=x86_64-unknown-linux-musl 14 | - os: osx 15 | rust: stable 16 | env: TARGET=x86_64-apple-darwin 17 | 18 | # Beta channel. 19 | - os: linux 20 | rust: beta 21 | env: TARGET=x86_64-unknown-linux-gnu 22 | 23 | # Nightly channel. 24 | - os: linux 25 | rust: nightly 26 | env: TARGET=x86_64-unknown-linux-gnu 27 | 28 | addons: 29 | apt: 30 | packages: 31 | # needed for linux-musl target 32 | - musl-tools 33 | 34 | env: 35 | global: 36 | # Default target on travis-ci. 37 | # Used as conditional check in the install stage 38 | - HOST=x86_64-unknown-linux-gnu 39 | # Used on the deployment script 40 | - PROJECT_NAME=fakio 41 | 42 | install: 43 | # prevent target re-add error from rustup 44 | - if [[ $TRAVIS_OS_NAME = linux && $HOST != $TARGET ]]; then rustup target add $TARGET; fi 45 | 46 | script: 47 | # Incorporate TARGET env var to the build and test process 48 | - cargo build --target $TARGET --verbose 49 | - cargo test --target $TARGET --verbose 50 | 51 | before_deploy: 52 | - bash tools/before_deploy.sh 53 | 54 | deploy: 55 | provider: releases 56 | # NOTE updating the `api_key.secure` 57 | # - go to: https://github.com/settings/tokens/new 58 | # - generate new token using `public_repo` scope 59 | # - encrypt it using: `travis encrypt API_KEY_HERE` 60 | # - paste the output below 61 | api_key: 62 | secure: "Y+5PgrH4V1j/k7thgX52bL7B9pLjjgdNf7ke0exWFdv0dypp+XdGdgDdkozkGuXNA8vLi1GfKlJpKhihkX/4FiltyNfiBfzd8JWjYkNJvoOoM0vwW2esrFN3dgI5KIEhbfiY7JqUPddWzmMksZpo064WsWlKhX398VaXC2pInug=" 63 | # for uploading multiple files 64 | file_glob: true 65 | # NOTE explanation on each env variable 66 | # - PROJECT_NAME: name of the project, set on the `env.global` above 67 | # - TRAVIS_TAG: tag name that the build is being deployed for, usually the version number 68 | # - TARGET: target triple of the build 69 | file: 70 | - $PROJECT_NAME-*-$TRAVIS_TAG-$TARGET 71 | # don't delete artifacts from previous stage 72 | skip_cleanup: true 73 | on: 74 | # deploy only if we push a tag 75 | tags: true 76 | # deploy only on stable channel that has TARGET env variable sets 77 | condition: $TRAVIS_RUST_VERSION = stable && $TARGET != "" 78 | 79 | notifications: 80 | email: 81 | on_success: never 82 | 83 | branches: 84 | only: 85 | - master 86 | # This regex matches semantic versions like v1.2.3-rc4+2016.02.22 87 | - /^v\d+\.\d+.*$/ 88 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "aho-corasick" 5 | version = "0.7.6" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "ansi_term" 13 | version = "0.11.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 17 | ] 18 | 19 | [[package]] 20 | name = "arc-swap" 21 | version = "0.4.4" 22 | source = "registry+https://github.com/rust-lang/crates.io-index" 23 | 24 | [[package]] 25 | name = "arrayref" 26 | version = "0.3.5" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | 29 | [[package]] 30 | name = "arrayvec" 31 | version = "0.5.1" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | 34 | [[package]] 35 | name = "atty" 36 | version = "0.2.13" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | dependencies = [ 39 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 40 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 41 | ] 42 | 43 | [[package]] 44 | name = "backtrace" 45 | version = "0.3.40" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | dependencies = [ 48 | "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", 49 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 50 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 51 | "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", 52 | ] 53 | 54 | [[package]] 55 | name = "backtrace-sys" 56 | version = "0.1.32" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | dependencies = [ 59 | "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", 60 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 61 | ] 62 | 63 | [[package]] 64 | name = "base64" 65 | version = "0.10.1" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | dependencies = [ 68 | "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", 69 | ] 70 | 71 | [[package]] 72 | name = "bitflags" 73 | version = "1.2.1" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | 76 | [[package]] 77 | name = "blake2b_simd" 78 | version = "0.5.9" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | dependencies = [ 81 | "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", 82 | "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 83 | "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 84 | ] 85 | 86 | [[package]] 87 | name = "byteorder" 88 | version = "1.3.2" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | 91 | [[package]] 92 | name = "bytes" 93 | version = "0.5.2" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | 96 | [[package]] 97 | name = "cc" 98 | version = "1.0.47" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | 101 | [[package]] 102 | name = "cfg-if" 103 | version = "0.1.10" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | 106 | [[package]] 107 | name = "cloudabi" 108 | version = "0.0.3" 109 | source = "registry+https://github.com/rust-lang/crates.io-index" 110 | dependencies = [ 111 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 112 | ] 113 | 114 | [[package]] 115 | name = "constant_time_eq" 116 | version = "0.1.4" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | 119 | [[package]] 120 | name = "crossbeam-utils" 121 | version = "0.6.6" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | dependencies = [ 124 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 125 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 126 | ] 127 | 128 | [[package]] 129 | name = "dirs" 130 | version = "2.0.2" 131 | source = "registry+https://github.com/rust-lang/crates.io-index" 132 | dependencies = [ 133 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 134 | "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", 135 | ] 136 | 137 | [[package]] 138 | name = "dirs-sys" 139 | version = "0.3.4" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | dependencies = [ 142 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 143 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 144 | "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 145 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 146 | ] 147 | 148 | [[package]] 149 | name = "env_logger" 150 | version = "0.5.13" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | dependencies = [ 153 | "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", 154 | "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 155 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 156 | "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 157 | "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", 158 | ] 159 | 160 | [[package]] 161 | name = "failure" 162 | version = "0.1.6" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | dependencies = [ 165 | "backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)", 166 | "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 167 | ] 168 | 169 | [[package]] 170 | name = "failure_derive" 171 | version = "0.1.6" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | dependencies = [ 174 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 175 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 176 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 177 | "synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", 178 | ] 179 | 180 | [[package]] 181 | name = "fakio" 182 | version = "0.3.0" 183 | dependencies = [ 184 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 185 | "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 186 | "env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)", 187 | "futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 188 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 189 | "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", 190 | "ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)", 191 | "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", 192 | "serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", 193 | "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", 194 | "tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 195 | "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 196 | ] 197 | 198 | [[package]] 199 | name = "fnv" 200 | version = "1.0.6" 201 | source = "registry+https://github.com/rust-lang/crates.io-index" 202 | 203 | [[package]] 204 | name = "fuchsia-cprng" 205 | version = "0.1.1" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | 208 | [[package]] 209 | name = "fuchsia-zircon" 210 | version = "0.3.3" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | dependencies = [ 213 | "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 214 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 215 | ] 216 | 217 | [[package]] 218 | name = "fuchsia-zircon-sys" 219 | version = "0.3.3" 220 | source = "registry+https://github.com/rust-lang/crates.io-index" 221 | 222 | [[package]] 223 | name = "futures" 224 | version = "0.3.1" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | dependencies = [ 227 | "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 228 | "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 229 | "futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 230 | "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 231 | "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 232 | "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 233 | "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 234 | ] 235 | 236 | [[package]] 237 | name = "futures-channel" 238 | version = "0.3.1" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | dependencies = [ 241 | "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 242 | "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 243 | ] 244 | 245 | [[package]] 246 | name = "futures-core" 247 | version = "0.3.1" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | 250 | [[package]] 251 | name = "futures-executor" 252 | version = "0.3.1" 253 | source = "registry+https://github.com/rust-lang/crates.io-index" 254 | dependencies = [ 255 | "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 256 | "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 257 | "futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 258 | ] 259 | 260 | [[package]] 261 | name = "futures-io" 262 | version = "0.3.1" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | 265 | [[package]] 266 | name = "futures-macro" 267 | version = "0.3.1" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | dependencies = [ 270 | "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 271 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 272 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 273 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 274 | ] 275 | 276 | [[package]] 277 | name = "futures-sink" 278 | version = "0.3.1" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | 281 | [[package]] 282 | name = "futures-task" 283 | version = "0.3.1" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | 286 | [[package]] 287 | name = "futures-util" 288 | version = "0.3.1" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | dependencies = [ 291 | "futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 292 | "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 293 | "futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 294 | "futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 295 | "futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 296 | "futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 297 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 298 | "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", 299 | "proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)", 300 | "proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 301 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 302 | ] 303 | 304 | [[package]] 305 | name = "hermit-abi" 306 | version = "0.1.3" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | dependencies = [ 309 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 310 | ] 311 | 312 | [[package]] 313 | name = "humantime" 314 | version = "1.3.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | dependencies = [ 317 | "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 318 | ] 319 | 320 | [[package]] 321 | name = "iovec" 322 | version = "0.1.4" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | dependencies = [ 325 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 326 | ] 327 | 328 | [[package]] 329 | name = "kernel32-sys" 330 | version = "0.2.2" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | dependencies = [ 333 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 334 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 335 | ] 336 | 337 | [[package]] 338 | name = "lazy_static" 339 | version = "1.4.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | 342 | [[package]] 343 | name = "libc" 344 | version = "0.2.66" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | 347 | [[package]] 348 | name = "log" 349 | version = "0.4.8" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | dependencies = [ 352 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 353 | ] 354 | 355 | [[package]] 356 | name = "memchr" 357 | version = "2.2.1" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | 360 | [[package]] 361 | name = "mio" 362 | version = "0.6.21" 363 | source = "registry+https://github.com/rust-lang/crates.io-index" 364 | dependencies = [ 365 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 366 | "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 367 | "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 368 | "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 369 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 370 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 371 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 372 | "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 373 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 374 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 375 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 376 | ] 377 | 378 | [[package]] 379 | name = "mio-named-pipes" 380 | version = "0.1.6" 381 | source = "registry+https://github.com/rust-lang/crates.io-index" 382 | dependencies = [ 383 | "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", 384 | "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", 385 | "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 386 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 387 | ] 388 | 389 | [[package]] 390 | name = "mio-uds" 391 | version = "0.6.7" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | dependencies = [ 394 | "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", 395 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 396 | "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", 397 | ] 398 | 399 | [[package]] 400 | name = "miow" 401 | version = "0.2.1" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | dependencies = [ 404 | "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", 405 | "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", 406 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 407 | "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 408 | ] 409 | 410 | [[package]] 411 | name = "miow" 412 | version = "0.3.3" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | dependencies = [ 415 | "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", 416 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 417 | ] 418 | 419 | [[package]] 420 | name = "net2" 421 | version = "0.2.33" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | dependencies = [ 424 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 425 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 426 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 427 | ] 428 | 429 | [[package]] 430 | name = "num_cpus" 431 | version = "1.11.1" 432 | source = "registry+https://github.com/rust-lang/crates.io-index" 433 | dependencies = [ 434 | "hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 435 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 436 | ] 437 | 438 | [[package]] 439 | name = "pin-project-lite" 440 | version = "0.1.1" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | 443 | [[package]] 444 | name = "pin-utils" 445 | version = "0.1.0-alpha.4" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | 448 | [[package]] 449 | name = "proc-macro-hack" 450 | version = "0.5.11" 451 | source = "registry+https://github.com/rust-lang/crates.io-index" 452 | dependencies = [ 453 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 454 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 455 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 456 | ] 457 | 458 | [[package]] 459 | name = "proc-macro-nested" 460 | version = "0.1.3" 461 | source = "registry+https://github.com/rust-lang/crates.io-index" 462 | 463 | [[package]] 464 | name = "proc-macro2" 465 | version = "1.0.6" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | dependencies = [ 468 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 469 | ] 470 | 471 | [[package]] 472 | name = "quick-error" 473 | version = "1.2.2" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | 476 | [[package]] 477 | name = "quote" 478 | version = "1.0.2" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | dependencies = [ 481 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 482 | ] 483 | 484 | [[package]] 485 | name = "rand" 486 | version = "0.5.6" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | dependencies = [ 489 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 490 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 491 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 492 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 493 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 494 | ] 495 | 496 | [[package]] 497 | name = "rand_core" 498 | version = "0.3.1" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | dependencies = [ 501 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 502 | ] 503 | 504 | [[package]] 505 | name = "rand_core" 506 | version = "0.4.2" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | 509 | [[package]] 510 | name = "rand_os" 511 | version = "0.1.3" 512 | source = "registry+https://github.com/rust-lang/crates.io-index" 513 | dependencies = [ 514 | "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", 515 | "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 516 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 517 | "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 518 | "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 519 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 520 | ] 521 | 522 | [[package]] 523 | name = "rdrand" 524 | version = "0.4.0" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | dependencies = [ 527 | "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 528 | ] 529 | 530 | [[package]] 531 | name = "redox_syscall" 532 | version = "0.1.56" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | 535 | [[package]] 536 | name = "redox_users" 537 | version = "0.3.1" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | dependencies = [ 540 | "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 541 | "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", 542 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 543 | "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 544 | ] 545 | 546 | [[package]] 547 | name = "regex" 548 | version = "1.3.1" 549 | source = "registry+https://github.com/rust-lang/crates.io-index" 550 | dependencies = [ 551 | "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", 552 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 553 | "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", 554 | "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", 555 | ] 556 | 557 | [[package]] 558 | name = "regex-syntax" 559 | version = "0.6.12" 560 | source = "registry+https://github.com/rust-lang/crates.io-index" 561 | 562 | [[package]] 563 | name = "ring" 564 | version = "0.13.5" 565 | source = "registry+https://github.com/rust-lang/crates.io-index" 566 | dependencies = [ 567 | "cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)", 568 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 569 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 570 | "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", 571 | ] 572 | 573 | [[package]] 574 | name = "rust-argon2" 575 | version = "0.5.1" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | dependencies = [ 578 | "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", 579 | "blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", 580 | "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", 581 | ] 582 | 583 | [[package]] 584 | name = "rustc-demangle" 585 | version = "0.1.16" 586 | source = "registry+https://github.com/rust-lang/crates.io-index" 587 | 588 | [[package]] 589 | name = "serde" 590 | version = "1.0.103" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | 593 | [[package]] 594 | name = "serde_derive" 595 | version = "1.0.103" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | dependencies = [ 598 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 599 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 600 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 601 | ] 602 | 603 | [[package]] 604 | name = "signal-hook-registry" 605 | version = "1.2.0" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | dependencies = [ 608 | "arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", 609 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 610 | ] 611 | 612 | [[package]] 613 | name = "slab" 614 | version = "0.4.2" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | 617 | [[package]] 618 | name = "socket2" 619 | version = "0.3.11" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | dependencies = [ 622 | "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", 623 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 624 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 625 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 626 | ] 627 | 628 | [[package]] 629 | name = "syn" 630 | version = "1.0.9" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | dependencies = [ 633 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 634 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 635 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 636 | ] 637 | 638 | [[package]] 639 | name = "synstructure" 640 | version = "0.12.3" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | dependencies = [ 643 | "proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 644 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 645 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 646 | "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 647 | ] 648 | 649 | [[package]] 650 | name = "termcolor" 651 | version = "1.0.5" 652 | source = "registry+https://github.com/rust-lang/crates.io-index" 653 | dependencies = [ 654 | "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 655 | ] 656 | 657 | [[package]] 658 | name = "thread_local" 659 | version = "0.3.6" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | dependencies = [ 662 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 663 | ] 664 | 665 | [[package]] 666 | name = "time" 667 | version = "0.1.42" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | dependencies = [ 670 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 671 | "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", 672 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 673 | ] 674 | 675 | [[package]] 676 | name = "tokio" 677 | version = "0.2.2" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | dependencies = [ 680 | "bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", 681 | "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", 682 | "futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 683 | "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 684 | "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", 685 | "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 686 | "mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)", 687 | "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", 688 | "mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)", 689 | "num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)", 690 | "pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 691 | "signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 692 | "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", 693 | "tokio-macros 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 694 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 695 | ] 696 | 697 | [[package]] 698 | name = "tokio-macros" 699 | version = "0.2.0" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | dependencies = [ 702 | "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", 703 | "syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", 704 | ] 705 | 706 | [[package]] 707 | name = "toml" 708 | version = "0.4.10" 709 | source = "registry+https://github.com/rust-lang/crates.io-index" 710 | dependencies = [ 711 | "serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)", 712 | ] 713 | 714 | [[package]] 715 | name = "unicode-xid" 716 | version = "0.2.0" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | 719 | [[package]] 720 | name = "untrusted" 721 | version = "0.6.2" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | 724 | [[package]] 725 | name = "winapi" 726 | version = "0.2.8" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | 729 | [[package]] 730 | name = "winapi" 731 | version = "0.3.8" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | dependencies = [ 734 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 735 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 736 | ] 737 | 738 | [[package]] 739 | name = "winapi-build" 740 | version = "0.1.1" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | 743 | [[package]] 744 | name = "winapi-i686-pc-windows-gnu" 745 | version = "0.4.0" 746 | source = "registry+https://github.com/rust-lang/crates.io-index" 747 | 748 | [[package]] 749 | name = "winapi-util" 750 | version = "0.1.2" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | dependencies = [ 753 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 754 | ] 755 | 756 | [[package]] 757 | name = "winapi-x86_64-pc-windows-gnu" 758 | version = "0.4.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | 761 | [[package]] 762 | name = "wincolor" 763 | version = "1.0.2" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | dependencies = [ 766 | "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 767 | "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 768 | ] 769 | 770 | [[package]] 771 | name = "ws2_32-sys" 772 | version = "0.2.1" 773 | source = "registry+https://github.com/rust-lang/crates.io-index" 774 | dependencies = [ 775 | "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", 776 | "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 777 | ] 778 | 779 | [metadata] 780 | "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" 781 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 782 | "checksum arc-swap 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d7b8a9123b8027467bce0099fe556c628a53c8d83df0507084c31e9ba2e39aff" 783 | "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" 784 | "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" 785 | "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" 786 | "checksum backtrace 0.3.40 (registry+https://github.com/rust-lang/crates.io-index)" = "924c76597f0d9ca25d762c25a4d369d51267536465dc5064bdf0eb073ed477ea" 787 | "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" 788 | "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" 789 | "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 790 | "checksum blake2b_simd 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b83b7baab1e671718d78204225800d6b170e648188ac7dc992e9d6bddf87d0c0" 791 | "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" 792 | "checksum bytes 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1c85319f157e4e26c703678e68e26ab71a46c0199286fa670b21cc9fec13d895" 793 | "checksum cc 1.0.47 (registry+https://github.com/rust-lang/crates.io-index)" = "aa87058dce70a3ff5621797f1506cb837edd02ac4c0ae642b4542dce802908b8" 794 | "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 795 | "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 796 | "checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" 797 | "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" 798 | "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" 799 | "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" 800 | "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" 801 | "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" 802 | "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" 803 | "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 804 | "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 805 | "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 806 | "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 807 | "checksum futures 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b6f16056ecbb57525ff698bb955162d0cd03bee84e6241c27ff75c08d8ca5987" 808 | "checksum futures-channel 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fcae98ca17d102fd8a3603727b9259fcf7fa4239b603d2142926189bc8999b86" 809 | "checksum futures-core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "79564c427afefab1dfb3298535b21eda083ef7935b4f0ecbfcb121f0aec10866" 810 | "checksum futures-executor 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1e274736563f686a837a0568b478bdabfeaec2dca794b5649b04e2fe1627c231" 811 | "checksum futures-io 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e676577d229e70952ab25f3945795ba5b16d63ca794ca9d2c860e5595d20b5ff" 812 | "checksum futures-macro 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52e7c56c15537adb4f76d0b7a76ad131cb4d2f4f32d3b0bcabcbe1c7c5e87764" 813 | "checksum futures-sink 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "171be33efae63c2d59e6dbba34186fe0d6394fb378069a76dfd80fdcffd43c16" 814 | "checksum futures-task 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0bae52d6b29cf440e298856fec3965ee6fa71b06aa7495178615953fd669e5f9" 815 | "checksum futures-util 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c0d66274fb76985d3c62c886d1da7ac4c0903a8c9f754e8fe0f35a6a6cc39e76" 816 | "checksum hermit-abi 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "307c3c9f937f38e3534b1d6447ecf090cafcc9744e4a6360e8b037b2cf5af120" 817 | "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 818 | "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 819 | "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 820 | "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 821 | "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" 822 | "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 823 | "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" 824 | "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" 825 | "checksum mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" 826 | "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 827 | "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 828 | "checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" 829 | "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 830 | "checksum num_cpus 1.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" 831 | "checksum pin-project-lite 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f0af6cbca0e6e3ce8692ee19fb8d734b641899e07b68eb73e9bbbd32f1703991" 832 | "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" 833 | "checksum proc-macro-hack 0.5.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ecd45702f76d6d3c75a80564378ae228a85f0b59d2f3ed43c91b4a69eb2ebfc5" 834 | "checksum proc-macro-nested 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "369a6ed065f249a159e06c45752c780bda2fb53c995718f9e484d08daa9eb42e" 835 | "checksum proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9c9e470a8dc4aeae2dee2f335e8f533e2d4b347e1434e5671afc49b054592f27" 836 | "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" 837 | "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" 838 | "checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" 839 | "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 840 | "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 841 | "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" 842 | "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 843 | "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 844 | "checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" 845 | "checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" 846 | "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" 847 | "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" 848 | "checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" 849 | "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" 850 | "checksum serde 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "1217f97ab8e8904b57dd22eb61cde455fa7446a9c1cf43966066da047c1f3702" 851 | "checksum serde_derive 1.0.103 (registry+https://github.com/rust-lang/crates.io-index)" = "a8c6faef9a2e64b0064f48570289b4bf8823b7581f1d6157c1b52152306651d0" 852 | "checksum signal-hook-registry 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" 853 | "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 854 | "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" 855 | "checksum syn 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f89693ae015201f8de93fd96bde2d065f8bfc3f97ce006d5bc9f900b97c0c7c0" 856 | "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" 857 | "checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e" 858 | "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" 859 | "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" 860 | "checksum tokio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2e765bf9f550bd9b8a970633ca3b56b8120c4b6c5dcbe26a93744cb02fee4b17" 861 | "checksum tokio-macros 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5795a71419535c6dcecc9b6ca95bdd3c2d6142f7e8343d7beb9923f129aa87e" 862 | "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" 863 | "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 864 | "checksum untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "55cd1f4b4e96b46aeb8d4855db4a7a9bd96eeeb5c6a1ab54593328761642ce2f" 865 | "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 866 | "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 867 | "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 868 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 869 | "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" 870 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 871 | "checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" 872 | "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 873 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fakio" 3 | version = "0.3.0" 4 | authors = ["Serho Liu "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "fakio" 9 | 10 | [[bin]] 11 | name = "fakio-client" 12 | path = "src/bin/client.rs" 13 | 14 | [[bin]] 15 | name = "fakio-server" 16 | path = "src/bin/server.rs" 17 | 18 | [dependencies] 19 | log = "0.4" 20 | time = "0.1" 21 | ansi_term = "0.11" 22 | env_logger = "0.5" 23 | futures = "0.3" 24 | tokio = { version = "0.2", features = ["full"] } 25 | ring = "0.13" 26 | rand = "0.5" 27 | toml = "0.4" 28 | serde = "1.0" 29 | serde_derive = "1.0" 30 | dirs = "2.0" 31 | -------------------------------------------------------------------------------- /MIT-LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2017 SerhoLiu, http://serholiu.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the “Software”), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to 8 | do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 14 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 15 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 16 | FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 17 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 18 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | client: 2 | RUST_BACKTRACE=1 cargo run --bin fakio-client 3 | 4 | server: 5 | RUST_BACKTRACE=1 cargo run --bin fakio-server 6 | 7 | release: 8 | cargo build --release 9 | 10 | clippy: 11 | cargo clippy -- -A many_single_char_names 12 | 13 | format: 14 | cargo fmt -- --check || exit 0 15 | cargo fmt || exit 0 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Fakio 2 | 3 | [![Build Status](https://travis-ci.org/SerhoLiu/fakio.svg?branch=master)](https://travis-ci.org/SerhoLiu/fakio) 4 | [![Build status](https://ci.appveyor.com/api/projects/status/a9qidcdwv39bg1qf?svg=true)](https://ci.appveyor.com/project/SerhoLiu/fakio) 5 | 6 | 7 | Protect your data transmission security. 8 | 9 | ## License 10 | 11 | MIT LICENSE, see MIT-LICENSE.txt 12 | 13 | ## Warning 14 | 15 | IGNORANCE IS STRENGTH 16 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/starkat99/appveyor-rust/ 2 | 3 | # Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. 4 | image: Visual Studio 2015 5 | 6 | environment: 7 | global: 8 | PROJECT_NAME: fakio 9 | matrix: 10 | # Stable channel 11 | - TARGET: x86_64-pc-windows-gnu 12 | CHANNEL: stable 13 | MSYS2_BITS: 64 14 | - TARGET: x86_64-pc-windows-msvc 15 | CHANNEL: stable 16 | 17 | # Install Rust and Cargo 18 | # (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) 19 | install: 20 | - curl -sSf -o rustup-init.exe https://win.rustup.rs 21 | - rustup-init.exe --default-host %TARGET% --default-toolchain %CHANNEL% -y 22 | - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin 23 | - if defined MSYS2_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS2_BITS%\bin 24 | - rustc -Vv 25 | - cargo -V 26 | 27 | # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents 28 | # the "directory does not contain a project or solution file" error. 29 | # source: https://github.com/starkat99/appveyor-rust/blob/master/appveyor.yml#L113 30 | build: false 31 | 32 | # Equivalent to Travis' `script` phase 33 | test_script: 34 | - cargo build --verbose 35 | - cargo test 36 | 37 | before_deploy: 38 | # Generate artifacts for release 39 | - cargo build --release 40 | - mkdir staging 41 | # release file will look like 'fakio-client-v3.0-x86_64-pc-windows-msvc' 42 | - copy target\release\fakio-server.exe staging\fakio-server-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.exe 43 | - copy target\release\fakio-client.exe staging\fakio-client-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.exe 44 | - appveyor PushArtifact staging\fakio-server-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.exe 45 | - appveyor PushArtifact staging\fakio-client-%APPVEYOR_REPO_TAG_NAME%-%TARGET%.exe 46 | 47 | deploy: 48 | description: 'Windows release' 49 | # All the exe artifacts will be deployed 50 | artifact: /.*\.exe/ 51 | # Here's how: 52 | # - Go to 'https://github.com/settings/tokens/new' and generate a Token with only the 53 | # `public_repo` scope enabled 54 | # - Then go to 'https://ci.appveyor.com/tools/encrypt' and enter the newly generated token. 55 | # - Enter the "encrypted value" below 56 | auth_token: 57 | secure: d98yfNgnq8nle1+Hgi6gWSeVLxO6YI77hAOVjyP8jA2tr0QX3eKMf1+8dttvFHBb 58 | provider: GitHub 59 | # deploy when a new tag is pushed and only on the stable channel 60 | on: 61 | # channel to use to produce the release artifacts 62 | CHANNEL: stable 63 | appveyor_repo_tag: true 64 | 65 | branches: 66 | only: 67 | - master 68 | # IMPORTANT Regex to match tags. Required, or appveyor may not trigger deploys when a new tag 69 | # is pushed. This regex matches semantic versions like v1.2.3-rc4+2016.02.22 70 | - /v.*/ 71 | 72 | # Disable caching, for now 73 | #cache: 74 | # - '%USERPROFILE%\.cargo' 75 | # - 'target -> Cargo.lock' -------------------------------------------------------------------------------- /conf/client.toml: -------------------------------------------------------------------------------- 1 | username = "serho" 2 | password = "123456" 3 | cipher = "aes-256-gcm" 4 | server = "127.0.0.1:8888" 5 | listen = "127.0.0.1:1080" 6 | -------------------------------------------------------------------------------- /conf/server.toml: -------------------------------------------------------------------------------- 1 | [server] 2 | listen = "127.0.0.1:8888" 3 | 4 | [users] 5 | serho = "123456" 6 | -------------------------------------------------------------------------------- /docs/protocol.txt: -------------------------------------------------------------------------------- 1 | Fakio 安全传输协议 2 | 3 | Author:Serho Liu 4 | Date: 2017-07-11 5 | Version: V3 6 | 7 | 8 | 一 整体结构图 9 | 10 | Response Buffer 11 | / | \ 12 | Remote Server Client 13 | \ | / 14 | Request Buffer 15 | 16 | 说明如下: 17 | 1. Client: 发起请求的一方 18 | 2. Remote: Client 请求的真实地址(主机) 19 | 20 | 21 | 二 密码套件 22 | 23 | 支持如下 AEAD ciphers 24 | 25 | * 0x01 aes-128-gcm: AES-128-GCM 26 | * 0x02 aes-256-gcm: AES-256-GCM 27 | * 0x03 chacha20-poly1305: CHACHA20-POLY1305 28 | 29 | 30 | 三 握手协议 31 | 32 | 1. Client 请求 33 | 34 | 根据 SOCKS5 协议的 Request 部分修改得到握手协议的 Client 请求部分, 协议格式如下所示: 35 | 36 | +---------------+------+--------------------------------------------------+ 37 | | Header | Plain| Data | 38 | +-----+---------+------+---------+-----+------+-----+----------+----------+ 39 | | LEN | LEN TAG | USER | PADDING | VER | CTYP |ATYP | DST.ADDR | DST.PORT | 40 | +-----+---------+------+---------+-----+------+----------------+----------+ 41 | | 2 | Var. | 32 | Var. | 1 | 1 | 1 | Var. | 2 | 42 | +-----+---------+------+---------+-----+------+-----+----------+----------+ 43 | 44 | 其中: 45 | +. 下面涉及加密的使用 aes-256-gcm, 密钥为预留 key SHA2 摘要 46 | +. Header LEN 为 Plain + (Data加密) 的长度, 并加密 47 | +. USER 为预留用户名 SHA2 摘要 48 | +. PADDING 为随机字符, 长度范围 1~255, 第一个字节表示长度 49 | +. VER 0x03 50 | +. CTYP 后续使用的密码套件, 编号见 <二> 51 | +. 后续地址跟 SOCKS5 协议一样 52 | +. Data 同 LEN 字段一样加密 53 | 54 | 2. Server 响应 55 | 56 | 响应 Client 客户端请求, 格式如下所示: 57 | 58 | +---------------+--------------------------------+ 59 | | Header | Data | 60 | +-----+---------+---------+-------+-------+------+ 61 | | LEN | LEN TAG | PADDING | RESP | EKEY | DKEY | 62 | +-----+---------+---------+-------+-------+------+ 63 | | 2 | Var. | Var. | 1 | Var. | Var. | 64 | +-----+---------+---------+-------+-------+------+ 65 | 66 | 其中: 67 | +. 下面涉及加密的使用 aes-256-gcm, 密钥为预留 key SHA2 摘要 68 | +. Header LEN 为 Plain + (Data加密) 的长度, 并加密 69 | +. PADDING 为随机字符, 长度范围 1~255, 第一个字节表示长度 70 | +. RESP 为服务器响应结果: 71 | 0x00 - 成功 72 | 0x01 - Cipher 不支持 73 | 0x02 - 服务器内部错误 74 | 0x03 - 请求 Remote 无法访问 75 | +. 在 RESP 为 0x00 的情况下 (第一个字节表示长度): 76 | EKEY - 客户端加密 Key 77 | DKEY - 客户端解密 Key 78 | 79 | 3. 数据传输 80 | 81 | 使用握手时协商的算法和获取到的 Key, 数据包格式如下: 82 | 83 | +-----+---------+---------+----------+ 84 | | LEN | LEN TAG | DATA | DATA TAG | 85 | +-----+---------+---------+----------+ 86 | | 2 | Var. | Var. | Var. | 87 | +-----+---------+---------+----------+ 88 | 89 | 其中: 90 | +. LEN 为 DATA + DATA TAG 长度 91 | +. LEN 和 DATA 加密 92 | -------------------------------------------------------------------------------- /src/bin/client.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process; 3 | 4 | use log::error; 5 | 6 | use fakio::{client, ClientConfig}; 7 | 8 | #[tokio::main] 9 | async fn main() { 10 | fakio::init_logger(); 11 | 12 | let config_path = env::args() 13 | .nth(1) 14 | .unwrap_or_else(|| "conf/client.toml".to_string()); 15 | 16 | let config_path = fakio::expand_tilde_path(&config_path); 17 | let config = match ClientConfig::new(&*config_path) { 18 | Ok(cfg) => cfg, 19 | Err(err) => { 20 | error!("load config {} failed, {}", config_path, err); 21 | process::exit(1); 22 | } 23 | }; 24 | 25 | if let Err(e) = client::serve(config).await { 26 | error!("start client failed, {}", e); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/bin/server.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::process; 3 | 4 | use log::error; 5 | 6 | use fakio::{server, ServerConfig}; 7 | 8 | #[tokio::main] 9 | async fn main() { 10 | fakio::init_logger(); 11 | 12 | let config_path = env::args() 13 | .nth(1) 14 | .unwrap_or_else(|| "conf/server.toml".to_string()); 15 | 16 | let config_path = fakio::expand_tilde_path(&config_path); 17 | let config = match ServerConfig::new(&*config_path) { 18 | Ok(cfg) => cfg, 19 | Err(err) => { 20 | error!("load config {} failed, {}", config_path, err); 21 | process::exit(1); 22 | } 23 | }; 24 | 25 | if let Err(e) = server::serve(config).await { 26 | error!("start client failed, {}", e); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/client.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::net::SocketAddr; 3 | use std::sync::Arc; 4 | 5 | use futures::future::try_join; 6 | use tokio; 7 | use tokio::net::TcpListener; 8 | use tokio::net::TcpStream; 9 | use tokio::prelude::*; 10 | use tokio::time::{timeout, Duration}; 11 | 12 | use super::config::ClientConfig; 13 | use super::crypto::{Crypto, KeyPair}; 14 | use super::socks5; 15 | use super::transfer::{self, Stat}; 16 | use super::util::RandomBytes; 17 | use super::v3; 18 | 19 | const HANDSHAKE_TIMEOUT: u64 = 10; 20 | 21 | pub async fn serve(config: ClientConfig) -> io::Result<()> { 22 | let config = Arc::new(config); 23 | let socks5_reply = Arc::new(socks5::Reply::new(config.listen)); 24 | 25 | let mut listener = TcpListener::bind(&config.listen) 26 | .await 27 | .map_err(|e| other(&format!("listen on {}, {}", config.listen, e)))?; 28 | 29 | info!("Listening for socks5 proxy on local {}", config.listen); 30 | 31 | loop { 32 | match listener.accept().await { 33 | Ok((client, peer_addr)) => { 34 | let config = config.clone(); 35 | let socks5_reply = socks5_reply.clone(); 36 | 37 | tokio::spawn(async move { 38 | match process(config, socks5_reply, client, peer_addr).await { 39 | Ok((req, stat)) => { 40 | info!("{} - request {} success, {}", peer_addr, req, stat) 41 | } 42 | Err(e) => error!("{} - failed by {}", peer_addr, e), 43 | } 44 | }); 45 | } 46 | Err(e) => error!("accepting socket, {}", e), 47 | } 48 | } 49 | } 50 | 51 | async fn process( 52 | config: Arc, 53 | socks5_reply: Arc, 54 | mut client: TcpStream, 55 | client_addr: SocketAddr, 56 | ) -> io::Result<(String, Stat)> { 57 | let handshake = async { 58 | let (mut server, req_addr) = 59 | socks5::handshake(&mut client, client_addr, config.server, &socks5_reply).await?; 60 | let req = req_addr.get()?; 61 | let req = format!("{}:{}", req.0, req.1); 62 | let mut handshake = Handshake::new(&config, &mut server, req_addr)?; 63 | let key_pair = handshake.hand().await?; 64 | Ok::<_, io::Error>((server, req, key_pair)) 65 | }; 66 | 67 | let (mut server, req_addr, key_pair) = 68 | timeout(Duration::from_secs(HANDSHAKE_TIMEOUT), handshake).await??; 69 | let (client_key, server_key) = key_pair.split(); 70 | 71 | let (cr, cw) = client.split(); 72 | let (sr, sw) = server.split(); 73 | 74 | let en = transfer::encrypt(cr, sw, config.cipher, client_key); 75 | let de = transfer::decrypt(sr, cw, config.cipher, server_key); 76 | 77 | let stat = try_join(en, de).await?; 78 | Ok((req_addr, Stat::new(stat.0, stat.1))) 79 | } 80 | 81 | struct Handshake<'a> { 82 | config: &'a ClientConfig, 83 | server: &'a mut TcpStream, 84 | req_addr: socks5::ReqAddr, 85 | 86 | crypto: Crypto, 87 | } 88 | 89 | impl<'a> Handshake<'a> { 90 | fn new( 91 | config: &'a ClientConfig, 92 | server: &'a mut TcpStream, 93 | req_addr: socks5::ReqAddr, 94 | ) -> io::Result> { 95 | let crypto = Crypto::new( 96 | v3::HANDSHAKE_CIPHER, 97 | config.password.as_ref(), 98 | config.password.as_ref(), 99 | )?; 100 | 101 | Ok(Handshake { 102 | config, 103 | server, 104 | req_addr, 105 | crypto, 106 | }) 107 | } 108 | 109 | async fn hand(&mut self) -> io::Result { 110 | let mut buf = [0u8; v3::MAX_BUFFER_SIZE]; 111 | 112 | let request_len = self.request(&mut buf[..])?; 113 | 114 | self.server.write_all(&buf[..request_len]).await?; 115 | 116 | let resp_header_len = v3::DATA_LEN_LEN + self.crypto.tag_len(); 117 | self.server.read_exact(&mut buf[..resp_header_len]).await?; 118 | let len = self.crypto.decrypt(&mut buf[..resp_header_len])?; 119 | 120 | debug_assert_eq!(len, v3::DATA_LEN_LEN); 121 | let data_len = ((buf[0] as usize) << 8) + (buf[1] as usize); 122 | // Padding len 1 + resp 1 123 | if data_len < 2 + self.crypto.tag_len() { 124 | return Err(other("response data length too small")); 125 | } 126 | 127 | self.server.read_exact(&mut buf[..data_len]).await?; 128 | let len = self.crypto.decrypt(&mut buf[..data_len])?; 129 | 130 | // +---------+-------+-------+------+ 131 | // | PADDING | RESP | EKEY | DKEY | 132 | // +---------+-------+-------+------+ 133 | // | Var. | 1 | Var. | Var. | 134 | // +---------+-------+-------+------+ 135 | let padding_len = (buf[0] as usize) + 1; 136 | if len < padding_len + 1 { 137 | return Err(other("response data length too small")); 138 | } 139 | 140 | match buf[padding_len] { 141 | v3::SERVER_RESP_SUCCEED => debug!("server resp success"), 142 | v3::SERVER_RESP_CIPHER_ERROR => return Err(other("server not support cipher")), 143 | v3::SERVER_RESP_ERROR => return Err(other("server internal error")), 144 | v3::SERVER_RESP_REMOTE_FAILED => { 145 | return Err(other("server connect request addr error")) 146 | } 147 | _ => return Err(other("server resp unknown")), 148 | } 149 | let key_len = len - padding_len - 1; 150 | if key_len != 2 * self.config.cipher.key_len() { 151 | return Err(other("server resp key length not match")); 152 | } 153 | 154 | Ok(KeyPair::from(&buf[padding_len + 1..len])) 155 | } 156 | 157 | // +---------------+------+--------------------------------------------------+ 158 | // | Header | Plain| Data | 159 | // +-----+---------+------+---------+-----+------+-----+----------+----------+ 160 | // | LEN | LEN TAG | USER | PADDING | VER | CTYP |ATYP | DST.ADDR | DST.PORT | 161 | // +-----+---------+------+---------+-----+------+----------------+----------+ 162 | // | 2 | Var. | 32 | Var. | 1 | 1 | 1 | Var. | 2 | 163 | // +-----+---------+------+---------+-----+------+-----+----------+----------+ 164 | fn request(&mut self, buf: &mut [u8]) -> io::Result { 165 | let tag_len = self.crypto.tag_len(); 166 | let header_len = v3::DATA_LEN_LEN + tag_len; 167 | 168 | // USER 169 | let start = header_len; 170 | let end = header_len + v3::DEFAULT_DIGEST_LEN; 171 | (&mut buf[start..end]).copy_from_slice(self.config.username.as_ref()); 172 | 173 | // PADDING 174 | let random_bytes = RandomBytes::new()?; 175 | let bytes = random_bytes.get(); 176 | let start = end; 177 | let end = end + bytes.len(); 178 | (&mut buf[start..end]).copy_from_slice(bytes); 179 | 180 | // VER and CTYP 181 | let start = end; 182 | let end = end + 2; 183 | 184 | buf[start] = v3::VERSION; 185 | buf[start + 1] = self.config.cipher.to_no(); 186 | 187 | // ADDR 188 | let bytes = self.req_addr.get_bytes(); 189 | let start = end; 190 | let end = end + bytes.len(); 191 | (&mut buf[start..end]).copy_from_slice(bytes); 192 | 193 | // Encrypt it 194 | let data_len = end - header_len; 195 | let encrypted_data_len = data_len + tag_len; 196 | buf[0] = (encrypted_data_len >> 8) as u8; 197 | buf[1] = encrypted_data_len as u8; 198 | self.crypto.encrypt(&mut buf[0..header_len], 2)?; 199 | 200 | let len = data_len - v3::DEFAULT_DIGEST_LEN; 201 | let start = header_len + v3::DEFAULT_DIGEST_LEN; 202 | let end = end + tag_len; 203 | self.crypto.encrypt(&mut buf[start..end], len)?; 204 | 205 | Ok(end) 206 | } 207 | } 208 | 209 | #[inline] 210 | fn other(desc: &str) -> io::Error { 211 | io::Error::new(io::ErrorKind::Other, desc) 212 | } 213 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use std::borrow; 2 | use std::collections::HashMap; 3 | use std::convert; 4 | use std::error; 5 | use std::fs; 6 | use std::net::{SocketAddr, ToSocketAddrs}; 7 | use std::result; 8 | 9 | use ring::digest; 10 | use serde::de; 11 | use serde_derive::Deserialize; 12 | use toml; 13 | 14 | use super::crypto::Cipher; 15 | use super::v3; 16 | 17 | pub type Result = result::Result>; 18 | 19 | #[derive(Debug, Hash, PartialEq, Eq, Copy, Clone)] 20 | pub struct Digest { 21 | value: [u8; v3::DEFAULT_DIGEST_LEN], 22 | } 23 | 24 | #[derive(Debug, Clone)] 25 | pub struct ClientConfig { 26 | pub username: Digest, 27 | pub password: Digest, 28 | pub cipher: Cipher, 29 | pub server: SocketAddr, 30 | pub listen: SocketAddr, 31 | } 32 | 33 | #[derive(Debug, Clone)] 34 | pub struct User { 35 | pub name: String, 36 | pub password: Digest, 37 | } 38 | 39 | #[derive(Debug, Clone)] 40 | pub struct ServerConfig { 41 | pub listen: SocketAddr, 42 | pub users: HashMap, 43 | } 44 | 45 | impl Digest { 46 | fn new(value: &str) -> Digest { 47 | let mut d = Digest { 48 | value: [0u8; v3::DEFAULT_DIGEST_LEN], 49 | }; 50 | 51 | d.value 52 | .copy_from_slice(digest::digest(v3::DEFAULT_DIGEST, value.as_bytes()).as_ref()); 53 | d 54 | } 55 | 56 | #[inline] 57 | pub fn size(&self) -> usize { 58 | v3::DEFAULT_DIGEST_LEN 59 | } 60 | } 61 | 62 | impl convert::AsRef<[u8]> for Digest { 63 | fn as_ref(&self) -> &[u8] { 64 | &self.value 65 | } 66 | } 67 | 68 | impl borrow::Borrow<[u8]> for Digest { 69 | fn borrow(&self) -> &[u8] { 70 | &self.value 71 | } 72 | } 73 | 74 | impl ClientConfig { 75 | pub fn new(path: &str) -> Result { 76 | let raw: TomlClientConfig = read_toml_config(path)?; 77 | 78 | let cipher = match raw.cipher { 79 | Some(ref c) => Cipher::new(c).map_err(|err| format!("'{}' {}", c, err))?, 80 | None => Cipher::default(), 81 | }; 82 | 83 | let server = raw 84 | .server 85 | .to_socket_addrs() 86 | .map(|mut addrs| addrs.nth(0).unwrap()) 87 | .map_err(|err| format!("resolve server {}, {}", raw.server, err))?; 88 | 89 | let listen = raw 90 | .listen 91 | .parse::() 92 | .map_err(|err| format!("parse listen {}, {}", raw.listen, err))?; 93 | 94 | Ok(ClientConfig { 95 | username: Digest::new(&raw.username), 96 | password: Digest::new(&raw.password), 97 | cipher, 98 | server, 99 | listen, 100 | }) 101 | } 102 | } 103 | 104 | impl ServerConfig { 105 | pub fn new(path: &str) -> Result { 106 | let raw: TomlServerConfig = read_toml_config(path)?; 107 | 108 | let listen = raw 109 | .server 110 | .listen 111 | .parse::() 112 | .map_err(|err| format!("parse listen {}, {}", raw.server.listen, err))?; 113 | 114 | let mut users = HashMap::new(); 115 | 116 | for (user, password) in raw.users { 117 | users.insert( 118 | Digest::new(&user), 119 | User { 120 | name: user, 121 | password: Digest::new(&password), 122 | }, 123 | ); 124 | } 125 | 126 | Ok(ServerConfig { listen, users }) 127 | } 128 | } 129 | 130 | #[derive(Debug, Clone, Deserialize)] 131 | struct TomlClientConfig { 132 | username: String, 133 | password: String, 134 | cipher: Option, 135 | server: String, 136 | listen: String, 137 | } 138 | 139 | #[derive(Debug, Deserialize)] 140 | struct TomlServer { 141 | listen: String, 142 | } 143 | 144 | #[derive(Debug, Deserialize)] 145 | struct TomlServerConfig { 146 | server: TomlServer, 147 | users: HashMap, 148 | } 149 | 150 | fn read_toml_config(path: &str) -> Result 151 | where 152 | T: de::DeserializeOwned, 153 | { 154 | let content = fs::read_to_string(path)?; 155 | let config = toml::from_str(&content)?; 156 | Ok(config) 157 | } 158 | -------------------------------------------------------------------------------- /src/crypto.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::result; 4 | 5 | use ring::{aead, digest, hkdf, hmac, rand}; 6 | 7 | pub type Result = result::Result; 8 | 9 | #[derive(Clone, Copy, Debug)] 10 | pub enum Error { 11 | CipherNotSupport, 12 | GenKey, 13 | KeyLenNotMatch(usize), 14 | OpenKey, 15 | SealKey, 16 | SealBufferTooSmall(usize), 17 | Open, 18 | Seal, 19 | } 20 | 21 | #[derive(Clone, Copy, Debug)] 22 | pub enum Cipher { 23 | AES128GCM, 24 | AES256GCM, 25 | CHACHA20POLY1305, 26 | } 27 | 28 | impl Cipher { 29 | pub fn new(name: &str) -> Result { 30 | match name.to_lowercase().as_ref() { 31 | "aes-128-gcm" => Ok(Cipher::AES128GCM), 32 | "aes-256-gcm" => Ok(Cipher::AES256GCM), 33 | "chacha20-poly1305" => Ok(Cipher::CHACHA20POLY1305), 34 | _ => Err(Error::CipherNotSupport), 35 | } 36 | } 37 | 38 | pub fn from_no(no: u8) -> Result { 39 | match no { 40 | 1 => Ok(Cipher::AES128GCM), 41 | 2 => Ok(Cipher::AES256GCM), 42 | 3 => Ok(Cipher::CHACHA20POLY1305), 43 | _ => Err(Error::CipherNotSupport), 44 | } 45 | } 46 | 47 | pub fn to_no(self) -> u8 { 48 | match self { 49 | Cipher::AES128GCM => 1, 50 | Cipher::AES256GCM => 2, 51 | Cipher::CHACHA20POLY1305 => 3, 52 | } 53 | } 54 | 55 | #[inline] 56 | pub fn key_len(self) -> usize { 57 | self.algorithm().key_len() 58 | } 59 | 60 | #[inline] 61 | pub fn tag_len(self) -> usize { 62 | self.algorithm().tag_len() 63 | } 64 | 65 | fn algorithm(self) -> &'static aead::Algorithm { 66 | match self { 67 | Cipher::AES128GCM => &aead::AES_128_GCM, 68 | Cipher::AES256GCM => &aead::AES_256_GCM, 69 | Cipher::CHACHA20POLY1305 => &aead::CHACHA20_POLY1305, 70 | } 71 | } 72 | } 73 | 74 | impl Default for Cipher { 75 | fn default() -> Cipher { 76 | Cipher::AES128GCM 77 | } 78 | } 79 | 80 | const INFO_KEY: &str = "hello kelsi"; 81 | 82 | #[derive(Debug)] 83 | pub struct KeyPair { 84 | value: Vec, 85 | } 86 | 87 | impl KeyPair { 88 | pub fn generate(secret: &[u8], cipher: Cipher) -> Result { 89 | let len = cipher.key_len() * 2; 90 | 91 | let salt = hmac::SigningKey::generate(&digest::SHA256, &rand::SystemRandom::new()) 92 | .map_err(|_| Error::GenKey)?; 93 | 94 | let mut out = Vec::with_capacity(len); 95 | 96 | // not need init it 97 | unsafe { 98 | out.set_len(len); 99 | } 100 | hkdf::extract_and_expand(&salt, secret, INFO_KEY.as_bytes(), &mut out); 101 | Ok(KeyPair { value: out }) 102 | } 103 | 104 | pub fn from(slice: &[u8]) -> KeyPair { 105 | let mut key = Vec::with_capacity(slice.len()); 106 | 107 | key.extend_from_slice(slice); 108 | KeyPair { value: key } 109 | } 110 | 111 | pub fn len(&self) -> usize { 112 | self.value.len() 113 | } 114 | 115 | pub fn split(&self) -> (&[u8], &[u8]) { 116 | let len = self.value.len() / 2; 117 | (&self.value[..len], &self.value[len..]) 118 | } 119 | } 120 | 121 | impl AsRef<[u8]> for KeyPair { 122 | fn as_ref(&self) -> &[u8] { 123 | self.value.as_ref() 124 | } 125 | } 126 | 127 | #[allow(dead_code)] 128 | pub struct Crypto { 129 | cipher: Cipher, 130 | aead: &'static aead::Algorithm, 131 | 132 | tag_len: usize, 133 | key_len: usize, 134 | nonce_len: usize, 135 | 136 | open_key: aead::OpeningKey, 137 | open_nonce: Vec, 138 | 139 | seal_key: aead::SealingKey, 140 | seal_nonce: Vec, 141 | } 142 | 143 | impl Crypto { 144 | pub fn new(cipher: Cipher, open_key: &[u8], seal_key: &[u8]) -> Result { 145 | let aead = cipher.algorithm(); 146 | let key_len = aead.key_len(); 147 | 148 | if open_key.len() != key_len { 149 | return Err(Error::KeyLenNotMatch(key_len)); 150 | } 151 | let open_key = aead::OpeningKey::new(aead, open_key).map_err(|_| Error::OpenKey)?; 152 | 153 | if seal_key.len() != key_len { 154 | return Err(Error::KeyLenNotMatch(key_len)); 155 | } 156 | let seal_key = aead::SealingKey::new(aead, seal_key).map_err(|_| Error::SealKey)?; 157 | 158 | let nonce_len = aead.nonce_len(); 159 | 160 | Ok(Crypto { 161 | cipher, 162 | aead, 163 | 164 | tag_len: aead.tag_len(), 165 | key_len: aead.key_len(), 166 | nonce_len: aead.nonce_len(), 167 | 168 | open_key, 169 | open_nonce: vec![0u8; nonce_len], 170 | seal_key, 171 | seal_nonce: vec![0u8; nonce_len], 172 | }) 173 | } 174 | 175 | #[inline] 176 | pub fn tag_len(&self) -> usize { 177 | self.tag_len 178 | } 179 | 180 | pub fn encrypt(&mut self, inout: &mut [u8], in_len: usize) -> Result { 181 | let out_len = in_len + self.tag_len; 182 | if inout.len() < out_len { 183 | return Err(Error::SealBufferTooSmall(out_len)); 184 | } 185 | 186 | match aead::seal_in_place( 187 | &self.seal_key, 188 | &self.seal_nonce, 189 | &[], 190 | &mut inout[..out_len], 191 | self.tag_len, 192 | ) { 193 | Ok(outlen) => debug_assert_eq!(out_len, outlen), 194 | Err(_) => return Err(Error::Seal), 195 | }; 196 | 197 | incr_nonce(&mut self.seal_nonce); 198 | 199 | Ok(out_len) 200 | } 201 | 202 | #[inline] 203 | pub fn decrypt(&mut self, inout: &mut [u8]) -> Result { 204 | match aead::open_in_place(&self.open_key, &self.open_nonce, &[], 0, inout) { 205 | Ok(buf) => { 206 | incr_nonce(&mut self.open_nonce); 207 | Ok(buf.len()) 208 | } 209 | Err(_) => Err(Error::Open), 210 | } 211 | } 212 | } 213 | 214 | fn incr_nonce(nonce: &mut [u8]) { 215 | for byte in nonce.iter_mut() { 216 | let (sum, overflow) = (*byte).overflowing_add(1); 217 | *byte = sum; 218 | if !overflow { 219 | break; 220 | } 221 | } 222 | } 223 | 224 | impl fmt::Display for Error { 225 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 226 | match *self { 227 | Error::CipherNotSupport => write!(fmt, "cipher not support"), 228 | Error::GenKey => write!(fmt, "generate key error"), 229 | Error::KeyLenNotMatch(need) => write!(fmt, "key length not match, need {}", need), 230 | Error::OpenKey => write!(fmt, "ring open key error"), 231 | Error::SealKey => write!(fmt, "ring seal key error"), 232 | Error::SealBufferTooSmall(need) => { 233 | write!(fmt, "seal inout buffer too small, need {}", need) 234 | } 235 | Error::Open => write!(fmt, "ring open error"), 236 | Error::Seal => write!(fmt, "ring seal error"), 237 | } 238 | } 239 | } 240 | 241 | impl From for io::Error { 242 | fn from(err: Error) -> io::Error { 243 | io::Error::new(io::ErrorKind::Other, format!("{}", err)) 244 | } 245 | } 246 | 247 | impl fmt::Display for Cipher { 248 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 249 | match *self { 250 | Cipher::AES128GCM => write!(fmt, "AES-128-GCM"), 251 | Cipher::AES256GCM => write!(fmt, "AES-256-GCM"), 252 | Cipher::CHACHA20POLY1305 => write!(fmt, "CHACHA20-POLY1305"), 253 | } 254 | } 255 | } 256 | 257 | #[cfg(test)] 258 | mod test { 259 | 260 | #[test] 261 | fn test_incr_nonce() { 262 | let mut nonce = [0u8; 4]; 263 | for i in 1..1024 { 264 | super::incr_nonce(&mut nonce); 265 | let x = (nonce[0] as usize) 266 | + ((nonce[1] as usize) << 8) 267 | + ((nonce[2] as usize) << 16) 268 | + ((nonce[3] as usize) << 24); 269 | assert_eq!(x, i); 270 | } 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | pub mod client; 5 | pub mod server; 6 | 7 | mod config; 8 | mod crypto; 9 | mod socks5; 10 | mod transfer; 11 | mod util; 12 | 13 | mod v3 { 14 | use ring::digest::{Algorithm, SHA256, SHA256_OUTPUT_LEN}; 15 | 16 | use crate::crypto::Cipher; 17 | 18 | pub const VERSION: u8 = 0x03; 19 | 20 | pub const MAX_BUFFER_SIZE: usize = 32 * 1024; 21 | 22 | pub const DATA_LEN_LEN: usize = 2; 23 | pub const MAX_PADDING_LEN: usize = 255; 24 | 25 | pub static DEFAULT_DIGEST: &'static Algorithm = &SHA256; 26 | pub const DEFAULT_DIGEST_LEN: usize = SHA256_OUTPUT_LEN; 27 | 28 | pub const HANDSHAKE_CIPHER: Cipher = Cipher::AES256GCM; 29 | 30 | pub const SERVER_RESP_SUCCEED: u8 = 0x00; 31 | pub const SERVER_RESP_CIPHER_ERROR: u8 = 0x01; 32 | pub const SERVER_RESP_ERROR: u8 = 0x02; 33 | pub const SERVER_RESP_REMOTE_FAILED: u8 = 0x03; 34 | } 35 | 36 | pub use crate::config::{ClientConfig, ServerConfig}; 37 | pub use crate::util::{expand_tilde_path, init_logger}; 38 | -------------------------------------------------------------------------------- /src/server.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::io; 3 | use std::mem; 4 | use std::net::SocketAddr; 5 | use std::sync::Arc; 6 | 7 | use futures::future::try_join; 8 | use rand; 9 | use tokio; 10 | use tokio::net::{TcpListener, TcpStream}; 11 | use tokio::prelude::*; 12 | use tokio::time::{timeout, Duration}; 13 | 14 | use super::config::{Digest, ServerConfig, User}; 15 | use super::crypto::{Cipher, Crypto, KeyPair}; 16 | use super::socks5; 17 | use super::transfer::{self, Stat}; 18 | use super::util::{self, RandomBytes}; 19 | use super::v3; 20 | 21 | pub async fn serve(config: ServerConfig) -> io::Result<()> { 22 | let mut listener = TcpListener::bind(&config.listen) 23 | .await 24 | .map_err(|e| other(&format!("listen on {}, {}", config.listen, e)))?; 25 | 26 | info!("Listening on {}", config.listen); 27 | let users = Arc::new(config.users); 28 | 29 | loop { 30 | match listener.accept().await { 31 | Ok((client, client_addr)) => { 32 | let users = users.clone(); 33 | 34 | tokio::spawn(async move { 35 | match process(client, client_addr, users).await { 36 | Ok((user, req, stat)) => info!( 37 | "{} - ({}) request {} success, {}", 38 | client_addr, user.name, req, stat, 39 | ), 40 | Err(e) => error!("{} - failed by {}", client_addr, e), 41 | } 42 | }); 43 | } 44 | Err(e) => error!("accepting socket, {}", e), 45 | } 46 | } 47 | } 48 | 49 | async fn process( 50 | mut client: TcpStream, 51 | client_addr: SocketAddr, 52 | users: Arc>, 53 | ) -> io::Result<(User, String, Stat)> { 54 | // timeout random [10, 40) 55 | let secs = u64::from(rand::random::() % 30 + 10); 56 | 57 | let mut handshake = Handshake::new(&mut client, client_addr, users); 58 | let mut ctx = timeout(Duration::from_secs(secs), handshake.hand()).await??; 59 | 60 | let (client_key, server_key) = ctx.key_pair.split(); 61 | let (cr, cw) = client.split(); 62 | let (rr, rw) = ctx.remote.split(); 63 | 64 | let en = transfer::encrypt(rr, cw, ctx.cipher, server_key); 65 | let de = transfer::decrypt(cr, rw, ctx.cipher, client_key); 66 | 67 | let stat = try_join(en, de).await?; 68 | Ok((ctx.user, ctx.addr, Stat::new(stat.0, stat.1))) 69 | } 70 | 71 | struct Context { 72 | user: User, 73 | addr: String, 74 | cipher: Cipher, 75 | key_pair: KeyPair, 76 | remote: TcpStream, 77 | } 78 | 79 | struct Request { 80 | addr: String, 81 | user: Option, 82 | crypto: Option, 83 | 84 | remote: Option, 85 | cipher: Option, 86 | key_pair: Option, 87 | } 88 | 89 | impl Request { 90 | pub fn none() -> Request { 91 | Request { 92 | addr: String::new(), 93 | user: None, 94 | crypto: None, 95 | remote: None, 96 | cipher: None, 97 | key_pair: None, 98 | } 99 | } 100 | } 101 | 102 | struct Handshake<'a> { 103 | client: &'a mut TcpStream, 104 | client_addr: SocketAddr, 105 | users: Arc>, 106 | 107 | req: Request, 108 | } 109 | 110 | impl<'a> Handshake<'a> { 111 | fn new( 112 | client: &'a mut TcpStream, 113 | client_addr: SocketAddr, 114 | users: Arc>, 115 | ) -> Handshake { 116 | Handshake { 117 | client, 118 | client_addr, 119 | users, 120 | req: Request::none(), 121 | } 122 | } 123 | 124 | // +-----+---------+------+ 125 | // | LEN | LEN TAG | USER | 126 | // +-----+---------+------+ 127 | // | 2 | Var. | 32 | 128 | // +-----+---------+------+ 129 | fn parse_header(&mut self, header: &mut [u8]) -> io::Result { 130 | let user = { 131 | let end = header.len(); 132 | let start = end - v3::DEFAULT_DIGEST_LEN; 133 | let user = &header[start..end]; 134 | self.users 135 | .get(user) 136 | .ok_or_else(|| other(&format!("user ({}) not exists", util::to_hex(user))))? 137 | }; 138 | 139 | let mut crypto = Crypto::new( 140 | v3::HANDSHAKE_CIPHER, 141 | user.password.as_ref(), 142 | user.password.as_ref(), 143 | )?; 144 | 145 | let size = crypto.decrypt(&mut header[..v3::DATA_LEN_LEN + crypto.tag_len()])?; 146 | debug_assert_eq!(size, v3::DATA_LEN_LEN); 147 | 148 | let len = ((header[0] as usize) << 8) + (header[1] as usize); 149 | if len <= v3::DEFAULT_DIGEST_LEN + crypto.tag_len() { 150 | return Err(other(&format!( 151 | "({}) request data length too small", 152 | user.name 153 | ))); 154 | } 155 | 156 | self.req.user = Some(user.clone()); 157 | self.req.crypto = Some(crypto); 158 | 159 | Ok(len - v3::DEFAULT_DIGEST_LEN) 160 | } 161 | 162 | // +---------+-----+------+-----+----------+----------+ 163 | // | PADDING | VER | CTYP |ATYP | DST.ADDR | DST.PORT | 164 | // +---------+-----+------+----------------+----------+ 165 | // | Var. | 1 | 1 | 1 | Var. | 2 | 166 | // +---------+-----+------+-----+----------+----------+ 167 | fn parse_data(&mut self, data: &mut [u8]) -> io::Result> { 168 | let crypto = self.req.crypto.as_mut().unwrap(); 169 | let user = self.req.user.as_ref().unwrap(); 170 | 171 | let data_len = crypto.decrypt(data)?; 172 | 173 | let padding_len = (data[0] as usize) + 1; 174 | if data_len < padding_len + 1 + 1 + 1 + 1 { 175 | return Err(other(&format!( 176 | "({}) request data length not match", 177 | user.name 178 | ))); 179 | } 180 | 181 | let buf = &data[padding_len..data_len]; 182 | if buf[0] != v3::VERSION { 183 | return Err(other(&format!( 184 | "({}) request version not match, need {}, but {}", 185 | user.name, 186 | v3::VERSION, 187 | buf[0] 188 | ))); 189 | } 190 | 191 | let cipher_no = buf[1]; 192 | 193 | let addr_start = padding_len + 1 + 1; 194 | let addr_len = match buf[2] { 195 | // 4 bytes ipv4 and 2 bytes port, and already read more 1 bytes 196 | socks5::ADDR_TYPE_IPV4 => 4 + 2 + 1, 197 | // 16 bytes ipv6 and 2 bytes port 198 | socks5::ADDR_TYPE_IPV6 => 16 + 2 + 1, 199 | socks5::ADDR_TYPE_DOMAIN_NAME => 1 + (buf[3] as usize) + 2 + 1, 200 | n => { 201 | return Err(other(&format!( 202 | "({}) request atyp {} unknown", 203 | user.name, n 204 | ))) 205 | } 206 | }; 207 | 208 | if data_len != addr_start + addr_len { 209 | return Err(other(&format!( 210 | "({}) request data length not match", 211 | user.name 212 | ))); 213 | } 214 | 215 | let addr = socks5::ReqAddr::new(&data[addr_start..data_len]) 216 | .get() 217 | .map_err(|err| other(&format!("({}) request req addr error, {}", user.name, err)))?; 218 | 219 | self.req.addr = format!("{}:{}", addr.0, addr.1); 220 | 221 | match Cipher::from_no(cipher_no) { 222 | Ok(cipher) => { 223 | self.req.cipher = Some(cipher); 224 | info!( 225 | "{} - ({}) request {}, cipher {}", 226 | self.client_addr, user.name, self.req.addr, cipher, 227 | ); 228 | Ok(Some(addr)) 229 | } 230 | Err(_) => { 231 | info!( 232 | "{} - ({}) request {}, cipher '{}' not support", 233 | self.client_addr, user.name, self.req.addr, cipher_no, 234 | ); 235 | Ok(None) 236 | } 237 | } 238 | } 239 | 240 | // +---------------+--------------------------------+ 241 | // | Header | Data | 242 | // +-----+---------+---------+-------+-------+------+ 243 | // | LEN | LEN TAG | PADDING | RESP | EKEY | DKEY | 244 | // +-----+---------+---------+-------+-------+------+ 245 | // | 2 | Var. | Var. | 1 | Var. | Var. | 246 | // +-----+---------+---------+-------+-------+------+ 247 | fn response(&mut self, buf: &mut [u8]) -> io::Result { 248 | let user = self.req.user.as_ref().unwrap(); 249 | let crypto = self.req.crypto.as_mut().unwrap(); 250 | 251 | let tag_len = crypto.tag_len(); 252 | let header_len = v3::DATA_LEN_LEN + tag_len; 253 | let mut data_len = 0; 254 | 255 | // PADDING 256 | let random_bytes = RandomBytes::new()?; 257 | let bytes = random_bytes.get(); 258 | let mut end = header_len + bytes.len(); 259 | 260 | (&mut buf[header_len..end]).copy_from_slice(bytes); 261 | data_len += bytes.len(); 262 | 263 | let mut resp = v3::SERVER_RESP_SUCCEED; 264 | 265 | self.req.key_pair = match self.req.cipher { 266 | Some(cipher) => match KeyPair::generate(user.password.as_ref(), cipher) { 267 | Ok(key) => Some(key), 268 | Err(e) => { 269 | error!( 270 | "{} - ({}) request generate key, {}", 271 | self.client_addr, user.name, e, 272 | ); 273 | resp = v3::SERVER_RESP_ERROR; 274 | None 275 | } 276 | }, 277 | None => { 278 | resp = v3::SERVER_RESP_CIPHER_ERROR; 279 | None 280 | } 281 | }; 282 | if self.req.key_pair.is_some() && self.req.remote.is_none() { 283 | resp = v3::SERVER_RESP_REMOTE_FAILED; 284 | } 285 | 286 | buf[end] = resp; 287 | end += 1; 288 | data_len += 1; 289 | 290 | // KEY 291 | if resp == v3::SERVER_RESP_SUCCEED { 292 | let key = self.req.key_pair.as_ref().unwrap(); 293 | (&mut buf[end..end + key.len()]).copy_from_slice(key.as_ref()); 294 | data_len += key.len(); 295 | } 296 | 297 | // Big Endian 298 | let data_len = data_len + tag_len; 299 | buf[0] = (data_len >> 8) as u8; 300 | buf[1] = data_len as u8; 301 | crypto.encrypt(&mut buf[..header_len], 2)?; 302 | crypto.encrypt( 303 | &mut buf[header_len..header_len + data_len], 304 | data_len - tag_len, 305 | )?; 306 | 307 | Ok(header_len + data_len) 308 | } 309 | 310 | async fn hand(&mut self) -> io::Result { 311 | let mut buf = [0u8; v3::MAX_BUFFER_SIZE]; 312 | let header_len = v3::DATA_LEN_LEN + v3::HANDSHAKE_CIPHER.tag_len() + v3::DEFAULT_DIGEST_LEN; 313 | 314 | self.client.read_exact(&mut buf[..header_len]).await?; 315 | let need = self.parse_header(&mut buf[..header_len])?; 316 | self.client.read_exact(&mut buf[..need]).await?; 317 | 318 | if let Some(addr) = self.parse_data(&mut buf[..need])? { 319 | match TcpStream::connect((&*addr.0, addr.1)).await { 320 | Ok(conn) => self.req.remote = Some(conn), 321 | Err(e) => { 322 | let user = self.req.user.as_ref().unwrap(); 323 | error!( 324 | "{} - ({}) connect remote {}, {}", 325 | self.client_addr, user.name, self.req.addr, e, 326 | ) 327 | } 328 | } 329 | } 330 | 331 | let len = self.response(&mut buf)?; 332 | self.client.write_all(&buf[..len]).await?; 333 | 334 | let req = mem::replace(&mut self.req, Request::none()); 335 | let user = req.user.unwrap(); 336 | let addr = req.addr; 337 | 338 | let remote = match req.remote { 339 | Some(conn) => conn, 340 | None => return Err(other(&format!("({}) request {} failed", user.name, addr))), 341 | }; 342 | Ok(Context { 343 | user, 344 | addr, 345 | cipher: req.cipher.unwrap(), 346 | key_pair: req.key_pair.unwrap(), 347 | remote, 348 | }) 349 | } 350 | } 351 | 352 | fn other(desc: &str) -> io::Error { 353 | io::Error::new(io::ErrorKind::Other, desc) 354 | } 355 | -------------------------------------------------------------------------------- /src/socks5.rs: -------------------------------------------------------------------------------- 1 | //! Socks5 protocol definition ([RFC1928](https://tools.ietf.org/rfc/rfc1928.txt)) 2 | 3 | use std::io; 4 | use std::net::{self, SocketAddr}; 5 | use std::str; 6 | 7 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 8 | use tokio::net::TcpStream; 9 | 10 | const VERSION: u8 = 0x05; 11 | const AUTH_METHOD_NONE: u8 = 0x00; 12 | const CMD_TCP_CONNECT: u8 = 0x01; 13 | 14 | pub const ADDR_TYPE_IPV4: u8 = 0x01; 15 | pub const ADDR_TYPE_IPV6: u8 = 0x04; 16 | pub const ADDR_TYPE_DOMAIN_NAME: u8 = 0x03; 17 | 18 | const REPLY_SUCCEEDED: u8 = 0x00; 19 | const REPLY_GENERAL_FAILURE: u8 = 0x01; 20 | const REPLY_CONNECTION_REFUSED: u8 = 0x05; 21 | 22 | // +----+-----+-------+------+----------+----------+ 23 | // |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | 24 | // +----+-----+-------+------+----------+----------+ 25 | // | 1 | 1 | X'00' | 1 | Variable | 2 | 26 | // +----+-----+-------+------+----------+----------+ 27 | const MAX_REQ_LEN: usize = 1 + 1 + 1 + 1 + 1 + 255 + 2; 28 | 29 | pub struct ReqAddr { 30 | len: usize, 31 | bytes: [u8; MAX_REQ_LEN], 32 | } 33 | 34 | impl ReqAddr { 35 | pub fn new(bytes: &[u8]) -> ReqAddr { 36 | let len = bytes.len(); 37 | assert!(len < MAX_REQ_LEN); 38 | 39 | let mut buf = [0u8; MAX_REQ_LEN]; 40 | buf[..len].copy_from_slice(bytes); 41 | ReqAddr { len, bytes: buf } 42 | } 43 | 44 | #[inline] 45 | pub fn get_bytes(&self) -> &[u8] { 46 | &self.bytes[..self.len] 47 | } 48 | 49 | pub fn get(&self) -> io::Result<(String, u16)> { 50 | let atyp = self.bytes[0]; 51 | let buf = &self.bytes[1..self.len]; 52 | 53 | match atyp { 54 | ADDR_TYPE_IPV4 => { 55 | let addr = net::Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3]); 56 | let port = (u16::from(buf[4]) << 8) | u16::from(buf[5]); 57 | Ok((format!("{}", addr), port)) 58 | } 59 | ADDR_TYPE_IPV6 => { 60 | let a = (u16::from(buf[0]) << 8) | u16::from(buf[1]); 61 | let b = (u16::from(buf[2]) << 8) | u16::from(buf[3]); 62 | let c = (u16::from(buf[4]) << 8) | u16::from(buf[5]); 63 | let d = (u16::from(buf[6]) << 8) | u16::from(buf[7]); 64 | let e = (u16::from(buf[8]) << 8) | u16::from(buf[9]); 65 | let f = (u16::from(buf[10]) << 8) | u16::from(buf[11]); 66 | let g = (u16::from(buf[12]) << 8) | u16::from(buf[13]); 67 | let h = (u16::from(buf[14]) << 8) | u16::from(buf[15]); 68 | let addr = net::Ipv6Addr::new(a, b, c, d, e, f, g, h); 69 | let port = (u16::from(buf[16]) << 8) | u16::from(buf[17]); 70 | 71 | Ok((format!("{}", addr), port)) 72 | } 73 | ADDR_TYPE_DOMAIN_NAME => { 74 | let len = buf[0] as usize; 75 | let domain = String::from_utf8(buf[1..len + 1].to_vec()) 76 | .map_err(|e| other(&format!("domain not valid utf-8, {}", e)))?; 77 | let pos = buf.len() - 2; 78 | let port = (u16::from(buf[pos]) << 8) | u16::from(buf[pos + 1]); 79 | Ok((domain, port)) 80 | } 81 | _ => unreachable!(), 82 | } 83 | } 84 | } 85 | 86 | // BND.ADDR use IP V4 or V6, 16 is ipv6 bytes 87 | pub const REPLY_LEN: usize = 1 + 1 + 1 + 1 + 16 + 2; 88 | 89 | pub struct Reply { 90 | len: usize, 91 | buffer: [u8; REPLY_LEN], 92 | } 93 | 94 | impl Reply { 95 | pub fn new(addr: SocketAddr) -> Reply { 96 | let mut buf = [0u8; REPLY_LEN]; 97 | 98 | // VER - protocol version 99 | buf[0] = VERSION; 100 | 101 | // RSV - reserved 102 | buf[2] = 0; 103 | 104 | let pos = match addr { 105 | SocketAddr::V4(ref a) => { 106 | buf[3] = ADDR_TYPE_IPV4; 107 | buf[4..8].copy_from_slice(&a.ip().octets()[..]); 108 | 8 109 | } 110 | SocketAddr::V6(ref a) => { 111 | buf[3] = ADDR_TYPE_IPV6; 112 | let mut pos = 4; 113 | for &segment in &a.ip().segments() { 114 | buf[pos] = (segment >> 8) as u8; 115 | buf[pos + 1] = segment as u8; 116 | pos += 2; 117 | } 118 | pos 119 | } 120 | }; 121 | buf[pos] = (addr.port() >> 8) as u8; 122 | buf[pos + 1] = addr.port() as u8; 123 | 124 | Reply { 125 | len: pos + 2, 126 | buffer: buf, 127 | } 128 | } 129 | 130 | fn get(&self, rtype: u8, out: &mut [u8]) -> usize { 131 | assert!(out.len() >= self.len); 132 | out[..self.len].copy_from_slice(&self.buffer[..self.len]); 133 | // REP 134 | out[1] = rtype; 135 | 136 | self.len 137 | } 138 | } 139 | 140 | pub async fn handshake( 141 | client: &mut TcpStream, 142 | client_addr: SocketAddr, 143 | remote_addr: SocketAddr, 144 | reply: &Reply, 145 | ) -> io::Result<(TcpStream, ReqAddr)> { 146 | let mut buf = [0u8; MAX_REQ_LEN]; 147 | 148 | // auth version 149 | client.read_exact(&mut buf[..1]).await?; 150 | if buf[0] != VERSION { 151 | return Err(other("only support socks version 5")); 152 | } 153 | 154 | // auth n method 155 | client.read_exact(&mut buf[..1]).await?; 156 | let n_method = buf[0] as usize; 157 | 158 | // methods 159 | client.read_exact(&mut buf[..n_method]).await?; 160 | if !(&buf[..n_method]).contains(&AUTH_METHOD_NONE) { 161 | return Err(other("no supported auth method given")); 162 | } 163 | 164 | // resp auth 165 | buf[0] = VERSION; 166 | buf[1] = AUTH_METHOD_NONE; 167 | client.write_all(&buf[..2]).await?; 168 | 169 | // req version 170 | client.read_exact(&mut buf[..1]).await?; 171 | if buf[0] != VERSION { 172 | return Err(other("only support socks version 5")); 173 | } 174 | 175 | // CMD + RSV + ATYP + 1 176 | let n = 4usize; 177 | client.read_exact(&mut buf[..n]).await?; 178 | if buf[0] != CMD_TCP_CONNECT { 179 | return Err(other("only support tcp connect command")); 180 | } 181 | let need = match buf[2] { 182 | // 4 bytes ipv4 and 2 bytes port, and already read more 1 bytes 183 | ADDR_TYPE_IPV4 => 4 + 2 - 1, 184 | // 16 bytes ipv6 and 2 bytes port 185 | ADDR_TYPE_IPV6 => 16 + 2 - 1, 186 | ADDR_TYPE_DOMAIN_NAME => (buf[3] as usize) + 2, 187 | n => { 188 | return Err(other(&format!("unknown ATYP received: {}", n))); 189 | } 190 | }; 191 | 192 | client.read_exact(&mut buf[n..n + need]).await?; 193 | let req_addr = ReqAddr::new(&buf[2..n + need]); 194 | let addr = req_addr.get()?; 195 | info!("{} - request {}:{}", client_addr, addr.0, addr.1); 196 | 197 | let remote = TcpStream::connect(&remote_addr).await; 198 | 199 | let rtype = if let Err(ref e) = remote { 200 | if e.kind() == io::ErrorKind::ConnectionRefused { 201 | REPLY_CONNECTION_REFUSED 202 | } else { 203 | REPLY_GENERAL_FAILURE 204 | } 205 | } else { 206 | REPLY_SUCCEEDED 207 | }; 208 | 209 | let reply_len = reply.get(rtype, &mut buf[..]); 210 | client.write_all(&mut buf[..reply_len]).await?; 211 | 212 | match remote { 213 | Ok(conn) => Ok((conn, req_addr)), 214 | Err(e) => Err(other(&format!("connect remote {}, {}", remote_addr, e))), 215 | } 216 | } 217 | 218 | #[inline] 219 | fn other(desc: &str) -> io::Error { 220 | io::Error::new(io::ErrorKind::Other, desc) 221 | } 222 | -------------------------------------------------------------------------------- /src/transfer.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io; 3 | use std::marker::Unpin; 4 | 5 | use tokio::io::{AsyncReadExt, AsyncWriteExt}; 6 | 7 | use super::crypto::{Cipher, Crypto}; 8 | use super::v3; 9 | 10 | pub async fn encrypt( 11 | mut reader: In, 12 | mut writer: Out, 13 | cipher: Cipher, 14 | key: &[u8], 15 | ) -> io::Result<(usize, usize)> { 16 | let mut nread = 0; 17 | let mut nwrite = 0; 18 | let mut crypto = Crypto::new(cipher, key, key)?; 19 | 20 | let crypto_tag_len = crypto.tag_len(); 21 | 22 | let mut buf = [0u8; v3::MAX_BUFFER_SIZE]; 23 | let start = v3::DATA_LEN_LEN + crypto_tag_len; 24 | let end = v3::MAX_BUFFER_SIZE - crypto_tag_len; 25 | 26 | loop { 27 | let n = reader.read(&mut buf[start..end]).await?; 28 | // read eof 29 | if n == 0 { 30 | writer.shutdown().await?; 31 | return Ok((nread, nwrite)); 32 | } 33 | nread += n; 34 | 35 | // Header 36 | let data_len = n + crypto_tag_len; 37 | // Big Endian 38 | buf[0] = (data_len >> 8) as u8; 39 | buf[1] = data_len as u8; 40 | crypto.encrypt(&mut buf[..start], 2)?; 41 | 42 | // Data 43 | let data_end = start + n + crypto_tag_len; 44 | crypto.encrypt(&mut buf[start..data_end], n)?; 45 | 46 | writer.write_all(&buf[..data_end]).await?; 47 | nwrite += data_end; 48 | } 49 | } 50 | 51 | pub async fn decrypt( 52 | mut reader: In, 53 | mut writer: Out, 54 | cipher: Cipher, 55 | key: &[u8], 56 | ) -> io::Result<(usize, usize)> { 57 | let mut nread = 0; 58 | let mut nwrite = 0; 59 | let mut crypto = Crypto::new(cipher, key, key)?; 60 | 61 | let crypto_tag_len = crypto.tag_len(); 62 | 63 | let mut buf = [0u8; v3::MAX_BUFFER_SIZE]; 64 | let header_len = v3::DATA_LEN_LEN + crypto_tag_len; 65 | 66 | loop { 67 | let n = reader.read(&mut buf[..header_len]).await?; 68 | // read eof 69 | if n == 0 { 70 | writer.shutdown().await?; 71 | return Ok((nread, nwrite)); 72 | } 73 | nread += n; 74 | 75 | // try read all header 76 | if n != header_len { 77 | reader.read_exact(&mut buf[n..header_len]).await?; 78 | nread += header_len - n; 79 | } 80 | 81 | let len = crypto.decrypt(&mut buf[..header_len])?; 82 | assert_eq!(len, v3::DATA_LEN_LEN); 83 | let data_len = ((buf[0] as usize) << 8) + (buf[1] as usize); 84 | 85 | // read data 86 | reader.read_exact(&mut buf[..data_len]).await?; 87 | nread += data_len; 88 | let len = crypto.decrypt(&mut buf[..data_len])?; 89 | 90 | writer.write_all(&buf[..len]).await?; 91 | nwrite += len; 92 | } 93 | } 94 | 95 | #[derive(Copy, Clone, Debug)] 96 | pub struct Stat { 97 | enc_read: usize, 98 | enc_write: usize, 99 | dec_read: usize, 100 | dec_write: usize, 101 | } 102 | 103 | impl Stat { 104 | pub fn new(enc: (usize, usize), dec: (usize, usize)) -> Stat { 105 | Stat { 106 | enc_read: enc.0, 107 | enc_write: enc.1, 108 | dec_read: dec.0, 109 | dec_write: dec.1, 110 | } 111 | } 112 | } 113 | 114 | impl fmt::Display for Stat { 115 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 116 | write!( 117 | f, 118 | "recv: {}/{} send: {}/{}", 119 | self.dec_write, self.dec_read, self.enc_read, self.enc_write 120 | ) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::env; 3 | use std::fmt; 4 | use std::io::{self, Write}; 5 | use std::path::MAIN_SEPARATOR; 6 | 7 | use ansi_term::Color; 8 | use dirs; 9 | use env_logger::{Builder, Formatter}; 10 | use log::{Level, LevelFilter, Record}; 11 | use ring::rand::{SecureRandom, SystemRandom}; 12 | use time; 13 | 14 | use super::v3::MAX_PADDING_LEN; 15 | 16 | struct ColorLevel(Level); 17 | 18 | impl fmt::Display for ColorLevel { 19 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 20 | match self.0 { 21 | Level::Trace => Color::Purple.paint("TRACE"), 22 | Level::Debug => Color::Blue.paint("DEBUG"), 23 | Level::Info => Color::Green.paint("INFO "), 24 | Level::Warn => Color::Yellow.paint("WARN "), 25 | Level::Error => Color::Red.paint("ERROR"), 26 | } 27 | .fmt(f) 28 | } 29 | } 30 | 31 | pub fn init_logger() { 32 | let format = |buf: &mut Formatter, record: &Record| { 33 | let now = time::now(); 34 | let ms = now.tm_nsec / 1000 / 1000; 35 | let t = time::strftime("%Y-%m-%d %T", &now).unwrap(); 36 | writeln!( 37 | buf, 38 | "{}.{:03} [{}] {}", 39 | t, 40 | ms, 41 | ColorLevel(record.level()), 42 | record.args() 43 | ) 44 | }; 45 | 46 | let mut builder = Builder::new(); 47 | builder.format(format).filter(None, LevelFilter::Info); 48 | 49 | if env::var("RUST_LOG").is_ok() { 50 | builder.parse(&env::var("RUST_LOG").unwrap()); 51 | } 52 | 53 | if env::var("FAKIO_LOG").is_ok() { 54 | builder.parse(&env::var("FAKIO_LOG").unwrap()); 55 | } 56 | 57 | builder.init(); 58 | } 59 | 60 | /// `MAX_PADDING_LEN 255` 61 | /// 62 | /// +-------+----------+ 63 | /// | len | bytes | 64 | /// +-------+----------+ 65 | /// | 1bytes| [0, 255] | 66 | /// +-------+----------+ 67 | pub struct RandomBytes { 68 | len: usize, 69 | bytes: [u8; 1 + MAX_PADDING_LEN], 70 | } 71 | 72 | impl RandomBytes { 73 | pub fn new() -> io::Result { 74 | let mut padding = [0u8; 1 + MAX_PADDING_LEN]; 75 | let rand = SystemRandom::new(); 76 | 77 | // 1. rand len 78 | rand.fill(&mut padding[..1]) 79 | .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("rand failed by {}", e)))?; 80 | 81 | // 2. rand data 82 | let len = padding[0] as usize; 83 | rand.fill(&mut padding[1..len + 1]) 84 | .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("rand failed by {}", e)))?; 85 | Ok(RandomBytes { 86 | len, 87 | bytes: padding, 88 | }) 89 | } 90 | 91 | #[inline] 92 | pub fn get(&self) -> &[u8] { 93 | &self.bytes[..self.len + 1] 94 | } 95 | } 96 | 97 | static CHARS: &'static [u8] = b"0123456789abcdef"; 98 | 99 | pub fn to_hex(slice: &[u8]) -> String { 100 | let mut v = Vec::with_capacity(slice.len() * 2); 101 | for &byte in slice.iter() { 102 | v.push(CHARS[(byte >> 4) as usize]); 103 | v.push(CHARS[(byte & 0xf) as usize]); 104 | } 105 | 106 | unsafe { String::from_utf8_unchecked(v) } 107 | } 108 | 109 | /// Expand path like ~/xxx 110 | pub fn expand_tilde_path(path: &str) -> Cow { 111 | if !path.starts_with('~') { 112 | return path.into(); 113 | } 114 | 115 | let path_after_tilde = &path[1..]; 116 | 117 | // on support windows `\` 118 | if path_after_tilde.is_empty() || path_after_tilde.starts_with(MAIN_SEPARATOR) { 119 | if let Some(hd) = dirs::home_dir() { 120 | let result = format!("{}{}", hd.display(), path_after_tilde); 121 | result.into() 122 | } else { 123 | // home dir is not available 124 | path.into() 125 | } 126 | } else { 127 | // we cannot handle `~otheruser/` paths yet 128 | path.into() 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod test { 134 | use std::io::ErrorKind; 135 | 136 | #[test] 137 | fn test_random_bytes() { 138 | match super::RandomBytes::new() { 139 | Ok(r) => { 140 | let bytes = r.get(); 141 | let size = bytes[0] as usize; 142 | assert_eq!(size + 1, bytes.len()); 143 | } 144 | Err(e) => assert_eq!(e.kind(), ErrorKind::Other), 145 | } 146 | } 147 | 148 | #[test] 149 | fn test_expand_tilde_path() { 150 | let mut home = match dirs::home_dir() { 151 | Some(hd) => hd, 152 | None => return, 153 | }; 154 | 155 | assert_eq!(format!("{}", home.display()), super::expand_tilde_path("~")); 156 | assert_eq!("~rick", super::expand_tilde_path("~rick")); 157 | 158 | if cfg!(target_os = "windows") { 159 | home.push("rick"); 160 | assert_eq!( 161 | format!("{}", home.display()), 162 | super::expand_tilde_path("~\\rick") 163 | ); 164 | home.push("morty.txt"); 165 | assert_eq!( 166 | format!("{}", home.display()), 167 | super::expand_tilde_path("~\\rick\\morty.txt") 168 | ); 169 | assert_eq!("C:\\home", super::expand_tilde_path("C:\\home")); 170 | } else { 171 | home.push("rick"); 172 | assert_eq!( 173 | format!("{}", home.display()), 174 | super::expand_tilde_path("~/rick") 175 | ); 176 | home.push("morty.txt"); 177 | assert_eq!( 178 | format!("{}", home.display()), 179 | super::expand_tilde_path("~/rick/morty.txt") 180 | ); 181 | assert_eq!("/home", super::expand_tilde_path("/home")); 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /tools/before_deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Building and packaging for release 3 | 4 | set -ex 5 | 6 | build() { 7 | cargo build --target "$TARGET" --release --verbose 8 | } 9 | 10 | copy() { 11 | local out_dir=$(pwd) 12 | local out_server="$out_dir/$PROJECT_NAME-server-$TRAVIS_TAG-$TARGET" 13 | local out_client="$out_dir/$PROJECT_NAME-client-$TRAVIS_TAG-$TARGET" 14 | 15 | # copy server 16 | cp "target/$TARGET/release/$PROJECT_NAME-server" "$out_server" 17 | strip "$out_server" 18 | 19 | # copy client 20 | cp "target/$TARGET/release/$PROJECT_NAME-client" "$out_client" 21 | strip "$out_client" 22 | } 23 | 24 | main() { 25 | build 26 | copy 27 | } 28 | 29 | main 30 | -------------------------------------------------------------------------------- /tools/com.serholiu.fakio.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | com.serholiu.fakio 7 | ProgramArguments 8 | 9 | /Path/your/fakio-client 10 | /Path/your/client.toml 11 | 12 | RunAtLoad 13 | 14 | KeepAlive 15 | 16 | StandardErrorPath 17 | /Path/your/fakio.log 18 | StandardOutPath 19 | /Path/your/fakio.log 20 | 21 | 22 | -------------------------------------------------------------------------------- /tools/fakio.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=fakio server 3 | After=network-online.target remote-fs.target nss-lookup.target 4 | 5 | [Service] 6 | ExecStart=/path/your/fakio-server server.toml 7 | Restart=always 8 | User=yourname 9 | Group=yourgroup 10 | WorkingDirectory=/path/your/work 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /tools/supervisord.conf: -------------------------------------------------------------------------------- 1 | [unix_http_server] 2 | file=/tmp/supervisor.sock ; path to your socket file 3 | 4 | [supervisord] 5 | logfile=/tmp/supervisord.log ; supervisord log file 6 | logfile_maxbytes=50MB ; maximum size of logfile before rotation 7 | logfile_backups=10 ; number of backed up logfiles 8 | loglevel=warn ; info, debug, warn, trace 9 | pidfile=/tmp/supervisord.pid ; pidfile location 10 | nodaemon=false ; run supervisord as a daemon 11 | minfds=1024 ; number of startup file descriptors 12 | minprocs=200 ; number of process descriptors 13 | user=yourname ;default user 14 | childlogdir=/home/my/ 15 | 16 | [rpcinterface:supervisor] 17 | supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface 18 | 19 | [supervisorctl] 20 | serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket 21 | 22 | [program:fakio-server] 23 | command=/path/your/fakio-server /path/your/fakio.conf 24 | autostart=true 25 | autorestart=true 26 | redirect_stderr=true 27 | stdout_logfile=/path/your/fakio-server.log 28 | --------------------------------------------------------------------------------