Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f7a72467e |
|
|
@ -1,9 +1,29 @@
|
||||||
---
|
---
|
||||||
name: New issue
|
name: Bug report
|
||||||
about: Create an issue
|
about: Create a report to help us improve
|
||||||
title: ''
|
title: ''
|
||||||
|
labels: ''
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Read this and delete before submitting:* Thanks for starting a discussion! Please provide as much context as possible so that others can understand your thoughts. If you have a specific change in mind, consider submitting a pull request instead.
|
**Please try to [download the latest version](https://github.com/schollz/croc/releases/latest) of croc before reporting a bug!**
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**To Reproduce**
|
||||||
|
Steps to reproduce the behavior:
|
||||||
|
1. Go to '...'
|
||||||
|
2. Click on '....'
|
||||||
|
3. Scroll down to '....'
|
||||||
|
4. See error
|
||||||
|
|
||||||
|
**Expected behavior**
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
|
||||||
|
**Version**
|
||||||
|
Check `croc -version` and report it.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context about the problem here.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Is your feature request related to a problem? Please describe.**
|
||||||
|
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||||
|
|
||||||
|
**Describe the solution you'd like**
|
||||||
|
A clear and concise description of what you want to happen.
|
||||||
|
|
||||||
|
**Describe alternatives you've considered**
|
||||||
|
A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
- package-ecosystem: "gomod"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
||||||
name: CI
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
pull_request:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit-tests:
|
|
||||||
name: Go unit tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.23'
|
|
||||||
- run: go version
|
|
||||||
- run: go test -v ./...
|
|
||||||
- name: Build files
|
|
||||||
run: |
|
|
||||||
go version
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
GOARM=5 CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=dragonfly GOARCH=amd64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=386 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=amd64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=netbsd GOARCH=arm64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=amd64 go build -ldflags '' -o croc
|
|
||||||
CGO_ENABLED=0 GOOS=openbsd GOARCH=arm64 go build -ldflags '' -o croc
|
|
||||||
- name: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.20.7' # go1.20.8+ refuses to build go1.22 code...
|
|
||||||
- name: Build Windows 7
|
|
||||||
run: |
|
|
||||||
go version
|
|
||||||
rm go.mod go.sum
|
|
||||||
go mod init github.com/schollz/croc/v10
|
|
||||||
go mod tidy
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
# This is a basic workflow to help you get started with Actions
|
|
||||||
|
|
||||||
name: CD
|
|
||||||
|
|
||||||
# Controls when the action will run.
|
|
||||||
on:
|
|
||||||
# Triggers the workflow on push or pull request events but only for the main branch
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- '*'
|
|
||||||
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
|
||||||
jobs:
|
|
||||||
docker:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
-
|
|
||||||
name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
-
|
|
||||||
name: Docker meta
|
|
||||||
id: docker_meta
|
|
||||||
uses: docker/metadata-action@v5
|
|
||||||
with:
|
|
||||||
images: schollz/croc
|
|
||||||
# generate Docker tags based on the following events/attributes
|
|
||||||
tags: |
|
|
||||||
type=ref,event=branch
|
|
||||||
type=ref,event=pr
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=semver,pattern={{major}}
|
|
||||||
type=sha
|
|
||||||
-
|
|
||||||
name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3
|
|
||||||
-
|
|
||||||
name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
-
|
|
||||||
name: Login to DockerHub
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
-
|
|
||||||
name: Build and push
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
file: ./Dockerfile
|
|
||||||
platforms: linux/amd64,linux/arm,linux/arm64,linux/386
|
|
||||||
push: ${{ github.event_name != 'pull_request' }}
|
|
||||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
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.23'
|
|
||||||
- name: Prepare source tarball
|
|
||||||
run: |
|
|
||||||
git clone -b ${{ github.event.release.name }} --depth 1 https://github.com/schollz/croc croc-${{ github.event.release.name }}
|
|
||||||
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
|
|
||||||
GOARM=5 CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARMv5.tar.gz croc LICENSE
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags '-extldflags "-static"' -o croc
|
|
||||||
tar -czvf croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz croc LICENSE
|
|
||||||
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags '-s -extldflags "-sectcreate __TEXT __info_plist Info.plist"' -o croc
|
|
||||||
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: Setup Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: '1.20.7' # go1.20.8+ refuses to build go1.22 code...
|
|
||||||
- name: Build Windows 7
|
|
||||||
run: |
|
|
||||||
go version
|
|
||||||
rm go.mod go.sum
|
|
||||||
go mod init github.com/schollz/croc/v10
|
|
||||||
go mod tidy
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
zip croc_${{ github.event.release.name }}_Windows7-64bit.zip croc.exe
|
|
||||||
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags '-extldflags "-static"' -o croc.exe
|
|
||||||
zip croc_${{ github.event.release.name }}_Windows7-32bit.zip croc.exe
|
|
||||||
- name: Create checksums.txt
|
|
||||||
run: |
|
|
||||||
touch croc_${{ github.event.release.name }}_checksums.txt
|
|
||||||
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-ARMv5.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
|
||||||
sha256sum croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
|
||||||
sha256sum croc_${{ github.event.release.name }}_macOS-64bit.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
|
||||||
sha256sum croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz >> croc_${{ github.event.release.name }}_checksums.txt
|
|
||||||
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-ARMv5.tar.gz
|
|
||||||
croc_${{ github.event.release.name }}_Linux-ARM64.tar.gz
|
|
||||||
croc_${{ github.event.release.name }}_macOS-64bit.tar.gz
|
|
||||||
croc_${{ github.event.release.name }}_macOS-ARM64.tar.gz
|
|
||||||
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
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# 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'
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
name: Publish to Winget
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [released]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: vedantmgoyal2009/winget-releaser@v2
|
|
||||||
with:
|
|
||||||
identifier: schollz.croc
|
|
||||||
installers-regex: '_Windows-\w+\.zip$'
|
|
||||||
token: ${{ secrets.WINGET_TOKEN }}
|
|
||||||
|
|
@ -6,8 +6,3 @@ bash_autocomplete
|
||||||
dist
|
dist
|
||||||
bin
|
bin
|
||||||
croc-stdin*
|
croc-stdin*
|
||||||
|
|
||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
src/utils/bigfile.test
|
|
||||||
test1/
|
|
||||||
|
|
|
||||||
12
.travis.yml
12
.travis.yml
|
|
@ -10,12 +10,12 @@ install: true
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- env GO111MODULE=on go build -v
|
- env GO111MODULE=on go build -v
|
||||||
- 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/v8/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/v8/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/v8/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/v8/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/v8/src/utils
|
||||||
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/comm
|
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v8/src/comm
|
||||||
|
|
||||||
branches:
|
branches:
|
||||||
except:
|
except:
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
FROM golang:1.22-alpine as builder
|
FROM golang:1.15-alpine as builder
|
||||||
RUN apk add --no-cache git gcc musl-dev
|
RUN apk add --no-cache git
|
||||||
WORKDIR /go/croc
|
WORKDIR /go/croc
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN go build -v -ldflags="-s -w"
|
RUN go build -v
|
||||||
|
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
EXPOSE 9009
|
EXPOSE 9009
|
||||||
|
|
@ -11,6 +11,5 @@ EXPOSE 9011
|
||||||
EXPOSE 9012
|
EXPOSE 9012
|
||||||
EXPOSE 9013
|
EXPOSE 9013
|
||||||
COPY --from=builder /go/croc/croc /go/croc/croc-entrypoint.sh /
|
COPY --from=builder /go/croc/croc /go/croc/croc-entrypoint.sh /
|
||||||
USER nobody
|
|
||||||
ENTRYPOINT ["/croc-entrypoint.sh"]
|
ENTRYPOINT ["/croc-entrypoint.sh"]
|
||||||
CMD ["relay"]
|
CMD ["relay"]
|
||||||
|
|
|
||||||
2
LICENSE
2
LICENSE
|
|
@ -1,6 +1,6 @@
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017-2024 Zack
|
Copyright (c) 2017-2020 Zack
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|
|
||||||
147
README.md
147
README.md
|
|
@ -4,11 +4,18 @@
|
||||||
src="https://user-images.githubusercontent.com/6550035/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg"
|
src="https://user-images.githubusercontent.com/6550035/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg"
|
||||||
width="408px" border="0" alt="croc">
|
width="408px" border="0" alt="croc">
|
||||||
<br>
|
<br>
|
||||||
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v10.1.1-brightgreen.svg?style=flat-square" alt="Version"></a>
|
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v8.6.7-brightgreen.svg?style=flat-square" alt="Version"></a>
|
||||||
<a href="https://github.com/schollz/croc/actions/workflows/ci.yml"><img
|
<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>
|
||||||
src="https://github.com/schollz/croc/actions/workflows/ci.yml/badge.svg" alt="Build
|
<a href="https://travis-ci.org/schollz/croc"><img
|
||||||
|
src="https://img.shields.io/travis/schollz/croc.svg?style=flat-square" alt="Build
|
||||||
Status"></a>
|
Status"></a>
|
||||||
<p align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
|
<p align="center">This project is supported by:</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.digitalocean.com/">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</p>
|
||||||
|
|
||||||
`croc` is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, *croc* is the only CLI file-transfer tool that does **all** of the following:
|
`croc` is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, *croc* is the only CLI file-transfer tool that does **all** of the following:
|
||||||
|
|
||||||
|
|
@ -21,7 +28,7 @@ Status"></a>
|
||||||
- **ipv6-first** with ipv4 fallback
|
- **ipv6-first** with ipv4 fallback
|
||||||
- can **use proxy**, like tor
|
- can **use proxy**, like tor
|
||||||
|
|
||||||
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6) or read a [recent interview I did](https://console.substack.com/p/console-91).
|
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
@ -30,99 +37,75 @@ For more information about `croc`, see [my blog post](https://schollz.com/softwa
|
||||||
Download [the latest release for your system](https://github.com/schollz/croc/releases/latest), or install a release from the command-line:
|
Download [the latest release for your system](https://github.com/schollz/croc/releases/latest), or install a release from the command-line:
|
||||||
|
|
||||||
```
|
```
|
||||||
curl https://getcroc.schollz.com | bash
|
$ curl https://getcroc.schollz.com | bash
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
On macOS you can install the latest release with [Homebrew](https://brew.sh/):
|
On macOS you can install the latest release with [Homebrew](https://brew.sh/):
|
||||||
|
|
||||||
```
|
```
|
||||||
brew install croc
|
$ brew install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On macOS you can also install the latest release with [MacPorts](https://macports.org/):
|
On macOS you can also install the latest release with [MacPorts](https://macports.org/):
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo port selfupdate
|
$ sudo port selfupdate
|
||||||
sudo port install croc
|
$ sudo port install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
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/):
|
On Windows you can install the latest release with [Scoop](https://scoop.sh/) or [Chocolatey](https://chocolatey.org):
|
||||||
|
|
||||||
```
|
```
|
||||||
scoop install croc
|
$ scoop install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```
|
||||||
choco install croc
|
$ choco install croc
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
winget install schollz.croc
|
|
||||||
```
|
```
|
||||||
|
|
||||||
On Unix you can install the latest release with [Nix](https://nixos.org/nix):
|
On Unix you can install the latest release with [Nix](https://nixos.org/nix):
|
||||||
|
|
||||||
```
|
```
|
||||||
nix-env -i croc
|
$ nix-env -i croc
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
On Alpine Linux you have to install dependencies first:
|
|
||||||
|
|
||||||
```
|
|
||||||
apk add bash coreutils
|
|
||||||
wget -qO- https://getcroc.schollz.com | bash
|
|
||||||
```
|
|
||||||
|
|
||||||
On Arch Linux you can install the latest release with `pacman`:
|
On Arch Linux you can install the latest release with `pacman`:
|
||||||
|
|
||||||
```
|
```
|
||||||
pacman -S croc
|
$ pacman -S croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On Fedora you can install with `dnf`:
|
On Ubuntu you can install with `snap`:
|
||||||
|
|
||||||
```
|
```
|
||||||
dnf install croc
|
$ snap install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On Gentoo you can install with `portage`:
|
On Gentoo you can install with `portage`:
|
||||||
```
|
```
|
||||||
emerge net-misc/croc
|
$ emerge net-misc/croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On Termux you can install with `pkg`:
|
On Termux you can install with `pkg`:
|
||||||
|
|
||||||
```
|
```
|
||||||
pkg install croc
|
$ pkg install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On FreeBSD you can install with `pkg`:
|
On FreeBSD you can install with `pkg`:
|
||||||
|
|
||||||
```
|
```
|
||||||
pkg install croc
|
$ pkg install croc
|
||||||
```
|
```
|
||||||
|
|
||||||
On Linux, macOS, and Windows you can install from [conda-forge](https://github.com/conda-forge/croc-feedstock/) globally with [`pixi`](https://pixi.sh/):
|
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.12+):
|
||||||
|
|
||||||
```
|
```
|
||||||
pixi global install croc
|
$ GO111MODULE=on go get -v github.com/schollz/croc/v8
|
||||||
```
|
```
|
||||||
|
|
||||||
or into a particular environment with [`conda`](https://docs.conda.io/projects/conda/):
|
|
||||||
|
|
||||||
```
|
|
||||||
conda install --channel conda-forge croc
|
|
||||||
```
|
|
||||||
|
|
||||||
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
|
|
||||||
|
|
||||||
```
|
|
||||||
go install github.com/schollz/croc/v10@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
On Android there is a 3rd party F-Droid app [available to download](https://f-droid.org/en/packages/com.github.howeyc.crocgui/).
|
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -138,61 +121,33 @@ Code is: code-phrase
|
||||||
Then to receive the file (or folder) on another computer, you can just do
|
Then to receive the file (or folder) on another computer, you can just do
|
||||||
|
|
||||||
```
|
```
|
||||||
croc code-phrase
|
$ croc code-phrase
|
||||||
```
|
```
|
||||||
|
|
||||||
The code phrase is used to establish password-authenticated key agreement ([PAKE](https://en.wikipedia.org/wiki/Password-authenticated_key_agreement)) which generates a secret key for the sender and recipient to use for end-to-end encryption.
|
The code phrase is used to establish password-authenticated key agreement ([PAKE](https://en.wikipedia.org/wiki/Password-authenticated_key_agreement)) which generates a secret key for the sender and recipient to use for end-to-end encryption.
|
||||||
|
|
||||||
There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`.
|
There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`.
|
||||||
|
|
||||||
### Using `croc` on Linux or Mac OS
|
|
||||||
|
|
||||||
On Linux and Mac OS, the sending & receiving is slightly different to avoid [leaking the secret via the process name](https://nvd.nist.gov/vuln/detail/CVE-2023-43621). On these systems you will need to run `croc` with the secret as an environment variable. For example, to receive with the secret `***`:
|
|
||||||
|
|
||||||
```
|
|
||||||
CROC_SECRET=*** croc
|
|
||||||
```
|
|
||||||
|
|
||||||
This will show only `croc` in the process list of a multi-user system and not leak the secret.
|
|
||||||
|
|
||||||
For a single-user system the default behavior can be permanently enabled by running
|
|
||||||
|
|
||||||
```
|
|
||||||
croc --classic
|
|
||||||
```
|
|
||||||
|
|
||||||
and confirming.
|
|
||||||
Run this command again to disable classic mode.
|
|
||||||
|
|
||||||
### Custom code phrase
|
### Custom code phrase
|
||||||
|
|
||||||
You can send with your own code phrase (must be more than 6 characters).
|
You can send with your own code phrase (must be more than 4 characters).
|
||||||
|
|
||||||
```
|
```
|
||||||
croc send --code [code-phrase] [file(s)-or-folder]
|
$ croc send --code [code-phrase] [file(s)-or-folder]
|
||||||
```
|
```
|
||||||
|
|
||||||
### Allow overwriting without prompt
|
|
||||||
|
|
||||||
By default, croc will prompt whether to overwrite a file. You can automatically overwrite files by using the `--overwrite` flag (recipient only). For example, receive a file to automatically overwrite:
|
|
||||||
|
|
||||||
```
|
|
||||||
croc --yes --overwrite <code>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Use pipes - stdin and stdout
|
### Use pipes - stdin and stdout
|
||||||
|
|
||||||
You can pipe to `croc`:
|
You can pipe to `croc`:
|
||||||
|
|
||||||
```
|
```
|
||||||
cat [filename] | croc send
|
$ cat [filename] | croc send
|
||||||
```
|
```
|
||||||
|
|
||||||
In this case `croc` will automatically use the stdin data and send and assign a filename like "croc-stdin-123456789". To receive to `stdout` at you can always just use the `--yes` will automatically approve the transfer and pipe it out to `stdout`.
|
In this case `croc` will automatically use the stdin data and send and assign a filename like "croc-stdin-123456789". To receive to `stdout` at you can always just use the `--yes` will automatically approve the transfer and pipe it out to `stdout`.
|
||||||
|
|
||||||
```
|
```
|
||||||
croc --yes [code-phrase] > out
|
$ croc --yes [code-phrase] > out
|
||||||
```
|
```
|
||||||
|
|
||||||
All of the other text printed to the console is going to `stderr` so it will not interfere with the message going to `stdout`.
|
All of the other text printed to the console is going to `stderr` so it will not interfere with the message going to `stdout`.
|
||||||
|
|
@ -203,7 +158,7 @@ All of the other text printed to the console is going to `stderr` so it will not
|
||||||
Sometimes you want to send URLs or short text. In addition to piping, you can easily send text with `croc`:
|
Sometimes you want to send URLs or short text. In addition to piping, you can easily send text with `croc`:
|
||||||
|
|
||||||
```
|
```
|
||||||
croc send --text "hello world"
|
$ croc send --text "hello world"
|
||||||
```
|
```
|
||||||
|
|
||||||
This will automatically tell the receiver to use `stdout` when they receive the text so it will be displayed.
|
This will automatically tell the receiver to use `stdout` when they receive the text so it will be displayed.
|
||||||
|
|
@ -214,25 +169,7 @@ This will automatically tell the receiver to use `stdout` when they receive the
|
||||||
You can use a proxy as your connection to the relay by adding a proxy address with `--socks5`. For example, you can send via a tor relay:
|
You can use a proxy as your connection to the relay by adding a proxy address with `--socks5`. For example, you can send via a tor relay:
|
||||||
|
|
||||||
```
|
```
|
||||||
croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
$ croc --socks5 "127.0.0.1:9050" send SOMEFILE
|
||||||
```
|
|
||||||
|
|
||||||
### Change encryption curve
|
|
||||||
|
|
||||||
You can choose from several different elliptic curves to use for encryption by using the `--curve` flag. Only the recipient can choose the curve. For example, receive a file using the P-521 curve:
|
|
||||||
|
|
||||||
```
|
|
||||||
croc --curve p521 <codephrase>
|
|
||||||
```
|
|
||||||
|
|
||||||
Available curves are P-256, P-348, P-521 and SIEC. P-256 is the default curve.
|
|
||||||
|
|
||||||
### Change hash algorithm
|
|
||||||
|
|
||||||
You can choose from several different hash algorithms. The default is the `xxhash` algorithm which is fast and thorough. If you want to optimize for speed you can use the `imohash` algorithm which is even faster, but since it samples files (versus reading the whole file) it can mistakenly determine that a file is the same on the two computers transferring - though this is only a problem if you are syncing files versus sending a new file to a computer.
|
|
||||||
|
|
||||||
```
|
|
||||||
croc send --hash imohash SOMEFILE
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Self-host relay
|
### Self-host relay
|
||||||
|
|
@ -240,15 +177,15 @@ croc send --hash imohash SOMEFILE
|
||||||
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
|
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
|
||||||
|
|
||||||
```
|
```
|
||||||
croc relay
|
$ croc relay
|
||||||
```
|
```
|
||||||
|
|
||||||
By default it uses TCP ports 9009-9013. Make sure to open those up. You can 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.
|
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.
|
||||||
|
|
||||||
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.
|
You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own.
|
||||||
|
|
||||||
```
|
```
|
||||||
croc --relay "myrelay.example.com:9009" send [filename]
|
$ croc --relay "myrelay.example.com:9009" send [filename]
|
||||||
```
|
```
|
||||||
|
|
||||||
Note, when sending, you only need to include the first port (the communication port). The subsequent ports for data transfer will be transmitted back to the user from the relay.
|
Note, when sending, you only need to include the first port (the communication port). The subsequent ports for data transfer will be transmitted back to the user from the relay.
|
||||||
|
|
@ -259,13 +196,13 @@ If it's easier you can also run a relay with Docker:
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
docker run -d -p 9009-9013:9009-9013 -e CROC_PASS='YOURPASSWORD' schollz/croc
|
$ docker run -d -p 9009-9013:9009-9013 -e CROC_PASS='YOURPASSWORD' schollz/croc
|
||||||
```
|
```
|
||||||
|
|
||||||
Be sure to include the password for the relay otherwise any requests will be rejected.
|
Be sure to include the password for the relay otherwise any requests will be rejected.
|
||||||
|
|
||||||
```
|
```
|
||||||
croc --pass YOURPASSWORD --relay "myreal.example.com:9009" send [filename]
|
$ croc --pass YOURPASSWORD --relay "myreal.example.com:9009" send [filename]
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: when including `--pass YOURPASSWORD` you can instead pass a file with the password, e.g. `--pass FILEWITHPASSWORD`.
|
Note: when including `--pass YOURPASSWORD` you can instead pass a file with the password, e.g. `--pass FILEWITHPASSWORD`.
|
||||||
|
|
@ -276,6 +213,6 @@ MIT
|
||||||
|
|
||||||
## Acknowledgements
|
## 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, or ask a question.
|
`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)).
|
||||||
|
|
||||||
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)!
|
Thanks [@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole), [@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28), [@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). Finally thanks for making pull requests [@maximbaz](https://github.com/maximbaz), [@meyermarcel](https://github.com/meyermarcel), [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu), [@nicolashardy](https://github.com/nicolashardy), [@fbartels](https://github.com/fbartels), [@rkuprov](https://github.com/rkuprov), [@xenrox](https://github.com/xenrox) and [Ipar](https://github.com/lpar)!
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ After=network.target
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
DynamicUser=yes
|
User=nobody
|
||||||
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||||
ExecStart=/usr/bin/croc relay
|
ExecStart=/usr/bin/croc relay
|
||||||
|
|
||||||
|
|
|
||||||
50
go.mod
50
go.mod
|
|
@ -1,39 +1,27 @@
|
||||||
module github.com/schollz/croc/v10
|
module github.com/schollz/croc/v8
|
||||||
|
|
||||||
go 1.22
|
go 1.13
|
||||||
|
|
||||||
toolchain go1.23.1
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/OneOfOne/xxhash v1.2.5 // indirect
|
||||||
github.com/cespare/xxhash v1.1.0
|
github.com/cespare/xxhash v1.1.0
|
||||||
github.com/chzyer/readline v1.5.1
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||||
github.com/denisbrodbeck/machineid v1.0.1
|
github.com/denisbrodbeck/machineid v1.0.1
|
||||||
github.com/kalafut/imohash v1.1.0
|
github.com/kalafut/imohash v1.0.0
|
||||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b
|
github.com/kr/pretty v0.1.0 // indirect
|
||||||
github.com/minio/highwayhash v1.0.3
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
|
|
||||||
github.com/schollz/cli/v2 v2.2.1
|
github.com/schollz/cli/v2 v2.2.1
|
||||||
github.com/schollz/logger v1.2.0
|
github.com/schollz/logger v1.2.0
|
||||||
github.com/schollz/pake/v3 v3.0.5
|
github.com/schollz/mnemonicode v1.0.1
|
||||||
github.com/schollz/peerdiscovery v1.7.5
|
github.com/schollz/pake/v2 v2.0.6
|
||||||
github.com/schollz/progressbar/v3 v3.17.1
|
github.com/schollz/peerdiscovery v1.6.2
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
github.com/schollz/progressbar/v3 v3.7.2
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
golang.org/x/crypto v0.29.0
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/net v0.31.0
|
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094
|
||||||
golang.org/x/sys v0.27.0
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||||
golang.org/x/term v0.26.0
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
|
||||||
golang.org/x/time v0.8.0
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
|
||||||
)
|
golang.org/x/text v0.3.4 // indirect
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
require (
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 // indirect
|
|
||||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|
|
||||||
169
go.sum
169
go.sum
|
|
@ -1,148 +1,91 @@
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.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/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
|
|
||||||
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
|
|
||||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
|
||||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
|
||||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
|
||||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
|
||||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
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 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
|
||||||
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||||
github.com/kalafut/imohash v1.1.0 h1:Lldcmx0SXgMSoABB2WBD8mTgf0OlVnISn2Dyrfg2Ep8=
|
github.com/kalafut/imohash v1.0.0 h1:LgCJ+p/BwM2HKpOxFopkeddpzVCfm15EtXMroXD1SYE=
|
||||||
github.com/kalafut/imohash v1.1.0/go.mod h1:6cn9lU0Sj8M4eu9UaQm1kR/5y3k/ayB68yntRhGloL4=
|
github.com/kalafut/imohash v1.0.0/go.mod h1:c3RHT80ZAp5C/aYgQI92ZlrOymqkZnRDprU87kg75HI=
|
||||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
|
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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||||
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.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 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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 h1:ou22Mj7ZPjrKz+8k2iDTWaHskEEV5NiAxGrdsCL36VU=
|
||||||
github.com/schollz/cli/v2 v2.2.1/go.mod h1:My6bfphRLZUhZdlFUK8scAxMWHydE7k4s2ed2Dtnn+s=
|
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 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
|
||||||
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
|
||||||
github.com/schollz/pake/v3 v3.0.5 h1:MnZVdI987lkjln9BSx/zUb724TZISa2jbO+dPj6BvgQ=
|
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
|
||||||
github.com/schollz/pake/v3 v3.0.5/go.mod h1:OGbG6htRwSKo6V8R5tg61ufpFmZM1b/PrrSp6g2ZLLc=
|
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
|
||||||
github.com/schollz/peerdiscovery v1.7.5 h1:0cEhO+o8i4fpeKBwl7u0UY3Kt3XVt5fSzS4rg17ZPb4=
|
github.com/schollz/pake/v2 v2.0.6 h1:+x8vTmj4uPHe7TNBcCjcwDmm9uMeFIEqh34u+5a+CCY=
|
||||||
github.com/schollz/peerdiscovery v1.7.5/go.mod h1:Crht2FOfD1/eL3U/AIM0vvwVZDPePlBgSX3Xw+TnJoE=
|
github.com/schollz/pake/v2 v2.0.6/go.mod h1:0NENjRsJxJQQ23Ei2fMSPo/mOtURNk/BWr3Cr9zpJ0w=
|
||||||
github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
|
github.com/schollz/peerdiscovery v1.6.2 h1:EE1mnCuiQXbp8sG0z0qx9xkuU2Eho6LgcwD3LfnNIlQ=
|
||||||
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
|
github.com/schollz/peerdiscovery v1.6.2/go.mod h1:rvHXqW4PX4F14ehw8CLQSQFsb14YGLAeYYshxZV5fWE=
|
||||||
|
github.com/schollz/progressbar/v3 v3.7.2 h1:0mjLacO6y9vSdcopQ8TEK8AsIWFJXTVU9eJDkoR/MkE=
|
||||||
|
github.com/schollz/progressbar/v3 v3.7.2/go.mod h1:CG/f0JmacksUc6TkZToO7tVq4t03zIQSQUtTd7F9GR4=
|
||||||
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
|
||||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 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/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/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094 h1:tZWtuLE+LbUwT4OP1oWBSB9zXA8qmQ5qEm4kV9R72oo=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
||||||
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
|
||||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 h1:sDWDZkwYqX0jvLWstKzFwh+pYhQNaVg65BgSkCP/f7U=
|
|
||||||
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
|
|
||||||
github.com/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
|
||||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
|
||||||
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-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.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
|
||||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
|
||||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
|
||||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
|
||||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
|
||||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
|
||||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|
||||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201113135734-0a15ea8d9b02/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
|
||||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-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-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
|
||||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
|
||||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
|
||||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
|
||||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
|
||||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 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/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
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.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=
|
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,6 @@ build:
|
||||||
ignore:
|
ignore:
|
||||||
- goos: darwin
|
- goos: darwin
|
||||||
goarch: 386
|
goarch: 386
|
||||||
- goos: freebsd
|
|
||||||
goarch: arm
|
|
||||||
goarm:
|
goarm:
|
||||||
- 7
|
- 7
|
||||||
nfpms:
|
nfpms:
|
||||||
|
|
@ -74,7 +72,7 @@ archives:
|
||||||
|
|
||||||
brews:
|
brews:
|
||||||
-
|
-
|
||||||
tap:
|
github:
|
||||||
owner: schollz
|
owner: schollz
|
||||||
name: homebrew-tap
|
name: homebrew-tap
|
||||||
folder: Formula
|
folder: Formula
|
||||||
|
|
@ -93,8 +91,4 @@ scoop:
|
||||||
homepage: "https://schollz.com/software/croc/"
|
homepage: "https://schollz.com/software/croc/"
|
||||||
description: "croc is a tool that allows any two computers to simply and securely transfer files and folders."
|
description: "croc is a tool that allows any two computers to simply and securely transfer files and folders."
|
||||||
license: MIT
|
license: MIT
|
||||||
announce:
|
|
||||||
twitter:
|
|
||||||
# Wether its enabled or not.
|
|
||||||
# Defaults to false.
|
|
||||||
enabled: false
|
|
||||||
30
main.go
30
main.go
|
|
@ -6,12 +6,8 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/cli"
|
"github.com/schollz/croc/v8/src/cli"
|
||||||
"github.com/schollz/croc/v10/src/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -31,25 +27,7 @@ func main() {
|
||||||
// fmt.Println("wrote profile")
|
// fmt.Println("wrote profile")
|
||||||
// }
|
// }
|
||||||
// }()
|
// }()
|
||||||
|
if err := cli.Run(); err != nil {
|
||||||
// Create a channel to receive OS signals
|
fmt.Println(err)
|
||||||
sigs := make(chan os.Signal, 1)
|
}
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
if err := cli.Run(); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// Exit the program gracefully
|
|
||||||
utils.RemoveMarkedFiles()
|
|
||||||
os.Exit(0)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Wait for a termination signal
|
|
||||||
_ = <-sigs
|
|
||||||
utils.RemoveMarkedFiles()
|
|
||||||
|
|
||||||
// Exit the program gracefully
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
498
src/cli/cli.go
498
src/cli/cli.go
|
|
@ -5,24 +5,21 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
|
||||||
"github.com/schollz/cli/v2"
|
"github.com/schollz/cli/v2"
|
||||||
"github.com/schollz/croc/v10/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v10/src/croc"
|
"github.com/schollz/croc/v8/src/croc"
|
||||||
"github.com/schollz/croc/v10/src/mnemonicode"
|
"github.com/schollz/croc/v8/src/models"
|
||||||
"github.com/schollz/croc/v10/src/models"
|
"github.com/schollz/croc/v8/src/tcp"
|
||||||
"github.com/schollz/croc/v10/src/tcp"
|
"github.com/schollz/croc/v8/src/utils"
|
||||||
"github.com/schollz/croc/v10/src/utils"
|
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/schollz/pake/v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version specifies the version
|
// Version specifies the version
|
||||||
|
|
@ -36,26 +33,14 @@ func Run() (err error) {
|
||||||
app := cli.NewApp()
|
app := cli.NewApp()
|
||||||
app.Name = "croc"
|
app.Name = "croc"
|
||||||
if Version == "" {
|
if Version == "" {
|
||||||
Version = "v10.1.0"
|
Version = "v8.6.7-05640cd"
|
||||||
}
|
}
|
||||||
app.Version = Version
|
app.Version = Version
|
||||||
app.Compiled = time.Now()
|
app.Compiled = time.Now()
|
||||||
app.Usage = "easily and securely transfer stuff from one computer to another"
|
app.Usage = "easily and securely transfer stuff from one computer to another"
|
||||||
app.UsageText = `croc [GLOBAL OPTIONS] [COMMAND] [COMMAND OPTIONS] [filename(s) or folder]
|
app.UsageText = `Send a file:
|
||||||
|
|
||||||
USAGE EXAMPLES:
|
|
||||||
Send a file:
|
|
||||||
croc send file.txt
|
croc send file.txt
|
||||||
|
|
||||||
-git to respect your .gitignore
|
|
||||||
Send multiple files:
|
|
||||||
croc send file1.txt file2.txt file3.txt
|
|
||||||
or
|
|
||||||
croc send *.jpg
|
|
||||||
|
|
||||||
Send everything in a folder:
|
|
||||||
croc send example-folder-name
|
|
||||||
|
|
||||||
Send a file with a custom code:
|
Send a file with a custom code:
|
||||||
croc send --code secret-code file.txt
|
croc send --code secret-code file.txt
|
||||||
|
|
||||||
|
|
@ -64,41 +49,35 @@ func Run() (err error) {
|
||||||
app.Commands = []*cli.Command{
|
app.Commands = []*cli.Command{
|
||||||
{
|
{
|
||||||
Name: "send",
|
Name: "send",
|
||||||
Usage: "send file(s), or folder (see options with croc send -h)",
|
Usage: "send a file (see options with croc send -h)",
|
||||||
Description: "send file(s), or folder, over the relay",
|
Description: "send a file over the relay",
|
||||||
ArgsUsage: "[filename(s) or folder]",
|
ArgsUsage: "[filename]",
|
||||||
Flags: []cli.Flag{
|
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: "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.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-local", Usage: "disable local relay when sending"},
|
||||||
&cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
|
&cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
|
||||||
&cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"},
|
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
|
||||||
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
|
|
||||||
&cli.IntFlag{Name: "transfers", Value: 4, Usage: "number of ports to use for transfers"},
|
|
||||||
&cli.BoolFlag{Name: "qrcode", Aliases: []string{"qr"}, Usage: "show receive code as a qrcode"},
|
|
||||||
},
|
},
|
||||||
HelpName: "croc send",
|
HelpName: "croc send",
|
||||||
Action: send,
|
Action: func(c *cli.Context) error {
|
||||||
|
return send(c)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "relay",
|
Name: "relay",
|
||||||
Usage: "start your own relay (optional)",
|
Usage: "start your own relay (optional)",
|
||||||
Description: "start relay",
|
Description: "start relay",
|
||||||
HelpName: "croc relay",
|
HelpName: "croc relay",
|
||||||
Action: relay,
|
Action: func(c *cli.Context) error {
|
||||||
|
return relay(c)
|
||||||
|
},
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{Name: "host", Usage: "host of the relay"},
|
|
||||||
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
|
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
|
||||||
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
|
|
||||||
&cli.IntFlag{Name: "transfers", Value: 5, Usage: "number of ports to use for relay"},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
app.Flags = []cli.Flag{
|
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: "remember", Usage: "save these settings to reuse next time"},
|
||||||
&cli.BoolFlag{Name: "debug", Usage: "toggle debug mode"},
|
&cli.BoolFlag{Name: "debug", Usage: "toggle debug mode"},
|
||||||
&cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
|
&cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
|
||||||
|
|
@ -106,19 +85,12 @@ func Run() (err error) {
|
||||||
&cli.BoolFlag{Name: "no-compress", Usage: "disable compression"},
|
&cli.BoolFlag{Name: "no-compress", Usage: "disable compression"},
|
||||||
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
|
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
|
||||||
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
|
&cli.BoolFlag{Name: "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 or resume"},
|
|
||||||
&cli.BoolFlag{Name: "testing", Usage: "flag for testing purposes"},
|
|
||||||
&cli.StringFlag{Name: "multicast", Value: "239.255.255.250", Usage: "multicast address to use for local discovery"},
|
|
||||||
&cli.StringFlag{Name: "curve", Value: "p256", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
|
|
||||||
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
|
||||||
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
|
||||||
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
|
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
|
||||||
&cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
|
&cli.StringFlag{Name: "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: "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: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
|
||||||
&cli.StringFlag{Name: "connect", Value: "", Usage: "add a http proxy", EnvVars: []string{"HTTP_PROXY"}},
|
|
||||||
&cli.StringFlag{Name: "throttleUpload", Value: "", Usage: "Throttle the upload speed e.g. 500k"},
|
|
||||||
}
|
}
|
||||||
app.EnableBashCompletion = true
|
app.EnableBashCompletion = true
|
||||||
app.HideHelp = false
|
app.HideHelp = false
|
||||||
|
|
@ -133,61 +105,6 @@ func Run() (err error) {
|
||||||
return true
|
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 trying to send but forgot send, let the user know
|
||||||
if c.Args().Present() && allStringsAreFiles(c.Args().Slice()) {
|
if c.Args().Present() && allStringsAreFiles(c.Args().Slice()) {
|
||||||
fnames := []string{}
|
fnames := []string{}
|
||||||
|
|
@ -195,38 +112,42 @@ Do you wish to continue to enable the classic mode? (y/N) `)
|
||||||
_, basename := filepath.Split(fpath)
|
_, basename := filepath.Split(fpath)
|
||||||
fnames = append(fnames, "'"+basename+"'")
|
fnames = append(fnames, "'"+basename+"'")
|
||||||
}
|
}
|
||||||
promptMessage := fmt.Sprintf("Did you mean to send %s? (Y/n) ", strings.Join(fnames, ", "))
|
yn := utils.GetInput(fmt.Sprintf("Did you mean to send %s? (y/n) ", strings.Join(fnames, ", ")))
|
||||||
choice := strings.ToLower(utils.GetInput(promptMessage))
|
if strings.ToLower(yn) == "y" {
|
||||||
if choice == "" || choice == "y" || choice == "yes" {
|
|
||||||
return send(c)
|
return send(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return receive(c)
|
return receive(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
return app.Run(os.Args)
|
return app.Run(os.Args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getConfigDir() (homedir string, err error) {
|
||||||
|
homedir, err = os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
homedir = path.Join(homedir, ".config", "croc")
|
||||||
|
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
||||||
|
log.Debugf("creating home directory %s", homedir)
|
||||||
|
err = os.MkdirAll(homedir, 0700)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func setDebugLevel(c *cli.Context) {
|
func setDebugLevel(c *cli.Context) {
|
||||||
if c.Bool("debug") {
|
if c.Bool("debug") {
|
||||||
log.SetLevel("debug")
|
log.SetLevel("debug")
|
||||||
log.Debug("debug mode on")
|
log.Debug("debug mode on")
|
||||||
// print the public IP address
|
|
||||||
ip, err := utils.PublicIP()
|
|
||||||
if err == nil {
|
|
||||||
log.Debugf("public IP address: %s", ip)
|
|
||||||
} else {
|
|
||||||
log.Debug(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
log.SetLevel("info")
|
log.SetLevel("info")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getSendConfigFile(requireValidPath bool) string {
|
func getConfigFile() string {
|
||||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
configFile, err := getConfigDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -234,27 +155,9 @@ func getSendConfigFile(requireValidPath bool) string {
|
||||||
return path.Join(configFile, "send.json")
|
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) {
|
func determinePass(c *cli.Context) (pass string) {
|
||||||
pass = c.String("pass")
|
pass = c.String("pass")
|
||||||
b, err := os.ReadFile(pass)
|
b, err := ioutil.ReadFile(pass)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
pass = strings.TrimSpace(string(b))
|
pass = strings.TrimSpace(string(b))
|
||||||
}
|
}
|
||||||
|
|
@ -264,54 +167,29 @@ func determinePass(c *cli.Context) (pass string) {
|
||||||
func send(c *cli.Context) (err error) {
|
func send(c *cli.Context) (err error) {
|
||||||
setDebugLevel(c)
|
setDebugLevel(c)
|
||||||
comm.Socks5Proxy = c.String("socks5")
|
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{
|
crocOptions := croc.Options{
|
||||||
SharedSecret: c.String("code"),
|
SharedSecret: c.String("code"),
|
||||||
IsSender: true,
|
IsSender: true,
|
||||||
Debug: c.Bool("debug"),
|
Debug: c.Bool("debug"),
|
||||||
NoPrompt: c.Bool("yes"),
|
NoPrompt: c.Bool("yes"),
|
||||||
RelayAddress: c.String("relay"),
|
RelayAddress: c.String("relay"),
|
||||||
RelayAddress6: c.String("relay6"),
|
RelayAddress6: c.String("relay6"),
|
||||||
Stdout: c.Bool("stdout"),
|
Stdout: c.Bool("stdout"),
|
||||||
DisableLocal: c.Bool("no-local"),
|
DisableLocal: c.Bool("no-local"),
|
||||||
OnlyLocal: c.Bool("local"),
|
OnlyLocal: c.Bool("local"),
|
||||||
IgnoreStdin: c.Bool("ignore-stdin"),
|
RelayPorts: strings.Split(c.String("ports"), ","),
|
||||||
RelayPorts: ports,
|
Ask: c.Bool("ask"),
|
||||||
Ask: c.Bool("ask"),
|
NoMultiplexing: c.Bool("no-multi"),
|
||||||
NoMultiplexing: c.Bool("no-multi"),
|
RelayPassword: determinePass(c),
|
||||||
RelayPassword: determinePass(c),
|
SendingText: c.String("text") != "",
|
||||||
SendingText: c.String("text") != "",
|
NoCompress: c.Bool("no-compress"),
|
||||||
NoCompress: c.Bool("no-compress"),
|
|
||||||
Overwrite: c.Bool("overwrite"),
|
|
||||||
Curve: c.String("curve"),
|
|
||||||
HashAlgorithm: c.String("hash"),
|
|
||||||
ThrottleUpload: c.String("throttleUpload"),
|
|
||||||
ZipFolder: c.Bool("zip"),
|
|
||||||
GitIgnore: c.Bool("git"),
|
|
||||||
ShowQrCode: c.Bool("qrcode"),
|
|
||||||
MulticastAddress: c.String("multicast"),
|
|
||||||
}
|
}
|
||||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||||
crocOptions.RelayAddress6 = ""
|
crocOptions.RelayAddress6 = ""
|
||||||
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
|
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
|
||||||
crocOptions.RelayAddress = ""
|
crocOptions.RelayAddress = ""
|
||||||
}
|
}
|
||||||
b, errOpen := os.ReadFile(getSendConfigFile(false))
|
b, errOpen := ioutil.ReadFile(getConfigFile())
|
||||||
if errOpen == nil && !c.Bool("remember") {
|
if errOpen == nil && !c.Bool("remember") {
|
||||||
var rememberedOptions croc.Options
|
var rememberedOptions croc.Options
|
||||||
err = json.Unmarshal(b, &rememberedOptions)
|
err = json.Unmarshal(b, &rememberedOptions)
|
||||||
|
|
@ -320,53 +198,34 @@ func send(c *cli.Context) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// update anything that isn't explicitly set
|
// update anything that isn't explicitly set
|
||||||
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
|
if !c.IsSet("relay") {
|
||||||
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
||||||
}
|
}
|
||||||
if !c.IsSet("no-local") {
|
if !c.IsSet("no-local") {
|
||||||
crocOptions.DisableLocal = rememberedOptions.DisableLocal
|
crocOptions.DisableLocal = rememberedOptions.DisableLocal
|
||||||
}
|
}
|
||||||
if !c.IsSet("ports") && len(rememberedOptions.RelayPorts) > 0 {
|
if !c.IsSet("ports") {
|
||||||
crocOptions.RelayPorts = rememberedOptions.RelayPorts
|
crocOptions.RelayPorts = rememberedOptions.RelayPorts
|
||||||
}
|
}
|
||||||
if !c.IsSet("code") {
|
if !c.IsSet("code") {
|
||||||
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
||||||
}
|
}
|
||||||
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
|
if !c.IsSet("pass") {
|
||||||
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
||||||
}
|
}
|
||||||
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
|
|
||||||
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
|
|
||||||
}
|
|
||||||
if !c.IsSet("overwrite") {
|
|
||||||
crocOptions.Overwrite = rememberedOptions.Overwrite
|
|
||||||
}
|
|
||||||
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
|
|
||||||
crocOptions.Curve = rememberedOptions.Curve
|
|
||||||
}
|
|
||||||
if !c.IsSet("local") {
|
|
||||||
crocOptions.OnlyLocal = rememberedOptions.OnlyLocal
|
|
||||||
}
|
|
||||||
if !c.IsSet("hash") {
|
|
||||||
crocOptions.HashAlgorithm = rememberedOptions.HashAlgorithm
|
|
||||||
}
|
|
||||||
if !c.IsSet("git") {
|
|
||||||
crocOptions.GitIgnore = rememberedOptions.GitIgnore
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var fnames []string
|
var fnames []string
|
||||||
stat, _ := os.Stdin.Stat()
|
stat, _ := os.Stdin.Stat()
|
||||||
if ((stat.Mode() & os.ModeCharDevice) == 0) && !c.Bool("ignore-stdin") {
|
if (stat.Mode() & os.ModeCharDevice) == 0 {
|
||||||
fnames, err = getStdin()
|
fnames, err = getStdin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.MarkFileForRemoval(fnames[0])
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := os.Remove(fnames[0])
|
err = os.Remove(fnames[0])
|
||||||
if e != nil {
|
if err != nil {
|
||||||
log.Error(e)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else if c.String("text") != "" {
|
} else if c.String("text") != "" {
|
||||||
|
|
@ -374,11 +233,10 @@ func send(c *cli.Context) (err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
utils.MarkFileForRemoval(fnames[0])
|
|
||||||
defer func() {
|
defer func() {
|
||||||
e := os.Remove(fnames[0])
|
err = os.Remove(fnames[0])
|
||||||
if e != nil {
|
if err != nil {
|
||||||
log.Error(e)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
|
@ -386,39 +244,15 @@ func send(c *cli.Context) (err error) {
|
||||||
fnames = c.Args().Slice()
|
fnames = c.Args().Slice()
|
||||||
}
|
}
|
||||||
if len(fnames) == 0 {
|
if len(fnames) == 0 {
|
||||||
return errors.New("must specify file: croc send [filename(s) or folder]")
|
return errors.New("must specify file: croc send [filename]")
|
||||||
}
|
|
||||||
|
|
||||||
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
|
|
||||||
if !classicInsecureMode {
|
|
||||||
// if operating system is UNIX, then use environmental variable to set the code
|
|
||||||
if (!(runtime.GOOS == "windows") && c.IsSet("code")) || os.Getenv("CROC_SECRET") != "" {
|
|
||||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
|
||||||
if crocOptions.SharedSecret == "" {
|
|
||||||
fmt.Printf(`On UNIX systems, to send with a custom code phrase,
|
|
||||||
you need to set the environmental variable CROC_SECRET:
|
|
||||||
|
|
||||||
CROC_SECRET=**** croc send file.txt
|
|
||||||
|
|
||||||
Or you can have the code phrase automatically generated:
|
|
||||||
|
|
||||||
croc send file.txt
|
|
||||||
|
|
||||||
Or you can go back to the classic croc behavior by enabling classic mode:
|
|
||||||
|
|
||||||
croc --classic
|
|
||||||
|
|
||||||
`)
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(crocOptions.SharedSecret) == 0 {
|
if len(crocOptions.SharedSecret) == 0 {
|
||||||
// generate code phrase
|
// generate code phrase
|
||||||
crocOptions.SharedSecret = utils.GetRandomName()
|
crocOptions.SharedSecret = utils.GetRandomName()
|
||||||
}
|
}
|
||||||
minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders, err := croc.GetFilesInfo(fnames, crocOptions.ZipFolder, crocOptions.GitIgnore)
|
|
||||||
|
paths, haveFolder, err := getPaths(fnames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -431,13 +265,16 @@ Or you can go back to the classic croc behavior by enabling classic mode:
|
||||||
// save the config
|
// save the config
|
||||||
saveConfig(c, crocOptions)
|
saveConfig(c, crocOptions)
|
||||||
|
|
||||||
err = cr.Send(minimalFileInfos, emptyFoldersToTransfer, totalNumberFolders)
|
err = cr.Send(croc.TransferOptions{
|
||||||
|
PathToFiles: paths,
|
||||||
|
KeepPathInRemote: haveFolder,
|
||||||
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getStdin() (fnames []string, err error) {
|
func getStdin() (fnames []string, err error) {
|
||||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -454,7 +291,7 @@ func getStdin() (fnames []string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeTempFileWithString(s string) (fnames []string, err error) {
|
func makeTempFileWithString(s string) (fnames []string, err error) {
|
||||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -470,11 +307,43 @@ func makeTempFileWithString(s string) (fnames []string, err error) {
|
||||||
}
|
}
|
||||||
fnames = []string{f.Name()}
|
fnames = []string{f.Name()}
|
||||||
return
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPaths(fnames []string) (paths []string, haveFolder bool, err error) {
|
||||||
|
haveFolder = false
|
||||||
|
paths = []string{}
|
||||||
|
for _, fname := range fnames {
|
||||||
|
stat, errStat := os.Stat(fname)
|
||||||
|
if errStat != nil {
|
||||||
|
err = errStat
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if stat.IsDir() {
|
||||||
|
haveFolder = true
|
||||||
|
err = filepath.Walk(fname,
|
||||||
|
func(pathName string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
paths = append(paths, filepath.ToSlash(pathName))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paths = append(paths, filepath.ToSlash(fname))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
||||||
if c.Bool("remember") {
|
if c.Bool("remember") {
|
||||||
configFile := getSendConfigFile(true)
|
configFile := getConfigFile()
|
||||||
log.Debug("saving config file")
|
log.Debug("saving config file")
|
||||||
var bConfig []byte
|
var bConfig []byte
|
||||||
// if the code wasn't set, don't save it
|
// if the code wasn't set, don't save it
|
||||||
|
|
@ -486,7 +355,7 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
err = ioutil.WriteFile(configFile, bConfig, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -495,55 +364,20 @@ 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) {
|
func receive(c *cli.Context) (err error) {
|
||||||
comm.Socks5Proxy = c.String("socks5")
|
comm.Socks5Proxy = c.String("socks5")
|
||||||
comm.HttpProxy = c.String("connect")
|
|
||||||
crocOptions := croc.Options{
|
crocOptions := croc.Options{
|
||||||
SharedSecret: c.String("code"),
|
SharedSecret: c.String("code"),
|
||||||
IsSender: false,
|
IsSender: false,
|
||||||
Debug: c.Bool("debug"),
|
Debug: c.Bool("debug"),
|
||||||
NoPrompt: c.Bool("yes"),
|
NoPrompt: c.Bool("yes"),
|
||||||
RelayAddress: c.String("relay"),
|
RelayAddress: c.String("relay"),
|
||||||
RelayAddress6: c.String("relay6"),
|
RelayAddress6: c.String("relay6"),
|
||||||
Stdout: c.Bool("stdout"),
|
Stdout: c.Bool("stdout"),
|
||||||
Ask: c.Bool("ask"),
|
Ask: c.Bool("ask"),
|
||||||
RelayPassword: determinePass(c),
|
RelayPassword: determinePass(c),
|
||||||
OnlyLocal: c.Bool("local"),
|
OnlyLocal: c.Bool("local"),
|
||||||
IP: c.String("ip"),
|
IP: c.String("ip"),
|
||||||
Overwrite: c.Bool("overwrite"),
|
|
||||||
Curve: c.String("curve"),
|
|
||||||
TestFlag: c.Bool("testing"),
|
|
||||||
MulticastAddress: c.String("multicast"),
|
|
||||||
}
|
}
|
||||||
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
|
||||||
crocOptions.RelayAddress6 = ""
|
crocOptions.RelayAddress6 = ""
|
||||||
|
|
@ -555,8 +389,6 @@ func receive(c *cli.Context) (err error) {
|
||||||
case 1:
|
case 1:
|
||||||
crocOptions.SharedSecret = c.Args().First()
|
crocOptions.SharedSecret = c.Args().First()
|
||||||
case 3:
|
case 3:
|
||||||
fallthrough
|
|
||||||
case 4:
|
|
||||||
var phrase []string
|
var phrase []string
|
||||||
phrase = append(phrase, c.Args().First())
|
phrase = append(phrase, c.Args().First())
|
||||||
phrase = append(phrase, c.Args().Tail()...)
|
phrase = append(phrase, c.Args().Tail()...)
|
||||||
|
|
@ -565,22 +397,22 @@ func receive(c *cli.Context) (err error) {
|
||||||
|
|
||||||
// load options here
|
// load options here
|
||||||
setDebugLevel(c)
|
setDebugLevel(c)
|
||||||
|
configFile, err := getConfigDir()
|
||||||
doRemember := c.Bool("remember")
|
if err != nil {
|
||||||
configFile, err := getReceiveConfigFile(doRemember)
|
log.Error(err)
|
||||||
if err != nil && doRemember {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b, errOpen := os.ReadFile(configFile)
|
configFile = path.Join(configFile, "receive.json")
|
||||||
if errOpen == nil && !doRemember {
|
b, errOpen := ioutil.ReadFile(configFile)
|
||||||
|
if errOpen == nil && !c.Bool("remember") {
|
||||||
var rememberedOptions croc.Options
|
var rememberedOptions croc.Options
|
||||||
err = json.Unmarshal(b, &rememberedOptions)
|
err = json.Unmarshal(b, &rememberedOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// update anything that isn't explicitly Globally set
|
// update anything that isn't expliciGlobalIsSettly set
|
||||||
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
|
if !c.IsSet("relay") {
|
||||||
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
crocOptions.RelayAddress = rememberedOptions.RelayAddress
|
||||||
}
|
}
|
||||||
if !c.IsSet("yes") {
|
if !c.IsSet("yes") {
|
||||||
|
|
@ -589,60 +421,13 @@ func receive(c *cli.Context) (err error) {
|
||||||
if crocOptions.SharedSecret == "" {
|
if crocOptions.SharedSecret == "" {
|
||||||
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
crocOptions.SharedSecret = rememberedOptions.SharedSecret
|
||||||
}
|
}
|
||||||
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
|
if !c.IsSet("pass") {
|
||||||
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
crocOptions.RelayPassword = rememberedOptions.RelayPassword
|
||||||
}
|
}
|
||||||
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
|
|
||||||
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
|
|
||||||
}
|
|
||||||
if !c.IsSet("overwrite") {
|
|
||||||
crocOptions.Overwrite = rememberedOptions.Overwrite
|
|
||||||
}
|
|
||||||
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
|
|
||||||
crocOptions.Curve = rememberedOptions.Curve
|
|
||||||
}
|
|
||||||
if !c.IsSet("local") {
|
|
||||||
crocOptions.OnlyLocal = rememberedOptions.OnlyLocal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
|
|
||||||
if crocOptions.SharedSecret == "" && os.Getenv("CROC_SECRET") != "" {
|
|
||||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
|
||||||
} else if !(runtime.GOOS == "windows") && crocOptions.SharedSecret != "" && !classicInsecureMode {
|
|
||||||
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
|
|
||||||
if crocOptions.SharedSecret == "" {
|
|
||||||
fmt.Printf(`On UNIX systems, to receive with croc you either need
|
|
||||||
to set a code phrase using your environmental variables:
|
|
||||||
|
|
||||||
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 == "" {
|
if crocOptions.SharedSecret == "" {
|
||||||
l, err := readline.NewEx(&readline.Config{
|
crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
|
||||||
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 c.String("out") != "" {
|
||||||
if err = os.Chdir(c.String("out")); err != nil {
|
if err = os.Chdir(c.String("out")); err != nil {
|
||||||
|
|
@ -656,7 +441,7 @@ Or you can go back to the classic croc behavior by enabling classic mode:
|
||||||
}
|
}
|
||||||
|
|
||||||
// save the config
|
// save the config
|
||||||
if doRemember {
|
if c.Bool("remember") {
|
||||||
log.Debug("saving config file")
|
log.Debug("saving config file")
|
||||||
var bConfig []byte
|
var bConfig []byte
|
||||||
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
|
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
|
||||||
|
|
@ -664,7 +449,7 @@ Or you can go back to the classic croc behavior by enabling classic mode:
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = os.WriteFile(configFile, bConfig, 0o644)
|
err = ioutil.WriteFile(configFile, bConfig, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
return
|
return
|
||||||
|
|
@ -682,37 +467,18 @@ func relay(c *cli.Context) (err error) {
|
||||||
if c.Bool("debug") {
|
if c.Bool("debug") {
|
||||||
debugString = "debug"
|
debugString = "debug"
|
||||||
}
|
}
|
||||||
host := c.String("host")
|
ports := strings.Split(c.String("ports"), ",")
|
||||||
var ports []string
|
|
||||||
|
|
||||||
if c.IsSet("ports") {
|
|
||||||
ports = strings.Split(c.String("ports"), ",")
|
|
||||||
} else {
|
|
||||||
portString := c.Int("port")
|
|
||||||
if portString == 0 {
|
|
||||||
portString = 9009
|
|
||||||
}
|
|
||||||
transfersString := c.Int("transfers")
|
|
||||||
if transfersString == 0 {
|
|
||||||
transfersString = 4
|
|
||||||
}
|
|
||||||
ports = make([]string, transfersString)
|
|
||||||
for i := range ports {
|
|
||||||
ports[i] = strconv.Itoa(portString + i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tcpPorts := strings.Join(ports[1:], ",")
|
tcpPorts := strings.Join(ports[1:], ",")
|
||||||
for i, port := range ports {
|
for i, port := range ports {
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go func(portStr string) {
|
go func(portStr string) {
|
||||||
err := tcp.Run(debugString, host, portStr, determinePass(c))
|
err = tcp.Run(debugString, portStr, determinePass(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}(port)
|
}(port)
|
||||||
}
|
}
|
||||||
return tcp.Run(debugString, host, ports[0], determinePass(c), tcpPorts)
|
return tcp.Run(debugString, ports[0], determinePass(c), tcpPorts)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/magisterquis/connectproxy"
|
"github.com/schollz/croc/v8/src/utils"
|
||||||
"github.com/schollz/croc/v10/src/utils"
|
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"golang.org/x/net/proxy"
|
"golang.org/x/net/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Socks5Proxy = ""
|
var Socks5Proxy = ""
|
||||||
var HttpProxy = ""
|
|
||||||
|
|
||||||
var MAGIC_BYTES = []byte("croc")
|
var MAGIC_BYTES = []byte("croc")
|
||||||
|
|
||||||
|
|
@ -35,52 +31,17 @@ func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err err
|
||||||
var connection net.Conn
|
var connection net.Conn
|
||||||
if Socks5Proxy != "" && !utils.IsLocalIP(address) {
|
if Socks5Proxy != "" && !utils.IsLocalIP(address) {
|
||||||
var dialer proxy.Dialer
|
var dialer proxy.Dialer
|
||||||
// prepend schema if no schema is given
|
dialer, err = proxy.SOCKS5("tcp", Socks5Proxy, nil, proxy.Direct)
|
||||||
if !strings.Contains(Socks5Proxy, `://`) {
|
|
||||||
Socks5Proxy = `socks5://` + Socks5Proxy
|
|
||||||
}
|
|
||||||
socks5ProxyURL, urlParseError := url.Parse(Socks5Proxy)
|
|
||||||
if urlParseError != nil {
|
|
||||||
err = fmt.Errorf("unable to parse socks proxy url: %s", urlParseError)
|
|
||||||
log.Debug(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dialer, err = proxy.FromURL(socks5ProxyURL, proxy.Direct)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("proxy failed: %w", err)
|
err = fmt.Errorf("proxy failed: %w", err)
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("dialing with dialer.Dial")
|
|
||||||
connection, err = dialer.Dial("tcp", address)
|
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 {
|
} else {
|
||||||
log.Debugf("dialing to %s with timelimit %s", address, tlimit)
|
|
||||||
connection, err = net.DialTimeout("tcp", address, tlimit)
|
connection, err = net.DialTimeout("tcp", address, tlimit)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("comm.NewConnection failed: %w", err)
|
err = fmt.Errorf("comm.NewConnection failed: %w", err)
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c = New(connection)
|
c = New(connection)
|
||||||
|
|
@ -138,7 +99,7 @@ func (c *Comm) Write(b []byte) (n int, err error) {
|
||||||
|
|
||||||
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||||
// long read deadline in case waiting for file
|
// 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)
|
log.Warnf("error setting read deadline: %v", err)
|
||||||
}
|
}
|
||||||
// must clear the timeout setting
|
// must clear the timeout setting
|
||||||
|
|
@ -175,7 +136,7 @@ func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
|
||||||
numBytes = int(numBytesUint32)
|
numBytes = int(numBytesUint32)
|
||||||
|
|
||||||
// shorten the reading deadline in case getting weird data
|
// 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)
|
log.Warnf("error setting read deadline: %v", err)
|
||||||
}
|
}
|
||||||
buf = make([]byte, numBytes)
|
buf = make([]byte, numBytes)
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ func TestComm(t *testing.T) {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||||
go func(_ string, connection net.Conn) {
|
go func(port string, connection net.Conn) {
|
||||||
c := New(connection)
|
c := New(connection)
|
||||||
err = c.Send([]byte("hello, world"))
|
err = c.Send([]byte("hello, world"))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
@ -49,7 +49,7 @@ func TestComm(t *testing.T) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
a, err := NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
a, err := NewConnection("localhost:"+port, 10*time.Minute)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
data, err := a.Receive()
|
data, err := a.Receive()
|
||||||
assert.Equal(t, []byte("hello, world"), data)
|
assert.Equal(t, []byte("hello, world"), data)
|
||||||
|
|
@ -63,4 +63,5 @@ func TestComm(t *testing.T) {
|
||||||
assert.NotNil(t, a.Send(token))
|
assert.NotNil(t, a.Send(token))
|
||||||
_, err = a.Write(token)
|
_, err = a.Write(token)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1430
src/croc/croc.go
1430
src/croc/croc.go
File diff suppressed because it is too large
Load Diff
|
|
@ -1,16 +1,13 @@
|
||||||
package croc
|
package croc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/tcp"
|
"github.com/schollz/croc/v8/src/tcp"
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
@ -18,11 +15,11 @@ import (
|
||||||
func init() {
|
func init() {
|
||||||
log.SetLevel("trace")
|
log.SetLevel("trace")
|
||||||
|
|
||||||
go tcp.Run("debug", "127.0.0.1", "8281", "pass123", "8282,8283,8284,8285")
|
go tcp.Run("debug", "8081", "pass123", "8082,8083,8084,8085")
|
||||||
go tcp.Run("debug", "127.0.0.1", "8282", "pass123")
|
go tcp.Run("debug", "8082", "pass123")
|
||||||
go tcp.Run("debug", "127.0.0.1", "8283", "pass123")
|
go tcp.Run("debug", "8083", "pass123")
|
||||||
go tcp.Run("debug", "127.0.0.1", "8284", "pass123")
|
go tcp.Run("debug", "8084", "pass123")
|
||||||
go tcp.Run("debug", "127.0.0.1", "8285", "pass123")
|
go tcp.Run("debug", "8085", "pass123")
|
||||||
time.Sleep(1 * time.Second)
|
time.Sleep(1 * time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,17 +29,14 @@ func TestCrocReadme(t *testing.T) {
|
||||||
log.Debug("setting up sender")
|
log.Debug("setting up sender")
|
||||||
sender, err := New(Options{
|
sender, err := New(Options{
|
||||||
IsSender: true,
|
IsSender: true,
|
||||||
SharedSecret: "8123-testingthecroc",
|
SharedSecret: "test",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
RelayAddress: "127.0.0.1:8281",
|
RelayAddress: "localhost:8081",
|
||||||
RelayPorts: []string{"8281"},
|
RelayPorts: []string{"8081"},
|
||||||
RelayPassword: "pass123",
|
RelayPassword: "pass123",
|
||||||
Stdout: false,
|
Stdout: false,
|
||||||
NoPrompt: true,
|
NoPrompt: true,
|
||||||
DisableLocal: true,
|
DisableLocal: true,
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
GitIgnore: false,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -51,15 +45,13 @@ func TestCrocReadme(t *testing.T) {
|
||||||
log.Debug("setting up receiver")
|
log.Debug("setting up receiver")
|
||||||
receiver, err := New(Options{
|
receiver, err := New(Options{
|
||||||
IsSender: false,
|
IsSender: false,
|
||||||
SharedSecret: "8123-testingthecroc",
|
SharedSecret: "test",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
RelayAddress: "127.0.0.1:8281",
|
RelayAddress: "localhost:8081",
|
||||||
RelayPassword: "pass123",
|
RelayPassword: "pass123",
|
||||||
Stdout: false,
|
Stdout: false,
|
||||||
NoPrompt: true,
|
NoPrompt: true,
|
||||||
DisableLocal: true,
|
DisableLocal: true,
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -68,11 +60,9 @@ func TestCrocReadme(t *testing.T) {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../README.md"}, false, false)
|
err := sender.Send(TransferOptions{
|
||||||
if errGet != nil {
|
PathToFiles: []string{"../../README.md"},
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
})
|
||||||
}
|
|
||||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("send failed: %v", err)
|
t.Errorf("send failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -90,175 +80,6 @@ func TestCrocReadme(t *testing.T) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCrocEmptyFolder(t *testing.T) {
|
|
||||||
pathName := "../../testEmpty"
|
|
||||||
defer os.RemoveAll(pathName)
|
|
||||||
defer os.RemoveAll("./testEmpty")
|
|
||||||
os.MkdirAll(pathName, 0o755)
|
|
||||||
|
|
||||||
log.Debug("setting up sender")
|
|
||||||
sender, err := New(Options{
|
|
||||||
IsSender: true,
|
|
||||||
SharedSecret: "8123-testingthecroc",
|
|
||||||
Debug: true,
|
|
||||||
RelayAddress: "127.0.0.1:8281",
|
|
||||||
RelayPorts: []string{"8281"},
|
|
||||||
RelayPassword: "pass123",
|
|
||||||
Stdout: false,
|
|
||||||
NoPrompt: true,
|
|
||||||
DisableLocal: true,
|
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("setting up receiver")
|
|
||||||
receiver, err := New(Options{
|
|
||||||
IsSender: false,
|
|
||||||
SharedSecret: "8123-testingthecroc",
|
|
||||||
Debug: true,
|
|
||||||
RelayAddress: "127.0.0.1:8281",
|
|
||||||
RelayPassword: "pass123",
|
|
||||||
Stdout: false,
|
|
||||||
NoPrompt: true,
|
|
||||||
DisableLocal: true,
|
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
go func() {
|
|
||||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
|
||||||
if errGet != nil {
|
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
|
||||||
}
|
|
||||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("send failed: %v", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
go func() {
|
|
||||||
err := receiver.Receive()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("receive failed: %v", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCrocSymlink(t *testing.T) {
|
|
||||||
pathName := "../link-in-folder"
|
|
||||||
defer os.RemoveAll(pathName)
|
|
||||||
defer os.RemoveAll("./link-in-folder")
|
|
||||||
os.MkdirAll(pathName, 0o755)
|
|
||||||
os.Symlink("../../README.md", filepath.Join(pathName, "README.link"))
|
|
||||||
|
|
||||||
log.Debug("setting up sender")
|
|
||||||
sender, err := New(Options{
|
|
||||||
IsSender: true,
|
|
||||||
SharedSecret: "8124-testingthecroc",
|
|
||||||
Debug: true,
|
|
||||||
RelayAddress: "127.0.0.1:8281",
|
|
||||||
RelayPorts: []string{"8281"},
|
|
||||||
RelayPassword: "pass123",
|
|
||||||
Stdout: false,
|
|
||||||
NoPrompt: true,
|
|
||||||
DisableLocal: true,
|
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
GitIgnore: false,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debug("setting up receiver")
|
|
||||||
receiver, err := New(Options{
|
|
||||||
IsSender: false,
|
|
||||||
SharedSecret: "8124-testingthecroc",
|
|
||||||
Debug: true,
|
|
||||||
RelayAddress: "127.0.0.1:8281",
|
|
||||||
RelayPassword: "pass123",
|
|
||||||
Stdout: false,
|
|
||||||
NoPrompt: true,
|
|
||||||
DisableLocal: true,
|
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(2)
|
|
||||||
go func() {
|
|
||||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{pathName}, false, false)
|
|
||||||
if errGet != nil {
|
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
|
||||||
}
|
|
||||||
err = sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("send failed: %v", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
time.Sleep(100 * time.Millisecond)
|
|
||||||
go func() {
|
|
||||||
err = receiver.Receive()
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("receive failed: %v", err)
|
|
||||||
}
|
|
||||||
wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
s, err := filepath.EvalSymlinks(path.Join(pathName, "README.link"))
|
|
||||||
if s != "../../README.md" && s != "..\\..\\README.md" {
|
|
||||||
log.Debug(s)
|
|
||||||
t.Errorf("symlink failed to transfer in folder")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("symlink transfer failed: %s", err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
func TestCrocIgnoreGit(t *testing.T) {
|
|
||||||
log.SetLevel("trace")
|
|
||||||
defer os.Remove(".gitignore")
|
|
||||||
time.Sleep(300 * time.Millisecond)
|
|
||||||
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
file, err := os.Create(".gitignore")
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error creating file")
|
|
||||||
}
|
|
||||||
_, err = file.WriteString("LICENSE")
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("error writing to file")
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
// due to how files are ignored in this function, all we have to do to test is make sure LICENSE doesn't get included in FilesInfo.
|
|
||||||
filesInfo, _, _, errGet := GetFilesInfo([]string{"../../LICENSE", ".gitignore", "croc.go"}, false, true)
|
|
||||||
if errGet != nil {
|
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
|
||||||
}
|
|
||||||
for _, file := range filesInfo {
|
|
||||||
if strings.Contains(file.Name, "LICENSE") {
|
|
||||||
t.Errorf("test failed, should ignore LICENSE")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCrocLocal(t *testing.T) {
|
func TestCrocLocal(t *testing.T) {
|
||||||
log.SetLevel("trace")
|
log.SetLevel("trace")
|
||||||
defer os.Remove("LICENSE")
|
defer os.Remove("LICENSE")
|
||||||
|
|
@ -268,17 +89,14 @@ func TestCrocLocal(t *testing.T) {
|
||||||
log.Debug("setting up sender")
|
log.Debug("setting up sender")
|
||||||
sender, err := New(Options{
|
sender, err := New(Options{
|
||||||
IsSender: true,
|
IsSender: true,
|
||||||
SharedSecret: "8123-testingthecroc",
|
SharedSecret: "test",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
RelayAddress: "127.0.0.1:8181",
|
RelayAddress: "localhost:8181",
|
||||||
RelayPorts: []string{"8181", "8182"},
|
RelayPorts: []string{"8181", "8182"},
|
||||||
RelayPassword: "pass123",
|
RelayPassword: "pass123",
|
||||||
Stdout: true,
|
Stdout: true,
|
||||||
NoPrompt: true,
|
NoPrompt: true,
|
||||||
DisableLocal: false,
|
DisableLocal: false,
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
GitIgnore: false,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -288,15 +106,13 @@ func TestCrocLocal(t *testing.T) {
|
||||||
log.Debug("setting up receiver")
|
log.Debug("setting up receiver")
|
||||||
receiver, err := New(Options{
|
receiver, err := New(Options{
|
||||||
IsSender: false,
|
IsSender: false,
|
||||||
SharedSecret: "8123-testingthecroc",
|
SharedSecret: "test",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
RelayAddress: "127.0.0.1:8181",
|
RelayAddress: "localhost:8181",
|
||||||
RelayPassword: "pass123",
|
RelayPassword: "pass123",
|
||||||
Stdout: true,
|
Stdout: true,
|
||||||
NoPrompt: true,
|
NoPrompt: true,
|
||||||
DisableLocal: false,
|
DisableLocal: false,
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -306,11 +122,10 @@ func TestCrocLocal(t *testing.T) {
|
||||||
os.Create("touched")
|
os.Create("touched")
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go func() {
|
go func() {
|
||||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{"../../LICENSE", "touched"}, false, false)
|
err := sender.Send(TransferOptions{
|
||||||
if errGet != nil {
|
PathToFiles: []string{"../../LICENSE", "touched"},
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
KeepPathInRemote: false,
|
||||||
}
|
})
|
||||||
err := sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("send failed: %v", err)
|
t.Errorf("send failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
@ -330,17 +145,17 @@ func TestCrocLocal(t *testing.T) {
|
||||||
|
|
||||||
func TestCrocError(t *testing.T) {
|
func TestCrocError(t *testing.T) {
|
||||||
content := []byte("temporary file's content")
|
content := []byte("temporary file's content")
|
||||||
tmpfile, err := os.CreateTemp("", "example")
|
tmpfile, err := ioutil.TempFile("", "example")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer os.Remove(tmpfile.Name()) // clean up
|
defer os.Remove(tmpfile.Name()) // clean up
|
||||||
|
|
||||||
if _, err = tmpfile.Write(content); err != nil {
|
if _, err := tmpfile.Write(content); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err = tmpfile.Close(); err != nil {
|
if err := tmpfile.Close(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -348,7 +163,7 @@ func TestCrocError(t *testing.T) {
|
||||||
log.SetLevel("warn")
|
log.SetLevel("warn")
|
||||||
sender, _ := New(Options{
|
sender, _ := New(Options{
|
||||||
IsSender: true,
|
IsSender: true,
|
||||||
SharedSecret: "8123-testingthecroc2",
|
SharedSecret: "test33",
|
||||||
Debug: true,
|
Debug: true,
|
||||||
RelayAddress: "doesntexistok.com:8381",
|
RelayAddress: "doesntexistok.com:8381",
|
||||||
RelayPorts: []string{"8381", "8382"},
|
RelayPorts: []string{"8381", "8382"},
|
||||||
|
|
@ -356,45 +171,12 @@ func TestCrocError(t *testing.T) {
|
||||||
Stdout: true,
|
Stdout: true,
|
||||||
NoPrompt: true,
|
NoPrompt: true,
|
||||||
DisableLocal: true,
|
DisableLocal: true,
|
||||||
Curve: "siec",
|
|
||||||
Overwrite: true,
|
|
||||||
})
|
})
|
||||||
filesInfo, emptyFolders, totalNumberFolders, errGet := GetFilesInfo([]string{tmpfile.Name()}, false, false)
|
err = sender.Send(TransferOptions{
|
||||||
if errGet != nil {
|
PathToFiles: []string{tmpfile.Name()},
|
||||||
t.Errorf("failed to get minimal info: %v", errGet)
|
KeepPathInRemote: true,
|
||||||
}
|
})
|
||||||
err = sender.Send(filesInfo, emptyFolders, totalNumberFolders)
|
|
||||||
log.Debug(err)
|
log.Debug(err)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
|
||||||
|
|
||||||
func TestCleanUp(t *testing.T) {
|
|
||||||
// windows allows files to be deleted only if they
|
|
||||||
// are not open by another program so the remove actions
|
|
||||||
// from the above tests will not always do a good clean up
|
|
||||||
// This "test" will make sure
|
|
||||||
operatingSystem := runtime.GOOS
|
|
||||||
log.Debugf("The operating system is %s", operatingSystem)
|
|
||||||
if operatingSystem == "windows" {
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
log.Debug("Full cleanup")
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for _, file := range []string{"README.md", "./README.md"} {
|
|
||||||
err = os.Remove(file)
|
|
||||||
if err == nil {
|
|
||||||
log.Debugf("Successfully purged %s", file)
|
|
||||||
} else {
|
|
||||||
log.Debugf("%s was already purged.", file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, folder := range []string{"./testEmpty", "./link-in-folder"} {
|
|
||||||
err = os.RemoveAll(folder)
|
|
||||||
if err == nil {
|
|
||||||
log.Debugf("Successfully purged %s", folder)
|
|
||||||
} else {
|
|
||||||
log.Debugf("%s was already purged.", folder)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"golang.org/x/crypto/argon2"
|
|
||||||
"golang.org/x/crypto/chacha20poly1305"
|
|
||||||
"golang.org/x/crypto/pbkdf2"
|
"golang.org/x/crypto/pbkdf2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -39,7 +37,7 @@ func Encrypt(plaintext []byte, key []byte) (encrypted []byte, err error) {
|
||||||
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
|
||||||
// Section 8.2
|
// Section 8.2
|
||||||
ivBytes := make([]byte, 12)
|
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)
|
log.Fatalf("can't initialize crypto: %v", err)
|
||||||
}
|
}
|
||||||
b, err := aes.NewCipher(key)
|
b, err := aes.NewCipher(key)
|
||||||
|
|
@ -72,54 +70,3 @@ func Decrypt(encrypted []byte, key []byte) (plaintext []byte, err error) {
|
||||||
plaintext, err = aesgcm.Open(nil, encrypted[:12], encrypted[12:], nil)
|
plaintext, err = aesgcm.Open(nil, encrypted[:12], encrypted[12:], nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArgon2 generates a new key based on a passphrase and salt
|
|
||||||
// using argon2
|
|
||||||
// https://pkg.go.dev/golang.org/x/crypto/argon2
|
|
||||||
func NewArgon2(passphrase []byte, usersalt []byte) (aead cipher.AEAD, salt []byte, err error) {
|
|
||||||
if len(passphrase) < 1 {
|
|
||||||
err = fmt.Errorf("need more than that for passphrase")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if usersalt == nil {
|
|
||||||
salt = make([]byte, 8)
|
|
||||||
// http://www.ietf.org/rfc/rfc2898.txt
|
|
||||||
// Salt.
|
|
||||||
if _, err = rand.Read(salt); err != nil {
|
|
||||||
log.Fatalf("can't get random salt: %v", err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
salt = usersalt
|
|
||||||
}
|
|
||||||
aead, err = chacha20poly1305.NewX(argon2.IDKey(passphrase, salt, 1, 64*1024, 4, 32))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key
|
|
||||||
// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
|
|
||||||
func EncryptChaCha(plaintext []byte, aead cipher.AEAD) (encrypted []byte, err error) {
|
|
||||||
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+len(plaintext)+aead.Overhead())
|
|
||||||
if _, err := rand.Read(nonce); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt the message and append the ciphertext to the nonce.
|
|
||||||
encrypted = aead.Seal(nonce, nonce, plaintext, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptChaCha will encrypt ChaCha20-Poly1305 using the pre-generated key
|
|
||||||
// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
|
|
||||||
func DecryptChaCha(encryptedMsg []byte, aead cipher.AEAD) (encrypted []byte, err error) {
|
|
||||||
if len(encryptedMsg) < aead.NonceSize() {
|
|
||||||
err = fmt.Errorf("ciphertext too short")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split nonce and ciphertext.
|
|
||||||
nonce, ciphertext := encryptedMsg[:aead.NonceSize()], encryptedMsg[aead.NonceSize():]
|
|
||||||
|
|
||||||
// Decrypt the message and check it wasn't tampered with.
|
|
||||||
encrypted, err = aead.Open(nil, nonce, ciphertext, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package crypt
|
package crypt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
@ -24,37 +23,6 @@ func BenchmarkDecrypt(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkNewPbkdf2(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
New([]byte("password"), nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNewArgon2(b *testing.B) {
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
NewArgon2([]byte("password"), nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkEncryptChaCha(b *testing.B) {
|
|
||||||
bob, _, _ := NewArgon2([]byte("password"), nil)
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
EncryptChaCha([]byte("hello, world"), bob)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkDecryptChaCha(b *testing.B) {
|
|
||||||
key, _, _ := NewArgon2([]byte("password"), nil)
|
|
||||||
msg := []byte("hello, world")
|
|
||||||
enc, _ := EncryptChaCha(msg, key)
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
DecryptChaCha(enc, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEncryption(t *testing.T) {
|
func TestEncryption(t *testing.T) {
|
||||||
key, salt, err := New([]byte("password"), nil)
|
key, salt, err := New([]byte("password"), nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
@ -66,54 +34,22 @@ func TestEncryption(t *testing.T) {
|
||||||
assert.Equal(t, msg, dec)
|
assert.Equal(t, msg, dec)
|
||||||
|
|
||||||
// check reusing the salt
|
// check reusing the salt
|
||||||
key2, _, _ := New([]byte("password"), salt)
|
key2, _, err := New([]byte("password"), salt)
|
||||||
dec, err = Decrypt(enc, key2)
|
dec, err = Decrypt(enc, key2)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, msg, dec)
|
assert.Equal(t, msg, dec)
|
||||||
|
|
||||||
// check reusing the salt
|
// check reusing the salt
|
||||||
key2, _, _ = New([]byte("wrong password"), salt)
|
key2, _, err = New([]byte("wrong password"), salt)
|
||||||
dec, err = Decrypt(enc, key2)
|
dec, err = Decrypt(enc, key2)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.NotEqual(t, msg, dec)
|
assert.NotEqual(t, msg, dec)
|
||||||
|
|
||||||
// error with no password
|
// error with no password
|
||||||
_, err = Decrypt([]byte(""), key)
|
dec, err = Decrypt([]byte(""), key)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
// error with small password
|
// error with small password
|
||||||
_, _, err = New([]byte(""), nil)
|
_, _, err = New([]byte(""), nil)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncryptionChaCha(t *testing.T) {
|
|
||||||
key, salt, err := NewArgon2([]byte("password"), nil)
|
|
||||||
fmt.Printf("key: %x\n", key)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
msg := []byte("hello, world")
|
|
||||||
enc, err := EncryptChaCha(msg, key)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
dec, err := DecryptChaCha(enc, key)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, msg, dec)
|
|
||||||
|
|
||||||
// check reusing the salt
|
|
||||||
key2, _, _ := NewArgon2([]byte("password"), salt)
|
|
||||||
dec, err = DecryptChaCha(enc, key2)
|
|
||||||
assert.Nil(t, err)
|
|
||||||
assert.Equal(t, msg, dec)
|
|
||||||
|
|
||||||
// check reusing the salt
|
|
||||||
key2, _, _ = NewArgon2([]byte("wrong password"), salt)
|
|
||||||
dec, err = DecryptChaCha(enc, key2)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
assert.NotEqual(t, msg, dec)
|
|
||||||
|
|
||||||
// error with no password
|
|
||||||
_, err = DecryptChaCha([]byte(""), key)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
|
|
||||||
// error with small password
|
|
||||||
_, _, err = NewArgon2([]byte(""), nil)
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
//go:build !windows
|
|
||||||
// +build !windows
|
|
||||||
|
|
||||||
package diskusage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DiskUsage contains usage data and provides user-friendly access methods
|
|
||||||
type DiskUsage struct {
|
|
||||||
stat *unix.Statfs_t
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDiskUsage returns an object holding the disk usage of volumePath
|
|
||||||
// or nil in case of error (invalid path, etc)
|
|
||||||
func NewDiskUsage(volumePath string) *DiskUsage {
|
|
||||||
stat := unix.Statfs_t{}
|
|
||||||
err := unix.Statfs(volumePath, &stat)
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return &DiskUsage{&stat}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free returns total free bytes on file system
|
|
||||||
func (du *DiskUsage) Free() uint64 {
|
|
||||||
return uint64(du.stat.Bfree) * uint64(du.stat.Bsize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Available return total available bytes on file system to an unprivileged user
|
|
||||||
func (du *DiskUsage) Available() uint64 {
|
|
||||||
return uint64(du.stat.Bavail) * uint64(du.stat.Bsize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns total size of the file system
|
|
||||||
func (du *DiskUsage) Size() uint64 {
|
|
||||||
return uint64(du.stat.Blocks) * uint64(du.stat.Bsize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used returns total bytes used in file system
|
|
||||||
func (du *DiskUsage) Used() uint64 {
|
|
||||||
return du.Size() - du.Free()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage returns percentage of use on the file system
|
|
||||||
func (du *DiskUsage) Usage() float32 {
|
|
||||||
return float32(du.Used()) / float32(du.Size())
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package diskusage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
var KB = uint64(1024)
|
|
||||||
|
|
||||||
func TestNewDiskUsage(t *testing.T) {
|
|
||||||
usage := NewDiskUsage(".")
|
|
||||||
fmt.Println("Free:", usage.Free()/(KB*KB))
|
|
||||||
fmt.Println("Available:", usage.Available()/(KB*KB))
|
|
||||||
fmt.Println("Size:", usage.Size()/(KB*KB))
|
|
||||||
fmt.Println("Used:", usage.Used()/(KB*KB))
|
|
||||||
fmt.Println("Usage:", usage.Usage()*100, "%")
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package diskusage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"golang.org/x/sys/windows"
|
|
||||||
)
|
|
||||||
|
|
||||||
type DiskUsage struct {
|
|
||||||
freeBytes int64
|
|
||||||
totalBytes int64
|
|
||||||
availBytes int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewDiskUsage returns an object holding the disk usage of volumePath
|
|
||||||
// or nil in case of error (invalid path, etc)
|
|
||||||
func NewDiskUsage(volumePath string) *DiskUsage {
|
|
||||||
h := windows.MustLoadDLL("kernel32.dll")
|
|
||||||
c := h.MustFindProc("GetDiskFreeSpaceExW")
|
|
||||||
|
|
||||||
du := &DiskUsage{}
|
|
||||||
|
|
||||||
c.Call(
|
|
||||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(volumePath))),
|
|
||||||
uintptr(unsafe.Pointer(&du.freeBytes)),
|
|
||||||
uintptr(unsafe.Pointer(&du.totalBytes)),
|
|
||||||
uintptr(unsafe.Pointer(&du.availBytes)))
|
|
||||||
|
|
||||||
return du
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free returns total free bytes on file system
|
|
||||||
func (du *DiskUsage) Free() uint64 {
|
|
||||||
return uint64(du.freeBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Available returns total available bytes on file system to an unprivileged user
|
|
||||||
func (du *DiskUsage) Available() uint64 {
|
|
||||||
return uint64(du.availBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Size returns total size of the file system
|
|
||||||
func (du *DiskUsage) Size() uint64 {
|
|
||||||
return uint64(du.totalBytes)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used returns total bytes used in file system
|
|
||||||
func (du *DiskUsage) Used() uint64 {
|
|
||||||
return du.Size() - du.Free()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage returns percentage of use on the file system
|
|
||||||
func (du *DiskUsage) Usage() float32 {
|
|
||||||
return float32(du.Used()) / float32(du.Size())
|
|
||||||
}
|
|
||||||
|
|
@ -13,7 +13,5 @@ release:
|
||||||
cd ../../ && ./src/install/upload-src-tarball.sh
|
cd ../../ && ./src/install/upload-src-tarball.sh
|
||||||
|
|
||||||
test:
|
test:
|
||||||
cp zsh_autocomplete ../../
|
|
||||||
cp bash_autocomplete ../../
|
|
||||||
cd ../../ && go generate
|
cd ../../ && go generate
|
||||||
cd ../../ && goreleaser release --skip-publish
|
cd ../../ && goreleaser release --skip-publish
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
#! /bin/bash
|
||||||
|
|
||||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||||
|
|
||||||
_cli_bash_autocomplete() {
|
_cli_bash_autocomplete() {
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,7 @@ print_help() {
|
||||||
Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android)
|
Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android)
|
||||||
|
|
||||||
-h
|
-h
|
||||||
Prints this helpful message and exit."
|
Prints this helpfull message and exit."
|
||||||
|
|
||||||
echo "${help_header}"
|
echo "${help_header}"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
@ -156,10 +156,10 @@ make_tempdir() {
|
||||||
|
|
||||||
#--- FUNCTION ----------------------------------------------------------------
|
#--- FUNCTION ----------------------------------------------------------------
|
||||||
# NAME: determine_os
|
# NAME: determine_os
|
||||||
# DESCRIPTION: Attempts to determine host os using uname
|
# DESCRIPTION: Attempts to determin host os using uname
|
||||||
# PARAMETERS: none
|
# PARAMETERS: none
|
||||||
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
|
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
|
||||||
# 1 = Unknown OS
|
# 1 = Unkown OS
|
||||||
# 20 = 'uname' not found in path
|
# 20 = 'uname' not found in path
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
determine_os() {
|
determine_os() {
|
||||||
|
|
@ -180,10 +180,10 @@ determine_os() {
|
||||||
|
|
||||||
#--- FUNCTION ----------------------------------------------------------------
|
#--- FUNCTION ----------------------------------------------------------------
|
||||||
# NAME: determine_arch
|
# NAME: determine_arch
|
||||||
# DESCRIPTION: Attempt to determine architecture of host
|
# DESCRIPTION: Attempt to determin architecture of host
|
||||||
# PARAMETERS: none
|
# PARAMETERS: none
|
||||||
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
|
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
|
||||||
# 1 = Unknown arch
|
# 1 = Unkown arch
|
||||||
# 20 = 'uname' not found in path
|
# 20 = 'uname' not found in path
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
determine_arch() {
|
determine_arch() {
|
||||||
|
|
@ -238,7 +238,7 @@ download_file() {
|
||||||
#--- FUNCTION ----------------------------------------------------------------
|
#--- FUNCTION ----------------------------------------------------------------
|
||||||
# NAME: checksum_check
|
# NAME: checksum_check
|
||||||
# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure
|
# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure
|
||||||
# integrity. Tries multiple tools before failing.
|
# integrity. Tries multiple tools before faling.
|
||||||
# PARAMETERS: $1 = path to checksum file
|
# PARAMETERS: $1 = path to checksum file
|
||||||
# $2 = location of file to check
|
# $2 = location of file to check
|
||||||
# $3 = working directory
|
# $3 = working directory
|
||||||
|
|
@ -300,10 +300,10 @@ checksum_check() {
|
||||||
#--- FUNCTION ----------------------------------------------------------------
|
#--- FUNCTION ----------------------------------------------------------------
|
||||||
# NAME: extract_file
|
# NAME: extract_file
|
||||||
# DESCRIPTION: Extracts a file into a location. Attempts to determine which
|
# DESCRIPTION: Extracts a file into a location. Attempts to determine which
|
||||||
# tool to use by checking file extension.
|
# tool to use by checking file extention.
|
||||||
# PARAMETERS: $1 = file to extract
|
# PARAMETERS: $1 = file to extract
|
||||||
# $2 = location to extract file into
|
# $2 = location to extract file into
|
||||||
# $3 = extension
|
# $3 = extention
|
||||||
# RETURNS: Return code of the tool used to extract the file
|
# RETURNS: Return code of the tool used to extract the file
|
||||||
# 20 = Failed to determine which tool to use
|
# 20 = Failed to determine which tool to use
|
||||||
# 30 = Failed to find tool in path
|
# 30 = Failed to find tool in path
|
||||||
|
|
@ -528,7 +528,7 @@ main() {
|
||||||
local autocomplete_install_rcode
|
local autocomplete_install_rcode
|
||||||
|
|
||||||
croc_bin_name="croc"
|
croc_bin_name="croc"
|
||||||
croc_version="10.1.1"
|
croc_version="8.6.7"
|
||||||
croc_dl_ext="tar.gz"
|
croc_dl_ext="tar.gz"
|
||||||
croc_base_url="https://github.com/schollz/croc/releases/download"
|
croc_base_url="https://github.com/schollz/croc/releases/download"
|
||||||
prefix="${1}"
|
prefix="${1}"
|
||||||
|
|
@ -577,9 +577,6 @@ main() {
|
||||||
|
|
||||||
case "${croc_os}" in
|
case "${croc_os}" in
|
||||||
"Darwin" ) croc_os="macOS";;
|
"Darwin" ) croc_os="macOS";;
|
||||||
*"BusyBox"* )
|
|
||||||
croc_os="Linux"
|
|
||||||
;;
|
|
||||||
"CYGWIN"* ) croc_os="Windows";
|
"CYGWIN"* ) croc_os="Windows";
|
||||||
croc_dl_ext="zip";
|
croc_dl_ext="zip";
|
||||||
print_message "== Cygwin is currently unsupported." "error";
|
print_message "== Cygwin is currently unsupported." "error";
|
||||||
|
|
@ -590,19 +587,16 @@ main() {
|
||||||
"x86_64" ) croc_arch="64bit";;
|
"x86_64" ) croc_arch="64bit";;
|
||||||
"amd64" ) croc_arch="64bit";;
|
"amd64" ) croc_arch="64bit";;
|
||||||
"aarch64" ) croc_arch="ARM64";;
|
"aarch64" ) croc_arch="ARM64";;
|
||||||
"arm64" ) croc_arch="ARM64";;
|
|
||||||
"armv7l" ) croc_arch="ARM";;
|
"armv7l" ) croc_arch="ARM";;
|
||||||
"armv8l" ) croc_arch="ARM";;
|
|
||||||
"armv9l" ) croc_arch="ARM";;
|
|
||||||
"i686" ) croc_arch="32bit";;
|
"i686" ) croc_arch="32bit";;
|
||||||
* ) croc_arch="unknown";;
|
* ) croc_arch="unknown";;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
croc_file="${croc_bin_name}_v${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
|
||||||
croc_checksum_file="${croc_bin_name}_v${croc_version}_checksums.txt"
|
croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt"
|
||||||
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
|
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
|
||||||
croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_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 "${croc_url}" "${tmpdir}" "${croc_file}"
|
||||||
download_file_rcode="${?}"
|
download_file_rcode="${?}"
|
||||||
if [[ "${download_file_rcode}" == "0" ]]; then
|
if [[ "${download_file_rcode}" == "0" ]]; then
|
||||||
|
|
@ -690,7 +684,7 @@ main() {
|
||||||
print_message "== Install prefix already exists. No need to create it." "info"
|
print_message "== Install prefix already exists. No need to create it." "info"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ ! -d "${bash_autocomplete_prefix}/croc" ] && mkdir -p "${bash_autocomplete_prefix}/croc" >/dev/null 2>&1
|
|
||||||
case "${croc_os}" in
|
case "${croc_os}" in
|
||||||
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
|
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
|
||||||
install_file_rcode="${?}";;
|
install_file_rcode="${?}";;
|
||||||
|
|
@ -718,27 +712,26 @@ main() {
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# case "$(basename ${SHELL})" in
|
case "$(basename ${SHELL})" in
|
||||||
# "bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
"bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
|
||||||
# autocomplete_install_rcode="${?}";;
|
autocomplete_install_rcode="${?}";;
|
||||||
# "zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
"zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
|
||||||
# autocomplete_install_rcode="${?}";
|
autocomplete_install_rcode="${?}";
|
||||||
# print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
|
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";;
|
print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
|
||||||
# *) autocomplete_install_rcode="1";;
|
esac
|
||||||
# esac
|
|
||||||
|
|
||||||
# if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
|
||||||
# print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
|
||||||
# elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
|
||||||
# print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
print_message "== Failed to install ${bash_autocomplete_file}" "error"
|
||||||
# elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
|
||||||
# print_message "== Failed to locate 'install' command" "error"
|
print_message "== Failed to locate 'install' command" "error"
|
||||||
# elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
|
||||||
# print_message "== Failed to locate 'sudo' command" "error"
|
print_message "== Failed to locate 'sudo' command" "error"
|
||||||
# else
|
else
|
||||||
# print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
|
||||||
# fi
|
fi
|
||||||
|
|
||||||
print_message "== Installation complete" "ok"
|
print_message "== Installation complete" "ok"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -42,7 +43,7 @@ func run() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceInFile(fname, start, end, replacement string) (err error) {
|
func replaceInFile(fname, start, end, replacement string) (err error) {
|
||||||
b, err := os.ReadFile(fname)
|
b, err := ioutil.ReadFile(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +58,7 @@ func replaceInFile(fname, start, end, replacement string) (err error) {
|
||||||
fmt.Sprintf("%s%s%s", start, replacement, end),
|
fmt.Sprintf("%s%s%s", start, replacement, end),
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
err = os.WriteFile(fname, []byte(newF), 0o644)
|
err = ioutil.WriteFile(fname, []byte(newF), 0644)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,32 +3,17 @@ package message
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v10/src/compress"
|
"github.com/schollz/croc/v8/src/compress"
|
||||||
"github.com/schollz/croc/v10/src/crypt"
|
"github.com/schollz/croc/v8/src/crypt"
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type is a message type
|
|
||||||
type Type string
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypePAKE Type = "pake"
|
|
||||||
TypeExternalIP Type = "externalip"
|
|
||||||
TypeFinished Type = "finished"
|
|
||||||
TypeError Type = "error"
|
|
||||||
TypeCloseRecipient Type = "close-recipient"
|
|
||||||
TypeCloseSender Type = "close-sender"
|
|
||||||
TypeRecipientReady Type = "recipientready"
|
|
||||||
TypeFileInfo Type = "fileinfo"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Message is the possible payload for messaging
|
// Message is the possible payload for messaging
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Type Type `json:"t,omitempty"`
|
Type string `json:"t,omitempty"`
|
||||||
Message string `json:"m,omitempty"`
|
Message string `json:"m,omitempty"`
|
||||||
Bytes []byte `json:"b,omitempty"`
|
Bytes []byte `json:"b,omitempty"`
|
||||||
Bytes2 []byte `json:"b2,omitempty"`
|
|
||||||
Num int `json:"n,omitempty"`
|
Num int `json:"n,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +43,7 @@ func Encode(key []byte, m Message) (b []byte, err error) {
|
||||||
log.Debugf("writing %s message (encrypted)", m.Type)
|
log.Debugf("writing %s message (encrypted)", m.Type)
|
||||||
b, err = crypt.Encrypt(b, key)
|
b, err = crypt.Encrypt(b, key)
|
||||||
} else {
|
} else {
|
||||||
log.Debugf("writing %s message (unencrypted)", m.Type)
|
log.Debugf("writing %s message", m.Type)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -73,12 +58,5 @@ func Decode(key []byte, b []byte) (m Message, err error) {
|
||||||
}
|
}
|
||||||
b = compress.Decompress(b)
|
b = compress.Decompress(b)
|
||||||
err = json.Unmarshal(b, &m)
|
err = json.Unmarshal(b, &m)
|
||||||
if err == nil {
|
|
||||||
if key != nil {
|
|
||||||
log.Debugf("read %s message (encrypted)", m.Type)
|
|
||||||
} else {
|
|
||||||
log.Debugf("read %s message (unencrypted)", m.Type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,18 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v10/src/crypt"
|
"github.com/schollz/croc/v8/src/crypt"
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
var TypeMessage Type = "message"
|
|
||||||
|
|
||||||
func TestMessage(t *testing.T) {
|
func TestMessage(t *testing.T) {
|
||||||
log.SetLevel("debug")
|
log.SetLevel("debug")
|
||||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
m := Message{Type: "message", Message: "hello, world"}
|
||||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
fmt.Println(string(salt))
|
fmt.Println(salt)
|
||||||
b, err := Encode(e, m)
|
b, err := Encode(e, m)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
fmt.Printf("%x\n", b)
|
fmt.Printf("%x\n", b)
|
||||||
|
|
@ -37,7 +35,7 @@ func TestMessage(t *testing.T) {
|
||||||
|
|
||||||
func TestMessageNoPass(t *testing.T) {
|
func TestMessageNoPass(t *testing.T) {
|
||||||
log.SetLevel("debug")
|
log.SetLevel("debug")
|
||||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
m := Message{Type: "message", Message: "hello, world"}
|
||||||
b, err := Encode(nil, m)
|
b, err := Encode(nil, m)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
fmt.Printf("%x\n", b)
|
fmt.Printf("%x\n", b)
|
||||||
|
|
@ -67,7 +65,7 @@ func TestSend(t *testing.T) {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
log.Debugf("client %s connected", connection.RemoteAddr().String())
|
||||||
go func(_ string, connection net.Conn) {
|
go func(port string, connection net.Conn) {
|
||||||
c := comm.New(connection)
|
c := comm.New(connection)
|
||||||
err = c.Send([]byte("hello, world"))
|
err = c.Send([]byte("hello, world"))
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
@ -84,10 +82,10 @@ func TestSend(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(800 * time.Millisecond)
|
time.Sleep(300 * time.Millisecond)
|
||||||
a, err := comm.NewConnection("127.0.0.1:"+port, 10*time.Minute)
|
a, err := comm.NewConnection("localhost:"+port, 10*time.Minute)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
m := Message{Type: TypeMessage, Message: "hello, world"}
|
m := Message{Type: "message", Message: "hello, world"}
|
||||||
e, salt, err := crypt.New([]byte("pass"), nil)
|
e, salt, err := crypt.New([]byte("pass"), nil)
|
||||||
log.Debug(salt)
|
log.Debug(salt)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
// From GitHub version/fork maintained by Stephen Paul Weber available at:
|
|
||||||
// https://github.com/singpolyma/mnemonicode
|
|
||||||
//
|
|
||||||
// Originally from:
|
|
||||||
// http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2000 Oren Tirosh <oren@hishome.net>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mnemonicode
|
|
||||||
|
|
||||||
const base = 1626
|
|
||||||
|
|
||||||
// WordsRequired returns the number of words required to encode input
|
|
||||||
// data of length bytes using mnomonic encoding.
|
|
||||||
//
|
|
||||||
// Every four bytes of input is encoded into three words. If there
|
|
||||||
// is an extra one or two bytes they get an extra one or two words
|
|
||||||
// respectively. If there is an extra three bytes, they will be encoded
|
|
||||||
// into three words with the last word being one of a small set of very
|
|
||||||
// short words (only needed to encode the last 3 bits).
|
|
||||||
func WordsRequired(length int) int {
|
|
||||||
return ((length + 1) * 3) / 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeWordList encodes src into mnemomic words which are appended to dst.
|
|
||||||
// The final wordlist is returned.
|
|
||||||
// There will be WordsRequired(len(src)) words appeneded.
|
|
||||||
func EncodeWordList(dst []string, src []byte) (result []string) {
|
|
||||||
if n := len(dst) + WordsRequired(len(src)); cap(dst) < n {
|
|
||||||
result = make([]string, len(dst), n)
|
|
||||||
copy(result, dst)
|
|
||||||
} else {
|
|
||||||
result = dst
|
|
||||||
}
|
|
||||||
|
|
||||||
var x uint32
|
|
||||||
for len(src) >= 4 {
|
|
||||||
x = uint32(src[0])
|
|
||||||
x |= uint32(src[1]) << 8
|
|
||||||
x |= uint32(src[2]) << 16
|
|
||||||
x |= uint32(src[3]) << 24
|
|
||||||
src = src[4:]
|
|
||||||
|
|
||||||
i0 := int(x % base)
|
|
||||||
i1 := int(x/base) % base
|
|
||||||
i2 := int(x/base/base) % base
|
|
||||||
result = append(result, WordList[i0], WordList[i1], WordList[i2])
|
|
||||||
}
|
|
||||||
if len(src) > 0 {
|
|
||||||
x = 0
|
|
||||||
for i := len(src) - 1; i >= 0; i-- {
|
|
||||||
x <<= 8
|
|
||||||
x |= uint32(src[i])
|
|
||||||
}
|
|
||||||
i := int(x % base)
|
|
||||||
result = append(result, WordList[i])
|
|
||||||
if len(src) >= 2 {
|
|
||||||
i = int(x/base) % base
|
|
||||||
result = append(result, WordList[i])
|
|
||||||
}
|
|
||||||
if len(src) == 3 {
|
|
||||||
i = base + int(x/base/base)%7
|
|
||||||
result = append(result, WordList[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
||||||
// From GitHub version/fork maintained by Stephen Paul Weber available at:
|
|
||||||
// https://github.com/singpolyma/mnemonicode
|
|
||||||
//
|
|
||||||
// Originally from:
|
|
||||||
// http://web.archive.org/web/20101031205747/http://www.tothink.com/mnemonic/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2000 Oren Tirosh <oren@hishome.net>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package mnemonicode
|
|
||||||
|
|
||||||
// WordListVersion is the version of compiled in word list.
|
|
||||||
const WordListVersion = "0.7"
|
|
||||||
|
|
||||||
var wordMap = make(map[string]int, len(WordList))
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for i, w := range WordList {
|
|
||||||
wordMap[w] = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const longestWord = 7
|
|
||||||
|
|
||||||
var WordList = []string{
|
|
||||||
"academy", "acrobat", "active", "actor", "adam", "admiral",
|
|
||||||
"adrian", "africa", "agenda", "agent", "airline", "airport",
|
|
||||||
"aladdin", "alarm", "alaska", "albert", "albino", "album",
|
|
||||||
"alcohol", "alex", "algebra", "alibi", "alice", "alien",
|
|
||||||
"alpha", "alpine", "amadeus", "amanda", "amazon", "amber",
|
|
||||||
"america", "amigo", "analog", "anatomy", "angel", "animal",
|
|
||||||
"antenna", "antonio", "apollo", "april", "archive", "arctic",
|
|
||||||
"arizona", "arnold", "aroma", "arthur", "artist", "asia",
|
|
||||||
"aspect", "aspirin", "athena", "athlete", "atlas", "audio",
|
|
||||||
"august", "austria", "axiom", "aztec", "balance", "ballad",
|
|
||||||
"banana", "bandit", "banjo", "barcode", "baron", "basic",
|
|
||||||
"battery", "belgium", "berlin", "bermuda", "bernard", "bikini",
|
|
||||||
"binary", "bingo", "biology", "block", "blonde", "bonus",
|
|
||||||
"boris", "boston", "boxer", "brandy", "bravo", "brazil",
|
|
||||||
"bronze", "brown", "bruce", "bruno", "burger", "burma",
|
|
||||||
"cabinet", "cactus", "cafe", "cairo", "cake", "calypso",
|
|
||||||
"camel", "camera", "campus", "canada", "canal", "cannon",
|
|
||||||
"canoe", "cantina", "canvas", "canyon", "capital", "caramel",
|
|
||||||
"caravan", "carbon", "cargo", "carlo", "carol", "carpet",
|
|
||||||
"cartel", "casino", "castle", "castro", "catalog", "caviar",
|
|
||||||
"cecilia", "cement", "center", "century", "ceramic", "chamber",
|
|
||||||
"chance", "change", "chaos", "charlie", "charm", "charter",
|
|
||||||
"chef", "chemist", "cherry", "chess", "chicago", "chicken",
|
|
||||||
"chief", "china", "cigar", "cinema", "circus", "citizen",
|
|
||||||
"city", "clara", "classic", "claudia", "clean", "client",
|
|
||||||
"climax", "clinic", "clock", "club", "cobra", "coconut",
|
|
||||||
"cola", "collect", "colombo", "colony", "color", "combat",
|
|
||||||
"comedy", "comet", "command", "compact", "company", "complex",
|
|
||||||
"concept", "concert", "connect", "consul", "contact", "context",
|
|
||||||
"contour", "control", "convert", "copy", "corner", "corona",
|
|
||||||
"correct", "cosmos", "couple", "courage", "cowboy", "craft",
|
|
||||||
"crash", "credit", "cricket", "critic", "crown", "crystal",
|
|
||||||
"cuba", "culture", "dallas", "dance", "daniel", "david",
|
|
||||||
"decade", "decimal", "deliver", "delta", "deluxe", "demand",
|
|
||||||
"demo", "denmark", "derby", "design", "detect", "develop",
|
|
||||||
"diagram", "dialog", "diamond", "diana", "diego", "diesel",
|
|
||||||
"diet", "digital", "dilemma", "diploma", "direct", "disco",
|
|
||||||
"disney", "distant", "doctor", "dollar", "dominic", "domino",
|
|
||||||
"donald", "dragon", "drama", "dublin", "duet", "dynamic",
|
|
||||||
"east", "ecology", "economy", "edgar", "egypt", "elastic",
|
|
||||||
"elegant", "element", "elite", "elvis", "email", "energy",
|
|
||||||
"engine", "english", "episode", "equator", "escort", "ethnic",
|
|
||||||
"europe", "everest", "evident", "exact", "example", "exit",
|
|
||||||
"exotic", "export", "express", "extra", "fabric", "factor",
|
|
||||||
"falcon", "family", "fantasy", "fashion", "fiber", "fiction",
|
|
||||||
"fidel", "fiesta", "figure", "film", "filter", "final",
|
|
||||||
"finance", "finish", "finland", "flash", "florida", "flower",
|
|
||||||
"fluid", "flute", "focus", "ford", "forest", "formal",
|
|
||||||
"format", "formula", "fortune", "forum", "fragile", "france",
|
|
||||||
"frank", "friend", "frozen", "future", "gabriel", "galaxy",
|
|
||||||
"gallery", "gamma", "garage", "garden", "garlic", "gemini",
|
|
||||||
"general", "genetic", "genius", "germany", "global", "gloria",
|
|
||||||
"golf", "gondola", "gong", "good", "gordon", "gorilla",
|
|
||||||
"grand", "granite", "graph", "green", "group", "guide",
|
|
||||||
"guitar", "guru", "hand", "happy", "harbor", "harmony",
|
|
||||||
"harvard", "havana", "hawaii", "helena", "hello", "henry",
|
|
||||||
"hilton", "history", "horizon", "hotel", "human", "humor",
|
|
||||||
"icon", "idea", "igloo", "igor", "image", "impact",
|
|
||||||
"import", "index", "india", "indigo", "input", "insect",
|
|
||||||
"instant", "iris", "italian", "jacket", "jacob", "jaguar",
|
|
||||||
"janet", "japan", "jargon", "jazz", "jeep", "john",
|
|
||||||
"joker", "jordan", "jumbo", "june", "jungle", "junior",
|
|
||||||
"jupiter", "karate", "karma", "kayak", "kermit", "kilo",
|
|
||||||
"king", "koala", "korea", "labor", "lady", "lagoon",
|
|
||||||
"laptop", "laser", "latin", "lava", "lecture", "left",
|
|
||||||
"legal", "lemon", "level", "lexicon", "liberal", "libra",
|
|
||||||
"limbo", "limit", "linda", "linear", "lion", "liquid",
|
|
||||||
"liter", "little", "llama", "lobby", "lobster", "local",
|
|
||||||
"logic", "logo", "lola", "london", "lotus", "lucas",
|
|
||||||
"lunar", "machine", "macro", "madam", "madonna", "madrid",
|
|
||||||
"maestro", "magic", "magnet", "magnum", "major", "mama",
|
|
||||||
"mambo", "manager", "mango", "manila", "marco", "marina",
|
|
||||||
"market", "mars", "martin", "marvin", "master", "matrix",
|
|
||||||
"maximum", "media", "medical", "mega", "melody", "melon",
|
|
||||||
"memo", "mental", "mentor", "menu", "mercury", "message",
|
|
||||||
"metal", "meteor", "meter", "method", "metro", "mexico",
|
|
||||||
"miami", "micro", "million", "mineral", "minimum", "minus",
|
|
||||||
"minute", "miracle", "mirage", "miranda", "mister", "mixer",
|
|
||||||
"mobile", "model", "modem", "modern", "modular", "moment",
|
|
||||||
"monaco", "monica", "monitor", "mono", "monster", "montana",
|
|
||||||
"morgan", "motel", "motif", "motor", "mozart", "multi",
|
|
||||||
"museum", "music", "mustang", "natural", "neon", "nepal",
|
|
||||||
"neptune", "nerve", "neutral", "nevada", "news", "ninja",
|
|
||||||
"nirvana", "normal", "nova", "novel", "nuclear", "numeric",
|
|
||||||
"nylon", "oasis", "object", "observe", "ocean", "octopus",
|
|
||||||
"olivia", "olympic", "omega", "opera", "optic", "optimal",
|
|
||||||
"orange", "orbit", "organic", "orient", "origin", "orlando",
|
|
||||||
"oscar", "oxford", "oxygen", "ozone", "pablo", "pacific",
|
|
||||||
"pagoda", "palace", "pamela", "panama", "panda", "panel",
|
|
||||||
"panic", "paradox", "pardon", "paris", "parker", "parking",
|
|
||||||
"parody", "partner", "passage", "passive", "pasta", "pastel",
|
|
||||||
"patent", "patriot", "patrol", "patron", "pegasus", "pelican",
|
|
||||||
"penguin", "pepper", "percent", "perfect", "perfume", "period",
|
|
||||||
"permit", "person", "peru", "phone", "photo", "piano",
|
|
||||||
"picasso", "picnic", "picture", "pigment", "pilgrim", "pilot",
|
|
||||||
"pirate", "pixel", "pizza", "planet", "plasma", "plaster",
|
|
||||||
"plastic", "plaza", "pocket", "poem", "poetic", "poker",
|
|
||||||
"polaris", "police", "politic", "polo", "polygon", "pony",
|
|
||||||
"popcorn", "popular", "postage", "postal", "precise", "prefix",
|
|
||||||
"premium", "present", "price", "prince", "printer", "prism",
|
|
||||||
"private", "product", "profile", "program", "project", "protect",
|
|
||||||
"proton", "public", "pulse", "puma", "pyramid", "queen",
|
|
||||||
"radar", "radio", "random", "rapid", "rebel", "record",
|
|
||||||
"recycle", "reflex", "reform", "regard", "regular", "relax",
|
|
||||||
"report", "reptile", "reverse", "ricardo", "ringo", "ritual",
|
|
||||||
"robert", "robot", "rocket", "rodeo", "romeo", "royal",
|
|
||||||
"russian", "safari", "salad", "salami", "salmon", "salon",
|
|
||||||
"salute", "samba", "sandra", "santana", "sardine", "school",
|
|
||||||
"screen", "script", "second", "secret", "section", "segment",
|
|
||||||
"select", "seminar", "senator", "senior", "sensor", "serial",
|
|
||||||
"service", "sheriff", "shock", "sierra", "signal", "silicon",
|
|
||||||
"silver", "similar", "simon", "single", "siren", "slogan",
|
|
||||||
"social", "soda", "solar", "solid", "solo", "sonic",
|
|
||||||
"soviet", "special", "speed", "spiral", "spirit", "sport",
|
|
||||||
"static", "station", "status", "stereo", "stone", "stop",
|
|
||||||
"street", "strong", "student", "studio", "style", "subject",
|
|
||||||
"sultan", "super", "susan", "sushi", "suzuki", "switch",
|
|
||||||
"symbol", "system", "tactic", "tahiti", "talent", "tango",
|
|
||||||
"tarzan", "taxi", "telex", "tempo", "tennis", "texas",
|
|
||||||
"textile", "theory", "thermos", "tiger", "titanic", "tokyo",
|
|
||||||
"tomato", "topic", "tornado", "toronto", "torpedo", "total",
|
|
||||||
"totem", "tourist", "tractor", "traffic", "transit", "trapeze",
|
|
||||||
"travel", "tribal", "trick", "trident", "trilogy", "tripod",
|
|
||||||
"tropic", "trumpet", "tulip", "tuna", "turbo", "twist",
|
|
||||||
"ultra", "uniform", "union", "uranium", "vacuum", "valid",
|
|
||||||
"vampire", "vanilla", "vatican", "velvet", "ventura", "venus",
|
|
||||||
"vertigo", "veteran", "victor", "video", "vienna", "viking",
|
|
||||||
"village", "vincent", "violet", "violin", "virtual", "virus",
|
|
||||||
"visa", "vision", "visitor", "visual", "vitamin", "viva",
|
|
||||||
"vocal", "vodka", "volcano", "voltage", "volume", "voyage",
|
|
||||||
"water", "weekend", "welcome", "western", "window", "winter",
|
|
||||||
"wizard", "wolf", "world", "xray", "yankee", "yoga",
|
|
||||||
"yogurt", "yoyo", "zebra", "zero", "zigzag", "zipper",
|
|
||||||
"zodiac", "zoom", "abraham", "action", "address", "alabama",
|
|
||||||
"alfred", "almond", "ammonia", "analyze", "annual", "answer",
|
|
||||||
"apple", "arena", "armada", "arsenal", "atlanta", "atomic",
|
|
||||||
"avenue", "average", "bagel", "baker", "ballet", "bambino",
|
|
||||||
"bamboo", "barbara", "basket", "bazaar", "benefit", "bicycle",
|
|
||||||
"bishop", "blitz", "bonjour", "bottle", "bridge", "british",
|
|
||||||
"brother", "brush", "budget", "cabaret", "cadet", "candle",
|
|
||||||
"capitan", "capsule", "career", "cartoon", "channel", "chapter",
|
|
||||||
"cheese", "circle", "cobalt", "cockpit", "college", "compass",
|
|
||||||
"comrade", "condor", "crimson", "cyclone", "darwin", "declare",
|
|
||||||
"degree", "delete", "delphi", "denver", "desert", "divide",
|
|
||||||
"dolby", "domain", "domingo", "double", "drink", "driver",
|
|
||||||
"eagle", "earth", "echo", "eclipse", "editor", "educate",
|
|
||||||
"edward", "effect", "electra", "emerald", "emotion", "empire",
|
|
||||||
"empty", "escape", "eternal", "evening", "exhibit", "expand",
|
|
||||||
"explore", "extreme", "ferrari", "first", "flag", "folio",
|
|
||||||
"forget", "forward", "freedom", "fresh", "friday", "fuji",
|
|
||||||
"galileo", "garcia", "genesis", "gold", "gravity", "habitat",
|
|
||||||
"hamlet", "harlem", "helium", "holiday", "house", "hunter",
|
|
||||||
"ibiza", "iceberg", "imagine", "infant", "isotope", "jackson",
|
|
||||||
"jamaica", "jasmine", "java", "jessica", "judo", "kitchen",
|
|
||||||
"lazarus", "letter", "license", "lithium", "loyal", "lucky",
|
|
||||||
"magenta", "mailbox", "manual", "marble", "mary", "maxwell",
|
|
||||||
"mayor", "milk", "monarch", "monday", "money", "morning",
|
|
||||||
"mother", "mystery", "native", "nectar", "nelson", "network",
|
|
||||||
"next", "nikita", "nobel", "nobody", "nominal", "norway",
|
|
||||||
"nothing", "number", "october", "office", "oliver", "opinion",
|
|
||||||
"option", "order", "outside", "package", "pancake", "pandora",
|
|
||||||
"panther", "papa", "patient", "pattern", "pedro", "pencil",
|
|
||||||
"people", "phantom", "philips", "pioneer", "pluto", "podium",
|
|
||||||
"portal", "potato", "prize", "process", "protein", "proxy",
|
|
||||||
"pump", "pupil", "python", "quality", "quarter", "quiet",
|
|
||||||
"rabbit", "radical", "radius", "rainbow", "ralph", "ramirez",
|
|
||||||
"ravioli", "raymond", "respect", "respond", "result", "resume",
|
|
||||||
"retro", "richard", "right", "risk", "river", "roger",
|
|
||||||
"roman", "rondo", "sabrina", "salary", "salsa", "sample",
|
|
||||||
"samuel", "saturn", "savage", "scarlet", "scoop", "scorpio",
|
|
||||||
"scratch", "scroll", "sector", "serpent", "shadow", "shampoo",
|
|
||||||
"sharon", "sharp", "short", "shrink", "silence", "silk",
|
|
||||||
"simple", "slang", "smart", "smoke", "snake", "society",
|
|
||||||
"sonar", "sonata", "soprano", "source", "sparta", "sphere",
|
|
||||||
"spider", "sponsor", "spring", "acid", "adios", "agatha",
|
|
||||||
"alamo", "alert", "almanac", "aloha", "andrea", "anita",
|
|
||||||
"arcade", "aurora", "avalon", "baby", "baggage", "balloon",
|
|
||||||
"bank", "basil", "begin", "biscuit", "blue", "bombay",
|
|
||||||
"brain", "brenda", "brigade", "cable", "carmen", "cello",
|
|
||||||
"celtic", "chariot", "chrome", "citrus", "civil", "cloud",
|
|
||||||
"common", "compare", "cool", "copper", "coral", "crater",
|
|
||||||
"cubic", "cupid", "cycle", "depend", "door", "dream",
|
|
||||||
"dynasty", "edison", "edition", "enigma", "equal", "eric",
|
|
||||||
"event", "evita", "exodus", "extend", "famous", "farmer",
|
|
||||||
"food", "fossil", "frog", "fruit", "geneva", "gentle",
|
|
||||||
"george", "giant", "gilbert", "gossip", "gram", "greek",
|
|
||||||
"grille", "hammer", "harvest", "hazard", "heaven", "herbert",
|
|
||||||
"heroic", "hexagon", "husband", "immune", "inca", "inch",
|
|
||||||
"initial", "isabel", "ivory", "jason", "jerome", "joel",
|
|
||||||
"joshua", "journal", "judge", "juliet", "jump", "justice",
|
|
||||||
"kimono", "kinetic", "leonid", "lima", "maze", "medusa",
|
|
||||||
"member", "memphis", "michael", "miguel", "milan", "mile",
|
|
||||||
"miller", "mimic", "mimosa", "mission", "monkey", "moral",
|
|
||||||
"moses", "mouse", "nancy", "natasha", "nebula", "nickel",
|
|
||||||
"nina", "noise", "orchid", "oregano", "origami", "orinoco",
|
|
||||||
"orion", "othello", "paper", "paprika", "prelude", "prepare",
|
|
||||||
"pretend", "profit", "promise", "provide", "puzzle", "remote",
|
|
||||||
"repair", "reply", "rival", "riviera", "robin", "rose",
|
|
||||||
"rover", "rudolf", "saga", "sahara", "scholar", "shelter",
|
|
||||||
"ship", "shoe", "sigma", "sister", "sleep", "smile",
|
|
||||||
"spain", "spark", "split", "spray", "square", "stadium",
|
|
||||||
"star", "storm", "story", "strange", "stretch", "stuart",
|
|
||||||
"subway", "sugar", "sulfur", "summer", "survive", "sweet",
|
|
||||||
"swim", "table", "taboo", "target", "teacher", "telecom",
|
|
||||||
"temple", "tibet", "ticket", "tina", "today", "toga",
|
|
||||||
"tommy", "tower", "trivial", "tunnel", "turtle", "twin",
|
|
||||||
"uncle", "unicorn", "unique", "update", "valery", "vega",
|
|
||||||
"version", "voodoo", "warning", "william", "wonder", "year",
|
|
||||||
"yellow", "young", "absent", "absorb", "accent", "alfonso",
|
|
||||||
"alias", "ambient", "andy", "anvil", "appear", "apropos",
|
|
||||||
"archer", "ariel", "armor", "arrow", "austin", "avatar",
|
|
||||||
"axis", "baboon", "bahama", "bali", "balsa", "bazooka",
|
|
||||||
"beach", "beast", "beatles", "beauty", "before", "benny",
|
|
||||||
"betty", "between", "beyond", "billy", "bison", "blast",
|
|
||||||
"bless", "bogart", "bonanza", "book", "border", "brave",
|
|
||||||
"bread", "break", "broken", "bucket", "buenos", "buffalo",
|
|
||||||
"bundle", "button", "buzzer", "byte", "caesar", "camilla",
|
|
||||||
"canary", "candid", "carrot", "cave", "chant", "child",
|
|
||||||
"choice", "chris", "cipher", "clarion", "clark", "clever",
|
|
||||||
"cliff", "clone", "conan", "conduct", "congo", "content",
|
|
||||||
"costume", "cotton", "cover", "crack", "current", "danube",
|
|
||||||
"data", "decide", "desire", "detail", "dexter", "dinner",
|
|
||||||
"dispute", "donor", "druid", "drum", "easy", "eddie",
|
|
||||||
"enjoy", "enrico", "epoxy", "erosion", "except", "exile",
|
|
||||||
"explain", "fame", "fast", "father", "felix", "field",
|
|
||||||
"fiona", "fire", "fish", "flame", "flex", "flipper",
|
|
||||||
"float", "flood", "floor", "forbid", "forever", "fractal",
|
|
||||||
"frame", "freddie", "front", "fuel", "gallop", "game",
|
|
||||||
"garbo", "gate", "gibson", "ginger", "giraffe", "gizmo",
|
|
||||||
"glass", "goblin", "gopher", "grace", "gray", "gregory",
|
|
||||||
"grid", "griffin", "ground", "guest", "gustav", "gyro",
|
|
||||||
"hair", "halt", "harris", "heart", "heavy", "herman",
|
|
||||||
"hippie", "hobby", "honey", "hope", "horse", "hostel",
|
|
||||||
"hydro", "imitate", "info", "ingrid", "inside", "invent",
|
|
||||||
"invest", "invite", "iron", "ivan", "james", "jester",
|
|
||||||
"jimmy", "join", "joseph", "juice", "julius", "july",
|
|
||||||
"justin", "kansas", "karl", "kevin", "kiwi", "ladder",
|
|
||||||
"lake", "laura", "learn", "legacy", "legend", "lesson",
|
|
||||||
"life", "light", "list", "locate", "lopez", "lorenzo",
|
|
||||||
"love", "lunch", "malta", "mammal", "margo", "marion",
|
|
||||||
"mask", "match", "mayday", "meaning", "mercy", "middle",
|
|
||||||
"mike", "mirror", "modest", "morph", "morris", "nadia",
|
|
||||||
"nato", "navy", "needle", "neuron", "never", "newton",
|
|
||||||
"nice", "night", "nissan", "nitro", "nixon", "north",
|
|
||||||
"oberon", "octavia", "ohio", "olga", "open", "opus",
|
|
||||||
"orca", "oval", "owner", "page", "paint", "palma",
|
|
||||||
"parade", "parent", "parole", "paul", "peace", "pearl",
|
|
||||||
"perform", "phoenix", "phrase", "pierre", "pinball", "place",
|
|
||||||
"plate", "plato", "plume", "pogo", "point", "polite",
|
|
||||||
"polka", "poncho", "powder", "prague", "press", "presto",
|
|
||||||
"pretty", "prime", "promo", "quasi", "quest", "quick",
|
|
||||||
"quiz", "quota", "race", "rachel", "raja", "ranger",
|
|
||||||
"region", "remark", "rent", "reward", "rhino", "ribbon",
|
|
||||||
"rider", "road", "rodent", "round", "rubber", "ruby",
|
|
||||||
"rufus", "sabine", "saddle", "sailor", "saint", "salt",
|
|
||||||
"satire", "scale", "scuba", "season", "secure", "shake",
|
|
||||||
"shallow", "shannon", "shave", "shelf", "sherman", "shine",
|
|
||||||
"shirt", "side", "sinatra", "sincere", "size", "slalom",
|
|
||||||
"slow", "small", "snow", "sofia", "song", "sound",
|
|
||||||
"south", "speech", "spell", "spend", "spoon", "stage",
|
|
||||||
"stamp", "stand", "state", "stella", "stick", "sting",
|
|
||||||
"stock", "store", "sunday", "sunset", "support", "sweden",
|
|
||||||
"swing", "tape", "think", "thomas", "tictac", "time",
|
|
||||||
"toast", "tobacco", "tonight", "torch", "torso", "touch",
|
|
||||||
"toyota", "trade", "tribune", "trinity", "triton", "truck",
|
|
||||||
"trust", "type", "under", "unit", "urban", "urgent",
|
|
||||||
"user", "value", "vendor", "venice", "verona", "vibrate",
|
|
||||||
"virgo", "visible", "vista", "vital", "voice", "vortex",
|
|
||||||
"waiter", "watch", "wave", "weather", "wedding", "wheel",
|
|
||||||
"whiskey", "wisdom", "deal", "null", "nurse", "quebec",
|
|
||||||
"reserve", "reunion", "roof", "singer", "verbal", "amen",
|
|
||||||
"ego", "fax", "jet", "job", "rio", "ski",
|
|
||||||
"yes",
|
|
||||||
}
|
|
||||||
|
|
@ -2,14 +2,8 @@ package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/utils"
|
|
||||||
log "github.com/schollz/logger"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TCP_BUFFER_SIZE is the maximum packet size
|
// TCP_BUFFER_SIZE is the maximum packet size
|
||||||
|
|
@ -21,146 +15,35 @@ var (
|
||||||
DEFAULT_RELAY6 = "croc6.schollz.com"
|
DEFAULT_RELAY6 = "croc6.schollz.com"
|
||||||
DEFAULT_PORT = "9009"
|
DEFAULT_PORT = "9009"
|
||||||
DEFAULT_PASSPHRASE = "pass123"
|
DEFAULT_PASSPHRASE = "pass123"
|
||||||
INTERNAL_DNS = false
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// publicDNS are servers to be queried if a local lookup fails
|
|
||||||
var publicDNS = []string{
|
|
||||||
"1.0.0.1", // Cloudflare
|
|
||||||
"1.1.1.1", // Cloudflare
|
|
||||||
"[2606:4700:4700::1111]", // Cloudflare
|
|
||||||
"[2606:4700:4700::1001]", // Cloudflare
|
|
||||||
"8.8.4.4", // Google
|
|
||||||
"8.8.8.8", // Google
|
|
||||||
"[2001:4860:4860::8844]", // Google
|
|
||||||
"[2001:4860:4860::8888]", // Google
|
|
||||||
"9.9.9.9", // Quad9
|
|
||||||
"149.112.112.112", // Quad9
|
|
||||||
"[2620:fe::fe]", // Quad9
|
|
||||||
"[2620:fe::fe:9]", // Quad9
|
|
||||||
"8.26.56.26", // Comodo
|
|
||||||
"8.20.247.20", // Comodo
|
|
||||||
"208.67.220.220", // Cisco OpenDNS
|
|
||||||
"208.67.222.222", // Cisco OpenDNS
|
|
||||||
"[2620:119:35::35]", // Cisco OpenDNS
|
|
||||||
"[2620:119:53::53]", // Cisco OpenDNS
|
|
||||||
}
|
|
||||||
|
|
||||||
func getConfigFile(requireValidPath bool) (fname string, err error) {
|
|
||||||
configFile, err := utils.GetConfigDir(requireValidPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
fname = path.Join(configFile, "internal-dns")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
log.SetLevel("info")
|
|
||||||
log.SetOutput(os.Stderr)
|
|
||||||
doRemember := false
|
|
||||||
for _, flag := range os.Args {
|
|
||||||
if flag == "--internal-dns" {
|
|
||||||
INTERNAL_DNS = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if flag == "--remember" {
|
|
||||||
doRemember = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if doRemember {
|
|
||||||
// save in config file
|
|
||||||
fname, err := getConfigFile(true)
|
|
||||||
if err == nil {
|
|
||||||
f, _ := os.Create(fname)
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !INTERNAL_DNS {
|
|
||||||
fname, err := getConfigFile(false)
|
|
||||||
if err == nil {
|
|
||||||
INTERNAL_DNS = utils.Exists(fname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Trace("Using internal DNS: ", INTERNAL_DNS)
|
|
||||||
var err error
|
var err error
|
||||||
var addr string
|
DEFAULT_RELAY, err = lookupIP(DEFAULT_RELAY)
|
||||||
addr, err = lookup(DEFAULT_RELAY)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
DEFAULT_RELAY = net.JoinHostPort(addr, DEFAULT_PORT)
|
DEFAULT_RELAY += ":" + DEFAULT_PORT
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_RELAY = ""
|
DEFAULT_RELAY = ""
|
||||||
}
|
}
|
||||||
log.Tracef("Default ipv4 relay: %s", addr)
|
DEFAULT_RELAY6, err = lookupIP(DEFAULT_RELAY6)
|
||||||
addr, err = lookup(DEFAULT_RELAY6)
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
DEFAULT_RELAY6 = net.JoinHostPort(addr, DEFAULT_PORT)
|
DEFAULT_RELAY6 = "[" + DEFAULT_RELAY6 + "]:" + DEFAULT_PORT
|
||||||
} else {
|
} else {
|
||||||
DEFAULT_RELAY6 = ""
|
DEFAULT_RELAY6 = ""
|
||||||
}
|
}
|
||||||
log.Tracef("Default ipv6 relay: %s", addr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve a hostname to an IP address using DNS.
|
func lookupIP(address string) (ipaddress string, err error) {
|
||||||
func lookup(address string) (ipaddress string, err error) {
|
|
||||||
if !INTERNAL_DNS {
|
|
||||||
log.Tracef("Using local DNS to resolve %s", address)
|
|
||||||
return localLookupIP(address)
|
|
||||||
}
|
|
||||||
type Result struct {
|
|
||||||
s string
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
result := make(chan Result, len(publicDNS))
|
|
||||||
for _, dns := range publicDNS {
|
|
||||||
go func(dns string) {
|
|
||||||
var r Result
|
|
||||||
r.s, r.err = remoteLookupIP(address, dns)
|
|
||||||
log.Tracef("Resolved %s to %s using %s", address, r.s, dns)
|
|
||||||
result <- r
|
|
||||||
}(dns)
|
|
||||||
}
|
|
||||||
for i := 0; i < len(publicDNS); i++ {
|
|
||||||
ipaddress = (<-result).s
|
|
||||||
log.Tracef("Resolved %s to %s", address, ipaddress)
|
|
||||||
if ipaddress != "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("failed to resolve %s: all DNS servers exhausted", address)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// localLookupIP returns a host's IP address using the local DNS configuration.
|
|
||||||
func localLookupIP(address string) (ipaddress string, err error) {
|
|
||||||
// Create a context with a 500 millisecond timeout
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
r := &net.Resolver{}
|
|
||||||
|
|
||||||
// Use the context with timeout in the LookupHost function
|
|
||||||
ip, err := r.LookupHost(ctx, address)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
ipaddress = ip[0]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// remoteLookupIP returns a host's IP address based on a remote DNS server.
|
|
||||||
func remoteLookupIP(address, dns string) (ipaddress string, err error) {
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
r := &net.Resolver{
|
r := &net.Resolver{
|
||||||
PreferGo: true,
|
PreferGo: true,
|
||||||
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
|
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
|
||||||
d := new(net.Dialer)
|
d := net.Dialer{
|
||||||
return d.DialContext(ctx, network, dns+":53")
|
Timeout: time.Millisecond * time.Duration(10000),
|
||||||
|
}
|
||||||
|
return d.DialContext(ctx, "udp", "1.1.1.1:53")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
ip, err := r.LookupHost(ctx, address)
|
ip, err := r.LookupHost(context.Background(), address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
|
|
@ -1,9 +0,0 @@
|
||||||
package tcp
|
|
||||||
|
|
||||||
import "time"
|
|
||||||
|
|
||||||
const (
|
|
||||||
DEFAULT_LOG_LEVEL = "debug"
|
|
||||||
DEFAULT_ROOM_CLEANUP_INTERVAL = 10 * time.Minute
|
|
||||||
DEFAULT_ROOM_TTL = 3 * time.Hour
|
|
||||||
)
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
package tcp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: maybe export from logger library?
|
|
||||||
var availableLogLevels = []string{"info", "error", "warn", "debug", "trace"}
|
|
||||||
|
|
||||||
type serverOptsFunc func(s *server) error
|
|
||||||
|
|
||||||
func WithBanner(banner ...string) serverOptsFunc {
|
|
||||||
return func(s *server) error {
|
|
||||||
if len(banner) > 0 {
|
|
||||||
s.banner = banner[0]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithLogLevel(level string) serverOptsFunc {
|
|
||||||
return func(s *server) error {
|
|
||||||
if !containsSlice(availableLogLevels, level) {
|
|
||||||
return fmt.Errorf("invalid log level specified: %s", level)
|
|
||||||
}
|
|
||||||
s.debugLevel = level
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithRoomCleanupInterval(interval time.Duration) serverOptsFunc {
|
|
||||||
return func(s *server) error {
|
|
||||||
s.roomCleanupInterval = interval
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithRoomTTL(ttl time.Duration) serverOptsFunc {
|
|
||||||
return func(s *server) error {
|
|
||||||
s.roomTTL = ttl
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsSlice(s []string, e string) bool {
|
|
||||||
for _, ss := range s {
|
|
||||||
if e == ss {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
194
src/tcp/tcp.go
194
src/tcp/tcp.go
|
|
@ -9,25 +9,19 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
log "github.com/schollz/logger"
|
log "github.com/schollz/logger"
|
||||||
"github.com/schollz/pake/v3"
|
"github.com/schollz/pake/v2"
|
||||||
|
|
||||||
"github.com/schollz/croc/v10/src/comm"
|
"github.com/schollz/croc/v8/src/comm"
|
||||||
"github.com/schollz/croc/v10/src/crypt"
|
"github.com/schollz/croc/v8/src/crypt"
|
||||||
"github.com/schollz/croc/v10/src/models"
|
"github.com/schollz/croc/v8/src/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
type server struct {
|
type server struct {
|
||||||
host string
|
|
||||||
port string
|
port string
|
||||||
debugLevel string
|
debugLevel string
|
||||||
banner string
|
banner string
|
||||||
password string
|
password string
|
||||||
rooms roomMap
|
rooms roomMap
|
||||||
|
|
||||||
roomCleanupInterval time.Duration
|
|
||||||
roomTTL time.Duration
|
|
||||||
|
|
||||||
stopRoomCleanup chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type roomInfo struct {
|
type roomInfo struct {
|
||||||
|
|
@ -42,57 +36,46 @@ type roomMap struct {
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
const pingRoom = "pinglkasjdlfjsaldjf"
|
var timeToRoomDeletion = 10 * time.Minute
|
||||||
|
var pingRoom = "pinglkasjdlfjsaldjf"
|
||||||
|
|
||||||
// newDefaultServer initializes a new server, with some default configuration options
|
// Run starts a tcp listener, run async
|
||||||
func newDefaultServer() *server {
|
func Run(debugLevel, port, password string, banner ...string) (err error) {
|
||||||
s := new(server)
|
s := new(server)
|
||||||
s.roomCleanupInterval = DEFAULT_ROOM_CLEANUP_INTERVAL
|
|
||||||
s.roomTTL = DEFAULT_ROOM_TTL
|
|
||||||
s.debugLevel = DEFAULT_LOG_LEVEL
|
|
||||||
s.stopRoomCleanup = make(chan struct{})
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// RunWithOptionsAsync asynchronously starts a TCP listener.
|
|
||||||
func RunWithOptionsAsync(host, port, password string, opts ...serverOptsFunc) error {
|
|
||||||
s := newDefaultServer()
|
|
||||||
s.host = host
|
|
||||||
s.port = port
|
s.port = port
|
||||||
s.password = password
|
s.password = password
|
||||||
for _, opt := range opts {
|
s.debugLevel = debugLevel
|
||||||
err := opt(s)
|
if len(banner) > 0 {
|
||||||
if err != nil {
|
s.banner = banner[0]
|
||||||
return fmt.Errorf("could not apply optional configurations: %w", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return s.start()
|
return s.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run starts a tcp listener, run async
|
|
||||||
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
|
|
||||||
return RunWithOptionsAsync(host, port, password, WithBanner(banner...), WithLogLevel(debugLevel))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) start() (err error) {
|
func (s *server) start() (err error) {
|
||||||
log.SetLevel(s.debugLevel)
|
log.SetLevel(s.debugLevel)
|
||||||
|
log.Debugf("starting with password '%s'", s.password)
|
||||||
// Mask our password in logs
|
|
||||||
maskedPassword := ""
|
|
||||||
if len(s.password) > 2 {
|
|
||||||
maskedPassword = fmt.Sprintf("%c***%c", s.password[0], s.password[len(s.password)-1])
|
|
||||||
} else {
|
|
||||||
maskedPassword = s.password
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("starting with password '%s'", maskedPassword)
|
|
||||||
|
|
||||||
s.rooms.Lock()
|
s.rooms.Lock()
|
||||||
s.rooms.rooms = make(map[string]roomInfo)
|
s.rooms.rooms = make(map[string]roomInfo)
|
||||||
s.rooms.Unlock()
|
s.rooms.Unlock()
|
||||||
|
|
||||||
go s.deleteOldRooms()
|
// delete old rooms
|
||||||
defer s.stopRoomDeletion()
|
go func() {
|
||||||
|
for {
|
||||||
|
time.Sleep(timeToRoomDeletion)
|
||||||
|
var roomsToDelete []string
|
||||||
|
s.rooms.Lock()
|
||||||
|
for room := range s.rooms.rooms {
|
||||||
|
if time.Since(s.rooms.rooms[room].opened) > 3*time.Hour {
|
||||||
|
roomsToDelete = append(roomsToDelete, room)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.rooms.Unlock()
|
||||||
|
|
||||||
|
for _, room := range roomsToDelete {
|
||||||
|
s.deleteRoom(room)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
err = s.run()
|
err = s.run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -102,32 +85,10 @@ func (s *server) start() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *server) run() (err error) {
|
func (s *server) run() (err error) {
|
||||||
network := "tcp"
|
log.Infof("starting TCP server on " + s.port)
|
||||||
addr := net.JoinHostPort(s.host, s.port)
|
server, err := net.Listen("tcp", ":"+s.port)
|
||||||
if s.host != "" {
|
|
||||||
ip := net.ParseIP(s.host)
|
|
||||||
if ip == nil {
|
|
||||||
var tcpIP *net.IPAddr
|
|
||||||
tcpIP, err = net.ResolveIPAddr("ip", s.host)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ip = tcpIP.IP
|
|
||||||
}
|
|
||||||
addr = net.JoinHostPort(ip.String(), s.port)
|
|
||||||
if s.host != "" {
|
|
||||||
if ip.To4() != nil {
|
|
||||||
network = "tcp4"
|
|
||||||
} else {
|
|
||||||
network = "tcp6"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addr = strings.Replace(addr, "127.0.0.1", "0.0.0.0", 1)
|
|
||||||
log.Infof("starting TCP server on " + addr)
|
|
||||||
server, err := net.Listen(network, addr)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error listening on %s: %w", addr, err)
|
return fmt.Errorf("error listening on %s: %w", s.port, err)
|
||||||
}
|
}
|
||||||
defer server.Close()
|
defer server.Close()
|
||||||
// spawn a new goroutine whenever a client connects
|
// spawn a new goroutine whenever a client connects
|
||||||
|
|
@ -187,44 +148,11 @@ func (s *server) run() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// deleteOldRooms checks for rooms at a regular interval and removes those that
|
|
||||||
// have exceeded their allocated TTL.
|
|
||||||
func (s *server) deleteOldRooms() {
|
|
||||||
ticker := time.NewTicker(s.roomCleanupInterval)
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
var roomsToDelete []string
|
|
||||||
s.rooms.Lock()
|
|
||||||
for room := range s.rooms.rooms {
|
|
||||||
if time.Since(s.rooms.rooms[room].opened) > s.roomTTL {
|
|
||||||
roomsToDelete = append(roomsToDelete, room)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.rooms.Unlock()
|
|
||||||
|
|
||||||
for _, room := range roomsToDelete {
|
|
||||||
s.deleteRoom(room)
|
|
||||||
log.Debugf("room cleaned up: %s", room)
|
|
||||||
}
|
|
||||||
case <-s.stopRoomCleanup:
|
|
||||||
ticker.Stop()
|
|
||||||
log.Debug("room cleanup stopped")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *server) stopRoomDeletion() {
|
|
||||||
log.Debug("stop room cleanup fired")
|
|
||||||
s.stopRoomCleanup <- struct{}{}
|
|
||||||
}
|
|
||||||
|
|
||||||
var weakKey = []byte{1, 2, 3}
|
var weakKey = []byte{1, 2, 3}
|
||||||
|
|
||||||
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
|
||||||
// establish secure password with PAKE for communication with relay
|
// establish secure password with PAKE for communication with relay
|
||||||
B, err := pake.InitCurve(weakKey, 1, "siec")
|
B, err := pake.InitCurve(weakKey, 1, "siec", 1*time.Microsecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -232,10 +160,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debugf("Abytes: %s", Abytes)
|
|
||||||
if bytes.Equal(Abytes, []byte("ping")) {
|
if bytes.Equal(Abytes, []byte("ping")) {
|
||||||
room = pingRoom
|
room = pingRoom
|
||||||
log.Debug("sending back pong")
|
|
||||||
c.Send([]byte("pong"))
|
c.Send([]byte("pong"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -244,6 +170,11 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send(B.Bytes())
|
err = c.Send(B.Bytes())
|
||||||
|
Abytes, err = c.Receive()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
err = B.Update(Abytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -255,9 +186,6 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
|
|
||||||
// receive salt
|
// receive salt
|
||||||
salt, err := c.Receive()
|
salt, err := c.Receive()
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
strongKeyForEncryption, _, err := crypt.New(strongKey, salt)
|
strongKeyForEncryption, _, err := crypt.New(strongKey, salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -274,8 +202,8 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(string(passwordBytes)) != s.password {
|
if strings.TrimSpace(string(passwordBytes)) != s.password {
|
||||||
err = fmt.Errorf("bad password")
|
err = fmt.Errorf("bad password")
|
||||||
enc, _ := crypt.Encrypt([]byte(err.Error()), strongKeyForEncryption)
|
enc, _ := crypt.Decrypt([]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 "", fmt.Errorf("send error: %w", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -340,6 +268,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
|
||||||
err = c.Send(bSend)
|
err = c.Send(bSend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
s.deleteRoom(room)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
@ -398,11 +327,11 @@ func (s *server) deleteRoom(room string) {
|
||||||
}
|
}
|
||||||
s.rooms.rooms[room] = roomInfo{first: nil, second: nil}
|
s.rooms.rooms[room] = roomInfo{first: nil, second: nil}
|
||||||
delete(s.rooms.rooms, room)
|
delete(s.rooms.rooms, room)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// chanFromConn creates a channel from a Conn object, and sends everything it
|
// 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 {
|
func chanFromConn(conn net.Conn) chan []byte {
|
||||||
c := make(chan []byte, 1)
|
c := make(chan []byte, 1)
|
||||||
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
if err := conn.SetReadDeadline(time.Now().Add(3 * time.Hour)); err != nil {
|
||||||
|
|
@ -459,20 +388,16 @@ func pipe(conn1 net.Conn, conn2 net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func PingServer(address string) (err error) {
|
func PingServer(address string) (err error) {
|
||||||
log.Debugf("pinging %s", address)
|
c, err := comm.NewConnection(address, 200*time.Millisecond)
|
||||||
c, err := comm.NewConnection(address, 300*time.Millisecond)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send([]byte("ping"))
|
err = c.Send([]byte("ping"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b, err := c.Receive()
|
b, err := c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if bytes.Equal(b, []byte("pong")) {
|
if bytes.Equal(b, []byte("pong")) {
|
||||||
|
|
@ -490,104 +415,87 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
|
||||||
c, err = comm.NewConnection(address)
|
c, err = comm.NewConnection(address)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get PAKE connection with server to establish strong key to transfer info
|
// get PAKE connection with server to establish strong key to transfer info
|
||||||
A, err := pake.InitCurve(weakKey, 0, "siec")
|
A, err := pake.InitCurve(weakKey, 0, "siec", 1*time.Microsecond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send(A.Bytes())
|
err = c.Send(A.Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
Bbytes, err := c.Receive()
|
Bbytes, err := c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = A.Update(Bbytes)
|
err = A.Update(Bbytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
return
|
||||||
|
}
|
||||||
|
err = c.Send(A.Bytes())
|
||||||
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongKey, err := A.SessionKey()
|
strongKey, err := A.SessionKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debugf("strong key: %x", strongKey)
|
log.Debugf("strong key: %x", strongKey)
|
||||||
|
|
||||||
strongKeyForEncryption, salt, err := crypt.New(strongKey, nil)
|
strongKeyForEncryption, salt, err := crypt.New(strongKey, nil)
|
||||||
if err != nil {
|
|
||||||
log.Debug(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// send salt
|
// send salt
|
||||||
err = c.Send(salt)
|
err = c.Send(salt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("sending password")
|
log.Debug("sending password")
|
||||||
bSend, err := crypt.Encrypt([]byte(password), strongKeyForEncryption)
|
bSend, err := crypt.Encrypt([]byte(password), strongKeyForEncryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send(bSend)
|
err = c.Send(bSend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("waiting for first ok")
|
log.Debug("waiting for first ok")
|
||||||
enc, err := c.Receive()
|
enc, err := c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err := crypt.Decrypt(enc, strongKeyForEncryption)
|
data, err := crypt.Decrypt(enc, strongKeyForEncryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !strings.Contains(string(data), "|||") {
|
if !strings.Contains(string(data), "|||") {
|
||||||
err = fmt.Errorf("bad response: %s", string(data))
|
err = fmt.Errorf("bad response: %s", string(data))
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
banner = strings.Split(string(data), "|||")[0]
|
banner = strings.Split(string(data), "|||")[0]
|
||||||
ipaddr = strings.Split(string(data), "|||")[1]
|
ipaddr = strings.Split(string(data), "|||")[1]
|
||||||
log.Debugf("sending room; %s", room)
|
log.Debug("sending room")
|
||||||
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
|
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = c.Send(bSend)
|
err = c.Send(bSend)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("waiting for room confirmation")
|
log.Debug("waiting for room confirmation")
|
||||||
enc, err = c.Receive()
|
enc, err = c.Receive()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, err = crypt.Decrypt(enc, strongKeyForEncryption)
|
data, err = crypt.Decrypt(enc, strongKeyForEncryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !bytes.Equal(data, []byte("ok")) {
|
if !bytes.Equal(data, []byte("ok")) {
|
||||||
err = fmt.Errorf("got bad response: %s", data)
|
err = fmt.Errorf("got bad response: %s", data)
|
||||||
log.Debug(err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Debug("all set")
|
log.Debug("all set")
|
||||||
|
|
|
||||||
|
|
@ -12,34 +12,34 @@ import (
|
||||||
|
|
||||||
func BenchmarkConnection(b *testing.B) {
|
func BenchmarkConnection(b *testing.B) {
|
||||||
log.SetLevel("trace")
|
log.SetLevel("trace")
|
||||||
go Run("debug", "127.0.0.1", "8283", "pass123", "8284")
|
go Run("debug", "8283", "pass123", "8284")
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
c, _, _, _ := ConnectToTCPServer("127.0.0.1:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
c, _, _, _ := ConnectToTCPServer("localhost:8283", "pass123", fmt.Sprintf("testroom%d", i), 1*time.Minute)
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTCP(t *testing.T) {
|
func TestTCP(t *testing.T) {
|
||||||
log.SetLevel("error")
|
log.SetLevel("error")
|
||||||
timeToRoomDeletion := 100 * time.Millisecond
|
timeToRoomDeletion = 100 * time.Millisecond
|
||||||
go RunWithOptionsAsync("127.0.0.1", "8381", "pass123", WithBanner("8382"), WithLogLevel("debug"), WithRoomTTL(timeToRoomDeletion))
|
go Run("debug", "8281", "pass123", "8282")
|
||||||
time.Sleep(timeToRoomDeletion)
|
time.Sleep(100 * time.Millisecond)
|
||||||
err := PingServer("127.0.0.1:8381")
|
err := PingServer("localhost:8281")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
err = PingServer("127.0.0.1:8333")
|
err = PingServer("localhost:8333")
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
time.Sleep(timeToRoomDeletion)
|
time.Sleep(100 * time.Millisecond)
|
||||||
c1, banner, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Minute)
|
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Minute)
|
||||||
assert.Equal(t, banner, "8382")
|
assert.Equal(t, banner, "8282")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
c2, _, _, err := ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
c2, _, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom")
|
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
_, _, _, err = ConnectToTCPServer("127.0.0.1:8381", "pass123", "testRoom", 1*time.Nanosecond)
|
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Nanosecond)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
// try sending data
|
// try sending data
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,28 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"compress/flate"
|
|
||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/cespare/xxhash"
|
"github.com/cespare/xxhash"
|
||||||
"github.com/kalafut/imohash"
|
"github.com/kalafut/imohash"
|
||||||
"github.com/minio/highwayhash"
|
"github.com/schollz/mnemonicode"
|
||||||
"github.com/schollz/croc/v10/src/mnemonicode"
|
|
||||||
log "github.com/schollz/logger"
|
|
||||||
"github.com/schollz/progressbar/v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const NbPinNumbers = 4
|
|
||||||
const NbBytesWords = 4
|
|
||||||
|
|
||||||
// Get or create home directory
|
|
||||||
func GetConfigDir(requireValidPath bool) (homedir string, err error) {
|
|
||||||
if envHomedir, isSet := os.LookupEnv("CROC_CONFIG_DIR"); isSet {
|
|
||||||
homedir = envHomedir
|
|
||||||
} else if xdgConfigHome, isSet := os.LookupEnv("XDG_CONFIG_HOME"); isSet {
|
|
||||||
homedir = path.Join(xdgConfigHome, "croc")
|
|
||||||
} else {
|
|
||||||
homedir, err = os.UserHomeDir()
|
|
||||||
if err != nil {
|
|
||||||
if !requireValidPath {
|
|
||||||
err = nil
|
|
||||||
homedir = ""
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
homedir = path.Join(homedir, ".config", "croc")
|
|
||||||
}
|
|
||||||
|
|
||||||
if requireValidPath {
|
|
||||||
if _, err = os.Stat(homedir); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(homedir, 0o700)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exists reports whether the named file or directory exists.
|
// Exists reports whether the named file or directory exists.
|
||||||
func Exists(name string) bool {
|
func Exists(name string) bool {
|
||||||
if _, err := os.Stat(name); err != nil {
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
|
@ -77,83 +41,13 @@ func GetInput(prompt string) string {
|
||||||
return strings.TrimSpace(text)
|
return strings.TrimSpace(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashFile returns the hash of a file or, in case of a symlink, the
|
// HashFile returns the hash of a file
|
||||||
// SHA256 hash of its target. Takes an argument to specify the algorithm to use.
|
func HashFile(fname string) (hash256 []byte, err error) {
|
||||||
func HashFile(fname string, algorithm string, showProgress ...bool) (hash256 []byte, err error) {
|
return IMOHashFile(fname)
|
||||||
doShowProgress := false
|
|
||||||
if len(showProgress) > 0 {
|
|
||||||
doShowProgress = showProgress[0]
|
|
||||||
}
|
|
||||||
var fstats os.FileInfo
|
|
||||||
fstats, err = os.Lstat(fname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if fstats.Mode()&os.ModeSymlink != 0 {
|
|
||||||
var target string
|
|
||||||
target, err = os.Readlink(fname)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return []byte(SHA256(target)), nil
|
|
||||||
}
|
|
||||||
switch algorithm {
|
|
||||||
case "imohash":
|
|
||||||
return IMOHashFile(fname)
|
|
||||||
case "md5":
|
|
||||||
return MD5HashFile(fname, doShowProgress)
|
|
||||||
case "xxhash":
|
|
||||||
return XXHashFile(fname, doShowProgress)
|
|
||||||
case "highway":
|
|
||||||
return HighwayHashFile(fname, doShowProgress)
|
|
||||||
}
|
|
||||||
err = fmt.Errorf("unspecified algorithm")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HighwayHashFile returns highwayhash of a file
|
|
||||||
func HighwayHashFile(fname string, doShowProgress bool) (hashHighway []byte, err error) {
|
|
||||||
f, err := os.Open(fname)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
key, err := hex.DecodeString("1553c5383fb0b86578c3310da665b4f6e0521acf22eb58a99532ffed02a6b115")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
h, err := highwayhash.New(key)
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("could not create highwayhash: %s", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if doShowProgress {
|
|
||||||
stat, _ := f.Stat()
|
|
||||||
fnameShort := path.Base(fname)
|
|
||||||
if len(fnameShort) > 20 {
|
|
||||||
fnameShort = fnameShort[:20] + "..."
|
|
||||||
}
|
|
||||||
bar := progressbar.NewOptions64(stat.Size(),
|
|
||||||
progressbar.OptionSetWriter(os.Stderr),
|
|
||||||
progressbar.OptionShowBytes(true),
|
|
||||||
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
|
|
||||||
progressbar.OptionClearOnFinish(),
|
|
||||||
)
|
|
||||||
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if _, err = io.Copy(h, f); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hashHighway = h.Sum(nil)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MD5HashFile returns MD5 hash
|
// MD5HashFile returns MD5 hash
|
||||||
func MD5HashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
|
func MD5HashFile(fname string) (hash256 []byte, err error) {
|
||||||
f, err := os.Open(fname)
|
f, err := os.Open(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -161,50 +55,23 @@ func MD5HashFile(fname string, doShowProgress bool) (hash256 []byte, err error)
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
h := md5.New()
|
h := md5.New()
|
||||||
if doShowProgress {
|
if _, err = io.Copy(h, f); err != nil {
|
||||||
stat, _ := f.Stat()
|
return
|
||||||
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)
|
hash256 = h.Sum(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var imofull = imohash.NewCustom(0, 0)
|
|
||||||
var imopartial = imohash.NewCustom(16*16*8*1024, 128*1024)
|
|
||||||
|
|
||||||
// IMOHashFile returns imohash
|
// IMOHashFile returns imohash
|
||||||
func IMOHashFile(fname string) (hash []byte, err error) {
|
func IMOHashFile(fname string) (hash []byte, err error) {
|
||||||
b, err := imopartial.SumFile(fname)
|
b, err := imohash.SumFile(fname)
|
||||||
hash = b[:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// IMOHashFileFull returns imohash of full file
|
|
||||||
func IMOHashFileFull(fname string) (hash []byte, err error) {
|
|
||||||
b, err := imofull.SumFile(fname)
|
|
||||||
hash = b[:]
|
hash = b[:]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXHashFile returns the xxhash of a file
|
// XXHashFile returns the xxhash of a file
|
||||||
func XXHashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
|
func XXHashFile(fname string) (hash256 []byte, err error) {
|
||||||
f, err := os.Open(fname)
|
f, err := os.Open(fname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
@ -212,25 +79,8 @@ func XXHashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
h := xxhash.New()
|
h := xxhash.New()
|
||||||
if doShowProgress {
|
if _, err = io.Copy(h, f); err != nil {
|
||||||
stat, _ := f.Stat()
|
return
|
||||||
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)
|
hash256 = h.Sum(nil)
|
||||||
|
|
@ -246,21 +96,19 @@ func SHA256(s string) string {
|
||||||
|
|
||||||
// PublicIP returns public ip address
|
// PublicIP returns public ip address
|
||||||
func PublicIP() (ip string, err error) {
|
func PublicIP() (ip string, err error) {
|
||||||
// ask ipv4.icanhazip.com for the public ip
|
resp, err := http.Get("https://canhazip.com")
|
||||||
// by making http request
|
|
||||||
// if the request fails, return nothing
|
|
||||||
resp, err := http.Get("http://ipv4.icanhazip.com")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
// read the body of the response
|
if resp.StatusCode == http.StatusOK {
|
||||||
// and return the ip address
|
bodyBytes, err := ioutil.ReadAll(resp.Body)
|
||||||
buf := new(bytes.Buffer)
|
if err != nil {
|
||||||
buf.ReadFrom(resp.Body)
|
return "", err
|
||||||
ip = strings.TrimSpace(buf.String())
|
}
|
||||||
|
ip = strings.TrimSpace(string(bodyBytes))
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -268,8 +116,7 @@ func PublicIP() (ip string, err error) {
|
||||||
func LocalIP() string {
|
func LocalIP() string {
|
||||||
conn, err := net.Dial("udp", "8.8.8.8:80")
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Fatal(err)
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
|
|
@ -278,27 +125,13 @@ func LocalIP() string {
|
||||||
return localAddr.IP.String()
|
return localAddr.IP.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateRandomPin() string {
|
// GetRandomName returns mnemoicoded random name
|
||||||
s := ""
|
|
||||||
max := new(big.Int)
|
|
||||||
max.SetInt64(9)
|
|
||||||
for i := 0; i < NbPinNumbers; i++ {
|
|
||||||
v, err := rand.Int(rand.Reader, max)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
s += fmt.Sprintf("%d", v)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetRandomName returns mnemonicoded random name
|
|
||||||
func GetRandomName() string {
|
func GetRandomName() string {
|
||||||
var result []string
|
var result []string
|
||||||
bs := make([]byte, NbBytesWords)
|
bs := make([]byte, 4)
|
||||||
rand.Read(bs)
|
rand.Read(bs)
|
||||||
result = mnemonicode.EncodeWordList(result, bs)
|
result = mnemonicode.EncodeWordList(result, bs)
|
||||||
return GenerateRandomPin() + "-" + strings.Join(result, "-")
|
return strings.Join(result, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ByteCountDecimal converts bytes to human readable byte string
|
// ByteCountDecimal converts bytes to human readable byte string
|
||||||
|
|
@ -364,6 +197,7 @@ func MissingChunks(fname string, fsize int64, chunkSize int) (chunkRanges []int6
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chunkRanges = append(chunkRanges, int64(curCount+1))
|
chunkRanges = append(chunkRanges, int64(curCount+1))
|
||||||
|
chunks = chunkRanges
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -402,7 +236,7 @@ func GetLocalIPs() (ips []string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func RandomFileName() (fname string, err error) {
|
func RandomFileName() (fname string, err error) {
|
||||||
f, err := os.CreateTemp(".", "croc-stdin-")
|
f, err := ioutil.TempFile(".", "croc-stdin-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -452,7 +286,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsLocalIP(ipaddress string) bool {
|
func IsLocalIP(ipaddress string) bool {
|
||||||
if strings.Contains(ipaddress, "127.0.0.1") {
|
if strings.Contains(ipaddress, "localhost") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
host, _, _ := net.SplitHostPort(ipaddress)
|
host, _, _ := net.SplitHostPort(ipaddress)
|
||||||
|
|
@ -467,171 +301,3 @@ func IsLocalIP(ipaddress string) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func ZipDirectory(destination string, source string) (err error) {
|
|
||||||
if _, err = os.Stat(destination); err == nil {
|
|
||||||
log.Errorf("%s file already exists!\n", destination)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "Zipping %s to %s\n", source, destination)
|
|
||||||
file, err := os.Create(destination)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(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.Error(err)
|
|
||||||
}
|
|
||||||
if info.Mode().IsRegular() {
|
|
||||||
f1, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
defer f1.Close()
|
|
||||||
zipPath := strings.ReplaceAll(path, source, strings.TrimSuffix(destination, ".zip"))
|
|
||||||
zipPath = filepath.ToSlash(zipPath)
|
|
||||||
w1, err := writer.Create(zipPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
if _, err := io.Copy(w1, f1); err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "\r\033[2K")
|
|
||||||
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(os.Stderr, "\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func UnzipDirectory(destination string, source string) error {
|
|
||||||
archive, err := zip.OpenReader(source)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(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.Errorf("Invalid file path %s\n", filePath)
|
|
||||||
}
|
|
||||||
if f.FileInfo().IsDir() {
|
|
||||||
os.MkdirAll(filePath, os.ModePerm)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
|
||||||
log.Error(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.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileInArchive, err := f.Open()
|
|
||||||
if err != nil {
|
|
||||||
log.Error(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
|
|
||||||
log.Error(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) (err error) {
|
|
||||||
// make sure it doesn't contain unicode or invisible characters
|
|
||||||
for _, r := range fname {
|
|
||||||
if !unicode.IsGraphic(r) {
|
|
||||||
err = fmt.Errorf("non-graphical unicode: %x U+%d in '%x'", string(r), r, fname)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !unicode.IsPrint(r) {
|
|
||||||
err = fmt.Errorf("non-printable unicode: %x U+%d in '%x'", string(r), r, fname)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// make sure basename does not include ".." or path separators
|
|
||||||
_, basename := filepath.Split(fname)
|
|
||||||
if strings.Contains(basename, "..") {
|
|
||||||
err = fmt.Errorf("basename cannot contain '..': '%s'", basename)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if strings.Contains(basename, string(os.PathSeparator)) {
|
|
||||||
err = fmt.Errorf("basename cannot contain path separators: '%s'", basename)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// make sure the filename is not an absolute path
|
|
||||||
if filepath.IsAbs(fname) {
|
|
||||||
err = fmt.Errorf("filename cannot be an absolute path: '%s'", fname)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const crocRemovalFile = "croc-marked-files.txt"
|
|
||||||
|
|
||||||
func MarkFileForRemoval(fname string) {
|
|
||||||
// append the fname to the list of files to remove
|
|
||||||
f, err := os.OpenFile(crocRemovalFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug(err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
_, err = f.WriteString(fname + "\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func RemoveMarkedFiles() (err error) {
|
|
||||||
// read the file and remove all the files
|
|
||||||
f, err := os.Open(crocRemovalFile)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
scanner := bufio.NewScanner(f)
|
|
||||||
for scanner.Scan() {
|
|
||||||
fname := scanner.Text()
|
|
||||||
err = os.Remove(fname)
|
|
||||||
if err == nil {
|
|
||||||
log.Tracef("Removed %s", fname)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
os.Remove(crocRemovalFile)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -3,29 +3,25 @@ package utils
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TCP_BUFFER_SIZE = 1024 * 64
|
|
||||||
|
|
||||||
var bigFileSize = 75000000
|
|
||||||
|
|
||||||
func bigFile() {
|
func bigFile() {
|
||||||
os.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0o666)
|
ioutil.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), 75000000), 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMD5(b *testing.B) {
|
func BenchmarkMD5(b *testing.B) {
|
||||||
bigFile()
|
bigFile()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
MD5HashFile("bigfile.test", false)
|
MD5HashFile("bigfile.test")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,10 +29,9 @@ func BenchmarkXXHash(b *testing.B) {
|
||||||
bigFile()
|
bigFile()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
XXHashFile("bigfile.test", false)
|
XXHashFile("bigfile.test")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkImoHash(b *testing.B) {
|
func BenchmarkImoHash(b *testing.B) {
|
||||||
bigFile()
|
bigFile()
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
|
|
@ -45,22 +40,6 @@ func BenchmarkImoHash(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkHighwayHash(b *testing.B) {
|
|
||||||
bigFile()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
HighwayHashFile("bigfile.test", false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkImoHashFull(b *testing.B) {
|
|
||||||
bigFile()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
IMOHashFileFull("bigfile.test")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSha256(b *testing.B) {
|
func BenchmarkSha256(b *testing.B) {
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
|
|
@ -68,14 +47,6 @@ func BenchmarkSha256(b *testing.B) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkMissingChunks(b *testing.B) {
|
|
||||||
bigFile()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
MissingChunks("bigfile.test", int64(bigFileSize), TCP_BUFFER_SIZE/2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestExists(t *testing.T) {
|
func TestExists(t *testing.T) {
|
||||||
bigFile()
|
bigFile()
|
||||||
defer os.Remove("bigfile.test")
|
defer os.Remove("bigfile.test")
|
||||||
|
|
@ -87,20 +58,10 @@ func TestExists(t *testing.T) {
|
||||||
func TestMD5HashFile(t *testing.T) {
|
func TestMD5HashFile(t *testing.T) {
|
||||||
bigFile()
|
bigFile()
|
||||||
defer os.Remove("bigfile.test")
|
defer os.Remove("bigfile.test")
|
||||||
b, err := MD5HashFile("bigfile.test", false)
|
b, err := MD5HashFile("bigfile.test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "8304ff018e02baad0e3555bade29a405", fmt.Sprintf("%x", b))
|
assert.Equal(t, "8304ff018e02baad0e3555bade29a405", fmt.Sprintf("%x", b))
|
||||||
_, err = MD5HashFile("bigfile.test.nofile", false)
|
_, err = MD5HashFile("bigfile.test.nofile")
|
||||||
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)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,16 +70,16 @@ func TestIMOHashFile(t *testing.T) {
|
||||||
defer os.Remove("bigfile.test")
|
defer os.Remove("bigfile.test")
|
||||||
b, err := IMOHashFile("bigfile.test")
|
b, err := IMOHashFile("bigfile.test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "c0d1e12301e6c635f6d4a8ea5c897437", fmt.Sprintf("%x", b))
|
assert.Equal(t, "c0d1e123ca94148ffea146137684ebb9", fmt.Sprintf("%x", b))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestXXHashFile(t *testing.T) {
|
func TestXXHashFile(t *testing.T) {
|
||||||
bigFile()
|
bigFile()
|
||||||
defer os.Remove("bigfile.test")
|
defer os.Remove("bigfile.test")
|
||||||
b, err := XXHashFile("bigfile.test", false)
|
b, err := XXHashFile("bigfile.test")
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "4918740eb5ccb6f7", fmt.Sprintf("%x", b))
|
assert.Equal(t, "4918740eb5ccb6f7", fmt.Sprintf("%x", b))
|
||||||
_, err = XXHashFile("nofile", false)
|
_, err = XXHashFile("nofile")
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,9 +99,9 @@ func TestMissingChunks(t *testing.T) {
|
||||||
rand.Seed(1)
|
rand.Seed(1)
|
||||||
bigBuff := make([]byte, fileSize)
|
bigBuff := make([]byte, fileSize)
|
||||||
rand.Read(bigBuff)
|
rand.Read(bigBuff)
|
||||||
os.WriteFile("missing.test", bigBuff, 0o644)
|
ioutil.WriteFile("missing.test", bigBuff, 0644)
|
||||||
empty := make([]byte, chunkSize)
|
empty := make([]byte, chunkSize)
|
||||||
f, err := os.OpenFile("missing.test", os.O_RDWR, 0o644)
|
f, err := os.OpenFile("missing.test", os.O_RDWR, 0644)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
for block := 0; block < fileSize/chunkSize; block++ {
|
for block := 0; block < fileSize/chunkSize; block++ {
|
||||||
if block == 0 || block == 4 || block == 5 || block >= 7 {
|
if block == 0 || block == 4 || block == 5 || block >= 7 {
|
||||||
|
|
@ -158,7 +119,7 @@ func TestMissingChunks(t *testing.T) {
|
||||||
os.Remove("missing.test")
|
os.Remove("missing.test")
|
||||||
|
|
||||||
content := []byte("temporary file's content")
|
content := []byte("temporary file's content")
|
||||||
tmpfile, err := os.CreateTemp("", "example")
|
tmpfile, err := ioutil.TempFile("", "example")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -190,22 +151,22 @@ func TestMissingChunks(t *testing.T) {
|
||||||
|
|
||||||
func TestHashFile(t *testing.T) {
|
func TestHashFile(t *testing.T) {
|
||||||
content := []byte("temporary file's content")
|
content := []byte("temporary file's content")
|
||||||
tmpfile, err := os.CreateTemp("", "example")
|
tmpfile, err := ioutil.TempFile("", "example")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
defer os.Remove(tmpfile.Name()) // clean up
|
defer os.Remove(tmpfile.Name()) // clean up
|
||||||
|
|
||||||
if _, err = tmpfile.Write(content); err != nil {
|
if _, err := tmpfile.Write(content); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
if err = tmpfile.Close(); err != nil {
|
if err := tmpfile.Close(); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
hashed, err := HashFile(tmpfile.Name(), "xxhash")
|
hashed, err := HashFile(tmpfile.Name())
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, "e66c561610ad51e2", fmt.Sprintf("%x", hashed))
|
assert.Equal(t, "18c9673a4bb8325d323e7f24fda9ae1e", fmt.Sprintf("%x", hashed))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublicIP(t *testing.T) {
|
func TestPublicIP(t *testing.T) {
|
||||||
|
|
@ -223,45 +184,14 @@ func TestLocalIP(t *testing.T) {
|
||||||
|
|
||||||
func TestGetRandomName(t *testing.T) {
|
func TestGetRandomName(t *testing.T) {
|
||||||
name := GetRandomName()
|
name := GetRandomName()
|
||||||
fmt.Println(name)
|
|
||||||
assert.NotEmpty(t, name)
|
assert.NotEmpty(t, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func intSliceSame(a, b []int) bool {
|
|
||||||
if len(a) != len(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for i, v := range a {
|
|
||||||
if v != b[i] {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindOpenPorts(t *testing.T) {
|
func TestFindOpenPorts(t *testing.T) {
|
||||||
openPorts := FindOpenPorts("127.0.0.1", 9009, 4)
|
openPorts := FindOpenPorts("localhost", 9009, 4)
|
||||||
if !intSliceSame(openPorts, []int{9009, 9010, 9011, 9012}) && !intSliceSame(openPorts, []int{9014, 9015, 9016, 9017}) {
|
assert.Equal(t, []int{9009, 9010, 9011, 9012}, openPorts)
|
||||||
t.Errorf("openPorts: %v", openPorts)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsLocalIP(t *testing.T) {
|
func TestIsLocalIP(t *testing.T) {
|
||||||
assert.True(t, IsLocalIP("192.168.0.14:9009"))
|
assert.True(t, IsLocalIP("192.168.0.14:9009"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidFileName(t *testing.T) {
|
|
||||||
// contains regular characters
|
|
||||||
assert.Nil(t, ValidFileName("中文.csl"))
|
|
||||||
// contains regular characters
|
|
||||||
assert.Nil(t, ValidFileName("[something].csl"))
|
|
||||||
// contains regular characters
|
|
||||||
assert.Nil(t, ValidFileName("[(something)].csl"))
|
|
||||||
// contains invisible character
|
|
||||||
err := ValidFileName("D中文.cslouglas")
|
|
||||||
assert.NotNil(t, err)
|
|
||||||
assert.Equal(t, "non-graphical unicode: e2808b U+8203 in '44e4b8ade696872e63736c6f75676c6173e2808b'", err.Error())
|
|
||||||
assert.NotNil(t, ValidFileName("hi..txt"))
|
|
||||||
assert.NotNil(t, ValidFileName(path.Join(string(os.PathSeparator), "abs", string(os.PathSeparator), "hi.txt")))
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue