├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── config.rs ├── main.rs └── packet_utils.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | config.yml 3 | 4 | # JetBrains 5 | .idea/ 6 | -------------------------------------------------------------------------------- /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.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada" 8 | dependencies = [ 9 | "memchr", 10 | ] 11 | 12 | [[package]] 13 | name = "anyhow" 14 | version = "1.0.28" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "d9a60d744a80c30fcb657dfe2c1b22bcb3e814c1a1e3674f32bf5820b570fbff" 17 | 18 | [[package]] 19 | name = "arc-swap" 20 | version = "0.4.5" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825" 23 | 24 | [[package]] 25 | name = "atty" 26 | version = "0.2.14" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 29 | dependencies = [ 30 | "hermit-abi", 31 | "libc", 32 | "winapi 0.3.8", 33 | ] 34 | 35 | [[package]] 36 | name = "bitflags" 37 | version = "1.2.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 40 | 41 | [[package]] 42 | name = "bytes" 43 | version = "0.5.4" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "130aac562c0dd69c56b3b1cc8ffd2e17be31d0b6c25b61c96b76231aa23e39e1" 46 | 47 | [[package]] 48 | name = "cfg-if" 49 | version = "0.1.10" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 52 | 53 | [[package]] 54 | name = "derive_more" 55 | version = "0.99.5" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "e2323f3f47db9a0e77ce7a300605d8d2098597fc451ed1a97bb1f6411bb550a7" 58 | dependencies = [ 59 | "proc-macro2", 60 | "quote", 61 | "syn", 62 | ] 63 | 64 | [[package]] 65 | name = "dtoa" 66 | version = "0.4.8" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" 69 | 70 | [[package]] 71 | name = "env_logger" 72 | version = "0.7.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" 75 | dependencies = [ 76 | "atty", 77 | "humantime", 78 | "log", 79 | "regex", 80 | "termcolor", 81 | ] 82 | 83 | [[package]] 84 | name = "fnv" 85 | version = "1.0.6" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" 88 | 89 | [[package]] 90 | name = "fuchsia-zircon" 91 | version = "0.3.3" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" 94 | dependencies = [ 95 | "bitflags", 96 | "fuchsia-zircon-sys", 97 | ] 98 | 99 | [[package]] 100 | name = "fuchsia-zircon-sys" 101 | version = "0.3.3" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" 104 | 105 | [[package]] 106 | name = "futures-core" 107 | version = "0.3.4" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a" 110 | 111 | [[package]] 112 | name = "hermit-abi" 113 | version = "0.1.11" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "8a0d737e0f947a1864e93d33fdef4af8445a00d1ed8dc0c8ddb73139ea6abf15" 116 | dependencies = [ 117 | "libc", 118 | ] 119 | 120 | [[package]] 121 | name = "humantime" 122 | version = "1.3.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" 125 | dependencies = [ 126 | "quick-error", 127 | ] 128 | 129 | [[package]] 130 | name = "iovec" 131 | version = "0.1.4" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" 134 | dependencies = [ 135 | "libc", 136 | ] 137 | 138 | [[package]] 139 | name = "kernel32-sys" 140 | version = "0.2.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" 143 | dependencies = [ 144 | "winapi 0.2.8", 145 | "winapi-build", 146 | ] 147 | 148 | [[package]] 149 | name = "lazy_static" 150 | version = "1.4.0" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 153 | 154 | [[package]] 155 | name = "libc" 156 | version = "0.2.69" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005" 159 | 160 | [[package]] 161 | name = "linked-hash-map" 162 | version = "0.5.4" 163 | source = "registry+https://github.com/rust-lang/crates.io-index" 164 | checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" 165 | 166 | [[package]] 167 | name = "log" 168 | version = "0.4.8" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" 171 | dependencies = [ 172 | "cfg-if", 173 | ] 174 | 175 | [[package]] 176 | name = "memchr" 177 | version = "2.3.3" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 180 | 181 | [[package]] 182 | name = "mio" 183 | version = "0.6.21" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" 186 | dependencies = [ 187 | "cfg-if", 188 | "fuchsia-zircon", 189 | "fuchsia-zircon-sys", 190 | "iovec", 191 | "kernel32-sys", 192 | "libc", 193 | "log", 194 | "miow 0.2.1", 195 | "net2", 196 | "slab", 197 | "winapi 0.2.8", 198 | ] 199 | 200 | [[package]] 201 | name = "mio-named-pipes" 202 | version = "0.1.6" 203 | source = "registry+https://github.com/rust-lang/crates.io-index" 204 | checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3" 205 | dependencies = [ 206 | "log", 207 | "mio", 208 | "miow 0.3.3", 209 | "winapi 0.3.8", 210 | ] 211 | 212 | [[package]] 213 | name = "mio-uds" 214 | version = "0.6.7" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" 217 | dependencies = [ 218 | "iovec", 219 | "libc", 220 | "mio", 221 | ] 222 | 223 | [[package]] 224 | name = "miow" 225 | version = "0.2.1" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" 228 | dependencies = [ 229 | "kernel32-sys", 230 | "net2", 231 | "winapi 0.2.8", 232 | "ws2_32-sys", 233 | ] 234 | 235 | [[package]] 236 | name = "miow" 237 | version = "0.3.3" 238 | source = "registry+https://github.com/rust-lang/crates.io-index" 239 | checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" 240 | dependencies = [ 241 | "socket2", 242 | "winapi 0.3.8", 243 | ] 244 | 245 | [[package]] 246 | name = "net2" 247 | version = "0.2.33" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" 250 | dependencies = [ 251 | "cfg-if", 252 | "libc", 253 | "winapi 0.3.8", 254 | ] 255 | 256 | [[package]] 257 | name = "num_cpus" 258 | version = "1.13.0" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" 261 | dependencies = [ 262 | "hermit-abi", 263 | "libc", 264 | ] 265 | 266 | [[package]] 267 | name = "pin-project-lite" 268 | version = "0.1.4" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "237844750cfbb86f67afe27eee600dfbbcb6188d734139b534cbfbf4f96792ae" 271 | 272 | [[package]] 273 | name = "proc-macro2" 274 | version = "1.0.10" 275 | source = "registry+https://github.com/rust-lang/crates.io-index" 276 | checksum = "df246d292ff63439fea9bc8c0a270bed0e390d5ebd4db4ba15aba81111b5abe3" 277 | dependencies = [ 278 | "unicode-xid", 279 | ] 280 | 281 | [[package]] 282 | name = "quick-error" 283 | version = "1.2.3" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" 286 | 287 | [[package]] 288 | name = "quote" 289 | version = "1.0.3" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" 292 | dependencies = [ 293 | "proc-macro2", 294 | ] 295 | 296 | [[package]] 297 | name = "redox_syscall" 298 | version = "0.1.56" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" 301 | 302 | [[package]] 303 | name = "regex" 304 | version = "1.3.6" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "7f6946991529684867e47d86474e3a6d0c0ab9b82d5821e314b1ede31fa3a4b3" 307 | dependencies = [ 308 | "aho-corasick", 309 | "memchr", 310 | "regex-syntax", 311 | "thread_local", 312 | ] 313 | 314 | [[package]] 315 | name = "regex-syntax" 316 | version = "0.6.17" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae" 319 | 320 | [[package]] 321 | name = "rust-minecraft-proxy" 322 | version = "0.1.0" 323 | dependencies = [ 324 | "anyhow", 325 | "derive_more", 326 | "env_logger", 327 | "log", 328 | "serde", 329 | "serde_yaml", 330 | "tokio", 331 | ] 332 | 333 | [[package]] 334 | name = "serde" 335 | version = "1.0.106" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" 338 | dependencies = [ 339 | "serde_derive", 340 | ] 341 | 342 | [[package]] 343 | name = "serde_derive" 344 | version = "1.0.106" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" 347 | dependencies = [ 348 | "proc-macro2", 349 | "quote", 350 | "syn", 351 | ] 352 | 353 | [[package]] 354 | name = "serde_yaml" 355 | version = "0.8.17" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" 358 | dependencies = [ 359 | "dtoa", 360 | "linked-hash-map", 361 | "serde", 362 | "yaml-rust", 363 | ] 364 | 365 | [[package]] 366 | name = "signal-hook-registry" 367 | version = "1.2.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "94f478ede9f64724c5d173d7bb56099ec3e2d9fc2774aac65d34b8b890405f41" 370 | dependencies = [ 371 | "arc-swap", 372 | "libc", 373 | ] 374 | 375 | [[package]] 376 | name = "slab" 377 | version = "0.4.2" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" 380 | 381 | [[package]] 382 | name = "socket2" 383 | version = "0.3.12" 384 | source = "registry+https://github.com/rust-lang/crates.io-index" 385 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 386 | dependencies = [ 387 | "cfg-if", 388 | "libc", 389 | "redox_syscall", 390 | "winapi 0.3.8", 391 | ] 392 | 393 | [[package]] 394 | name = "syn" 395 | version = "1.0.17" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "0df0eb663f387145cab623dea85b09c2c5b4b0aef44e945d928e682fce71bb03" 398 | dependencies = [ 399 | "proc-macro2", 400 | "quote", 401 | "unicode-xid", 402 | ] 403 | 404 | [[package]] 405 | name = "termcolor" 406 | version = "1.1.0" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" 409 | dependencies = [ 410 | "winapi-util", 411 | ] 412 | 413 | [[package]] 414 | name = "thread_local" 415 | version = "1.0.1" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" 418 | dependencies = [ 419 | "lazy_static", 420 | ] 421 | 422 | [[package]] 423 | name = "tokio" 424 | version = "0.2.18" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "34ef16d072d2b6dc8b4a56c70f5c5ced1a37752116f8e7c1e80c659aa7cb6713" 427 | dependencies = [ 428 | "bytes", 429 | "fnv", 430 | "futures-core", 431 | "iovec", 432 | "lazy_static", 433 | "libc", 434 | "memchr", 435 | "mio", 436 | "mio-named-pipes", 437 | "mio-uds", 438 | "num_cpus", 439 | "pin-project-lite", 440 | "signal-hook-registry", 441 | "slab", 442 | "tokio-macros", 443 | "winapi 0.3.8", 444 | ] 445 | 446 | [[package]] 447 | name = "tokio-macros" 448 | version = "0.2.5" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389" 451 | dependencies = [ 452 | "proc-macro2", 453 | "quote", 454 | "syn", 455 | ] 456 | 457 | [[package]] 458 | name = "unicode-xid" 459 | version = "0.2.0" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" 462 | 463 | [[package]] 464 | name = "winapi" 465 | version = "0.2.8" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" 468 | 469 | [[package]] 470 | name = "winapi" 471 | version = "0.3.8" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" 474 | dependencies = [ 475 | "winapi-i686-pc-windows-gnu", 476 | "winapi-x86_64-pc-windows-gnu", 477 | ] 478 | 479 | [[package]] 480 | name = "winapi-build" 481 | version = "0.1.1" 482 | source = "registry+https://github.com/rust-lang/crates.io-index" 483 | checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" 484 | 485 | [[package]] 486 | name = "winapi-i686-pc-windows-gnu" 487 | version = "0.4.0" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 490 | 491 | [[package]] 492 | name = "winapi-util" 493 | version = "0.1.4" 494 | source = "registry+https://github.com/rust-lang/crates.io-index" 495 | checksum = "fa515c5163a99cc82bab70fd3bfdd36d827be85de63737b40fcef2ce084a436e" 496 | dependencies = [ 497 | "winapi 0.3.8", 498 | ] 499 | 500 | [[package]] 501 | name = "winapi-x86_64-pc-windows-gnu" 502 | version = "0.4.0" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 505 | 506 | [[package]] 507 | name = "ws2_32-sys" 508 | version = "0.2.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" 511 | dependencies = [ 512 | "winapi 0.2.8", 513 | "winapi-build", 514 | ] 515 | 516 | [[package]] 517 | name = "yaml-rust" 518 | version = "0.4.5" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" 521 | dependencies = [ 522 | "linked-hash-map", 523 | ] 524 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-minecraft-proxy" 3 | version = "0.1.0" 4 | authors = ["shirokuro <46kuro212@gmail.com>", "CM8DoubleCheck "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tokio = { version = "0.2", features = ["full"] } 11 | log = "0.4" 12 | env_logger = "0.7" 13 | serde = { version = "1.0", features = ["derive"] } 14 | serde_yaml = "0.8" 15 | anyhow = "1.0" 16 | derive_more = "0.99" 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright for portions of minecraft-proxy-rs (original repository) are held by 2 | shirokuro <46kuro212@gmail.com>. All newer updates are copyright for 3 | rust-minecraft-proxy and are held by CM8DoubleCheck . 4 | The project rust-minecraft-proxy maintains the MIT license. 5 | 6 | 7 | MIT License 8 | 9 | Copyright (c) 2020 shirokuro 10 | 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall be included in all 19 | copies or substantial portions of the Software. 20 | 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 27 | SOFTWARE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-minecraft-proxy 2 | 3 | Discord 4 | 5 | 6 | Updated fork of [kuro46/minecraft-proxy-rs](https://github.com/kuro46/minecraft-proxy-rs) with the goal of adding additional features 7 | 8 | ## Goals 9 | - ~~Unknown host MOTD~~ ✔️ 10 | - ~~Forge Support~~ ✔️ 11 | - Custom online & offline MOTD overrides for defined hosts 12 | - Load balancing between defined hosts 13 | - Proxy Protocol support (both incoming and outgoing) 14 | - TCPShield compatibility & passthrough 15 | - IP based filtering 16 | - Optional Redis support 17 | - Basic anti-bot system 18 | 19 | ## Disclaimer 20 | This is a relatively new fork and is not yet ready for production. You can follow the instructions below to get an instance running, but there is a high chance of the structure of the project and configuration changing. Once the project has reached a point where I feel it is ready for a release, such release will be created on this repository. This project also serves as a way for me to learn Rust, so I am by no means a Rust developer yet. If there's anything that can be done better, feel free to fork and submit a pull request as well as add me on discord (DoubleCheck#0001) for code suggestions. 21 | 22 | ## Usage 23 | 24 | 1. Download binary from releases or build with `cargo build --release` and run. 25 | 1. `config.yml` will be created. Please edit if you need. 26 | 27 | ### Configuration example 28 | 29 | ```yaml 30 | listen_addr: "0.0.0.0:25565" 31 | unknown_host: 32 | kick_message: "§bRust Minecraft Proxy\n\n§cInvalid Address" 33 | motd: 34 | text: "§cUnknown host!\n§7Please use a valid address to connect." 35 | protocol_name: §crust-minecraft-proxy 36 | host: 37 | hub.example.com: 38 | ip: "127.0.0.1:35560" 39 | minigame.example.com: 40 | ip: "127.0.0.1:25561" 41 | ``` 42 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::collections::BTreeMap; 3 | use std::default::Default; 4 | use std::fs; 5 | use std::path::Path; 6 | 7 | #[derive(Debug, Clone, Serialize, Deserialize)] 8 | pub struct MOTD { 9 | text: String, 10 | protocol_name: String, 11 | } 12 | 13 | #[derive(Debug, Clone, Serialize, Deserialize)] 14 | pub struct Server { 15 | pub(crate) ip: String, 16 | 17 | } 18 | 19 | #[derive(Debug, Clone, Serialize, Deserialize)] 20 | pub struct UnknownHost { 21 | kick_message: String, 22 | motd: MOTD 23 | } 24 | 25 | #[derive(Debug, Clone, Serialize, Deserialize)] 26 | pub struct Config { 27 | listen_addr: String, 28 | unknown_host: UnknownHost, 29 | hosts: BTreeMap 30 | } 31 | 32 | impl Default for Config { 33 | fn default() -> Self { 34 | 35 | let unknown_host = UnknownHost { 36 | kick_message: "§bRust Minecraft Proxy\n\n§cInvalid Address".to_string(), 37 | motd: MOTD { text: "§cUnknown host!\n§7Please use a valid address to connect.".to_string(), protocol_name: "§crust-minecraft-proxy".to_string() } 38 | }; 39 | 40 | let mut hosts: BTreeMap = BTreeMap::new(); 41 | hosts.insert("hub.example.com".to_string(), Server { ip: "127.0.0.1:35560".to_string() }); 42 | hosts.insert("minigame.example.com".to_string(), Server { ip: "127.0.0.1:25561".to_string() }); 43 | 44 | Self { 45 | listen_addr: "0.0.0.0:25565".to_string(), 46 | unknown_host, 47 | hosts 48 | } 49 | } 50 | } 51 | 52 | impl Config { 53 | pub fn load_or_init(path: &Path) -> Config { 54 | if path.exists() { 55 | serde_yaml::from_str(&fs::read_to_string(path).unwrap()).unwrap() 56 | } else { 57 | info!("Configuration file does not exist. Use defaults."); 58 | let default = Config::default(); 59 | trace!("Default configuration: {:?}", default); 60 | let string = serde_yaml::to_string(&default).unwrap(); 61 | fs::write(path, &string).unwrap(); 62 | default 63 | } 64 | } 65 | 66 | pub fn get_unknown_host_kick_msg(&self) -> String { 67 | let mut message: String = "{\"text\":\"".to_owned(); 68 | message.push_str(&self.unknown_host.kick_message); 69 | message.push_str("\"}"); 70 | message 71 | } 72 | 73 | pub fn get_unknown_host_motd(&self) -> String { 74 | let mut motd: String = "{\"version\": {\"name\": \"".to_owned(); 75 | motd.push_str(&self.unknown_host.motd.protocol_name); 76 | motd.push_str("\", \"protocol\": -1 }, \"players\": {\"max\": 0, \"online\": 0, \"sample\": [] }, \"description\": { \"text\": \""); 77 | motd.push_str(&self.unknown_host.motd.text); 78 | motd.push_str("\" }}"); 79 | motd 80 | } 81 | 82 | pub fn get_listen_addr(&self) -> &str { 83 | &self.listen_addr 84 | } 85 | 86 | pub fn get_hosts(&self) -> &BTreeMap { 87 | &self.hosts 88 | } 89 | 90 | pub fn get_addr_by_host(&self, host: &str) -> Option<&Server> { 91 | self.hosts.get(host) 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | #[macro_use] 4 | extern crate anyhow; 5 | 6 | pub mod config; 7 | pub mod packet_utils; 8 | 9 | use anyhow::Result; 10 | use config::Config; 11 | use packet_utils::{HandshakeRequest, NextState}; 12 | use std::env; 13 | use std::io::Cursor; 14 | use std::net::SocketAddr; 15 | use std::path::Path; 16 | use std::sync::Arc; 17 | use tokio::net::{TcpListener, TcpStream}; 18 | use tokio::prelude::*; 19 | 20 | #[tokio::main] 21 | async fn main() { 22 | if env::var("RUST_LOG").is_err() { 23 | env::set_var("RUST_LOG", "info"); 24 | } 25 | env_logger::init(); 26 | 27 | let config = load_conf(); 28 | debug!("Configuration: {:?}", config); 29 | 30 | start(config).await; 31 | } 32 | 33 | async fn start(config: Config) { 34 | info!("Listening on {}", config.get_listen_addr()); 35 | let mut listener = TcpListener::bind(config.get_listen_addr()).await.unwrap(); 36 | let config = Arc::new(config); 37 | loop { 38 | let client = accept_client(&mut listener).await; 39 | if let Err(e) = client { 40 | error!("Failed to accept a client: {}", e); 41 | continue; 42 | } 43 | let (stream, addr) = client.unwrap(); 44 | debug!("Client connected from {:?}", addr); 45 | let config = Arc::clone(&config); 46 | tokio::spawn(async move { 47 | let result = handle_client(&config, stream, &addr).await; 48 | if let Err(e) = result { 49 | error!("{}: An error occurred: {}", addr, e); 50 | } 51 | }); 52 | } 53 | } 54 | 55 | async fn accept_client(listener: &mut TcpListener) -> Result<(TcpStream, SocketAddr)> { 56 | let client = listener.accept().await?; 57 | client.0.set_nodelay(true)?; 58 | Ok(client) 59 | } 60 | 61 | async fn handle_client(config: &Config, mut stream: TcpStream, addr: &SocketAddr) -> Result<()> { 62 | let handshake = HandshakeRequest::read(&mut stream).await?; 63 | 64 | let host: &str = &handle_hostname(handshake.get_host()).await; 65 | 66 | let redirect_target = config.get_addr_by_host(host); 67 | info!( 68 | "{}: {}: {}:{} -> {}", 69 | addr, 70 | handshake.get_next_state(), 71 | host, 72 | handshake.get_port(), 73 | redirect_target 74 | .map(|s| s.ip.to_string()) 75 | .unwrap_or_else(|| "unknown".to_string()) 76 | ); 77 | if redirect_target.is_none() { 78 | if *handshake.get_next_state() == NextState::Login { // Unknown host, disconnect 79 | write_string(&mut stream, &mut &**&mut config.get_unknown_host_kick_msg()).await?; // Disconnect Message 80 | } else if *handshake.get_next_state() == NextState::Status { // Unknown host, send unknown host MOTD 81 | write_string(&mut stream, &mut &**&mut config.get_unknown_host_motd()).await?; // MOTD 82 | } 83 | return Ok(()); 84 | } 85 | let mut server = TcpStream::connect(redirect_target.map(|s| s.ip.to_string()).unwrap()).await?; 86 | server.set_nodelay(true)?; 87 | packet_utils::write_var_int(&mut server, handshake.get_size()).await?; 88 | server.write_all(handshake.get_raw_body()).await?; 89 | 90 | let (mut client_reader, mut client_writer) = tokio::io::split(stream); 91 | let (mut server_reader, mut server_writer) = tokio::io::split(server); 92 | let addr_clone = *addr; 93 | tokio::spawn(async move { 94 | let result = tokio::io::copy(&mut client_reader, &mut server_writer).await; 95 | if let Some(err) = result.err() { 96 | debug!( 97 | "{}: An error occurred in client-to-server bridge. Maybe disconnected: {}", 98 | addr_clone, err 99 | ); 100 | } 101 | }); 102 | let result = tokio::io::copy(&mut server_reader, &mut client_writer).await; 103 | if let Some(err) = result.err() { 104 | debug!( 105 | "{}: An error occurred in server-to-client bridge. Maybe disconnected: {}", 106 | addr, err 107 | ); 108 | } 109 | Ok(()) 110 | } 111 | 112 | async fn handle_hostname(hostname: &str) -> String { 113 | let mut host: String = hostname.to_owned(); 114 | 115 | // TCPShield Support (UNTESTED!) 116 | if host.contains("///") { 117 | let parts = host.split("///"); 118 | for part in parts { 119 | host = part.to_owned(); 120 | break; 121 | } 122 | } 123 | 124 | // Forge Support 125 | if host.contains("FML2") { 126 | host = host.replace("FML2", ""); 127 | } else if host.contains("FML") { 128 | host = host.replace("FML", ""); 129 | } 130 | host 131 | } 132 | 133 | async fn write_string(stream: &mut TcpStream, string: &mut &str) -> Result<()> { 134 | let mut temp: Cursor> = Cursor::new(Vec::new()); 135 | packet_utils::write_var_int(&mut temp, 0).await?; 136 | packet_utils::write_var_int(&mut temp, string.len() as i32).await?; 137 | temp.write_all(&string.as_bytes()).await?; 138 | let temp = temp.into_inner(); 139 | packet_utils::write_var_int(stream, temp.len() as i32).await?; 140 | stream.write_all(&temp).await?; 141 | Ok(()) 142 | } 143 | 144 | fn load_conf() -> Config { 145 | let config_path = Path::new("./config.yml"); 146 | info!("Configuration file: {:?}", config_path); 147 | Config::load_or_init(config_path) 148 | } 149 | -------------------------------------------------------------------------------- /src/packet_utils.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result; 2 | use derive_more::Display; 3 | use std::io::Cursor; 4 | use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; 5 | use tokio::net::TcpStream; 6 | 7 | pub async fn read_var_int(stream: &mut T) -> Result 8 | where 9 | T: AsyncRead + std::marker::Unpin, 10 | { 11 | let mut num_read: i32 = 0; 12 | let mut result: i32 = 0; 13 | loop { 14 | let read = stream.read_u8().await? as i32; 15 | let value = read & 0b0111_1111; 16 | result |= value << (7 * num_read); 17 | num_read += 1; 18 | if num_read > 5 { 19 | return Err(anyhow!("VarInt too big!")); 20 | } 21 | if (read & 0b1000_0000) == 0 { 22 | break; 23 | } 24 | } 25 | Ok(result) 26 | } 27 | 28 | pub async fn write_var_int(stream: &mut T, mut value: i32) -> Result<()> 29 | where 30 | T: AsyncWrite + std::marker::Unpin, 31 | { 32 | loop { 33 | let mut temp: i16 = (value & 0b0111_1111) as i16; 34 | value >>= 7; 35 | if value != 0 { 36 | temp |= 0b1000_0000; 37 | } 38 | stream.write_i8(temp as i8).await?; 39 | if value == 0 { 40 | break Ok(()); 41 | } 42 | } 43 | } 44 | pub async fn read_string(stream: &mut T) -> Result 45 | where 46 | T: AsyncRead + std::marker::Unpin, 47 | { 48 | let length = read_var_int(stream).await?; 49 | let mut buf = vec![0u8; length as usize]; 50 | stream.read_exact(&mut buf).await?; 51 | Ok(String::from_utf8_lossy(&buf).to_string()) 52 | } 53 | 54 | #[derive(Debug, Clone)] 55 | pub struct HandshakeRequest { 56 | size: i32, 57 | raw_body: Vec, 58 | id: i32, 59 | version: i32, 60 | host: String, 61 | port: u16, 62 | next_state: NextState, 63 | } 64 | 65 | impl HandshakeRequest { 66 | pub async fn read(stream: &mut TcpStream) -> Result { 67 | let size = read_var_int(stream).await?; 68 | let mut raw_body = vec![0u8; size as usize]; 69 | stream.read_exact(&mut raw_body).await?; 70 | let mut raw_body = Cursor::new(raw_body); 71 | let id = read_var_int(&mut raw_body).await?; 72 | if id != 0 { 73 | return Err(anyhow!("{} is not a id of handshake packet", id)); 74 | } 75 | let version = read_var_int(&mut raw_body).await?; 76 | let host = read_string(&mut raw_body).await?; 77 | let port = raw_body.read_u16().await?; 78 | let next_state = NextState::from_i32(read_var_int(&mut raw_body).await?)?; 79 | Ok(Self { 80 | size, 81 | id, 82 | version, 83 | host, 84 | port, 85 | next_state, 86 | raw_body: raw_body.into_inner(), 87 | }) 88 | } 89 | 90 | pub fn get_host(&self) -> &str { 91 | &self.host 92 | } 93 | 94 | pub fn get_next_state(&self) -> &NextState { 95 | &self.next_state 96 | } 97 | 98 | pub fn get_size(&self) -> i32 { 99 | self.size 100 | } 101 | 102 | pub fn get_raw_body(&self) -> &[u8] { 103 | &self.raw_body 104 | } 105 | 106 | pub fn get_port(&self) -> u16 { 107 | self.port 108 | } 109 | } 110 | 111 | #[derive(Debug, Clone, Eq, PartialEq, Display)] 112 | pub enum NextState { 113 | Status, 114 | Login, 115 | } 116 | 117 | impl NextState { 118 | pub fn from_i32(num: i32) -> Result { 119 | Ok(match num { 120 | 1 => Self::Status, 121 | 2 => Self::Login, 122 | _ => return Err(anyhow!("Cannot convert {} to NextState", num)), 123 | }) 124 | } 125 | } 126 | --------------------------------------------------------------------------------