mirror of https://github.com/schollz/croc.git
Compare commits
259 Commits
Author | SHA1 | Date |
---|---|---|
Zack | 50e0f625bc | |
Zack | 94af2374c3 | |
Zack | a4322faa25 | |
Zack | 23dce2aa3e | |
Zack | 88002b322d | |
Zack | 9246408278 | |
Zack | fbf1eeedce | |
Zack | e4c9f2d9fb | |
Zack | c0c3370d9b | |
Zack | f6bd13fa06 | |
Zack Scholl | f83616e9bd | |
Zack Scholl | 23f385ab2f | |
Zack Scholl | 78feb393de | |
Zack | 69fc3cee47 | |
Zack | a5da77cf49 | |
Zack | f66e17dd46 | |
Zack | 63c9201938 | |
Zack | ca7a5979cc | |
Zack | fc457557e0 | |
Zack | f044f4dd86 | |
Zack | a2e71c7e1a | |
Zack | dff34fa7fc | |
Zack Scholl | 08103bb7bb | |
Zack Scholl | 4091ef0496 | |
Zack | 381f8369a3 | |
Zack | a95d67e31c | |
Zack | b0920bbe70 | |
Zack | ed55c746c2 | |
Zack | 8e10eac5c5 | |
Zack | 43f1c53538 | |
Zack | 3acac5d53b | |
Zack | 66f0d1264a | |
Zack | ee713c5146 | |
Zack | 6181903c83 | |
Zack | 7acd2def69 | |
Zack | eb0909033e | |
Zack | f6d862eac0 | |
Zack | 5a6005f1eb | |
Zack | f6633cbac9 | |
Zack | d8ef7cda20 | |
Zack | 7622e636e4 | |
Zack | bb018fd725 | |
Zack | 863dabb93a | |
Zack | 6f5f16aa1c | |
Zack | 0f1ca436cd | |
Zack | 4929635eb8 | |
Zack | 3f12f75fae | |
Zack | e255d472a6 | |
Zack | 2ffd4daeaf | |
Zack | accb310337 | |
Zack | 2b4c088100 | |
Zack | a591833dbf | |
Zack | b3668a6f5c | |
Zack | b05c3c8c42 | |
Zack | 13bc190f8b | |
Zack | 1b90484bb8 | |
Zack Scholl | 05359d6976 | |
Zack Scholl | cc4d74c490 | |
Zack | d81116382f | |
Zack Scholl | 94cc880568 | |
Zack | 24b907f4bb | |
Zack Scholl | 8166b2dbed | |
Zack | 14187f6f30 | |
dependabot[bot] | 90682d3ebd | |
Zack Scholl | f4057aa28b | |
Zack | 3c2548aa69 | |
dependabot[bot] | 7bab9c3cb5 | |
Zack Scholl | 355628f895 | |
Zack | eaffc6dcac | |
a1lu | 4baec420c8 | |
a1lu | cd89e8043f | |
Zack Scholl | 1324ff8897 | |
Zack | 8bc7a62b9e | |
Zack Scholl | 0c49ac3f02 | |
Zack | 8b4ab4c86c | |
Zack Scholl | f8f69e3157 | |
Zack | 4e75e131c4 | |
a1lu | e599e56415 | |
a1lu | 956598c427 | |
a1lu | 618ae1e5d0 | |
Zack Scholl | 7763a971f2 | |
Zack | d2b7c80369 | |
Rahul Garg | 241176d8a4 | |
Zack Scholl | 719f9b62c9 | |
Zack Scholl | 483c5255bb | |
Zack Scholl | 03e6dcd220 | |
Zack Scholl | 22ddbd83c2 | |
Zack Scholl | 6b930c365b | |
Zack | 1f6851f33b | |
Zack | 61224b4e6b | |
Zack Scholl | 6f2771e7b5 | |
Zack | c21beccb7a | |
dependabot[bot] | 40f5e9ca1e | |
bitraid | ed8c0475bf | |
dependabot[bot] | d7d7d3c8dc | |
Zack Scholl | 945ac74690 | |
Zack Scholl | fff6f48001 | |
Zack Scholl | 28ef8e99ac | |
Zack | 28bb36b321 | |
Zack Scholl | ab2cb477a8 | |
Zack | abc5029fce | |
Zack | e7f67ebea7 | |
Zack Scholl | c7f0228786 | |
Zack | 741714504a | |
D. Bohdan | be8a6689ff | |
Zack Scholl | de9c54e57a | |
Zack | 53fc9ebd99 | |
Zack | 4159ba7668 | |
Zack Scholl | 064f84ccd3 | |
Zack | 8ac1e3a501 | |
Zack | 11bc4eecc6 | |
Zack | d92cff92b9 | |
Zack | 00f12b5742 | |
Travis | 4f1f57b1ba | |
Zack | a240a4b982 | |
Zack | 508e0be335 | |
qk-santi | e1644401da | |
qk-santi | c83eb59963 | |
Zack | f874e30151 | |
Zack | b278f5a41d | |
Zack | ce91e3b420 | |
Zack | 30a6b14443 | |
qk-santi | 87152f8706 | |
qk-santi | 48eb2a2a7c | |
dependabot[bot] | 816ad09a50 | |
Zack | a2228e80c2 | |
Zack Scholl | 230de9184d | |
Zack | 3c781069ca | |
Zack | 7274a8bd4b | |
Zack Scholl | 3382306342 | |
dependabot[bot] | ad47739c8f | |
Zack | 2f33b14b3d | |
Zack | 4025efcd48 | |
Zack | f41a2afead | |
Zack | ba75f7badb | |
Zack | 55c954117a | |
Zack | 4d083f8d10 | |
Rehan Mahmood | 9ad2af18bd | |
Zack | 47fc96b98f | |
Rehan Mahmood | d4bba88fb1 | |
Zack Scholl | c1367671b6 | |
Zack Scholl | 1e572067ec | |
Zack Scholl | 8aa9f040f6 | |
Zack Scholl | 96bb34485e | |
Zack | 159f0f8d9b | |
Sandeep | c4d5c89e5f | |
Zack | 6ac67b68fc | |
PThorpe92 | 0af35d7149 | |
Zack | f91c7a9948 | |
Zack | cff8cddd13 | |
zx9597446 | d724f11297 | |
ferebee | 80aabea29b | |
sitiom | f8bb011eac | |
Zack Scholl | ef68dfa54c | |
Zack Scholl | 8ab65d06b5 | |
Zack Scholl | 9c82914e7c | |
Zack | 95717f16c9 | |
glitsj16 | 5a58ae294b | |
Zack Scholl | 0aa5c80393 | |
Zack | 23a8904193 | |
N0mansky | d5e63cd0bf | |
N0mansky | 1b1dc5cdfe | |
Zack | cd1162f85c | |
Alexander Seiler | ad7a22b218 | |
Zack | e3a18cd7a7 | |
Zack | 8611bfa44a | |
Zack Scholl | b3c0625659 | |
dependabot[bot] | 40e5893bb8 | |
Zack | e1b49e6d1c | |
BayLee4 | fae2e81b4a | |
BayLee4 | f5a02df17b | |
dependabot[bot] | d813aa2fa9 | |
Zack | 8d8d06557e | |
Zack Scholl | 3ae46c3c18 | |
Zack Scholl | 1fce28e72f | |
Zack Scholl | ac3caa5564 | |
Zack Scholl | 790ee9e6eb | |
Zack Scholl | 0656bb7851 | |
Zack Scholl | 5ed8204cb1 | |
Zack Scholl | 1220d54a65 | |
Zack Scholl | 642d20b48d | |
Zack | 58bb226ba5 | |
stefins | 080c21b66b | |
stefins | 4944355b75 | |
Zack Scholl | 3c1d60d6d2 | |
Zack Scholl | 5bdd4453cd | |
Zack Scholl | 5b333fc85c | |
Zack Scholl | 90e4e33dcd | |
Zack Scholl | 3135bd74e3 | |
Zack Scholl | 7c98ae3d5a | |
sitiom | 908f33cd61 | |
sitiom | 9eaba7be7c | |
Ryan Caezar Itang | b3db9d88c1 | |
Zack Scholl | 9006cde1b3 | |
Zack | 8f75bb8299 | |
Zack | faf64aadf1 | |
stefins | 65760b1189 | |
stefins | 24679d4e02 | |
Zack Scholl | 8b9977a7c4 | |
Zack | ae71b4b63b | |
Zack Scholl | c462b5948f | |
Zack | a2c1dc6a89 | |
chncaption | 98391bff63 | |
BayLee4 | adadaba75e | |
BayLee4 | 0024beaedc | |
Zack Scholl | 63feece074 | |
Zack Scholl | 9e280d7f48 | |
Zack Scholl | 7c7be95d6b | |
Zack | c20beb1be7 | |
Zack Scholl | e08c5e7f38 | |
Zack Scholl | 3b819346fa | |
Zack Scholl | 2467411bae | |
Zack Scholl | b436b31970 | |
Zack Scholl | e3ce565ca6 | |
Zack Scholl | e04981698c | |
Zack Scholl | 07a67ed54e | |
Zack | 9a8e584aca | |
Tai Groot | fa7ae114f5 | |
Zack | 8a4326bc0d | |
Tai Groot | ae20f2b5ac | |
Zack Scholl | cd6eb1ba53 | |
Zack Scholl | 8454008b45 | |
Zack | fce4629120 | |
Tai Groot | fd0db2ad55 | |
Zack | 7f9688c7a1 | |
IrishMaestro | f1e15a54ee | |
Zack Scholl | 0f7f449d7f | |
Zack Scholl | b27982a742 | |
Zack Scholl | 30f5a3e84e | |
Zack Scholl | 786fc8d34f | |
Zack | 8de4508876 | |
VRDighe | e640e4fce0 | |
VRDighe | 4f8b2aba9b | |
Zack | 1517767129 | |
Zack Scholl | 3dedd41557 | |
Zack | 69f939c0cd | |
Zack | cd5872bd2d | |
cui fliter | 1c6583f925 | |
chavacava | 40ac320261 | |
Zack Scholl | 1851327df7 | |
Zack Scholl | 0e93f1e285 | |
Zack Scholl | 7e0814a57e | |
Zack Scholl | c6bcb79928 | |
Zack | 134673691e | |
Stefin | d226ba536e | |
Stefin | b50fe88474 | |
Stefin | 37ae453ff7 | |
Stefin | ee772c4cec | |
Stefin | ed030375e5 | |
Stefin | ad36e21051 | |
Stefin | 4ea9a96d88 | |
Stefin | f0f9b80bdf | |
Zack | 7a0c0a8200 | |
Zack Scholl | 5270755c15 | |
Zack Scholl | b7e4a73c27 | |
Zack Scholl | da5d19ef28 | |
Zack | c68cfcea8a | |
DasSkelett | 38ed8ecc3c | |
Tadej Janež | a5d3e00f2b |
|
@ -2,7 +2,6 @@
|
|||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
@ -12,15 +11,10 @@ assignees: ''
|
|||
<!-- 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.
|
||||
|
@ -28,14 +22,10 @@ Steps to reproduce the behavior:
|
|||
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. -->
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
|
@ -1,18 +1,18 @@
|
|||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
unit-tests:
|
||||
name: Go unit tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: '1.18'
|
||||
go-version: '1.20'
|
||||
- run: go version
|
||||
- run: go test -v ./...
|
||||
|
|
|
@ -4,10 +4,15 @@ name: CD
|
|||
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
# Triggers the workflow on push or pull request events but only for the master branch
|
||||
# Triggers the workflow on push or pull request events but only for the main branch
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- '*'
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
@ -19,32 +24,37 @@ jobs:
|
|||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
-
|
||||
name: Docker meta
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: schollz/croc
|
||||
# tag-semver: "{{version}}"
|
||||
tag-sha: true
|
||||
tag-latest: true
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
-
|
||||
name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
uses: docker/setup-qemu-action@v3
|
||||
-
|
||||
name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
-
|
||||
name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
-
|
||||
name: Build and push
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
name: Make release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout project
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- name: Build Windows 7
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows7-64bit.zip croc.exe
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows7-32bit.zip croc.exe
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.21'
|
||||
- name: Prepare source tarball
|
||||
run: |
|
||||
git clone -b ${{ github.event.release.name }} --depth 1 https://github.com/schollz/croc croc-${{ github.event.release.name }}
|
||||
cd croc-${{ github.event.release.name }} && go mod tidy && go mod vendor
|
||||
cd .. && tar -czvf croc_${{ github.event.release.name }}_src.tar.gz croc-${{ github.event.release.name }}
|
||||
- name: Build files
|
||||
run: |
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-64bit.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-32bit.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-ARM.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
||||
zip croc_${{ github.event.release.name }}_Windows-ARM64.zip croc.exe LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-32bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARM.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_macOS-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz croc LICENSE
|
||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm64 go build -ldflags '' -o croc
|
||||
tar -czvf croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz croc LICENSE
|
||||
- name: Create checksums.txt
|
||||
run: |
|
||||
touch croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_src.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-64bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-32bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-ARM.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows-ARM64.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows7-64bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Windows7-32bit.zip >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-32bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-ARM.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_macOS-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
sha256sum croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
croc_${{ github.event.release.name }}_checksums.txt
|
||||
croc_${{ github.event.release.name }}_src.tar.gz
|
||||
croc_${{ github.event.release.name }}_Windows-64bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows-32bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows-ARM.zip
|
||||
croc_${{ github.event.release.name }}_Windows-ARM64.zip
|
||||
croc_${{ github.event.release.name }}_Windows7-64bit.zip
|
||||
croc_${{ github.event.release.name }}_Windows7-32bit.zip
|
||||
croc_${{ github.event.release.name }}_Linux-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-32bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-ARM.tar.gz
|
||||
croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_macOS-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_DragonFlyBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_FreeBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_FreeBSD-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-32bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_NetBSD-ARM64.tar.gz
|
||||
croc_${{ github.event.release.name }}_OpenBSD-64bit.tar.gz
|
||||
croc_${{ github.event.release.name }}_OpenBSD-ARM64.tar.gz
|
|
@ -0,0 +1,27 @@
|
|||
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/actions/stale
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '19 12 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'Stale issue message'
|
||||
stale-pr-message: 'Stale pull request message'
|
||||
stale-issue-label: 'no-issue-activity'
|
||||
stale-pr-label: 'no-pr-activity'
|
|
@ -0,0 +1,14 @@
|
|||
name: Publish to Winget
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
||||
with:
|
||||
identifier: schollz.croc
|
||||
installers-regex: '_Windows-\w+\.zip$'
|
||||
token: ${{ secrets.WINGET_TOKEN }}
|
|
@ -9,3 +9,5 @@ croc-stdin*
|
|||
|
||||
.idea/
|
||||
.vscode/
|
||||
src/utils/bigfile.test
|
||||
test1/
|
||||
|
|
|
@ -97,4 +97,4 @@ announce:
|
|||
twitter:
|
||||
# Wether its enabled or not.
|
||||
# Defaults to false.
|
||||
enabled: true
|
||||
enabled: false
|
12
.travis.yml
12
.travis.yml
|
@ -10,12 +10,12 @@ install: true
|
|||
|
||||
script:
|
||||
- env GO111MODULE=on go build -v
|
||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/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:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
FROM golang:1.17-alpine as builder
|
||||
RUN apk add --no-cache git
|
||||
FROM golang:1.22-alpine as builder
|
||||
RUN apk add --no-cache git gcc musl-dev
|
||||
WORKDIR /go/croc
|
||||
COPY . .
|
||||
RUN go build -v -ldflags="-s -w"
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017-2021 Zack
|
||||
Copyright (c) 2017-2024 Zack
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
25
README.md
25
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.5.5-brightgreen.svg?style=flat-square" alt="Version"></a>
|
||||
<a href="https://coveralls.io/github/schollz/croc"><img src="https://img.shields.io/badge/coverage-81%25-green.svg?style=flat-square" alt="Coverage"></a>
|
||||
<a href="https://travis-ci.org/schollz/croc"><img
|
||||
src="https://img.shields.io/travis/schollz/croc.svg?style=flat-square" alt="Build
|
||||
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v10.0.7-brightgreen.svg?style=flat-square" alt="Version"></a>
|
||||
<a href="https://github.com/schollz/croc/actions/workflows/ci.yml"><img
|
||||
src="https://github.com/schollz/croc/actions/workflows/ci.yml/badge.svg" alt="Build
|
||||
Status"></a>
|
||||
<p align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
|
||||
|
||||
|
@ -48,7 +47,7 @@ sudo port selfupdate
|
|||
sudo port install croc
|
||||
```
|
||||
|
||||
On Windows you can install the latest release with [Scoop](https://scoop.sh/) or [Chocolatey](https://chocolatey.org):
|
||||
On Windows you can install the latest release with [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org), or [Winget](https://learn.microsoft.com/en-us/windows/package-manager/):
|
||||
|
||||
```
|
||||
scoop install croc
|
||||
|
@ -58,6 +57,10 @@ scoop install croc
|
|||
choco install croc
|
||||
```
|
||||
|
||||
```
|
||||
winget install schollz.croc
|
||||
```
|
||||
|
||||
On Unix you can install the latest release with [Nix](https://nixos.org/nix):
|
||||
|
||||
```
|
||||
|
@ -78,6 +81,12 @@ On Arch Linux you can install the latest release with `pacman`:
|
|||
pacman -S croc
|
||||
```
|
||||
|
||||
On Fedora you can install with `dnf`:
|
||||
|
||||
```
|
||||
dnf install croc
|
||||
```
|
||||
|
||||
On Gentoo you can install with `portage`:
|
||||
```
|
||||
emerge net-misc/croc
|
||||
|
@ -98,7 +107,7 @@ pkg install croc
|
|||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
|
||||
|
||||
```
|
||||
go install github.com/schollz/croc/v9@latest
|
||||
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/).
|
||||
|
@ -203,7 +212,7 @@ The relay is needed to staple the parallel incoming and outgoing connections. By
|
|||
croc relay
|
||||
```
|
||||
|
||||
By default it uses TCP ports 9009-9013. Make sure to open those up. You can customized the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer.
|
||||
By default it uses TCP ports 9009-9013. Make sure to open those up. You can customize the ports (e.g. `croc relay --ports 1111,1112`), but you must have a minimum of **2** ports for the relay. The first port is for communication and the subsequent ports are used for the multiplexed data transfer.
|
||||
|
||||
You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own.
|
||||
|
||||
|
@ -236,6 +245,6 @@ MIT
|
|||
|
||||
## Acknowledgements
|
||||
|
||||
`croc` has gone through many iterations, and I am awed by all the great contributions! If you feel like contributing, in any way, by all means you can send an Issue, a PR, ask a question, or tweet me ([@yakczar](http://ctt.ec/Rq054)).
|
||||
`croc` has gone through many iterations, and I am awed by all the great contributions! If you feel like contributing, in any way, by all means you can send an Issue, a PR, or ask a question.
|
||||
|
||||
Thanks [@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole), [@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28), [@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). Finally thanks for making pull requests [@maximbaz](https://github.com/maximbaz), [@meyermarcel](https://github.com/meyermarcel), [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu), [@nicolashardy](https://github.com/nicolashardy), [@fbartels](https://github.com/fbartels), [@rkuprov](https://github.com/rkuprov), [@hreese](https://github.com/hreese), [@xenrox](https://github.com/xenrox) and [Ipar](https://github.com/lpar)!
|
||||
|
|
|
@ -4,7 +4,7 @@ After=network.target
|
|||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=nobody
|
||||
DynamicUser=yes
|
||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
ExecStart=/usr/bin/croc relay
|
||||
|
||||
|
|
49
go.mod
49
go.mod
|
@ -1,39 +1,42 @@
|
|||
module github.com/schollz/croc/v9
|
||||
module github.com/schollz/croc/v10
|
||||
|
||||
go 1.17
|
||||
go 1.20
|
||||
|
||||
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.2
|
||||
github.com/kalafut/imohash v1.0.3
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
|
||||
github.com/schollz/cli/v2 v2.2.1
|
||||
github.com/schollz/logger v1.2.0
|
||||
github.com/schollz/mnemonicode v1.0.1
|
||||
github.com/schollz/pake/v3 v3.0.4
|
||||
github.com/schollz/peerdiscovery v1.6.11
|
||||
github.com/schollz/progressbar/v3 v3.8.6
|
||||
github.com/stretchr/testify v1.6.1
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d
|
||||
github.com/schollz/pake/v3 v3.0.5
|
||||
github.com/schollz/peerdiscovery v1.7.3
|
||||
github.com/schollz/progressbar/v3 v3.14.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
golang.org/x/crypto v0.23.0
|
||||
golang.org/x/net v0.25.0
|
||||
golang.org/x/time v0.5.0
|
||||
)
|
||||
|
||||
require github.com/minio/highwayhash v1.0.2
|
||||
|
||||
require (
|
||||
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // 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.2.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-20210707234609-9bdfc483d499 // indirect
|
||||
github.com/twmb/murmur3 v1.1.6 // indirect
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // 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
|
||||
)
|
||||
|
|
137
go.sum
137
go.sum
|
@ -1,93 +1,132 @@
|
|||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
|
||||
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kalafut/imohash v1.0.2 h1:j/cUPa15YvXv7abJlM+kdJIycbBMpmO7WqhPl4YB76I=
|
||||
github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kalafut/imohash v1.0.3 h1:p9c61km8+6ZMqKRnERwdoxp/CztrdLNEbpsyGgf+A4M=
|
||||
github.com/kalafut/imohash v1.0.3/go.mod h1:6cn9lU0Sj8M4eu9UaQm1kR/5y3k/ayB68yntRhGloL4=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI=
|
||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
|
||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
|
||||
github.com/schollz/cli/v2 v2.2.1 h1:ou22Mj7ZPjrKz+8k2iDTWaHskEEV5NiAxGrdsCL36VU=
|
||||
github.com/schollz/cli/v2 v2.2.1/go.mod h1:My6bfphRLZUhZdlFUK8scAxMWHydE7k4s2ed2Dtnn+s=
|
||||
github.com/schollz/logger v1.2.0 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
|
||||
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
||||
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
|
||||
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||
github.com/schollz/pake/v3 v3.0.4 h1:rDNk9MwEVJ/C1+eDCr0DirJfpG+/sAZJ4YsYIyOTkSc=
|
||||
github.com/schollz/pake/v3 v3.0.4/go.mod h1:Q28rSQCHYzEtMQoi80R1MdpPRgNN/Si2Xdilx4NvsoQ=
|
||||
github.com/schollz/peerdiscovery v1.6.11 h1:3SG5vV1plIxylDg81fKgnqyrvlem5MrNcgcb0TLBjlE=
|
||||
github.com/schollz/peerdiscovery v1.6.11/go.mod h1:duO2S6wH3IuPJXwniPXp/9f69S2gFUSA9ePbAcKatJg=
|
||||
github.com/schollz/progressbar/v3 v3.8.6 h1:QruMUdzZ1TbEP++S1m73OqRJk20ON11m6Wqv4EoGg8c=
|
||||
github.com/schollz/progressbar/v3 v3.8.6/go.mod h1:W5IEwbJecncFGBvuEh4A7HT1nZZ6WNIL2i3qbnI0WKY=
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d h1:3zCjdgCJbo9Fot3UoqZkpGiDgT6Nf+iUnOsDEJQay+c=
|
||||
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||
github.com/schollz/pake/v3 v3.0.5 h1:MnZVdI987lkjln9BSx/zUb724TZISa2jbO+dPj6BvgQ=
|
||||
github.com/schollz/pake/v3 v3.0.5/go.mod h1:OGbG6htRwSKo6V8R5tg61ufpFmZM1b/PrrSp6g2ZLLc=
|
||||
github.com/schollz/peerdiscovery v1.7.3 h1:/pt1G0rZ80fSPoI/FgGC5P7MxpkRXD6u0pe6PJbYcIE=
|
||||
github.com/schollz/peerdiscovery v1.7.3/go.mod h1:mVlPNJ5DWbMi52VzpXxGbqXKdFANx3qw0Jsp3EQMCrE=
|
||||
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
|
||||
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499 h1:bPQ48TuiAuGTZDm54H2EV/2+eRRBHP61bKDkKSEPW4A=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 h1:sDWDZkwYqX0jvLWstKzFwh+pYhQNaVg65BgSkCP/f7U=
|
||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
|
||||
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
|
||||
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
||||
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 h1:xHms4gcpe1YE7A3yIllJXP16CMAGuqwO2lX1mTyyRRc=
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
|
||||
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
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/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/tylerb/is.v1 v1.1.2 h1:AB/MANFml2ySf+adwcinvajyHvsYltAOD+rb/8njfSU=
|
||||
gopkg.in/tylerb/is.v1 v1.1.2/go.mod h1:9yQB2tyIhZ5oph6Kk5Sq7cJMd9c5Jpa1p3hr9kxzPqo=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
2
main.go
2
main.go
|
@ -7,7 +7,7 @@ package main
|
|||
import (
|
||||
"log"
|
||||
|
||||
"github.com/schollz/croc/v9/src/cli"
|
||||
"github.com/schollz/croc/v10/src/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
251
src/cli/cli.go
251
src/cli/cli.go
|
@ -9,16 +9,19 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"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/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"
|
||||
)
|
||||
|
||||
|
@ -33,7 +36,7 @@ func Run() (err error) {
|
|||
app := cli.NewApp()
|
||||
app.Name = "croc"
|
||||
if Version == "" {
|
||||
Version = "v9.5.5-a6a3a57"
|
||||
Version = "v10.0.7"
|
||||
}
|
||||
app.Version = Version
|
||||
app.Compiled = time.Now()
|
||||
|
@ -41,6 +44,7 @@ func Run() (err error) {
|
|||
app.UsageText = `Send a file:
|
||||
croc send file.txt
|
||||
|
||||
-git to respect your .gitignore
|
||||
Send multiple files:
|
||||
croc send file1.txt file2.txt file3.txt
|
||||
or
|
||||
|
@ -61,12 +65,15 @@ func Run() (err error) {
|
|||
Description: "send file(s), or folder, over the relay",
|
||||
ArgsUsage: "[filename(s) or folder]",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{Name: "zip", Usage: "zip folder before sending"},
|
||||
&cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"},
|
||||
&cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"},
|
||||
&cli.StringFlag{Name: "text", Aliases: []string{"t"}, Usage: "send some text"},
|
||||
&cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
|
||||
&cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
|
||||
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
|
||||
&cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"},
|
||||
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
|
||||
&cli.IntFlag{Name: "transfers", Value: 4, Usage: "number of ports to use for transfers"},
|
||||
},
|
||||
HelpName: "croc send",
|
||||
Action: send,
|
||||
|
@ -85,6 +92,7 @@ func Run() (err error) {
|
|||
}
|
||||
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"},
|
||||
|
@ -94,6 +102,7 @@ func Run() (err error) {
|
|||
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
|
||||
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
|
||||
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite"},
|
||||
&cli.BoolFlag{Name: "testing", Usage: "flag for testing purposes"},
|
||||
&cli.StringFlag{Name: "curve", Value: "p256", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
|
||||
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
||||
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
||||
|
@ -101,6 +110,7 @@ func Run() (err error) {
|
|||
&cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
|
||||
&cli.StringFlag{Name: "pass", Value: models.DEFAULT_PASSPHRASE, Usage: "password for the relay", EnvVars: []string{"CROC_PASS"}},
|
||||
&cli.StringFlag{Name: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
|
||||
&cli.StringFlag{Name: "connect", Value: "", Usage: "add a http proxy", EnvVars: []string{"HTTP_PROXY"}},
|
||||
&cli.StringFlag{Name: "throttleUpload", Value: "", Usage: "Throttle the upload speed e.g. 500k"},
|
||||
}
|
||||
app.EnableBashCompletion = true
|
||||
|
@ -116,6 +126,61 @@ func Run() (err error) {
|
|||
return true
|
||||
}
|
||||
|
||||
// check if "classic" is set
|
||||
classicFile := getClassicConfigFile(true)
|
||||
classicInsecureMode := utils.Exists(classicFile)
|
||||
if c.Bool("classic") {
|
||||
if classicInsecureMode {
|
||||
// classic mode not enabled
|
||||
fmt.Print(`Classic mode is currently ENABLED.
|
||||
|
||||
Disabling this mode will prevent the shared secret from being visible
|
||||
on the host's process list when passed via the command line. On a
|
||||
multi-user system, this will help ensure that other local users cannot
|
||||
access the shared secret and receive the files instead of the intended
|
||||
recipient.
|
||||
|
||||
Do you wish to continue to DISABLE the classic mode? (Y/n) `)
|
||||
choice := strings.ToLower(utils.GetInput(""))
|
||||
if choice == "y" || choice == "yes" {
|
||||
os.Remove(classicFile)
|
||||
fmt.Print("\nClassic mode DISABLED.\n\n")
|
||||
fmt.Print(`To send and receive, export the CROC_SECRET variable with the code phrase:
|
||||
|
||||
Send: CROC_SECRET=*** croc send file.txt
|
||||
|
||||
Receive: CROC_SECRET=*** croc` + "\n\n")
|
||||
} else {
|
||||
fmt.Print("\nClassic mode ENABLED.\n")
|
||||
|
||||
}
|
||||
} else {
|
||||
// enable classic mode
|
||||
// touch the file
|
||||
fmt.Print(`Classic mode is currently DISABLED.
|
||||
|
||||
Please note that enabling this mode will make the shared secret visible
|
||||
on the host's process list when passed via the command line. On a
|
||||
multi-user system, this could allow other local users to access the
|
||||
shared secret and receive the files instead of the intended recipient.
|
||||
|
||||
Do you wish to continue to enable the classic mode? (Y/n) `)
|
||||
choice := strings.ToLower(utils.GetInput(""))
|
||||
if choice == "y" || choice == "yes" {
|
||||
fmt.Print("\nClassic mode ENABLED.\n\n")
|
||||
os.WriteFile(classicFile, []byte("enabled"), 0o644)
|
||||
fmt.Print(`To send and receive, use the code phrase:
|
||||
|
||||
Send: croc send --code *** file.txt
|
||||
|
||||
Receive: croc ***` + "\n\n")
|
||||
} else {
|
||||
fmt.Print("\nClassic mode DISABLED.\n")
|
||||
}
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// if trying to send but forgot send, let the user know
|
||||
if c.Args().Present() && allStringsAreFiles(c.Args().Slice()) {
|
||||
fnames := []string{}
|
||||
|
@ -129,6 +194,7 @@ func Run() (err error) {
|
|||
return send(c)
|
||||
}
|
||||
}
|
||||
|
||||
return receive(c)
|
||||
}
|
||||
|
||||
|
@ -144,8 +210,8 @@ func setDebugLevel(c *cli.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
func getConfigFile() string {
|
||||
configFile, err := utils.GetConfigDir()
|
||||
func getSendConfigFile(requireValidPath bool) string {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return ""
|
||||
|
@ -153,6 +219,24 @@ func getConfigFile() string {
|
|||
return path.Join(configFile, "send.json")
|
||||
}
|
||||
|
||||
func getClassicConfigFile(requireValidPath bool) string {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return ""
|
||||
}
|
||||
return path.Join(configFile, "classic_enabled")
|
||||
}
|
||||
|
||||
func getReceiveConfigFile(requireValidPath bool) (string, error) {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return "", err
|
||||
}
|
||||
return path.Join(configFile, "receive.json"), nil
|
||||
}
|
||||
|
||||
func determinePass(c *cli.Context) (pass string) {
|
||||
pass = c.String("pass")
|
||||
b, err := os.ReadFile(pass)
|
||||
|
@ -165,6 +249,22 @@ func determinePass(c *cli.Context) (pass string) {
|
|||
func send(c *cli.Context) (err error) {
|
||||
setDebugLevel(c)
|
||||
comm.Socks5Proxy = c.String("socks5")
|
||||
comm.HttpProxy = c.String("connect")
|
||||
|
||||
portParam := c.Int("port")
|
||||
if portParam == 0 {
|
||||
portParam = 9009
|
||||
}
|
||||
transfersParam := c.Int("transfers")
|
||||
if transfersParam == 0 {
|
||||
transfersParam = 4
|
||||
}
|
||||
|
||||
ports := make([]string, transfersParam+1)
|
||||
for i := 0; i <= transfersParam; i++ {
|
||||
ports[i] = strconv.Itoa(portParam + i)
|
||||
}
|
||||
|
||||
crocOptions := croc.Options{
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: true,
|
||||
|
@ -176,7 +276,7 @@ func send(c *cli.Context) (err error) {
|
|||
DisableLocal: c.Bool("no-local"),
|
||||
OnlyLocal: c.Bool("local"),
|
||||
IgnoreStdin: c.Bool("ignore-stdin"),
|
||||
RelayPorts: strings.Split(c.String("ports"), ","),
|
||||
RelayPorts: ports,
|
||||
Ask: c.Bool("ask"),
|
||||
NoMultiplexing: c.Bool("no-multi"),
|
||||
RelayPassword: determinePass(c),
|
||||
|
@ -186,13 +286,15 @@ func send(c *cli.Context) (err error) {
|
|||
Curve: c.String("curve"),
|
||||
HashAlgorithm: c.String("hash"),
|
||||
ThrottleUpload: c.String("throttleUpload"),
|
||||
ZipFolder: c.Bool("zip"),
|
||||
GitIgnore: c.Bool("git"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
|
||||
crocOptions.RelayAddress = ""
|
||||
}
|
||||
b, errOpen := os.ReadFile(getConfigFile())
|
||||
b, errOpen := os.ReadFile(getSendConfigFile(false))
|
||||
if errOpen == nil && !c.Bool("remember") {
|
||||
var rememberedOptions croc.Options
|
||||
err = json.Unmarshal(b, &rememberedOptions)
|
||||
|
@ -228,6 +330,12 @@ func send(c *cli.Context) (err error) {
|
|||
if !c.IsSet("local") {
|
||||
crocOptions.OnlyLocal = rememberedOptions.OnlyLocal
|
||||
}
|
||||
if !c.IsSet("hash") {
|
||||
crocOptions.HashAlgorithm = rememberedOptions.HashAlgorithm
|
||||
}
|
||||
if !c.IsSet("git") {
|
||||
crocOptions.GitIgnore = rememberedOptions.GitIgnore
|
||||
}
|
||||
}
|
||||
|
||||
var fnames []string
|
||||
|
@ -262,12 +370,37 @@ 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:
|
||||
|
||||
export CROC_SECRET="****"
|
||||
croc send file.txt
|
||||
|
||||
Or you can have the code phrase automaticlaly generated:
|
||||
|
||||
croc send file.txt
|
||||
|
||||
Or you can go back to the classic croc behavior by enabling classic mode:
|
||||
|
||||
croc --classic
|
||||
|
||||
`)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(crocOptions.SharedSecret) == 0 {
|
||||
// generate code phrase
|
||||
crocOptions.SharedSecret = utils.GetRandomName()
|
||||
}
|
||||
|
||||
minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames)
|
||||
minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder, crocOptions.GitIgnore)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -323,7 +456,7 @@ func makeTempFileWithString(s string) (fnames []string, err error) {
|
|||
|
||||
func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
||||
if c.Bool("remember") {
|
||||
configFile := getConfigFile()
|
||||
configFile := getSendConfigFile(true)
|
||||
log.Debug("saving config file")
|
||||
var bConfig []byte
|
||||
// if the code wasn't set, don't save it
|
||||
|
@ -335,7 +468,7 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(configFile, bConfig, 0644)
|
||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
@ -344,8 +477,39 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
|||
}
|
||||
}
|
||||
|
||||
type TabComplete struct{}
|
||||
|
||||
func (t TabComplete) Do(line []rune, pos int) ([][]rune, int) {
|
||||
var words = strings.SplitAfter(string(line), "-")
|
||||
var lastPartialWord = words[len(words)-1]
|
||||
var nbCharacter = len(lastPartialWord)
|
||||
if nbCharacter == 0 {
|
||||
// No completion
|
||||
return [][]rune{[]rune("")}, 0
|
||||
}
|
||||
if len(words) == 1 && nbCharacter == utils.NbPinNumbers {
|
||||
// Check if word is indeed a number
|
||||
_, err := strconv.Atoi(lastPartialWord)
|
||||
if err == nil {
|
||||
return [][]rune{[]rune("-")}, nbCharacter
|
||||
}
|
||||
}
|
||||
var strArray [][]rune
|
||||
for _, s := range mnemonicode.WordList {
|
||||
if strings.HasPrefix(s, lastPartialWord) {
|
||||
var completionCandidate = s[nbCharacter:]
|
||||
if len(words) <= mnemonicode.WordsRequired(utils.NbBytesWords) {
|
||||
completionCandidate += "-"
|
||||
}
|
||||
strArray = append(strArray, []rune(completionCandidate))
|
||||
}
|
||||
}
|
||||
return strArray, nbCharacter
|
||||
}
|
||||
|
||||
func receive(c *cli.Context) (err error) {
|
||||
comm.Socks5Proxy = c.String("socks5")
|
||||
comm.HttpProxy = c.String("connect")
|
||||
crocOptions := croc.Options{
|
||||
SharedSecret: c.String("code"),
|
||||
IsSender: false,
|
||||
|
@ -360,6 +524,7 @@ func receive(c *cli.Context) (err error) {
|
|||
IP: c.String("ip"),
|
||||
Overwrite: c.Bool("overwrite"),
|
||||
Curve: c.String("curve"),
|
||||
TestFlag: c.Bool("testing"),
|
||||
}
|
||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||
crocOptions.RelayAddress6 = ""
|
||||
|
@ -371,6 +536,8 @@ func receive(c *cli.Context) (err error) {
|
|||
case 1:
|
||||
crocOptions.SharedSecret = c.Args().First()
|
||||
case 3:
|
||||
fallthrough
|
||||
case 4:
|
||||
var phrase []string
|
||||
phrase = append(phrase, c.Args().First())
|
||||
phrase = append(phrase, c.Args().Tail()...)
|
||||
|
@ -379,14 +546,14 @@ func receive(c *cli.Context) (err error) {
|
|||
|
||||
// load options here
|
||||
setDebugLevel(c)
|
||||
configFile, err := utils.GetConfigDir()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
||||
doRemember := c.Bool("remember")
|
||||
configFile, err := getReceiveConfigFile(doRemember)
|
||||
if err != nil && doRemember {
|
||||
return
|
||||
}
|
||||
configFile = path.Join(configFile, "receive.json")
|
||||
b, errOpen := os.ReadFile(configFile)
|
||||
if errOpen == nil && !c.Bool("remember") {
|
||||
if errOpen == nil && !doRemember {
|
||||
var rememberedOptions croc.Options
|
||||
err = json.Unmarshal(b, &rememberedOptions)
|
||||
if err != nil {
|
||||
|
@ -420,8 +587,44 @@ 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:
|
||||
|
||||
export CROC_SECRET="****"
|
||||
croc
|
||||
|
||||
Or you can specify the code phrase when you run croc without
|
||||
declaring the secret on the command line:
|
||||
|
||||
croc
|
||||
Enter receive code: ****
|
||||
|
||||
Or you can go back to the classic croc behavior by enabling classic mode:
|
||||
|
||||
croc --classic
|
||||
|
||||
`)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
if crocOptions.SharedSecret == "" {
|
||||
crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
|
||||
l, err := readline.NewEx(&readline.Config{
|
||||
Prompt: "Enter receive code: ",
|
||||
AutoComplete: TabComplete{},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
crocOptions.SharedSecret, err = l.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if c.String("out") != "" {
|
||||
if err = os.Chdir(c.String("out")); err != nil {
|
||||
|
@ -435,7 +638,7 @@ func receive(c *cli.Context) (err error) {
|
|||
}
|
||||
|
||||
// save the config
|
||||
if c.Bool("remember") {
|
||||
if doRemember {
|
||||
log.Debug("saving config file")
|
||||
var bConfig []byte
|
||||
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
|
||||
|
@ -443,7 +646,7 @@ func receive(c *cli.Context) (err error) {
|
|||
log.Error(err)
|
||||
return
|
||||
}
|
||||
err = os.WriteFile(configFile, bConfig, 0644)
|
||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
@ -469,7 +672,7 @@ func relay(c *cli.Context) (err error) {
|
|||
continue
|
||||
}
|
||||
go func(portStr string) {
|
||||
err = tcp.Run(debugString, host, portStr, determinePass(c))
|
||||
err := tcp.Run(debugString, host, portStr, determinePass(c))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
|
@ -10,12 +10,14 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/magisterquis/connectproxy"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
log "github.com/schollz/logger"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
|
||||
var Socks5Proxy = ""
|
||||
var HttpProxy = ""
|
||||
|
||||
var MAGIC_BYTES = []byte("croc")
|
||||
|
||||
|
@ -39,7 +41,7 @@ func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err err
|
|||
}
|
||||
socks5ProxyURL, urlParseError := url.Parse(Socks5Proxy)
|
||||
if urlParseError != nil {
|
||||
err = fmt.Errorf("Unable to parse socks proxy url: %s", urlParseError)
|
||||
err = fmt.Errorf("unable to parse socks proxy url: %s", urlParseError)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
|
@ -51,6 +53,27 @@ func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err err
|
|||
}
|
||||
log.Debug("dialing with dialer.Dial")
|
||||
connection, err = dialer.Dial("tcp", address)
|
||||
} else if HttpProxy != "" && !utils.IsLocalIP(address) {
|
||||
var dialer proxy.Dialer
|
||||
// prepend schema if no schema is given
|
||||
if !strings.Contains(HttpProxy, `://`) {
|
||||
HttpProxy = `http://` + HttpProxy
|
||||
}
|
||||
HttpProxyURL, urlParseError := url.Parse(HttpProxy)
|
||||
if urlParseError != nil {
|
||||
err = fmt.Errorf("unable to parse http proxy url: %s", urlParseError)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
dialer, err = connectproxy.New(HttpProxyURL, proxy.Direct)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("proxy failed: %w", err)
|
||||
log.Debug(err)
|
||||
return
|
||||
}
|
||||
log.Debug("dialing with dialer.Dial")
|
||||
connection, err = dialer.Dial("tcp", address)
|
||||
|
||||
} else {
|
||||
log.Debugf("dialing to %s with timelimit %s", address, tlimit)
|
||||
connection, err = net.DialTimeout("tcp", address, tlimit)
|
||||
|
@ -115,7 +138,7 @@ func (c *Comm) Write(b []byte) (n int, err error) {
|
|||
|
||||
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||
// long read deadline in case waiting for file
|
||||
if err := c.connection.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
if err = c.connection.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
log.Warnf("error setting read deadline: %v", err)
|
||||
}
|
||||
// must clear the timeout setting
|
||||
|
@ -152,7 +175,7 @@ func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
|||
numBytes = int(numBytesUint32)
|
||||
|
||||
// shorten the reading deadline in case getting weird data
|
||||
if err := c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
if err = c.connection.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
log.Warnf("error setting read deadline: %v", err)
|
||||
}
|
||||
buf = make([]byte, numBytes)
|
||||
|
|
|
@ -31,7 +31,7 @@ func TestComm(t *testing.T) {
|
|||
log.Error(err)
|
||||
}
|
||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||
go func(port string, connection net.Conn) {
|
||||
go func(_ string, connection net.Conn) {
|
||||
c := New(connection)
|
||||
err = c.Send([]byte("hello, world"))
|
||||
assert.Nil(t, err)
|
||||
|
@ -49,7 +49,7 @@ func TestComm(t *testing.T) {
|
|||
}()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
a, err := NewConnection("localhost:"+port, 10*time.Minute)
|
||||
a, err := NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
||||
assert.Nil(t, err)
|
||||
data, err := a.Receive()
|
||||
assert.Equal(t, []byte("hello, world"), data)
|
||||
|
@ -63,5 +63,4 @@ func TestComm(t *testing.T) {
|
|||
assert.NotNil(t, a.Send(token))
|
||||
_, err = a.Write(token)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
}
|
||||
|
|
556
src/croc/croc.go
556
src/croc/croc.go
|
@ -3,7 +3,9 @@ package croc
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -20,18 +22,19 @@ import (
|
|||
"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/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 (
|
||||
|
@ -56,6 +59,7 @@ func Debug(debug bool) {
|
|||
type Options struct {
|
||||
IsSender bool
|
||||
SharedSecret string
|
||||
RoomName string
|
||||
Debug bool
|
||||
RelayAddress string
|
||||
RelayAddress6 string
|
||||
|
@ -75,6 +79,14 @@ type Options struct {
|
|||
Curve string
|
||||
HashAlgorithm string
|
||||
ThrottleUpload string
|
||||
ZipFolder bool
|
||||
TestFlag bool
|
||||
GitIgnore bool
|
||||
}
|
||||
|
||||
type SimpleMessage struct {
|
||||
Bytes []byte
|
||||
Kind string
|
||||
}
|
||||
|
||||
// Client holds the state of the croc transfer
|
||||
|
@ -99,6 +111,7 @@ type Client struct {
|
|||
TotalNumberFolders int
|
||||
FilesToTransferCurrentNum int
|
||||
FilesHasFinished map[int]struct{}
|
||||
TotalFilesIgnored int
|
||||
|
||||
// send / receive information of current file
|
||||
CurrentFile *os.File
|
||||
|
@ -146,6 +159,8 @@ type FileInfo struct {
|
|||
IsEncrypted bool `json:"e,omitempty"`
|
||||
Symlink string `json:"sy,omitempty"`
|
||||
Mode os.FileMode `json:"md,omitempty"`
|
||||
TempFile bool `json:"tf,omitempty"`
|
||||
IsIgnored bool `json:"ig,omitempty"`
|
||||
}
|
||||
|
||||
// RemoteFileRequest requests specific bytes
|
||||
|
@ -175,19 +190,23 @@ 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)
|
||||
|
||||
// initialize throttler
|
||||
if len(c.Options.ThrottleUpload) > 1 && c.Options.IsSender {
|
||||
upload := c.Options.ThrottleUpload[:len(c.Options.ThrottleUpload)-1]
|
||||
uploadLimit, err := strconv.ParseInt(upload, 10, 64)
|
||||
var uploadLimit int64
|
||||
uploadLimit, err = strconv.ParseInt(upload, 10, 64)
|
||||
if err != nil {
|
||||
panic("Could not parse given Upload Limit")
|
||||
}
|
||||
|
@ -206,7 +225,7 @@ func New(ops Options) (c *Client, err error) {
|
|||
panic("Could not parse given Upload Limit")
|
||||
}
|
||||
}
|
||||
// Somehow 4* is neccessary
|
||||
// Somehow 4* is necessary
|
||||
rt = rate.Every(time.Second / (4 * time.Duration(uploadLimit)))
|
||||
if int(uploadLimit) > minBurstSize {
|
||||
minBurstSize = int(uploadLimit)
|
||||
|
@ -247,10 +266,52 @@ func isEmptyFolder(folderPath string) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
// This function retrives the important file informations
|
||||
// for every file that will be transfered
|
||||
func GetFilesInfo(fnames []string) (filesInfo []FileInfo, emptyFolders []FileInfo, totalNumberFolders int, err error) {
|
||||
// fnames: the relativ/absolute paths of files/folders that will be transfered
|
||||
// helper function to walk each subfolder and parses against an ignore file.
|
||||
// returns a hashmap Key: Absolute filepath, Value: boolean (true=ignore)
|
||||
func gitWalk(dir string, gitObj *ignore.GitIgnore, files map[string]bool) {
|
||||
var ignoredDir bool
|
||||
var current string
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isChild(current, path) && ignoredDir {
|
||||
files[path] = true
|
||||
return nil
|
||||
}
|
||||
if info.IsDir() && filepath.Base(path) == filepath.Base(dir) {
|
||||
ignoredDir = false // Skip applying ignore rules for root directory
|
||||
return nil
|
||||
}
|
||||
if gitObj.MatchesPath(info.Name()) {
|
||||
files[path] = true
|
||||
ignoredDir = true
|
||||
current = path
|
||||
return nil
|
||||
} else {
|
||||
files[path] = false
|
||||
ignoredDir = false
|
||||
return nil
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("filepath error")
|
||||
}
|
||||
}
|
||||
|
||||
func isChild(parentPath, childPath string) bool {
|
||||
relPath, err := filepath.Rel(parentPath, childPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !strings.HasPrefix(relPath, "..")
|
||||
|
||||
}
|
||||
|
||||
// This function retrieves the important file information
|
||||
// for every file that will be transferred
|
||||
func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []FileInfo, emptyFolders []FileInfo, totalNumberFolders int, err error) {
|
||||
// fnames: the relative/absolute paths of files/folders that will be transferred
|
||||
totalNumberFolders = 0
|
||||
var paths []string
|
||||
for _, fname := range fnames {
|
||||
|
@ -267,21 +328,92 @@ func GetFilesInfo(fnames []string) (filesInfo []FileInfo, emptyFolders []FileInf
|
|||
paths = append(paths, fname)
|
||||
}
|
||||
}
|
||||
|
||||
for _, path := range paths {
|
||||
stat, errStat := os.Lstat(path)
|
||||
var ignoredPaths = make(map[string]bool)
|
||||
if ignoreGit {
|
||||
wd, wdErr := os.Stat(".gitignore")
|
||||
if wdErr == nil {
|
||||
gitIgnore, gitErr := ignore.CompileIgnoreFile(wd.Name())
|
||||
if gitErr == nil {
|
||||
for _, path := range paths {
|
||||
abs, absErr := filepath.Abs(path)
|
||||
if absErr != nil {
|
||||
err = absErr
|
||||
return
|
||||
}
|
||||
if gitIgnore.MatchesPath(path) {
|
||||
ignoredPaths[abs] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, path := range paths {
|
||||
abs, absErr := filepath.Abs(path)
|
||||
if absErr != nil {
|
||||
err = absErr
|
||||
return
|
||||
}
|
||||
file, fileErr := os.Stat(path)
|
||||
if fileErr == nil && file.IsDir() {
|
||||
_, subErr := os.Stat(filepath.Join(path, ".gitignore"))
|
||||
if subErr == nil {
|
||||
gitObj, gitObjErr := ignore.CompileIgnoreFile(filepath.Join(path, ".gitignore"))
|
||||
if gitObjErr != nil {
|
||||
err = gitObjErr
|
||||
return
|
||||
}
|
||||
gitWalk(abs, gitObj, ignoredPaths)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, fpath := range paths {
|
||||
stat, errStat := os.Lstat(fpath)
|
||||
|
||||
if errStat != nil {
|
||||
err = errStat
|
||||
return
|
||||
}
|
||||
|
||||
absPath, errAbs := filepath.Abs(path)
|
||||
absPath, errAbs := filepath.Abs(fpath)
|
||||
|
||||
if errAbs != nil {
|
||||
err = errAbs
|
||||
return
|
||||
}
|
||||
if stat.IsDir() && zipfolder {
|
||||
if fpath[len(fpath)-1:] != "/" {
|
||||
fpath += "/"
|
||||
}
|
||||
fpath = filepath.Dir(fpath)
|
||||
dest := filepath.Base(fpath) + ".zip"
|
||||
utils.ZipDirectory(dest, fpath)
|
||||
stat, errStat = os.Lstat(dest)
|
||||
if errStat != nil {
|
||||
err = errStat
|
||||
return
|
||||
}
|
||||
absPath, errAbs = filepath.Abs(dest)
|
||||
if errAbs != nil {
|
||||
err = errAbs
|
||||
return
|
||||
}
|
||||
|
||||
fInfo := FileInfo{
|
||||
Name: stat.Name(),
|
||||
FolderRemote: "./",
|
||||
FolderSource: filepath.Dir(absPath),
|
||||
Size: stat.Size(),
|
||||
ModTime: stat.ModTime(),
|
||||
Mode: stat.Mode(),
|
||||
TempFile: true,
|
||||
IsIgnored: ignoredPaths[absPath],
|
||||
}
|
||||
if fInfo.IsIgnored {
|
||||
continue
|
||||
}
|
||||
filesInfo = append(filesInfo, fInfo)
|
||||
continue
|
||||
}
|
||||
|
||||
if stat.IsDir() {
|
||||
err = filepath.Walk(absPath,
|
||||
|
@ -292,22 +424,32 @@ func GetFilesInfo(fnames []string) (filesInfo []FileInfo, emptyFolders []FileInf
|
|||
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName),
|
||||
filepath.Dir(absPath)+string(os.PathSeparator))
|
||||
if !info.IsDir() {
|
||||
filesInfo = append(filesInfo, FileInfo{
|
||||
fInfo := FileInfo{
|
||||
Name: info.Name(),
|
||||
FolderRemote: strings.Replace(remoteFolder, string(os.PathSeparator), "/", -1) + "/",
|
||||
FolderRemote: strings.ReplaceAll(remoteFolder, string(os.PathSeparator), "/") + "/",
|
||||
FolderSource: filepath.Dir(pathName),
|
||||
Size: info.Size(),
|
||||
ModTime: info.ModTime(),
|
||||
Mode: info.Mode(),
|
||||
})
|
||||
TempFile: false,
|
||||
IsIgnored: ignoredPaths[pathName],
|
||||
}
|
||||
if fInfo.IsIgnored && ignoreGit {
|
||||
return nil
|
||||
} else {
|
||||
filesInfo = append(filesInfo, fInfo)
|
||||
}
|
||||
} else {
|
||||
totalNumberFolders++
|
||||
if ignoredPaths[pathName] {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
isEmptyFolder, _ := isEmptyFolder(pathName)
|
||||
totalNumberFolders++
|
||||
if isEmptyFolder {
|
||||
emptyFolders = append(emptyFolders, FileInfo{
|
||||
// Name: info.Name(),
|
||||
FolderRemote: strings.Replace(strings.TrimPrefix(pathName,
|
||||
filepath.Dir(absPath)+string(os.PathSeparator)), string(os.PathSeparator), "/", -1) + "/",
|
||||
FolderRemote: strings.ReplaceAll(strings.TrimPrefix(pathName,
|
||||
filepath.Dir(absPath)+string(os.PathSeparator)), string(os.PathSeparator), "/") + "/",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -316,17 +458,24 @@ func GetFilesInfo(fnames []string) (filesInfo []FileInfo, emptyFolders []FileInf
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
} else {
|
||||
filesInfo = append(filesInfo, FileInfo{
|
||||
fInfo := FileInfo{
|
||||
Name: stat.Name(),
|
||||
FolderRemote: "./",
|
||||
FolderSource: filepath.Dir(absPath),
|
||||
Size: stat.Size(),
|
||||
ModTime: stat.ModTime(),
|
||||
Mode: stat.Mode(),
|
||||
})
|
||||
TempFile: false,
|
||||
IsIgnored: ignoredPaths[absPath],
|
||||
}
|
||||
if fInfo.IsIgnored && ignoreGit {
|
||||
continue
|
||||
} else {
|
||||
filesInfo = append(filesInfo, fInfo)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -357,7 +506,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 {
|
||||
|
@ -381,14 +530,18 @@ func (c *Client) sendCollectFiles(filesInfo []FileInfo) (err error) {
|
|||
}
|
||||
|
||||
fmt.Fprintf(os.Stderr, "\r ")
|
||||
fmt.Fprintf(os.Stderr, "\rSending %s and %s (%s)\n", fname, folderName, utils.ByteCountDecimal(totalFilesSize))
|
||||
if c.TotalNumberFolders > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\rSending %s and %s (%s)\n", fname, folderName, utils.ByteCountDecimal(totalFilesSize))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "\rSending %s (%s)\n", fname, utils.ByteCountDecimal(totalFilesSize))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) setupLocalRelay() {
|
||||
// setup the relay locally
|
||||
firstPort, _ := strconv.Atoi(c.Options.RelayPorts[0])
|
||||
openPorts := utils.FindOpenPorts("localhost", firstPort, len(c.Options.RelayPorts))
|
||||
openPorts := utils.FindOpenPorts("127.0.0.1", firstPort, len(c.Options.RelayPorts))
|
||||
if len(openPorts) < len(c.Options.RelayPorts) {
|
||||
panic("not enough open ports to run local relay")
|
||||
}
|
||||
|
@ -401,7 +554,7 @@ func (c *Client) setupLocalRelay() {
|
|||
if c.Options.Debug {
|
||||
debugString = "debug"
|
||||
}
|
||||
err := tcp.Run(debugString, "localhost", portStr, c.Options.RelayPassword, strings.Join(c.Options.RelayPorts[1:], ","))
|
||||
err := tcp.Run(debugString, "127.0.0.1", portStr, c.Options.RelayPassword, strings.Join(c.Options.RelayPorts[1:], ","))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -411,7 +564,7 @@ func (c *Client) setupLocalRelay() {
|
|||
|
||||
func (c *Client) broadcastOnLocalNetwork(useipv6 bool) {
|
||||
var timeLimit time.Duration
|
||||
//if we don't use an external relay, the broadcast messages need to be sent continuously
|
||||
// if we don't use an external relay, the broadcast messages need to be sent continuously
|
||||
if c.Options.OnlyLocal {
|
||||
timeLimit = -1 * time.Second
|
||||
} else {
|
||||
|
@ -440,10 +593,10 @@ 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("localhost:"+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 localhost:%s: %w", c.Options.RelayPorts[0], err)
|
||||
err = fmt.Errorf("could not connect to 127.0.0.1:%s: %w", c.Options.RelayPorts[0], err)
|
||||
log.Debug(err)
|
||||
// not really an error because it will try to connect over the actual relay
|
||||
return
|
||||
|
@ -461,7 +614,7 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) {
|
|||
}
|
||||
c.conn[0] = conn
|
||||
log.Debug("exchanged header message")
|
||||
c.Options.RelayAddress = "localhost"
|
||||
c.Options.RelayAddress = "127.0.0.1"
|
||||
c.Options.RelayPorts = strings.Split(banner, ",")
|
||||
if c.Options.NoMultiplexing {
|
||||
log.Debug("no multiplexing")
|
||||
|
@ -477,12 +630,11 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
|
|||
c.TotalNumberFolders = totalNumberFolders
|
||||
c.TotalNumberOfContents = len(filesInfo)
|
||||
err = c.sendCollectFiles(filesInfo)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
flags := &strings.Builder{}
|
||||
if c.Options.RelayAddress != models.DEFAULT_RELAY {
|
||||
if c.Options.RelayAddress != models.DEFAULT_RELAY && !c.Options.OnlyLocal {
|
||||
flags.WriteString("--relay " + c.Options.RelayAddress + " ")
|
||||
}
|
||||
if c.Options.RelayPassword != models.DEFAULT_PASSPHRASE {
|
||||
|
@ -529,7 +681,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
|
||||
|
@ -547,13 +699,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
|
||||
|
@ -561,22 +734,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)
|
||||
if err := conn.Send(bips); err != nil {
|
||||
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
|
||||
|
@ -726,7 +925,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
|
||||
|
@ -741,23 +940,74 @@ func (c *Client) Receive() (err error) {
|
|||
log.Debugf("receiver connection established: %+v", c.conn[0])
|
||||
log.Debugf("banner: %s", banner)
|
||||
|
||||
if !usingLocal && !c.Options.DisableLocal && !isIPset {
|
||||
if c.Options.TestFlag {
|
||||
log.Debugf("TEST FLAG ENABLED, TESTING LOCAL IPS")
|
||||
}
|
||||
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:]
|
||||
|
@ -768,8 +1018,10 @@ func (c *Client) Receive() (err error) {
|
|||
haveLocalIP := false
|
||||
for _, localIP := range localIps {
|
||||
localIPparsed := net.ParseIP(localIP)
|
||||
log.Debugf("localIP: %+v, localIPparsed: %+v", localIP, localIPparsed)
|
||||
if ipv4Net.Contains(localIPparsed) {
|
||||
haveLocalIP = true
|
||||
log.Debugf("ip: %+v is a local IP", ip)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -778,8 +1030,8 @@ func (c *Client) Receive() (err error) {
|
|||
continue
|
||||
}
|
||||
|
||||
serverTry := fmt.Sprintf("%s:%s", ip, port)
|
||||
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 500*time.Millisecond)
|
||||
serverTry := net.JoinHostPort(ip, port)
|
||||
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)
|
||||
|
@ -799,7 +1051,7 @@ func (c *Client) Receive() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := c.conn[0].Send(handshakeRequest); err != nil {
|
||||
if err = c.conn[0].Send(handshakeRequest); err != nil {
|
||||
log.Errorf("handshake send error: %v", err)
|
||||
}
|
||||
c.Options.RelayPorts = strings.Split(banner, ",")
|
||||
|
@ -812,7 +1064,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
|
||||
|
@ -851,6 +1103,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
|
||||
}
|
||||
|
@ -865,6 +1118,24 @@ func (c *Client) transfer() (err error) {
|
|||
}
|
||||
err = nil
|
||||
}
|
||||
if c.Options.IsSender && c.SuccessfulTransfer {
|
||||
for _, file := range c.FilesToTransfer {
|
||||
if file.TempFile {
|
||||
fmt.Println("Removing " + file.Name)
|
||||
os.Remove(file.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.SuccessfulTransfer && !c.Options.IsSender {
|
||||
for _, file := range c.FilesToTransfer {
|
||||
if file.TempFile {
|
||||
utils.UnzipDirectory(".", file.Name)
|
||||
os.Remove(file.Name)
|
||||
log.Debugf("Removing %s\n", file.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.Options.Stdout && !c.Options.IsSender {
|
||||
pathToFile := path.Join(
|
||||
|
@ -877,10 +1148,10 @@ func (c *Client) transfer() (err error) {
|
|||
c.CurrentFile.Close()
|
||||
c.CurrentFileIsClosed = true
|
||||
}
|
||||
if err := os.Remove(pathToFile); err != nil {
|
||||
if err = os.Remove(pathToFile); err != nil {
|
||||
log.Warnf("error removing %s: %v", pathToFile, err)
|
||||
}
|
||||
fmt.Print("\n")
|
||||
fmt.Fprint(os.Stderr, "\n")
|
||||
}
|
||||
if err != nil && strings.Contains(err.Error(), "pake not successful") {
|
||||
log.Debugf("pake error: %s", err.Error())
|
||||
|
@ -888,7 +1159,7 @@ func (c *Client) transfer() (err error) {
|
|||
}
|
||||
if err != nil && strings.Contains(err.Error(), "unexpected end of JSON input") {
|
||||
log.Debugf("error: %s", err.Error())
|
||||
err = fmt.Errorf("room not ready")
|
||||
err = fmt.Errorf("room (secure channel) not ready, maybe peer disconnected")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -928,6 +1199,21 @@ 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)
|
||||
}
|
||||
// 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
|
||||
if !utils.ValidFileName(path.Join(c.FilesToTransfer[i].FolderRemote, fi.Name)) {
|
||||
return true, fmt.Errorf("invalid filename detected: '%s'", fi.Name)
|
||||
}
|
||||
}
|
||||
c.TotalNumberOfContents = 0
|
||||
if c.FilesToTransfer != nil {
|
||||
c.TotalNumberOfContents += len(c.FilesToTransfer)
|
||||
|
@ -976,7 +1262,11 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
|
|||
machID, _ := machineid.ID()
|
||||
fmt.Fprintf(os.Stderr, "\rYour machine id is '%s'.\n%s %s (%s) from '%s'? (Y/n) ", machID, action, fname, utils.ByteCountDecimal(totalSize), senderInfo.MachineID)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "\r%s %s and %s (%s)? (Y/n) ", action, fname, folderName, utils.ByteCountDecimal(totalSize))
|
||||
if c.TotalNumberFolders > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\r%s %s and %s (%s)? (Y/n) ", action, fname, folderName, utils.ByteCountDecimal(totalSize))
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "\r%s %s (%s)? (Y/n) ", action, fname, utils.ByteCountDecimal(totalSize))
|
||||
}
|
||||
}
|
||||
choice := strings.ToLower(utils.GetInput(""))
|
||||
if choice != "" && choice != "y" && choice != "yes" {
|
||||
|
@ -1018,7 +1308,7 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
|
|||
}
|
||||
}
|
||||
|
||||
// if no files are to be transfered, then we can end the file transfer process
|
||||
// if no files are to be transferred, then we can end the file transfer process
|
||||
if c.FilesToTransfer == nil {
|
||||
c.SuccessfulTransfer = true
|
||||
c.Step3RecipientRequestFile = true
|
||||
|
@ -1093,7 +1383,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
|
|||
go func(j int) {
|
||||
defer wg.Done()
|
||||
var host string
|
||||
if c.Options.RelayAddress == "localhost" {
|
||||
if c.Options.RelayAddress == "127.0.0.1" {
|
||||
host = c.Options.RelayAddress
|
||||
} else {
|
||||
host, _, err = net.SplitHostPort(c.Options.RelayAddress)
|
||||
|
@ -1107,7 +1397,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)
|
||||
|
@ -1296,7 +1586,7 @@ func (c *Client) recipientInitializeFile() (err error) {
|
|||
var errOpen error
|
||||
c.CurrentFile, errOpen = os.OpenFile(
|
||||
pathToFile,
|
||||
os.O_WRONLY, 0666)
|
||||
os.O_WRONLY, 0o666)
|
||||
var truncate bool // default false
|
||||
c.CurrentFileChunks = []int64{}
|
||||
c.CurrentFileChunkRanges = []int64{}
|
||||
|
@ -1319,6 +1609,10 @@ func (c *Client) recipientInitializeFile() (err error) {
|
|||
log.Error(errOpen)
|
||||
return errOpen
|
||||
}
|
||||
errChmod := os.Chmod(pathToFile, c.FilesToTransfer[c.FilesToTransferCurrentNum].Mode.Perm())
|
||||
if errChmod != nil {
|
||||
log.Error(errChmod)
|
||||
}
|
||||
truncate = true
|
||||
}
|
||||
if truncate {
|
||||
|
@ -1344,6 +1638,7 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
|
|||
}
|
||||
c.SuccessfulTransfer = true
|
||||
c.FilesHasFinished[c.FilesToTransferCurrentNum] = struct{}{}
|
||||
return
|
||||
}
|
||||
|
||||
err = c.recipientInitializeFile()
|
||||
|
@ -1416,6 +1711,9 @@ func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error)
|
|||
} else {
|
||||
description = " " + description
|
||||
}
|
||||
if len(description) > 20 {
|
||||
description = description[:17] + "..."
|
||||
}
|
||||
c.bar = progressbar.NewOptions64(1,
|
||||
progressbar.OptionOnCompletion(func() {
|
||||
c.fmtPrintUpdate()
|
||||
|
@ -1433,7 +1731,7 @@ func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error)
|
|||
}
|
||||
|
||||
func (c *Client) updateIfRecipientHasFileInfo() (err error) {
|
||||
if !(!c.Options.IsSender && c.Step2FileInfoTransferred && !c.Step3RecipientRequestFile) {
|
||||
if c.Options.IsSender || !c.Step2FileInfoTransferred || c.Step3RecipientRequestFile {
|
||||
return
|
||||
}
|
||||
// find the next file to transfer and send that number
|
||||
|
@ -1483,7 +1781,7 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
|
|||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -1571,7 +1869,6 @@ func (c *Client) updateState() (err error) {
|
|||
c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderSource,
|
||||
c.FilesToTransfer[c.FilesToTransferCurrentNum].Name,
|
||||
)
|
||||
|
||||
c.fread, err = os.Open(pathToFile)
|
||||
c.numfinished = 0
|
||||
if err != nil {
|
||||
|
@ -1593,6 +1890,9 @@ func (c *Client) setBar() {
|
|||
} else if !c.Options.IsSender {
|
||||
description = " " + description
|
||||
}
|
||||
if len(description) > 20 {
|
||||
description = description[:17] + "..."
|
||||
}
|
||||
c.bar = progressbar.NewOptions64(
|
||||
c.FilesToTransfer[c.FilesToTransferCurrentNum].Size,
|
||||
progressbar.OptionOnCompletion(func() {
|
||||
|
@ -1620,14 +1920,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
|
||||
}
|
||||
|
||||
|
@ -1661,7 +1961,7 @@ func (c *Client) receiveData(i int) {
|
|||
if !c.CurrentFileIsClosed && (c.TotalChunksTransferred == len(c.CurrentFileChunks) || c.TotalSent == c.FilesToTransfer[c.FilesToTransferCurrentNum].Size) {
|
||||
c.CurrentFileIsClosed = true
|
||||
log.Debug("finished receiving!")
|
||||
if err := c.CurrentFile.Close(); err != nil {
|
||||
if err = c.CurrentFile.Close(); err != nil {
|
||||
log.Debugf("error closing %s: %v", c.CurrentFile.Name(), err)
|
||||
} else {
|
||||
log.Debugf("Successful closing %s", c.CurrentFile.Name())
|
||||
|
@ -1703,62 +2003,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)
|
||||
|
||||
|
|
|
@ -5,11 +5,12 @@ import (
|
|||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"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"
|
||||
)
|
||||
|
@ -17,11 +18,11 @@ import (
|
|||
func init() {
|
||||
log.SetLevel("trace")
|
||||
|
||||
go tcp.Run("debug", "localhost", "8281", "pass123", "8282,8283,8284,8285")
|
||||
go tcp.Run("debug", "localhost", "8282", "pass123")
|
||||
go tcp.Run("debug", "localhost", "8283", "pass123")
|
||||
go tcp.Run("debug", "localhost", "8284", "pass123")
|
||||
go tcp.Run("debug", "localhost", "8285", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8281", "pass123", "8282,8283,8284,8285")
|
||||
go tcp.Run("debug", "127.0.0.1", "8282", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8283", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8284", "pass123")
|
||||
go tcp.Run("debug", "127.0.0.1", "8285", "pass123")
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
|
@ -33,7 +34,7 @@ func TestCrocReadme(t *testing.T) {
|
|||
IsSender: true,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
|
@ -41,6 +42,7 @@ func TestCrocReadme(t *testing.T) {
|
|||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -51,7 +53,7 @@ func TestCrocReadme(t *testing.T) {
|
|||
IsSender: false,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
|
@ -66,7 +68,7 @@ func TestCrocReadme(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../README.md"})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../README.md"}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
|
@ -92,14 +94,14 @@ func TestCrocEmptyFolder(t *testing.T) {
|
|||
pathName := "../../testEmpty"
|
||||
defer os.RemoveAll(pathName)
|
||||
defer os.RemoveAll("./testEmpty")
|
||||
os.MkdirAll(pathName, 0755)
|
||||
os.MkdirAll(pathName, 0o755)
|
||||
|
||||
log.Debug("setting up sender")
|
||||
sender, err := New(Options{
|
||||
IsSender: true,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
|
@ -117,7 +119,7 @@ func TestCrocEmptyFolder(t *testing.T) {
|
|||
IsSender: false,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
|
@ -132,7 +134,7 @@ func TestCrocEmptyFolder(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
|
@ -158,7 +160,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
pathName := "../link-in-folder"
|
||||
defer os.RemoveAll(pathName)
|
||||
defer os.RemoveAll("./link-in-folder")
|
||||
os.MkdirAll(pathName, 0755)
|
||||
os.MkdirAll(pathName, 0o755)
|
||||
os.Symlink("../../README.md", filepath.Join(pathName, "README.link"))
|
||||
|
||||
log.Debug("setting up sender")
|
||||
|
@ -166,7 +168,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
IsSender: true,
|
||||
SharedSecret: "8124-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPorts: []string{"8281"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
|
@ -174,6 +176,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
DisableLocal: true,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -184,7 +187,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
IsSender: false,
|
||||
SharedSecret: "8124-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8281",
|
||||
RelayAddress: "127.0.0.1:8281",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: false,
|
||||
NoPrompt: true,
|
||||
|
@ -199,11 +202,11 @@ func TestCrocSymlink(t *testing.T) {
|
|||
var wg sync.WaitGroup
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
err = sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
||||
if err != nil {
|
||||
t.Errorf("send failed: %v", err)
|
||||
}
|
||||
|
@ -211,7 +214,7 @@ func TestCrocSymlink(t *testing.T) {
|
|||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
err := receiver.Receive()
|
||||
err = receiver.Receive()
|
||||
if err != nil {
|
||||
t.Errorf("receive failed: %v", err)
|
||||
}
|
||||
|
@ -229,6 +232,32 @@ func TestCrocSymlink(t *testing.T) {
|
|||
t.Errorf("symlink transfer failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
func testCrocIgnoreGit(t *testing.T) {
|
||||
log.SetLevel("trace")
|
||||
defer os.Remove(".gitignore")
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
file, err := os.Create(".gitignore")
|
||||
if err != nil {
|
||||
log.Errorf("error creating file")
|
||||
}
|
||||
_, err = file.WriteString("LICENSE")
|
||||
if err != nil {
|
||||
log.Errorf("error writing to file")
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
// due to how files are ignored in this function, all we have to do to test is make sure LICENSE doesn't get included in FilesInfo.
|
||||
filesInfo, _, _, errGet := GetFilesInfo([]string{"../../LICENSE", ".gitignore", "croc.go"}, false, true)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
for _, file := range filesInfo {
|
||||
if strings.Contains(file.Name, "LICENSE") {
|
||||
t.Errorf("test failed, should ignore LICENSE")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrocLocal(t *testing.T) {
|
||||
log.SetLevel("trace")
|
||||
|
@ -241,7 +270,7 @@ func TestCrocLocal(t *testing.T) {
|
|||
IsSender: true,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8181",
|
||||
RelayAddress: "127.0.0.1:8181",
|
||||
RelayPorts: []string{"8181", "8182"},
|
||||
RelayPassword: "pass123",
|
||||
Stdout: true,
|
||||
|
@ -249,6 +278,7 @@ func TestCrocLocal(t *testing.T) {
|
|||
DisableLocal: false,
|
||||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
GitIgnore: false,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -260,7 +290,7 @@ func TestCrocLocal(t *testing.T) {
|
|||
IsSender: false,
|
||||
SharedSecret: "8123-testingthecroc",
|
||||
Debug: true,
|
||||
RelayAddress: "localhost:8181",
|
||||
RelayAddress: "127.0.0.1:8181",
|
||||
RelayPassword: "pass123",
|
||||
Stdout: true,
|
||||
NoPrompt: true,
|
||||
|
@ -276,7 +306,7 @@ func TestCrocLocal(t *testing.T) {
|
|||
os.Create("touched")
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../LICENSE", "touched"})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../LICENSE", "touched"}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
|
@ -307,10 +337,10 @@ func TestCrocError(t *testing.T) {
|
|||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
if _, err = tmpfile.Write(content); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
|
@ -329,7 +359,7 @@ func TestCrocError(t *testing.T) {
|
|||
Curve: "siec",
|
||||
Overwrite: true,
|
||||
})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{tmpfile.Name()})
|
||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{tmpfile.Name()}, false, false)
|
||||
if errGet != nil {
|
||||
t.Errorf("failed to get minimal info: %v", errGet)
|
||||
}
|
||||
|
@ -353,7 +383,7 @@ func TestCleanUp(t *testing.T) {
|
|||
for _, file := range []string{"README.md", "./README.md"} {
|
||||
err = os.Remove(file)
|
||||
if err == nil {
|
||||
log.Debugf("Successfuly purged %s", file)
|
||||
log.Debugf("Successfully purged %s", file)
|
||||
} else {
|
||||
log.Debugf("%s was already purged.", file)
|
||||
}
|
||||
|
@ -361,7 +391,7 @@ func TestCleanUp(t *testing.T) {
|
|||
for _, folder := range []string{"./testEmpty", "./link-in-folder"} {
|
||||
err = os.RemoveAll(folder)
|
||||
if err == nil {
|
||||
log.Debugf("Successfuly purged %s", folder)
|
||||
log.Debugf("Successfully purged %s", folder)
|
||||
} else {
|
||||
log.Debugf("%s was already purged.", folder)
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ func Encrypt(plaintext []byte, key []byte) (encrypted []byte, err error) {
|
|||
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||
// Section 8.2
|
||||
ivBytes := make([]byte, 12)
|
||||
if _, err := rand.Read(ivBytes); err != nil {
|
||||
if _, err = rand.Read(ivBytes); err != nil {
|
||||
log.Fatalf("can't initialize crypto: %v", err)
|
||||
}
|
||||
b, err := aes.NewCipher(key)
|
||||
|
@ -85,7 +85,7 @@ func NewArgon2(passphrase []byte, usersalt []byte) (aead cipher.AEAD, salt []byt
|
|||
salt = make([]byte, 8)
|
||||
// http://www.ietf.org/rfc/rfc2898.txt
|
||||
// Salt.
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
if _, err = rand.Read(salt); err != nil {
|
||||
log.Fatalf("can't get random salt: %v", err)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -52,19 +52,19 @@ func TestEncryption(t *testing.T) {
|
|||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err := New([]byte("password"), salt)
|
||||
key2, _, _ := New([]byte("password"), salt)
|
||||
dec, err = Decrypt(enc, key2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err = New([]byte("wrong password"), salt)
|
||||
key2, _, _ = New([]byte("wrong password"), salt)
|
||||
dec, err = Decrypt(enc, key2)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEqual(t, msg, dec)
|
||||
|
||||
// error with no password
|
||||
dec, err = Decrypt([]byte(""), key)
|
||||
_, err = Decrypt([]byte(""), key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// error with small password
|
||||
|
@ -84,19 +84,19 @@ func TestEncryptionChaCha(t *testing.T) {
|
|||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err := NewArgon2([]byte("password"), salt)
|
||||
key2, _, _ := NewArgon2([]byte("password"), salt)
|
||||
dec, err = DecryptChaCha(enc, key2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, msg, dec)
|
||||
|
||||
// check reusing the salt
|
||||
key2, _, err = NewArgon2([]byte("wrong password"), salt)
|
||||
key2, _, _ = NewArgon2([]byte("wrong password"), salt)
|
||||
dec, err = DecryptChaCha(enc, key2)
|
||||
assert.NotNil(t, err)
|
||||
assert.NotEqual(t, msg, dec)
|
||||
|
||||
// error with no password
|
||||
dec, err = DecryptChaCha([]byte(""), key)
|
||||
_, err = DecryptChaCha([]byte(""), key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// error with small password
|
||||
|
|
|
@ -13,5 +13,7 @@ release:
|
|||
cd ../../ && ./src/install/upload-src-tarball.sh
|
||||
|
||||
test:
|
||||
cp zsh_autocomplete ../../
|
||||
cp bash_autocomplete ../../
|
||||
cd ../../ && go generate
|
||||
cd ../../ && goreleaser release --skip-publish
|
||||
|
|
|
@ -86,7 +86,7 @@ print_help() {
|
|||
Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android)
|
||||
|
||||
-h
|
||||
Prints this helpfull message and exit."
|
||||
Prints this helpful message and exit."
|
||||
|
||||
echo "${help_header}"
|
||||
echo ""
|
||||
|
@ -156,7 +156,7 @@ make_tempdir() {
|
|||
|
||||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: determine_os
|
||||
# DESCRIPTION: Attempts to determin host os using uname
|
||||
# DESCRIPTION: Attempts to determine host os using uname
|
||||
# PARAMETERS: none
|
||||
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
|
||||
# 1 = Unknown OS
|
||||
|
@ -180,7 +180,7 @@ determine_os() {
|
|||
|
||||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: determine_arch
|
||||
# DESCRIPTION: Attempt to determin architecture of host
|
||||
# DESCRIPTION: Attempt to determine architecture of host
|
||||
# PARAMETERS: none
|
||||
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
|
||||
# 1 = Unknown arch
|
||||
|
@ -300,10 +300,10 @@ checksum_check() {
|
|||
#--- FUNCTION ----------------------------------------------------------------
|
||||
# NAME: extract_file
|
||||
# DESCRIPTION: Extracts a file into a location. Attempts to determine which
|
||||
# tool to use by checking file extention.
|
||||
# tool to use by checking file extension.
|
||||
# PARAMETERS: $1 = file to extract
|
||||
# $2 = location to extract file into
|
||||
# $3 = extention
|
||||
# $3 = extension
|
||||
# RETURNS: Return code of the tool used to extract the file
|
||||
# 20 = Failed to determine which tool to use
|
||||
# 30 = Failed to find tool in path
|
||||
|
@ -528,7 +528,7 @@ main() {
|
|||
local autocomplete_install_rcode
|
||||
|
||||
croc_bin_name="croc"
|
||||
croc_version="9.5.5"
|
||||
croc_version="10.0.7"
|
||||
croc_dl_ext="tar.gz"
|
||||
croc_base_url="https://github.com/schollz/croc/releases/download"
|
||||
prefix="${1}"
|
||||
|
@ -587,16 +587,17 @@ main() {
|
|||
"x86_64" ) croc_arch="64bit";;
|
||||
"amd64" ) croc_arch="64bit";;
|
||||
"aarch64" ) croc_arch="ARM64";;
|
||||
"arm64" ) croc_arch="ARM64";;
|
||||
"armv7l" ) croc_arch="ARM";;
|
||||
"i686" ) croc_arch="32bit";;
|
||||
* ) croc_arch="unknown";;
|
||||
esac
|
||||
|
||||
croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
||||
croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt"
|
||||
croc_file="${croc_bin_name}_v${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
||||
croc_checksum_file="${croc_bin_name}_v${croc_version}_checksums.txt"
|
||||
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
|
||||
croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}"
|
||||
|
||||
echo "${croc_url}" "${tmpdir}" "${croc_file}"
|
||||
download_file "${croc_url}" "${tmpdir}" "${croc_file}"
|
||||
download_file_rcode="${?}"
|
||||
if [[ "${download_file_rcode}" == "0" ]]; then
|
||||
|
@ -712,27 +713,27 @@ main() {
|
|||
exit 1
|
||||
fi
|
||||
|
||||
case "$(basename ${SHELL})" in
|
||||
"bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
||||
autocomplete_install_rcode="${?}";;
|
||||
"zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
||||
autocomplete_install_rcode="${?}";
|
||||
print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
|
||||
print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
|
||||
*) autocomplete_install_rcode="1";;
|
||||
esac
|
||||
# case "$(basename ${SHELL})" in
|
||||
# "bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
||||
# autocomplete_install_rcode="${?}";;
|
||||
# "zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
||||
# autocomplete_install_rcode="${?}";
|
||||
# print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
|
||||
# print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
|
||||
# *) autocomplete_install_rcode="1";;
|
||||
# esac
|
||||
|
||||
if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
||||
print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
||||
elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
||||
print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
||||
elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
||||
print_message "== Failed to locate 'install' command" "error"
|
||||
elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
||||
print_message "== Failed to locate 'sudo' command" "error"
|
||||
else
|
||||
print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
||||
fi
|
||||
# if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
||||
# print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
||||
# print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
||||
# print_message "== Failed to locate 'install' command" "error"
|
||||
# elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
||||
# print_message "== Failed to locate 'sudo' command" "error"
|
||||
# else
|
||||
# print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
||||
# fi
|
||||
|
||||
print_message "== Installation complete" "ok"
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ func replaceInFile(fname, start, end, replacement string) (err error) {
|
|||
fmt.Sprintf("%s%s%s", start, replacement, end),
|
||||
1,
|
||||
)
|
||||
err = os.WriteFile(fname, []byte(newF), 0644)
|
||||
err = os.WriteFile(fname, []byte(newF), 0o644)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ func TestMessage(t *testing.T) {
|
|||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||
assert.Nil(t, err)
|
||||
fmt.Println(salt)
|
||||
fmt.Println(string(salt))
|
||||
b, err := Encode(e, m)
|
||||
assert.Nil(t, err)
|
||||
fmt.Printf("%x\n", b)
|
||||
|
@ -67,7 +67,7 @@ func TestSend(t *testing.T) {
|
|||
log.Error(err)
|
||||
}
|
||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||
go func(port string, connection net.Conn) {
|
||||
go func(_ string, connection net.Conn) {
|
||||
c := comm.New(connection)
|
||||
err = c.Send([]byte("hello, world"))
|
||||
assert.Nil(t, err)
|
||||
|
@ -84,8 +84,8 @@ func TestSend(t *testing.T) {
|
|||
}
|
||||
}()
|
||||
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
a, err := comm.NewConnection("localhost:"+port, 10*time.Minute)
|
||||
time.Sleep(800 * time.Millisecond)
|
||||
a, err := comm.NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
||||
assert.Nil(t, err)
|
||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/schollz/croc/v9/src/utils"
|
||||
"github.com/schollz/croc/v10/src/utils"
|
||||
)
|
||||
|
||||
// TCP_BUFFER_SIZE is the maximum packet size
|
||||
|
@ -22,8 +22,8 @@ var (
|
|||
INTERNAL_DNS = false
|
||||
)
|
||||
|
||||
// publicDns are servers to be queried if a local lookup fails
|
||||
var publicDns = []string{
|
||||
// publicDNS are servers to be queried if a local lookup fails
|
||||
var publicDNS = []string{
|
||||
"1.0.0.1", // Cloudflare
|
||||
"1.1.1.1", // Cloudflare
|
||||
"[2606:4700:4700::1111]", // Cloudflare
|
||||
|
@ -44,8 +44,8 @@ var publicDns = []string{
|
|||
"[2620:119:53::53]", // Cisco OpenDNS
|
||||
}
|
||||
|
||||
func getConfigFile() (fname string, err error) {
|
||||
configFile, err := utils.GetConfigDir()
|
||||
func getConfigFile(requireValidPath bool) (fname string, err error) {
|
||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -66,28 +66,29 @@ func init() {
|
|||
}
|
||||
if doRemember {
|
||||
// save in config file
|
||||
fname, err := getConfigFile()
|
||||
fname, err := getConfigFile(true)
|
||||
if err == nil {
|
||||
f, _ := os.Create(fname)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
if !INTERNAL_DNS {
|
||||
fname, err := getConfigFile()
|
||||
fname, err := getConfigFile(false)
|
||||
if err == nil {
|
||||
INTERNAL_DNS = utils.Exists(fname)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
DEFAULT_RELAY, err = lookup(DEFAULT_RELAY)
|
||||
var addr string
|
||||
addr, err = lookup(DEFAULT_RELAY)
|
||||
if err == nil {
|
||||
DEFAULT_RELAY += ":" + DEFAULT_PORT
|
||||
DEFAULT_RELAY = net.JoinHostPort(addr, DEFAULT_PORT)
|
||||
} else {
|
||||
DEFAULT_RELAY = ""
|
||||
}
|
||||
DEFAULT_RELAY6, err = lookup(DEFAULT_RELAY6)
|
||||
addr, err = lookup(DEFAULT_RELAY6)
|
||||
if err == nil {
|
||||
DEFAULT_RELAY6 = "[" + DEFAULT_RELAY6 + "]:" + DEFAULT_PORT
|
||||
DEFAULT_RELAY6 = net.JoinHostPort(addr, DEFAULT_PORT)
|
||||
} else {
|
||||
DEFAULT_RELAY6 = ""
|
||||
}
|
||||
|
@ -102,15 +103,15 @@ func lookup(address string) (ipaddress string, err error) {
|
|||
s string
|
||||
err error
|
||||
}
|
||||
result := make(chan Result, len(publicDns))
|
||||
for _, dns := range publicDns {
|
||||
result := make(chan Result, len(publicDNS))
|
||||
for _, dns := range publicDNS {
|
||||
go func(dns string) {
|
||||
var r Result
|
||||
r.s, r.err = remoteLookupIP(address, dns)
|
||||
result <- r
|
||||
}(dns)
|
||||
}
|
||||
for i := 0; i < len(publicDns); i++ {
|
||||
for i := 0; i < len(publicDNS); i++ {
|
||||
ipaddress = (<-result).s
|
||||
if ipaddress != "" {
|
||||
return
|
||||
|
@ -134,7 +135,7 @@ func localLookupIP(address string) (ipaddress string, err error) {
|
|||
func remoteLookupIP(address, dns string) (ipaddress string, err error) {
|
||||
r := &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
||||
d := new(net.Dialer)
|
||||
return d.DialContext(ctx, network, dns+":53")
|
||||
},
|
||||
|
|
|
@ -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 {
|
||||
|
@ -93,7 +93,8 @@ func (s *server) run() (err error) {
|
|||
if s.host != "" {
|
||||
ip := net.ParseIP(s.host)
|
||||
if ip == nil {
|
||||
tcpIP, err := net.ResolveIPAddr("ip", s.host)
|
||||
var tcpIP *net.IPAddr
|
||||
tcpIP, err = net.ResolveIPAddr("ip", s.host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -227,7 +228,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
|||
if strings.TrimSpace(string(passwordBytes)) != s.password {
|
||||
err = fmt.Errorf("bad password")
|
||||
enc, _ := crypt.Encrypt([]byte(err.Error()), strongKeyForEncryption)
|
||||
if err := c.Send(enc); err != nil {
|
||||
if err = c.Send(enc); err != nil {
|
||||
return "", fmt.Errorf("send error: %w", err)
|
||||
}
|
||||
return
|
||||
|
@ -350,11 +351,11 @@ func (s *server) deleteRoom(room string) {
|
|||
}
|
||||
s.rooms.rooms[room] = roomInfo{first: nil, second: nil}
|
||||
delete(s.rooms.rooms, room)
|
||||
|
||||
}
|
||||
|
||||
// chanFromConn creates a channel from a Conn object, and sends everything it
|
||||
// Read()s from the socket to the channel.
|
||||
//
|
||||
// Read()s from the socket to the channel.
|
||||
func chanFromConn(conn net.Conn) chan []byte {
|
||||
c := make(chan []byte, 1)
|
||||
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||
|
@ -515,7 +516,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)
|
||||
|
|
|
@ -12,11 +12,11 @@ import (
|
|||
|
||||
func BenchmarkConnection(b *testing.B) {
|
||||
log.SetLevel("trace")
|
||||
go Run("debug", "localhost", "8283", "pass123", "8284")
|
||||
go Run("debug", "127.0.0.1", "8283", "pass123", "8284")
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
c, _, _, _ := ConnectToTCPServer("localhost:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
||||
c, _, _, _ := ConnectToTCPServer("127.0.0.1:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
||||
c.Close()
|
||||
}
|
||||
}
|
||||
|
@ -24,22 +24,22 @@ func BenchmarkConnection(b *testing.B) {
|
|||
func TestTCP(t *testing.T) {
|
||||
log.SetLevel("error")
|
||||
timeToRoomDeletion = 100 * time.Millisecond
|
||||
go Run("debug", "localhost", "8381", "pass123", "8382")
|
||||
go Run("debug", "127.0.0.1", "8381", "pass123", "8382")
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
err := PingServer("localhost:8381")
|
||||
err := PingServer("127.0.0.1:8381")
|
||||
assert.Nil(t, err)
|
||||
err = PingServer("localhost:8333")
|
||||
err = PingServer("127.0.0.1:8333")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
time.Sleep(timeToRoomDeletion)
|
||||
c1, banner, _, err := ConnectToTCPServer("localhost:8381", "pass123", "testRoom", 1*time.Minute)
|
||||
c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Minute)
|
||||
assert.Equal(t, banner, "8382")
|
||||
assert.Nil(t, err)
|
||||
c2, _, _, err := ConnectToTCPServer("localhost:8381", "pass123", "testRoom")
|
||||
c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
||||
assert.Nil(t, err)
|
||||
_, _, _, err = ConnectToTCPServer("localhost:8381", "pass123", "testRoom")
|
||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
||||
assert.NotNil(t, err)
|
||||
_, _, _, err = ConnectToTCPServer("localhost:8381", "pass123", "testRoom", 1*time.Nanosecond)
|
||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Nanosecond)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
// try sending data
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
|
@ -16,31 +18,43 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
"github.com/kalafut/imohash"
|
||||
"github.com/minio/highwayhash"
|
||||
"github.com/schollz/mnemonicode"
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
// Get or create home directory
|
||||
func GetConfigDir() (homedir string, err error) {
|
||||
homedir, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
const NbPinNumbers = 4
|
||||
const NbBytesWords = 4
|
||||
|
||||
// Get or create home directory
|
||||
func GetConfigDir(requireValidPath bool) (homedir string, err error) {
|
||||
if envHomedir, isSet := os.LookupEnv("CROC_CONFIG_DIR"); isSet {
|
||||
homedir = envHomedir
|
||||
} else if xdgConfigHome, isSet := os.LookupEnv("XDG_CONFIG_HOME"); isSet {
|
||||
homedir = path.Join(xdgConfigHome, "croc")
|
||||
} else {
|
||||
homedir, err = os.UserHomeDir()
|
||||
if err != nil {
|
||||
if !requireValidPath {
|
||||
err = nil
|
||||
homedir = ""
|
||||
}
|
||||
return
|
||||
}
|
||||
homedir = path.Join(homedir, ".config", "croc")
|
||||
}
|
||||
|
||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(homedir, 0700)
|
||||
if requireValidPath {
|
||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||
err = os.MkdirAll(homedir, 0o700)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -65,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 {
|
||||
|
@ -83,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
|
||||
|
@ -100,8 +161,25 @@ func MD5HashFile(fname string) (hash256 []byte, err error) {
|
|||
defer f.Close()
|
||||
|
||||
h := md5.New()
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
if doShowProgress {
|
||||
stat, _ := f.Stat()
|
||||
fnameShort := path.Base(fname)
|
||||
if len(fnameShort) > 20 {
|
||||
fnameShort = fnameShort[:20] + "..."
|
||||
}
|
||||
bar := progressbar.NewOptions64(stat.Size(),
|
||||
progressbar.OptionSetWriter(os.Stderr),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
)
|
||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hash256 = h.Sum(nil)
|
||||
|
@ -125,7 +203,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
|
||||
|
@ -133,8 +211,25 @@ func XXHashFile(fname string) (hash256 []byte, err error) {
|
|||
defer f.Close()
|
||||
|
||||
h := xxhash.New()
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
if doShowProgress {
|
||||
stat, _ := f.Stat()
|
||||
fnameShort := path.Base(fname)
|
||||
if len(fnameShort) > 20 {
|
||||
fnameShort = fnameShort[:20] + "..."
|
||||
}
|
||||
bar := progressbar.NewOptions64(stat.Size(),
|
||||
progressbar.OptionSetWriter(os.Stderr),
|
||||
progressbar.OptionShowBytes(true),
|
||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
||||
progressbar.OptionClearOnFinish(),
|
||||
)
|
||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if _, err = io.Copy(h, f); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
hash256 = h.Sum(nil)
|
||||
|
@ -183,7 +278,7 @@ func GenerateRandomPin() string {
|
|||
s := ""
|
||||
max := new(big.Int)
|
||||
max.SetInt64(9)
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := 0; i < NbPinNumbers; i++ {
|
||||
v, err := rand.Int(rand.Reader, max)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -196,7 +291,7 @@ func GenerateRandomPin() string {
|
|||
// GetRandomName returns mnemonicoded random name
|
||||
func GetRandomName() string {
|
||||
var result []string
|
||||
bs := make([]byte, 4)
|
||||
bs := make([]byte, NbBytesWords)
|
||||
rand.Read(bs)
|
||||
result = mnemonicode.EncodeWordList(result, bs)
|
||||
return GenerateRandomPin() + "-" + strings.Join(result, "-")
|
||||
|
@ -265,7 +360,6 @@ func MissingChunks(fname string, fsize int64, chunkSize int) (chunkRanges []int6
|
|||
}
|
||||
}
|
||||
chunkRanges = append(chunkRanges, int64(curCount+1))
|
||||
chunks = chunkRanges
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -354,7 +448,7 @@ func init() {
|
|||
}
|
||||
|
||||
func IsLocalIP(ipaddress string) bool {
|
||||
if strings.Contains(ipaddress, "localhost") {
|
||||
if strings.Contains(ipaddress, "127.0.0.1") {
|
||||
return true
|
||||
}
|
||||
host, _, _ := net.SplitHostPort(ipaddress)
|
||||
|
@ -369,3 +463,127 @@ func IsLocalIP(ipaddress string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func ZipDirectory(destination string, source string) (err error) {
|
||||
if _, err = os.Stat(destination); err == nil {
|
||||
log.Fatalf("%s file already exists!\n", destination)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Zipping %s to %s\n", source, destination)
|
||||
file, err := os.Create(destination)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer file.Close()
|
||||
writer := zip.NewWriter(file)
|
||||
// no compression because croc does its compression on the fly
|
||||
writer.RegisterCompressor(zip.Deflate, func(out io.Writer) (io.WriteCloser, error) {
|
||||
return flate.NewWriter(out, flate.NoCompression)
|
||||
})
|
||||
defer writer.Close()
|
||||
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if info.Mode().IsRegular() {
|
||||
f1, err := os.Open(path)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer f1.Close()
|
||||
zipPath := strings.ReplaceAll(path, source, strings.TrimSuffix(destination, ".zip"))
|
||||
zipPath = filepath.ToSlash(zipPath)
|
||||
w1, err := writer.Create(zipPath)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if _, err := io.Copy(w1, f1); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
||||
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnzipDirectory(destination string, source string) error {
|
||||
archive, err := zip.OpenReader(source)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
defer archive.Close()
|
||||
|
||||
for _, f := range archive.File {
|
||||
filePath := filepath.Join(destination, f.Name)
|
||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
||||
fmt.Fprintf(os.Stderr, "\rUnzipping file %s", filePath)
|
||||
// Issue #593 conceal path traversal vulnerability
|
||||
// make sure the filepath does not have ".."
|
||||
filePath = filepath.Clean(filePath)
|
||||
if strings.Contains(filePath, "..") {
|
||||
log.Fatalf("Invalid file path %s\n", filePath)
|
||||
}
|
||||
if f.FileInfo().IsDir() {
|
||||
os.MkdirAll(filePath, os.ModePerm)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
// check if file exists
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) ", filePath)
|
||||
choice := strings.ToLower(GetInput(prompt))
|
||||
if choice != "y" && choice != "yes" {
|
||||
fmt.Fprintf(os.Stderr, "Skipping '%s'\n", filePath)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
fileInArchive, err := f.Open()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
dstFile.Close()
|
||||
fileInArchive.Close()
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidFileName checks if a filename is valid
|
||||
// by making sure it has no invisible characters
|
||||
func ValidFileName(fname string) bool {
|
||||
clean1 := strings.Map(func(r rune) rune {
|
||||
if unicode.IsGraphic(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, fname)
|
||||
|
||||
clean2 := strings.Map(func(r rune) rune {
|
||||
if unicode.IsPrint(r) {
|
||||
return r
|
||||
}
|
||||
return -1
|
||||
}, fname)
|
||||
|
||||
return (fname == clean1) && (fname == clean2)
|
||||
}
|
||||
|
|
|
@ -17,14 +17,14 @@ const TCP_BUFFER_SIZE = 1024 * 64
|
|||
var bigFileSize = 75000000
|
||||
|
||||
func bigFile() {
|
||||
os.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0666)
|
||||
os.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0o666)
|
||||
}
|
||||
|
||||
func BenchmarkMD5(b *testing.B) {
|
||||
bigFile()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
MD5HashFile("bigfile.test")
|
||||
MD5HashFile("bigfile.test", false)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,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 +44,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 +86,20 @@ func TestExists(t *testing.T) {
|
|||
func TestMD5HashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := MD5HashFile("bigfile.test")
|
||||
b, err := MD5HashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "8304ff018e02baad0e3555bade29a405", fmt.Sprintf("%x", b))
|
||||
_, err = MD5HashFile("bigfile.test.nofile")
|
||||
_, err = MD5HashFile("bigfile.test.nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestHighwayHashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := HighwayHashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "3c32999529323ed66a67aeac5720c7bf1301dcc5dca87d8d46595e85ff990329", fmt.Sprintf("%x", b))
|
||||
_, err = HighwayHashFile("bigfile.test.nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
@ -96,10 +114,10 @@ func TestIMOHashFile(t *testing.T) {
|
|||
func TestXXHashFile(t *testing.T) {
|
||||
bigFile()
|
||||
defer os.Remove("bigfile.test")
|
||||
b, err := XXHashFile("bigfile.test")
|
||||
b, err := XXHashFile("bigfile.test", false)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "4918740eb5ccb6f7", fmt.Sprintf("%x", b))
|
||||
_, err = XXHashFile("nofile")
|
||||
_, err = XXHashFile("nofile", false)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
@ -119,9 +137,9 @@ func TestMissingChunks(t *testing.T) {
|
|||
rand.Seed(1)
|
||||
bigBuff := make([]byte, fileSize)
|
||||
rand.Read(bigBuff)
|
||||
os.WriteFile("missing.test", bigBuff, 0644)
|
||||
os.WriteFile("missing.test", bigBuff, 0o644)
|
||||
empty := make([]byte, chunkSize)
|
||||
f, err := os.OpenFile("missing.test", os.O_RDWR, 0644)
|
||||
f, err := os.OpenFile("missing.test", os.O_RDWR, 0o644)
|
||||
assert.Nil(t, err)
|
||||
for block := 0; block < fileSize/chunkSize; block++ {
|
||||
if block == 0 || block == 4 || block == 5 || block >= 7 {
|
||||
|
@ -178,10 +196,10 @@ func TestHashFile(t *testing.T) {
|
|||
|
||||
defer os.Remove(tmpfile.Name()) // clean up
|
||||
|
||||
if _, err := tmpfile.Write(content); err != nil {
|
||||
if _, err = tmpfile.Write(content); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
if err = tmpfile.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
hashed, err := HashFile(tmpfile.Name(), "xxhash")
|
||||
|
@ -208,11 +226,37 @@ 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("localhost", 9009, 4)
|
||||
assert.Equal(t, []int{9009, 9010, 9011, 9012}, openPorts)
|
||||
openPorts := FindOpenPorts("127.0.0.1", 9009, 4)
|
||||
if !intSliceSame(openPorts, []int{9009, 9010, 9011, 9012}) && !intSliceSame(openPorts, []int{9014, 9015, 9016, 9017}) {
|
||||
t.Errorf("openPorts: %v", openPorts)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsLocalIP(t *testing.T) {
|
||||
assert.True(t, IsLocalIP("192.168.0.14:9009"))
|
||||
}
|
||||
|
||||
func TestValidFileName(t *testing.T) {
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("中文.csl"))
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("[something].csl"))
|
||||
// contains regular characters
|
||||
assert.True(t, ValidFileName("[(something)].csl"))
|
||||
// contains invisible character
|
||||
assert.False(t, ValidFileName("D中文.cslouglas"))
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue