Compare commits
183 Commits
schollz/is
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6012939f3 | ||
|
|
bda3dcc670 | ||
|
|
a96e7945e1 | ||
|
|
278e1b4099 | ||
|
|
c369c78b42 | ||
|
|
a61a461436 | ||
|
|
d616b8fff7 | ||
|
|
3252f2abb2 | ||
|
|
53323cb8c4 | ||
|
|
d7ebb3d97c | ||
|
|
30684fb383 | ||
|
|
09376c5c4b | ||
|
|
917d9f16b4 | ||
|
|
1adf845284 | ||
|
|
10bd3b47e9 | ||
|
|
0d48e346ad | ||
|
|
234eb86f69 | ||
|
|
856e1c966e | ||
|
|
12cc09f6e5 | ||
|
|
7470ed1ea2 | ||
|
|
555cfc085a | ||
|
|
325cff6661 | ||
|
|
71369dee36 | ||
|
|
146f6413a9 | ||
|
|
38acf6f7e3 | ||
|
|
81d3593164 | ||
|
|
3ca13a405f | ||
|
|
b0a01a6634 | ||
|
|
c1392acb70 | ||
|
|
73ca44f064 | ||
|
|
a7235e1fcd | ||
|
|
7bb1ea7a01 | ||
|
|
4266ff0e41 | ||
|
|
2d9769837f | ||
|
|
c275151ae4 | ||
|
|
9231dc6bdd | ||
|
|
2e18768770 | ||
|
|
5c5e00310a | ||
|
|
9be175f1b0 | ||
|
|
8c4594ad31 | ||
|
|
519ce8c669 | ||
|
|
76b1df5096 | ||
|
|
d6f1a63b7b | ||
|
|
d0da7aafa2 | ||
|
|
ca8c36f38b | ||
|
|
f9241b73fd | ||
|
|
bb74eafd36 | ||
|
|
149d7364fb | ||
|
|
cdf3aa0a31 | ||
|
|
e663aa90cb | ||
|
|
986d005449 | ||
|
|
0d8e4e10d6 | ||
|
|
95de3790d7 | ||
|
|
defee4b4f6 | ||
|
|
c146b150b8 | ||
|
|
a29b8e6d1d | ||
|
|
4c1facd68e | ||
|
|
0f1d75a2ef | ||
|
|
0017def27c | ||
|
|
319a403082 | ||
|
|
90ed1f797e | ||
|
|
bc9dbfd905 | ||
|
|
01609df902 | ||
|
|
6fbb02c196 | ||
|
|
1b81055552 | ||
|
|
de623679fb | ||
|
|
f8cf2eeb82 | ||
|
|
565f252628 | ||
|
|
d49aaa05fe | ||
|
|
df5b2994f6 | ||
|
|
1a43826308 | ||
|
|
69af916d36 | ||
|
|
bf26db019c | ||
|
|
35bf1548c4 | ||
|
|
f7bad72f6a | ||
|
|
f32d060bef | ||
|
|
6f3e0f1eaf | ||
|
|
8681a4dc6d | ||
|
|
07c0a804d7 | ||
|
|
5b93408c6e | ||
|
|
dccd6fe395 | ||
|
|
5a6f36626d | ||
|
|
677f44da6d | ||
|
|
66efe74094 | ||
|
|
5da253e940 | ||
|
|
e439b65daa | ||
|
|
6413c1c3e1 | ||
|
|
ce3c65ef1d | ||
|
|
42bb64c434 | ||
|
|
8f226f72b6 | ||
|
|
4f1a6a8d4f | ||
|
|
9235c341f9 | ||
|
|
b2f446fd6b | ||
|
|
5ea4661e4b | ||
|
|
522ef9ef23 | ||
|
|
bc8b4b8ef9 | ||
|
|
d7c7440ae4 | ||
|
|
527af8a612 | ||
|
|
da51eb8da3 | ||
|
|
b5da962bd1 | ||
|
|
72e51d8c2a | ||
|
|
c9bc41fc58 | ||
|
|
94cc704928 | ||
|
|
6b25ca4bff | ||
|
|
8577745d74 | ||
|
|
232e162f8c | ||
|
|
4c120c4963 | ||
|
|
7c3dc4453a | ||
|
|
3e804a4a09 | ||
|
|
907f2b3824 | ||
|
|
05da5007ed | ||
|
|
791e4c22d2 | ||
|
|
dd3e3ff1ab | ||
|
|
44f2b94164 | ||
|
|
6c7120edbb | ||
|
|
1e34537bb2 | ||
|
|
0d0effd983 | ||
|
|
e85575f3a5 | ||
|
|
0afdcd7005 | ||
|
|
ca8f011844 | ||
|
|
55518176e1 | ||
|
|
e502cf0c82 | ||
|
|
5cf4d7c103 | ||
|
|
c28c4786a1 | ||
|
|
6f974d96e8 | ||
|
|
c2cb15cb73 | ||
|
|
2d648aa580 | ||
|
|
80074b3727 | ||
|
|
e47ca8fbcb | ||
|
|
d011290559 | ||
|
|
50e0f625bc | ||
|
|
94af2374c3 | ||
|
|
a4322faa25 | ||
|
|
23dce2aa3e | ||
|
|
88002b322d | ||
|
|
9246408278 | ||
|
|
fbf1eeedce | ||
|
|
e4c9f2d9fb | ||
|
|
c0c3370d9b | ||
|
|
f6bd13fa06 | ||
|
|
f83616e9bd | ||
|
|
23f385ab2f | ||
|
|
78feb393de | ||
|
|
69fc3cee47 | ||
|
|
a5da77cf49 | ||
|
|
f66e17dd46 | ||
|
|
63c9201938 | ||
|
|
ca7a5979cc | ||
|
|
fc457557e0 | ||
|
|
f044f4dd86 | ||
|
|
a2e71c7e1a | ||
|
|
dff34fa7fc | ||
|
|
08103bb7bb | ||
|
|
4091ef0496 | ||
|
|
381f8369a3 | ||
|
|
a95d67e31c | ||
|
|
b0920bbe70 | ||
|
|
ed55c746c2 | ||
|
|
8e10eac5c5 | ||
|
|
43f1c53538 | ||
|
|
3acac5d53b | ||
|
|
66f0d1264a | ||
|
|
ee713c5146 | ||
|
|
6181903c83 | ||
|
|
7acd2def69 | ||
|
|
eb0909033e | ||
|
|
f6d862eac0 | ||
|
|
5a6005f1eb | ||
|
|
f6633cbac9 | ||
|
|
d8ef7cda20 | ||
|
|
7622e636e4 | ||
|
|
bb018fd725 | ||
|
|
863dabb93a | ||
|
|
6f5f16aa1c | ||
|
|
0f1ca436cd | ||
|
|
4929635eb8 | ||
|
|
3f12f75fae | ||
|
|
e255d472a6 | ||
|
|
2ffd4daeaf | ||
|
|
accb310337 | ||
|
|
2b4c088100 | ||
|
|
a591833dbf | ||
|
|
b05c3c8c42 |
|
|
@ -1,41 +1,9 @@
|
|||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
name: New issue
|
||||
about: Create an issue
|
||||
title: ''
|
||||
labels: bug
|
||||
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 latest, 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. -->
|
||||
|
||||
|
||||
## To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
<-- 1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error -->
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
|
||||
## Expected behaviour
|
||||
<-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
|
||||
## Version
|
||||
<-- Check "croc -v" and report it -->
|
||||
|
||||
|
||||
## Additional context
|
||||
<-- Add any other context about the problem here. -->
|
||||
|
||||
|
||||
*Read this and delete before submitting:* Thanks for starting a discussion! Please provide as much context as possible so that others can understand your thoughts. If you have a specific change in mind, consider submitting a pull request instead.
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
|
@ -13,6 +13,40 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.23'
|
||||
- run: go version
|
||||
- run: go test -v ./...
|
||||
- name: Build files
|
||||
run: |
|
||||
go version
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
||||
GOARM=5 CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20.7' # go1.20.8+ refuses to build go1.22 code...
|
||||
- name: Build Windows 7
|
||||
run: |
|
||||
go version
|
||||
rm go.mod go.sum
|
||||
go mod init github.com/schollz/croc/v10
|
||||
go mod tidy
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ jobs:
|
|||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
|
|
|
|||
|
|
@ -17,17 +17,7 @@ jobs:
|
|||
- 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'
|
||||
go-version: '1.23'
|
||||
- name: Prepare source tarball
|
||||
run: |
|
||||
git clone -b ${{ github.event.release.name }} --depth 1 https://github.com/schollz/croc croc-${{ github.event.release.name }}
|
||||
|
|
@ -49,6 +39,8 @@ jobs:
|
|||
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
|
||||
GOARM=5 CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARMv5.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
|
||||
|
|
@ -71,6 +63,20 @@ jobs:
|
|||
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: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.20.7' # go1.20.8+ refuses to build go1.22 code...
|
||||
- name: Build Windows 7
|
||||
run: |
|
||||
go version
|
||||
rm go.mod go.sum
|
||||
go mod init github.com/schollz/croc/v10
|
||||
go mod tidy
|
||||
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: Create checksums.txt
|
||||
run: |
|
||||
touch croc_${{ github.event.release.name }}_checksums.txt
|
||||
|
|
@ -84,6 +90,7 @@ jobs:
|
|||
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-ARMv5.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
|
||||
|
|
@ -110,6 +117,7 @@ jobs:
|
|||
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-ARMv5.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
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ croc-stdin*
|
|||
.idea/
|
||||
.vscode/
|
||||
src/utils/bigfile.test
|
||||
test1/
|
||||
|
|
|
|||
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/v9/src/compress
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/croc
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/crypt
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/tcp
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/utils
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/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:
|
||||
|
|
|
|||
46
README.md
46
README.md
|
|
@ -4,10 +4,9 @@
|
|||
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-v9.6.15-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.1.1-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 align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
|
||||
|
||||
|
|
@ -105,16 +104,28 @@ On FreeBSD you can install with `pkg`:
|
|||
pkg install croc
|
||||
```
|
||||
|
||||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
|
||||
On Linux, macOS, and Windows you can install from [conda-forge](https://github.com/conda-forge/croc-feedstock/) globally with [`pixi`](https://pixi.sh/):
|
||||
|
||||
```
|
||||
go install github.com/schollz/croc/v9@latest
|
||||
pixi global install croc
|
||||
```
|
||||
|
||||
or into a particular environment with [`conda`](https://docs.conda.io/projects/conda/):
|
||||
|
||||
```
|
||||
conda install --channel conda-forge croc
|
||||
```
|
||||
|
||||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
|
||||
|
||||
```
|
||||
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
|
||||
## Usage
|
||||
|
||||
To send a file, simply do:
|
||||
|
||||
|
|
@ -134,6 +145,25 @@ The code phrase is used to establish password-authenticated key agreement ([PAKE
|
|||
|
||||
There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`.
|
||||
|
||||
### Using `croc` on Linux or Mac OS
|
||||
|
||||
On Linux and Mac OS, the sending & receiving is slightly different to avoid [leaking the secret via the process name](https://nvd.nist.gov/vuln/detail/CVE-2023-43621). On these systems you will need to run `croc` with the secret as an environment variable. For example, to receive with the secret `***`:
|
||||
|
||||
```
|
||||
CROC_SECRET=*** croc
|
||||
```
|
||||
|
||||
This will show only `croc` in the process list of a multi-user system and not leak the secret.
|
||||
|
||||
For a single-user system the default behavior can be permanently enabled by running
|
||||
|
||||
```
|
||||
croc --classic
|
||||
```
|
||||
|
||||
and confirming.
|
||||
Run this command again to disable classic mode.
|
||||
|
||||
### Custom code phrase
|
||||
|
||||
You can send with your own code phrase (must be more than 6 characters).
|
||||
|
|
@ -168,7 +198,7 @@ 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`.
|
||||
|
||||
|
||||
### Send text
|
||||
### Send text
|
||||
|
||||
Sometimes you want to send URLs or short text. In addition to piping, you can easily send text with `croc`:
|
||||
|
||||
|
|
|
|||
33
go.mod
33
go.mod
|
|
@ -1,40 +1,39 @@
|
|||
module github.com/schollz/croc/v9
|
||||
module github.com/schollz/croc/v10
|
||||
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
toolchain go1.23.1
|
||||
|
||||
require (
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/chzyer/readline v1.5.1
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/kalafut/imohash v1.0.3
|
||||
github.com/kalafut/imohash v1.1.0
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b
|
||||
github.com/minio/highwayhash 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.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/schollz/peerdiscovery v1.7.5
|
||||
github.com/schollz/progressbar/v3 v3.17.1
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
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
|
||||
golang.org/x/crypto v0.29.0
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/sys v0.27.0
|
||||
golang.org/x/term v0.26.0
|
||||
golang.org/x/time v0.8.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // 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
|
||||
)
|
||||
|
|
|
|||
83
go.sum
83
go.sum
|
|
@ -1,9 +1,10 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
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/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
|
||||
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
|
||||
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=
|
||||
|
|
@ -11,24 +12,22 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
|
|||
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.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/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.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/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/kalafut/imohash v1.1.0 h1:Lldcmx0SXgMSoABB2WBD8mTgf0OlVnISn2Dyrfg2Ep8=
|
||||
github.com/kalafut/imohash v1.1.0/go.mod h1:6cn9lU0Sj8M4eu9UaQm1kR/5y3k/ayB68yntRhGloL4=
|
||||
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/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
|
||||
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
|
||||
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=
|
||||
|
|
@ -44,20 +43,18 @@ 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.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/schollz/peerdiscovery v1.7.5 h1:0cEhO+o8i4fpeKBwl7u0UY3Kt3XVt5fSzS4rg17ZPb4=
|
||||
github.com/schollz/peerdiscovery v1.7.5/go.mod h1:Crht2FOfD1/eL3U/AIM0vvwVZDPePlBgSX3Xw+TnJoE=
|
||||
github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
|
||||
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
||||
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.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=
|
||||
|
|
@ -70,22 +67,35 @@ github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq
|
|||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
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/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
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/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
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/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
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/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -93,36 +103,45 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
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.12.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/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
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/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
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/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.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/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
|||
32
main.go
32
main.go
|
|
@ -5,9 +5,13 @@ package main
|
|||
//go:generate git tag -af v$VERSION -m "v$VERSION"
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/schollz/croc/v9/src/cli"
|
||||
"github.com/schollz/croc/v10/src/cli"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -27,7 +31,25 @@ func main() {
|
|||
// fmt.Println("wrote profile")
|
||||
// }
|
||||
// }()
|
||||
if err := cli.Run(); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// Create a channel to receive OS signals
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
if err := cli.Run(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
// Exit the program gracefully
|
||||
utils.RemoveMarkedFiles()
|
||||
os.Exit(0)
|
||||
}()
|
||||
|
||||
// Wait for a termination signal
|
||||
_ = <-sigs
|
||||
utils.RemoveMarkedFiles()
|
||||
|
||||
// Exit the program gracefully
|
||||
os.Exit(0)
|
||||
}
|
||||
|
|
|
|||
246
src/cli/cli.go
246
src/cli/cli.go
|
|
@ -15,13 +15,13 @@ import (
|
|||
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/schollz/cli/v2"
|
||||
"github.com/schollz/croc/v9/src/comm"
|
||||
"github.com/schollz/croc/v9/src/croc"
|
||||
"github.com/schollz/croc/v9/src/models"
|
||||
"github.com/schollz/croc/v9/src/tcp"
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/croc"
|
||||
"github.com/schollz/croc/v10/src/mnemonicode"
|
||||
"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"
|
||||
)
|
||||
|
||||
|
|
@ -36,12 +36,15 @@ func Run() (err error) {
|
|||
app := cli.NewApp()
|
||||
app.Name = "croc"
|
||||
if Version == "" {
|
||||
Version = "v9.6.15"
|
||||
Version = "v10.1.0"
|
||||
}
|
||||
app.Version = Version
|
||||
app.Compiled = time.Now()
|
||||
app.Usage = "easily and securely transfer stuff from one computer to another"
|
||||
app.UsageText = `Send a file:
|
||||
app.UsageText = `croc [GLOBAL OPTIONS] [COMMAND] [COMMAND OPTIONS] [filename(s) or folder]
|
||||
|
||||
USAGE EXAMPLES:
|
||||
Send a file:
|
||||
croc send file.txt
|
||||
|
||||
-git to respect your .gitignore
|
||||
|
|
@ -74,6 +77,7 @@ func Run() (err error) {
|
|||
&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"},
|
||||
&cli.BoolFlag{Name: "qrcode", Aliases: []string{"qr"}, Usage: "show receive code as a qrcode"},
|
||||
},
|
||||
HelpName: "croc send",
|
||||
Action: send,
|
||||
|
|
@ -87,11 +91,14 @@ func Run() (err error) {
|
|||
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"},
|
||||
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
|
||||
&cli.IntFlag{Name: "transfers", Value: 5, Usage: "number of ports to use for 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"},
|
||||
|
|
@ -100,8 +107,9 @@ func Run() (err error) {
|
|||
&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: "overwrite", Usage: "do not prompt to overwrite or resume"},
|
||||
&cli.BoolFlag{Name: "testing", Usage: "flag for testing purposes"},
|
||||
&cli.StringFlag{Name: "multicast", Value: "239.255.255.250", Usage: "multicast address to use for local discovery"},
|
||||
&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"}},
|
||||
|
|
@ -125,6 +133,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{}
|
||||
|
|
@ -138,6 +201,7 @@ func Run() (err error) {
|
|||
return send(c)
|
||||
}
|
||||
}
|
||||
|
||||
return receive(c)
|
||||
}
|
||||
|
||||
|
|
@ -148,6 +212,14 @@ func setDebugLevel(c *cli.Context) {
|
|||
if c.Bool("debug") {
|
||||
log.SetLevel("debug")
|
||||
log.Debug("debug mode on")
|
||||
// print the public IP address
|
||||
ip, err := utils.PublicIP()
|
||||
if err == nil {
|
||||
log.Debugf("public IP address: %s", ip)
|
||||
} else {
|
||||
log.Debug(err)
|
||||
}
|
||||
|
||||
} else {
|
||||
log.SetLevel("info")
|
||||
}
|
||||
|
|
@ -162,6 +234,15 @@ func getSendConfigFile(requireValidPath bool) 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 {
|
||||
|
|
@ -200,28 +281,30 @@ func send(c *cli.Context) (err error) {
|
|||
}
|
||||
|
||||
crocOptions := croc.Options{
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: true,
|
||||
Debug: c.Bool("debug"),
|
||||
NoPrompt: c.Bool("yes"),
|
||||
RelayAddress: c.String("relay"),
|
||||
RelayAddress6: c.String("relay6"),
|
||||
Stdout: c.Bool("stdout"),
|
||||
DisableLocal: c.Bool("no-local"),
|
||||
OnlyLocal: c.Bool("local"),
|
||||
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"),
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: true,
|
||||
Debug: c.Bool("debug"),
|
||||
NoPrompt: c.Bool("yes"),
|
||||
RelayAddress: c.String("relay"),
|
||||
RelayAddress6: c.String("relay6"),
|
||||
Stdout: c.Bool("stdout"),
|
||||
DisableLocal: c.Bool("no-local"),
|
||||
OnlyLocal: c.Bool("local"),
|
||||
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"),
|
||||
ShowQrCode: c.Bool("qrcode"),
|
||||
MulticastAddress: c.String("multicast"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
|
|
@ -279,6 +362,7 @@ func send(c *cli.Context) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
utils.MarkFileForRemoval(fnames[0])
|
||||
defer func() {
|
||||
e := os.Remove(fnames[0])
|
||||
if e != nil {
|
||||
|
|
@ -290,6 +374,7 @@ func send(c *cli.Context) (err error) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
utils.MarkFileForRemoval(fnames[0])
|
||||
defer func() {
|
||||
e := os.Remove(fnames[0])
|
||||
if e != nil {
|
||||
|
|
@ -304,6 +389,31 @@ func send(c *cli.Context) (err error) {
|
|||
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:
|
||||
|
||||
CROC_SECRET=**** croc send file.txt
|
||||
|
||||
Or you can have the code phrase automatically 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()
|
||||
|
|
@ -419,20 +529,21 @@ 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,
|
||||
Debug: c.Bool("debug"),
|
||||
NoPrompt: c.Bool("yes"),
|
||||
RelayAddress: c.String("relay"),
|
||||
RelayAddress6: c.String("relay6"),
|
||||
Stdout: c.Bool("stdout"),
|
||||
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"),
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: false,
|
||||
Debug: c.Bool("debug"),
|
||||
NoPrompt: c.Bool("yes"),
|
||||
RelayAddress: c.String("relay"),
|
||||
RelayAddress6: c.String("relay6"),
|
||||
Stdout: c.Bool("stdout"),
|
||||
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"),
|
||||
MulticastAddress: c.String("multicast"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
|
|
@ -495,6 +606,31 @@ func receive(c *cli.Context) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
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:
|
||||
|
||||
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 == "" {
|
||||
l, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "Enter receive code: ",
|
||||
|
|
@ -547,7 +683,25 @@ func relay(c *cli.Context) (err error) {
|
|||
debugString = "debug"
|
||||
}
|
||||
host := c.String("host")
|
||||
ports := strings.Split(c.String("ports"), ",")
|
||||
var ports []string
|
||||
|
||||
if c.IsSet("ports") {
|
||||
ports = strings.Split(c.String("ports"), ",")
|
||||
} else {
|
||||
portString := c.Int("port")
|
||||
if portString == 0 {
|
||||
portString = 9009
|
||||
}
|
||||
transfersString := c.Int("transfers")
|
||||
if transfersString == 0 {
|
||||
transfersString = 4
|
||||
}
|
||||
ports = make([]string, transfersString)
|
||||
for i := range ports {
|
||||
ports[i] = strconv.Itoa(portString + i)
|
||||
}
|
||||
}
|
||||
|
||||
tcpPorts := strings.Join(ports[1:], ",")
|
||||
for i, port := range ports {
|
||||
if i == 0 {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/magisterquis/connectproxy"
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
log "github.com/schollz/logger"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
|
|
|||
473
src/croc/croc.go
473
src/croc/croc.go
|
|
@ -3,36 +3,41 @@ package croc
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/denisbrodbeck/machineid"
|
||||
ignore "github.com/sabhiram/go-gitignore"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/schollz/pake/v3"
|
||||
"github.com/schollz/peerdiscovery"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"golang.org/x/term"
|
||||
"golang.org/x/time/rate"
|
||||
|
||||
"github.com/schollz/croc/v9/src/comm"
|
||||
"github.com/schollz/croc/v9/src/compress"
|
||||
"github.com/schollz/croc/v9/src/crypt"
|
||||
"github.com/schollz/croc/v9/src/message"
|
||||
"github.com/schollz/croc/v9/src/models"
|
||||
"github.com/schollz/croc/v9/src/tcp"
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/schollz/croc/v10/src/comm"
|
||||
"github.com/schollz/croc/v10/src/compress"
|
||||
"github.com/schollz/croc/v10/src/crypt"
|
||||
"github.com/schollz/croc/v10/src/message"
|
||||
"github.com/schollz/croc/v10/src/models"
|
||||
"github.com/schollz/croc/v10/src/tcp"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -55,30 +60,38 @@ func Debug(debug bool) {
|
|||
|
||||
// Options specifies user specific options
|
||||
type Options struct {
|
||||
IsSender bool
|
||||
SharedSecret string
|
||||
Debug bool
|
||||
RelayAddress string
|
||||
RelayAddress6 string
|
||||
RelayPorts []string
|
||||
RelayPassword string
|
||||
Stdout bool
|
||||
NoPrompt bool
|
||||
NoMultiplexing bool
|
||||
DisableLocal bool
|
||||
OnlyLocal bool
|
||||
IgnoreStdin bool
|
||||
Ask bool
|
||||
SendingText bool
|
||||
NoCompress bool
|
||||
IP string
|
||||
Overwrite bool
|
||||
Curve string
|
||||
HashAlgorithm string
|
||||
ThrottleUpload string
|
||||
ZipFolder bool
|
||||
TestFlag bool
|
||||
GitIgnore bool
|
||||
IsSender bool
|
||||
SharedSecret string
|
||||
RoomName string
|
||||
Debug bool
|
||||
RelayAddress string
|
||||
RelayAddress6 string
|
||||
RelayPorts []string
|
||||
RelayPassword string
|
||||
Stdout bool
|
||||
NoPrompt bool
|
||||
NoMultiplexing bool
|
||||
DisableLocal bool
|
||||
OnlyLocal bool
|
||||
IgnoreStdin bool
|
||||
Ask bool
|
||||
SendingText bool
|
||||
NoCompress bool
|
||||
IP string
|
||||
Overwrite bool
|
||||
Curve string
|
||||
HashAlgorithm string
|
||||
ThrottleUpload string
|
||||
ZipFolder bool
|
||||
TestFlag bool
|
||||
GitIgnore bool
|
||||
MulticastAddress string
|
||||
ShowQrCode bool
|
||||
}
|
||||
|
||||
type SimpleMessage struct {
|
||||
Bytes []byte
|
||||
Kind string
|
||||
}
|
||||
|
||||
// Client holds the state of the croc transfer
|
||||
|
|
@ -182,12 +195,15 @@ func New(ops Options) (c *Client, err error) {
|
|||
// setup basic info
|
||||
c.Options = ops
|
||||
Debug(c.Options.Debug)
|
||||
log.Debugf("options: %+v", c.Options)
|
||||
|
||||
if len(c.Options.SharedSecret) < 6 {
|
||||
err = fmt.Errorf("code is too short")
|
||||
return
|
||||
}
|
||||
// Create a hash of part of the shared secret to use as the room name
|
||||
hashExtra := "croc"
|
||||
roomNameBytes := sha256.Sum256([]byte(c.Options.SharedSecret[:4] + hashExtra))
|
||||
c.Options.RoomName = hex.EncodeToString(roomNameBytes[:])
|
||||
|
||||
c.conn = make([]*comm.Comm, 16)
|
||||
|
||||
|
|
@ -214,8 +230,8 @@ func New(ops Options) (c *Client, err error) {
|
|||
panic("Could not parse given Upload Limit")
|
||||
}
|
||||
}
|
||||
// Somehow 4* is necessary
|
||||
rt = rate.Every(time.Second / (4 * time.Duration(uploadLimit)))
|
||||
|
||||
rt = rate.Every(time.Second / time.Duration(uploadLimit))
|
||||
if int(uploadLimit) > minBurstSize {
|
||||
minBurstSize = int(uploadLimit)
|
||||
}
|
||||
|
|
@ -294,7 +310,6 @@ func isChild(parentPath, childPath string) bool {
|
|||
return false
|
||||
}
|
||||
return !strings.HasPrefix(relPath, "..")
|
||||
|
||||
}
|
||||
|
||||
// This function retrieves the important file information
|
||||
|
|
@ -317,7 +332,7 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
|
|||
paths = append(paths, fname)
|
||||
}
|
||||
}
|
||||
var ignoredPaths = make(map[string]bool)
|
||||
ignoredPaths := make(map[string]bool)
|
||||
if ignoreGit {
|
||||
wd, wdErr := os.Stat(".gitignore")
|
||||
if wdErr == nil {
|
||||
|
|
@ -376,6 +391,7 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
|
|||
fpath = filepath.Dir(fpath)
|
||||
dest := filepath.Base(fpath) + ".zip"
|
||||
utils.ZipDirectory(dest, fpath)
|
||||
utils.MarkFileForRemoval(dest)
|
||||
stat, errStat = os.Lstat(dest)
|
||||
if errStat != nil {
|
||||
err = errStat
|
||||
|
|
@ -410,8 +426,14 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName),
|
||||
filepath.Dir(absPath)+string(os.PathSeparator))
|
||||
absPathWithSeparator := filepath.Dir(absPath)
|
||||
if !strings.HasSuffix(absPathWithSeparator, string(os.PathSeparator)) {
|
||||
absPathWithSeparator += string(os.PathSeparator)
|
||||
}
|
||||
if strings.HasSuffix(absPathWithSeparator, string(os.PathSeparator)+string(os.PathSeparator)) {
|
||||
absPathWithSeparator = strings.TrimSuffix(absPathWithSeparator, string(os.PathSeparator))
|
||||
}
|
||||
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName), absPathWithSeparator)
|
||||
if !info.IsDir() {
|
||||
fInfo := FileInfo{
|
||||
Name: info.Name(),
|
||||
|
|
@ -495,7 +517,7 @@ func (c *Client) sendCollectFiles(filesInfo []FileInfo) (err error) {
|
|||
c.Options.HashAlgorithm = "xxhash"
|
||||
}
|
||||
|
||||
c.FilesToTransfer[i].Hash, err = utils.HashFile(fullPath, c.Options.HashAlgorithm)
|
||||
c.FilesToTransfer[i].Hash, err = utils.HashFile(fullPath, c.Options.HashAlgorithm, fileInfo.Size > 1e7)
|
||||
log.Debugf("hashed %s to %x using %s", fullPath, c.FilesToTransfer[i].Hash, c.Options.HashAlgorithm)
|
||||
totalFilesSize += fileInfo.Size
|
||||
if err != nil {
|
||||
|
|
@ -568,6 +590,8 @@ func (c *Client) broadcastOnLocalNetwork(useipv6 bool) {
|
|||
}
|
||||
if useipv6 {
|
||||
settings.IPVersion = peerdiscovery.IPv6
|
||||
} else {
|
||||
settings.MulticastAddress = c.Options.MulticastAddress
|
||||
}
|
||||
|
||||
discoveries, err := peerdiscovery.Discover(settings)
|
||||
|
|
@ -582,7 +606,7 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) {
|
|||
time.Sleep(500 * time.Millisecond)
|
||||
log.Debug("establishing connection")
|
||||
var banner string
|
||||
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.SharedSecret[:3])
|
||||
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.RoomName)
|
||||
log.Debugf("banner: %s", banner)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("could not connect to 127.0.0.1:%s: %w", c.Options.RelayPorts[0], err)
|
||||
|
|
@ -629,7 +653,18 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
if c.Options.RelayPassword != models.DEFAULT_PASSPHRASE {
|
||||
flags.WriteString("--pass " + c.Options.RelayPassword + " ")
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Code is: %[1]s\nOn the other computer run\n\ncroc %[2]s%[1]s\n", c.Options.SharedSecret, flags.String())
|
||||
fmt.Fprintf(os.Stderr, `Code is: %[1]s
|
||||
|
||||
On the other computer run:
|
||||
(For Windows)
|
||||
croc %[2]s%[1]s
|
||||
(For Linux/OSX)
|
||||
CROC_SECRET=%[1]q croc %[2]s
|
||||
`, c.Options.SharedSecret, flags.String())
|
||||
copyToClipboard(c.Options.SharedSecret)
|
||||
if c.Options.ShowQrCode {
|
||||
showReceiveCommandQrCode(fmt.Sprintf("%[1]s", c.Options.SharedSecret))
|
||||
}
|
||||
if c.Options.Ask {
|
||||
machid, _ := machineid.ID()
|
||||
fmt.Fprintf(os.Stderr, "\rYour machine ID is '%s'\n", machid)
|
||||
|
|
@ -670,7 +705,7 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
log.Debugf("got host '%v' and port '%v'", host, port)
|
||||
address = net.JoinHostPort(host, port)
|
||||
log.Debugf("trying connection to %s", address)
|
||||
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
|
||||
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RoomName, durations[i])
|
||||
if err == nil {
|
||||
c.Options.RelayAddress = address
|
||||
break
|
||||
|
|
@ -688,13 +723,34 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
}
|
||||
log.Debugf("banner: %s", banner)
|
||||
log.Debugf("connection established: %+v", conn)
|
||||
var kB []byte
|
||||
B, _ := pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 1, c.Options.Curve)
|
||||
for {
|
||||
log.Debug("waiting for bytes")
|
||||
var dataMessage SimpleMessage
|
||||
log.Trace("waiting for bytes")
|
||||
data, errConn := conn.Receive()
|
||||
if errConn != nil {
|
||||
log.Debugf("[%+v] had error: %s", conn, errConn.Error())
|
||||
log.Tracef("[%+v] had error: %s", conn, errConn.Error())
|
||||
}
|
||||
json.Unmarshal(data, &dataMessage)
|
||||
log.Tracef("data: %+v '%s'", data, data)
|
||||
log.Tracef("dataMessage: %s", dataMessage)
|
||||
log.Tracef("kB: %x", kB)
|
||||
// if kB not null, then use it to decrypt
|
||||
if kB != nil {
|
||||
var decryptErr error
|
||||
var dataDecrypt []byte
|
||||
dataDecrypt, decryptErr = crypt.Decrypt(data, kB)
|
||||
if decryptErr != nil {
|
||||
log.Tracef("error decrypting: %v: '%s'", decryptErr, data)
|
||||
} else {
|
||||
// copy dataDecrypt to data
|
||||
data = dataDecrypt
|
||||
log.Tracef("decrypted: %s", data)
|
||||
}
|
||||
}
|
||||
if bytes.Equal(data, ipRequest) {
|
||||
log.Tracef("got ipRequest")
|
||||
// recipient wants to try to connect to local ips
|
||||
var ips []string
|
||||
// only get local ips if the local is enabled
|
||||
|
|
@ -702,22 +758,48 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
// get list of local ips
|
||||
ips, err = utils.GetLocalIPs()
|
||||
if err != nil {
|
||||
log.Debugf("error getting local ips: %v", err)
|
||||
log.Tracef("error getting local ips: %v", err)
|
||||
}
|
||||
// prepend the port that is being listened to
|
||||
ips = append([]string{c.Options.RelayPorts[0]}, ips...)
|
||||
}
|
||||
bips, _ := json.Marshal(ips)
|
||||
log.Tracef("sending ips: %+v", ips)
|
||||
bips, errIps := json.Marshal(ips)
|
||||
if errIps != nil {
|
||||
log.Tracef("error marshalling ips: %v", errIps)
|
||||
}
|
||||
bips, errIps = crypt.Encrypt(bips, kB)
|
||||
if errIps != nil {
|
||||
log.Tracef("error encrypting ips: %v", errIps)
|
||||
}
|
||||
if err = conn.Send(bips); err != nil {
|
||||
log.Errorf("error sending: %v", err)
|
||||
}
|
||||
} else if dataMessage.Kind == "pake1" {
|
||||
log.Trace("got pake1")
|
||||
var pakeError error
|
||||
pakeError = B.Update(dataMessage.Bytes)
|
||||
if pakeError == nil {
|
||||
kB, pakeError = B.SessionKey()
|
||||
if pakeError == nil {
|
||||
log.Tracef("dataMessage kB: %x", kB)
|
||||
dataMessage.Bytes = B.Bytes()
|
||||
dataMessage.Kind = "pake2"
|
||||
data, _ = json.Marshal(dataMessage)
|
||||
if pakeError = conn.Send(data); err != nil {
|
||||
log.Errorf("dataMessage error sending: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else if bytes.Equal(data, handshakeRequest) {
|
||||
log.Trace("got handshake")
|
||||
break
|
||||
} else if bytes.Equal(data, []byte{1}) {
|
||||
log.Debug("got ping")
|
||||
log.Trace("got ping")
|
||||
continue
|
||||
} else {
|
||||
log.Debugf("[%+v] got weird bytes: %+v", conn, data)
|
||||
log.Tracef("[%+v] got weird bytes: %+v", conn, data)
|
||||
// throttle the reading
|
||||
errchan <- fmt.Errorf("gracefully refusing using the public relay")
|
||||
return
|
||||
|
|
@ -755,6 +837,13 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
return err
|
||||
}
|
||||
|
||||
func showReceiveCommandQrCode(command string) {
|
||||
qrCode, err := qrcode.New(command, qrcode.Medium)
|
||||
if err == nil {
|
||||
fmt.Println(qrCode.ToSmallString(false))
|
||||
}
|
||||
}
|
||||
|
||||
// Receive will receive a file
|
||||
func (c *Client) Receive() (err error) {
|
||||
fmt.Fprintf(os.Stderr, "connecting...")
|
||||
|
|
@ -790,10 +879,11 @@ func (c *Client) Receive() (err error) {
|
|||
go func() {
|
||||
defer wgDiscovery.Done()
|
||||
ipv4discoveries, err1 := peerdiscovery.Discover(peerdiscovery.Settings{
|
||||
Limit: 1,
|
||||
Payload: []byte("ok"),
|
||||
Delay: 20 * time.Millisecond,
|
||||
TimeLimit: 200 * time.Millisecond,
|
||||
Limit: 1,
|
||||
Payload: []byte("ok"),
|
||||
Delay: 20 * time.Millisecond,
|
||||
TimeLimit: 200 * time.Millisecond,
|
||||
MulticastAddress: c.Options.MulticastAddress,
|
||||
})
|
||||
if err1 == nil && len(ipv4discoveries) > 0 {
|
||||
dmux.Lock()
|
||||
|
|
@ -867,7 +957,7 @@ func (c *Client) Receive() (err error) {
|
|||
log.Debugf("got host '%v' and port '%v'", host, port)
|
||||
address = net.JoinHostPort(host, port)
|
||||
log.Debugf("trying connection to %s", address)
|
||||
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
|
||||
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RoomName, durations[i])
|
||||
if err == nil {
|
||||
c.Options.RelayAddress = address
|
||||
break
|
||||
|
|
@ -888,20 +978,68 @@ func (c *Client) Receive() (err error) {
|
|||
if c.Options.TestFlag || (!usingLocal && !c.Options.DisableLocal && !isIPset) {
|
||||
// ask the sender for their local ips and port
|
||||
// and try to connect to them
|
||||
log.Debug("sending ips?")
|
||||
var data []byte
|
||||
if err = c.conn[0].Send(ipRequest); err != nil {
|
||||
log.Errorf("ips send error: %v", err)
|
||||
}
|
||||
data, err = c.conn[0].Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("ips data: %s", data)
|
||||
|
||||
var ips []string
|
||||
if err = json.Unmarshal(data, &ips); err != nil {
|
||||
log.Debugf("ips unmarshal error: %v", err)
|
||||
}
|
||||
err = func() (err error) {
|
||||
var A *pake.Pake
|
||||
var data []byte
|
||||
A, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataMessage := SimpleMessage{
|
||||
Bytes: A.Bytes(),
|
||||
Kind: "pake1",
|
||||
}
|
||||
data, _ = json.Marshal(dataMessage)
|
||||
if err = c.conn[0].Send(data); err != nil {
|
||||
log.Errorf("dataMessage send error: %v", err)
|
||||
return
|
||||
}
|
||||
data, err = c.conn[0].Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = json.Unmarshal(data, &dataMessage)
|
||||
if err != nil || dataMessage.Kind != "pake2" {
|
||||
log.Debugf("data: %s", data)
|
||||
return fmt.Errorf("dataMessage %s pake failed", ipRequest)
|
||||
}
|
||||
err = A.Update(dataMessage.Bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var kA []byte
|
||||
kA, err = A.SessionKey()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("dataMessage kA: %x", kA)
|
||||
|
||||
// secure ipRequest
|
||||
data, err = crypt.Encrypt([]byte(ipRequest), kA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debug("sending ips?")
|
||||
if err = c.conn[0].Send(data); err != nil {
|
||||
log.Errorf("ips send error: %v", err)
|
||||
}
|
||||
data, err = c.conn[0].Receive()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
data, err = crypt.Decrypt(data, kA)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
log.Debugf("ips data: %s", data)
|
||||
if err = json.Unmarshal(data, &ips); err != nil {
|
||||
log.Debugf("ips unmarshal error: %v", err)
|
||||
}
|
||||
return
|
||||
}()
|
||||
|
||||
if len(ips) > 1 {
|
||||
port := ips[0]
|
||||
ips = ips[1:]
|
||||
|
|
@ -925,7 +1063,7 @@ func (c *Client) Receive() (err error) {
|
|||
}
|
||||
|
||||
serverTry := net.JoinHostPort(ip, port)
|
||||
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 500*time.Millisecond)
|
||||
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.RoomName, 500*time.Millisecond)
|
||||
if errConn != nil {
|
||||
log.Debug(errConn)
|
||||
log.Debugf("could not connect to " + serverTry)
|
||||
|
|
@ -958,7 +1096,7 @@ func (c *Client) Receive() (err error) {
|
|||
err = c.transfer()
|
||||
if err == nil {
|
||||
if c.numberOfTransferredFiles+len(c.EmptyFoldersToTransfer) == 0 {
|
||||
fmt.Fprintf(os.Stderr, "\rNo files transferred.")
|
||||
fmt.Fprintf(os.Stderr, "\rNo files transferred.\n")
|
||||
}
|
||||
}
|
||||
return
|
||||
|
|
@ -997,6 +1135,7 @@ func (c *Client) transfer() (err error) {
|
|||
}
|
||||
done, err = c.processMessage(data)
|
||||
if err != nil {
|
||||
log.Debugf("data: %s", data)
|
||||
log.Debugf("got error processing: %v", err)
|
||||
break
|
||||
}
|
||||
|
|
@ -1092,6 +1231,31 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
|
|||
c.EmptyFoldersToTransfer = senderInfo.EmptyFoldersToTransfer
|
||||
c.TotalNumberFolders = senderInfo.TotalNumberFolders
|
||||
c.FilesToTransfer = senderInfo.FilesToTransfer
|
||||
for i, fi := range c.FilesToTransfer {
|
||||
// Issues #593 - sanitize the sender paths and prevent ".." from being used
|
||||
c.FilesToTransfer[i].FolderRemote = filepath.Clean(fi.FolderRemote)
|
||||
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "../") {
|
||||
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
|
||||
}
|
||||
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "/..") {
|
||||
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
|
||||
}
|
||||
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "\\..") {
|
||||
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
|
||||
}
|
||||
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "..\\") {
|
||||
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
|
||||
}
|
||||
// Issues #593 - disallow specific folders like .ssh
|
||||
if strings.Contains(c.FilesToTransfer[i].FolderRemote, ".ssh") {
|
||||
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
|
||||
}
|
||||
// Issue #595 - disallow filenames with invisible characters
|
||||
errFileName := utils.ValidFileName(path.Join(c.FilesToTransfer[i].FolderRemote, fi.Name))
|
||||
if errFileName != nil {
|
||||
return true, errFileName
|
||||
}
|
||||
}
|
||||
c.TotalNumberOfContents = 0
|
||||
if c.FilesToTransfer != nil {
|
||||
c.TotalNumberOfContents += len(c.FilesToTransfer)
|
||||
|
|
@ -1129,6 +1293,12 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
|
|||
}
|
||||
}
|
||||
}
|
||||
// // check the totalSize does not exceed disk space
|
||||
// usage := diskusage.NewDiskUsage(".")
|
||||
// if usage.Available() < uint64(totalSize) {
|
||||
// return true, fmt.Errorf("not enough disk space")
|
||||
// }
|
||||
|
||||
// c.spinner.Stop()
|
||||
action := "Accept"
|
||||
if c.Options.SendingText {
|
||||
|
|
@ -1275,7 +1445,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
|
|||
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
|
||||
server,
|
||||
c.Options.RelayPassword,
|
||||
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j),
|
||||
fmt.Sprintf("%s-%d", c.Options.RoomName, j),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -1516,6 +1686,7 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
|
|||
}
|
||||
c.SuccessfulTransfer = true
|
||||
c.FilesHasFinished[c.FilesToTransferCurrentNum] = struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
err = c.recipientInitializeFile()
|
||||
|
|
@ -1551,6 +1722,25 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func max(a int, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func formatDescription(description string) string {
|
||||
width, _, err := term.GetSize(int(os.Stdout.Fd()))
|
||||
width = max(20, width-60)
|
||||
if err != nil {
|
||||
return description
|
||||
}
|
||||
if len(description) > width {
|
||||
description = description[:(width-3)] + "..."
|
||||
}
|
||||
return description
|
||||
}
|
||||
|
||||
func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error) {
|
||||
log.Debugf("touching file with folder / name")
|
||||
if !utils.Exists(fileInfo.FolderRemote) {
|
||||
|
|
@ -1593,7 +1783,7 @@ func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error)
|
|||
c.fmtPrintUpdate()
|
||||
}),
|
||||
progressbar.OptionSetWidth(20),
|
||||
progressbar.OptionSetDescription(description),
|
||||
progressbar.OptionSetDescription(formatDescription(description)),
|
||||
progressbar.OptionSetRenderBlankState(true),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionShowCount(),
|
||||
|
|
@ -1649,13 +1839,13 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
|
|||
percentDone := 100 - float64(len(missingChunks)*models.TCP_BUFFER_SIZE/2)/float64(fileInfo.Size)*100
|
||||
|
||||
log.Debug("asking to overwrite")
|
||||
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) ", path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
||||
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) (use --overwrite to omit) ", path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
||||
if percentDone < 99 {
|
||||
prompt = fmt.Sprintf("\nResume '%s' (%2.1f%%)? (y/N) ", path.Join(fileInfo.FolderRemote, fileInfo.Name), percentDone)
|
||||
prompt = fmt.Sprintf("\nResume '%s' (%2.1f%%)? (y/N) (use --overwrite to omit) ", path.Join(fileInfo.FolderRemote, fileInfo.Name), percentDone)
|
||||
}
|
||||
choice := strings.ToLower(utils.GetInput(prompt))
|
||||
if choice != "y" && choice != "yes" {
|
||||
fmt.Fprintf(os.Stderr, "skipping '%s'", path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
||||
fmt.Fprintf(os.Stderr, "Skipping '%s'\n", path.Join(fileInfo.FolderRemote, fileInfo.Name))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -1717,12 +1907,13 @@ func (c *Client) updateState() (err error) {
|
|||
description = c.FilesToTransfer[i].Name
|
||||
// description = ""
|
||||
}
|
||||
|
||||
c.bar = progressbar.NewOptions64(1,
|
||||
progressbar.OptionOnCompletion(func() {
|
||||
c.fmtPrintUpdate()
|
||||
}),
|
||||
progressbar.OptionSetWidth(20),
|
||||
progressbar.OptionSetDescription(description),
|
||||
progressbar.OptionSetDescription(formatDescription(description)),
|
||||
progressbar.OptionSetRenderBlankState(true),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionShowCount(),
|
||||
|
|
@ -1770,7 +1961,7 @@ func (c *Client) setBar() {
|
|||
c.fmtPrintUpdate()
|
||||
}),
|
||||
progressbar.OptionSetWidth(20),
|
||||
progressbar.OptionSetDescription(description),
|
||||
progressbar.OptionSetDescription(formatDescription(description)),
|
||||
progressbar.OptionSetRenderBlankState(true),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionShowCount(),
|
||||
|
|
@ -1791,14 +1982,14 @@ func (c *Client) setBar() {
|
|||
}
|
||||
|
||||
func (c *Client) receiveData(i int) {
|
||||
log.Debugf("%d receiving data", i)
|
||||
log.Tracef("%d receiving data", i)
|
||||
for {
|
||||
data, err := c.conn[i+1].Receive()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if bytes.Equal(data, []byte{1}) {
|
||||
log.Debug("got ping")
|
||||
log.Trace("got ping")
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -1874,62 +2065,66 @@ func (c *Client) sendData(i int) {
|
|||
curi := float64(0)
|
||||
for {
|
||||
// Read file
|
||||
data := make([]byte, models.TCP_BUFFER_SIZE/2)
|
||||
// log.Debugf("%d trying to read", i)
|
||||
n, errRead := c.fread.ReadAt(data, readingPos)
|
||||
// log.Debugf("%d read %d bytes", i, n)
|
||||
readingPos += int64(n)
|
||||
if c.limiter != nil {
|
||||
r := c.limiter.ReserveN(time.Now(), n)
|
||||
log.Debugf("Limiting Upload for %d", r.Delay())
|
||||
time.Sleep(r.Delay())
|
||||
}
|
||||
|
||||
var n int
|
||||
var errRead error
|
||||
if math.Mod(curi, float64(len(c.Options.RelayPorts))) == float64(i) {
|
||||
// check to see if this is a chunk that the recipient wants
|
||||
usableChunk := true
|
||||
c.mutex.Lock()
|
||||
if len(c.chunkMap) != 0 {
|
||||
if _, ok := c.chunkMap[pos]; !ok {
|
||||
usableChunk = false
|
||||
} else {
|
||||
delete(c.chunkMap, pos)
|
||||
}
|
||||
data := make([]byte, models.TCP_BUFFER_SIZE/2)
|
||||
n, errRead = c.fread.ReadAt(data, readingPos)
|
||||
if c.limiter != nil {
|
||||
r := c.limiter.ReserveN(time.Now(), n)
|
||||
log.Debugf("Limiting Upload for %d", r.Delay())
|
||||
time.Sleep(r.Delay())
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
if usableChunk {
|
||||
// log.Debugf("sending chunk %d", pos)
|
||||
posByte := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(posByte, pos)
|
||||
var err error
|
||||
var dataToSend []byte
|
||||
if c.Options.NoCompress {
|
||||
dataToSend, err = crypt.Encrypt(
|
||||
append(posByte, data[:n]...),
|
||||
c.Key,
|
||||
)
|
||||
} else {
|
||||
dataToSend, err = crypt.Encrypt(
|
||||
compress.Compress(
|
||||
if n > 0 {
|
||||
// check to see if this is a chunk that the recipient wants
|
||||
usableChunk := true
|
||||
c.mutex.Lock()
|
||||
if len(c.chunkMap) != 0 {
|
||||
if _, ok := c.chunkMap[pos]; !ok {
|
||||
usableChunk = false
|
||||
} else {
|
||||
delete(c.chunkMap, pos)
|
||||
}
|
||||
}
|
||||
c.mutex.Unlock()
|
||||
if usableChunk {
|
||||
// log.Debugf("sending chunk %d", pos)
|
||||
posByte := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(posByte, pos)
|
||||
var err error
|
||||
var dataToSend []byte
|
||||
if c.Options.NoCompress {
|
||||
dataToSend, err = crypt.Encrypt(
|
||||
append(posByte, data[:n]...),
|
||||
),
|
||||
c.Key,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Key,
|
||||
)
|
||||
} else {
|
||||
dataToSend, err = crypt.Encrypt(
|
||||
compress.Compress(
|
||||
append(posByte, data[:n]...),
|
||||
),
|
||||
c.Key,
|
||||
)
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = c.conn[i+1].Send(dataToSend)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
err = c.conn[i+1].Send(dataToSend)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.bar.Add(n)
|
||||
c.TotalSent += int64(n)
|
||||
// time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
c.bar.Add(n)
|
||||
c.TotalSent += int64(n)
|
||||
// time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 0 {
|
||||
n = models.TCP_BUFFER_SIZE / 2
|
||||
}
|
||||
readingPos += int64(n)
|
||||
curi++
|
||||
pos += uint64(n)
|
||||
|
||||
|
|
@ -1941,3 +2136,27 @@ func (c *Client) sendData(i int) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyToClipboard(str string) {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = exec.Command("clip")
|
||||
case "darwin":
|
||||
cmd = exec.Command("pbcopy")
|
||||
case "linux":
|
||||
if os.Getenv("XDG_SESSION_TYPE") == "wayland" {
|
||||
cmd = exec.Command("wl-copy")
|
||||
} else {
|
||||
cmd = exec.Command("xclip", "-selection", "clipboard")
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
cmd.Stdin = bytes.NewReader([]byte(str))
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Debugf("error copying to clipboard: %v", err)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Code copied to clipboard\n")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v9/src/tcp"
|
||||
"github.com/schollz/croc/v10/src/tcp"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
@ -232,7 +232,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
t.Errorf("symlink transfer failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
func testCrocIgnoreGit(t *testing.T) {
|
||||
func TestCrocIgnoreGit(t *testing.T) {
|
||||
log.SetLevel("trace")
|
||||
defer os.Remove(".gitignore")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,20 @@ func BenchmarkDecrypt(b *testing.B) {
|
|||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewPbkdf2(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
New([]byte("password"), nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNewArgon2(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
NewArgon2([]byte("password"), nil)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncryptChaCha(b *testing.B) {
|
||||
bob, _, _ := NewArgon2([]byte("password"), nil)
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package diskusage
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// DiskUsage contains usage data and provides user-friendly access methods
|
||||
type DiskUsage struct {
|
||||
stat *unix.Statfs_t
|
||||
}
|
||||
|
||||
// NewDiskUsage returns an object holding the disk usage of volumePath
|
||||
// or nil in case of error (invalid path, etc)
|
||||
func NewDiskUsage(volumePath string) *DiskUsage {
|
||||
stat := unix.Statfs_t{}
|
||||
err := unix.Statfs(volumePath, &stat)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return &DiskUsage{&stat}
|
||||
}
|
||||
|
||||
// Free returns total free bytes on file system
|
||||
func (du *DiskUsage) Free() uint64 {
|
||||
return uint64(du.stat.Bfree) * uint64(du.stat.Bsize)
|
||||
}
|
||||
|
||||
// Available return total available bytes on file system to an unprivileged user
|
||||
func (du *DiskUsage) Available() uint64 {
|
||||
return uint64(du.stat.Bavail) * uint64(du.stat.Bsize)
|
||||
}
|
||||
|
||||
// Size returns total size of the file system
|
||||
func (du *DiskUsage) Size() uint64 {
|
||||
return uint64(du.stat.Blocks) * uint64(du.stat.Bsize)
|
||||
}
|
||||
|
||||
// Used returns total bytes used in file system
|
||||
func (du *DiskUsage) Used() uint64 {
|
||||
return du.Size() - du.Free()
|
||||
}
|
||||
|
||||
// Usage returns percentage of use on the file system
|
||||
func (du *DiskUsage) Usage() float32 {
|
||||
return float32(du.Used()) / float32(du.Size())
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package diskusage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var KB = uint64(1024)
|
||||
|
||||
func TestNewDiskUsage(t *testing.T) {
|
||||
usage := NewDiskUsage(".")
|
||||
fmt.Println("Free:", usage.Free()/(KB*KB))
|
||||
fmt.Println("Available:", usage.Available()/(KB*KB))
|
||||
fmt.Println("Size:", usage.Size()/(KB*KB))
|
||||
fmt.Println("Used:", usage.Used()/(KB*KB))
|
||||
fmt.Println("Usage:", usage.Usage()*100, "%")
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package diskusage
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type DiskUsage struct {
|
||||
freeBytes int64
|
||||
totalBytes int64
|
||||
availBytes int64
|
||||
}
|
||||
|
||||
// NewDiskUsage returns an object holding the disk usage of volumePath
|
||||
// or nil in case of error (invalid path, etc)
|
||||
func NewDiskUsage(volumePath string) *DiskUsage {
|
||||
h := windows.MustLoadDLL("kernel32.dll")
|
||||
c := h.MustFindProc("GetDiskFreeSpaceExW")
|
||||
|
||||
du := &DiskUsage{}
|
||||
|
||||
c.Call(
|
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(volumePath))),
|
||||
uintptr(unsafe.Pointer(&du.freeBytes)),
|
||||
uintptr(unsafe.Pointer(&du.totalBytes)),
|
||||
uintptr(unsafe.Pointer(&du.availBytes)))
|
||||
|
||||
return du
|
||||
}
|
||||
|
||||
// Free returns total free bytes on file system
|
||||
func (du *DiskUsage) Free() uint64 {
|
||||
return uint64(du.freeBytes)
|
||||
}
|
||||
|
||||
// Available returns total available bytes on file system to an unprivileged user
|
||||
func (du *DiskUsage) Available() uint64 {
|
||||
return uint64(du.availBytes)
|
||||
}
|
||||
|
||||
// Size returns total size of the file system
|
||||
func (du *DiskUsage) Size() uint64 {
|
||||
return uint64(du.totalBytes)
|
||||
}
|
||||
|
||||
// Used returns total bytes used in file system
|
||||
func (du *DiskUsage) Used() uint64 {
|
||||
return du.Size() - du.Free()
|
||||
}
|
||||
|
||||
// Usage returns percentage of use on the file system
|
||||
func (du *DiskUsage) Usage() float32 {
|
||||
return float32(du.Used()) / float32(du.Size())
|
||||
}
|
||||
|
|
@ -528,7 +528,7 @@ main() {
|
|||
local autocomplete_install_rcode
|
||||
|
||||
croc_bin_name="croc"
|
||||
croc_version="9.6.15"
|
||||
croc_version="10.1.1"
|
||||
croc_dl_ext="tar.gz"
|
||||
croc_base_url="https://github.com/schollz/croc/releases/download"
|
||||
prefix="${1}"
|
||||
|
|
@ -577,6 +577,9 @@ main() {
|
|||
|
||||
case "${croc_os}" in
|
||||
"Darwin" ) croc_os="macOS";;
|
||||
*"BusyBox"* )
|
||||
croc_os="Linux"
|
||||
;;
|
||||
"CYGWIN"* ) croc_os="Windows";
|
||||
croc_dl_ext="zip";
|
||||
print_message "== Cygwin is currently unsupported." "error";
|
||||
|
|
@ -589,6 +592,8 @@ main() {
|
|||
"aarch64" ) croc_arch="ARM64";;
|
||||
"arm64" ) croc_arch="ARM64";;
|
||||
"armv7l" ) croc_arch="ARM";;
|
||||
"armv8l" ) croc_arch="ARM";;
|
||||
"armv9l" ) croc_arch="ARM";;
|
||||
"i686" ) croc_arch="32bit";;
|
||||
* ) croc_arch="unknown";;
|
||||
esac
|
||||
|
|
@ -685,7 +690,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"
|
||||
[ ! -d "${bash_autocomplete_prefix}/croc" ] && mkdir -p "${bash_autocomplete_prefix}/croc" >/dev/null 2>&1
|
||||
case "${croc_os}" in
|
||||
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
|
||||
install_file_rcode="${?}";;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package message
|
|||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/schollz/croc/v9/src/comm"
|
||||
"github.com/schollz/croc/v9/src/compress"
|
||||
"github.com/schollz/croc/v9/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"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v9/src/comm"
|
||||
"github.com/schollz/croc/v9/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"
|
||||
)
|
||||
|
|
@ -84,7 +84,7 @@ func TestSend(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
a, err := comm.NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
||||
assert.Nil(t, err)
|
||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
// From GitHub version/fork maintained by Stephen Paul Weber available at:
|
||||
// https://github.com/singpolyma/mnemonicode
|
||||
//
|
||||
// Originally from:
|
||||
// http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/
|
||||
|
||||
/*
|
||||
Copyright (c) 2000 Oren Tirosh <oren@hishome.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package mnemonicode
|
||||
|
||||
const base = 1626
|
||||
|
||||
// WordsRequired returns the number of words required to encode input
|
||||
// data of length bytes using mnomonic encoding.
|
||||
//
|
||||
// Every four bytes of input is encoded into three words. If there
|
||||
// is an extra one or two bytes they get an extra one or two words
|
||||
// respectively. If there is an extra three bytes, they will be encoded
|
||||
// into three words with the last word being one of a small set of very
|
||||
// short words (only needed to encode the last 3 bits).
|
||||
func WordsRequired(length int) int {
|
||||
return ((length + 1) * 3) / 4
|
||||
}
|
||||
|
||||
// EncodeWordList encodes src into mnemomic words which are appended to dst.
|
||||
// The final wordlist is returned.
|
||||
// There will be WordsRequired(len(src)) words appeneded.
|
||||
func EncodeWordList(dst []string, src []byte) (result []string) {
|
||||
if n := len(dst) + WordsRequired(len(src)); cap(dst) < n {
|
||||
result = make([]string, len(dst), n)
|
||||
copy(result, dst)
|
||||
} else {
|
||||
result = dst
|
||||
}
|
||||
|
||||
var x uint32
|
||||
for len(src) >= 4 {
|
||||
x = uint32(src[0])
|
||||
x |= uint32(src[1]) << 8
|
||||
x |= uint32(src[2]) << 16
|
||||
x |= uint32(src[3]) << 24
|
||||
src = src[4:]
|
||||
|
||||
i0 := int(x % base)
|
||||
i1 := int(x/base) % base
|
||||
i2 := int(x/base/base) % base
|
||||
result = append(result, WordList[i0], WordList[i1], WordList[i2])
|
||||
}
|
||||
if len(src) > 0 {
|
||||
x = 0
|
||||
for i := len(src) - 1; i >= 0; i-- {
|
||||
x <<= 8
|
||||
x |= uint32(src[i])
|
||||
}
|
||||
i := int(x % base)
|
||||
result = append(result, WordList[i])
|
||||
if len(src) >= 2 {
|
||||
i = int(x/base) % base
|
||||
result = append(result, WordList[i])
|
||||
}
|
||||
if len(src) == 3 {
|
||||
i = base + int(x/base/base)%7
|
||||
result = append(result, WordList[i])
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
// From GitHub version/fork maintained by Stephen Paul Weber available at:
|
||||
// https://github.com/singpolyma/mnemonicode
|
||||
//
|
||||
// Originally from:
|
||||
// http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/
|
||||
|
||||
/*
|
||||
Copyright (c) 2000 Oren Tirosh <oren@hishome.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package mnemonicode
|
||||
|
||||
// WordListVersion is the version of compiled in word list.
|
||||
const WordListVersion = "0.7"
|
||||
|
||||
var wordMap = make(map[string]int, len(WordList))
|
||||
|
||||
func init() {
|
||||
for i, w := range WordList {
|
||||
wordMap[w] = i
|
||||
}
|
||||
}
|
||||
|
||||
const longestWord = 7
|
||||
|
||||
var WordList = []string{
|
||||
"academy", "acrobat", "active", "actor", "adam", "admiral",
|
||||
"adrian", "africa", "agenda", "agent", "airline", "airport",
|
||||
"aladdin", "alarm", "alaska", "albert", "albino", "album",
|
||||
"alcohol", "alex", "algebra", "alibi", "alice", "alien",
|
||||
"alpha", "alpine", "amadeus", "amanda", "amazon", "amber",
|
||||
"america", "amigo", "analog", "anatomy", "angel", "animal",
|
||||
"antenna", "antonio", "apollo", "april", "archive", "arctic",
|
||||
"arizona", "arnold", "aroma", "arthur", "artist", "asia",
|
||||
"aspect", "aspirin", "athena", "athlete", "atlas", "audio",
|
||||
"august", "austria", "axiom", "aztec", "balance", "ballad",
|
||||
"banana", "bandit", "banjo", "barcode", "baron", "basic",
|
||||
"battery", "belgium", "berlin", "bermuda", "bernard", "bikini",
|
||||
"binary", "bingo", "biology", "block", "blonde", "bonus",
|
||||
"boris", "boston", "boxer", "brandy", "bravo", "brazil",
|
||||
"bronze", "brown", "bruce", "bruno", "burger", "burma",
|
||||
"cabinet", "cactus", "cafe", "cairo", "cake", "calypso",
|
||||
"camel", "camera", "campus", "canada", "canal", "cannon",
|
||||
"canoe", "cantina", "canvas", "canyon", "capital", "caramel",
|
||||
"caravan", "carbon", "cargo", "carlo", "carol", "carpet",
|
||||
"cartel", "casino", "castle", "castro", "catalog", "caviar",
|
||||
"cecilia", "cement", "center", "century", "ceramic", "chamber",
|
||||
"chance", "change", "chaos", "charlie", "charm", "charter",
|
||||
"chef", "chemist", "cherry", "chess", "chicago", "chicken",
|
||||
"chief", "china", "cigar", "cinema", "circus", "citizen",
|
||||
"city", "clara", "classic", "claudia", "clean", "client",
|
||||
"climax", "clinic", "clock", "club", "cobra", "coconut",
|
||||
"cola", "collect", "colombo", "colony", "color", "combat",
|
||||
"comedy", "comet", "command", "compact", "company", "complex",
|
||||
"concept", "concert", "connect", "consul", "contact", "context",
|
||||
"contour", "control", "convert", "copy", "corner", "corona",
|
||||
"correct", "cosmos", "couple", "courage", "cowboy", "craft",
|
||||
"crash", "credit", "cricket", "critic", "crown", "crystal",
|
||||
"cuba", "culture", "dallas", "dance", "daniel", "david",
|
||||
"decade", "decimal", "deliver", "delta", "deluxe", "demand",
|
||||
"demo", "denmark", "derby", "design", "detect", "develop",
|
||||
"diagram", "dialog", "diamond", "diana", "diego", "diesel",
|
||||
"diet", "digital", "dilemma", "diploma", "direct", "disco",
|
||||
"disney", "distant", "doctor", "dollar", "dominic", "domino",
|
||||
"donald", "dragon", "drama", "dublin", "duet", "dynamic",
|
||||
"east", "ecology", "economy", "edgar", "egypt", "elastic",
|
||||
"elegant", "element", "elite", "elvis", "email", "energy",
|
||||
"engine", "english", "episode", "equator", "escort", "ethnic",
|
||||
"europe", "everest", "evident", "exact", "example", "exit",
|
||||
"exotic", "export", "express", "extra", "fabric", "factor",
|
||||
"falcon", "family", "fantasy", "fashion", "fiber", "fiction",
|
||||
"fidel", "fiesta", "figure", "film", "filter", "final",
|
||||
"finance", "finish", "finland", "flash", "florida", "flower",
|
||||
"fluid", "flute", "focus", "ford", "forest", "formal",
|
||||
"format", "formula", "fortune", "forum", "fragile", "france",
|
||||
"frank", "friend", "frozen", "future", "gabriel", "galaxy",
|
||||
"gallery", "gamma", "garage", "garden", "garlic", "gemini",
|
||||
"general", "genetic", "genius", "germany", "global", "gloria",
|
||||
"golf", "gondola", "gong", "good", "gordon", "gorilla",
|
||||
"grand", "granite", "graph", "green", "group", "guide",
|
||||
"guitar", "guru", "hand", "happy", "harbor", "harmony",
|
||||
"harvard", "havana", "hawaii", "helena", "hello", "henry",
|
||||
"hilton", "history", "horizon", "hotel", "human", "humor",
|
||||
"icon", "idea", "igloo", "igor", "image", "impact",
|
||||
"import", "index", "india", "indigo", "input", "insect",
|
||||
"instant", "iris", "italian", "jacket", "jacob", "jaguar",
|
||||
"janet", "japan", "jargon", "jazz", "jeep", "john",
|
||||
"joker", "jordan", "jumbo", "june", "jungle", "junior",
|
||||
"jupiter", "karate", "karma", "kayak", "kermit", "kilo",
|
||||
"king", "koala", "korea", "labor", "lady", "lagoon",
|
||||
"laptop", "laser", "latin", "lava", "lecture", "left",
|
||||
"legal", "lemon", "level", "lexicon", "liberal", "libra",
|
||||
"limbo", "limit", "linda", "linear", "lion", "liquid",
|
||||
"liter", "little", "llama", "lobby", "lobster", "local",
|
||||
"logic", "logo", "lola", "london", "lotus", "lucas",
|
||||
"lunar", "machine", "macro", "madam", "madonna", "madrid",
|
||||
"maestro", "magic", "magnet", "magnum", "major", "mama",
|
||||
"mambo", "manager", "mango", "manila", "marco", "marina",
|
||||
"market", "mars", "martin", "marvin", "master", "matrix",
|
||||
"maximum", "media", "medical", "mega", "melody", "melon",
|
||||
"memo", "mental", "mentor", "menu", "mercury", "message",
|
||||
"metal", "meteor", "meter", "method", "metro", "mexico",
|
||||
"miami", "micro", "million", "mineral", "minimum", "minus",
|
||||
"minute", "miracle", "mirage", "miranda", "mister", "mixer",
|
||||
"mobile", "model", "modem", "modern", "modular", "moment",
|
||||
"monaco", "monica", "monitor", "mono", "monster", "montana",
|
||||
"morgan", "motel", "motif", "motor", "mozart", "multi",
|
||||
"museum", "music", "mustang", "natural", "neon", "nepal",
|
||||
"neptune", "nerve", "neutral", "nevada", "news", "ninja",
|
||||
"nirvana", "normal", "nova", "novel", "nuclear", "numeric",
|
||||
"nylon", "oasis", "object", "observe", "ocean", "octopus",
|
||||
"olivia", "olympic", "omega", "opera", "optic", "optimal",
|
||||
"orange", "orbit", "organic", "orient", "origin", "orlando",
|
||||
"oscar", "oxford", "oxygen", "ozone", "pablo", "pacific",
|
||||
"pagoda", "palace", "pamela", "panama", "panda", "panel",
|
||||
"panic", "paradox", "pardon", "paris", "parker", "parking",
|
||||
"parody", "partner", "passage", "passive", "pasta", "pastel",
|
||||
"patent", "patriot", "patrol", "patron", "pegasus", "pelican",
|
||||
"penguin", "pepper", "percent", "perfect", "perfume", "period",
|
||||
"permit", "person", "peru", "phone", "photo", "piano",
|
||||
"picasso", "picnic", "picture", "pigment", "pilgrim", "pilot",
|
||||
"pirate", "pixel", "pizza", "planet", "plasma", "plaster",
|
||||
"plastic", "plaza", "pocket", "poem", "poetic", "poker",
|
||||
"polaris", "police", "politic", "polo", "polygon", "pony",
|
||||
"popcorn", "popular", "postage", "postal", "precise", "prefix",
|
||||
"premium", "present", "price", "prince", "printer", "prism",
|
||||
"private", "product", "profile", "program", "project", "protect",
|
||||
"proton", "public", "pulse", "puma", "pyramid", "queen",
|
||||
"radar", "radio", "random", "rapid", "rebel", "record",
|
||||
"recycle", "reflex", "reform", "regard", "regular", "relax",
|
||||
"report", "reptile", "reverse", "ricardo", "ringo", "ritual",
|
||||
"robert", "robot", "rocket", "rodeo", "romeo", "royal",
|
||||
"russian", "safari", "salad", "salami", "salmon", "salon",
|
||||
"salute", "samba", "sandra", "santana", "sardine", "school",
|
||||
"screen", "script", "second", "secret", "section", "segment",
|
||||
"select", "seminar", "senator", "senior", "sensor", "serial",
|
||||
"service", "sheriff", "shock", "sierra", "signal", "silicon",
|
||||
"silver", "similar", "simon", "single", "siren", "slogan",
|
||||
"social", "soda", "solar", "solid", "solo", "sonic",
|
||||
"soviet", "special", "speed", "spiral", "spirit", "sport",
|
||||
"static", "station", "status", "stereo", "stone", "stop",
|
||||
"street", "strong", "student", "studio", "style", "subject",
|
||||
"sultan", "super", "susan", "sushi", "suzuki", "switch",
|
||||
"symbol", "system", "tactic", "tahiti", "talent", "tango",
|
||||
"tarzan", "taxi", "telex", "tempo", "tennis", "texas",
|
||||
"textile", "theory", "thermos", "tiger", "titanic", "tokyo",
|
||||
"tomato", "topic", "tornado", "toronto", "torpedo", "total",
|
||||
"totem", "tourist", "tractor", "traffic", "transit", "trapeze",
|
||||
"travel", "tribal", "trick", "trident", "trilogy", "tripod",
|
||||
"tropic", "trumpet", "tulip", "tuna", "turbo", "twist",
|
||||
"ultra", "uniform", "union", "uranium", "vacuum", "valid",
|
||||
"vampire", "vanilla", "vatican", "velvet", "ventura", "venus",
|
||||
"vertigo", "veteran", "victor", "video", "vienna", "viking",
|
||||
"village", "vincent", "violet", "violin", "virtual", "virus",
|
||||
"visa", "vision", "visitor", "visual", "vitamin", "viva",
|
||||
"vocal", "vodka", "volcano", "voltage", "volume", "voyage",
|
||||
"water", "weekend", "welcome", "western", "window", "winter",
|
||||
"wizard", "wolf", "world", "xray", "yankee", "yoga",
|
||||
"yogurt", "yoyo", "zebra", "zero", "zigzag", "zipper",
|
||||
"zodiac", "zoom", "abraham", "action", "address", "alabama",
|
||||
"alfred", "almond", "ammonia", "analyze", "annual", "answer",
|
||||
"apple", "arena", "armada", "arsenal", "atlanta", "atomic",
|
||||
"avenue", "average", "bagel", "baker", "ballet", "bambino",
|
||||
"bamboo", "barbara", "basket", "bazaar", "benefit", "bicycle",
|
||||
"bishop", "blitz", "bonjour", "bottle", "bridge", "british",
|
||||
"brother", "brush", "budget", "cabaret", "cadet", "candle",
|
||||
"capitan", "capsule", "career", "cartoon", "channel", "chapter",
|
||||
"cheese", "circle", "cobalt", "cockpit", "college", "compass",
|
||||
"comrade", "condor", "crimson", "cyclone", "darwin", "declare",
|
||||
"degree", "delete", "delphi", "denver", "desert", "divide",
|
||||
"dolby", "domain", "domingo", "double", "drink", "driver",
|
||||
"eagle", "earth", "echo", "eclipse", "editor", "educate",
|
||||
"edward", "effect", "electra", "emerald", "emotion", "empire",
|
||||
"empty", "escape", "eternal", "evening", "exhibit", "expand",
|
||||
"explore", "extreme", "ferrari", "first", "flag", "folio",
|
||||
"forget", "forward", "freedom", "fresh", "friday", "fuji",
|
||||
"galileo", "garcia", "genesis", "gold", "gravity", "habitat",
|
||||
"hamlet", "harlem", "helium", "holiday", "house", "hunter",
|
||||
"ibiza", "iceberg", "imagine", "infant", "isotope", "jackson",
|
||||
"jamaica", "jasmine", "java", "jessica", "judo", "kitchen",
|
||||
"lazarus", "letter", "license", "lithium", "loyal", "lucky",
|
||||
"magenta", "mailbox", "manual", "marble", "mary", "maxwell",
|
||||
"mayor", "milk", "monarch", "monday", "money", "morning",
|
||||
"mother", "mystery", "native", "nectar", "nelson", "network",
|
||||
"next", "nikita", "nobel", "nobody", "nominal", "norway",
|
||||
"nothing", "number", "october", "office", "oliver", "opinion",
|
||||
"option", "order", "outside", "package", "pancake", "pandora",
|
||||
"panther", "papa", "patient", "pattern", "pedro", "pencil",
|
||||
"people", "phantom", "philips", "pioneer", "pluto", "podium",
|
||||
"portal", "potato", "prize", "process", "protein", "proxy",
|
||||
"pump", "pupil", "python", "quality", "quarter", "quiet",
|
||||
"rabbit", "radical", "radius", "rainbow", "ralph", "ramirez",
|
||||
"ravioli", "raymond", "respect", "respond", "result", "resume",
|
||||
"retro", "richard", "right", "risk", "river", "roger",
|
||||
"roman", "rondo", "sabrina", "salary", "salsa", "sample",
|
||||
"samuel", "saturn", "savage", "scarlet", "scoop", "scorpio",
|
||||
"scratch", "scroll", "sector", "serpent", "shadow", "shampoo",
|
||||
"sharon", "sharp", "short", "shrink", "silence", "silk",
|
||||
"simple", "slang", "smart", "smoke", "snake", "society",
|
||||
"sonar", "sonata", "soprano", "source", "sparta", "sphere",
|
||||
"spider", "sponsor", "spring", "acid", "adios", "agatha",
|
||||
"alamo", "alert", "almanac", "aloha", "andrea", "anita",
|
||||
"arcade", "aurora", "avalon", "baby", "baggage", "balloon",
|
||||
"bank", "basil", "begin", "biscuit", "blue", "bombay",
|
||||
"brain", "brenda", "brigade", "cable", "carmen", "cello",
|
||||
"celtic", "chariot", "chrome", "citrus", "civil", "cloud",
|
||||
"common", "compare", "cool", "copper", "coral", "crater",
|
||||
"cubic", "cupid", "cycle", "depend", "door", "dream",
|
||||
"dynasty", "edison", "edition", "enigma", "equal", "eric",
|
||||
"event", "evita", "exodus", "extend", "famous", "farmer",
|
||||
"food", "fossil", "frog", "fruit", "geneva", "gentle",
|
||||
"george", "giant", "gilbert", "gossip", "gram", "greek",
|
||||
"grille", "hammer", "harvest", "hazard", "heaven", "herbert",
|
||||
"heroic", "hexagon", "husband", "immune", "inca", "inch",
|
||||
"initial", "isabel", "ivory", "jason", "jerome", "joel",
|
||||
"joshua", "journal", "judge", "juliet", "jump", "justice",
|
||||
"kimono", "kinetic", "leonid", "lima", "maze", "medusa",
|
||||
"member", "memphis", "michael", "miguel", "milan", "mile",
|
||||
"miller", "mimic", "mimosa", "mission", "monkey", "moral",
|
||||
"moses", "mouse", "nancy", "natasha", "nebula", "nickel",
|
||||
"nina", "noise", "orchid", "oregano", "origami", "orinoco",
|
||||
"orion", "othello", "paper", "paprika", "prelude", "prepare",
|
||||
"pretend", "profit", "promise", "provide", "puzzle", "remote",
|
||||
"repair", "reply", "rival", "riviera", "robin", "rose",
|
||||
"rover", "rudolf", "saga", "sahara", "scholar", "shelter",
|
||||
"ship", "shoe", "sigma", "sister", "sleep", "smile",
|
||||
"spain", "spark", "split", "spray", "square", "stadium",
|
||||
"star", "storm", "story", "strange", "stretch", "stuart",
|
||||
"subway", "sugar", "sulfur", "summer", "survive", "sweet",
|
||||
"swim", "table", "taboo", "target", "teacher", "telecom",
|
||||
"temple", "tibet", "ticket", "tina", "today", "toga",
|
||||
"tommy", "tower", "trivial", "tunnel", "turtle", "twin",
|
||||
"uncle", "unicorn", "unique", "update", "valery", "vega",
|
||||
"version", "voodoo", "warning", "william", "wonder", "year",
|
||||
"yellow", "young", "absent", "absorb", "accent", "alfonso",
|
||||
"alias", "ambient", "andy", "anvil", "appear", "apropos",
|
||||
"archer", "ariel", "armor", "arrow", "austin", "avatar",
|
||||
"axis", "baboon", "bahama", "bali", "balsa", "bazooka",
|
||||
"beach", "beast", "beatles", "beauty", "before", "benny",
|
||||
"betty", "between", "beyond", "billy", "bison", "blast",
|
||||
"bless", "bogart", "bonanza", "book", "border", "brave",
|
||||
"bread", "break", "broken", "bucket", "buenos", "buffalo",
|
||||
"bundle", "button", "buzzer", "byte", "caesar", "camilla",
|
||||
"canary", "candid", "carrot", "cave", "chant", "child",
|
||||
"choice", "chris", "cipher", "clarion", "clark", "clever",
|
||||
"cliff", "clone", "conan", "conduct", "congo", "content",
|
||||
"costume", "cotton", "cover", "crack", "current", "danube",
|
||||
"data", "decide", "desire", "detail", "dexter", "dinner",
|
||||
"dispute", "donor", "druid", "drum", "easy", "eddie",
|
||||
"enjoy", "enrico", "epoxy", "erosion", "except", "exile",
|
||||
"explain", "fame", "fast", "father", "felix", "field",
|
||||
"fiona", "fire", "fish", "flame", "flex", "flipper",
|
||||
"float", "flood", "floor", "forbid", "forever", "fractal",
|
||||
"frame", "freddie", "front", "fuel", "gallop", "game",
|
||||
"garbo", "gate", "gibson", "ginger", "giraffe", "gizmo",
|
||||
"glass", "goblin", "gopher", "grace", "gray", "gregory",
|
||||
"grid", "griffin", "ground", "guest", "gustav", "gyro",
|
||||
"hair", "halt", "harris", "heart", "heavy", "herman",
|
||||
"hippie", "hobby", "honey", "hope", "horse", "hostel",
|
||||
"hydro", "imitate", "info", "ingrid", "inside", "invent",
|
||||
"invest", "invite", "iron", "ivan", "james", "jester",
|
||||
"jimmy", "join", "joseph", "juice", "julius", "july",
|
||||
"justin", "kansas", "karl", "kevin", "kiwi", "ladder",
|
||||
"lake", "laura", "learn", "legacy", "legend", "lesson",
|
||||
"life", "light", "list", "locate", "lopez", "lorenzo",
|
||||
"love", "lunch", "malta", "mammal", "margo", "marion",
|
||||
"mask", "match", "mayday", "meaning", "mercy", "middle",
|
||||
"mike", "mirror", "modest", "morph", "morris", "nadia",
|
||||
"nato", "navy", "needle", "neuron", "never", "newton",
|
||||
"nice", "night", "nissan", "nitro", "nixon", "north",
|
||||
"oberon", "octavia", "ohio", "olga", "open", "opus",
|
||||
"orca", "oval", "owner", "page", "paint", "palma",
|
||||
"parade", "parent", "parole", "paul", "peace", "pearl",
|
||||
"perform", "phoenix", "phrase", "pierre", "pinball", "place",
|
||||
"plate", "plato", "plume", "pogo", "point", "polite",
|
||||
"polka", "poncho", "powder", "prague", "press", "presto",
|
||||
"pretty", "prime", "promo", "quasi", "quest", "quick",
|
||||
"quiz", "quota", "race", "rachel", "raja", "ranger",
|
||||
"region", "remark", "rent", "reward", "rhino", "ribbon",
|
||||
"rider", "road", "rodent", "round", "rubber", "ruby",
|
||||
"rufus", "sabine", "saddle", "sailor", "saint", "salt",
|
||||
"satire", "scale", "scuba", "season", "secure", "shake",
|
||||
"shallow", "shannon", "shave", "shelf", "sherman", "shine",
|
||||
"shirt", "side", "sinatra", "sincere", "size", "slalom",
|
||||
"slow", "small", "snow", "sofia", "song", "sound",
|
||||
"south", "speech", "spell", "spend", "spoon", "stage",
|
||||
"stamp", "stand", "state", "stella", "stick", "sting",
|
||||
"stock", "store", "sunday", "sunset", "support", "sweden",
|
||||
"swing", "tape", "think", "thomas", "tictac", "time",
|
||||
"toast", "tobacco", "tonight", "torch", "torso", "touch",
|
||||
"toyota", "trade", "tribune", "trinity", "triton", "truck",
|
||||
"trust", "type", "under", "unit", "urban", "urgent",
|
||||
"user", "value", "vendor", "venice", "verona", "vibrate",
|
||||
"virgo", "visible", "vista", "vital", "voice", "vortex",
|
||||
"waiter", "watch", "wave", "weather", "wedding", "wheel",
|
||||
"whiskey", "wisdom", "deal", "null", "nurse", "quebec",
|
||||
"reserve", "reunion", "roof", "singer", "verbal", "amen",
|
||||
"ego", "fax", "jet", "job", "rio", "ski",
|
||||
"yes",
|
||||
}
|
||||
|
|
@ -6,8 +6,10 @@ import (
|
|||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
log "github.com/schollz/logger"
|
||||
)
|
||||
|
||||
// TCP_BUFFER_SIZE is the maximum packet size
|
||||
|
|
@ -54,6 +56,8 @@ func getConfigFile(requireValidPath bool) (fname string, err error) {
|
|||
}
|
||||
|
||||
func init() {
|
||||
log.SetLevel("info")
|
||||
log.SetOutput(os.Stderr)
|
||||
doRemember := false
|
||||
for _, flag := range os.Args {
|
||||
if flag == "--internal-dns" {
|
||||
|
|
@ -78,6 +82,7 @@ func init() {
|
|||
INTERNAL_DNS = utils.Exists(fname)
|
||||
}
|
||||
}
|
||||
log.Trace("Using internal DNS: ", INTERNAL_DNS)
|
||||
var err error
|
||||
var addr string
|
||||
addr, err = lookup(DEFAULT_RELAY)
|
||||
|
|
@ -86,17 +91,20 @@ func init() {
|
|||
} else {
|
||||
DEFAULT_RELAY = ""
|
||||
}
|
||||
log.Tracef("Default ipv4 relay: %s", addr)
|
||||
addr, err = lookup(DEFAULT_RELAY6)
|
||||
if err == nil {
|
||||
DEFAULT_RELAY6 = net.JoinHostPort(addr, DEFAULT_PORT)
|
||||
} else {
|
||||
DEFAULT_RELAY6 = ""
|
||||
}
|
||||
log.Tracef("Default ipv6 relay: %s", addr)
|
||||
}
|
||||
|
||||
// Resolve a hostname to an IP address using DNS.
|
||||
func lookup(address string) (ipaddress string, err error) {
|
||||
if !INTERNAL_DNS {
|
||||
log.Tracef("Using local DNS to resolve %s", address)
|
||||
return localLookupIP(address)
|
||||
}
|
||||
type Result struct {
|
||||
|
|
@ -108,11 +116,13 @@ func lookup(address string) (ipaddress string, err error) {
|
|||
go func(dns string) {
|
||||
var r Result
|
||||
r.s, r.err = remoteLookupIP(address, dns)
|
||||
log.Tracef("Resolved %s to %s using %s", address, r.s, dns)
|
||||
result <- r
|
||||
}(dns)
|
||||
}
|
||||
for i := 0; i < len(publicDNS); i++ {
|
||||
ipaddress = (<-result).s
|
||||
log.Tracef("Resolved %s to %s", address, ipaddress)
|
||||
if ipaddress != "" {
|
||||
return
|
||||
}
|
||||
|
|
@ -121,9 +131,16 @@ func lookup(address string) (ipaddress string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// localLookupIP returns a host's IP address based on the local resolver.
|
||||
// localLookupIP returns a host's IP address using the local DNS configuration.
|
||||
func localLookupIP(address string) (ipaddress string, err error) {
|
||||
ip, err := net.LookupHost(address)
|
||||
// Create a context with a 500 millisecond timeout
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
r := &net.Resolver{}
|
||||
|
||||
// Use the context with timeout in the LookupHost function
|
||||
ip, err := r.LookupHost(ctx, address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -133,6 +150,9 @@ func localLookupIP(address string) (ipaddress string, err error) {
|
|||
|
||||
// remoteLookupIP returns a host's IP address based on a remote DNS server.
|
||||
func remoteLookupIP(address, dns string) (ipaddress string, err error) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
r := &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||
|
|
@ -140,7 +160,7 @@ func remoteLookupIP(address, dns string) (ipaddress string, err error) {
|
|||
return d.DialContext(ctx, network, dns+":53")
|
||||
},
|
||||
}
|
||||
ip, err := r.LookupHost(context.Background(), address)
|
||||
ip, err := r.LookupHost(ctx, address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
package tcp
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
DEFAULT_LOG_LEVEL = "debug"
|
||||
DEFAULT_ROOM_CLEANUP_INTERVAL = 10 * time.Minute
|
||||
DEFAULT_ROOM_TTL = 3 * time.Hour
|
||||
)
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO: maybe export from logger library?
|
||||
var availableLogLevels = []string{"info", "error", "warn", "debug", "trace"}
|
||||
|
||||
type serverOptsFunc func(s *server) error
|
||||
|
||||
func WithBanner(banner ...string) serverOptsFunc {
|
||||
return func(s *server) error {
|
||||
if len(banner) > 0 {
|
||||
s.banner = banner[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithLogLevel(level string) serverOptsFunc {
|
||||
return func(s *server) error {
|
||||
if !containsSlice(availableLogLevels, level) {
|
||||
return fmt.Errorf("invalid log level specified: %s", level)
|
||||
}
|
||||
s.debugLevel = level
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithRoomCleanupInterval(interval time.Duration) serverOptsFunc {
|
||||
return func(s *server) error {
|
||||
s.roomCleanupInterval = interval
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithRoomTTL(ttl time.Duration) serverOptsFunc {
|
||||
return func(s *server) error {
|
||||
s.roomTTL = ttl
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func containsSlice(s []string, e string) bool {
|
||||
for _, ss := range s {
|
||||
if e == ss {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
107
src/tcp/tcp.go
107
src/tcp/tcp.go
|
|
@ -11,9 +11,9 @@ import (
|
|||
log "github.com/schollz/logger"
|
||||
"github.com/schollz/pake/v3"
|
||||
|
||||
"github.com/schollz/croc/v9/src/comm"
|
||||
"github.com/schollz/croc/v9/src/crypt"
|
||||
"github.com/schollz/croc/v9/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 {
|
||||
|
|
@ -23,6 +23,11 @@ type server struct {
|
|||
banner string
|
||||
password string
|
||||
rooms roomMap
|
||||
|
||||
roomCleanupInterval time.Duration
|
||||
roomTTL time.Duration
|
||||
|
||||
stopRoomCleanup chan struct{}
|
||||
}
|
||||
|
||||
type roomInfo struct {
|
||||
|
|
@ -39,46 +44,55 @@ type roomMap struct {
|
|||
|
||||
const pingRoom = "pinglkasjdlfjsaldjf"
|
||||
|
||||
var timeToRoomDeletion = 10 * time.Minute
|
||||
|
||||
// Run starts a tcp listener, run async
|
||||
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
|
||||
// newDefaultServer initializes a new server, with some default configuration options
|
||||
func newDefaultServer() *server {
|
||||
s := new(server)
|
||||
s.roomCleanupInterval = DEFAULT_ROOM_CLEANUP_INTERVAL
|
||||
s.roomTTL = DEFAULT_ROOM_TTL
|
||||
s.debugLevel = DEFAULT_LOG_LEVEL
|
||||
s.stopRoomCleanup = make(chan struct{})
|
||||
return s
|
||||
}
|
||||
|
||||
// RunWithOptionsAsync asynchronously starts a TCP listener.
|
||||
func RunWithOptionsAsync(host, port, password string, opts ...serverOptsFunc) error {
|
||||
s := newDefaultServer()
|
||||
s.host = host
|
||||
s.port = port
|
||||
s.password = password
|
||||
s.debugLevel = debugLevel
|
||||
if len(banner) > 0 {
|
||||
s.banner = banner[0]
|
||||
for _, opt := range opts {
|
||||
err := opt(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not apply optional configurations: %w", err)
|
||||
}
|
||||
}
|
||||
return s.start()
|
||||
}
|
||||
|
||||
// Run starts a tcp listener, run async
|
||||
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
|
||||
return RunWithOptionsAsync(host, port, password, WithBanner(banner...), WithLogLevel(debugLevel))
|
||||
}
|
||||
|
||||
func (s *server) start() (err error) {
|
||||
log.SetLevel(s.debugLevel)
|
||||
log.Debugf("starting with password '%s'", s.password)
|
||||
|
||||
// Mask our password in logs
|
||||
maskedPassword := ""
|
||||
if len(s.password) > 2 {
|
||||
maskedPassword = fmt.Sprintf("%c***%c", s.password[0], s.password[len(s.password)-1])
|
||||
} else {
|
||||
maskedPassword = s.password
|
||||
}
|
||||
|
||||
log.Debugf("starting with password '%s'", maskedPassword)
|
||||
|
||||
s.rooms.Lock()
|
||||
s.rooms.rooms = make(map[string]roomInfo)
|
||||
s.rooms.Unlock()
|
||||
|
||||
// delete old rooms
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
var roomsToDelete []string
|
||||
s.rooms.Lock()
|
||||
for room := range s.rooms.rooms {
|
||||
if time.Since(s.rooms.rooms[room].opened) > 3*time.Hour {
|
||||
roomsToDelete = append(roomsToDelete, room)
|
||||
}
|
||||
}
|
||||
s.rooms.Unlock()
|
||||
|
||||
for _, room := range roomsToDelete {
|
||||
s.deleteRoom(room)
|
||||
}
|
||||
}
|
||||
}()
|
||||
go s.deleteOldRooms()
|
||||
defer s.stopRoomDeletion()
|
||||
|
||||
err = s.run()
|
||||
if err != nil {
|
||||
|
|
@ -173,6 +187,39 @@ func (s *server) run() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// deleteOldRooms checks for rooms at a regular interval and removes those that
|
||||
// have exceeded their allocated TTL.
|
||||
func (s *server) deleteOldRooms() {
|
||||
ticker := time.NewTicker(s.roomCleanupInterval)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
var roomsToDelete []string
|
||||
s.rooms.Lock()
|
||||
for room := range s.rooms.rooms {
|
||||
if time.Since(s.rooms.rooms[room].opened) > s.roomTTL {
|
||||
roomsToDelete = append(roomsToDelete, room)
|
||||
}
|
||||
}
|
||||
s.rooms.Unlock()
|
||||
|
||||
for _, room := range roomsToDelete {
|
||||
s.deleteRoom(room)
|
||||
log.Debugf("room cleaned up: %s", room)
|
||||
}
|
||||
case <-s.stopRoomCleanup:
|
||||
ticker.Stop()
|
||||
log.Debug("room cleanup stopped")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *server) stopRoomDeletion() {
|
||||
log.Debug("stop room cleanup fired")
|
||||
s.stopRoomCleanup <- struct{}{}
|
||||
}
|
||||
|
||||
var weakKey = []byte{1, 2, 3}
|
||||
|
||||
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
||||
|
|
@ -516,7 +563,7 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
|
|||
}
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ func BenchmarkConnection(b *testing.B) {
|
|||
|
||||
func TestTCP(t *testing.T) {
|
||||
log.SetLevel("error")
|
||||
timeToRoomDeletion = 100 * time.Millisecond
|
||||
go Run("debug", "127.0.0.1", "8381", "pass123", "8382")
|
||||
timeToRoomDeletion := 100 * time.Millisecond
|
||||
go RunWithOptionsAsync("127.0.0.1", "8381", "pass123", WithBanner("8382"), WithLogLevel("debug"), WithRoomTTL(timeToRoomDeletion))
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
err := PingServer("127.0.0.1:8381")
|
||||
assert.Nil(t, err)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import (
|
|||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math"
|
||||
"math/big"
|
||||
"net"
|
||||
|
|
@ -21,10 +20,14 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
"github.com/kalafut/imohash"
|
||||
"github.com/schollz/mnemonicode"
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/schollz/croc/v10/src/mnemonicode"
|
||||
log "github.com/schollz/logger"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
const NbPinNumbers = 4
|
||||
|
|
@ -76,7 +79,11 @@ func GetInput(prompt string) string {
|
|||
|
||||
// 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) (hash256 []byte, err error) {
|
||||
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 {
|
||||
|
|
@ -94,16 +101,59 @@ func HashFile(fname string, algorithm string) (hash256 []byte, err error) {
|
|||
case "imohash":
|
||||
return IMOHashFile(fname)
|
||||
case "md5":
|
||||
return MD5HashFile(fname)
|
||||
return MD5HashFile(fname, doShowProgress)
|
||||
case "xxhash":
|
||||
return XXHashFile(fname)
|
||||
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
|
||||
|
|
@ -111,23 +161,41 @@ 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)
|
||||
return
|
||||
}
|
||||
|
||||
var imofull = imohash.NewCustom(0, 0)
|
||||
var imopartial = imohash.NewCustom(16*16*8*1024, 128*1024)
|
||||
|
||||
// IMOHashFile returns imohash
|
||||
func IMOHashFile(fname string) (hash []byte, err error) {
|
||||
b, err := imohash.SumFile(fname)
|
||||
b, err := imopartial.SumFile(fname)
|
||||
hash = b[:]
|
||||
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)
|
||||
|
|
@ -136,7 +204,7 @@ func IMOHashFileFull(fname string) (hash []byte, err error) {
|
|||
}
|
||||
|
||||
// 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
|
||||
|
|
@ -144,8 +212,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)
|
||||
|
|
@ -161,19 +246,21 @@ func SHA256(s string) string {
|
|||
|
||||
// PublicIP returns public ip address
|
||||
func PublicIP() (ip string, err error) {
|
||||
resp, err := http.Get("https://canhazip.com")
|
||||
// ask ipv4.icanhazip.com for the public ip
|
||||
// by making http request
|
||||
// if the request fails, return nothing
|
||||
resp, err := http.Get("http://ipv4.icanhazip.com")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == http.StatusOK {
|
||||
bodyBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
ip = strings.TrimSpace(string(bodyBytes))
|
||||
}
|
||||
// read the body of the response
|
||||
// and return the ip address
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(resp.Body)
|
||||
ip = strings.TrimSpace(buf.String())
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -181,7 +268,8 @@ func PublicIP() (ip string, err error) {
|
|||
func LocalIP() string {
|
||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
log.Error(err)
|
||||
return ""
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
|
|
@ -382,12 +470,12 @@ func IsLocalIP(ipaddress string) bool {
|
|||
|
||||
func ZipDirectory(destination string, source string) (err error) {
|
||||
if _, err = os.Stat(destination); err == nil {
|
||||
log.Fatalf("%s file already exists!\n", destination)
|
||||
log.Errorf("%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)
|
||||
log.Error(err)
|
||||
}
|
||||
defer file.Close()
|
||||
writer := zip.NewWriter(file)
|
||||
|
|
@ -398,22 +486,22 @@ func ZipDirectory(destination string, source string) (err error) {
|
|||
defer writer.Close()
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
if info.Mode().IsRegular() {
|
||||
f1, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(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)
|
||||
log.Error(err)
|
||||
}
|
||||
if _, err := io.Copy(w1, f1); err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
||||
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
|
||||
|
|
@ -421,7 +509,7 @@ func ZipDirectory(destination string, source string) (err error) {
|
|||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
return nil
|
||||
|
|
@ -430,7 +518,7 @@ func ZipDirectory(destination string, source string) (err error) {
|
|||
func UnzipDirectory(destination string, source string) error {
|
||||
archive, err := zip.OpenReader(source)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
|
|
@ -438,13 +526,19 @@ func UnzipDirectory(destination string, source string) error {
|
|||
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.Errorf("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)
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
// check if file exists
|
||||
|
|
@ -452,23 +546,23 @@ func UnzipDirectory(destination string, source string) error {
|
|||
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) ", filePath)
|
||||
choice := strings.ToLower(GetInput(prompt))
|
||||
if choice != "y" && choice != "yes" {
|
||||
fmt.Fprintf(os.Stderr, "skipping '%s'", filePath)
|
||||
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)
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
fileInArchive, err := f.Open()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
|
||||
log.Fatalln(err)
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
dstFile.Close()
|
||||
|
|
@ -477,3 +571,67 @@ func UnzipDirectory(destination string, source string) error {
|
|||
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) (err error) {
|
||||
// make sure it doesn't contain unicode or invisible characters
|
||||
for _, r := range fname {
|
||||
if !unicode.IsGraphic(r) {
|
||||
err = fmt.Errorf("non-graphical unicode: %x U+%d in '%x'", string(r), r, fname)
|
||||
return
|
||||
}
|
||||
if !unicode.IsPrint(r) {
|
||||
err = fmt.Errorf("non-printable unicode: %x U+%d in '%x'", string(r), r, fname)
|
||||
return
|
||||
}
|
||||
}
|
||||
// make sure basename does not include ".." or path separators
|
||||
_, basename := filepath.Split(fname)
|
||||
if strings.Contains(basename, "..") {
|
||||
err = fmt.Errorf("basename cannot contain '..': '%s'", basename)
|
||||
return
|
||||
}
|
||||
if strings.Contains(basename, string(os.PathSeparator)) {
|
||||
err = fmt.Errorf("basename cannot contain path separators: '%s'", basename)
|
||||
return
|
||||
}
|
||||
// make sure the filename is not an absolute path
|
||||
if filepath.IsAbs(fname) {
|
||||
err = fmt.Errorf("filename cannot be an absolute path: '%s'", fname)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const crocRemovalFile = "croc-marked-files.txt"
|
||||
|
||||
func MarkFileForRemoval(fname string) {
|
||||
// append the fname to the list of files to remove
|
||||
f, err := os.OpenFile(crocRemovalFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
||||
if err != nil {
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
_, err = f.WriteString(fname + "\n")
|
||||
}
|
||||
|
||||
func RemoveMarkedFiles() (err error) {
|
||||
// read the file and remove all the files
|
||||
f, err := os.Open(crocRemovalFile)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
fname := scanner.Text()
|
||||
err = os.Remove(fname)
|
||||
if err == nil {
|
||||
log.Tracef("Removed %s", fname)
|
||||
}
|
||||
}
|
||||
os.Remove(crocRemovalFile)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -24,7 +25,7 @@ func BenchmarkMD5(b *testing.B) {
|
|||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
MD5HashFile("bigfile.test")
|
||||
MD5HashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ func BenchmarkXXHash(b *testing.B) {
|
|||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
XXHashFile("bigfile.test")
|
||||
XXHashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +45,14 @@ 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()
|
||||
|
|
@ -78,10 +87,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)
|
||||
}
|
||||
|
||||
|
|
@ -90,16 +109,16 @@ func TestIMOHashFile(t *testing.T) {
|
|||
defer os.Remove("bigfile.test")
|
||||
b, err := IMOHashFile("bigfile.test")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "c0d1e123ca94148ffea146137684ebb9", fmt.Sprintf("%x", b))
|
||||
assert.Equal(t, "c0d1e12301e6c635f6d4a8ea5c897437", fmt.Sprintf("%x", b))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
@ -208,11 +227,41 @@ func TestGetRandomName(t *testing.T) {
|
|||
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("127.0.0.1", 9009, 4)
|
||||
assert.Equal(t, []int{9009, 9010, 9011, 9012}, openPorts)
|
||||
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.Nil(t, ValidFileName("中文.csl"))
|
||||
// contains regular characters
|
||||
assert.Nil(t, ValidFileName("[something].csl"))
|
||||
// contains regular characters
|
||||
assert.Nil(t, ValidFileName("[(something)].csl"))
|
||||
// contains invisible character
|
||||
err := ValidFileName("D中文.cslouglas")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, "non-graphical unicode: e2808b U+8203 in '44e4b8ade696872e63736c6f75676c6173e2808b'", err.Error())
|
||||
assert.NotNil(t, ValidFileName("hi..txt"))
|
||||
assert.NotNil(t, ValidFileName(path.Join(string(os.PathSeparator), "abs", string(os.PathSeparator), "hi.txt")))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue