Compare commits

..

No commits in common. "main" and "v9.6.6" have entirely different histories.
main ... v9.6.6

36 changed files with 481 additions and 1996 deletions

View File

@ -1,9 +1,41 @@
---
name: New issue
about: Create an issue
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
<!-- The comments between these brackets won't show up in the submitted issue (as you can see in the Preview). -->
<!-- Please try to download latest, https://github.com/schollz/croc/releases/latest of croc before reporting a bug! -->
## Describe the bug
<-- A clear and concise description of what the bug is. -->
## To Reproduce
Steps to reproduce the behavior:
<-- 1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error -->
1.
2.
3.
4.
## Expected behaviour
<-- A clear and concise description of what you expected to happen. -->
## Version
<-- Check "croc -v" and report it -->
## Additional context
<-- Add any other context about the problem here. -->
*Read this and delete before submitting:* Thanks for starting a discussion! Please provide as much context as possible so that others can understand your thoughts. If you have a specific change in mind, consider submitting a pull request instead.

View File

@ -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.

View File

@ -10,43 +10,9 @@ jobs:
name: Go unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
- uses: actions/checkout@v2
- uses: actions/setup-go@v4
with:
go-version: '1.23'
go-version: '1.20'
- 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

View File

@ -6,13 +6,8 @@ name: CD
on:
# Triggers the workflow on push or pull request events but only for the main branch
push:
branches:
- '*'
tags:
- 'v*'
branches: [ main ]
pull_request:
branches:
- '*'
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
@ -24,37 +19,32 @@ jobs:
steps:
-
name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v2
-
name: Docker meta
id: docker_meta
uses: docker/metadata-action@v5
uses: crazy-max/ghaction-docker-meta@v1
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
# tag-semver: "{{version}}"
tag-sha: true
tag-latest: true
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
uses: docker/setup-buildx-action@v2
-
name: Login to DockerHub
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v6
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile

View File

@ -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

View File

@ -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'

View File

@ -5,10 +5,10 @@ on:
jobs:
publish:
runs-on: ubuntu-latest
runs-on: windows-latest # Action can only run on Windows
steps:
- uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: schollz.croc
installers-regex: '_Windows-\w+\.zip$'
token: ${{ secrets.WINGET_TOKEN }}
token: ${{ secrets.WINGET_TOKEN }}

2
.gitignore vendored
View File

@ -9,5 +9,3 @@ croc-stdin*
.idea/
.vscode/
src/utils/bigfile.test
test1/

View File

@ -10,12 +10,12 @@ install: true
script:
- 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/v10/src/croc
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/crypt
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/tcp
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/utils
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v10/src/comm
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/compress
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/croc
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/crypt
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/tcp
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/utils
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v9/src/comm
branches:
except:

View File

@ -1,5 +1,5 @@
FROM golang:1.22-alpine as builder
RUN apk add --no-cache git gcc musl-dev
FROM golang:1.19-alpine as builder
RUN apk add --no-cache git
WORKDIR /go/croc
COPY . .
RUN go build -v -ldflags="-s -w"

View File

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2017-2024 Zack
Copyright (c) 2017-2021 Zack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -4,9 +4,10 @@
src="https://user-images.githubusercontent.com/6550035/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg"
width="408px" border="0" alt="croc">
<br>
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v10.1.1-brightgreen.svg?style=flat-square" alt="Version"></a>
<a href="https://github.com/schollz/croc/actions/workflows/ci.yml"><img
src="https://github.com/schollz/croc/actions/workflows/ci.yml/badge.svg" alt="Build
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v9.6.6-brightgreen.svg?style=flat-square" alt="Version"></a>
<a href="https://coveralls.io/github/schollz/croc"><img src="https://img.shields.io/badge/coverage-81%25-green.svg?style=flat-square" alt="Coverage"></a>
<a href="https://travis-ci.org/schollz/croc"><img
src="https://img.shields.io/travis/schollz/croc.svg?style=flat-square" alt="Build
Status"></a>
<p align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
@ -104,28 +105,16 @@ On FreeBSD you can install with `pkg`:
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.17+):
```
pixi global install croc
```
or into a particular environment with [`conda`](https://docs.conda.io/projects/conda/):
```
conda install --channel conda-forge croc
```
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
```
go install github.com/schollz/croc/v10@latest
go install github.com/schollz/croc/v9@latest
```
On Android there is a 3rd party F-Droid app [available to download](https://f-droid.org/en/packages/com.github.howeyc.crocgui/).
## Usage
## Usage
To send a file, simply do:
@ -145,25 +134,6 @@ The code phrase is used to establish password-authenticated key agreement ([PAKE
There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`.
### Using `croc` on Linux or Mac OS
On Linux and Mac OS, the sending & receiving is slightly different to avoid [leaking the secret via the process name](https://nvd.nist.gov/vuln/detail/CVE-2023-43621). On these systems you will need to run `croc` with the secret as an environment variable. For example, to receive with the secret `***`:
```
CROC_SECRET=*** croc
```
This will show only `croc` in the process list of a multi-user system and not leak the secret.
For a single-user system the default behavior can be permanently enabled by running
```
croc --classic
```
and confirming.
Run this command again to disable classic mode.
### Custom code phrase
You can send with your own code phrase (must be more than 6 characters).
@ -198,7 +168,7 @@ croc --yes [code-phrase] > out
All of the other text printed to the console is going to `stderr` so it will not interfere with the message going to `stdout`.
### Send text
### Send text
Sometimes you want to send URLs or short text. In addition to piping, you can easily send text with `croc`:

39
go.mod
View File

@ -1,39 +1,40 @@
module github.com/schollz/croc/v10
module github.com/schollz/croc/v9
go 1.22
toolchain go1.23.1
go 1.20
require (
github.com/cespare/xxhash v1.1.0
github.com/chzyer/readline v1.5.1
github.com/denisbrodbeck/machineid v1.0.1
github.com/kalafut/imohash v1.1.0
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b
github.com/minio/highwayhash v1.0.3
github.com/kalafut/imohash v1.0.2
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/schollz/cli/v2 v2.2.1
github.com/schollz/logger v1.2.0
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d
github.com/schollz/pake/v3 v3.0.5
github.com/schollz/peerdiscovery v1.7.5
github.com/schollz/progressbar/v3 v3.17.1
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.29.0
golang.org/x/net v0.31.0
golang.org/x/sys v0.27.0
golang.org/x/term v0.26.0
golang.org/x/time v0.8.0
github.com/schollz/peerdiscovery v1.7.2
github.com/schollz/progressbar/v3 v3.14.1
github.com/stretchr/testify v1.8.2
golang.org/x/crypto v0.15.0
golang.org/x/net v0.18.0
golang.org/x/time v0.4.0
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/tscholl2/siec v0.0.0-20240310163802-c2c6f6198406 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499 // indirect
github.com/twmb/murmur3 v1.1.8 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/term v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

113
go.sum
View File

@ -1,10 +1,9 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
@ -12,28 +11,30 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/kalafut/imohash v1.1.0 h1:Lldcmx0SXgMSoABB2WBD8mTgf0OlVnISn2Dyrfg2Ep8=
github.com/kalafut/imohash v1.1.0/go.mod h1:6cn9lU0Sj8M4eu9UaQm1kR/5y3k/ayB68yntRhGloL4=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/kalafut/imohash v1.0.2 h1:j/cUPa15YvXv7abJlM+kdJIycbBMpmO7WqhPl4YB76I=
github.com/kalafut/imohash v1.0.2/go.mod h1:PjHBF0vpo1q7zMqiTn0qwSTQU2wDn5QIe8S8sFQuZS8=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b h1:xZ59n7Frzh8CwyfAapUZLSg+gXH5m63YEaFCMpDHhpI=
github.com/magisterquis/connectproxy v0.0.0-20200725203833-3582e84f0c9b/go.mod h1:uDd4sYVYsqcxAB8j+Q7uhL6IJCs/r1kxib1HV4bgOMg=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -43,59 +44,49 @@ github.com/schollz/cli/v2 v2.2.1 h1:ou22Mj7ZPjrKz+8k2iDTWaHskEEV5NiAxGrdsCL36VU=
github.com/schollz/cli/v2 v2.2.1/go.mod h1:My6bfphRLZUhZdlFUK8scAxMWHydE7k4s2ed2Dtnn+s=
github.com/schollz/logger v1.2.0 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d h1:3zCjdgCJbo9Fot3UoqZkpGiDgT6Nf+iUnOsDEJQay+c=
github.com/schollz/mnemonicode v1.0.2-0.20190421205639-63fa713ece0d/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
github.com/schollz/pake/v3 v3.0.5 h1:MnZVdI987lkjln9BSx/zUb724TZISa2jbO+dPj6BvgQ=
github.com/schollz/pake/v3 v3.0.5/go.mod h1:OGbG6htRwSKo6V8R5tg61ufpFmZM1b/PrrSp6g2ZLLc=
github.com/schollz/peerdiscovery v1.7.5 h1:0cEhO+o8i4fpeKBwl7u0UY3Kt3XVt5fSzS4rg17ZPb4=
github.com/schollz/peerdiscovery v1.7.5/go.mod h1:Crht2FOfD1/eL3U/AIM0vvwVZDPePlBgSX3Xw+TnJoE=
github.com/schollz/progressbar/v3 v3.17.1 h1:bI1MTaoQO+v5kzklBjYNRQLoVpe0zbyRZNK6DFkVC5U=
github.com/schollz/progressbar/v3 v3.17.1/go.mod h1:RzqpnsPQNjUyIgdglUjRLgD7sVnxN1wpmBMV+UiEbL4=
github.com/schollz/peerdiscovery v1.7.2 h1:H5IAGcJIRkh2aIl00HnaqpUBJsZTDhWqmpLR0RaR21Y=
github.com/schollz/peerdiscovery v1.7.2/go.mod h1:NtkZS9cL2C/zSUC9dwxX4lyyZjwbjD5to3DgHh8uYtc=
github.com/schollz/progressbar/v3 v3.14.1 h1:VD+MJPCr4s3wdhTc7OEJ/Z3dAeBzJ7yKH/P4lC5yRTI=
github.com/schollz/progressbar/v3 v3.14.1/go.mod h1:Zc9xXneTzWXF81TGoqL71u0sBPjULtEHYtj/WVgVy8E=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499 h1:bPQ48TuiAuGTZDm54H2EV/2+eRRBHP61bKDkKSEPW4A=
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
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/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -103,45 +94,35 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/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/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.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/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
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/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY=
golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

32
main.go
View File

@ -5,13 +5,9 @@ package main
//go:generate git tag -af v$VERSION -m "v$VERSION"
import (
"fmt"
"os"
"os/signal"
"syscall"
"log"
"github.com/schollz/croc/v10/src/cli"
"github.com/schollz/croc/v10/src/utils"
"github.com/schollz/croc/v9/src/cli"
)
func main() {
@ -31,25 +27,7 @@ func main() {
// fmt.Println("wrote profile")
// }
// }()
// Create a channel to receive OS signals
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
if err := cli.Run(); err != nil {
fmt.Println(err)
os.Exit(1)
}
// Exit the program gracefully
utils.RemoveMarkedFiles()
os.Exit(0)
}()
// Wait for a termination signal
_ = <-sigs
utils.RemoveMarkedFiles()
// Exit the program gracefully
os.Exit(0)
if err := cli.Run(); err != nil {
log.Fatalln(err)
}
}

View File

@ -15,13 +15,13 @@ import (
"github.com/chzyer/readline"
"github.com/schollz/cli/v2"
"github.com/schollz/croc/v10/src/comm"
"github.com/schollz/croc/v10/src/croc"
"github.com/schollz/croc/v10/src/mnemonicode"
"github.com/schollz/croc/v10/src/models"
"github.com/schollz/croc/v10/src/tcp"
"github.com/schollz/croc/v10/src/utils"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/croc"
"github.com/schollz/croc/v9/src/models"
"github.com/schollz/croc/v9/src/tcp"
"github.com/schollz/croc/v9/src/utils"
log "github.com/schollz/logger"
"github.com/schollz/mnemonicode"
"github.com/schollz/pake/v3"
)
@ -36,15 +36,12 @@ func Run() (err error) {
app := cli.NewApp()
app.Name = "croc"
if Version == "" {
Version = "v10.1.0"
Version = "v9.6.6-1e57206"
}
app.Version = Version
app.Compiled = time.Now()
app.Usage = "easily and securely transfer stuff from one computer to another"
app.UsageText = `croc [GLOBAL OPTIONS] [COMMAND] [COMMAND OPTIONS] [filename(s) or folder]
USAGE EXAMPLES:
Send a file:
app.UsageText = `Send a file:
croc send file.txt
-git to respect your .gitignore
@ -75,9 +72,7 @@ func Run() (err error) {
&cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
&cli.BoolFlag{Name: "no-multi", Usage: "disable multiplexing"},
&cli.BoolFlag{Name: "git", Usage: "enable .gitignore respect / don't send ignored files"},
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
&cli.IntFlag{Name: "transfers", Value: 4, Usage: "number of ports to use for transfers"},
&cli.BoolFlag{Name: "qrcode", Aliases: []string{"qr"}, Usage: "show receive code as a qrcode"},
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
},
HelpName: "croc send",
Action: send,
@ -91,14 +86,11 @@ func Run() (err error) {
Flags: []cli.Flag{
&cli.StringFlag{Name: "host", Usage: "host of the relay"},
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
&cli.IntFlag{Name: "port", Value: 9009, Usage: "base port for the relay"},
&cli.IntFlag{Name: "transfers", Value: 5, Usage: "number of ports to use for relay"},
},
},
}
app.Flags = []cli.Flag{
&cli.BoolFlag{Name: "internal-dns", Usage: "use a built-in DNS stub resolver rather than the host operating system"},
&cli.BoolFlag{Name: "classic", Usage: "toggle between the classic mode (insecure due to local attack vector) and new mode (secure)"},
&cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
&cli.BoolFlag{Name: "debug", Usage: "toggle debug mode"},
&cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
@ -107,9 +99,8 @@ func Run() (err error) {
&cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite or resume"},
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite"},
&cli.BoolFlag{Name: "testing", Usage: "flag for testing purposes"},
&cli.StringFlag{Name: "multicast", Value: "239.255.255.250", Usage: "multicast address to use for local discovery"},
&cli.StringFlag{Name: "curve", Value: "p256", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
@ -133,61 +124,6 @@ func Run() (err error) {
return true
}
// check if "classic" is set
classicFile := getClassicConfigFile(true)
classicInsecureMode := utils.Exists(classicFile)
if c.Bool("classic") {
if classicInsecureMode {
// classic mode not enabled
fmt.Print(`Classic mode is currently ENABLED.
Disabling this mode will prevent the shared secret from being visible
on the host's process list when passed via the command line. On a
multi-user system, this will help ensure that other local users cannot
access the shared secret and receive the files instead of the intended
recipient.
Do you wish to continue to DISABLE the classic mode? (y/N) `)
choice := strings.ToLower(utils.GetInput(""))
if choice == "y" || choice == "yes" {
os.Remove(classicFile)
fmt.Print("\nClassic mode DISABLED.\n\n")
fmt.Print(`To send and receive, export the CROC_SECRET variable with the code phrase:
Send: CROC_SECRET=*** croc send file.txt
Receive: CROC_SECRET=*** croc` + "\n\n")
} else {
fmt.Print("\nClassic mode ENABLED.\n")
}
} else {
// enable classic mode
// touch the file
fmt.Print(`Classic mode is currently DISABLED.
Please note that enabling this mode will make the shared secret visible
on the host's process list when passed via the command line. On a
multi-user system, this could allow other local users to access the
shared secret and receive the files instead of the intended recipient.
Do you wish to continue to enable the classic mode? (y/N) `)
choice := strings.ToLower(utils.GetInput(""))
if choice == "y" || choice == "yes" {
fmt.Print("\nClassic mode ENABLED.\n\n")
os.WriteFile(classicFile, []byte("enabled"), 0o644)
fmt.Print(`To send and receive, use the code phrase:
Send: croc send --code *** file.txt
Receive: croc ***` + "\n\n")
} else {
fmt.Print("\nClassic mode DISABLED.\n")
}
}
os.Exit(0)
}
// if trying to send but forgot send, let the user know
if c.Args().Present() && allStringsAreFiles(c.Args().Slice()) {
fnames := []string{}
@ -201,7 +137,6 @@ Do you wish to continue to enable the classic mode? (y/N) `)
return send(c)
}
}
return receive(c)
}
@ -212,21 +147,13 @@ func setDebugLevel(c *cli.Context) {
if c.Bool("debug") {
log.SetLevel("debug")
log.Debug("debug mode on")
// print the public IP address
ip, err := utils.PublicIP()
if err == nil {
log.Debugf("public IP address: %s", ip)
} else {
log.Debug(err)
}
} else {
log.SetLevel("info")
}
}
func getSendConfigFile(requireValidPath bool) string {
configFile, err := utils.GetConfigDir(requireValidPath)
func getConfigFile() string {
configFile, err := utils.GetConfigDir()
if err != nil {
log.Error(err)
return ""
@ -234,24 +161,6 @@ func getSendConfigFile(requireValidPath bool) string {
return path.Join(configFile, "send.json")
}
func getClassicConfigFile(requireValidPath bool) string {
configFile, err := utils.GetConfigDir(requireValidPath)
if err != nil {
log.Error(err)
return ""
}
return path.Join(configFile, "classic_enabled")
}
func getReceiveConfigFile(requireValidPath bool) (string, error) {
configFile, err := utils.GetConfigDir(requireValidPath)
if err != nil {
log.Error(err)
return "", err
}
return path.Join(configFile, "receive.json"), nil
}
func determinePass(c *cli.Context) (pass string) {
pass = c.String("pass")
b, err := os.ReadFile(pass)
@ -265,53 +174,40 @@ func send(c *cli.Context) (err error) {
setDebugLevel(c)
comm.Socks5Proxy = c.String("socks5")
comm.HttpProxy = c.String("connect")
portParam := c.Int("port")
if portParam == 0 {
portParam = 9009
portsString := c.String("ports")
if portsString == "" {
portsString = "9009,9010,9011,9012,9013"
}
transfersParam := c.Int("transfers")
if transfersParam == 0 {
transfersParam = 4
}
ports := make([]string, transfersParam+1)
for i := 0; i <= transfersParam; i++ {
ports[i] = strconv.Itoa(portParam + i)
}
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: true,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
DisableLocal: c.Bool("no-local"),
OnlyLocal: c.Bool("local"),
IgnoreStdin: c.Bool("ignore-stdin"),
RelayPorts: ports,
Ask: c.Bool("ask"),
NoMultiplexing: c.Bool("no-multi"),
RelayPassword: determinePass(c),
SendingText: c.String("text") != "",
NoCompress: c.Bool("no-compress"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
HashAlgorithm: c.String("hash"),
ThrottleUpload: c.String("throttleUpload"),
ZipFolder: c.Bool("zip"),
GitIgnore: c.Bool("git"),
ShowQrCode: c.Bool("qrcode"),
MulticastAddress: c.String("multicast"),
SharedSecret: c.String("code"),
IsSender: true,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
DisableLocal: c.Bool("no-local"),
OnlyLocal: c.Bool("local"),
IgnoreStdin: c.Bool("ignore-stdin"),
RelayPorts: strings.Split(portsString, ","),
Ask: c.Bool("ask"),
NoMultiplexing: c.Bool("no-multi"),
RelayPassword: determinePass(c),
SendingText: c.String("text") != "",
NoCompress: c.Bool("no-compress"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
HashAlgorithm: c.String("hash"),
ThrottleUpload: c.String("throttleUpload"),
ZipFolder: c.Bool("zip"),
GitIgnore: c.Bool("git"),
}
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
crocOptions.RelayAddress6 = ""
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
crocOptions.RelayAddress = ""
}
b, errOpen := os.ReadFile(getSendConfigFile(false))
b, errOpen := os.ReadFile(getConfigFile())
if errOpen == nil && !c.Bool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
@ -362,7 +258,6 @@ func send(c *cli.Context) (err error) {
if err != nil {
return
}
utils.MarkFileForRemoval(fnames[0])
defer func() {
e := os.Remove(fnames[0])
if e != nil {
@ -374,7 +269,6 @@ func send(c *cli.Context) (err error) {
if err != nil {
return
}
utils.MarkFileForRemoval(fnames[0])
defer func() {
e := os.Remove(fnames[0])
if e != nil {
@ -389,31 +283,6 @@ func send(c *cli.Context) (err error) {
return errors.New("must specify file: croc send [filename(s) or folder]")
}
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
if !classicInsecureMode {
// if operating system is UNIX, then use environmental variable to set the code
if (!(runtime.GOOS == "windows") && c.IsSet("code")) || os.Getenv("CROC_SECRET") != "" {
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
if crocOptions.SharedSecret == "" {
fmt.Printf(`On UNIX systems, to send with a custom code phrase,
you need to set the environmental variable CROC_SECRET:
CROC_SECRET=**** croc send file.txt
Or you can have the code phrase automatically generated:
croc send file.txt
Or you can go back to the classic croc behavior by enabling classic mode:
croc --classic
`)
os.Exit(0)
}
}
}
if len(crocOptions.SharedSecret) == 0 {
// generate code phrase
crocOptions.SharedSecret = utils.GetRandomName()
@ -474,7 +343,7 @@ func makeTempFileWithString(s string) (fnames []string, err error) {
func saveConfig(c *cli.Context, crocOptions croc.Options) {
if c.Bool("remember") {
configFile := getSendConfigFile(true)
configFile := getConfigFile()
log.Debug("saving config file")
var bConfig []byte
// if the code wasn't set, don't save it
@ -529,21 +398,20 @@ func receive(c *cli.Context) (err error) {
comm.Socks5Proxy = c.String("socks5")
comm.HttpProxy = c.String("connect")
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: false,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
Ask: c.Bool("ask"),
RelayPassword: determinePass(c),
OnlyLocal: c.Bool("local"),
IP: c.String("ip"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
TestFlag: c.Bool("testing"),
MulticastAddress: c.String("multicast"),
SharedSecret: c.String("code"),
IsSender: false,
Debug: c.Bool("debug"),
NoPrompt: c.Bool("yes"),
RelayAddress: c.String("relay"),
RelayAddress6: c.String("relay6"),
Stdout: c.Bool("stdout"),
Ask: c.Bool("ask"),
RelayPassword: determinePass(c),
OnlyLocal: c.Bool("local"),
IP: c.String("ip"),
Overwrite: c.Bool("overwrite"),
Curve: c.String("curve"),
TestFlag: c.Bool("testing"),
}
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
crocOptions.RelayAddress6 = ""
@ -555,8 +423,6 @@ func receive(c *cli.Context) (err error) {
case 1:
crocOptions.SharedSecret = c.Args().First()
case 3:
fallthrough
case 4:
var phrase []string
phrase = append(phrase, c.Args().First())
phrase = append(phrase, c.Args().Tail()...)
@ -565,14 +431,14 @@ func receive(c *cli.Context) (err error) {
// load options here
setDebugLevel(c)
doRemember := c.Bool("remember")
configFile, err := getReceiveConfigFile(doRemember)
if err != nil && doRemember {
configFile, err := utils.GetConfigDir()
if err != nil {
log.Error(err)
return
}
configFile = path.Join(configFile, "receive.json")
b, errOpen := os.ReadFile(configFile)
if errOpen == nil && !doRemember {
if errOpen == nil && !c.Bool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
if err != nil {
@ -606,31 +472,6 @@ func receive(c *cli.Context) (err error) {
}
}
classicInsecureMode := utils.Exists(getClassicConfigFile(true))
if crocOptions.SharedSecret == "" && os.Getenv("CROC_SECRET") != "" {
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
} else if !(runtime.GOOS == "windows") && crocOptions.SharedSecret != "" && !classicInsecureMode {
crocOptions.SharedSecret = os.Getenv("CROC_SECRET")
if crocOptions.SharedSecret == "" {
fmt.Printf(`On UNIX systems, to receive with croc you either need
to set a code phrase using your environmental variables:
CROC_SECRET=**** croc
Or you can specify the code phrase when you run croc without
declaring the secret on the command line:
croc
Enter receive code: ****
Or you can go back to the classic croc behavior by enabling classic mode:
croc --classic
`)
os.Exit(0)
}
}
if crocOptions.SharedSecret == "" {
l, err := readline.NewEx(&readline.Config{
Prompt: "Enter receive code: ",
@ -656,7 +497,7 @@ Or you can go back to the classic croc behavior by enabling classic mode:
}
// save the config
if doRemember {
if c.Bool("remember") {
log.Debug("saving config file")
var bConfig []byte
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
@ -683,25 +524,7 @@ func relay(c *cli.Context) (err error) {
debugString = "debug"
}
host := c.String("host")
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)
}
}
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
for i, port := range ports {
if i == 0 {

View File

@ -11,7 +11,7 @@ import (
"time"
"github.com/magisterquis/connectproxy"
"github.com/schollz/croc/v10/src/utils"
"github.com/schollz/croc/v9/src/utils"
log "github.com/schollz/logger"
"golang.org/x/net/proxy"
)

View File

@ -3,41 +3,36 @@ package croc
import (
"bytes"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"math"
"net"
"os"
"os/exec"
"path"
"path/filepath"
"runtime"
"strconv"
"strings"
"sync"
"time"
"golang.org/x/time/rate"
"github.com/denisbrodbeck/machineid"
ignore "github.com/sabhiram/go-gitignore"
log "github.com/schollz/logger"
"github.com/schollz/pake/v3"
"github.com/schollz/peerdiscovery"
"github.com/schollz/progressbar/v3"
"github.com/skip2/go-qrcode"
"golang.org/x/term"
"golang.org/x/time/rate"
"github.com/schollz/croc/v10/src/comm"
"github.com/schollz/croc/v10/src/compress"
"github.com/schollz/croc/v10/src/crypt"
"github.com/schollz/croc/v10/src/message"
"github.com/schollz/croc/v10/src/models"
"github.com/schollz/croc/v10/src/tcp"
"github.com/schollz/croc/v10/src/utils"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/compress"
"github.com/schollz/croc/v9/src/crypt"
"github.com/schollz/croc/v9/src/message"
"github.com/schollz/croc/v9/src/models"
"github.com/schollz/croc/v9/src/tcp"
"github.com/schollz/croc/v9/src/utils"
)
var (
@ -60,38 +55,30 @@ func Debug(debug bool) {
// Options specifies user specific options
type Options struct {
IsSender bool
SharedSecret string
RoomName string
Debug bool
RelayAddress string
RelayAddress6 string
RelayPorts []string
RelayPassword string
Stdout bool
NoPrompt bool
NoMultiplexing bool
DisableLocal bool
OnlyLocal bool
IgnoreStdin bool
Ask bool
SendingText bool
NoCompress bool
IP string
Overwrite bool
Curve string
HashAlgorithm string
ThrottleUpload string
ZipFolder bool
TestFlag bool
GitIgnore bool
MulticastAddress string
ShowQrCode bool
}
type SimpleMessage struct {
Bytes []byte
Kind string
IsSender bool
SharedSecret string
Debug bool
RelayAddress string
RelayAddress6 string
RelayPorts []string
RelayPassword string
Stdout bool
NoPrompt bool
NoMultiplexing bool
DisableLocal bool
OnlyLocal bool
IgnoreStdin bool
Ask bool
SendingText bool
NoCompress bool
IP string
Overwrite bool
Curve string
HashAlgorithm string
ThrottleUpload string
ZipFolder bool
TestFlag bool
GitIgnore bool
}
// Client holds the state of the croc transfer
@ -195,15 +182,12 @@ func New(ops Options) (c *Client, err error) {
// setup basic info
c.Options = ops
Debug(c.Options.Debug)
log.Debugf("options: %+v", c.Options)
if len(c.Options.SharedSecret) < 6 {
err = fmt.Errorf("code is too short")
return
}
// Create a hash of part of the shared secret to use as the room name
hashExtra := "croc"
roomNameBytes := sha256.Sum256([]byte(c.Options.SharedSecret[:4] + hashExtra))
c.Options.RoomName = hex.EncodeToString(roomNameBytes[:])
c.conn = make([]*comm.Comm, 16)
@ -230,8 +214,8 @@ func New(ops Options) (c *Client, err error) {
panic("Could not parse given Upload Limit")
}
}
rt = rate.Every(time.Second / time.Duration(uploadLimit))
// Somehow 4* is necessary
rt = rate.Every(time.Second / (4 * time.Duration(uploadLimit)))
if int(uploadLimit) > minBurstSize {
minBurstSize = int(uploadLimit)
}
@ -310,6 +294,7 @@ func isChild(parentPath, childPath string) bool {
return false
}
return !strings.HasPrefix(relPath, "..")
}
// This function retrieves the important file information
@ -332,7 +317,7 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
paths = append(paths, fname)
}
}
ignoredPaths := make(map[string]bool)
var ignoredPaths = make(map[string]bool)
if ignoreGit {
wd, wdErr := os.Stat(".gitignore")
if wdErr == nil {
@ -391,7 +376,6 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
fpath = filepath.Dir(fpath)
dest := filepath.Base(fpath) + ".zip"
utils.ZipDirectory(dest, fpath)
utils.MarkFileForRemoval(dest)
stat, errStat = os.Lstat(dest)
if errStat != nil {
err = errStat
@ -426,14 +410,8 @@ func GetFilesInfo(fnames []string, zipfolder bool, ignoreGit bool) (filesInfo []
if err != nil {
return err
}
absPathWithSeparator := filepath.Dir(absPath)
if !strings.HasSuffix(absPathWithSeparator, string(os.PathSeparator)) {
absPathWithSeparator += string(os.PathSeparator)
}
if strings.HasSuffix(absPathWithSeparator, string(os.PathSeparator)+string(os.PathSeparator)) {
absPathWithSeparator = strings.TrimSuffix(absPathWithSeparator, string(os.PathSeparator))
}
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName), absPathWithSeparator)
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName),
filepath.Dir(absPath)+string(os.PathSeparator))
if !info.IsDir() {
fInfo := FileInfo{
Name: info.Name(),
@ -517,7 +495,7 @@ func (c *Client) sendCollectFiles(filesInfo []FileInfo) (err error) {
c.Options.HashAlgorithm = "xxhash"
}
c.FilesToTransfer[i].Hash, err = utils.HashFile(fullPath, c.Options.HashAlgorithm, fileInfo.Size > 1e7)
c.FilesToTransfer[i].Hash, err = utils.HashFile(fullPath, c.Options.HashAlgorithm)
log.Debugf("hashed %s to %x using %s", fullPath, c.FilesToTransfer[i].Hash, c.Options.HashAlgorithm)
totalFilesSize += fileInfo.Size
if err != nil {
@ -590,8 +568,6 @@ func (c *Client) broadcastOnLocalNetwork(useipv6 bool) {
}
if useipv6 {
settings.IPVersion = peerdiscovery.IPv6
} else {
settings.MulticastAddress = c.Options.MulticastAddress
}
discoveries, err := peerdiscovery.Discover(settings)
@ -606,7 +582,7 @@ func (c *Client) transferOverLocalRelay(errchan chan<- error) {
time.Sleep(500 * time.Millisecond)
log.Debug("establishing connection")
var banner string
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.RoomName)
conn, banner, ipaddr, err := tcp.ConnectToTCPServer("127.0.0.1:"+c.Options.RelayPorts[0], c.Options.RelayPassword, c.Options.SharedSecret[:3])
log.Debugf("banner: %s", banner)
if err != nil {
err = fmt.Errorf("could not connect to 127.0.0.1:%s: %w", c.Options.RelayPorts[0], err)
@ -647,24 +623,13 @@ func (c *Client) Send(filesInfo []FileInfo, emptyFoldersToTransfer []FileInfo, t
return
}
flags := &strings.Builder{}
if c.Options.RelayAddress != models.DEFAULT_RELAY && !c.Options.OnlyLocal {
if c.Options.RelayAddress != models.DEFAULT_RELAY {
flags.WriteString("--relay " + c.Options.RelayAddress + " ")
}
if c.Options.RelayPassword != models.DEFAULT_PASSPHRASE {
flags.WriteString("--pass " + c.Options.RelayPassword + " ")
}
fmt.Fprintf(os.Stderr, `Code is: %[1]s
On the other computer run:
(For Windows)
croc %[2]s%[1]s
(For Linux/OSX)
CROC_SECRET=%[1]q croc %[2]s
`, c.Options.SharedSecret, flags.String())
copyToClipboard(c.Options.SharedSecret)
if c.Options.ShowQrCode {
showReceiveCommandQrCode(fmt.Sprintf("%[1]s", c.Options.SharedSecret))
}
fmt.Fprintf(os.Stderr, "Code is: %[1]s\nOn the other computer run\n\ncroc %[2]s%[1]s\n", c.Options.SharedSecret, flags.String())
if c.Options.Ask {
machid, _ := machineid.ID()
fmt.Fprintf(os.Stderr, "\rYour machine ID is '%s'\n", machid)
@ -705,7 +670,7 @@ On the other computer run:
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address)
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RoomName, durations[i])
conn, banner, ipaddr, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
if err == nil {
c.Options.RelayAddress = address
break
@ -723,34 +688,13 @@ On the other computer run:
}
log.Debugf("banner: %s", banner)
log.Debugf("connection established: %+v", conn)
var kB []byte
B, _ := pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 1, c.Options.Curve)
for {
var dataMessage SimpleMessage
log.Trace("waiting for bytes")
log.Debug("waiting for bytes")
data, errConn := conn.Receive()
if errConn != nil {
log.Tracef("[%+v] had error: %s", conn, errConn.Error())
}
json.Unmarshal(data, &dataMessage)
log.Tracef("data: %+v '%s'", data, data)
log.Tracef("dataMessage: %s", dataMessage)
log.Tracef("kB: %x", kB)
// if kB not null, then use it to decrypt
if kB != nil {
var decryptErr error
var dataDecrypt []byte
dataDecrypt, decryptErr = crypt.Decrypt(data, kB)
if decryptErr != nil {
log.Tracef("error decrypting: %v: '%s'", decryptErr, data)
} else {
// copy dataDecrypt to data
data = dataDecrypt
log.Tracef("decrypted: %s", data)
}
log.Debugf("[%+v] had error: %s", conn, errConn.Error())
}
if bytes.Equal(data, ipRequest) {
log.Tracef("got ipRequest")
// recipient wants to try to connect to local ips
var ips []string
// only get local ips if the local is enabled
@ -758,48 +702,22 @@ On the other computer run:
// get list of local ips
ips, err = utils.GetLocalIPs()
if err != nil {
log.Tracef("error getting local ips: %v", err)
log.Debugf("error getting local ips: %v", err)
}
// prepend the port that is being listened to
ips = append([]string{c.Options.RelayPorts[0]}, ips...)
}
log.Tracef("sending ips: %+v", ips)
bips, errIps := json.Marshal(ips)
if errIps != nil {
log.Tracef("error marshalling ips: %v", errIps)
}
bips, errIps = crypt.Encrypt(bips, kB)
if errIps != nil {
log.Tracef("error encrypting ips: %v", errIps)
}
bips, _ := json.Marshal(ips)
if err = conn.Send(bips); err != nil {
log.Errorf("error sending: %v", err)
}
} else if dataMessage.Kind == "pake1" {
log.Trace("got pake1")
var pakeError error
pakeError = B.Update(dataMessage.Bytes)
if pakeError == nil {
kB, pakeError = B.SessionKey()
if pakeError == nil {
log.Tracef("dataMessage kB: %x", kB)
dataMessage.Bytes = B.Bytes()
dataMessage.Kind = "pake2"
data, _ = json.Marshal(dataMessage)
if pakeError = conn.Send(data); err != nil {
log.Errorf("dataMessage error sending: %v", err)
}
}
}
} else if bytes.Equal(data, handshakeRequest) {
log.Trace("got handshake")
break
} else if bytes.Equal(data, []byte{1}) {
log.Trace("got ping")
log.Debug("got ping")
continue
} else {
log.Tracef("[%+v] got weird bytes: %+v", conn, data)
log.Debugf("[%+v] got weird bytes: %+v", conn, data)
// throttle the reading
errchan <- fmt.Errorf("gracefully refusing using the public relay")
return
@ -837,13 +755,6 @@ On the other computer run:
return err
}
func showReceiveCommandQrCode(command string) {
qrCode, err := qrcode.New(command, qrcode.Medium)
if err == nil {
fmt.Println(qrCode.ToSmallString(false))
}
}
// Receive will receive a file
func (c *Client) Receive() (err error) {
fmt.Fprintf(os.Stderr, "connecting...")
@ -879,11 +790,10 @@ func (c *Client) Receive() (err error) {
go func() {
defer wgDiscovery.Done()
ipv4discoveries, err1 := peerdiscovery.Discover(peerdiscovery.Settings{
Limit: 1,
Payload: []byte("ok"),
Delay: 20 * time.Millisecond,
TimeLimit: 200 * time.Millisecond,
MulticastAddress: c.Options.MulticastAddress,
Limit: 1,
Payload: []byte("ok"),
Delay: 20 * time.Millisecond,
TimeLimit: 200 * time.Millisecond,
})
if err1 == nil && len(ipv4discoveries) > 0 {
dmux.Lock()
@ -957,7 +867,7 @@ func (c *Client) Receive() (err error) {
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
log.Debugf("trying connection to %s", address)
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.RoomName, durations[i])
c.conn[0], banner, c.ExternalIP, err = tcp.ConnectToTCPServer(address, c.Options.RelayPassword, c.Options.SharedSecret[:3], durations[i])
if err == nil {
c.Options.RelayAddress = address
break
@ -978,68 +888,20 @@ func (c *Client) Receive() (err error) {
if c.Options.TestFlag || (!usingLocal && !c.Options.DisableLocal && !isIPset) {
// ask the sender for their local ips and port
// and try to connect to them
var ips []string
err = func() (err error) {
var A *pake.Pake
var data []byte
A, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
if err != nil {
return err
}
dataMessage := SimpleMessage{
Bytes: A.Bytes(),
Kind: "pake1",
}
data, _ = json.Marshal(dataMessage)
if err = c.conn[0].Send(data); err != nil {
log.Errorf("dataMessage send error: %v", err)
return
}
data, err = c.conn[0].Receive()
if err != nil {
return
}
err = json.Unmarshal(data, &dataMessage)
if err != nil || dataMessage.Kind != "pake2" {
log.Debugf("data: %s", data)
return fmt.Errorf("dataMessage %s pake failed", ipRequest)
}
err = A.Update(dataMessage.Bytes)
if err != nil {
return
}
var kA []byte
kA, err = A.SessionKey()
if err != nil {
return
}
log.Debugf("dataMessage kA: %x", kA)
// secure ipRequest
data, err = crypt.Encrypt([]byte(ipRequest), kA)
if err != nil {
return
}
log.Debug("sending ips?")
if err = c.conn[0].Send(data); err != nil {
log.Errorf("ips send error: %v", err)
}
data, err = c.conn[0].Receive()
if err != nil {
return
}
data, err = crypt.Decrypt(data, kA)
if err != nil {
return
}
log.Debugf("ips data: %s", data)
if err = json.Unmarshal(data, &ips); err != nil {
log.Debugf("ips unmarshal error: %v", err)
}
log.Debug("sending ips?")
var data []byte
if err = c.conn[0].Send(ipRequest); err != nil {
log.Errorf("ips send error: %v", err)
}
data, err = c.conn[0].Receive()
if err != nil {
return
}()
}
log.Debugf("ips data: %s", data)
var ips []string
if err = json.Unmarshal(data, &ips); err != nil {
log.Debugf("ips unmarshal error: %v", err)
}
if len(ips) > 1 {
port := ips[0]
ips = ips[1:]
@ -1063,7 +925,7 @@ func (c *Client) Receive() (err error) {
}
serverTry := net.JoinHostPort(ip, port)
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.RoomName, 500*time.Millisecond)
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 500*time.Millisecond)
if errConn != nil {
log.Debug(errConn)
log.Debugf("could not connect to " + serverTry)
@ -1096,7 +958,7 @@ func (c *Client) Receive() (err error) {
err = c.transfer()
if err == nil {
if c.numberOfTransferredFiles+len(c.EmptyFoldersToTransfer) == 0 {
fmt.Fprintf(os.Stderr, "\rNo files transferred.\n")
fmt.Fprintf(os.Stderr, "\rNo files transferred.")
}
}
return
@ -1135,7 +997,6 @@ func (c *Client) transfer() (err error) {
}
done, err = c.processMessage(data)
if err != nil {
log.Debugf("data: %s", data)
log.Debugf("got error processing: %v", err)
break
}
@ -1183,7 +1044,7 @@ func (c *Client) transfer() (err error) {
if err = os.Remove(pathToFile); err != nil {
log.Warnf("error removing %s: %v", pathToFile, err)
}
fmt.Fprint(os.Stderr, "\n")
fmt.Print("\n")
}
if err != nil && strings.Contains(err.Error(), "pake not successful") {
log.Debugf("pake error: %s", err.Error())
@ -1191,7 +1052,7 @@ func (c *Client) transfer() (err error) {
}
if err != nil && strings.Contains(err.Error(), "unexpected end of JSON input") {
log.Debugf("error: %s", err.Error())
err = fmt.Errorf("room (secure channel) not ready, maybe peer disconnected")
err = fmt.Errorf("room not ready")
}
return
}
@ -1231,31 +1092,6 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
c.EmptyFoldersToTransfer = senderInfo.EmptyFoldersToTransfer
c.TotalNumberFolders = senderInfo.TotalNumberFolders
c.FilesToTransfer = senderInfo.FilesToTransfer
for i, fi := range c.FilesToTransfer {
// Issues #593 - sanitize the sender paths and prevent ".." from being used
c.FilesToTransfer[i].FolderRemote = filepath.Clean(fi.FolderRemote)
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "../") {
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
}
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "/..") {
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
}
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "\\..") {
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
}
if strings.Contains(c.FilesToTransfer[i].FolderRemote, "..\\") {
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
}
// Issues #593 - disallow specific folders like .ssh
if strings.Contains(c.FilesToTransfer[i].FolderRemote, ".ssh") {
return true, fmt.Errorf("invalid path detected: '%s'", fi.FolderRemote)
}
// Issue #595 - disallow filenames with invisible characters
errFileName := utils.ValidFileName(path.Join(c.FilesToTransfer[i].FolderRemote, fi.Name))
if errFileName != nil {
return true, errFileName
}
}
c.TotalNumberOfContents = 0
if c.FilesToTransfer != nil {
c.TotalNumberOfContents += len(c.FilesToTransfer)
@ -1293,12 +1129,6 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
}
}
}
// // check the totalSize does not exceed disk space
// usage := diskusage.NewDiskUsage(".")
// if usage.Available() < uint64(totalSize) {
// return true, fmt.Errorf("not enough disk space")
// }
// c.spinner.Stop()
action := "Accept"
if c.Options.SendingText {
@ -1445,7 +1275,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
c.conn[j+1], _, _, err = tcp.ConnectToTCPServer(
server,
c.Options.RelayPassword,
fmt.Sprintf("%s-%d", c.Options.RoomName, j),
fmt.Sprintf("%s-%d", utils.SHA256(c.Options.SharedSecret[:5])[:6], j),
)
if err != nil {
panic(err)
@ -1686,7 +1516,6 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
}
c.SuccessfulTransfer = true
c.FilesHasFinished[c.FilesToTransferCurrentNum] = struct{}{}
return
}
err = c.recipientInitializeFile()
@ -1722,25 +1551,6 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
return
}
func max(a int, b int) int {
if a > b {
return a
}
return b
}
func formatDescription(description string) string {
width, _, err := term.GetSize(int(os.Stdout.Fd()))
width = max(20, width-60)
if err != nil {
return description
}
if len(description) > width {
description = description[:(width-3)] + "..."
}
return description
}
func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error) {
log.Debugf("touching file with folder / name")
if !utils.Exists(fileInfo.FolderRemote) {
@ -1783,7 +1593,7 @@ func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error)
c.fmtPrintUpdate()
}),
progressbar.OptionSetWidth(20),
progressbar.OptionSetDescription(formatDescription(description)),
progressbar.OptionSetDescription(description),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionShowBytes(true),
progressbar.OptionShowCount(),
@ -1839,13 +1649,13 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
percentDone := 100 - float64(len(missingChunks)*models.TCP_BUFFER_SIZE/2)/float64(fileInfo.Size)*100
log.Debug("asking to overwrite")
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) (use --overwrite to omit) ", path.Join(fileInfo.FolderRemote, fileInfo.Name))
prompt := fmt.Sprintf("\nOverwrite '%s'? (y/N) ", path.Join(fileInfo.FolderRemote, fileInfo.Name))
if percentDone < 99 {
prompt = fmt.Sprintf("\nResume '%s' (%2.1f%%)? (y/N) (use --overwrite to omit) ", path.Join(fileInfo.FolderRemote, fileInfo.Name), percentDone)
prompt = fmt.Sprintf("\nResume '%s' (%2.1f%%)? (y/N) ", path.Join(fileInfo.FolderRemote, fileInfo.Name), percentDone)
}
choice := strings.ToLower(utils.GetInput(prompt))
if choice != "y" && choice != "yes" {
fmt.Fprintf(os.Stderr, "Skipping '%s'\n", path.Join(fileInfo.FolderRemote, fileInfo.Name))
fmt.Fprintf(os.Stderr, "skipping '%s'", path.Join(fileInfo.FolderRemote, fileInfo.Name))
continue
}
}
@ -1907,13 +1717,12 @@ func (c *Client) updateState() (err error) {
description = c.FilesToTransfer[i].Name
// description = ""
}
c.bar = progressbar.NewOptions64(1,
progressbar.OptionOnCompletion(func() {
c.fmtPrintUpdate()
}),
progressbar.OptionSetWidth(20),
progressbar.OptionSetDescription(formatDescription(description)),
progressbar.OptionSetDescription(description),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionShowBytes(true),
progressbar.OptionShowCount(),
@ -1961,7 +1770,7 @@ func (c *Client) setBar() {
c.fmtPrintUpdate()
}),
progressbar.OptionSetWidth(20),
progressbar.OptionSetDescription(formatDescription(description)),
progressbar.OptionSetDescription(description),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionShowBytes(true),
progressbar.OptionShowCount(),
@ -1982,14 +1791,14 @@ func (c *Client) setBar() {
}
func (c *Client) receiveData(i int) {
log.Tracef("%d receiving data", i)
log.Debugf("%d receiving data", i)
for {
data, err := c.conn[i+1].Receive()
if err != nil {
break
}
if bytes.Equal(data, []byte{1}) {
log.Trace("got ping")
log.Debug("got ping")
continue
}
@ -2065,66 +1874,62 @@ func (c *Client) sendData(i int) {
curi := float64(0)
for {
// Read file
var n int
var errRead error
if math.Mod(curi, float64(len(c.Options.RelayPorts))) == float64(i) {
data := make([]byte, models.TCP_BUFFER_SIZE/2)
n, errRead = c.fread.ReadAt(data, readingPos)
if c.limiter != nil {
r := c.limiter.ReserveN(time.Now(), n)
log.Debugf("Limiting Upload for %d", r.Delay())
time.Sleep(r.Delay())
}
if n > 0 {
// check to see if this is a chunk that the recipient wants
usableChunk := true
c.mutex.Lock()
if len(c.chunkMap) != 0 {
if _, ok := c.chunkMap[pos]; !ok {
usableChunk = false
} else {
delete(c.chunkMap, pos)
}
}
c.mutex.Unlock()
if usableChunk {
// log.Debugf("sending chunk %d", pos)
posByte := make([]byte, 8)
binary.LittleEndian.PutUint64(posByte, pos)
var err error
var dataToSend []byte
if c.Options.NoCompress {
dataToSend, err = crypt.Encrypt(
append(posByte, data[:n]...),
c.Key,
)
} else {
dataToSend, err = crypt.Encrypt(
compress.Compress(
append(posByte, data[:n]...),
),
c.Key,
)
}
if err != nil {
panic(err)
}
err = c.conn[i+1].Send(dataToSend)
if err != nil {
panic(err)
}
c.bar.Add(n)
c.TotalSent += int64(n)
// time.Sleep(100 * time.Millisecond)
}
}
}
if n == 0 {
n = models.TCP_BUFFER_SIZE / 2
}
data := make([]byte, models.TCP_BUFFER_SIZE/2)
// log.Debugf("%d trying to read", i)
n, errRead := c.fread.ReadAt(data, readingPos)
// log.Debugf("%d read %d bytes", i, n)
readingPos += int64(n)
if c.limiter != nil {
r := c.limiter.ReserveN(time.Now(), n)
log.Debugf("Limiting Upload for %d", r.Delay())
time.Sleep(r.Delay())
}
if math.Mod(curi, float64(len(c.Options.RelayPorts))) == float64(i) {
// check to see if this is a chunk that the recipient wants
usableChunk := true
c.mutex.Lock()
if len(c.chunkMap) != 0 {
if _, ok := c.chunkMap[pos]; !ok {
usableChunk = false
} else {
delete(c.chunkMap, pos)
}
}
c.mutex.Unlock()
if usableChunk {
// log.Debugf("sending chunk %d", pos)
posByte := make([]byte, 8)
binary.LittleEndian.PutUint64(posByte, pos)
var err error
var dataToSend []byte
if c.Options.NoCompress {
dataToSend, err = crypt.Encrypt(
append(posByte, data[:n]...),
c.Key,
)
} else {
dataToSend, err = crypt.Encrypt(
compress.Compress(
append(posByte, data[:n]...),
),
c.Key,
)
}
if err != nil {
panic(err)
}
err = c.conn[i+1].Send(dataToSend)
if err != nil {
panic(err)
}
c.bar.Add(n)
c.TotalSent += int64(n)
// time.Sleep(100 * time.Millisecond)
}
}
curi++
pos += uint64(n)
@ -2136,27 +1941,3 @@ func (c *Client) sendData(i int) {
}
}
}
func copyToClipboard(str string) {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("clip")
case "darwin":
cmd = exec.Command("pbcopy")
case "linux":
if os.Getenv("XDG_SESSION_TYPE") == "wayland" {
cmd = exec.Command("wl-copy")
} else {
cmd = exec.Command("xclip", "-selection", "clipboard")
}
default:
return
}
cmd.Stdin = bytes.NewReader([]byte(str))
if err := cmd.Run(); err != nil {
log.Debugf("error copying to clipboard: %v", err)
return
}
fmt.Fprintf(os.Stderr, "Code copied to clipboard\n")
}

View File

@ -10,7 +10,7 @@ import (
"testing"
"time"
"github.com/schollz/croc/v10/src/tcp"
"github.com/schollz/croc/v9/src/tcp"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
@ -232,7 +232,7 @@ func TestCrocSymlink(t *testing.T) {
t.Errorf("symlink transfer failed: %s", err.Error())
}
}
func TestCrocIgnoreGit(t *testing.T) {
func testCrocIgnoreGit(t *testing.T) {
log.SetLevel("trace")
defer os.Remove(".gitignore")
time.Sleep(300 * time.Millisecond)

View File

@ -24,20 +24,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++ {

View File

@ -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())
}

View File

@ -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, "%")
}

View File

@ -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())
}

View File

@ -528,7 +528,7 @@ main() {
local autocomplete_install_rcode
croc_bin_name="croc"
croc_version="10.1.1"
croc_version="9.6.6"
croc_dl_ext="tar.gz"
croc_base_url="https://github.com/schollz/croc/releases/download"
prefix="${1}"
@ -577,9 +577,6 @@ main() {
case "${croc_os}" in
"Darwin" ) croc_os="macOS";;
*"BusyBox"* )
croc_os="Linux"
;;
"CYGWIN"* ) croc_os="Windows";
croc_dl_ext="zip";
print_message "== Cygwin is currently unsupported." "error";
@ -592,17 +589,15 @@ main() {
"aarch64" ) croc_arch="ARM64";;
"arm64" ) croc_arch="ARM64";;
"armv7l" ) croc_arch="ARM";;
"armv8l" ) croc_arch="ARM";;
"armv9l" ) croc_arch="ARM";;
"i686" ) croc_arch="32bit";;
* ) croc_arch="unknown";;
esac
croc_file="${croc_bin_name}_v${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
croc_checksum_file="${croc_bin_name}_v${croc_version}_checksums.txt"
croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt"
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}"
echo "${croc_url}" "${tmpdir}" "${croc_file}"
download_file "${croc_url}" "${tmpdir}" "${croc_file}"
download_file_rcode="${?}"
if [[ "${download_file_rcode}" == "0" ]]; then
@ -690,7 +685,7 @@ main() {
print_message "== Install prefix already exists. No need to create it." "info"
fi
[ ! -d "${bash_autocomplete_prefix}/croc" ] && mkdir -p "${bash_autocomplete_prefix}/croc" >/dev/null 2>&1
[ ! -d "/etc/bash_completion.d/croc" ] && mkdir -p "/etc/bash_completion.d/croc"
case "${croc_os}" in
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;
@ -718,27 +713,27 @@ main() {
exit 1
fi
# case "$(basename ${SHELL})" in
# "bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
# autocomplete_install_rcode="${?}";;
# "zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
# autocomplete_install_rcode="${?}";
# print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
# print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
# *) autocomplete_install_rcode="1";;
# esac
case "$(basename ${SHELL})" in
"bash" ) install_file_linux "${tmpdir}/${bash_autocomplete_file}" "${bash_autocomplete_prefix}/croc";
autocomplete_install_rcode="${?}";;
"zsh" ) install_file_linux "${tmpdir}/${zsh_autocomplete_file}" "${zsh_autocomplete_prefix}/zsh_autocomplete_croc";
autocomplete_install_rcode="${?}";
print_message "== You will need to add the following to your ~/.zshrc to enable autocompletion" "info";
print_message "\nPROG=croc\n_CLI_ZSH_AUTOCOMPLETE_HACK=1\nsource /etc/zsh/zsh_autocomplete_croc\n" "info";;
*) autocomplete_install_rcode="1";;
esac
# if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
# print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
# elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
# print_message "== Failed to install ${bash_autocomplete_file}" "error"
# elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
# print_message "== Failed to locate 'install' command" "error"
# elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
# print_message "== Failed to locate 'sudo' command" "error"
# else
# print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
# fi
if [[ "${autocomplete_install_rcode}" == "0" ]] ; then
print_message "== Installed autocompletions for $(basename "${SHELL}")" "ok"
elif [[ "${autocomplete_install_rcode}" == "1" ]]; then
print_message "== Failed to install ${bash_autocomplete_file}" "error"
elif [[ "${autocomplete_install_rcode}" == "20" ]]; then
print_message "== Failed to locate 'install' command" "error"
elif [[ "${autocomplete_install_rcode}" == "21" ]]; then
print_message "== Failed to locate 'sudo' command" "error"
else
print_message "== Install attempt returned an unexpected value of ${autocomplete_install_rcode}" "error"
fi
print_message "== Installation complete" "ok"

View File

@ -3,9 +3,9 @@ package message
import (
"encoding/json"
"github.com/schollz/croc/v10/src/comm"
"github.com/schollz/croc/v10/src/compress"
"github.com/schollz/croc/v10/src/crypt"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/compress"
"github.com/schollz/croc/v9/src/crypt"
log "github.com/schollz/logger"
)

View File

@ -7,8 +7,8 @@ import (
"testing"
"time"
"github.com/schollz/croc/v10/src/comm"
"github.com/schollz/croc/v10/src/crypt"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/crypt"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
@ -84,7 +84,7 @@ 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)
assert.Nil(t, err)
m := Message{Type: TypeMessage, Message: "hello, world"}

View File

@ -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
}

View File

@ -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",
}

View File

@ -6,10 +6,8 @@ import (
"net"
"os"
"path"
"time"
"github.com/schollz/croc/v10/src/utils"
log "github.com/schollz/logger"
"github.com/schollz/croc/v9/src/utils"
)
// TCP_BUFFER_SIZE is the maximum packet size
@ -46,8 +44,8 @@ var publicDNS = []string{
"[2620:119:53::53]", // Cisco OpenDNS
}
func getConfigFile(requireValidPath bool) (fname string, err error) {
configFile, err := utils.GetConfigDir(requireValidPath)
func getConfigFile() (fname string, err error) {
configFile, err := utils.GetConfigDir()
if err != nil {
return
}
@ -56,8 +54,6 @@ func getConfigFile(requireValidPath bool) (fname string, err error) {
}
func init() {
log.SetLevel("info")
log.SetOutput(os.Stderr)
doRemember := false
for _, flag := range os.Args {
if flag == "--internal-dns" {
@ -70,19 +66,18 @@ func init() {
}
if doRemember {
// save in config file
fname, err := getConfigFile(true)
fname, err := getConfigFile()
if err == nil {
f, _ := os.Create(fname)
f.Close()
}
}
if !INTERNAL_DNS {
fname, err := getConfigFile(false)
fname, err := getConfigFile()
if err == nil {
INTERNAL_DNS = utils.Exists(fname)
}
}
log.Trace("Using internal DNS: ", INTERNAL_DNS)
var err error
var addr string
addr, err = lookup(DEFAULT_RELAY)
@ -91,20 +86,17 @@ func init() {
} else {
DEFAULT_RELAY = ""
}
log.Tracef("Default ipv4 relay: %s", addr)
addr, err = lookup(DEFAULT_RELAY6)
if err == nil {
DEFAULT_RELAY6 = net.JoinHostPort(addr, DEFAULT_PORT)
} else {
DEFAULT_RELAY6 = ""
}
log.Tracef("Default ipv6 relay: %s", addr)
}
// Resolve a hostname to an IP address using DNS.
func lookup(address string) (ipaddress string, err error) {
if !INTERNAL_DNS {
log.Tracef("Using local DNS to resolve %s", address)
return localLookupIP(address)
}
type Result struct {
@ -116,13 +108,11 @@ func lookup(address string) (ipaddress string, err error) {
go func(dns string) {
var r Result
r.s, r.err = remoteLookupIP(address, dns)
log.Tracef("Resolved %s to %s using %s", address, r.s, dns)
result <- r
}(dns)
}
for i := 0; i < len(publicDNS); i++ {
ipaddress = (<-result).s
log.Tracef("Resolved %s to %s", address, ipaddress)
if ipaddress != "" {
return
}
@ -131,16 +121,9 @@ func lookup(address string) (ipaddress string, err error) {
return
}
// localLookupIP returns a host's IP address using the local DNS configuration.
// localLookupIP returns a host's IP address based on the local resolver.
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)
ip, err := net.LookupHost(address)
if err != nil {
return
}
@ -150,9 +133,6 @@ func localLookupIP(address string) (ipaddress string, err error) {
// remoteLookupIP returns a host's IP address based on a remote DNS server.
func remoteLookupIP(address, dns string) (ipaddress string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
defer cancel()
r := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, _ string) (net.Conn, error) {
@ -160,7 +140,7 @@ func remoteLookupIP(address, dns string) (ipaddress string, err error) {
return d.DialContext(ctx, network, dns+":53")
},
}
ip, err := r.LookupHost(ctx, address)
ip, err := r.LookupHost(context.Background(), address)
if err != nil {
return
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

View File

@ -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
)

View File

@ -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
}

View File

@ -11,9 +11,9 @@ import (
log "github.com/schollz/logger"
"github.com/schollz/pake/v3"
"github.com/schollz/croc/v10/src/comm"
"github.com/schollz/croc/v10/src/crypt"
"github.com/schollz/croc/v10/src/models"
"github.com/schollz/croc/v9/src/comm"
"github.com/schollz/croc/v9/src/crypt"
"github.com/schollz/croc/v9/src/models"
)
type server struct {
@ -23,11 +23,6 @@ type server struct {
banner string
password string
rooms roomMap
roomCleanupInterval time.Duration
roomTTL time.Duration
stopRoomCleanup chan struct{}
}
type roomInfo struct {
@ -44,55 +39,46 @@ type roomMap struct {
const pingRoom = "pinglkasjdlfjsaldjf"
// newDefaultServer initializes a new server, with some default configuration options
func newDefaultServer() *server {
s := new(server)
s.roomCleanupInterval = DEFAULT_ROOM_CLEANUP_INTERVAL
s.roomTTL = DEFAULT_ROOM_TTL
s.debugLevel = DEFAULT_LOG_LEVEL
s.stopRoomCleanup = make(chan struct{})
return s
}
var timeToRoomDeletion = 10 * time.Minute
// RunWithOptionsAsync asynchronously starts a TCP listener.
func RunWithOptionsAsync(host, port, password string, opts ...serverOptsFunc) error {
s := newDefaultServer()
// Run starts a tcp listener, run async
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
s := new(server)
s.host = host
s.port = port
s.password = password
for _, opt := range opts {
err := opt(s)
if err != nil {
return fmt.Errorf("could not apply optional configurations: %w", err)
}
s.debugLevel = debugLevel
if len(banner) > 0 {
s.banner = banner[0]
}
return s.start()
}
// Run starts a tcp listener, run async
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
return RunWithOptionsAsync(host, port, password, WithBanner(banner...), WithLogLevel(debugLevel))
}
func (s *server) start() (err error) {
log.SetLevel(s.debugLevel)
// 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)
log.Debugf("starting with password '%s'", s.password)
s.rooms.Lock()
s.rooms.rooms = make(map[string]roomInfo)
s.rooms.Unlock()
go s.deleteOldRooms()
defer s.stopRoomDeletion()
// delete old rooms
go func() {
for {
time.Sleep(timeToRoomDeletion)
var roomsToDelete []string
s.rooms.Lock()
for room := range s.rooms.rooms {
if time.Since(s.rooms.rooms[room].opened) > 3*time.Hour {
roomsToDelete = append(roomsToDelete, room)
}
}
s.rooms.Unlock()
for _, room := range roomsToDelete {
s.deleteRoom(room)
}
}
}()
err = s.run()
if err != nil {
@ -187,39 +173,6 @@ func (s *server) run() (err error) {
}
}
// deleteOldRooms checks for rooms at a regular interval and removes those that
// have exceeded their allocated TTL.
func (s *server) deleteOldRooms() {
ticker := time.NewTicker(s.roomCleanupInterval)
for {
select {
case <-ticker.C:
var roomsToDelete []string
s.rooms.Lock()
for room := range s.rooms.rooms {
if time.Since(s.rooms.rooms[room].opened) > s.roomTTL {
roomsToDelete = append(roomsToDelete, room)
}
}
s.rooms.Unlock()
for _, room := range roomsToDelete {
s.deleteRoom(room)
log.Debugf("room cleaned up: %s", room)
}
case <-s.stopRoomCleanup:
ticker.Stop()
log.Debug("room cleanup stopped")
return
}
}
}
func (s *server) stopRoomDeletion() {
log.Debug("stop room cleanup fired")
s.stopRoomCleanup <- struct{}{}
}
var weakKey = []byte{1, 2, 3}
func (s *server) clientCommunication(port string, c *comm.Comm) (room string, err error) {
@ -563,7 +516,7 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
}
banner = strings.Split(string(data), "|||")[0]
ipaddr = strings.Split(string(data), "|||")[1]
log.Debugf("sending room; %s", room)
log.Debug("sending room")
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
if err != nil {
log.Debug(err)

View File

@ -23,8 +23,8 @@ func BenchmarkConnection(b *testing.B) {
func TestTCP(t *testing.T) {
log.SetLevel("error")
timeToRoomDeletion := 100 * time.Millisecond
go RunWithOptionsAsync("127.0.0.1", "8381", "pass123", WithBanner("8382"), WithLogLevel("debug"), WithRoomTTL(timeToRoomDeletion))
timeToRoomDeletion = 100 * time.Millisecond
go Run("debug", "127.0.0.1", "8381", "pass123", "8382")
time.Sleep(timeToRoomDeletion)
err := PingServer("127.0.0.1:8381")
assert.Nil(t, err)

View File

@ -11,6 +11,7 @@ import (
"encoding/hex"
"fmt"
"io"
"log"
"math"
"math/big"
"net"
@ -20,41 +21,32 @@ import (
"path/filepath"
"strings"
"time"
"unicode"
"github.com/cespare/xxhash"
"github.com/kalafut/imohash"
"github.com/minio/highwayhash"
"github.com/schollz/croc/v10/src/mnemonicode"
log "github.com/schollz/logger"
"github.com/schollz/progressbar/v3"
"github.com/schollz/mnemonicode"
)
const NbPinNumbers = 4
const NbBytesWords = 4
// Get or create home directory
func GetConfigDir(requireValidPath bool) (homedir string, err error) {
func GetConfigDir() (homedir string, err error) {
homedir, err = os.UserHomeDir()
if err != nil {
return
}
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)
}
if _, err = os.Stat(homedir); os.IsNotExist(err) {
err = os.MkdirAll(homedir, 0o700)
}
return
}
@ -79,11 +71,7 @@ func GetInput(prompt string) string {
// HashFile returns the hash of a file or, in case of a symlink, the
// SHA256 hash of its target. Takes an argument to specify the algorithm to use.
func HashFile(fname string, algorithm string, showProgress ...bool) (hash256 []byte, err error) {
doShowProgress := false
if len(showProgress) > 0 {
doShowProgress = showProgress[0]
}
func HashFile(fname string, algorithm string) (hash256 []byte, err error) {
var fstats os.FileInfo
fstats, err = os.Lstat(fname)
if err != nil {
@ -101,59 +89,16 @@ func HashFile(fname string, algorithm string, showProgress ...bool) (hash256 []b
case "imohash":
return IMOHashFile(fname)
case "md5":
return MD5HashFile(fname, doShowProgress)
return MD5HashFile(fname)
case "xxhash":
return XXHashFile(fname, doShowProgress)
case "highway":
return HighwayHashFile(fname, doShowProgress)
return XXHashFile(fname)
}
err = fmt.Errorf("unspecified algorithm")
return
}
// HighwayHashFile returns highwayhash of a file
func HighwayHashFile(fname string, doShowProgress bool) (hashHighway []byte, err error) {
f, err := os.Open(fname)
if err != nil {
return
}
defer f.Close()
key, err := hex.DecodeString("1553c5383fb0b86578c3310da665b4f6e0521acf22eb58a99532ffed02a6b115")
if err != nil {
return
}
h, err := highwayhash.New(key)
if err != nil {
err = fmt.Errorf("could not create highwayhash: %s", err.Error())
return
}
if doShowProgress {
stat, _ := f.Stat()
fnameShort := path.Base(fname)
if len(fnameShort) > 20 {
fnameShort = fnameShort[:20] + "..."
}
bar := progressbar.NewOptions64(stat.Size(),
progressbar.OptionSetWriter(os.Stderr),
progressbar.OptionShowBytes(true),
progressbar.OptionSetDescription(fmt.Sprintf("Hashing %s", fnameShort)),
progressbar.OptionClearOnFinish(),
)
if _, err = io.Copy(io.MultiWriter(h, bar), f); err != nil {
return
}
} else {
if _, err = io.Copy(h, f); err != nil {
return
}
}
hashHighway = h.Sum(nil)
return
}
// MD5HashFile returns MD5 hash
func MD5HashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
func MD5HashFile(fname string) (hash256 []byte, err error) {
f, err := os.Open(fname)
if err != nil {
return
@ -161,41 +106,23 @@ func MD5HashFile(fname string, doShowProgress bool) (hash256 []byte, err error)
defer f.Close()
h := md5.New()
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
}
if _, err = io.Copy(h, f); err != nil {
return
}
hash256 = h.Sum(nil)
return
}
var imofull = imohash.NewCustom(0, 0)
var imopartial = imohash.NewCustom(16*16*8*1024, 128*1024)
// IMOHashFile returns imohash
func IMOHashFile(fname string) (hash []byte, err error) {
b, err := imopartial.SumFile(fname)
b, err := imohash.SumFile(fname)
hash = b[:]
return
}
var imofull = imohash.NewCustom(0, 0)
// IMOHashFileFull returns imohash of full file
func IMOHashFileFull(fname string) (hash []byte, err error) {
b, err := imofull.SumFile(fname)
@ -204,7 +131,7 @@ func IMOHashFileFull(fname string) (hash []byte, err error) {
}
// 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)
if err != nil {
return
@ -212,25 +139,8 @@ func XXHashFile(fname string, doShowProgress bool) (hash256 []byte, err error) {
defer f.Close()
h := xxhash.New()
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
}
if _, err = io.Copy(h, f); err != nil {
return
}
hash256 = h.Sum(nil)
@ -246,21 +156,19 @@ func SHA256(s string) string {
// PublicIP returns public ip address
func PublicIP() (ip string, err error) {
// ask ipv4.icanhazip.com for the public ip
// by making http request
// if the request fails, return nothing
resp, err := http.Get("http://ipv4.icanhazip.com")
resp, err := http.Get("https://canhazip.com")
if err != nil {
return
}
defer resp.Body.Close()
// read the body of the response
// and return the ip address
buf := new(bytes.Buffer)
buf.ReadFrom(resp.Body)
ip = strings.TrimSpace(buf.String())
if resp.StatusCode == http.StatusOK {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
ip = strings.TrimSpace(string(bodyBytes))
}
return
}
@ -268,8 +176,7 @@ func PublicIP() (ip string, err error) {
func LocalIP() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
log.Error(err)
return ""
log.Fatal(err)
}
defer conn.Close()
@ -470,12 +377,12 @@ func IsLocalIP(ipaddress string) bool {
func ZipDirectory(destination string, source string) (err error) {
if _, err = os.Stat(destination); err == nil {
log.Errorf("%s file already exists!\n", destination)
log.Fatalf("%s file already exists!\n", destination)
}
fmt.Fprintf(os.Stderr, "Zipping %s to %s\n", source, destination)
file, err := os.Create(destination)
if err != nil {
log.Error(err)
log.Fatalln(err)
}
defer file.Close()
writer := zip.NewWriter(file)
@ -486,22 +393,22 @@ func ZipDirectory(destination string, source string) (err error) {
defer writer.Close()
err = filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
if err != nil {
log.Error(err)
log.Fatalln(err)
}
if info.Mode().IsRegular() {
f1, err := os.Open(path)
if err != nil {
log.Error(err)
log.Fatalln(err)
}
defer f1.Close()
zipPath := strings.ReplaceAll(path, source, strings.TrimSuffix(destination, ".zip"))
zipPath = filepath.ToSlash(zipPath)
w1, err := writer.Create(zipPath)
if err != nil {
log.Error(err)
log.Fatalln(err)
}
if _, err := io.Copy(w1, f1); err != nil {
log.Error(err)
log.Fatalln(err)
}
fmt.Fprintf(os.Stderr, "\r\033[2K")
fmt.Fprintf(os.Stderr, "\rAdding %s", zipPath)
@ -509,7 +416,7 @@ func ZipDirectory(destination string, source string) (err error) {
return nil
})
if err != nil {
log.Error(err)
log.Fatalln(err)
}
fmt.Fprintf(os.Stderr, "\n")
return nil
@ -518,7 +425,7 @@ func ZipDirectory(destination string, source string) (err error) {
func UnzipDirectory(destination string, source string) error {
archive, err := zip.OpenReader(source)
if err != nil {
log.Error(err)
log.Fatalln(err)
}
defer archive.Close()
@ -526,43 +433,27 @@ func UnzipDirectory(destination string, source string) error {
filePath := filepath.Join(destination, f.Name)
fmt.Fprintf(os.Stderr, "\r\033[2K")
fmt.Fprintf(os.Stderr, "\rUnzipping file %s", filePath)
// Issue #593 conceal path traversal vulnerability
// make sure the filepath does not have ".."
filePath = filepath.Clean(filePath)
if strings.Contains(filePath, "..") {
log.Errorf("Invalid file path %s\n", filePath)
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
log.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
}
log.Fatalln(err)
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
log.Error(err)
log.Fatalln(err)
}
fileInArchive, err := f.Open()
if err != nil {
log.Error(err)
log.Fatalln(err)
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
log.Error(err)
log.Fatalln(err)
}
dstFile.Close()
@ -571,67 +462,3 @@ func UnzipDirectory(destination string, source string) error {
fmt.Fprintf(os.Stderr, "\n")
return nil
}
// ValidFileName checks if a filename is valid
// by making sure it has no invisible characters
func ValidFileName(fname string) (err error) {
// make sure it doesn't contain unicode or invisible characters
for _, r := range fname {
if !unicode.IsGraphic(r) {
err = fmt.Errorf("non-graphical unicode: %x U+%d in '%x'", string(r), r, fname)
return
}
if !unicode.IsPrint(r) {
err = fmt.Errorf("non-printable unicode: %x U+%d in '%x'", string(r), r, fname)
return
}
}
// make sure basename does not include ".." or path separators
_, basename := filepath.Split(fname)
if strings.Contains(basename, "..") {
err = fmt.Errorf("basename cannot contain '..': '%s'", basename)
return
}
if strings.Contains(basename, string(os.PathSeparator)) {
err = fmt.Errorf("basename cannot contain path separators: '%s'", basename)
return
}
// make sure the filename is not an absolute path
if filepath.IsAbs(fname) {
err = fmt.Errorf("filename cannot be an absolute path: '%s'", fname)
return
}
return
}
const crocRemovalFile = "croc-marked-files.txt"
func MarkFileForRemoval(fname string) {
// append the fname to the list of files to remove
f, err := os.OpenFile(crocRemovalFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o600)
if err != nil {
log.Debug(err)
return
}
defer f.Close()
_, err = f.WriteString(fname + "\n")
}
func RemoveMarkedFiles() (err error) {
// read the file and remove all the files
f, err := os.Open(crocRemovalFile)
if err != nil {
return
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
fname := scanner.Text()
err = os.Remove(fname)
if err == nil {
log.Tracef("Removed %s", fname)
}
}
os.Remove(crocRemovalFile)
return
}

View File

@ -6,7 +6,6 @@ import (
"log"
"math/rand"
"os"
"path"
"strings"
"testing"
@ -25,7 +24,7 @@ func BenchmarkMD5(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
MD5HashFile("bigfile.test", false)
MD5HashFile("bigfile.test")
}
}
@ -33,7 +32,7 @@ func BenchmarkXXHash(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
XXHashFile("bigfile.test", false)
XXHashFile("bigfile.test")
}
}
@ -45,14 +44,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()
@ -87,20 +78,10 @@ func TestExists(t *testing.T) {
func TestMD5HashFile(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
b, err := MD5HashFile("bigfile.test", false)
b, err := MD5HashFile("bigfile.test")
assert.Nil(t, err)
assert.Equal(t, "8304ff018e02baad0e3555bade29a405", fmt.Sprintf("%x", b))
_, err = MD5HashFile("bigfile.test.nofile", false)
assert.NotNil(t, err)
}
func TestHighwayHashFile(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
b, err := HighwayHashFile("bigfile.test", false)
assert.Nil(t, err)
assert.Equal(t, "3c32999529323ed66a67aeac5720c7bf1301dcc5dca87d8d46595e85ff990329", fmt.Sprintf("%x", b))
_, err = HighwayHashFile("bigfile.test.nofile", false)
_, err = MD5HashFile("bigfile.test.nofile")
assert.NotNil(t, err)
}
@ -109,16 +90,16 @@ func TestIMOHashFile(t *testing.T) {
defer os.Remove("bigfile.test")
b, err := IMOHashFile("bigfile.test")
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) {
bigFile()
defer os.Remove("bigfile.test")
b, err := XXHashFile("bigfile.test", false)
b, err := XXHashFile("bigfile.test")
assert.Nil(t, err)
assert.Equal(t, "4918740eb5ccb6f7", fmt.Sprintf("%x", b))
_, err = XXHashFile("nofile", false)
_, err = XXHashFile("nofile")
assert.NotNil(t, err)
}
@ -227,41 +208,11 @@ func TestGetRandomName(t *testing.T) {
assert.NotEmpty(t, name)
}
func intSliceSame(a, b []int) bool {
if len(a) != len(b) {
return false
}
for i, v := range a {
if v != b[i] {
return false
}
}
return true
}
func TestFindOpenPorts(t *testing.T) {
openPorts := FindOpenPorts("127.0.0.1", 9009, 4)
if !intSliceSame(openPorts, []int{9009, 9010, 9011, 9012}) && !intSliceSame(openPorts, []int{9014, 9015, 9016, 9017}) {
t.Errorf("openPorts: %v", openPorts)
}
assert.Equal(t, []int{9009, 9010, 9011, 9012}, openPorts)
}
func TestIsLocalIP(t *testing.T) {
assert.True(t, IsLocalIP("192.168.0.14:9009"))
}
func TestValidFileName(t *testing.T) {
// contains regular characters
assert.Nil(t, ValidFileName("中文.csl"))
// contains regular characters
assert.Nil(t, ValidFileName("[something].csl"))
// contains regular characters
assert.Nil(t, ValidFileName("[(something)].csl"))
// contains invisible character
err := ValidFileName("D中文.cslouglas")
assert.NotNil(t, err)
assert.Equal(t, "non-graphical unicode: e2808b U+8203 in '44e4b8ade696872e63736c6f75676c6173e2808b'", err.Error())
assert.NotNil(t, ValidFileName("hi..txt"))
assert.NotNil(t, ValidFileName(path.Join(string(os.PathSeparator), "abs", string(os.PathSeparator), "hi.txt")))
}