mirror of https://github.com/schollz/croc.git
Compare commits
560 Commits
Author | SHA1 | Date |
---|---|---|
Zack | 50e0f625bc | |
Zack | 94af2374c3 | |
Zack | a4322faa25 | |
Zack | 23dce2aa3e | |
Zack | 88002b322d | |
Zack | 9246408278 | |
Zack | fbf1eeedce | |
Zack | e4c9f2d9fb | |
Zack | c0c3370d9b | |
Zack | f6bd13fa06 | |
Zack Scholl | f83616e9bd | |
Zack Scholl | 23f385ab2f | |
Zack Scholl | 78feb393de | |
Zack | 69fc3cee47 | |
Zack | a5da77cf49 | |
Zack | f66e17dd46 | |
Zack | 63c9201938 | |
Zack | ca7a5979cc | |
Zack | fc457557e0 | |
Zack | f044f4dd86 | |
Zack | a2e71c7e1a | |
Zack | dff34fa7fc | |
Zack Scholl | 08103bb7bb | |
Zack Scholl | 4091ef0496 | |
Zack | 381f8369a3 | |
Zack | a95d67e31c | |
Zack | b0920bbe70 | |
Zack | ed55c746c2 | |
Zack | 8e10eac5c5 | |
Zack | 43f1c53538 | |
Zack | 3acac5d53b | |
Zack | 66f0d1264a | |
Zack | ee713c5146 | |
Zack | 6181903c83 | |
Zack | 7acd2def69 | |
Zack | eb0909033e | |
Zack | f6d862eac0 | |
Zack | 5a6005f1eb | |
Zack | f6633cbac9 | |
Zack | d8ef7cda20 | |
Zack | 7622e636e4 | |
Zack | bb018fd725 | |
Zack | 863dabb93a | |
Zack | 6f5f16aa1c | |
Zack | 0f1ca436cd | |
Zack | 4929635eb8 | |
Zack | 3f12f75fae | |
Zack | e255d472a6 | |
Zack | 2ffd4daeaf | |
Zack | accb310337 | |
Zack | 2b4c088100 | |
Zack | a591833dbf | |
Zack | b3668a6f5c | |
Zack | b05c3c8c42 | |
Zack | 13bc190f8b | |
Zack | 1b90484bb8 | |
Zack Scholl | 05359d6976 | |
Zack Scholl | cc4d74c490 | |
Zack | d81116382f | |
Zack Scholl | 94cc880568 | |
Zack | 24b907f4bb | |
Zack Scholl | 8166b2dbed | |
Zack | 14187f6f30 | |
dependabot[bot] | 90682d3ebd | |
Zack Scholl | f4057aa28b | |
Zack | 3c2548aa69 | |
dependabot[bot] | 7bab9c3cb5 | |
Zack Scholl | 355628f895 | |
Zack | eaffc6dcac | |
a1lu | 4baec420c8 | |
a1lu | cd89e8043f | |
Zack Scholl | 1324ff8897 | |
Zack | 8bc7a62b9e | |
Zack Scholl | 0c49ac3f02 | |
Zack | 8b4ab4c86c | |
Zack Scholl | f8f69e3157 | |
Zack | 4e75e131c4 | |
a1lu | e599e56415 | |
a1lu | 956598c427 | |
a1lu | 618ae1e5d0 | |
Zack Scholl | 7763a971f2 | |
Zack | d2b7c80369 | |
Rahul Garg | 241176d8a4 | |
Zack Scholl | 719f9b62c9 | |
Zack Scholl | 483c5255bb | |
Zack Scholl | 03e6dcd220 | |
Zack Scholl | 22ddbd83c2 | |
Zack Scholl | 6b930c365b | |
Zack | 1f6851f33b | |
Zack | 61224b4e6b | |
Zack Scholl | 6f2771e7b5 | |
Zack | c21beccb7a | |
dependabot[bot] | 40f5e9ca1e | |
bitraid | ed8c0475bf | |
dependabot[bot] | d7d7d3c8dc | |
Zack Scholl | 945ac74690 | |
Zack Scholl | fff6f48001 | |
Zack Scholl | 28ef8e99ac | |
Zack | 28bb36b321 | |
Zack Scholl | ab2cb477a8 | |
Zack | abc5029fce | |
Zack | e7f67ebea7 | |
Zack Scholl | c7f0228786 | |
Zack | 741714504a | |
D. Bohdan | be8a6689ff | |
Zack Scholl | de9c54e57a | |
Zack | 53fc9ebd99 | |
Zack | 4159ba7668 | |
Zack Scholl | 064f84ccd3 | |
Zack | 8ac1e3a501 | |
Zack | 11bc4eecc6 | |
Zack | d92cff92b9 | |
Zack | 00f12b5742 | |
Travis | 4f1f57b1ba | |
Zack | a240a4b982 | |
Zack | 508e0be335 | |
qk-santi | e1644401da | |
qk-santi | c83eb59963 | |
Zack | f874e30151 | |
Zack | b278f5a41d | |
Zack | ce91e3b420 | |
Zack | 30a6b14443 | |
qk-santi | 87152f8706 | |
qk-santi | 48eb2a2a7c | |
dependabot[bot] | 816ad09a50 | |
Zack | a2228e80c2 | |
Zack Scholl | 230de9184d | |
Zack | 3c781069ca | |
Zack | 7274a8bd4b | |
Zack Scholl | 3382306342 | |
dependabot[bot] | ad47739c8f | |
Zack | 2f33b14b3d | |
Zack | 4025efcd48 | |
Zack | f41a2afead | |
Zack | ba75f7badb | |
Zack | 55c954117a | |
Zack | 4d083f8d10 | |
Rehan Mahmood | 9ad2af18bd | |
Zack | 47fc96b98f | |
Rehan Mahmood | d4bba88fb1 | |
Zack Scholl | c1367671b6 | |
Zack Scholl | 1e572067ec | |
Zack Scholl | 8aa9f040f6 | |
Zack Scholl | 96bb34485e | |
Zack | 159f0f8d9b | |
Sandeep | c4d5c89e5f | |
Zack | 6ac67b68fc | |
PThorpe92 | 0af35d7149 | |
Zack | f91c7a9948 | |
Zack | cff8cddd13 | |
zx9597446 | d724f11297 | |
ferebee | 80aabea29b | |
sitiom | f8bb011eac | |
Zack Scholl | ef68dfa54c | |
Zack Scholl | 8ab65d06b5 | |
Zack Scholl | 9c82914e7c | |
Zack | 95717f16c9 | |
glitsj16 | 5a58ae294b | |
Zack Scholl | 0aa5c80393 | |
Zack | 23a8904193 | |
N0mansky | d5e63cd0bf | |
N0mansky | 1b1dc5cdfe | |
Zack | cd1162f85c | |
Alexander Seiler | ad7a22b218 | |
Zack | e3a18cd7a7 | |
Zack | 8611bfa44a | |
Zack Scholl | b3c0625659 | |
dependabot[bot] | 40e5893bb8 | |
Zack | e1b49e6d1c | |
BayLee4 | fae2e81b4a | |
BayLee4 | f5a02df17b | |
dependabot[bot] | d813aa2fa9 | |
Zack | 8d8d06557e | |
Zack Scholl | 3ae46c3c18 | |
Zack Scholl | 1fce28e72f | |
Zack Scholl | ac3caa5564 | |
Zack Scholl | 790ee9e6eb | |
Zack Scholl | 0656bb7851 | |
Zack Scholl | 5ed8204cb1 | |
Zack Scholl | 1220d54a65 | |
Zack Scholl | 642d20b48d | |
Zack | 58bb226ba5 | |
stefins | 080c21b66b | |
stefins | 4944355b75 | |
Zack Scholl | 3c1d60d6d2 | |
Zack Scholl | 5bdd4453cd | |
Zack Scholl | 5b333fc85c | |
Zack Scholl | 90e4e33dcd | |
Zack Scholl | 3135bd74e3 | |
Zack Scholl | 7c98ae3d5a | |
sitiom | 908f33cd61 | |
sitiom | 9eaba7be7c | |
Ryan Caezar Itang | b3db9d88c1 | |
Zack Scholl | 9006cde1b3 | |
Zack | 8f75bb8299 | |
Zack | faf64aadf1 | |
stefins | 65760b1189 | |
stefins | 24679d4e02 | |
Zack Scholl | 8b9977a7c4 | |
Zack | ae71b4b63b | |
Zack Scholl | c462b5948f | |
Zack | a2c1dc6a89 | |
chncaption | 98391bff63 | |
BayLee4 | adadaba75e | |
BayLee4 | 0024beaedc | |
Zack Scholl | 63feece074 | |
Zack Scholl | 9e280d7f48 | |
Zack Scholl | 7c7be95d6b | |
Zack | c20beb1be7 | |
Zack Scholl | e08c5e7f38 | |
Zack Scholl | 3b819346fa | |
Zack Scholl | 2467411bae | |
Zack Scholl | b436b31970 | |
Zack Scholl | e3ce565ca6 | |
Zack Scholl | e04981698c | |
Zack Scholl | 07a67ed54e | |
Zack | 9a8e584aca | |
Tai Groot | fa7ae114f5 | |
Zack | 8a4326bc0d | |
Tai Groot | ae20f2b5ac | |
Zack Scholl | cd6eb1ba53 | |
Zack Scholl | 8454008b45 | |
Zack | fce4629120 | |
Tai Groot | fd0db2ad55 | |
Zack | 7f9688c7a1 | |
IrishMaestro | f1e15a54ee | |
Zack Scholl | 0f7f449d7f | |
Zack Scholl | b27982a742 | |
Zack Scholl | 30f5a3e84e | |
Zack Scholl | 786fc8d34f | |
Zack | 8de4508876 | |
VRDighe | e640e4fce0 | |
VRDighe | 4f8b2aba9b | |
Zack | 1517767129 | |
Zack Scholl | 3dedd41557 | |
Zack | 69f939c0cd | |
Zack | cd5872bd2d | |
cui fliter | 1c6583f925 | |
chavacava | 40ac320261 | |
Zack Scholl | 1851327df7 | |
Zack Scholl | 0e93f1e285 | |
Zack Scholl | 7e0814a57e | |
Zack Scholl | c6bcb79928 | |
Zack | 134673691e | |
Stefin | d226ba536e | |
Stefin | b50fe88474 | |
Stefin | 37ae453ff7 | |
Stefin | ee772c4cec | |
Stefin | ed030375e5 | |
Stefin | ad36e21051 | |
Stefin | 4ea9a96d88 | |
Stefin | f0f9b80bdf | |
Zack | 7a0c0a8200 | |
Zack Scholl | 5270755c15 | |
Zack Scholl | b7e4a73c27 | |
Zack Scholl | da5d19ef28 | |
Zack | c68cfcea8a | |
DasSkelett | 38ed8ecc3c | |
Tadej Janež | a5d3e00f2b | |
Zack Scholl | 9de06a6bf9 | |
Zack Scholl | a6a3a57361 | |
Zack | 148c1a6cdd | |
Zack Scholl | 817905cf5a | |
Zack Scholl | 6158b42ad1 | |
Zack Scholl | 80539f27c3 | |
Zack | 35652e60a3 | |
RCL98 | 2ad8b1f1ce | |
RCL98 | b316c0159f | |
RCL98 | f78ee15605 | |
iulius98 | 15d0209a29 | |
Zack | 386c99d057 | |
Zack Scholl | fd99a405fd | |
Zack Scholl | 54f52e5427 | |
Zack Scholl | ec949888d8 | |
Zack Scholl | 0d52ead66e | |
Zack Scholl | b715993435 | |
Zack Scholl | 7e817e1cbf | |
Zack | 4abdbf1ad2 | |
Zack Scholl | 087c22d833 | |
Zack Scholl | 789b5bf607 | |
Davide Cavalca | e42f81a404 | |
Zack Scholl | 20bf7dd91d | |
Zack Scholl | 4a8c19b115 | |
Zack Scholl | a1a17ce6f7 | |
Zack Scholl | 00c4d248fd | |
Zack Scholl | 073196ccd2 | |
Zack Scholl | 635b362ca0 | |
Zack Scholl | 53f35c1da0 | |
Zack | 030ed2a03d | |
Zack Scholl | c28d731a66 | |
Zack Scholl | ef6683b550 | |
Zack | f6a75c8f53 | |
RCL98 | 7c1a59c102 | |
iulius98 | 32a188fa85 | |
RCL98 | ce8fb796b3 | |
Zack | 1bd705d2e1 | |
iulius98 | 1a635de69c | |
Zack | 353ae0db16 | |
Abhishek | 9f66842322 | |
Zack Scholl | 94c0f66a26 | |
Zack Scholl | b0e7d4d5df | |
Zack Scholl | 9b5252d54c | |
Zack Scholl | 1d3c822ef3 | |
Zack | df78f3333d | |
voocel | b45e625298 | |
Zack Scholl | 7390e7ed45 | |
Zack | f210ef8877 | |
Amey Shrivastava | 2964ede174 | |
Zack Scholl | cc837fa863 | |
Zack Scholl | 87bfdf11a8 | |
Zack | c30492609e | |
Zack | 2f733891db | |
Zack | 3eb66284e0 | |
Eng Zer Jun | 1645759742 | |
Eng Zer Jun | da8beb65bf | |
Hinux Chau | d30433658b | |
Marcel Battista | d372fa1fa4 | |
Marcel Battista | 8ff3fb9b89 | |
Zack | 696479024e | |
Zack | 9dfa0284fa | |
Zack Scholl | a12f374aba | |
Zack | 8ee1825f84 | |
Zack | f99c33d794 | |
Maxim Baz | 417b371454 | |
jolheiser | 2ffb20201c | |
Zack Scholl | 89efd38c10 | |
Zack Scholl | 49f50a53d5 | |
Zack Scholl | 5d9afe03ef | |
Zack Scholl | 1637765aad | |
Zack Scholl | ce628ea604 | |
Zack Scholl | ff4307c3c6 | |
Zack Scholl | e5f59fa0f9 | |
Zack Scholl | 04662df347 | |
Zack Scholl | 4ea66fbd18 | |
Zack Scholl | d96c98f4ce | |
Zack Scholl | 31db6a76e5 | |
Zack Scholl | a996f3c4f4 | |
Zack Scholl | 4334c870f5 | |
Zack Scholl | 2b1367a09b | |
Zack Scholl | add1650cbd | |
Zack Scholl | 04f2dfac97 | |
Zack Scholl | 060ce516b1 | |
Zack Scholl | 78b6f2d858 | |
Zack | d77c83ce09 | |
Zack | a34d29befc | |
Zack | 914d0c98a8 | |
Zack | 55cb35d12b | |
Brandon Ingalls | 860a75fee6 | |
jolheiser | e380c7b1f1 | |
jolheiser | 2381f26c61 | |
KallyDev | de454bbf5a | |
Zack Scholl | e19e06a652 | |
Zack | 6deafaadae | |
Austin Dworaczyk Wiltshire | ef3953a586 | |
Zack Scholl | 1d3fdffed6 | |
Zack Scholl | 94b2aff637 | |
Zack | 9ce2321d01 | |
Charlie Jonas | d75530b78b | |
Charlie Jonas | f64f68d5a9 | |
Charlie Jonas | be7705efc3 | |
Charlie Jonas | df2e29b74d | |
Zack Scholl | b73fdff702 | |
Zack Scholl | 664936feb2 | |
Zack Scholl | 4b1eb6519c | |
Zack Scholl | 86aeed93db | |
Zack Scholl | 7993e73ac2 | |
Zack Scholl | d922808fd8 | |
Zack Scholl | 8d22737fd3 | |
Zack Scholl | d442755814 | |
Zack Scholl | 0afdf71305 | |
Zack Scholl | ecf3eb872e | |
Zack Scholl | 646c879edc | |
Zack Scholl | cbe9cbe32a | |
Zack Scholl | 4796404b7e | |
Zack Scholl | 3e2c88c5bc | |
Zack Scholl | 0124356cd7 | |
Zack | f2ef6f19a5 | |
Niko Köser | 5e0d6522b0 | |
Niko Köser | c72aaf63cb | |
Zack Scholl | a9e77673f4 | |
Zack Scholl | aa93d3e30f | |
Zack Scholl | 15a19e1998 | |
Zack Scholl | e1ccb58d8f | |
Zack Scholl | d0ebb7ad38 | |
Zack Scholl | 2bfbacf972 | |
Zack Scholl | e29b4c47fd | |
Zack Scholl | b4deedf367 | |
Zack Scholl | 68e507dbba | |
Zack Scholl | 66f30c0565 | |
Zack Scholl | 340bafba3d | |
Zack Scholl | e47776bb10 | |
Zack | 422f099e5e | |
Zack Scholl | bd4886ae3c | |
Markus Wamser | d5fd2060dd | |
Zack Scholl | 7c28279a58 | |
Zack Scholl | 27b7cdbf12 | |
Zack Scholl | 24fb8746a4 | |
Zack | 45cb545a82 | |
Alvar Penning | 8f5c73837a | |
Zack | b90f9329ca | |
Craeckie | 968950f783 | |
Zack Scholl | 5b0883e1fe | |
Zack Scholl | 420030998f | |
Zack Scholl | de2bcf114c | |
Zack Scholl | ab5ae5cbb6 | |
Zack Scholl | 94b3dba034 | |
Zack | 1a47543be9 | |
Zack Scholl | 407e162ec2 | |
Zack | 35106d4dbe | |
Zack Scholl | d6af319ad8 | |
Zack Scholl | 85e7576311 | |
Zack Scholl | 7ac7be37af | |
Zack Scholl | 669aeb377a | |
Zack Scholl | b655afb533 | |
Zack Scholl | e9949ce3d7 | |
Zack Scholl | 91a3dd5866 | |
Zack Scholl | 026487a833 | |
Zack Scholl | f8ef096784 | |
Zack Scholl | 0c2089bd4c | |
Zack Scholl | 5ad68b9ced | |
Zack Scholl | 9286b3c965 | |
Zack Scholl | c1edf24338 | |
Zack Scholl | d38fc18390 | |
Zack Scholl | f4cedd1e91 | |
Zack Scholl | fc66c3b91f | |
Zack Scholl | 2ac7d4f31f | |
Zack Scholl | a96762b942 | |
Zack Scholl | 0a0f7a03d7 | |
Zack Scholl | 8148e191ae | |
Zack Scholl | 25a985464a | |
Zack | 333ece706e | |
Zack Scholl | daf10395a3 | |
Zack Scholl | 9231b1a661 | |
Zack Scholl | 3e56d4cdb9 | |
Zack Scholl | cec39ba2ce | |
Zack Scholl | 7a997156ed | |
Zack Scholl | c02b4f1256 | |
Zack Scholl | babfd5f35f | |
Zack Scholl | be5ceae8c7 | |
Zack Scholl | 6be4080f05 | |
Zack Scholl | b0693751c1 | |
Zack Scholl | 8250a39534 | |
Zack Scholl | 2131e99826 | |
Zack Scholl | 628043b228 | |
Zack Scholl | 362a30e5e7 | |
Zack | 828de41d6c | |
Tobias Dussa | 876ce5764b | |
Zack Scholl | 59b287df7f | |
Zack | 3fd125178f | |
Zack | 136faac448 | |
Zack | 85a0bfb898 | |
Missu | 4718f3897c | |
Sting Alleman | 068d5c1031 | |
Zack Scholl | 4dfa1a9236 | |
Zack Scholl | 6ba6ec9d32 | |
Zack Scholl | c373b38b59 | |
Zack Scholl | f2af5b7256 | |
Zack Scholl | 483f840401 | |
Zack Scholl | ff12c33e7f | |
Zack Scholl | e4cecf670b | |
Zack Scholl | 94e0e3ca67 | |
Zack Scholl | a9d582cc6a | |
k3b | ab9396937c | |
Matej Kafka | c1e546ede6 | |
Zack Scholl | 63ba28c9b0 | |
Zack | 6caf72df82 | |
Jona | 2273438373 | |
Zack Scholl | 50a0d86e3d | |
Zack Scholl | 50aa24d86d | |
Zack Scholl | 674bff7a13 | |
Zack Scholl | 757ed180fb | |
Zack Scholl | 76648a2926 | |
Zack Scholl | 0b76edc362 | |
Zack Scholl | c39061c7fc | |
Zack Scholl | fb2723d80b | |
Zack Scholl | f84341a197 | |
Zack | 6e9156d49c | |
Zack | cd6d9a5aa0 | |
Lars Lehtonen | 3e5b876a32 | |
Lars Lehtonen | a9487a332c | |
Zack Scholl | de924f6a4f | |
Zack Scholl | 03a27b6683 | |
Zack Scholl | 0b719b9b77 | |
Zack Scholl | fc34d21cb8 | |
Zack Scholl | bb05d48fe9 | |
Zack | 949480517f | |
Heiko Reese | a20ebd7120 | |
Zack Scholl | d49bb8632b | |
Zack Scholl | 10e57c3f97 | |
Zack Scholl | be95662e35 | |
Zack Scholl | 105fb9a3ce | |
Zack Scholl | 30783ce79c | |
Zack Scholl | a46f09ee1c | |
Zack Scholl | cdabf629b6 | |
Zack Scholl | 40cda1bed7 | |
Zack Scholl | 92ab62d03e | |
Zack Scholl | 31012b8f7d | |
Zack Scholl | 4b0d01d8f0 | |
Zack Scholl | 648eb6c71d | |
Zack Scholl | b19a028aea | |
Zack | 9588f2b4a4 | |
Zack Scholl | cd2802b8b5 | |
Zack Scholl | c5ff55a193 | |
Zack Scholl | 1cf309faf2 | |
Zack | 8dc5bd6e04 | |
leko | 37f09049a4 | |
Zack Scholl | eaa18cd502 | |
Zack Scholl | b9d461e5df | |
Zack Scholl | 89ea0166fa | |
Zack Scholl | 77fd78e408 | |
Zack Scholl | edeb7c8886 | |
Zack Scholl | 4481fe2336 | |
Zack | ef43873c3b | |
Zack Scholl | 0bec0b26ab | |
Zack | 681c824ef4 | |
Zack Scholl | 950b6f83d9 | |
Zack Scholl | 7f5d704a26 | |
Zack | a6a8ab71ce | |
Zack | de56d2f6dd | |
Zack Scholl | 2f96b77b73 | |
Zack | 0548c2e45f | |
Niko Köser | 0b60fef246 | |
Niko Köser | 3ccd4d07e9 | |
Benjamin Wen | b5f1b35213 | |
Spaceface16518 | 90eda5639f | |
Zack Scholl | 77d0c7ae2e | |
Zack | 493eb075f1 | |
Spaceface16518 | 247d2b18ec | |
Spaceface16518 | 6b8a14d831 | |
Amrit | 743b2c1bf4 | |
Peter | 17f3df7e36 | |
Zack Scholl | dab683c9dc | |
Zack | e829ca0ff4 | |
Zack Scholl | e27c32cf6e | |
Zack Scholl | a2b243ad94 | |
Zack Scholl | 05640cd49f | |
Zack Scholl | 037dbbd4e9 | |
Zack | 0994e4eda5 | |
Zack | 235f065a9c | |
vzlcn0 | 52bc6312e1 | |
Marcus | d735cca970 | |
Zack Scholl | af5ba6ead8 | |
Zack Scholl | 5978896936 | |
Zack Scholl | 2ffafd4607 | |
Zack Scholl | e7ed4fc05f | |
Zack Scholl | 1da2f58a53 | |
Zack Scholl | 284af7fc7a | |
Zack Scholl | dbc4a08ad0 | |
Zack Scholl | c55e9be0ef | |
Zack Scholl | 10ed62cfd5 | |
Zack Scholl | cdb1981358 | |
Zack Scholl | a45e66733b | |
Zack Scholl | d112f43f63 | |
Zack Scholl | 05c82809c6 | |
Zack Scholl | 6ad8e08b0c | |
Zack Scholl | 6c86188564 | |
Zack Scholl | 4663df1059 | |
Zack Scholl | 61ffdc61f3 | |
Zack Scholl | 1a242711cc | |
Zack Scholl | 3129b097af |
|
@ -2,28 +2,30 @@
|
|||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview). -->
|
||||
|
||||
**Please try to [download the latest version](https://github.com/schollz/croc/releases/latest) of croc before reporting a bug!**
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
<!-- Please try to download latest, https://github.com/schollz/croc/releases/latest of croc before reporting a bug! -->
|
||||
|
||||
**To Reproduce**
|
||||
## Describe the bug
|
||||
|
||||
|
||||
## To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
**Version**
|
||||
Check `croc -version` and report it.
|
||||
## Expected behaviour
|
||||
|
||||
|
||||
## Version
|
||||
|
||||
|
||||
## Additional context
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -0,0 +1,18 @@
|
|||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Go unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- run: go version
|
||||
- run: go test -v ./...
|
|
@ -0,0 +1,64 @@
|
|||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: CD
|
||||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the main branch
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: schollz/croc
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm,linux/arm64,linux/386
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
@ -0,0 +1,123 @@
|
|||
name: Make release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- name: Build Windows 7
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows7-64bit.zip croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows7-32bit.zip croc.exe
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.21'
|
||||
- name: Prepare source tarball
|
||||
run: |
|
||||
git clone -b ${{ github.event.release.name }} --depth 1 https://github.com/schollz/croc croc-${{ github.event.release.name }}
|
||||
cd croc-${{ github.event.release.name }} && go mod tidy && go mod vendor
|
||||
cd .. && tar -czvf croc_${{ github.event.release.name }}_src.tar.gz croc-${{ github.event.release.name }}
|
||||
- name: Build files
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-64bit.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-32bit.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-ARM.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-ARM64.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-32bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARM.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_macOS-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz croc LICENSE
|
||||
- name: Create checksums.txt
|
||||
run: |
|
||||
touch croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_src.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-64bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-32bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-ARM.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-ARM64.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows7-64bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows7-32bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-32bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-ARM.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_macOS-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
croc_${{ github.event.release.name }}_checksums.txt
|
||||
croc_${{ github.event.release.name }}_src.tar.gz
|
||||
croc_${{ github.event.release.name }}_Windows-64bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows-32bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows-ARM.zip
|
||||
croc_${{ github.event.release.name }}_Windows-ARM64.zip
|
||||
croc_${{ github.event.release.name }}_Windows7-64bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows7-32bit.zip
|
||||
croc_${{ github.event.release.name }}_Linux-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-32bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-ARM.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_macOS-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz
|
|
@ -0,0 +1,27 @@
|
|||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '19 12 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
|
@ -0,0 +1,14 @@
|
|||
name: Publish to Winget
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: schollz.croc
|
||||
installers-regex: '_Windows-\w+\.zip$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
|
@ -6,3 +6,8 @@ bash_autocomplete
|
|||
dist
|
||||
bin
|
||||
croc-stdin*
|
||||
|
||||
.idea/
|
||||
.vscode/
|
||||
src/utils/bigfile.test
|
||||
test1/
|
||||
|
|
|
@ -21,6 +21,8 @@ build:
|
|||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: freebsd
|
||||
goarch: arm
|
||||
goarm:
|
||||
- 7
|
||||
nfpms:
|
||||
|
@ -72,7 +74,7 @@ archives:
|
|||
|
||||
brews:
|
||||
-
|
||||
github:
|
||||
tap:
|
||||
owner: schollz
|
||||
name: homebrew-tap
|
||||
folder: Formula
|
||||
|
@ -91,4 +93,8 @@ scoop:
|
|||
homepage: "https://schollz.com/software/croc/"
|
||||
description: "croc is a tool that allows any two computers to simply and securely transfer files and folders."
|
||||
license: MIT
|
||||
|
||||
announce:
|
||||
twitter:
|
||||
# Wether its enabled or not.
|
||||
# Defaults to false.
|
||||
enabled: false
|
12
.travis.yml
12
.travis.yml
|
@ -10,12 +10,12 @@ install: true
|
|||
|
||||
script:
|
||||
- env GO111MODULE=on go build -v
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/compress
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/croc
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/crypt
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/tcp
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/utils
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/comm
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/compress
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/croc
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/crypt
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/tcp
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/utils
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/comm
|
||||
|
||||
branches:
|
||||
except:
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
FROM golang:1.15-alpine as builder
|
||||
RUN apk add --no-cache git
|
||||
FROM golang:1.22-alpine as builder
|
||||
RUN apk add --no-cache git gcc musl-dev
|
||||
WORKDIR /go/croc
|
||||
COPY . .
|
||||
RUN go build -v
|
||||
RUN go build -v -ldflags="-s -w"
|
||||
|
||||
FROM alpine:latest
|
||||
FROM alpine:latest
|
||||
EXPOSE 9009
|
||||
EXPOSE 9010
|
||||
EXPOSE 9011
|
||||
EXPOSE 9012
|
||||
EXPOSE 9013
|
||||
COPY --from=builder /go/croc/croc /go/croc/croc-entrypoint.sh /
|
||||
USER nobody
|
||||
ENTRYPOINT ["/croc-entrypoint.sh"]
|
||||
CMD ["relay"]
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017-2020 Zack
|
||||
Copyright (c) 2017-2024 Zack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
110
README.md
110
README.md
|
@ -4,12 +4,11 @@
|
|||
src="https://user-images.githubusercontent.com/6550035/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg"
|
||||
width="408px" border="0" alt="croc">
|
||||
<br>
|
||||
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v8.6.5-brightgreen.svg?style=flat-square" alt="Version"></a>
|
||||
<a href="https://coveralls.io/github/schollz/croc"><img src="https://img.shields.io/badge/coverage-81%25-green.svg?style=flat-square" alt="Coverage"></a>
|
||||
<a href="https://travis-ci.org/schollz/croc"><img
|
||||
src="https://img.shields.io/travis/schollz/croc.svg?style=flat-square" alt="Build
|
||||
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v10.0.7-brightgreen.svg?style=flat-square" alt="Version"></a>
|
||||
<a href="https://github.com/schollz/croc/actions/workflows/ci.yml"><img
|
||||
src="https://github.com/schollz/croc/actions/workflows/ci.yml/badge.svg" alt="Build
|
||||
Status"></a>
|
||||
</p>
|
||||
<p align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
|
||||
|
||||
`croc` is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, *croc* is the only CLI file-transfer tool that does **all** of the following:
|
||||
|
||||
|
@ -22,7 +21,7 @@ Status"></a>
|
|||
- **ipv6-first** with ipv4 fallback
|
||||
- can **use proxy**, like tor
|
||||
|
||||
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6).
|
||||
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6) or read a [recent interview I did](https://console.substack.com/p/console-91).
|
||||
|
||||
![Example](src/install/customization.gif)
|
||||
|
||||
|
@ -31,75 +30,87 @@ For more information about `croc`, see [my blog post](https://schollz.com/softwa
|
|||
Download [the latest release for your system](https://github.com/schollz/croc/releases/latest), or install a release from the command-line:
|
||||
|
||||
```
|
||||
$ curl https://getcroc.schollz.com | bash
|
||||
curl https://getcroc.schollz.com | bash
|
||||
```
|
||||
|
||||
|
||||
On macOS you can install the latest release with [Homebrew](https://brew.sh/):
|
||||
|
||||
```
|
||||
$ brew install croc
|
||||
brew install croc
|
||||
```
|
||||
|
||||
On macOS you can also install the latest release with [MacPorts](https://macports.org/):
|
||||
|
||||
```
|
||||
$ sudo port selfupdate
|
||||
$ sudo port install croc
|
||||
sudo port selfupdate
|
||||
sudo port install croc
|
||||
```
|
||||
|
||||
On Windows you can install the latest release with [Scoop](https://scoop.sh/) or [Chocolatey](https://chocolatey.org):
|
||||
On Windows you can install the latest release with [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org), or [Winget](https://learn.microsoft.com/en-us/windows/package-manager/):
|
||||
|
||||
```
|
||||
$ scoop install croc
|
||||
scoop install croc
|
||||
```
|
||||
|
||||
```
|
||||
$ choco install croc
|
||||
choco install croc
|
||||
```
|
||||
|
||||
```
|
||||
winget install schollz.croc
|
||||
```
|
||||
|
||||
On Unix you can install the latest release with [Nix](https://nixos.org/nix):
|
||||
|
||||
```
|
||||
$ nix-env -i croc
|
||||
nix-env -i croc
|
||||
```
|
||||
|
||||
|
||||
On Alpine Linux you have to install dependencies first:
|
||||
|
||||
```
|
||||
apk add bash coreutils
|
||||
wget -qO- https://getcroc.schollz.com | bash
|
||||
```
|
||||
|
||||
On Arch Linux you can install the latest release with `pacman`:
|
||||
|
||||
```
|
||||
$ pacman -S croc
|
||||
pacman -S croc
|
||||
```
|
||||
|
||||
On Ubuntu you can install with `snap`:
|
||||
On Fedora you can install with `dnf`:
|
||||
|
||||
```
|
||||
$ snap install croc
|
||||
dnf install croc
|
||||
```
|
||||
|
||||
On Gentoo you can install with `portage`:
|
||||
```
|
||||
$ emerge net-misc/croc
|
||||
emerge net-misc/croc
|
||||
```
|
||||
|
||||
On Termux you can install with `pkg`:
|
||||
|
||||
```
|
||||
$ pkg install croc
|
||||
pkg install croc
|
||||
```
|
||||
|
||||
On FreeBSD you can install with `pkg`:
|
||||
|
||||
```
|
||||
$ pkg install croc
|
||||
pkg install croc
|
||||
```
|
||||
|
||||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.12+):
|
||||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
|
||||
|
||||
```
|
||||
$ GO111MODULE=on go get -v github.com/schollz/croc/v8
|
||||
go install github.com/schollz/croc/v10@latest
|
||||
```
|
||||
|
||||
On Android there is a 3rd party F-Droid app [available to download](https://f-droid.org/en/packages/com.github.howeyc.crocgui/).
|
||||
|
||||
|
||||
## Usage
|
||||
|
@ -115,7 +126,7 @@ Code is: code-phrase
|
|||
Then to receive the file (or folder) on another computer, you can just do
|
||||
|
||||
```
|
||||
$ croc code-phrase
|
||||
croc code-phrase
|
||||
```
|
||||
|
||||
The code phrase is used to establish password-authenticated key agreement ([PAKE](https://en.wikipedia.org/wiki/Password-authenticated_key_agreement)) which generates a secret key for the sender and recipient to use for end-to-end encryption.
|
||||
|
@ -124,24 +135,33 @@ There are a number of configurable options (see `--help`). A set of options (lik
|
|||
|
||||
### Custom code phrase
|
||||
|
||||
You can send with your own code phrase (must be more than 4 characters).
|
||||
You can send with your own code phrase (must be more than 6 characters).
|
||||
|
||||
```
|
||||
$ croc send --code [code-phrase] [file(s)-or-folder]
|
||||
croc send --code [code-phrase] [file(s)-or-folder]
|
||||
```
|
||||
|
||||
### Allow overwriting without prompt
|
||||
|
||||
By default, croc will prompt whether to overwrite a file. You can automatically overwrite files by using the `--overwrite` flag (recipient only). For example, receive a file to automatically overwrite:
|
||||
|
||||
```
|
||||
croc --yes --overwrite <code>
|
||||
```
|
||||
|
||||
|
||||
### Use pipes - stdin and stdout
|
||||
|
||||
You can pipe to `croc`:
|
||||
|
||||
```
|
||||
$ cat [filename] | croc send
|
||||
cat [filename] | croc send
|
||||
```
|
||||
|
||||
In this case `croc` will automatically use the stdin data and send and assign a filename like "croc-stdin-123456789". To receive to `stdout` at you can always just use the `--yes` will automatically approve the transfer and pipe it out to `stdout`.
|
||||
|
||||
```
|
||||
$ croc --yes [code-phrase] > out
|
||||
croc --yes [code-phrase] > out
|
||||
```
|
||||
|
||||
All of the other text printed to the console is going to `stderr` so it will not interfere with the message going to `stdout`.
|
||||
|
@ -152,7 +172,7 @@ All of the other text printed to the console is going to `stderr` so it will not
|
|||
Sometimes you want to send URLs or short text. In addition to piping, you can easily send text with `croc`:
|
||||
|
||||
```
|
||||
$ croc send --text "hello world"
|
||||
croc send --text "hello world"
|
||||
```
|
||||
|
||||
This will automatically tell the receiver to use `stdout` when they receive the text so it will be displayed.
|
||||
|
@ -163,7 +183,25 @@ This will automatically tell the receiver to use `stdout` when they receive the
|
|||
You can use a proxy as your connection to the relay by adding a proxy address with `--socks5`. For example, you can send via a tor relay:
|
||||
|
||||
```
|
||||
$ croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
||||
croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
||||
```
|
||||
|
||||
### Change encryption curve
|
||||
|
||||
You can choose from several different elliptic curves to use for encryption by using the `--curve` flag. Only the recipient can choose the curve. For example, receive a file using the P-521 curve:
|
||||
|
||||
```
|
||||
croc --curve p521 <codephrase>
|
||||
```
|
||||
|
||||
Available curves are P-256, P-348, P-521 and SIEC. P-256 is the default curve.
|
||||
|
||||
### Change hash algorithm
|
||||
|
||||
You can choose from several different hash algorithms. The default is the `xxhash` algorithm which is fast and thorough. If you want to optimize for speed you can use the `imohash` algorithm which is even faster, but since it samples files (versus reading the whole file) it can mistakenly determine that a file is the same on the two computers transferring - though this is only a problem if you are syncing files versus sending a new file to a computer.
|
||||
|
||||
```
|
||||
croc send --hash imohash SOMEFILE
|
||||
```
|
||||
|
||||
### Self-host relay
|
||||
|
@ -171,15 +209,15 @@ $ croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
|||
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
|
||||
|
||||
```
|
||||
$ croc relay
|
||||
croc relay
|
||||
```
|
||||
|
||||
By default it uses TCP ports 9009-9013. Make sure to open those up. You can customized the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer.
|
||||
By default it uses TCP ports 9009-9013. Make sure to open those up. You can customize the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer.
|
||||
|
||||
You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own.
|
||||
|
||||
```
|
||||
$ croc --relay "myrelay.example.com:9009" send [filename]
|
||||
croc --relay "myrelay.example.com:9009" send [filename]
|
||||
```
|
||||
|
||||
Note, when sending, you only need to include the first port (the communication port). The subsequent ports for data transfer will be transmitted back to the user from the relay.
|
||||
|
@ -190,13 +228,13 @@ If it's easier you can also run a relay with Docker:
|
|||
|
||||
|
||||
```
|
||||
$ docker run -d -p 9009-9013:9009-9013 -e CROC_PASS='YOURPASSWORD' schollz/croc
|
||||
docker run -d -p 9009-9013:9009-9013 -e CROC_PASS='YOURPASSWORD' schollz/croc
|
||||
```
|
||||
|
||||
Be sure to include the password for the relay otherwise any requests will be rejected.
|
||||
|
||||
```
|
||||
$ croc --pass YOURPASSWORD --relay "myreal.example.com:9009" send [filename]
|
||||
croc --pass YOURPASSWORD --relay "myreal.example.com:9009" send [filename]
|
||||
```
|
||||
|
||||
Note: when including `--pass YOURPASSWORD` you can instead pass a file with the password, e.g. `--pass FILEWITHPASSWORD`.
|
||||
|
@ -207,6 +245,6 @@ MIT
|
|||
|
||||
## Acknowledgements
|
||||
|
||||
`croc` has gone through many iterations, and I am awed by all the great contributions! If you feel like contributing, in any way, by all means you can send an Issue, a PR, ask a question, or tweet me ([@yakczar](http://ctt.ec/Rq054)).
|
||||
`croc` has gone through many iterations, and I am awed by all the great contributions! If you feel like contributing, in any way, by all means you can send an Issue, a PR, or ask a question.
|
||||
|
||||
Thanks [@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole), [@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28), [@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). Finally thanks for making pull requests [@maximbaz](https://github.com/maximbaz), [@meyermarcel](https://github.com/meyermarcel), [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu), [@nicolashardy](https://github.com/nicolashardy), [@fbartels](https://github.com/fbartels), [@rkuprov](https://github.com/rkuprov), [@xenrox](https://github.com/xenrox) and [Ipar](https://github.com/lpar)!
|
||||
Thanks [@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole), [@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28), [@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). Finally thanks for making pull requests [@maximbaz](https://github.com/maximbaz), [@meyermarcel](https://github.com/meyermarcel), [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu), [@nicolashardy](https://github.com/nicolashardy), [@fbartels](https://github.com/fbartels), [@rkuprov](https://github.com/rkuprov), [@hreese](https://github.com/hreese), [@xenrox](https://github.com/xenrox) and [Ipar](https://github.com/lpar)!
|
||||
|
|
|
@ -4,7 +4,7 @@ After=network.target
|
|||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=nobody
|
||||
DynamicUser=yes
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
ExecStart=/usr/bin/croc relay
|
||||
|
||||
|
|
52
go.mod
52
go.mod
|
@ -1,26 +1,42 @@
|
|||
module github.com/schollz/croc/v8
|
||||
module github.com/schollz/croc/v10
|
||||
|
||||
go 1.13
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/kalafut/imohash v1.0.0
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/kalafut/imohash v1.0.3
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
|
||||
github.com/schollz/cli/v2 v2.2.1
|
||||
github.com/schollz/logger v1.2.0
|
||||
github.com/schollz/mnemonicode v1.0.1
|
||||
github.com/schollz/pake/v2 v2.0.4
|
||||
github.com/schollz/peerdiscovery v1.6.0
|
||||
github.com/schollz/progressbar/v3 v3.6.2
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.7 // indirect
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d
|
||||
github.com/schollz/pake/v3 v3.0.5
|
||||
github.com/schollz/peerdiscovery v1.7.3
|
||||
github.com/schollz/progressbar/v3 v3.14.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/time v0.5.0
|
||||
)
|
||||
|
||||
require github.com/minio/highwayhash v1.0.2
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/text v0.15.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
153
go.sum
153
go.sum
|
@ -1,93 +1,132 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kalafut/imohash v1.0.0 h1:LgCJ+p/BwM2HKpOxFopkeddpzVCfm15EtXMroXD1SYE=
|
||||
github.com/kalafut/imohash v1.0.0/go.mod h1:c3RHT80ZAp5C/aYgQI92ZlrOymqkZnRDprU87kg75HI=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kalafut/imohash v1.0.3 h1:p9c61km8+6ZMqKRnERwdoxp/CztrdLNEbpsyGgf+A4M=
|
||||
github.com/kalafut/imohash v1.0.3/go.mod h1:6cn9lU0Sj8M4eu9UaQm1kR/5y3k/ayB68yntRhGloL4=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI=
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
|
||||
github.com/schollz/cli/v2 v2.2.1 h1:ou22Mj7ZPjrKz+8k2iDTWaHskEEV5NiAxGrdsCL36VU=
|
||||
github.com/schollz/cli/v2 v2.2.1/go.mod h1:My6bfphRLZUhZdlFUK8scAxMWHydE7k4s2ed2Dtnn+s=
|
||||
github.com/schollz/logger v1.2.0 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
|
||||
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
||||
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
|
||||
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||
github.com/schollz/pake/v2 v2.0.4 h1:C49hvs+4J+Vl375IKzyC/RDYzGO1q4zPfuWtr3ehl88=
|
||||
github.com/schollz/pake/v2 v2.0.4/go.mod h1:y4xMfWSvjSFF2I0RRWZl1o/vskyh9dmKmmvFUXTsyRg=
|
||||
github.com/schollz/peerdiscovery v1.6.0 h1:Ep8TWQVOBIsXyCAwf04iV+jpi9YeuiHB7Z1Sieu6y8o=
|
||||
github.com/schollz/peerdiscovery v1.6.0/go.mod h1:hSU7N/NkfNH6AZwU/WBcDZtMABVbTfAWk/XD3XKxN+s=
|
||||
github.com/schollz/progressbar/v2 v2.15.0 h1:dVzHQ8fHRmtPjD3K10jT3Qgn/+H+92jhPrhmxIJfDz8=
|
||||
github.com/schollz/progressbar/v2 v2.15.0/go.mod h1:UdPq3prGkfQ7MOzZKlDRpYKcFqEMczbD7YmbPgpzKMI=
|
||||
github.com/schollz/progressbar/v3 v3.6.2 h1:y4mDZHjSsqYYZJEMCoLJEfL/CNnqify3+QHYHVt34G8=
|
||||
github.com/schollz/progressbar/v3 v3.6.2/go.mod h1:3B25e7a0JCjz1joGNAk7E2TnSr0x+aYQ0sZPs8fPwC0=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d h1:3zCjdgCJbo9Fot3UoqZkpGiDgT6Nf+iUnOsDEJQay+c=
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||
github.com/schollz/pake/v3 v3.0.5 h1:MnZVdI987lkjln9BSx/zUb724TZISa2jbO+dPj6BvgQ=
|
||||
github.com/schollz/pake/v3 v3.0.5/go.mod h1:OGbG6htRwSKo6V8R5tg61ufpFmZM1b/PrrSp6g2ZLLc=
|
||||
github.com/schollz/peerdiscovery v1.7.3 h1:/pt1G0rZ80fSPoI/FgGC5P7MxpkRXD6u0pe6PJbYcIE=
|
||||
github.com/schollz/peerdiscovery v1.7.3/go.mod h1:mVlPNJ5DWbMi52VzpXxGbqXKdFANx3qw0Jsp3EQMCrE=
|
||||
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
|
||||
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094 h1:tZWtuLE+LbUwT4OP1oWBSB9zXA8qmQ5qEm4kV9R72oo=
|
||||
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 h1:sDWDZkwYqX0jvLWstKzFwh+pYhQNaVg65BgSkCP/f7U=
|
||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
||||
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
|
||||
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70 h1:Z6x4N9mAi4oF0TbHweCsH618MO6OI6UFgV0FP5n0wBY=
|
||||
golang.org/x/net v0.0.0-20201022231255-08b38378de70/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd h1:WgqgiQvkiZWz7XLhphjt2GI2GcGCTIZs9jqXMWmH+oc=
|
||||
golang.org/x/sys v0.0.0-20201022201747-fb209a7c41cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/tylerb/is.v1 v1.1.2 h1:AB/MANFml2ySf+adwcinvajyHvsYltAOD+rb/8njfSU=
|
||||
gopkg.in/tylerb/is.v1 v1.1.2/go.mod h1:9yQB2tyIhZ5oph6Kk5Sq7cJMd9c5Jpa1p3hr9kxzPqo=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
6
main.go
6
main.go
|
@ -5,9 +5,9 @@ package main
|
|||
//go:generate git tag -af v$VERSION -m "v$VERSION"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/schollz/croc/v8/src/cli"
|
||||
"github.com/schollz/croc/v10/src/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
@ -28,6 +28,6 @@ func main() {
|
|||
// }
|
||||
// }()
|
||||
if err := cli.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
log.Fatalln(err)
|
||||
}
|
||||
}
|
||||
|
|
410
src/cli/cli.go
410
src/cli/cli.go
|
@ -5,21 +5,24 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/schollz/cli/v2"
|
||||
"github.com/schollz/croc/v8/src/comm"
|
||||
"github.com/schollz/croc/v8/src/croc"
|
||||
"github.com/schollz/croc/v8/src/models"
|
||||
"github.com/schollz/croc/v8/src/tcp"
|
||||
"github.com/schollz/croc/v8/src/utils"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/croc"
|
||||
"github.com/schollz/croc/v10/src/models"
|
||||
"github.com/schollz/croc/v10/src/tcp"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/schollz/mnemonicode"
|
||||
"github.com/schollz/pake/v3"
|
||||
)
|
||||
|
||||
// Version specifies the version
|
||||
|
@ -33,7 +36,7 @@ func Run() (err error) {
|
|||
app := cli.NewApp()
|
||||
app.Name = "croc"
|
||||
if Version == "" {
|
||||
Version = "v8.6.5-8d430b6"
|
||||
Version = "v10.0.7"
|
||||
}
|
||||
app.Version = Version
|
||||
app.Compiled = time.Now()
|
||||
|
@ -41,6 +44,15 @@ func Run() (err error) {
|
|||
app.UsageText = `Send a file:
|
||||
croc send file.txt
|
||||
|
||||
-git to respect your .gitignore
|
||||
Send multiple files:
|
||||
croc send file1.txt file2.txt file3.txt
|
||||
or
|
||||
croc send *.jpg
|
||||
|
||||
Send everything in a folder:
|
||||
croc send example-folder-name
|
||||
|
||||
Send a file with a custom code:
|
||||
croc send --code secret-code file.txt
|
||||
|
||||
|
@ -49,35 +61,38 @@ func Run() (err error) {
|
|||
app.Commands = []*cli.Command{
|
||||
{
|
||||
Name: "send",
|
||||
Usage: "send a file (see options with croc send -h)",
|
||||
Description: "send a file over the relay",
|
||||
ArgsUsage: "[filename]",
|
||||
Usage: "send file(s), or folder (see options with croc send -h)",
|
||||
Description: "send file(s), or folder, over the relay",
|
||||
ArgsUsage: "[filename(s) or folder]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "zip", Usage: "zip folder before sending"},
|
||||
&cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"},
|
||||
&cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"},
|
||||
&cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"},
|
||||
&cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
|
||||
&cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
|
||||
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
|
||||
&cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"},
|
||||
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
|
||||
&cli.IntFlag{Name: "transfers", Value: 4, Usage: "number of ports to use for transfers"},
|
||||
},
|
||||
HelpName: "croc send",
|
||||
Action: func(c *cli.Context) error {
|
||||
return send(c)
|
||||
},
|
||||
Action: send,
|
||||
},
|
||||
{
|
||||
Name: "relay",
|
||||
Usage: "start your own relay (optional)",
|
||||
Description: "start relay",
|
||||
HelpName: "croc relay",
|
||||
Action: func(c *cli.Context) error {
|
||||
return relay(c)
|
||||
},
|
||||
Action: relay,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{Name: "host", Usage: "host of the relay"},
|
||||
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.BoolFlag{Name: "internal-dns", Usage: "use a built-in DNS stub resolver rather than the host operating system"},
|
||||
&cli.BoolFlag{Name: "classic", Usage: "toggle between the classic mode (insecure due to local attack vector) and new mode (secure)"},
|
||||
&cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
|
||||
&cli.BoolFlag{Name: "debug", Usage: "toggle debug mode"},
|
||||
&cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
|
||||
|
@ -85,11 +100,18 @@ func Run() (err error) {
|
|||
&cli.BoolFlag{Name: "no-compress", Usage: "disable compression"},
|
||||
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
|
||||
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
|
||||
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
|
||||
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite"},
|
||||
&cli.BoolFlag{Name: "testing", Usage: "flag for testing purposes"},
|
||||
&cli.StringFlag{Name: "curve", Value: "p256", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
|
||||
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
||||
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
||||
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
|
||||
&cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
|
||||
&cli.StringFlag{Name: "pass", Value: models.DEFAULT_PASSPHRASE, Usage: "password for the relay", EnvVars: []string{"CROC_PASS"}},
|
||||
&cli.StringFlag{Name: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
|
||||
&cli.StringFlag{Name: "connect", Value: "", Usage: "add a http proxy", EnvVars: []string{"HTTP_PROXY"}},
|
||||
&cli.StringFlag{Name: "throttleUpload", Value: "", Usage: "Throttle the upload speed e.g. 500k"},
|
||||
}
|
||||
app.EnableBashCompletion = true
|
||||
app.HideHelp = false
|
||||
|
@ -104,6 +126,61 @@ func Run() (err error) {
|
|||
return true
|
||||
}
|
||||
|
||||
// check if "classic" is set
|
||||
classicFile := getClassicConfigFile(true)
|
||||
classicInsecureMode := utils.Exists(classicFile)
|
||||
if c.Bool("classic") {
|
||||
if classicInsecureMode {
|
||||
// classic mode not enabled
|
||||
fmt.Print(`Classic mode is currently ENABLED.
|
||||
|
||||
Disabling this mode will prevent the shared secret from being visible
|
||||
on the host's process list when passed via the command line. On a
|
||||
multi-user system, this will help ensure that other local users cannot
|
||||
access the shared secret and receive the files instead of the intended
|
||||
recipient.
|
||||
|
||||
Do you wish to continue to DISABLE the classic mode? (Y/n) `)
|
||||
choice := strings.ToLower(utils.GetInput(""))
|
||||
if choice == "y" || choice == "yes" {
|
||||
os.Remove(classicFile)
|
||||
fmt.Print("\nClassic mode DISABLED.\n\n")
|
||||
fmt.Print(`To send and receive, export the CROC_SECRET variable with the code phrase:
|
||||
|
||||
Send: CROC_SECRET=*** croc send file.txt
|
||||
|
||||
Receive: CROC_SECRET=*** croc` + "\n\n")
|
||||
} else {
|
||||
fmt.Print("\nClassic mode ENABLED.\n")
|
||||
|
||||
}
|
||||
} else {
|
||||
// enable classic mode
|
||||
// touch the file
|
||||
fmt.Print(`Classic mode is currently DISABLED.
|
||||
|
||||
Please note that enabling this mode will make the shared secret visible
|
||||
on the host's process list when passed via the command line. On a
|
||||
multi-user system, this could allow other local users to access the
|
||||
shared secret and receive the files instead of the intended recipient.
|
||||
|
||||
Do you wish to continue to enable the classic mode? (Y/n) `)
|
||||
choice := strings.ToLower(utils.GetInput(""))
|
||||
if choice == "y" || choice == "yes" {
|
||||
fmt.Print("\nClassic mode ENABLED.\n\n")
|
||||
os.WriteFile(classicFile, []byte("enabled"), 0o644)
|
||||
fmt.Print(`To send and receive, use the code phrase:
|
||||
|
||||
Send: croc send --code *** file.txt
|
||||
|
||||
Receive: croc ***` + "\n\n")
|
||||
} else {
|
||||
fmt.Print("\nClassic mode DISABLED.\n")
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// if trying to send but forgot send, let the user know
|
||||
if c.Args().Present() && allStringsAreFiles(c.Args().Slice()) {
|
||||
fnames := []string{}
|
||||
|
@ -111,31 +188,19 @@ func Run() (err error) {
|
|||
_, basename := filepath.Split(fpath)
|
||||
fnames = append(fnames, "'"+basename+"'")
|
||||
}
|
||||
yn := utils.GetInput(fmt.Sprintf("Did you mean to send %s? (y/n) ", strings.Join(fnames, ", ")))
|
||||
if strings.ToLower(yn) == "y" {
|
||||
promptMessage := fmt.Sprintf("Did you mean to send %s? (Y/n) ", strings.Join(fnames, ", "))
|
||||
choice := strings.ToLower(utils.GetInput(promptMessage))
|
||||
if choice == "" || choice == "y" || choice == "yes" {
|
||||
return send(c)
|
||||
}
|
||||
}
|
||||
|
||||
return receive(c)
|
||||
}
|
||||
|
||||
return app.Run(os.Args)
|
||||
}
|
||||
|
||||
func getConfigDir() (homedir string, err error) {
|
||||
homedir, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
homedir = path.Join(homedir, ".config", "croc")
|
||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||
log.Debugf("creating home directory %s", homedir)
|
||||
err = os.MkdirAll(homedir, 0700)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setDebugLevel(c *cli.Context) {
|
||||
if c.Bool("debug") {
|
||||
log.SetLevel("debug")
|
||||
|
@ -145,8 +210,8 @@ func setDebugLevel(c *cli.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func getConfigFile() string {
|
||||
configFile, err := getConfigDir()
|
||||
func getSendConfigFile(requireValidPath bool) string {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return ""
|
||||
|
@ -154,9 +219,27 @@ func getConfigFile() string {
|
|||
return path.Join(configFile, "send.json")
|
||||
}
|
||||
|
||||
func getClassicConfigFile(requireValidPath bool) string {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return ""
|
||||
}
|
||||
return path.Join(configFile, "classic_enabled")
|
||||
}
|
||||
|
||||
func getReceiveConfigFile(requireValidPath bool) (string, error) {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", err
|
||||
}
|
||||
return path.Join(configFile, "receive.json"), nil
|
||||
}
|
||||
|
||||
func determinePass(c *cli.Context) (pass string) {
|
||||
pass = c.String("pass")
|
||||
b, err := ioutil.ReadFile(pass)
|
||||
b, err := os.ReadFile(pass)
|
||||
if err == nil {
|
||||
pass = strings.TrimSpace(string(b))
|
||||
}
|
||||
|
@ -166,6 +249,22 @@ func determinePass(c *cli.Context) (pass string) {
|
|||
func send(c *cli.Context) (err error) {
|
||||
setDebugLevel(c)
|
||||
comm.Socks5Proxy = c.String("socks5")
|
||||
comm.HttpProxy = c.String("connect")
|
||||
|
||||
portParam := c.Int("port")
|
||||
if portParam == 0 {
|
||||
portParam = 9009
|
||||
}
|
||||
transfersParam := c.Int("transfers")
|
||||
if transfersParam == 0 {
|
||||
transfersParam = 4
|
||||
}
|
||||
|
||||
ports := make([]string, transfersParam+1)
|
||||
for i := 0; i <= transfersParam; i++ {
|
||||
ports[i] = strconv.Itoa(portParam + i)
|
||||
}
|
||||
|
||||
crocOptions := croc.Options{
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: true,
|
||||
|
@ -176,19 +275,26 @@ func send(c *cli.Context) (err error) {
|
|||
Stdout: c.Bool("stdout"),
|
||||
DisableLocal: c.Bool("no-local"),
|
||||
OnlyLocal: c.Bool("local"),
|
||||
RelayPorts: strings.Split(c.String("ports"), ","),
|
||||
IgnoreStdin: c.Bool("ignore-stdin"),
|
||||
RelayPorts: ports,
|
||||
Ask: c.Bool("ask"),
|
||||
NoMultiplexing: c.Bool("no-multi"),
|
||||
RelayPassword: determinePass(c),
|
||||
SendingText: c.String("text") != "",
|
||||
NoCompress: c.Bool("no-compress"),
|
||||
Overwrite: c.Bool("overwrite"),
|
||||
Curve: c.String("curve"),
|
||||
HashAlgorithm: c.String("hash"),
|
||||
ThrottleUpload: c.String("throttleUpload"),
|
||||
ZipFolder: c.Bool("zip"),
|
||||
GitIgnore: c.Bool("git"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
|
||||
crocOptions.RelayAddress = ""
|
||||
}
|
||||
b, errOpen := ioutil.ReadFile(getConfigFile())
|
||||
b, errOpen := os.ReadFile(getSendConfigFile(false))
|
||||
if errOpen == nil && !c.Bool("remember") {
|
||||
var rememberedOptions croc.Options
|
||||
err = json.Unmarshal(b, &rememberedOptions)
|
||||
|
@ -197,34 +303,52 @@ func send(c *cli.Context) (err error) {
|
|||
return
|
||||
}
|
||||
// update anything that isn't explicitly set
|
||||
if !c.IsSet("relay") {
|
||||
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
|
||||
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
||||
}
|
||||
if !c.IsSet("no-local") {
|
||||
crocOptions.DisableLocal = rememberedOptions.DisableLocal
|
||||
}
|
||||
if !c.IsSet("ports") {
|
||||
if !c.IsSet("ports") && len(rememberedOptions.RelayPorts) > 0 {
|
||||
crocOptions.RelayPorts = rememberedOptions.RelayPorts
|
||||
}
|
||||
if !c.IsSet("code") {
|
||||
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
||||
}
|
||||
if !c.IsSet("pass") {
|
||||
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
|
||||
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
||||
}
|
||||
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
|
||||
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
|
||||
}
|
||||
if !c.IsSet("overwrite") {
|
||||
crocOptions.Overwrite = rememberedOptions.Overwrite
|
||||
}
|
||||
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
|
||||
crocOptions.Curve = rememberedOptions.Curve
|
||||
}
|
||||
if !c.IsSet("local") {
|
||||
crocOptions.OnlyLocal = rememberedOptions.OnlyLocal
|
||||
}
|
||||
if !c.IsSet("hash") {
|
||||
crocOptions.HashAlgorithm = rememberedOptions.HashAlgorithm
|
||||
}
|
||||
if !c.IsSet("git") {
|
||||
crocOptions.GitIgnore = rememberedOptions.GitIgnore
|
||||
}
|
||||
}
|
||||
|
||||
var fnames []string
|
||||
stat, _ := os.Stdin.Stat()
|
||||
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||
if ((stat.Mode() & os.ModeCharDevice) == 0) && !c.Bool("ignore-stdin") {
|
||||
fnames, err = getStdin()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err = os.Remove(fnames[0])
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
e := os.Remove(fnames[0])
|
||||
if e != nil {
|
||||
log.Error(e)
|
||||
}
|
||||
}()
|
||||
} else if c.String("text") != "" {
|
||||
|
@ -233,9 +357,9 @@ func send(c *cli.Context) (err error) {
|
|||
return
|
||||
}
|
||||
defer func() {
|
||||
err = os.Remove(fnames[0])
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
e := os.Remove(fnames[0])
|
||||
if e != nil {
|
||||
log.Error(e)
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -243,15 +367,40 @@ func send(c *cli.Context) (err error) {
|
|||
fnames = c.Args().Slice()
|
||||
}
|
||||
if len(fnames) == 0 {
|
||||
return errors.New("must specify file: croc send [filename]")
|
||||
return errors.New("must specify file: croc send [filename(s) or folder]")
|
||||
}
|
||||
|
||||
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
|
||||
if !classicInsecureMode {
|
||||
// if operating system is UNIX, then use environmental variable to set the code
|
||||
if (!(runtime.GOOS == "windows") && c.IsSet("code")) || os.Getenv("CROC_SECRET") != "" {
|
||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
||||
if crocOptions.SharedSecret == "" {
|
||||
fmt.Printf(`On UNIX systems, to send with a custom code phrase,
|
||||
you need to set the environmental variable CROC_SECRET:
|
||||
|
||||
export CROC_SECRET="****"
|
||||
croc send file.txt
|
||||
|
||||
Or you can have the code phrase automaticlaly generated:
|
||||
|
||||
croc send file.txt
|
||||
|
||||
Or you can go back to the classic croc behavior by enabling classic mode:
|
||||
|
||||
croc --classic
|
||||
|
||||
`)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(crocOptions.SharedSecret) == 0 {
|
||||
// generate code phrase
|
||||
crocOptions.SharedSecret = utils.GetRandomName()
|
||||
}
|
||||
|
||||
paths, haveFolder, err := getPaths(fnames)
|
||||
minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder, crocOptions.GitIgnore)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -264,16 +413,13 @@ func send(c *cli.Context) (err error) {
|
|||
// save the config
|
||||
saveConfig(c, crocOptions)
|
||||
|
||||
err = cr.Send(croc.TransferOptions{
|
||||
PathToFiles: paths,
|
||||
KeepPathInRemote: haveFolder,
|
||||
})
|
||||
err = cr.Send(minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func getStdin() (fnames []string, err error) {
|
||||
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -290,7 +436,7 @@ func getStdin() (fnames []string, err error) {
|
|||
}
|
||||
|
||||
func makeTempFileWithString(s string) (fnames []string, err error) {
|
||||
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -306,43 +452,11 @@ func makeTempFileWithString(s string) (fnames []string, err error) {
|
|||
}
|
||||
fnames = []string{f.Name()}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func getPaths(fnames []string) (paths []string, haveFolder bool, err error) {
|
||||
haveFolder = false
|
||||
paths = []string{}
|
||||
for _, fname := range fnames {
|
||||
stat, errStat := os.Stat(fname)
|
||||
if errStat != nil {
|
||||
err = errStat
|
||||
return
|
||||
}
|
||||
if stat.IsDir() {
|
||||
haveFolder = true
|
||||
err = filepath.Walk(fname,
|
||||
func(pathName string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !info.IsDir() {
|
||||
paths = append(paths, filepath.ToSlash(pathName))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
paths = append(paths, filepath.ToSlash(fname))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
||||
if c.Bool("remember") {
|
||||
configFile := getConfigFile()
|
||||
configFile := getSendConfigFile(true)
|
||||
log.Debug("saving config file")
|
||||
var bConfig []byte
|
||||
// if the code wasn't set, don't save it
|
||||
|
@ -354,7 +468,7 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(configFile, bConfig, 0644)
|
||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
@ -363,8 +477,39 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
|||
}
|
||||
}
|
||||
|
||||
type TabComplete struct{}
|
||||
|
||||
func (t TabComplete) Do(line []rune, pos int) ([][]rune, int) {
|
||||
var words = strings.SplitAfter(string(line), "-")
|
||||
var lastPartialWord = words[len(words)-1]
|
||||
var nbCharacter = len(lastPartialWord)
|
||||
if nbCharacter == 0 {
|
||||
// No completion
|
||||
return [][]rune{[]rune("")}, 0
|
||||
}
|
||||
if len(words) == 1 && nbCharacter == utils.NbPinNumbers {
|
||||
// Check if word is indeed a number
|
||||
_, err := strconv.Atoi(lastPartialWord)
|
||||
if err == nil {
|
||||
return [][]rune{[]rune("-")}, nbCharacter
|
||||
}
|
||||
}
|
||||
var strArray [][]rune
|
||||
for _, s := range mnemonicode.WordList {
|
||||
if strings.HasPrefix(s, lastPartialWord) {
|
||||
var completionCandidate = s[nbCharacter:]
|
||||
if len(words) <= mnemonicode.WordsRequired(utils.NbBytesWords) {
|
||||
completionCandidate += "-"
|
||||
}
|
||||
strArray = append(strArray, []rune(completionCandidate))
|
||||
}
|
||||
}
|
||||
return strArray, nbCharacter
|
||||
}
|
||||
|
||||
func receive(c *cli.Context) (err error) {
|
||||
comm.Socks5Proxy = c.String("socks5")
|
||||
comm.HttpProxy = c.String("connect")
|
||||
crocOptions := croc.Options{
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: false,
|
||||
|
@ -376,6 +521,10 @@ func receive(c *cli.Context) (err error) {
|
|||
Ask: c.Bool("ask"),
|
||||
RelayPassword: determinePass(c),
|
||||
OnlyLocal: c.Bool("local"),
|
||||
IP: c.String("ip"),
|
||||
Overwrite: c.Bool("overwrite"),
|
||||
Curve: c.String("curve"),
|
||||
TestFlag: c.Bool("testing"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
|
@ -387,6 +536,8 @@ func receive(c *cli.Context) (err error) {
|
|||
case 1:
|
||||
crocOptions.SharedSecret = c.Args().First()
|
||||
case 3:
|
||||
fallthrough
|
||||
case 4:
|
||||
var phrase []string
|
||||
phrase = append(phrase, c.Args().First())
|
||||
phrase = append(phrase, c.Args().Tail()...)
|
||||
|
@ -395,22 +546,22 @@ func receive(c *cli.Context) (err error) {
|
|||
|
||||
// load options here
|
||||
setDebugLevel(c)
|
||||
configFile, err := getConfigDir()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
||||
doRemember := c.Bool("remember")
|
||||
configFile, err := getReceiveConfigFile(doRemember)
|
||||
if err != nil && doRemember {
|
||||
return
|
||||
}
|
||||
configFile = path.Join(configFile, "receive.json")
|
||||
b, errOpen := ioutil.ReadFile(configFile)
|
||||
if errOpen == nil && !c.Bool("remember") {
|
||||
b, errOpen := os.ReadFile(configFile)
|
||||
if errOpen == nil && !doRemember {
|
||||
var rememberedOptions croc.Options
|
||||
err = json.Unmarshal(b, &rememberedOptions)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
// update anything that isn't expliciGlobalIsSettly set
|
||||
if !c.IsSet("relay") {
|
||||
// update anything that isn't explicitly Globally set
|
||||
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
|
||||
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
||||
}
|
||||
if !c.IsSet("yes") {
|
||||
|
@ -419,13 +570,61 @@ func receive(c *cli.Context) (err error) {
|
|||
if crocOptions.SharedSecret == "" {
|
||||
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
||||
}
|
||||
if !c.IsSet("pass") {
|
||||
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
|
||||
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
||||
}
|
||||
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
|
||||
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
|
||||
}
|
||||
if !c.IsSet("overwrite") {
|
||||
crocOptions.Overwrite = rememberedOptions.Overwrite
|
||||
}
|
||||
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
|
||||
crocOptions.Curve = rememberedOptions.Curve
|
||||
}
|
||||
if !c.IsSet("local") {
|
||||
crocOptions.OnlyLocal = rememberedOptions.OnlyLocal
|
||||
}
|
||||
}
|
||||
|
||||
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
|
||||
if crocOptions.SharedSecret == "" && os.Getenv("CROC_SECRET") != "" {
|
||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
||||
} else if !(runtime.GOOS == "windows") && crocOptions.SharedSecret != "" && !classicInsecureMode {
|
||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
||||
if crocOptions.SharedSecret == "" {
|
||||
fmt.Printf(`On UNIX systems, to receive with croc you either need
|
||||
to set a code phrase using your environmental variables:
|
||||
|
||||
export CROC_SECRET="****"
|
||||
croc
|
||||
|
||||
Or you can specify the code phrase when you run croc without
|
||||
declaring the secret on the command line:
|
||||
|
||||
croc
|
||||
Enter receive code: ****
|
||||
|
||||
Or you can go back to the classic croc behavior by enabling classic mode:
|
||||
|
||||
croc --classic
|
||||
|
||||
`)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
if crocOptions.SharedSecret == "" {
|
||||
crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
|
||||
l, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "Enter receive code: ",
|
||||
AutoComplete: TabComplete{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crocOptions.SharedSecret, err = l.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.String("out") != "" {
|
||||
if err = os.Chdir(c.String("out")); err != nil {
|
||||
|
@ -439,7 +638,7 @@ func receive(c *cli.Context) (err error) {
|
|||
}
|
||||
|
||||
// save the config
|
||||
if c.Bool("remember") {
|
||||
if doRemember {
|
||||
log.Debug("saving config file")
|
||||
var bConfig []byte
|
||||
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
|
||||
|
@ -447,7 +646,7 @@ func receive(c *cli.Context) (err error) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = ioutil.WriteFile(configFile, bConfig, 0644)
|
||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
@ -465,6 +664,7 @@ func relay(c *cli.Context) (err error) {
|
|||
if c.Bool("debug") {
|
||||
debugString = "debug"
|
||||
}
|
||||
host := c.String("host")
|
||||
ports := strings.Split(c.String("ports"), ",")
|
||||
tcpPorts := strings.Join(ports[1:], ",")
|
||||
for i, port := range ports {
|
||||
|
@ -472,11 +672,11 @@ func relay(c *cli.Context) (err error) {
|
|||
continue
|
||||
}
|
||||
go func(portStr string) {
|
||||
err = tcp.Run(debugString, portStr, determinePass(c))
|
||||
err := tcp.Run(debugString, host, portStr, determinePass(c))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}(port)
|
||||
}
|
||||
return tcp.Run(debugString, ports[0], determinePass(c), tcpPorts)
|
||||
return tcp.Run(debugString, host, ports[0], determinePass(c), tcpPorts)
|
||||
}
|
||||
|
|
|
@ -6,16 +6,20 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v8/src/utils"
|
||||
"github.com/magisterquis/connectproxy"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
log "github.com/schollz/logger"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
var Socks5Proxy = ""
|
||||
var HttpProxy = ""
|
||||
|
||||
const MAXBYTES = 4000000
|
||||
var MAGIC_BYTES = []byte("croc")
|
||||
|
||||
// Comm is some basic TCP communication
|
||||
type Comm struct {
|
||||
|
@ -31,17 +35,52 @@ func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err err
|
|||
var connection net.Conn
|
||||
if Socks5Proxy != "" && !utils.IsLocalIP(address) {
|
||||
var dialer proxy.Dialer
|
||||
dialer, err = proxy.SOCKS5("tcp", Socks5Proxy, nil, proxy.Direct)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("proxy failed: %w", err)
|
||||
// prepend schema if no schema is given
|
||||
if !strings.Contains(Socks5Proxy, `://`) {
|
||||
Socks5Proxy = `socks5://` + Socks5Proxy
|
||||
}
|
||||
socks5ProxyURL, urlParseError := url.Parse(Socks5Proxy)
|
||||
if urlParseError != nil {
|
||||
err = fmt.Errorf("unable to parse socks proxy url: %s", urlParseError)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
dialer, err = proxy.FromURL(socks5ProxyURL, proxy.Direct)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("proxy failed: %w", err)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("dialing with dialer.Dial")
|
||||
connection, err = dialer.Dial("tcp", address)
|
||||
} else if HttpProxy != "" && !utils.IsLocalIP(address) {
|
||||
var dialer proxy.Dialer
|
||||
// prepend schema if no schema is given
|
||||
if !strings.Contains(HttpProxy, `://`) {
|
||||
HttpProxy = `http://` + HttpProxy
|
||||
}
|
||||
HttpProxyURL, urlParseError := url.Parse(HttpProxy)
|
||||
if urlParseError != nil {
|
||||
err = fmt.Errorf("unable to parse http proxy url: %s", urlParseError)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
dialer, err = connectproxy.New(HttpProxyURL, proxy.Direct)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("proxy failed: %w", err)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("dialing with dialer.Dial")
|
||||
connection, err = dialer.Dial("tcp", address)
|
||||
|
||||
} else {
|
||||
log.Debugf("dialing to %s with timelimit %s", address, tlimit)
|
||||
connection, err = net.DialTimeout("tcp", address, tlimit)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("comm.NewConnection failed: %w", err)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
c = New(connection)
|
||||
|
@ -84,6 +123,7 @@ func (c *Comm) Write(b []byte) (n int, err error) {
|
|||
fmt.Println("binary.Write failed:", err)
|
||||
}
|
||||
tmpCopy := append(header.Bytes(), b...)
|
||||
tmpCopy = append(MAGIC_BYTES, tmpCopy...)
|
||||
n, err = c.connection.Write(tmpCopy)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("connection.Write failed: %w", err)
|
||||
|
@ -98,19 +138,31 @@ func (c *Comm) Write(b []byte) (n int, err error) {
|
|||
|
||||
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||
// long read deadline in case waiting for file
|
||||
if err := c.connection.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
if err = c.connection.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
log.Warnf("error setting read deadline: %v", err)
|
||||
}
|
||||
// must clear the timeout setting
|
||||
defer c.connection.SetDeadline(time.Time{})
|
||||
|
||||
// read until we get 4 bytes for the header
|
||||
// read until we get 4 bytes for the magic
|
||||
header := make([]byte, 4)
|
||||
_, err = io.ReadFull(c.connection, header)
|
||||
if err != nil {
|
||||
log.Debugf("initial read error: %v", err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(header, MAGIC_BYTES) {
|
||||
err = fmt.Errorf("initial bytes are not magic: %x", header)
|
||||
return
|
||||
}
|
||||
|
||||
// read until we get 4 bytes for the header
|
||||
header = make([]byte, 4)
|
||||
_, err = io.ReadFull(c.connection, header)
|
||||
if err != nil {
|
||||
log.Debugf("initial read error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var numBytesUint32 uint32
|
||||
rbuf := bytes.NewReader(header)
|
||||
|
@ -121,14 +173,9 @@ func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
|||
return
|
||||
}
|
||||
numBytes = int(numBytesUint32)
|
||||
if numBytes > MAXBYTES {
|
||||
err = fmt.Errorf("too many bytes: %d", numBytes)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
// shorten the reading deadline in case getting weird data
|
||||
if err := c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
if err = c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
log.Warnf("error setting read deadline: %v", err)
|
||||
}
|
||||
buf = make([]byte, numBytes)
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
)
|
||||
|
||||
func TestComm(t *testing.T) {
|
||||
token := make([]byte, MAXBYTES)
|
||||
token := make([]byte, 3000)
|
||||
if _, err := rand.Read(token); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ func TestComm(t *testing.T) {
|
|||
log.Error(err)
|
||||
}
|
||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||
go func(port string, connection net.Conn) {
|
||||
go func(_ string, connection net.Conn) {
|
||||
c := New(connection)
|
||||
err = c.Send([]byte("hello, world"))
|
||||
assert.Nil(t, err)
|
||||
|
@ -49,7 +49,7 @@ func TestComm(t *testing.T) {
|
|||
}()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
a, err := NewConnection("localhost:"+port, 10*time.Minute)
|
||||
a, err := NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
||||
assert.Nil(t, err)
|
||||
data, err := a.Receive()
|
||||
assert.Equal(t, []byte("hello, world"), data)
|
||||
|
@ -63,5 +63,4 @@ func TestComm(t *testing.T) {
|
|||
assert.NotNil(t, a.Send(token))
|
||||
_, err = a.Write(token)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
|
1281
src/croc/croc.go
1281
src/croc/croc.go
File diff suppressed because it is too large
Load Diff
|
@ -1,13 +1,16 @@
|
|||
package croc
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v8/src/tcp"
|
||||
"github.com/schollz/croc/v10/src/tcp"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -15,11 +18,11 @@ import (
|
|||
func init() {
|
||||
log.SetLevel("trace")
|
||||
|
||||
go tcp.Run("debug", "8081", "pass123", "8082,8083,8084,8085")
|
||||
go tcp.Run("debug", "8082", "pass123")
|
||||
go tcp.Run("debug", "8083", "pass123")
|
||||
go tcp.Run("debug", "8084", "pass123")
|
||||
go tcp.Run("debug", "8085", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8281", "pass123", "8282,8283,8284,8285")
|
||||
go tcp.Run("debug", "127.0.0.1", "8282", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8283", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8284", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8285", "pass123")
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
|
@ -29,14 +32,17 @@ func TestCrocReadme(t *testing.T) {
|
|||
log.Debug("setting up sender")
|
||||
sender, err := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "test",
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8081",
|
||||
RelayPorts: []string{"8081"},
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -45,13 +51,15 @@ func TestCrocReadme(t *testing.T) {
|
|||
log.Debug("setting up receiver")
|
||||
receiver, err := New(Options{
|
||||
IsSender: false,
|
||||
SharedSecret: "test",
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8081",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -60,9 +68,11 @@ func TestCrocReadme(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
err := sender.Send(TransferOptions{
|
||||
PathToFiles: []string{"../../README.md"},
|
||||
})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../README.md"}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
if err != nil {
|
||||
t.Errorf("send failed: %v", err)
|
||||
}
|
||||
|
@ -80,6 +90,175 @@ func TestCrocReadme(t *testing.T) {
|
|||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCrocEmptyFolder(t *testing.T) {
|
||||
pathName := "../../testEmpty"
|
||||
defer os.RemoveAll(pathName)
|
||||
defer os.RemoveAll("./testEmpty")
|
||||
os.MkdirAll(pathName, 0o755)
|
||||
|
||||
log.Debug("setting up sender")
|
||||
sender, err := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Debug("setting up receiver")
|
||||
receiver, err := New(Options{
|
||||
IsSender: false,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
if err != nil {
|
||||
t.Errorf("send failed: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
err := receiver.Receive()
|
||||
if err != nil {
|
||||
t.Errorf("receive failed: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestCrocSymlink(t *testing.T) {
|
||||
pathName := "../link-in-folder"
|
||||
defer os.RemoveAll(pathName)
|
||||
defer os.RemoveAll("./link-in-folder")
|
||||
os.MkdirAll(pathName, 0o755)
|
||||
os.Symlink("../../README.md", filepath.Join(pathName, "README.link"))
|
||||
|
||||
log.Debug("setting up sender")
|
||||
sender, err := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "8124-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Debug("setting up receiver")
|
||||
receiver, err := New(Options{
|
||||
IsSender: false,
|
||||
SharedSecret: "8124-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err = sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
if err != nil {
|
||||
t.Errorf("send failed: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
err = receiver.Receive()
|
||||
if err != nil {
|
||||
t.Errorf("receive failed: %v", err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
s, err := filepath.EvalSymlinks(path.Join(pathName, "README.link"))
|
||||
if s != "../../README.md" && s != "..\\..\\README.md" {
|
||||
log.Debug(s)
|
||||
t.Errorf("symlink failed to transfer in folder")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("symlink transfer failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
func testCrocIgnoreGit(t *testing.T) {
|
||||
log.SetLevel("trace")
|
||||
defer os.Remove(".gitignore")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
file, err := os.Create(".gitignore")
|
||||
if err != nil {
|
||||
log.Errorf("error creating file")
|
||||
}
|
||||
_, err = file.WriteString("LICENSE")
|
||||
if err != nil {
|
||||
log.Errorf("error writing to file")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
// due to how files are ignored in this function, all we have to do to test is make sure LICENSE doesn't get included in FilesInfo.
|
||||
filesInfo, _, _, errGet := GetFilesInfo([]string{"../../LICENSE", ".gitignore", "croc.go"}, false, true)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
for _, file := range filesInfo {
|
||||
if strings.Contains(file.Name, "LICENSE") {
|
||||
t.Errorf("test failed, should ignore LICENSE")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrocLocal(t *testing.T) {
|
||||
log.SetLevel("trace")
|
||||
defer os.Remove("LICENSE")
|
||||
|
@ -89,14 +268,17 @@ func TestCrocLocal(t *testing.T) {
|
|||
log.Debug("setting up sender")
|
||||
sender, err := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "test",
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8181",
|
||||
RelayAddress: "127.0.0.1:8181",
|
||||
RelayPorts: []string{"8181", "8182"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: true,
|
||||
NoPrompt: true,
|
||||
DisableLocal: false,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -106,13 +288,15 @@ func TestCrocLocal(t *testing.T) {
|
|||
log.Debug("setting up receiver")
|
||||
receiver, err := New(Options{
|
||||
IsSender: false,
|
||||
SharedSecret: "test",
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8181",
|
||||
RelayAddress: "127.0.0.1:8181",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: true,
|
||||
NoPrompt: true,
|
||||
DisableLocal: false,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -122,10 +306,11 @@ func TestCrocLocal(t *testing.T) {
|
|||
os.Create("touched")
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
err := sender.Send(TransferOptions{
|
||||
PathToFiles: []string{"../../LICENSE", "touched"},
|
||||
KeepPathInRemote: false,
|
||||
})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../LICENSE", "touched"}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
if err != nil {
|
||||
t.Errorf("send failed: %v", err)
|
||||
}
|
||||
|
@ -145,17 +330,17 @@ func TestCrocLocal(t *testing.T) {
|
|||
|
||||
func TestCrocError(t *testing.T) {
|
||||
content := []byte("temporary file's content")
|
||||
tmpfile, err := ioutil.TempFile("", "example")
|
||||
tmpfile, err := os.CreateTemp("", "example")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
if _, err = tmpfile.Write(content); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
@ -163,7 +348,7 @@ func TestCrocError(t *testing.T) {
|
|||
log.SetLevel("warn")
|
||||
sender, _ := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "test33",
|
||||
SharedSecret: "8123-testingthecroc2",
|
||||
Debug: true,
|
||||
RelayAddress: "doesntexistok.com:8381",
|
||||
RelayPorts: []string{"8381", "8382"},
|
||||
|
@ -171,12 +356,45 @@ func TestCrocError(t *testing.T) {
|
|||
Stdout: true,
|
||||
NoPrompt: true,
|
||||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
err = sender.Send(TransferOptions{
|
||||
PathToFiles: []string{tmpfile.Name()},
|
||||
KeepPathInRemote: true,
|
||||
})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{tmpfile.Name()}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err = sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
log.Debug(err)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestCleanUp(t *testing.T) {
|
||||
// windows allows files to be deleted only if they
|
||||
// are not open by another program so the remove actions
|
||||
// from the above tests will not always do a good clean up
|
||||
// This "test" will make sure
|
||||
operatingSystem := runtime.GOOS
|
||||
log.Debugf("The operating system is %s", operatingSystem)
|
||||
if operatingSystem == "windows" {
|
||||
time.Sleep(1 * time.Second)
|
||||
log.Debug("Full cleanup")
|
||||
var err error
|
||||
|
||||
for _, file := range []string{"README.md", "./README.md"} {
|
||||
err = os.Remove(file)
|
||||
if err == nil {
|
||||
log.Debugf("Successfully purged %s", file)
|
||||
} else {
|
||||
log.Debugf("%s was already purged.", file)
|
||||
}
|
||||
}
|
||||
for _, folder := range []string{"./testEmpty", "./link-in-folder"} {
|
||||
err = os.RemoveAll(folder)
|
||||
if err == nil {
|
||||
log.Debugf("Successfully purged %s", folder)
|
||||
} else {
|
||||
log.Debugf("%s was already purged.", folder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/argon2"
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
|
@ -37,7 +39,7 @@ func Encrypt(plaintext []byte, key []byte) (encrypted []byte, err error) {
|
|||
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
// Section 8.2
|
||||
ivBytes := make([]byte, 12)
|
||||
if _, err := rand.Read(ivBytes); err != nil {
|
||||
if _, err = rand.Read(ivBytes); err != nil {
|
||||
log.Fatalf("can't initialize crypto: %v", err)
|
||||
}
|
||||
b, err := aes.NewCipher(key)
|
||||
|
@ -70,3 +72,54 @@ func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) {
|
|||
plaintext, err = aesgcm.Open(nil, encrypted[:12], encrypted[12:], nil)
|
||||
return
|
||||
}
|
||||
|
||||
// NewArgon2 generates a new key based on a passphrase and salt
|
||||
// using argon2
|
||||
// https://pkg.go.dev/golang.org/x/crypto/argon2
|
||||
func NewArgon2(passphrase []byte, usersalt []byte) (aead cipher.AEAD, salt []byte, err error) {
|
||||
if len(passphrase) < 1 {
|
||||
err = fmt.Errorf("need more than that for passphrase")
|
||||
return
|
||||
}
|
||||
if usersalt == nil {
|
||||
salt = make([]byte, 8)
|
||||
// http://www.ietf.org/rfc/rfc2898.txt
|
||||
// Salt.
|
||||
if _, err = rand.Read(salt); err != nil {
|
||||
log.Fatalf("can't get random salt: %v", err)
|
||||
}
|
||||
} else {
|
||||
salt = usersalt
|
||||
}
|
||||
aead, err = chacha20poly1305.NewX(argon2.IDKey(passphrase, salt, 1, 64*1024, 4, 32))
|
||||
return
|
||||
}
|
||||
|
||||
// EncryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key
|
||||
// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
|
||||
func EncryptChaCha(plaintext []byte, aead cipher.AEAD) (encrypted []byte, err error) {
|
||||
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plaintext)+aead.Overhead())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Encrypt the message and append the ciphertext to the nonce.
|
||||
encrypted = aead.Seal(nonce, nonce, plaintext, nil)
|
||||
return
|
||||
}
|
||||
|
||||
// DecryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key
|
||||
// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
|
||||
func DecryptChaCha(encryptedMsg []byte, aead cipher.AEAD) (encrypted []byte, err error) {
|
||||
if len(encryptedMsg) < aead.NonceSize() {
|
||||
err = fmt.Errorf("ciphertext too short")
|
||||
return
|
||||
}
|
||||
|
||||
// Split nonce and ciphertext.
|
||||
nonce, ciphertext := encryptedMsg[:aead.NonceSize()], encryptedMsg[aead.NonceSize():]
|
||||
|
||||
// Decrypt the message and check it wasn't tampered with.
|
||||
encrypted, err = aead.Open(nil, nonce, ciphertext, nil)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package crypt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -23,6 +24,23 @@ func BenchmarkDecrypt(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncryptChaCha(b *testing.B) {
|
||||
bob, _, _ := NewArgon2([]byte("password"), nil)
|
||||
for i := 0; i < b.N; i++ {
|
||||
EncryptChaCha([]byte("hello, world"), bob)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecryptChaCha(b *testing.B) {
|
||||
key, _, _ := NewArgon2([]byte("password"), nil)
|
||||
msg := []byte("hello, world")
|
||||
enc, _ := EncryptChaCha(msg, key)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
DecryptChaCha(enc, key)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryption(t *testing.T) {
|
||||
key, salt, err := New([]byte("password"), nil)
|
||||
assert.Nil(t, err)
|
||||
|
@ -34,22 +52,54 @@ func TestEncryption(t *testing.T) {
|
|||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err := New([]byte("password"), salt)
|
||||
key2, _, _ := New([]byte("password"), salt)
|
||||
dec, err = Decrypt(enc, key2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err = New([]byte("wrong password"), salt)
|
||||
key2, _, _ = New([]byte("wrong password"), salt)
|
||||
dec, err = Decrypt(enc, key2)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEqual(t, msg, dec)
|
||||
|
||||
// error with no password
|
||||
dec, err = Decrypt([]byte(""), key)
|
||||
_, err = Decrypt([]byte(""), key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// error with small password
|
||||
_, _, err = New([]byte(""), nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestEncryptionChaCha(t *testing.T) {
|
||||
key, salt, err := NewArgon2([]byte("password"), nil)
|
||||
fmt.Printf("key: %x\n", key)
|
||||
assert.Nil(t, err)
|
||||
msg := []byte("hello, world")
|
||||
enc, err := EncryptChaCha(msg, key)
|
||||
assert.Nil(t, err)
|
||||
dec, err := DecryptChaCha(enc, key)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, _ := NewArgon2([]byte("password"), salt)
|
||||
dec, err = DecryptChaCha(enc, key2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, _ = NewArgon2([]byte("wrong password"), salt)
|
||||
dec, err = DecryptChaCha(enc, key2)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEqual(t, msg, dec)
|
||||
|
||||
// error with no password
|
||||
_, err = DecryptChaCha([]byte(""), key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// error with small password
|
||||
_, _, err = NewArgon2([]byte(""), nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
|
|
@ -13,5 +13,7 @@ release:
|
|||
cd ../../ && ./src/install/upload-src-tarball.sh
|
||||
|
||||
test:
|
||||
cp zsh_autocomplete ../../
|
||||
cp bash_autocomplete ../../
|
||||
cd ../../ && go generate
|
||||
cd ../../ && goreleaser release --skip-publish
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#! /bin/bash
|
||||
|
||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
|
|
|
@ -86,7 +86,7 @@ print_help() {
|
|||
Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android)
|
||||
|
||||
-h
|
||||
Prints this helpfull message and exit."
|
||||
Prints this helpful message and exit."
|
||||
|
||||
echo "${help_header}"
|
||||
echo ""
|
||||
|
@ -156,10 +156,10 @@ make_tempdir() {
|
|||
|
||||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: determine_os
|
||||
# DESCRIPTION: Attempts to determin host os using uname
|
||||
# DESCRIPTION: Attempts to determine host os using uname
|
||||
# PARAMETERS: none
|
||||
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
|
||||
# 1 = Unkown OS
|
||||
# 1 = Unknown OS
|
||||
# 20 = 'uname' not found in path
|
||||
#-------------------------------------------------------------------------------
|
||||
determine_os() {
|
||||
|
@ -180,10 +180,10 @@ determine_os() {
|
|||
|
||||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: determine_arch
|
||||
# DESCRIPTION: Attempt to determin architecture of host
|
||||
# DESCRIPTION: Attempt to determine architecture of host
|
||||
# PARAMETERS: none
|
||||
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
|
||||
# 1 = Unkown arch
|
||||
# 1 = Unknown arch
|
||||
# 20 = 'uname' not found in path
|
||||
#-------------------------------------------------------------------------------
|
||||
determine_arch() {
|
||||
|
@ -238,7 +238,7 @@ download_file() {
|
|||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: checksum_check
|
||||
# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure
|
||||
# integrity. Tries multiple tools before faling.
|
||||
# integrity. Tries multiple tools before failing.
|
||||
# PARAMETERS: $1 = path to checksum file
|
||||
# $2 = location of file to check
|
||||
# $3 = working directory
|
||||
|
@ -300,10 +300,10 @@ checksum_check() {
|
|||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: extract_file
|
||||
# DESCRIPTION: Extracts a file into a location. Attempts to determine which
|
||||
# tool to use by checking file extention.
|
||||
# tool to use by checking file extension.
|
||||
# PARAMETERS: $1 = file to extract
|
||||
# $2 = location to extract file into
|
||||
# $3 = extention
|
||||
# $3 = extension
|
||||
# RETURNS: Return code of the tool used to extract the file
|
||||
# 20 = Failed to determine which tool to use
|
||||
# 30 = Failed to find tool in path
|
||||
|
@ -528,7 +528,7 @@ main() {
|
|||
local autocomplete_install_rcode
|
||||
|
||||
croc_bin_name="croc"
|
||||
croc_version="8.6.5"
|
||||
croc_version="10.0.7"
|
||||
croc_dl_ext="tar.gz"
|
||||
croc_base_url="https://github.com/schollz/croc/releases/download"
|
||||
prefix="${1}"
|
||||
|
@ -587,16 +587,17 @@ main() {
|
|||
"x86_64" ) croc_arch="64bit";;
|
||||
"amd64" ) croc_arch="64bit";;
|
||||
"aarch64" ) croc_arch="ARM64";;
|
||||
"arm64" ) croc_arch="ARM64";;
|
||||
"armv7l" ) croc_arch="ARM";;
|
||||
"i686" ) croc_arch="32bit";;
|
||||
* ) croc_arch="unknown";;
|
||||
esac
|
||||
|
||||
croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
||||
croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt"
|
||||
croc_file="${croc_bin_name}_v${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
||||
croc_checksum_file="${croc_bin_name}_v${croc_version}_checksums.txt"
|
||||
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
|
||||
croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}"
|
||||
|
||||
echo "${croc_url}" "${tmpdir}" "${croc_file}"
|
||||
download_file "${croc_url}" "${tmpdir}" "${croc_file}"
|
||||
download_file_rcode="${?}"
|
||||
if [[ "${download_file_rcode}" == "0" ]]; then
|
||||
|
@ -684,7 +685,7 @@ main() {
|
|||
print_message "== Install prefix already exists. No need to create it." "info"
|
||||
fi
|
||||
|
||||
|
||||
[ ! -d "/etc/bash_completion.d/croc" ] && mkdir -p "/etc/bash_completion.d/croc"
|
||||
case "${croc_os}" in
|
||||
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
|
||||
install_file_rcode="${?}";;
|
||||
|
@ -712,26 +713,27 @@ main() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
case "$(basename ${SHELL})" in
|
||||
"bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
||||
autocomplete_install_rcode="${?}";;
|
||||
"zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
||||
autocomplete_install_rcode="${?}";
|
||||
print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
|
||||
print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
|
||||
esac
|
||||
# case "$(basename ${SHELL})" in
|
||||
# "bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
||||
# autocomplete_install_rcode="${?}";;
|
||||
# "zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
||||
# autocomplete_install_rcode="${?}";
|
||||
# print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
|
||||
# print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
|
||||
# *) autocomplete_install_rcode="1";;
|
||||
# esac
|
||||
|
||||
if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
||||
print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
||||
elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
||||
print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
||||
elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
||||
print_message "== Failed to locate 'install' command" "error"
|
||||
elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
||||
print_message "== Failed to locate 'sudo' command" "error"
|
||||
else
|
||||
print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
||||
fi
|
||||
# if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
||||
# print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
||||
# print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
||||
# print_message "== Failed to locate 'install' command" "error"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
||||
# print_message "== Failed to locate 'sudo' command" "error"
|
||||
# else
|
||||
# print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
||||
# fi
|
||||
|
||||
print_message "== Installation complete" "ok"
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
@ -43,7 +42,7 @@ func run() (err error) {
|
|||
}
|
||||
|
||||
func replaceInFile(fname, start, end, replacement string) (err error) {
|
||||
b, err := ioutil.ReadFile(fname)
|
||||
b, err := os.ReadFile(fname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -58,7 +57,7 @@ func replaceInFile(fname, start, end, replacement string) (err error) {
|
|||
fmt.Sprintf("%s%s%s", start, replacement, end),
|
||||
1,
|
||||
)
|
||||
err = ioutil.WriteFile(fname, []byte(newF), 0644)
|
||||
err = os.WriteFile(fname, []byte(newF), 0o644)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,17 +3,32 @@ package message
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/schollz/croc/v8/src/comm"
|
||||
"github.com/schollz/croc/v8/src/compress"
|
||||
"github.com/schollz/croc/v8/src/crypt"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/compress"
|
||||
"github.com/schollz/croc/v10/src/crypt"
|
||||
log "github.com/schollz/logger"
|
||||
)
|
||||
|
||||
// Type is a message type
|
||||
type Type string
|
||||
|
||||
const (
|
||||
TypePAKE Type = "pake"
|
||||
TypeExternalIP Type = "externalip"
|
||||
TypeFinished Type = "finished"
|
||||
TypeError Type = "error"
|
||||
TypeCloseRecipient Type = "close-recipient"
|
||||
TypeCloseSender Type = "close-sender"
|
||||
TypeRecipientReady Type = "recipientready"
|
||||
TypeFileInfo Type = "fileinfo"
|
||||
)
|
||||
|
||||
// Message is the possible payload for messaging
|
||||
type Message struct {
|
||||
Type string `json:"t,omitempty"`
|
||||
Type Type `json:"t,omitempty"`
|
||||
Message string `json:"m,omitempty"`
|
||||
Bytes []byte `json:"b,omitempty"`
|
||||
Bytes2 []byte `json:"b2,omitempty"`
|
||||
Num int `json:"n,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -43,7 +58,7 @@ func Encode(key []byte, m Message) (b []byte, err error) {
|
|||
log.Debugf("writing %s message (encrypted)", m.Type)
|
||||
b, err = crypt.Encrypt(b, key)
|
||||
} else {
|
||||
log.Debugf("writing %s message", m.Type)
|
||||
log.Debugf("writing %s message (unencrypted)", m.Type)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -58,5 +73,12 @@ func Decode(key []byte, b []byte) (m Message, err error) {
|
|||
}
|
||||
b = compress.Decompress(b)
|
||||
err = json.Unmarshal(b, &m)
|
||||
if err == nil {
|
||||
if key != nil {
|
||||
log.Debugf("read %s message (encrypted)", m.Type)
|
||||
} else {
|
||||
log.Debugf("read %s message (unencrypted)", m.Type)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -7,18 +7,20 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v8/src/comm"
|
||||
"github.com/schollz/croc/v8/src/crypt"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/crypt"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var TypeMessage Type = "message"
|
||||
|
||||
func TestMessage(t *testing.T) {
|
||||
log.SetLevel("debug")
|
||||
m := Message{Type: "message", Message: "hello, world"}
|
||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(salt)
|
||||
fmt.Println(string(salt))
|
||||
b, err := Encode(e, m)
|
||||
assert.Nil(t, err)
|
||||
fmt.Printf("%x\n", b)
|
||||
|
@ -35,7 +37,7 @@ func TestMessage(t *testing.T) {
|
|||
|
||||
func TestMessageNoPass(t *testing.T) {
|
||||
log.SetLevel("debug")
|
||||
m := Message{Type: "message", Message: "hello, world"}
|
||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
b, err := Encode(nil, m)
|
||||
assert.Nil(t, err)
|
||||
fmt.Printf("%x\n", b)
|
||||
|
@ -65,7 +67,7 @@ func TestSend(t *testing.T) {
|
|||
log.Error(err)
|
||||
}
|
||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||
go func(port string, connection net.Conn) {
|
||||
go func(_ string, connection net.Conn) {
|
||||
c := comm.New(connection)
|
||||
err = c.Send([]byte("hello, world"))
|
||||
assert.Nil(t, err)
|
||||
|
@ -82,10 +84,10 @@ func TestSend(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
a, err := comm.NewConnection("localhost:"+port, 10*time.Minute)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
a, err := comm.NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
||||
assert.Nil(t, err)
|
||||
m := Message{Type: "message", Message: "hello, world"}
|
||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||
log.Debug(salt)
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
package models
|
||||
|
||||
import "net"
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
)
|
||||
|
||||
// TCP_BUFFER_SIZE is the maximum packet size
|
||||
const TCP_BUFFER_SIZE = 1024 * 64
|
||||
|
@ -11,15 +19,131 @@ var (
|
|||
DEFAULT_RELAY6 = "croc6.schollz.com"
|
||||
DEFAULT_PORT = "9009"
|
||||
DEFAULT_PASSPHRASE = "pass123"
|
||||
INTERNAL_DNS = false
|
||||
)
|
||||
|
||||
func init() {
|
||||
iprecords, _ := net.LookupIP(DEFAULT_RELAY)
|
||||
for _, ip := range iprecords {
|
||||
DEFAULT_RELAY = ip.String() + ":" + DEFAULT_PORT
|
||||
// publicDNS are servers to be queried if a local lookup fails
|
||||
var publicDNS = []string{
|
||||
"1.0.0.1", // Cloudflare
|
||||
"1.1.1.1", // Cloudflare
|
||||
"[2606:4700:4700::1111]", // Cloudflare
|
||||
"[2606:4700:4700::1001]", // Cloudflare
|
||||
"8.8.4.4", // Google
|
||||
"8.8.8.8", // Google
|
||||
"[2001:4860:4860::8844]", // Google
|
||||
"[2001:4860:4860::8888]", // Google
|
||||
"9.9.9.9", // Quad9
|
||||
"149.112.112.112", // Quad9
|
||||
"[2620:fe::fe]", // Quad9
|
||||
"[2620:fe::fe:9]", // Quad9
|
||||
"8.26.56.26", // Comodo
|
||||
"8.20.247.20", // Comodo
|
||||
"208.67.220.220", // Cisco OpenDNS
|
||||
"208.67.222.222", // Cisco OpenDNS
|
||||
"[2620:119:35::35]", // Cisco OpenDNS
|
||||
"[2620:119:53::53]", // Cisco OpenDNS
|
||||
}
|
||||
|
||||
func getConfigFile(requireValidPath bool) (fname string, err error) {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
iprecords, _ = net.LookupIP(DEFAULT_RELAY6)
|
||||
for _, ip := range iprecords {
|
||||
DEFAULT_RELAY6 = "[" + ip.String() + "]:" + DEFAULT_PORT
|
||||
fname = path.Join(configFile, "internal-dns")
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
doRemember := false
|
||||
for _, flag := range os.Args {
|
||||
if flag == "--internal-dns" {
|
||||
INTERNAL_DNS = true
|
||||
break
|
||||
}
|
||||
if flag == "--remember" {
|
||||
doRemember = true
|
||||
}
|
||||
}
|
||||
if doRemember {
|
||||
// save in config file
|
||||
fname, err := getConfigFile(true)
|
||||
if err == nil {
|
||||
f, _ := os.Create(fname)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
if !INTERNAL_DNS {
|
||||
fname, err := getConfigFile(false)
|
||||
if err == nil {
|
||||
INTERNAL_DNS = utils.Exists(fname)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
var addr string
|
||||
addr, err = lookup(DEFAULT_RELAY)
|
||||
if err == nil {
|
||||
DEFAULT_RELAY = net.JoinHostPort(addr, DEFAULT_PORT)
|
||||
} else {
|
||||
DEFAULT_RELAY = ""
|
||||
}
|
||||
addr, err = lookup(DEFAULT_RELAY6)
|
||||
if err == nil {
|
||||
DEFAULT_RELAY6 = net.JoinHostPort(addr, DEFAULT_PORT)
|
||||
} else {
|
||||
DEFAULT_RELAY6 = ""
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve a hostname to an IP address using DNS.
|
||||
func lookup(address string) (ipaddress string, err error) {
|
||||
if !INTERNAL_DNS {
|
||||
return localLookupIP(address)
|
||||
}
|
||||
type Result struct {
|
||||
s string
|
||||
err error
|
||||
}
|
||||
result := make(chan Result, len(publicDNS))
|
||||
for _, dns := range publicDNS {
|
||||
go func(dns string) {
|
||||
var r Result
|
||||
r.s, r.err = remoteLookupIP(address, dns)
|
||||
result <- r
|
||||
}(dns)
|
||||
}
|
||||
for i := 0; i < len(publicDNS); i++ {
|
||||
ipaddress = (<-result).s
|
||||
if ipaddress != "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("failed to resolve %s: all DNS servers exhausted", address)
|
||||
return
|
||||
}
|
||||
|
||||
// localLookupIP returns a host's IP address based on the local resolver.
|
||||
func localLookupIP(address string) (ipaddress string, err error) {
|
||||
ip, err := net.LookupHost(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ipaddress = ip[0]
|
||||
return
|
||||
}
|
||||
|
||||
// remoteLookupIP returns a host's IP address based on a remote DNS server.
|
||||
func remoteLookupIP(address, dns string) (ipaddress string, err error) {
|
||||
r := &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||
d := new(net.Dialer)
|
||||
return d.DialContext(ctx, network, dns+":53")
|
||||
},
|
||||
}
|
||||
ip, err := r.LookupHost(context.Background(), address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ipaddress = ip[0]
|
||||
return
|
||||
}
|
||||
|
|
|
@ -9,14 +9,15 @@ import (
|
|||
"time"
|
||||
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/schollz/pake/v2"
|
||||
"github.com/schollz/pake/v3"
|
||||
|
||||
"github.com/schollz/croc/v8/src/comm"
|
||||
"github.com/schollz/croc/v8/src/crypt"
|
||||
"github.com/schollz/croc/v8/src/models"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/crypt"
|
||||
"github.com/schollz/croc/v10/src/models"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
host string
|
||||
port string
|
||||
debugLevel string
|
||||
banner string
|
||||
|
@ -36,12 +37,14 @@ type roomMap struct {
|
|||
sync.Mutex
|
||||
}
|
||||
|
||||
const pingRoom = "pinglkasjdlfjsaldjf"
|
||||
|
||||
var timeToRoomDeletion = 10 * time.Minute
|
||||
var pingRoom = "pinglkasjdlfjsaldjf"
|
||||
|
||||
// Run starts a tcp listener, run async
|
||||
func Run(debugLevel, port, password string, banner ...string) (err error) {
|
||||
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
|
||||
s := new(server)
|
||||
s.host = host
|
||||
s.port = port
|
||||
s.password = password
|
||||
s.debugLevel = debugLevel
|
||||
|
@ -85,10 +88,32 @@ func (s *server) start() (err error) {
|
|||
}
|
||||
|
||||
func (s *server) run() (err error) {
|
||||
log.Infof("starting TCP server on " + s.port)
|
||||
server, err := net.Listen("tcp", ":"+s.port)
|
||||
network := "tcp"
|
||||
addr := net.JoinHostPort(s.host, s.port)
|
||||
if s.host != "" {
|
||||
ip := net.ParseIP(s.host)
|
||||
if ip == nil {
|
||||
var tcpIP *net.IPAddr
|
||||
tcpIP, err = net.ResolveIPAddr("ip", s.host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip = tcpIP.IP
|
||||
}
|
||||
addr = net.JoinHostPort(ip.String(), s.port)
|
||||
if s.host != "" {
|
||||
if ip.To4() != nil {
|
||||
network = "tcp4"
|
||||
} else {
|
||||
network = "tcp6"
|
||||
}
|
||||
}
|
||||
}
|
||||
addr = strings.Replace(addr, "127.0.0.1", "0.0.0.0", 1)
|
||||
log.Infof("starting TCP server on " + addr)
|
||||
server, err := net.Listen(network, addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error listening on %s: %w", s.port, err)
|
||||
return fmt.Errorf("error listening on %s: %w", addr, err)
|
||||
}
|
||||
defer server.Close()
|
||||
// spawn a new goroutine whenever a client connects
|
||||
|
@ -152,7 +177,7 @@ var weakKey = []byte{1, 2, 3}
|
|||
|
||||
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
||||
// establish secure password with PAKE for communication with relay
|
||||
B, err := pake.InitCurve(weakKey, 1, "siec", 1*time.Microsecond)
|
||||
B, err := pake.InitCurve(weakKey, 1, "siec")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -160,8 +185,10 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("Abytes: %s", Abytes)
|
||||
if bytes.Equal(Abytes, []byte("ping")) {
|
||||
room = pingRoom
|
||||
log.Debug("sending back pong")
|
||||
c.Send([]byte("pong"))
|
||||
return
|
||||
}
|
||||
|
@ -170,11 +197,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
return
|
||||
}
|
||||
err = c.Send(B.Bytes())
|
||||
Abytes, err = c.Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = B.Update(Abytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -186,6 +208,9 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
|
||||
// receive salt
|
||||
salt, err := c.Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
strongKeyForEncryption, _, err := crypt.New(strongKey, salt)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -202,8 +227,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
}
|
||||
if strings.TrimSpace(string(passwordBytes)) != s.password {
|
||||
err = fmt.Errorf("bad password")
|
||||
enc, _ := crypt.Decrypt([]byte(err.Error()), strongKeyForEncryption)
|
||||
if err := c.Send(enc); err != nil {
|
||||
enc, _ := crypt.Encrypt([]byte(err.Error()), strongKeyForEncryption)
|
||||
if err = c.Send(enc); err != nil {
|
||||
return "", fmt.Errorf("send error: %w", err)
|
||||
}
|
||||
return
|
||||
|
@ -268,7 +293,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
err = c.Send(bSend)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
s.deleteRoom(room)
|
||||
return
|
||||
}
|
||||
return
|
||||
|
@ -327,11 +351,11 @@ func (s *server) deleteRoom(room string) {
|
|||
}
|
||||
s.rooms.rooms[room] = roomInfo{first: nil, second: nil}
|
||||
delete(s.rooms.rooms, room)
|
||||
|
||||
}
|
||||
|
||||
// chanFromConn creates a channel from a Conn object, and sends everything it
|
||||
// Read()s from the socket to the channel.
|
||||
//
|
||||
// Read()s from the socket to the channel.
|
||||
func chanFromConn(conn net.Conn) chan []byte {
|
||||
c := make(chan []byte, 1)
|
||||
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
|
@ -388,16 +412,20 @@ func pipe(conn1 net.Conn, conn2 net.Conn) {
|
|||
}
|
||||
|
||||
func PingServer(address string) (err error) {
|
||||
c, err := comm.NewConnection(address, 200*time.Millisecond)
|
||||
log.Debugf("pinging %s", address)
|
||||
c, err := comm.NewConnection(address, 300*time.Millisecond)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
err = c.Send([]byte("ping"))
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
b, err := c.Receive()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
if bytes.Equal(b, []byte("pong")) {
|
||||
|
@ -415,87 +443,104 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
|
|||
c, err = comm.NewConnection(address)
|
||||
}
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
// get PAKE connection with server to establish strong key to transfer info
|
||||
A, err := pake.InitCurve(weakKey, 0, "siec", 1*time.Microsecond)
|
||||
A, err := pake.InitCurve(weakKey, 0, "siec")
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
err = c.Send(A.Bytes())
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
Bbytes, err := c.Receive()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
err = A.Update(Bbytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = c.Send(A.Bytes())
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
strongKey, err := A.SessionKey()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debugf("strong key: %x", strongKey)
|
||||
|
||||
strongKeyForEncryption, salt, err := crypt.New(strongKey, nil)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
// send salt
|
||||
err = c.Send(salt)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug("sending password")
|
||||
bSend, err := crypt.Encrypt([]byte(password), strongKeyForEncryption)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
err = c.Send(bSend)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("waiting for first ok")
|
||||
enc, err := c.Receive()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
data, err := crypt.Decrypt(enc, strongKeyForEncryption)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
if !strings.Contains(string(data), "|||") {
|
||||
err = fmt.Errorf("bad response: %s", string(data))
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
banner = strings.Split(string(data), "|||")[0]
|
||||
ipaddr = strings.Split(string(data), "|||")[1]
|
||||
log.Debug("sending room")
|
||||
log.Debugf("sending room; %s", room)
|
||||
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
err = c.Send(bSend)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("waiting for room confirmation")
|
||||
enc, err = c.Receive()
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
data, err = crypt.Decrypt(enc, strongKeyForEncryption)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
if !bytes.Equal(data, []byte("ok")) {
|
||||
err = fmt.Errorf("got bad response: %s", data)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("all set")
|
||||
|
|
|
@ -12,11 +12,11 @@ import (
|
|||
|
||||
func BenchmarkConnection(b *testing.B) {
|
||||
log.SetLevel("trace")
|
||||
go Run("debug", "8283", "pass123", "8284")
|
||||
go Run("debug", "127.0.0.1", "8283", "pass123", "8284")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c, _, _, _ := ConnectToTCPServer("localhost:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
||||
c, _, _, _ := ConnectToTCPServer("127.0.0.1:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
@ -24,22 +24,22 @@ func BenchmarkConnection(b *testing.B) {
|
|||
func TestTCP(t *testing.T) {
|
||||
log.SetLevel("error")
|
||||
timeToRoomDeletion = 100 * time.Millisecond
|
||||
go Run("debug", "8281", "pass123", "8282")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
err := PingServer("localhost:8281")
|
||||
go Run("debug", "127.0.0.1", "8381", "pass123", "8382")
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
err := PingServer("127.0.0.1:8381")
|
||||
assert.Nil(t, err)
|
||||
err = PingServer("localhost:8333")
|
||||
err = PingServer("127.0.0.1:8333")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Minute)
|
||||
assert.Equal(t, banner, "8282")
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Minute)
|
||||
assert.Equal(t, banner, "8382")
|
||||
assert.Nil(t, err)
|
||||
c2, _, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
|
||||
c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
||||
assert.Nil(t, err)
|
||||
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
|
||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
||||
assert.NotNil(t, err)
|
||||
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Nanosecond)
|
||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Nanosecond)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// try sending data
|
||||
|
|
|
@ -1,28 +1,64 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
"github.com/kalafut/imohash"
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/schollz/mnemonicode"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
const NbPinNumbers = 4
|
||||
const NbBytesWords = 4
|
||||
|
||||
// Get or create home directory
|
||||
func GetConfigDir(requireValidPath bool) (homedir string, err error) {
|
||||
if envHomedir, isSet := os.LookupEnv("CROC_CONFIG_DIR"); isSet {
|
||||
homedir = envHomedir
|
||||
} else if xdgConfigHome, isSet := os.LookupEnv("XDG_CONFIG_HOME"); isSet {
|
||||
homedir = path.Join(xdgConfigHome, "croc")
|
||||
} else {
|
||||
homedir, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
if !requireValidPath {
|
||||
err = nil
|
||||
homedir = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
homedir = path.Join(homedir, ".config", "croc")
|
||||
}
|
||||
|
||||
if requireValidPath {
|
||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(homedir, 0o700)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Exists reports whether the named file or directory exists.
|
||||
func Exists(name string) bool {
|
||||
if _, err := os.Stat(name); err != nil {
|
||||
|
@ -41,13 +77,83 @@ func GetInput(prompt string) string {
|
|||
return strings.TrimSpace(text)
|
||||
}
|
||||
|
||||
// HashFile returns the hash of a file
|
||||
func HashFile(fname string) (hash256 []byte, err error) {
|
||||
return IMOHashFile(fname)
|
||||
// HashFile returns the hash of a file or, in case of a symlink, the
|
||||
// SHA256 hash of its target. Takes an argument to specify the algorithm to use.
|
||||
func HashFile(fname string, algorithm string, showProgress ...bool) (hash256 []byte, err error) {
|
||||
doShowProgress := false
|
||||
if len(showProgress) > 0 {
|
||||
doShowProgress = showProgress[0]
|
||||
}
|
||||
var fstats os.FileInfo
|
||||
fstats, err = os.Lstat(fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if fstats.Mode()&os.ModeSymlink != 0 {
|
||||
var target string
|
||||
target, err = os.Readlink(fname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(SHA256(target)), nil
|
||||
}
|
||||
switch algorithm {
|
||||
case "imohash":
|
||||
return IMOHashFile(fname)
|
||||
case "md5":
|
||||
return MD5HashFile(fname, doShowProgress)
|
||||
case "xxhash":
|
||||
return XXHashFile(fname, doShowProgress)
|
||||
case "highway":
|
||||
return HighwayHashFile(fname, doShowProgress)
|
||||
}
|
||||
err = fmt.Errorf("unspecified algorithm")
|
||||
return
|
||||
}
|
||||
|
||||
// HighwayHashFile returns highwayhash of a file
|
||||
func HighwayHashFile(fname string, doShowProgress bool) (hashHighway []byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
key, err := hex.DecodeString("1553c5383fb0b86578c3310da665b4f6e0521acf22eb58a99532ffed02a6b115")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
h, err := highwayhash.New(key)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not create highwayhash: %s", err.Error())
|
||||
return
|
||||
}
|
||||
if doShowProgress {
|
||||
stat, _ := f.Stat()
|
||||
fnameShort := path.Base(fname)
|
||||
if len(fnameShort) > 20 {
|
||||
fnameShort = fnameShort[:20] + "..."
|
||||
}
|
||||
bar := progressbar.NewOptions64(stat.Size(),
|
||||
progressbar.OptionSetWriter(os.Stderr),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
)
|
||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hashHighway = h.Sum(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// MD5HashFile returns MD5 hash
|
||||
func MD5HashFile(fname string) (hash256 []byte, err error) {
|
||||
func MD5HashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -55,8 +161,25 @@ func MD5HashFile(fname string) (hash256 []byte, err error) {
|
|||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
if doShowProgress {
|
||||
stat, _ := f.Stat()
|
||||
fnameShort := path.Base(fname)
|
||||
if len(fnameShort) > 20 {
|
||||
fnameShort = fnameShort[:20] + "..."
|
||||
}
|
||||
bar := progressbar.NewOptions64(stat.Size(),
|
||||
progressbar.OptionSetWriter(os.Stderr),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
)
|
||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hash256 = h.Sum(nil)
|
||||
|
@ -70,8 +193,17 @@ func IMOHashFile(fname string) (hash []byte, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
var imofull = imohash.NewCustom(0, 0)
|
||||
|
||||
// IMOHashFileFull returns imohash of full file
|
||||
func IMOHashFileFull(fname string) (hash []byte, err error) {
|
||||
b, err := imofull.SumFile(fname)
|
||||
hash = b[:]
|
||||
return
|
||||
}
|
||||
|
||||
// XXHashFile returns the xxhash of a file
|
||||
func XXHashFile(fname string) (hash256 []byte, err error) {
|
||||
func XXHashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
|
||||
f, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -79,8 +211,25 @@ func XXHashFile(fname string) (hash256 []byte, err error) {
|
|||
defer f.Close()
|
||||
|
||||
h := xxhash.New()
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
if doShowProgress {
|
||||
stat, _ := f.Stat()
|
||||
fnameShort := path.Base(fname)
|
||||
if len(fnameShort) > 20 {
|
||||
fnameShort = fnameShort[:20] + "..."
|
||||
}
|
||||
bar := progressbar.NewOptions64(stat.Size(),
|
||||
progressbar.OptionSetWriter(os.Stderr),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
)
|
||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hash256 = h.Sum(nil)
|
||||
|
@ -103,7 +252,7 @@ func PublicIP() (ip string, err error) {
|
|||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -125,18 +274,32 @@ func LocalIP() string {
|
|||
return localAddr.IP.String()
|
||||
}
|
||||
|
||||
// GetRandomName returns mnemoicoded random name
|
||||
func GenerateRandomPin() string {
|
||||
s := ""
|
||||
max := new(big.Int)
|
||||
max.SetInt64(9)
|
||||
for i := 0; i < NbPinNumbers; i++ {
|
||||
v, err := rand.Int(rand.Reader, max)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s += fmt.Sprintf("%d", v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetRandomName returns mnemonicoded random name
|
||||
func GetRandomName() string {
|
||||
var result []string
|
||||
bs := make([]byte, 4)
|
||||
bs := make([]byte, NbBytesWords)
|
||||
rand.Read(bs)
|
||||
result = mnemonicode.EncodeWordList(result, bs)
|
||||
return strings.Join(result, "-")
|
||||
return GenerateRandomPin() + "-" + strings.Join(result, "-")
|
||||
}
|
||||
|
||||
// ByteCountDecimal converts bytes to human readable byte string
|
||||
func ByteCountDecimal(b int64) string {
|
||||
const unit = 1000
|
||||
const unit = 1024
|
||||
if b < unit {
|
||||
return fmt.Sprintf("%d B", b)
|
||||
}
|
||||
|
@ -197,7 +360,6 @@ func MissingChunks(fname string, fsize int64, chunkSize int) (chunkRanges []int6
|
|||
}
|
||||
}
|
||||
chunkRanges = append(chunkRanges, int64(curCount+1))
|
||||
chunks = chunkRanges
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -236,7 +398,7 @@ func GetLocalIPs() (ips []string, err error) {
|
|||
}
|
||||
|
||||
func RandomFileName() (fname string, err error) {
|
||||
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -286,7 +448,7 @@ func init() {
|
|||
}
|
||||
|
||||
func IsLocalIP(ipaddress string) bool {
|
||||
if strings.Contains(ipaddress, "localhost") {
|
||||
if strings.Contains(ipaddress, "127.0.0.1") {
|
||||
return true
|
||||
}
|
||||
host, _, _ := net.SplitHostPort(ipaddress)
|
||||
|
@ -301,3 +463,127 @@ func IsLocalIP(ipaddress string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ZipDirectory(destination string, source string) (err error) {
|
||||
if _, err = os.Stat(destination); err == nil {
|
||||
log.Fatalf("%s file already exists!\n", destination)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Zipping %s to %s\n", source, destination)
|
||||
file, err := os.Create(destination)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer file.Close()
|
||||
writer := zip.NewWriter(file)
|
||||
// no compression because croc does its compression on the fly
|
||||
writer.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||
return flate.NewWriter(out, flate.NoCompression)
|
||||
})
|
||||
defer writer.Close()
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if info.Mode().IsRegular() {
|
||||
f1, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer f1.Close()
|
||||
zipPath := strings.ReplaceAll(path, source, strings.TrimSuffix(destination, ".zip"))
|
||||
zipPath = filepath.ToSlash(zipPath)
|
||||
w1, err := writer.Create(zipPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if _, err := io.Copy(w1, f1); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
||||
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnzipDirectory(destination string, source string) error {
|
||||
archive, err := zip.OpenReader(source)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
for _, f := range archive.File {
|
||||
filePath := filepath.Join(destination, f.Name)
|
||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
||||
fmt.Fprintf(os.Stderr, "\rUnzipping file %s", filePath)
|
||||
// Issue #593 conceal path traversal vulnerability
|
||||
// make sure the filepath does not have ".."
|
||||
filePath = filepath.Clean(filePath)
|
||||
if strings.Contains(filePath, "..") {
|
||||
log.Fatalf("Invalid file path %s\n", filePath)
|
||||
}
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(filePath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// check if file exists
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) ", filePath)
|
||||
choice := strings.ToLower(GetInput(prompt))
|
||||
if choice != "y" && choice != "yes" {
|
||||
fmt.Fprintf(os.Stderr, "Skipping '%s'\n", filePath)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fileInArchive, err := f.Open()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
dstFile.Close()
|
||||
fileInArchive.Close()
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidFileName checks if a filename is valid
|
||||
// by making sure it has no invisible characters
|
||||
func ValidFileName(fname string) bool {
|
||||
clean1 := strings.Map(func(r rune) rune {
|
||||
if unicode.IsGraphic(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, fname)
|
||||
|
||||
clean2 := strings.Map(func(r rune) rune {
|
||||
if unicode.IsPrint(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, fname)
|
||||
|
||||
return (fname == clean1) && (fname == clean2)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package utils
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
@ -13,15 +12,19 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const TCP_BUFFER_SIZE = 1024 * 64
|
||||
|
||||
var bigFileSize = 75000000
|
||||
|
||||
func bigFile() {
|
||||
ioutil.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), 75000000), 0666)
|
||||
os.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0o666)
|
||||
}
|
||||
|
||||
func BenchmarkMD5(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
MD5HashFile("bigfile.test")
|
||||
MD5HashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,9 +32,10 @@ func BenchmarkXXHash(b *testing.B) {
|
|||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
XXHashFile("bigfile.test")
|
||||
XXHashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkImoHash(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
|
@ -40,6 +44,22 @@ func BenchmarkImoHash(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkHighwayHash(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
HighwayHashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkImoHashFull(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IMOHashFileFull("bigfile.test")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha256(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
@ -47,6 +67,14 @@ func BenchmarkSha256(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkMissingChunks(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
MissingChunks("bigfile.test", int64(bigFileSize), TCP_BUFFER_SIZE/2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
|
@ -58,10 +86,20 @@ func TestExists(t *testing.T) {
|
|||
func TestMD5HashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := MD5HashFile("bigfile.test")
|
||||
b, err := MD5HashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "8304ff018e02baad0e3555bade29a405", fmt.Sprintf("%x", b))
|
||||
_, err = MD5HashFile("bigfile.test.nofile")
|
||||
_, err = MD5HashFile("bigfile.test.nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestHighwayHashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := HighwayHashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "3c32999529323ed66a67aeac5720c7bf1301dcc5dca87d8d46595e85ff990329", fmt.Sprintf("%x", b))
|
||||
_, err = HighwayHashFile("bigfile.test.nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
@ -76,10 +114,10 @@ func TestIMOHashFile(t *testing.T) {
|
|||
func TestXXHashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := XXHashFile("bigfile.test")
|
||||
b, err := XXHashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "4918740eb5ccb6f7", fmt.Sprintf("%x", b))
|
||||
_, err = XXHashFile("nofile")
|
||||
_, err = XXHashFile("nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
@ -88,9 +126,9 @@ func TestSHA256(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestByteCountDecimal(t *testing.T) {
|
||||
assert.Equal(t, "10.0 kB", ByteCountDecimal(10000))
|
||||
assert.Equal(t, "10.0 kB", ByteCountDecimal(10240))
|
||||
assert.Equal(t, "50 B", ByteCountDecimal(50))
|
||||
assert.Equal(t, "12.4 MB", ByteCountDecimal(12378517))
|
||||
assert.Equal(t, "12.4 MB", ByteCountDecimal(13002343))
|
||||
}
|
||||
|
||||
func TestMissingChunks(t *testing.T) {
|
||||
|
@ -99,9 +137,9 @@ func TestMissingChunks(t *testing.T) {
|
|||
rand.Seed(1)
|
||||
bigBuff := make([]byte, fileSize)
|
||||
rand.Read(bigBuff)
|
||||
ioutil.WriteFile("missing.test", bigBuff, 0644)
|
||||
os.WriteFile("missing.test", bigBuff, 0o644)
|
||||
empty := make([]byte, chunkSize)
|
||||
f, err := os.OpenFile("missing.test", os.O_RDWR, 0644)
|
||||
f, err := os.OpenFile("missing.test", os.O_RDWR, 0o644)
|
||||
assert.Nil(t, err)
|
||||
for block := 0; block < fileSize/chunkSize; block++ {
|
||||
if block == 0 || block == 4 || block == 5 || block >= 7 {
|
||||
|
@ -119,7 +157,7 @@ func TestMissingChunks(t *testing.T) {
|
|||
os.Remove("missing.test")
|
||||
|
||||
content := []byte("temporary file's content")
|
||||
tmpfile, err := ioutil.TempFile("", "example")
|
||||
tmpfile, err := os.CreateTemp("", "example")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -151,22 +189,22 @@ func TestMissingChunks(t *testing.T) {
|
|||
|
||||
func TestHashFile(t *testing.T) {
|
||||
content := []byte("temporary file's content")
|
||||
tmpfile, err := ioutil.TempFile("", "example")
|
||||
tmpfile, err := os.CreateTemp("", "example")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
if _, err = tmpfile.Write(content); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hashed, err := HashFile(tmpfile.Name())
|
||||
hashed, err := HashFile(tmpfile.Name(), "xxhash")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "18c9673a4bb8325d323e7f24fda9ae1e", fmt.Sprintf("%x", hashed))
|
||||
assert.Equal(t, "e66c561610ad51e2", fmt.Sprintf("%x", hashed))
|
||||
}
|
||||
|
||||
func TestPublicIP(t *testing.T) {
|
||||
|
@ -184,14 +222,41 @@ func TestLocalIP(t *testing.T) {
|
|||
|
||||
func TestGetRandomName(t *testing.T) {
|
||||
name := GetRandomName()
|
||||
fmt.Println(name)
|
||||
assert.NotEmpty(t, name)
|
||||
}
|
||||
|
||||
func intSliceSame(a, b []int) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i, v := range a {
|
||||
if v != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestFindOpenPorts(t *testing.T) {
|
||||
openPorts := FindOpenPorts("localhost", 9009, 4)
|
||||
assert.Equal(t, []int{9009, 9010, 9011, 9012}, openPorts)
|
||||
openPorts := FindOpenPorts("127.0.0.1", 9009, 4)
|
||||
if !intSliceSame(openPorts, []int{9009, 9010, 9011, 9012}) && !intSliceSame(openPorts, []int{9014, 9015, 9016, 9017}) {
|
||||
t.Errorf("openPorts: %v", openPorts)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLocalIP(t *testing.T) {
|
||||
assert.True(t, IsLocalIP("192.168.0.14:9009"))
|
||||
}
|
||||
|
||||
func TestValidFileName(t *testing.T) {
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("中文.csl"))
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("[something].csl"))
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("[(something)].csl"))
|
||||
// contains invisible character
|
||||
assert.False(t, ValidFileName("D中文.cslouglas"))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue