Merge branch 'master' into master

This commit is contained in:
Mawoka 2022-04-25 16:22:51 +02:00 committed by GitHub
commit 89387a3168
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 663 additions and 362 deletions

18
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
unit-tests:
name: Go unit tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '1.18'
- run: go version
- run: go test -v ./...

View File

@ -93,4 +93,8 @@ scoop:
homepage: "https://schollz.com/software/croc/"
description: "croc is a tool that allows any two computers to simply and securely transfer files and folders."
license: MIT
announce:
twitter:
# Wether its enabled or not.
# Defaults to false.
enabled: true

View File

@ -1,10 +1,10 @@
FROM golang:1.16-alpine as builder
FROM golang:1.17-alpine as builder
RUN apk add --no-cache git
WORKDIR /go/croc
COPY . .
RUN go build -v -ldflags="-s -w"
FROM alpine:latest
FROM alpine:latest
EXPOSE 9009
EXPOSE 9010
EXPOSE 9011

View File

@ -4,18 +4,12 @@
src="https://user-images.githubusercontent.com/6550035/46709024-9b23ad00-cbf6-11e8-9fb2-ca8b20b7dbec.jpg"
width="408px" border="0" alt="croc">
<br>
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v9.2.0-brightgreen.svg?style=flat-square" alt="Version"></a>
<a href="https://github.com/schollz/croc/releases/latest"><img src="https://img.shields.io/badge/version-v9.5.3-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:</p>
<p align="center">
<a href="https://www.digitalocean.com/">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
</p>
<p align="center">This project is supported by <a href="https://github.com/sponsors/schollz">Github sponsors</a>.</p>
`croc` is a tool that allows any two computers to simply and securely transfer files and folders. AFAIK, *croc* is the only CLI file-transfer tool that does **all** of the following:
@ -28,7 +22,7 @@ Status"></a>
- **ipv6-first** with ipv4 fallback
- can **use proxy**, like tor
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6).
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6) or read a [recent interview I did](https://console.substack.com/p/console-91).
![Example](src/install/customization.gif)
@ -101,7 +95,7 @@ On FreeBSD you can install with `pkg`:
pkg install croc
```
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.15+):
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.17+):
```
go install github.com/schollz/croc/v9@latest
@ -191,8 +185,15 @@ You can choose from several different elliptic curves to use for encryption by u
croc --curve p521 <codephrase>
```
Available curves are P-256, P-348, P-521 and SIEC. SIEC is the default curve used, it is a lesser known curve that belongs to a class of "super-isolated" curves which has security that does not reduce to the security of curves around it. (Scholl, Travis. Experimental Mathematics 28.4 (2019): 385-397)
Available curves are P-256, P-348, P-521 and SIEC. P-256 is the default curve.
### Change hash algorithm
You can choose from several different hash algorithms. The default is the `xxhash` algorithm which is fast and thorough. If you want to optimize for speed you can use the `imohash` algorithm which is even faster, but since it samples files (versus reading the whole file) it can mistakenly determine that a file is the same on the two computers transferring - though this is only a problem if you are syncing files versus sending a new file to a computer.
```
croc send --hash imohash SOMEFILE
```
### Self-host relay

38
go.mod
View File

@ -1,25 +1,41 @@
module github.com/schollz/croc/v9
go 1.13
go 1.17
require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/buger/jsonparser v1.1.1 // Added by Mawoka to Parse JSON coming from GitHub-Api
github.com/cespare/xxhash v1.1.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/denisbrodbeck/machineid v1.0.1
github.com/kalafut/imohash v1.0.2
github.com/kr/pretty v0.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/schollz/cli/v2 v2.2.1
github.com/schollz/logger v1.2.0
github.com/schollz/mnemonicode v1.0.1
github.com/schollz/pake/v3 v3.0.2
github.com/schollz/peerdiscovery v1.6.6
github.com/schollz/progressbar/v3 v3.8.2
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/schollz/pake/v3 v3.0.4
github.com/schollz/peerdiscovery v1.6.11
github.com/schollz/progressbar/v3 v3.8.6
github.com/stretchr/testify v1.6.1
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
golang.org/x/net v0.0.0-20220325170049-de3da57026de
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
)
require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/tscholl2/siec v0.0.0-20210707234609-9bdfc483d499 // indirect
github.com/twmb/murmur3 v1.1.6 // indirect
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

57
go.sum
View File

@ -7,8 +7,8 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/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=
@ -22,7 +22,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
@ -40,12 +40,12 @@ github.com/schollz/logger v1.2.0 h1:5WXfINRs3lEUTCZ7YXhj0uN+qukjizvITLm3Ca2m0Ho=
github.com/schollz/logger v1.2.0/go.mod h1:P6F4/dGMGcx8wh+kG1zrNEd4vnNpEBY/mwEMd/vn6AM=
github.com/schollz/mnemonicode v1.0.1 h1:LiH5hwADZwjwnfXsaD4xgnMyTAtaKHN+e5AyjRU6WSU=
github.com/schollz/mnemonicode v1.0.1/go.mod h1:cl4UAOhUV0mkdjMj/QYaUZbZZdF8BnOqoz8rHMzwboY=
github.com/schollz/pake/v3 v3.0.2 h1:/S5yyXjlir24/+UYOjuGp6Dk9B351J2Wbi/YdNKSL/8=
github.com/schollz/pake/v3 v3.0.2/go.mod h1:U8udmizi/9pb5OqW+ieb4ebU5wGCqnLhXqYXSJRpeD4=
github.com/schollz/peerdiscovery v1.6.6 h1:caRe3cxuV/IK3v5oxrG07EFPLLRA8FLvPeR52WpX/N8=
github.com/schollz/peerdiscovery v1.6.6/go.mod h1:RLoWlEF1cJbSZhZUdBLCfwbW/4eWgFtZ6WYHcgKT9/8=
github.com/schollz/progressbar/v3 v3.8.2 h1:2kZJwZCpb+E/V79kGO7daeq+hUwUJW0A5QD1Wv455dA=
github.com/schollz/progressbar/v3 v3.8.2/go.mod h1:9KHLdyuXczIsyStQwzvW8xiELskmX7fQMaZdN23nAv8=
github.com/schollz/pake/v3 v3.0.4 h1:rDNk9MwEVJ/C1+eDCr0DirJfpG+/sAZJ4YsYIyOTkSc=
github.com/schollz/pake/v3 v3.0.4/go.mod h1:Q28rSQCHYzEtMQoi80R1MdpPRgNN/Si2Xdilx4NvsoQ=
github.com/schollz/peerdiscovery v1.6.11 h1:3SG5vV1plIxylDg81fKgnqyrvlem5MrNcgcb0TLBjlE=
github.com/schollz/peerdiscovery v1.6.11/go.mod h1:duO2S6wH3IuPJXwniPXp/9f69S2gFUSA9ePbAcKatJg=
github.com/schollz/progressbar/v3 v3.8.6 h1:QruMUdzZ1TbEP++S1m73OqRJk20ON11m6Wqv4EoGg8c=
github.com/schollz/progressbar/v3 v3.8.6/go.mod h1:W5IEwbJecncFGBvuEh4A7HT1nZZ6WNIL2i3qbnI0WKY=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
@ -54,30 +54,35 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094 h1:tZWtuLE+LbUwT4OP1oWBSB9zXA8qmQ5qEm4kV9R72oo=
github.com/tscholl2/siec v0.0.0-20191122224205-8da93652b094/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
github.com/twmb/murmur3 v1.1.5 h1:i9OLS9fkuLzBXjt6dptlAEyk58fJsSTXbRg3SgVyqgk=
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/twmb/murmur3 v1.1.5/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
github.com/twmb/murmur3 v1.1.6 h1:mqrRot1BRxm+Yct+vavLMou2/iJt0tNVTTC0QoIjaZg=
github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f h1:rlezHXNlxYWvBCzNses9Dlc7nGFaNMJeqLolcmQSSZY=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b h1:9zKuko04nR4gjZ4+DNjHqRlAJqbJETHwiNKDqTfOjfE=
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
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.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs=
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=

View File

@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
@ -34,7 +33,7 @@ func Run() (err error) {
app := cli.NewApp()
app.Name = "croc"
if Version == "" {
Version = "v9.2.0-d442755"
Version = "v9.5.3-54f52e5"
}
app.Version = Version
app.Compiled = time.Now()
@ -42,6 +41,14 @@ func Run() (err error) {
app.UsageText = `Send a file:
croc send file.txt
Send multiple files:
croc send file1.txt file2.txt file3.txt
or
croc send *.jpg
Send everything in a folder:
croc send example-folder-name
Send a file with a custom code:
croc send --code secret-code file.txt
@ -50,9 +57,9 @@ func Run() (err error) {
app.Commands = []*cli.Command{
{
Name: "send",
Usage: "send a file (see options with croc send -h)",
Description: "send a file over the relay",
ArgsUsage: "[filename]",
Usage: "send file(s), or folder (see options with croc send -h)",
Description: "send file(s), or folder, over the relay",
ArgsUsage: "[filename(s) or folder]",
Flags: []cli.Flag{
&cli.StringFlag{Name: "code", Aliases: []string{"c"}, Usage: "codephrase used to connect to relay"},
&cli.StringFlag{Name: "hash", Value: "xxhash", Usage: "hash algorithm (xxhash, imohash, md5)"},
@ -62,24 +69,22 @@ func Run() (err error) {
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the local relay (optional)"},
},
HelpName: "croc send",
Action: func(c *cli.Context) error {
return send(c)
},
Action: send,
},
{
Name: "relay",
Usage: "start your own relay (optional)",
Description: "start relay",
HelpName: "croc relay",
Action: func(c *cli.Context) error {
return relay(c)
},
Action: relay,
Flags: []cli.Flag{
&cli.StringFlag{Name: "host", Usage: "host of the relay"},
&cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
},
},
}
app.Flags = []cli.Flag{
&cli.BoolFlag{Name: "internal-dns", Usage: "use a built-in DNS stub resolver rather than the host operating system"},
&cli.BoolFlag{Name: "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"},
@ -89,7 +94,7 @@ func Run() (err error) {
&cli.BoolFlag{Name: "local", Usage: "force to use only local connections"},
&cli.BoolFlag{Name: "ignore-stdin", Usage: "ignore piped stdin"},
&cli.BoolFlag{Name: "overwrite", Usage: "do not prompt to overwrite"},
&cli.StringFlag{Name: "curve", Value: "siec", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
&cli.StringFlag{Name: "curve", Value: "p256", Usage: "choose an encryption curve (" + strings.Join(pake.AvailableCurves(), ", ") + ")"},
&cli.StringFlag{Name: "ip", Value: "", Usage: "set sender ip if known e.g. 10.0.0.1:9009, [::1]:9009"},
&cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay", EnvVars: []string{"CROC_RELAY"}},
&cli.StringFlag{Name: "relay6", Value: models.DEFAULT_RELAY6, Usage: "ipv6 address of the relay", EnvVars: []string{"CROC_RELAY6"}},
@ -97,6 +102,7 @@ func Run() (err error) {
&cli.StringFlag{Name: "pass", Value: models.DEFAULT_PASSPHRASE, Usage: "password for the relay", EnvVars: []string{"CROC_PASS"}},
&cli.StringFlag{Name: "socks5", Value: "", Usage: "add a socks5 proxy", EnvVars: []string{"SOCKS5_PROXY"}},
&cli.StringFlag{Name: "no-update-notice", Usage: "Removes up-to-date check on error", EnvVars: []string{"NO_UPDATE_CHECK"}},
&cli.StringFlag{Name: "throttleUpload", Value: "", Usage: "Throttle the upload speed e.g. 500k"},
}
app.EnableBashCompletion = true
app.HideHelp = false
@ -130,28 +136,6 @@ func Run() (err error) {
return app.Run(os.Args)
}
func getConfigDir() (homedir string, err error) {
homedir, err = os.UserHomeDir()
if err != nil {
log.Error(err)
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 = path.Join(homedir, ".config", "croc")
}
if _, err = os.Stat(homedir); os.IsNotExist(err) {
log.Debugf("creating home directory %s", homedir)
err = os.MkdirAll(homedir, 0700)
}
return
}
func setDebugLevel(c *cli.Context) {
if c.Bool("debug") {
log.SetLevel("debug")
@ -162,7 +146,7 @@ func setDebugLevel(c *cli.Context) {
}
func getConfigFile() string {
configFile, err := getConfigDir()
configFile, err := utils.GetConfigDir()
if err != nil {
log.Error(err)
return ""
@ -172,7 +156,7 @@ func getConfigFile() string {
func determinePass(c *cli.Context) (pass string) {
pass = c.String("pass")
b, err := ioutil.ReadFile(pass)
b, err := os.ReadFile(pass)
if err == nil {
pass = strings.TrimSpace(string(b))
}
@ -203,13 +187,14 @@ func send(c *cli.Context) (err error) {
Curve: c.String("curve"),
HashAlgorithm: c.String("hash"),
NoUpdateCheck: c.Bool("no-update"),
ThrottleUpload: c.String("throttleUpload"),
}
if crocOptions.RelayAddress != models.DEFAULT_RELAY {
crocOptions.RelayAddress6 = ""
} else if crocOptions.RelayAddress6 != models.DEFAULT_RELAY6 {
crocOptions.RelayAddress = ""
}
b, errOpen := ioutil.ReadFile(getConfigFile())
b, errOpen := os.ReadFile(getConfigFile())
if errOpen == nil && !c.Bool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
@ -218,28 +203,28 @@ func send(c *cli.Context) (err error) {
return
}
// update anything that isn't explicitly set
if !c.IsSet("relay") {
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
crocOptions.RelayAddress = rememberedOptions.RelayAddress
}
if !c.IsSet("no-local") {
crocOptions.DisableLocal = rememberedOptions.DisableLocal
}
if !c.IsSet("ports") {
if !c.IsSet("ports") && len(rememberedOptions.RelayPorts) > 0 {
crocOptions.RelayPorts = rememberedOptions.RelayPorts
}
if !c.IsSet("code") {
crocOptions.SharedSecret = rememberedOptions.SharedSecret
}
if !c.IsSet("pass") {
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
crocOptions.RelayPassword = rememberedOptions.RelayPassword
}
if !c.IsSet("relay6") {
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
}
if !c.IsSet("overwrite") {
crocOptions.Overwrite = rememberedOptions.Overwrite
}
if !c.IsSet("curve") {
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
crocOptions.Curve = rememberedOptions.Curve
}
if !c.IsSet("local") {
@ -279,7 +264,7 @@ func send(c *cli.Context) (err error) {
fnames = c.Args().Slice()
}
if len(fnames) == 0 {
return errors.New("must specify file: croc send [filename]")
return errors.New("must specify file: croc send [filename(s) or folder]")
}
if len(crocOptions.SharedSecret) == 0 {
@ -287,7 +272,7 @@ func send(c *cli.Context) (err error) {
crocOptions.SharedSecret = utils.GetRandomName()
}
paths, haveFolder, err := getPaths(fnames)
minimalFileInfos, err := croc.GetFilesInfo(fnames)
if err != nil {
return
}
@ -300,16 +285,13 @@ func send(c *cli.Context) (err error) {
// save the config
saveConfig(c, crocOptions)
err = cr.Send(croc.TransferOptions{
PathToFiles: paths,
KeepPathInRemote: haveFolder,
}, Version)
err = cr.Send(minimalFileInfos, Version)
return
}
func getStdin() (fnames []string, err error) {
f, err := ioutil.TempFile(".", "croc-stdin-")
f, err := os.CreateTemp(".", "croc-stdin-")
if err != nil {
return
}
@ -326,7 +308,7 @@ func getStdin() (fnames []string, err error) {
}
func makeTempFileWithString(s string) (fnames []string, err error) {
f, err := ioutil.TempFile(".", "croc-stdin-")
f, err := os.CreateTemp(".", "croc-stdin-")
if err != nil {
return
}
@ -344,37 +326,6 @@ func makeTempFileWithString(s string) (fnames []string, err error) {
return
}
func getPaths(fnames []string) (paths []string, haveFolder bool, err error) {
haveFolder = false
paths = []string{}
for _, fname := range fnames {
stat, errStat := os.Lstat(fname)
if errStat != nil {
err = errStat
return
}
if stat.IsDir() {
haveFolder = true
err = filepath.Walk(fname,
func(pathName string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
paths = append(paths, filepath.ToSlash(pathName))
}
return nil
})
if err != nil {
return
}
} else {
paths = append(paths, filepath.ToSlash(fname))
}
}
return
}
func saveConfig(c *cli.Context, crocOptions croc.Options) {
if c.Bool("remember") {
configFile := getConfigFile()
@ -389,7 +340,7 @@ func saveConfig(c *cli.Context, crocOptions croc.Options) {
log.Error(err)
return
}
err = ioutil.WriteFile(configFile, bConfig, 0644)
err = os.WriteFile(configFile, bConfig, 0644)
if err != nil {
log.Error(err)
return
@ -434,13 +385,13 @@ func receive(c *cli.Context) (err error) {
// load options here
setDebugLevel(c)
configFile, err := getConfigDir()
configFile, err := utils.GetConfigDir()
if err != nil {
log.Error(err)
return
}
configFile = path.Join(configFile, "receive.json")
b, errOpen := ioutil.ReadFile(configFile)
b, errOpen := os.ReadFile(configFile)
if errOpen == nil && !c.Bool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
@ -448,8 +399,8 @@ func receive(c *cli.Context) (err error) {
log.Error(err)
return
}
// update anything that isn't expliciGlobalIsSettly set
if !c.IsSet("relay") {
// update anything that isn't explicitly Globally set
if !c.IsSet("relay") && rememberedOptions.RelayAddress != "" {
crocOptions.RelayAddress = rememberedOptions.RelayAddress
}
if !c.IsSet("yes") {
@ -458,16 +409,16 @@ func receive(c *cli.Context) (err error) {
if crocOptions.SharedSecret == "" {
crocOptions.SharedSecret = rememberedOptions.SharedSecret
}
if !c.IsSet("pass") {
if !c.IsSet("pass") && rememberedOptions.RelayPassword != "" {
crocOptions.RelayPassword = rememberedOptions.RelayPassword
}
if !c.IsSet("relay6") {
if !c.IsSet("relay6") && rememberedOptions.RelayAddress6 != "" {
crocOptions.RelayAddress6 = rememberedOptions.RelayAddress6
}
if !c.IsSet("overwrite") {
crocOptions.Overwrite = rememberedOptions.Overwrite
}
if !c.IsSet("curve") {
if !c.IsSet("curve") && rememberedOptions.Curve != "" {
crocOptions.Curve = rememberedOptions.Curve
}
if !c.IsSet("local") {
@ -501,7 +452,7 @@ func receive(c *cli.Context) (err error) {
log.Error(err)
return
}
err = ioutil.WriteFile(configFile, bConfig, 0644)
err = os.WriteFile(configFile, bConfig, 0644)
if err != nil {
log.Error(err)
return
@ -519,6 +470,7 @@ func relay(c *cli.Context) (err error) {
if c.Bool("debug") {
debugString = "debug"
}
host := c.String("host")
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
for i, port := range ports {
@ -526,11 +478,11 @@ func relay(c *cli.Context) (err error) {
continue
}
go func(portStr string) {
err = tcp.Run(debugString, portStr, determinePass(c))
err = tcp.Run(debugString, host, portStr, determinePass(c))
if err != nil {
panic(err)
}
}(port)
}
return tcp.Run(debugString, ports[0], determinePass(c), tcpPorts)
return tcp.Run(debugString, host, ports[0], determinePass(c), tcpPorts)
}

View File

@ -40,19 +40,24 @@ func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err err
socks5ProxyURL, urlParseError := url.Parse(Socks5Proxy)
if urlParseError != nil {
err = fmt.Errorf("Unable to parse socks proxy url: %s", urlParseError)
log.Debug(err)
return
}
dialer, err = proxy.FromURL(socks5ProxyURL, proxy.Direct)
if err != nil {
err = fmt.Errorf("proxy failed: %w", err)
log.Debug(err)
return
}
log.Debug("dialing with dialer.Dial")
connection, err = dialer.Dial("tcp", address)
} else {
log.Debugf("dialing to %s with timelimit %s", address, tlimit)
connection, err = net.DialTimeout("tcp", address, tlimit)
}
if err != nil {
err = fmt.Errorf("comm.NewConnection failed: %w", err)
log.Debug(err)
return
}
c = New(connection)

View File

@ -7,7 +7,6 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"math"
"net"
"net/http" // Added by Mawoka to do the api-request
@ -21,6 +20,8 @@ import (
"time"
"github.com/buger/jsonparser" // Added by Mawoka for github-api-parsing
"golang.org/x/time/rate"
"github.com/denisbrodbeck/machineid"
log "github.com/schollz/logger"
"github.com/schollz/pake/v3"
@ -36,6 +37,11 @@ import (
"github.com/schollz/croc/v9/src/utils"
)
var (
ipRequest = []byte("ips?")
handshakeRequest = []byte("handshake")
)
func init() {
log.SetLevel("debug")
}
@ -72,6 +78,7 @@ type Options struct {
Curve string
HashAlgorithm string
NoUpdateCheck bool
ThrottleUpload string
}
// Client holds the state of the croc transfer
@ -83,7 +90,7 @@ type Client struct {
// steps involved in forming relationship
Step1ChannelSecured bool
Step2FileInfoTransfered bool
Step2FileInfoTransferred bool
Step3RecipientRequestFile bool
Step4FileTransfer bool
Step5CloseChannels bool
@ -101,9 +108,10 @@ type Client struct {
CurrentFileIsClosed bool
LastFolder string
TotalSent int64
TotalChunksTransfered int
chunkMap map[uint64]struct{}
TotalSent int64
TotalChunksTransferred int
chunkMap map[uint64]struct{}
limiter *rate.Limiter
// tcp connections
conn []*comm.Comm
@ -112,12 +120,12 @@ type Client struct {
longestFilename int
firstSend bool
mutex *sync.Mutex
fread *os.File
numfinished int
quit chan bool
finishedNum int
numberOfTransferedFiles int
mutex *sync.Mutex
fread *os.File
numfinished int
quit chan bool
finishedNum int
numberOfTransferredFiles int
}
// Chunk contains information about the
@ -129,15 +137,16 @@ type Chunk struct {
// FileInfo registers the information about the file
type FileInfo struct {
Name string `json:"n,omitempty"`
FolderRemote string `json:"fr,omitempty"`
FolderSource string `json:"fs,omitempty"`
Hash []byte `json:"h,omitempty"`
Size int64 `json:"s,omitempty"`
ModTime time.Time `json:"m,omitempty"`
IsCompressed bool `json:"c,omitempty"`
IsEncrypted bool `json:"e,omitempty"`
Symlink string `json:"sy,omitempty"`
Name string `json:"n,omitempty"`
FolderRemote string `json:"fr,omitempty"`
FolderSource string `json:"fs,omitempty"`
Hash []byte `json:"h,omitempty"`
Size int64 `json:"s,omitempty"`
ModTime time.Time `json:"m,omitempty"`
IsCompressed bool `json:"c,omitempty"`
IsEncrypted bool `json:"e,omitempty"`
Symlink string `json:"sy,omitempty"`
Mode os.FileMode `json:"md,omitempty"`
}
// RemoteFileRequest requests specific bytes
@ -174,6 +183,37 @@ func New(ops Options) (c *Client, err error) {
c.conn = make([]*comm.Comm, 16)
// initialize throttler
if len(c.Options.ThrottleUpload) > 1 && c.Options.IsSender {
upload := c.Options.ThrottleUpload[:len(c.Options.ThrottleUpload)-1]
uploadLimit, err := strconv.ParseInt(upload, 10, 64)
if err != nil {
panic("Could not parse given Upload Limit")
}
minBurstSize := models.TCP_BUFFER_SIZE
var rt rate.Limit
switch unit := string(c.Options.ThrottleUpload[len(c.Options.ThrottleUpload)-1:]); unit {
case "g", "G":
uploadLimit = uploadLimit * 1024 * 1024 * 1024
case "m", "M":
uploadLimit = uploadLimit * 1024 * 1024
case "k", "K":
uploadLimit = uploadLimit * 1024
default:
uploadLimit, err = strconv.ParseInt(c.Options.ThrottleUpload, 10, 64)
if err != nil {
panic("Could not parse given Upload Limit")
}
}
// Somehow 4* is neccessary
rt = rate.Every(time.Second / (4 * time.Duration(uploadLimit)))
if int(uploadLimit) > minBurstSize {
minBurstSize = int(uploadLimit)
}
c.limiter = rate.NewLimiter(rt, minBurstSize)
log.Debugf("Throttling Upload to %#v", c.limiter.Limit())
}
// initialize pake for recipient
if !c.Options.IsSender {
c.Pake, err = pake.InitCurve([]byte(c.Options.SharedSecret[5:]), 0, c.Options.Curve)
@ -192,72 +232,113 @@ type TransferOptions struct {
KeepPathInRemote bool
}
func (c *Client) sendCollectFiles(options TransferOptions) (err error) {
c.FilesToTransfer = make([]FileInfo, len(options.PathToFiles))
totalFilesSize := int64(0)
for i, pathToFile := range options.PathToFiles {
var fstats os.FileInfo
var fullPath string
fullPath, err = filepath.Abs(pathToFile)
if err != nil {
return
}
fullPath = filepath.Clean(fullPath)
var folderName string
folderName, _ = filepath.Split(fullPath)
// This function retrives the important file informations
// for every file that will be transfered
func GetFilesInfo(fnames []string) (filesInfo []FileInfo, err error) {
// fnames: the relativ/absolute paths of files/folders that will be transfered
fstats, err = os.Lstat(fullPath)
if err != nil {
var paths []string
for _, fname := range fnames {
// Support wildcard
if strings.Contains(fname, "*") {
matches, errGlob := filepath.Glob(fname)
if errGlob != nil {
err = errGlob
return
}
paths = append(paths, matches...)
continue
} else {
paths = append(paths, fname)
}
}
for _, path := range paths {
stat, errStat := os.Lstat(path)
if errStat != nil {
err = errStat
return
}
if len(fstats.Name()) > c.longestFilename {
c.longestFilename = len(fstats.Name())
absPath, errAbs := filepath.Abs(path)
if errAbs != nil {
err = errAbs
return
}
c.FilesToTransfer[i] = FileInfo{
Name: fstats.Name(),
FolderRemote: ".",
FolderSource: folderName,
Size: fstats.Size(),
ModTime: fstats.ModTime(),
if stat.IsDir() {
err = filepath.Walk(absPath,
func(pathName string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
remoteFolder := strings.TrimPrefix(filepath.Dir(pathName),
filepath.Dir(absPath)+string(os.PathSeparator))
filesInfo = append(filesInfo, FileInfo{
Name: info.Name(),
FolderRemote: strings.Replace(remoteFolder, string(os.PathSeparator), "/", -1) + "/",
FolderSource: filepath.Dir(pathName),
Size: info.Size(),
ModTime: info.ModTime(),
Mode: info.Mode(),
})
}
return nil
})
if err != nil {
return
}
} else {
filesInfo = append(filesInfo, FileInfo{
Name: stat.Name(),
FolderRemote: "./",
FolderSource: filepath.Dir(absPath),
Size: stat.Size(),
ModTime: stat.ModTime(),
Mode: stat.Mode(),
})
}
if fstats.Mode()&os.ModeSymlink != 0 {
log.Debugf("%s is symlink", fstats.Name())
c.FilesToTransfer[i].Symlink, err = os.Readlink(pathToFile)
}
return
}
func (c *Client) sendCollectFiles(filesInfo []FileInfo) (err error) {
c.FilesToTransfer = filesInfo
totalFilesSize := int64(0)
for i, fileInfo := range c.FilesToTransfer {
var fullPath string
fullPath = fileInfo.FolderSource + string(os.PathSeparator) + fileInfo.Name
fullPath = filepath.Clean(fullPath)
if len(fileInfo.Name) > c.longestFilename {
c.longestFilename = len(fileInfo.Name)
}
if fileInfo.Mode&os.ModeSymlink != 0 {
log.Debugf("%s is symlink", fileInfo.Name)
c.FilesToTransfer[i].Symlink, err = os.Readlink(fullPath)
if err != nil {
log.Debugf("error getting symlink: %s", err.Error())
}
log.Debugf("%+v", c.FilesToTransfer[i])
}
if c.Options.HashAlgorithm == "" {
c.Options.HashAlgorithm = "xxhash"
}
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 += fstats.Size()
totalFilesSize += fileInfo.Size
if err != nil {
return
}
if options.KeepPathInRemote {
var curFolder string
curFolder, err = os.Getwd()
if err != nil {
return
}
curFolder, err = filepath.Abs(curFolder)
if err != nil {
return
}
if !strings.HasPrefix(folderName, curFolder) {
err = fmt.Errorf("remote directory must be relative to current")
return
}
c.FilesToTransfer[i].FolderRemote = strings.TrimPrefix(folderName, curFolder)
c.FilesToTransfer[i].FolderRemote = filepath.ToSlash(c.FilesToTransfer[i].FolderRemote)
c.FilesToTransfer[i].FolderRemote = strings.TrimPrefix(c.FilesToTransfer[i].FolderRemote, "/")
if c.FilesToTransfer[i].FolderRemote == "" {
c.FilesToTransfer[i].FolderRemote = "."
}
}
log.Debugf("file %d info: %+v", i, c.FilesToTransfer[i])
fmt.Fprintf(os.Stderr, "\r ")
fmt.Fprintf(os.Stderr, "\rSending %d files (%s)", i, utils.ByteCountDecimal(totalFilesSize))
@ -295,7 +376,7 @@ func (c *Client) setupLocalRelay() {
if c.Options.Debug {
debugString = "debug"
}
err := tcp.Run(debugString, portStr, c.Options.RelayPassword, strings.Join(c.Options.RelayPorts[1:], ","))
err := tcp.Run(debugString, "localhost", portStr, c.Options.RelayPassword, strings.Join(c.Options.RelayPorts[1:], ","))
if err != nil {
panic(err)
}
@ -304,12 +385,19 @@ func (c *Client) setupLocalRelay() {
}
func (c *Client) broadcastOnLocalNetwork(useipv6 bool) {
var timeLimit time.Duration
//if we don't use an external relay, the broadcast messages need to be sent continuously
if c.Options.OnlyLocal {
timeLimit = -1 * time.Second
} else {
timeLimit = 30 * time.Second
}
// look for peers first
settings := peerdiscovery.Settings{
Limit: -1,
Payload: []byte("croc" + c.Options.RelayPorts[0]),
Delay: 20 * time.Millisecond,
TimeLimit: 30 * time.Second,
TimeLimit: timeLimit,
}
if useipv6 {
settings.IPVersion = peerdiscovery.IPv6
@ -323,7 +411,7 @@ func (c *Client) broadcastOnLocalNetwork(useipv6 bool) {
}
}
func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<- error) {
func (c *Client) transferOverLocalRelay(errchan chan<- error) {
time.Sleep(500 * time.Millisecond)
log.Debug("establishing connection")
var banner string
@ -338,7 +426,7 @@ func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<-
log.Debugf("local connection established: %+v", conn)
for {
data, _ := conn.Receive()
if bytes.Equal(data, []byte("handshake")) {
if bytes.Equal(data, handshakeRequest) {
break
} else if bytes.Equal(data, []byte{1}) {
log.Debug("got ping")
@ -355,12 +443,12 @@ func (c *Client) transferOverLocalRelay(options TransferOptions, errchan chan<-
c.Options.RelayPorts = []string{c.Options.RelayPorts[0]}
}
c.ExternalIP = ipaddr
errchan <- c.transfer(options)
errchan <- c.transfer()
}
// Send will send the specified file
func (c *Client) Send(options TransferOptions, Version string) (err error) {
err = c.sendCollectFiles(options)
func (c *Client) Send(filesInfo []FileInfo, Version string) (err error) {
err = c.sendCollectFiles(filesInfo)
if err != nil {
return
}
@ -390,7 +478,7 @@ func (c *Client) Send(options TransferOptions, Version string) (err error) {
go c.broadcastOnLocalNetwork(false)
// broadcast on ipv6
go c.broadcastOnLocalNetwork(true)
go c.transferOverLocalRelay(options, errchan)
go c.transferOverLocalRelay(errchan)
}
if !c.Options.OnlyLocal {
@ -407,7 +495,7 @@ func (c *Client) Send(options TransferOptions, Version string) (err error) {
// Default port to :9009
if port == "" {
host = address
port = "9009"
port = models.DEFAULT_PORT
}
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
@ -453,7 +541,7 @@ func (c *Client) Send(options TransferOptions, Version string) (err error) {
if errConn != nil {
log.Debugf("[%+v] had error: %s", conn, errConn.Error())
}
if bytes.Equal(data, []byte("ips?")) {
if bytes.Equal(data, ipRequest) {
// recipient wants to try to connect to local ips
var ips []string
// only get local ips if the local is enabled
@ -470,7 +558,7 @@ func (c *Client) Send(options TransferOptions, Version string) (err error) {
if err := conn.Send(bips); err != nil {
log.Errorf("error sending: %v", err)
}
} else if bytes.Equal(data, []byte("handshake")) {
} else if bytes.Equal(data, handshakeRequest) {
break
} else if bytes.Equal(data, []byte{1}) {
log.Debug("got ping")
@ -491,7 +579,7 @@ func (c *Client) Send(options TransferOptions, Version string) (err error) {
}
c.ExternalIP = ipaddr
log.Debug("exchanged header message")
errchan <- c.transfer(options)
errchan <- c.transfer()
}()
}
@ -588,18 +676,21 @@ func (c *Client) Receive() (err error) {
continue
}
log.Debug("switching to local")
portToUse := string(bytes.TrimPrefix(discoveries[0].Payload, []byte("croc")))
portToUse := string(bytes.TrimPrefix(discoveries[i].Payload, []byte("croc")))
if portToUse == "" {
portToUse = "9009"
portToUse = models.DEFAULT_PORT
}
address := net.JoinHostPort(discoveries[0].Address, portToUse)
if tcp.PingServer(address) == nil {
log.Debugf("succesfully pinged '%s'", address)
address := net.JoinHostPort(discoveries[i].Address, portToUse)
errPing := tcp.PingServer(address)
if errPing == nil {
log.Debugf("successfully pinged '%s'", address)
c.Options.RelayAddress = address
c.ExternalIPConnected = c.Options.RelayAddress
c.Options.RelayAddress6 = ""
usingLocal = true
break
} else {
log.Debugf("could not ping: %+v", errPing)
}
}
}
@ -618,7 +709,7 @@ func (c *Client) Receive() (err error) {
// Default port to :9009
if port == "" {
host = address
port = "9009"
port = models.DEFAULT_PORT
}
log.Debugf("got host '%v' and port '%v'", host, port)
address = net.JoinHostPort(host, port)
@ -643,7 +734,7 @@ func (c *Client) Receive() (err error) {
// and try to connect to them
log.Debug("sending ips?")
var data []byte
if err := c.conn[0].Send([]byte("ips?")); err != nil {
if err := c.conn[0].Send(ipRequest); err != nil {
log.Errorf("ips send error: %v", err)
}
data, err = c.conn[0].Receive()
@ -676,8 +767,9 @@ func (c *Client) Receive() (err error) {
}
serverTry := fmt.Sprintf("%s:%s", ip, port)
conn, banner2, externalIP, errConn := tcp.ConnectToTCPServer(serverTry, c.Options.RelayPassword, c.Options.SharedSecret[:3], 250*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)
continue
}
@ -695,7 +787,7 @@ func (c *Client) Receive() (err error) {
}
}
if err := c.conn[0].Send([]byte("handshake")); err != nil {
if err := c.conn[0].Send(handshakeRequest); err != nil {
log.Errorf("handshake send error: %v", err)
}
c.Options.RelayPorts = strings.Split(banner, ",")
@ -705,16 +797,16 @@ func (c *Client) Receive() (err error) {
}
log.Debug("exchanged header message")
fmt.Fprintf(os.Stderr, "\rsecuring channel...")
err = c.transfer(TransferOptions{})
err = c.transfer()
if err == nil {
if c.numberOfTransferedFiles == 0 {
if c.numberOfTransferredFiles == 0 {
fmt.Fprintf(os.Stderr, "\rNo files transferred.")
}
}
return
}
func (c *Client) transfer(options TransferOptions) (err error) {
func (c *Client) transfer() (err error) {
// connect to the server
// quit with c.quit <- true
@ -724,7 +816,7 @@ func (c *Client) transfer(options TransferOptions) (err error) {
log.Debug("ready")
if !c.Options.IsSender && !c.Step1ChannelSecured {
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "pake",
Type: message.TypePAKE,
Bytes: c.Pake.Bytes(),
Bytes2: []byte(c.Options.Curve),
})
@ -820,7 +912,7 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
if len(fi.Name) > c.longestFilename {
c.longestFilename = len(fi.Name)
}
if strings.HasPrefix(fi.Name, "croc-stdin-") {
if strings.HasPrefix(fi.Name, "croc-stdin-") && c.Options.SendingText {
c.FilesToTransfer[i].Name, err = utils.RandomFileName()
if err != nil {
return
@ -843,7 +935,7 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
choice := strings.ToLower(utils.GetInput(""))
if choice != "" && choice != "y" && choice != "yes" {
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "error",
Type: message.TypeError,
Message: "refusing files",
})
if err != nil {
@ -857,7 +949,7 @@ func (c *Client) processMessageFileInfo(m message.Message) (done bool, err error
fmt.Fprintf(os.Stderr, "\nReceiving (<-%s)\n", c.ExternalIPConnected)
log.Debug(c.FilesToTransfer)
c.Step2FileInfoTransfered = true
c.Step2FileInfoTransferred = true
return
}
@ -889,7 +981,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
}
log.Debug("sender sending pake+salt")
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "pake",
Type: message.TypePAKE,
Bytes: c.Pake.Bytes(),
Bytes2: salt,
})
@ -949,7 +1041,7 @@ func (c *Client) processMessagePake(m message.Message) (err error) {
if !c.Options.IsSender {
log.Debug("sending external IP")
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "externalip",
Type: message.TypeExternalIP,
Message: c.ExternalIP,
Bytes: m.Bytes,
})
@ -961,7 +1053,7 @@ func (c *Client) processExternalIP(m message.Message) (done bool, err error) {
log.Debugf("received external IP: %+v", m)
if c.Options.IsSender {
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "externalip",
Type: message.TypeExternalIP,
Message: c.ExternalIP,
})
if err != nil {
@ -988,36 +1080,36 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
// only "pake" messages should be unencrypted
// if a non-"pake" message is received unencrypted something
// is weird
if m.Type != "pake" && c.Key == nil {
if m.Type != message.TypePAKE && c.Key == nil {
err = fmt.Errorf("unencrypted communication rejected")
done = true
return
}
switch m.Type {
case "finished":
case message.TypeFinished:
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "finished",
Type: message.TypeFinished,
})
done = true
c.SuccessfulTransfer = true
return
case "pake":
case message.TypePAKE:
err = c.processMessagePake(m)
if err != nil {
err = fmt.Errorf("pake not successful: %w", err)
log.Debug(err)
}
case "externalip":
case message.TypeExternalIP:
done, err = c.processExternalIP(m)
case "error":
case message.TypeError:
// c.spinner.Stop()
fmt.Print("\r")
err = fmt.Errorf("peer error: %s", m.Message)
return true, err
case "fileinfo":
case message.TypeFileInfo:
done, err = c.processMessageFileInfo(m)
case "recipientready":
case message.TypeRecipientReady:
var remoteFile RemoteFileRequest
err = json.Unmarshal(m.Bytes, &remoteFile)
if err != nil {
@ -1040,23 +1132,23 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
choice := strings.ToLower(utils.GetInput(""))
if choice != "" && choice != "y" && choice != "yes" {
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "error",
Type: message.TypeError,
Message: "refusing files",
})
done = true
return
}
}
case "close-sender":
case message.TypeCloseSender:
c.bar.Finish()
log.Debug("close-sender received...")
c.Step4FileTransfer = false
c.Step3RecipientRequestFile = false
log.Debug("sending close-recipient")
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "close-recipient",
Type: message.TypeCloseRecipient,
})
case "close-recipient":
case message.TypeCloseRecipient:
c.Step4FileTransfer = false
c.Step3RecipientRequestFile = false
}
@ -1073,7 +1165,7 @@ func (c *Client) processMessage(payload []byte) (done bool, err error) {
}
func (c *Client) updateIfSenderChannelSecured() (err error) {
if c.Options.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransfered {
if c.Options.IsSender && c.Step1ChannelSecured && !c.Step2FileInfoTransferred {
var b []byte
machID, _ := machineid.ID()
b, err = json.Marshal(SenderInfo{
@ -1089,14 +1181,14 @@ func (c *Client) updateIfSenderChannelSecured() (err error) {
return
}
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "fileinfo",
Type: message.TypeFileInfo,
Bytes: b,
})
if err != nil {
return
}
c.Step2FileInfoTransfered = true
c.Step2FileInfoTransferred = true
}
return
}
@ -1161,7 +1253,7 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
// TODO: do the last finishing stuff
log.Debug("finished")
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "finished",
Type: message.TypeFinished,
})
if err != nil {
panic(err)
@ -1193,7 +1285,7 @@ func (c *Client) recipientGetFileReady(finished bool) (err error) {
log.Debugf("sending recipient ready with %d chunks", len(c.CurrentFileChunks))
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "recipientready",
Type: message.TypeRecipientReady,
Bytes: bRequest,
})
if err != nil {
@ -1257,7 +1349,7 @@ func (c *Client) createEmptyFileAndFinish(fileInfo FileInfo, i int) (err error)
}
func (c *Client) updateIfRecipientHasFileInfo() (err error) {
if !(!c.Options.IsSender && c.Step2FileInfoTransfered && !c.Step3RecipientRequestFile) {
if !(!c.Options.IsSender && c.Step2FileInfoTransferred && !c.Step3RecipientRequestFile) {
return
}
// find the next file to transfer and send that number
@ -1284,7 +1376,7 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
if err != nil {
return
} else {
c.numberOfTransferedFiles++
c.numberOfTransferredFiles++
}
continue
}
@ -1292,7 +1384,7 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
if !bytes.Equal(fileHash, fileInfo.Hash) {
log.Debugf("hashed %s to %x using %s", fileInfo.Name, fileHash, c.Options.HashAlgorithm)
log.Debugf("hashes are not equal %x != %x", fileHash, fileInfo.Hash)
if errHash == nil && !c.Options.Overwrite && errRecipientFile == nil && !strings.HasPrefix(fileInfo.Name, "croc-stdin-") {
if errHash == nil && !c.Options.Overwrite && errRecipientFile == nil && !strings.HasPrefix(fileInfo.Name, "croc-stdin-") && !c.Options.SendingText {
missingChunks := utils.ChunkRangesToChunks(utils.MissingChunks(
path.Join(fileInfo.FolderRemote, fileInfo.Name),
@ -1322,9 +1414,9 @@ func (c *Client) updateIfRecipientHasFileInfo() (err error) {
if errHash != nil || !bytes.Equal(fileHash, fileInfo.Hash) {
finished = false
c.FilesToTransferCurrentNum = i
c.numberOfTransferedFiles++
c.numberOfTransferredFiles++
newFolder, _ := filepath.Split(fileInfo.FolderRemote)
if newFolder != c.LastFolder && len(c.FilesToTransfer) > 0 {
if newFolder != c.LastFolder && len(c.FilesToTransfer) > 0 && !c.Options.SendingText && newFolder != "./" {
fmt.Fprintf(os.Stderr, "\r%s\n", newFolder)
}
c.LastFolder = newFolder
@ -1475,14 +1567,15 @@ func (c *Client) receiveData(i int) {
c.mutex.Lock()
_, err = c.CurrentFile.WriteAt(data[8:], positionInt64)
c.mutex.Unlock()
if err != nil {
panic(err)
}
c.bar.Add(len(data[8:]))
c.TotalSent += int64(len(data[8:]))
c.TotalChunksTransfered++
if !c.CurrentFileIsClosed && (c.TotalChunksTransfered == len(c.CurrentFileChunks) || c.TotalSent == c.FilesToTransfer[c.FilesToTransferCurrentNum].Size) {
c.TotalChunksTransferred++
// log.Debug(len(c.CurrentFileChunks), c.TotalChunksTransferred, c.TotalSent, c.FilesToTransfer[c.FilesToTransferCurrentNum].Size)
if !c.CurrentFileIsClosed && (c.TotalChunksTransferred == len(c.CurrentFileChunks) || c.TotalSent == c.FilesToTransfer[c.FilesToTransferCurrentNum].Size) {
c.CurrentFileIsClosed = true
log.Debug("finished receiving!")
if err := c.CurrentFile.Close(); err != nil {
@ -1493,17 +1586,18 @@ func (c *Client) receiveData(i int) {
c.FilesToTransfer[c.FilesToTransferCurrentNum].FolderRemote,
c.FilesToTransfer[c.FilesToTransferCurrentNum].Name,
)
b, _ := ioutil.ReadFile(pathToFile)
b, _ := os.ReadFile(pathToFile)
fmt.Print(string(b))
}
log.Debug("sending close-sender")
err = message.Send(c.conn[0], c.Key, message.Message{
Type: "close-sender",
Type: message.TypeCloseSender,
})
if err != nil {
panic(err)
}
}
c.mutex.Unlock()
}
}
@ -1529,6 +1623,11 @@ func (c *Client) sendData(i int) {
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

View File

@ -1,8 +1,9 @@
package croc
import (
"io/ioutil"
"os"
"path"
"path/filepath"
"sync"
"testing"
"time"
@ -18,11 +19,11 @@ import (
func init() {
log.SetLevel("trace")
go tcp.Run("debug", "8081", "pass123", "8082,8083,8084,8085")
go tcp.Run("debug", "8082", "pass123")
go tcp.Run("debug", "8083", "pass123")
go tcp.Run("debug", "8084", "pass123")
go tcp.Run("debug", "8085", "pass123")
go tcp.Run("debug", "localhost", "8281", "pass123", "8282,8283,8284,8285")
go tcp.Run("debug", "localhost", "8282", "pass123")
go tcp.Run("debug", "localhost", "8283", "pass123")
go tcp.Run("debug", "localhost", "8284", "pass123")
go tcp.Run("debug", "localhost", "8285", "pass123")
time.Sleep(1 * time.Second)
}
@ -34,8 +35,8 @@ func TestCrocReadme(t *testing.T) {
IsSender: true,
SharedSecret: "8123-testingthecroc",
Debug: true,
RelayAddress: "localhost:8081",
RelayPorts: []string{"8081"},
RelayAddress: "localhost:8281",
RelayPorts: []string{"8281"},
RelayPassword: "pass123",
Stdout: false,
NoPrompt: true,
@ -52,7 +53,7 @@ func TestCrocReadme(t *testing.T) {
IsSender: false,
SharedSecret: "8123-testingthecroc",
Debug: true,
RelayAddress: "localhost:8081",
RelayAddress: "localhost:8281",
RelayPassword: "pass123",
Stdout: false,
NoPrompt: true,
@ -70,9 +71,11 @@ func TestCrocReadme(t *testing.T) {
body, _ := ioutil.ReadAll(http_resp.Body)
gh_version, _ := jsonparser.GetString([]byte(fmt.Sprintf("%s\n", body)), "name")
go func() {
err := sender.Send(TransferOptions{
PathToFiles: []string{"../../README.md"},
}, gh_version)
filesInfo, errGet := GetFilesInfo([]string{"../../README.md"})
if errGet != nil {
t.Errorf("failed to get minimal info: %v", errGet)
}
err := sender.Send(filesInfo, gh_version)
if err != nil {
t.Errorf("send failed: %v", err)
}
@ -90,6 +93,80 @@ func TestCrocReadme(t *testing.T) {
wg.Wait()
}
func TestCrocSymlink(t *testing.T) {
pathName := "../link-in-folder"
defer os.RemoveAll(pathName)
os.MkdirAll(pathName, 0755)
os.Symlink("../../README.md", filepath.Join(pathName, "README.link"))
log.Debug("setting up sender")
sender, err := New(Options{
IsSender: true,
SharedSecret: "8124-testingthecroc",
Debug: true,
RelayAddress: "localhost:8281",
RelayPorts: []string{"8281"},
RelayPassword: "pass123",
Stdout: false,
NoPrompt: true,
DisableLocal: true,
Curve: "siec",
Overwrite: true,
})
if err != nil {
panic(err)
}
log.Debug("setting up receiver")
receiver, err := New(Options{
IsSender: false,
SharedSecret: "8124-testingthecroc",
Debug: true,
RelayAddress: "localhost:8281",
RelayPassword: "pass123",
Stdout: false,
NoPrompt: true,
DisableLocal: true,
Curve: "siec",
Overwrite: true,
})
if err != nil {
panic(err)
}
var wg sync.WaitGroup
wg.Add(2)
go func() {
filesInfo, errGet := GetFilesInfo([]string{pathName})
if errGet != nil {
t.Errorf("failed to get minimal info: %v", errGet)
}
err := sender.Send(filesInfo)
if err != nil {
t.Errorf("send failed: %v", err)
}
wg.Done()
}()
time.Sleep(100 * time.Millisecond)
go func() {
err := receiver.Receive()
if err != nil {
t.Errorf("receive failed: %v", err)
}
wg.Done()
}()
wg.Wait()
s, err := filepath.EvalSymlinks(path.Join(pathName, "README.link"))
if s != "../../README.md" {
t.Errorf("symlink failed to transfer in folder")
}
if err != nil {
t.Errorf("symlink transfer failed: %s", err.Error())
}
}
func TestCrocLocal(t *testing.T) {
log.SetLevel("trace")
defer os.Remove("LICENSE")
@ -143,6 +220,12 @@ func TestCrocLocal(t *testing.T) {
PathToFiles: []string{"../../LICENSE", "touched"},
KeepPathInRemote: false,
}, gh_version)
filesInfo, errGet := GetFilesInfo([]string{"../../LICENSE", "touched"})
if errGet != nil {
t.Errorf("failed to get minimal info: %v", errGet)
}
err := sender.Send(filesInfo, gh_version)
if err != nil {
t.Errorf("send failed: %v", err)
}
@ -162,7 +245,7 @@ func TestCrocLocal(t *testing.T) {
func TestCrocError(t *testing.T) {
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
panic(err)
}
@ -194,10 +277,11 @@ func TestCrocError(t *testing.T) {
http_resp, _ := http.Get("https://api.github.com/repos/schollz/croc/releases/latest")
body, _ := ioutil.ReadAll(http_resp.Body)
gh_version, _ := jsonparser.GetString([]byte(fmt.Sprintf("%s\n", body)), "name")
err = sender.Send(TransferOptions{
PathToFiles: []string{tmpfile.Name()},
KeepPathInRemote: true,
}, gh_version)
filesInfo, errGet := GetFilesInfo([]string{tmpfile.Name()})
if errGet != nil {
t.Errorf("failed to get minimal info: %v", errGet)
}
err = sender.Send(filesInfo, gh_version)
log.Debug(err)
assert.NotNil(t, err)

View File

@ -1,5 +1,3 @@
#! /bin/bash
: ${PROG:=$(basename ${BASH_SOURCE})}
_cli_bash_autocomplete() {

View File

@ -159,7 +159,7 @@ make_tempdir() {
# DESCRIPTION: Attempts to determin host os using uname
# PARAMETERS: none
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
# 1 = Unkown OS
# 1 = Unknown OS
# 20 = 'uname' not found in path
#-------------------------------------------------------------------------------
determine_os() {
@ -183,7 +183,7 @@ determine_os() {
# DESCRIPTION: Attempt to determin architecture of host
# PARAMETERS: none
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
# 1 = Unkown arch
# 1 = Unknown arch
# 20 = 'uname' not found in path
#-------------------------------------------------------------------------------
determine_arch() {
@ -238,7 +238,7 @@ download_file() {
#--- FUNCTION ----------------------------------------------------------------
# NAME: checksum_check
# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure
# integrity. Tries multiple tools before faling.
# integrity. Tries multiple tools before failing.
# PARAMETERS: $1 = path to checksum file
# $2 = location of file to check
# $3 = working directory
@ -528,7 +528,7 @@ main() {
local autocomplete_install_rcode
croc_bin_name="croc"
croc_version="9.2.0"
croc_version="9.5.3"
croc_dl_ext="tar.gz"
croc_base_url="https://github.com/schollz/croc/releases/download"
prefix="${1}"
@ -684,7 +684,7 @@ main() {
print_message "== Install prefix already exists. No need to create it." "info"
fi
[ ! -d "/etc/bash_completion.d/croc" ] && mkdir -p "/etc/bash_completion.d/croc"
case "${croc_os}" in
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;

View File

@ -2,7 +2,6 @@ package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
@ -43,7 +42,7 @@ func run() (err error) {
}
func replaceInFile(fname, start, end, replacement string) (err error) {
b, err := ioutil.ReadFile(fname)
b, err := os.ReadFile(fname)
if err != nil {
return
}
@ -58,7 +57,7 @@ func replaceInFile(fname, start, end, replacement string) (err error) {
fmt.Sprintf("%s%s%s", start, replacement, end),
1,
)
err = ioutil.WriteFile(fname, []byte(newF), 0644)
err = os.WriteFile(fname, []byte(newF), 0644)
return
}

View File

@ -9,9 +9,23 @@ import (
log "github.com/schollz/logger"
)
// Type is a message type
type Type string
const (
TypePAKE Type = "pake"
TypeExternalIP Type = "externalip"
TypeFinished Type = "finished"
TypeError Type = "error"
TypeCloseRecipient Type = "close-recipient"
TypeCloseSender Type = "close-sender"
TypeRecipientReady Type = "recipientready"
TypeFileInfo Type = "fileinfo"
)
// Message is the possible payload for messaging
type Message struct {
Type string `json:"t,omitempty"`
Type Type `json:"t,omitempty"`
Message string `json:"m,omitempty"`
Bytes []byte `json:"b,omitempty"`
Bytes2 []byte `json:"b2,omitempty"`

View File

@ -13,9 +13,11 @@ import (
"github.com/stretchr/testify/assert"
)
var TypeMessage Type = "message"
func TestMessage(t *testing.T) {
log.SetLevel("debug")
m := Message{Type: "message", Message: "hello, world"}
m := Message{Type: TypeMessage, Message: "hello, world"}
e, salt, err := crypt.New([]byte("pass"), nil)
assert.Nil(t, err)
fmt.Println(salt)
@ -35,7 +37,7 @@ func TestMessage(t *testing.T) {
func TestMessageNoPass(t *testing.T) {
log.SetLevel("debug")
m := Message{Type: "message", Message: "hello, world"}
m := Message{Type: TypeMessage, Message: "hello, world"}
b, err := Encode(nil, m)
assert.Nil(t, err)
fmt.Printf("%x\n", b)
@ -85,7 +87,7 @@ func TestSend(t *testing.T) {
time.Sleep(300 * time.Millisecond)
a, err := comm.NewConnection("localhost:"+port, 10*time.Minute)
assert.Nil(t, err)
m := Message{Type: "message", Message: "hello, world"}
m := Message{Type: TypeMessage, Message: "hello, world"}
e, salt, err := crypt.New([]byte("pass"), nil)
log.Debug(salt)
assert.Nil(t, err)

View File

@ -4,7 +4,10 @@ import (
"context"
"fmt"
"net"
"time"
"os"
"path"
"github.com/schollz/croc/v9/src/utils"
)
// TCP_BUFFER_SIZE is the maximum packet size
@ -16,25 +19,65 @@ var (
DEFAULT_RELAY6 = "croc6.schollz.com"
DEFAULT_PORT = "9009"
DEFAULT_PASSPHRASE = "pass123"
INTERNAL_DNS = false
)
// lookupTimeout for DNS requests
const lookupTimeout = time.Second
// publicDns are servers to be queried if a local lookup fails
var publicDns = []string{
"1.0.0.1", // Cloudflare
"1.1.1.1", // Cloudflare
"[2606:4700:4700::1111]", // Cloudflare
"[2606:4700:4700::1001]", // Cloudflare
"8.8.4.4", // Google
"8.8.8.8", // Google
"8.26.56.26", // Comodo
"208.67.220.220", // Cisco OpenDNS
"208.67.222.222", // Cisco OpenDNS
"[2001:4860:4860::8844]", // Google
"[2001:4860:4860::8888]", // Google
"9.9.9.9", // Quad9
"149.112.112.112", // Quad9
"[2620:fe::fe]", // Quad9
"[2620:fe::fe:9]", // Quad9
"8.26.56.26", // Comodo
"8.20.247.20", // Comodo
"208.67.220.220", // Cisco OpenDNS
"208.67.222.222", // Cisco OpenDNS
"[2620:119:35::35]", // Cisco OpenDNS
"[2620:119:53::53]", // Cisco OpenDNS
}
func getConfigFile() (fname string, err error) {
configFile, err := utils.GetConfigDir()
if err != nil {
return
}
fname = path.Join(configFile, "internal-dns")
return
}
func init() {
doRemember := false
for _, flag := range os.Args {
if flag == "--internal-dns" {
INTERNAL_DNS = true
break
}
if flag == "--remember" {
doRemember = true
}
}
if doRemember {
// save in config file
fname, err := getConfigFile()
if err == nil {
f, _ := os.Create(fname)
f.Close()
}
}
if !INTERNAL_DNS {
fname, err := getConfigFile()
if err == nil {
INTERNAL_DNS = utils.Exists(fname)
}
}
var err error
DEFAULT_RELAY, err = lookup(DEFAULT_RELAY)
if err == nil {
@ -50,42 +93,36 @@ func init() {
}
}
// lookup an IP address.
//
// Priority is given to local queries, and the system falls back to a list of
// public DNS servers.
// Resolve a hostname to an IP address using DNS.
func lookup(address string) (ipaddress string, err error) {
ipaddress, err = localLookupIP(address)
if err == nil {
return
if !INTERNAL_DNS {
return localLookupIP(address)
}
err = nil
result := make(chan string, len(publicDns))
type Result struct {
s string
err error
}
result := make(chan Result, len(publicDns))
for _, dns := range publicDns {
go func(dns string) {
s, _ := remoteLookupIP(address, dns)
result <- s
var r Result
r.s, r.err = remoteLookupIP(address, dns)
result <- r
}(dns)
}
for i := 0; i < len(publicDns); i++ {
ipaddress = <-result
ipaddress = (<-result).s
if ipaddress != "" {
return
}
}
err = fmt.Errorf("failed to lookup %s at any DNS server", address)
err = fmt.Errorf("failed to resolve %s: all DNS servers exhausted", address)
return
}
// localLookupIP returns a host's IP address based on the local resolver.
func localLookupIP(address string) (ipaddress string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), lookupTimeout)
defer cancel()
ip, err := net.DefaultResolver.LookupHost(ctx, address)
ip, err := net.LookupHost(address)
if err != nil {
return
}
@ -98,10 +135,8 @@ func remoteLookupIP(address, dns string) (ipaddress string, err error) {
r := &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
d := net.Dialer{
Timeout: lookupTimeout,
}
return d.DialContext(ctx, "udp", dns+":53")
d := new(net.Dialer)
return d.DialContext(ctx, network, dns+":53")
},
}
ip, err := r.LookupHost(context.Background(), address)

View File

@ -17,6 +17,7 @@ import (
)
type server struct {
host string
port string
debugLevel string
banner string
@ -36,12 +37,14 @@ type roomMap struct {
sync.Mutex
}
const pingRoom = "pinglkasjdlfjsaldjf"
var timeToRoomDeletion = 10 * time.Minute
var pingRoom = "pinglkasjdlfjsaldjf"
// Run starts a tcp listener, run async
func Run(debugLevel, port, password string, banner ...string) (err error) {
func Run(debugLevel, host, port, password string, banner ...string) (err error) {
s := new(server)
s.host = host
s.port = port
s.password = password
s.debugLevel = debugLevel
@ -85,10 +88,31 @@ func (s *server) start() (err error) {
}
func (s *server) run() (err error) {
log.Infof("starting TCP server on " + s.port)
server, err := net.Listen("tcp", ":"+s.port)
network := "tcp"
addr := net.JoinHostPort(s.host, s.port)
if s.host != "" {
ip := net.ParseIP(s.host)
if ip == nil {
tcpIP, err := net.ResolveIPAddr("ip", s.host)
if err != nil {
return err
}
ip = tcpIP.IP
}
addr = net.JoinHostPort(ip.String(), s.port)
if s.host != "" {
if ip.To4() != nil {
network = "tcp4"
} else {
network = "tcp6"
}
}
}
addr = strings.Replace(addr, "127.0.0.1", "0.0.0.0", 1)
log.Infof("starting TCP server on " + addr)
server, err := net.Listen(network, addr)
if err != nil {
return fmt.Errorf("error listening on %s: %w", s.port, err)
return fmt.Errorf("error listening on %s: %w", addr, err)
}
defer server.Close()
// spawn a new goroutine whenever a client connects
@ -160,8 +184,10 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
if err != nil {
return
}
log.Debugf("Abytes: %s", Abytes)
if bytes.Equal(Abytes, []byte("ping")) {
room = pingRoom
log.Debug("sending back pong")
c.Send([]byte("pong"))
return
}
@ -200,7 +226,7 @@ func (s *server) clientCommunication(port string, c *comm.Comm) (room string, er
}
if strings.TrimSpace(string(passwordBytes)) != s.password {
err = fmt.Errorf("bad password")
enc, _ := crypt.Decrypt([]byte(err.Error()), strongKeyForEncryption)
enc, _ := crypt.Encrypt([]byte(err.Error()), strongKeyForEncryption)
if err := c.Send(enc); err != nil {
return "", fmt.Errorf("send error: %w", err)
}
@ -385,16 +411,20 @@ func pipe(conn1 net.Conn, conn2 net.Conn) {
}
func PingServer(address string) (err error) {
c, err := comm.NewConnection(address, 200*time.Millisecond)
log.Debugf("pinging %s", address)
c, err := comm.NewConnection(address, 300*time.Millisecond)
if err != nil {
log.Debug(err)
return
}
err = c.Send([]byte("ping"))
if err != nil {
log.Debug(err)
return
}
b, err := c.Receive()
if err != nil {
log.Debug(err)
return
}
if bytes.Equal(b, []byte("pong")) {
@ -412,62 +442,75 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
c, err = comm.NewConnection(address)
}
if err != nil {
log.Debug(err)
return
}
// get PAKE connection with server to establish strong key to transfer info
A, err := pake.InitCurve(weakKey, 0, "siec")
if err != nil {
log.Debug(err)
return
}
err = c.Send(A.Bytes())
if err != nil {
log.Debug(err)
return
}
Bbytes, err := c.Receive()
if err != nil {
log.Debug(err)
return
}
err = A.Update(Bbytes)
if err != nil {
log.Debug(err)
return
}
strongKey, err := A.SessionKey()
if err != nil {
log.Debug(err)
return
}
log.Debugf("strong key: %x", strongKey)
strongKeyForEncryption, salt, err := crypt.New(strongKey, nil)
if err != nil {
log.Debug(err)
return
}
// send salt
err = c.Send(salt)
if err != nil {
log.Debug(err)
return
}
log.Debug("sending password")
bSend, err := crypt.Encrypt([]byte(password), strongKeyForEncryption)
if err != nil {
log.Debug(err)
return
}
err = c.Send(bSend)
if err != nil {
log.Debug(err)
return
}
log.Debug("waiting for first ok")
enc, err := c.Receive()
if err != nil {
log.Debug(err)
return
}
data, err := crypt.Decrypt(enc, strongKeyForEncryption)
if err != nil {
log.Debug(err)
return
}
if !strings.Contains(string(data), "|||") {
err = fmt.Errorf("bad response: %s", string(data))
log.Debug(err)
return
}
banner = strings.Split(string(data), "|||")[0]
@ -475,23 +518,28 @@ func ConnectToTCPServer(address, password, room string, timelimit ...time.Durati
log.Debug("sending room")
bSend, err = crypt.Encrypt([]byte(room), strongKeyForEncryption)
if err != nil {
log.Debug(err)
return
}
err = c.Send(bSend)
if err != nil {
log.Debug(err)
return
}
log.Debug("waiting for room confirmation")
enc, err = c.Receive()
if err != nil {
log.Debug(err)
return
}
data, err = crypt.Decrypt(enc, strongKeyForEncryption)
if err != nil {
log.Debug(err)
return
}
if !bytes.Equal(data, []byte("ok")) {
err = fmt.Errorf("got bad response: %s", data)
log.Debug(err)
return
}
log.Debug("all set")

View File

@ -12,7 +12,7 @@ import (
func BenchmarkConnection(b *testing.B) {
log.SetLevel("trace")
go Run("debug", "8283", "pass123", "8284")
go Run("debug", "localhost", "8283", "pass123", "8284")
time.Sleep(100 * time.Millisecond)
b.ResetTimer()
for i := 0; i < b.N; i++ {
@ -24,22 +24,22 @@ func BenchmarkConnection(b *testing.B) {
func TestTCP(t *testing.T) {
log.SetLevel("error")
timeToRoomDeletion = 100 * time.Millisecond
go Run("debug", "8281", "pass123", "8282")
time.Sleep(100 * time.Millisecond)
err := PingServer("localhost:8281")
go Run("debug", "localhost", "8381", "pass123", "8382")
time.Sleep(timeToRoomDeletion)
err := PingServer("localhost:8381")
assert.Nil(t, err)
err = PingServer("localhost:8333")
assert.NotNil(t, err)
time.Sleep(100 * time.Millisecond)
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Minute)
assert.Equal(t, banner, "8282")
time.Sleep(timeToRoomDeletion)
c1, banner, _, err := ConnectToTCPServer("localhost:8381", "pass123", "testRoom", 1*time.Minute)
assert.Equal(t, banner, "8382")
assert.Nil(t, err)
c2, _, _, err := ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
c2, _, _, err := ConnectToTCPServer("localhost:8381", "pass123", "testRoom")
assert.Nil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom")
_, _, _, err = ConnectToTCPServer("localhost:8381", "pass123", "testRoom")
assert.NotNil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "pass123", "testRoom", 1*time.Nanosecond)
_, _, _, err = ConnectToTCPServer("localhost:8381", "pass123", "testRoom", 1*time.Nanosecond)
assert.NotNil(t, err)
// try sending data

View File

@ -9,13 +9,13 @@ import (
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"math/big"
"net"
"net/http"
"os"
"path"
"strings"
"time"
@ -24,6 +24,27 @@ import (
"github.com/schollz/mnemonicode"
)
// Get or create home directory
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 = path.Join(homedir, ".config", "croc")
}
if _, err = os.Stat(homedir); os.IsNotExist(err) {
err = os.MkdirAll(homedir, 0700)
}
return
}
// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
if _, err := os.Stat(name); err != nil {
@ -53,11 +74,11 @@ func HashFile(fname string, algorithm string) (hash256 []byte, err error) {
if fstats.Mode()&os.ModeSymlink != 0 {
var target string
target, err = os.Readlink(fname)
if err != nil {
return nil, err
}
return []byte(SHA256(target)), nil
}
if err != nil {
return nil, err
}
switch algorithm {
case "imohash":
return IMOHashFile(fname)
@ -136,7 +157,7 @@ func PublicIP() (ip string, err error) {
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
}
@ -172,7 +193,7 @@ func GenerateRandomPin() string {
return s
}
// GetRandomName returns mnemoicoded random name
// GetRandomName returns mnemonicoded random name
func GetRandomName() string {
var result []string
bs := make([]byte, 4)
@ -283,7 +304,7 @@ func GetLocalIPs() (ips []string, err error) {
}
func RandomFileName() (fname string, err error) {
f, err := ioutil.TempFile(".", "croc-stdin-")
f, err := os.CreateTemp(".", "croc-stdin-")
if err != nil {
return
}

View File

@ -3,21 +3,21 @@ package utils
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"strings"
"testing"
"github.com/schollz/croc/v9/src/models"
"github.com/stretchr/testify/assert"
)
const TCP_BUFFER_SIZE = 1024 * 64
var bigFileSize = 75000000
func bigFile() {
ioutil.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0666)
os.WriteFile("bigfile.test", bytes.Repeat([]byte("z"), bigFileSize), 0666)
}
func BenchmarkMD5(b *testing.B) {
@ -63,7 +63,7 @@ func BenchmarkMissingChunks(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
MissingChunks("bigfile.test", int64(bigFileSize), models.TCP_BUFFER_SIZE/2)
MissingChunks("bigfile.test", int64(bigFileSize), TCP_BUFFER_SIZE/2)
}
}
@ -119,7 +119,7 @@ func TestMissingChunks(t *testing.T) {
rand.Seed(1)
bigBuff := make([]byte, fileSize)
rand.Read(bigBuff)
ioutil.WriteFile("missing.test", bigBuff, 0644)
os.WriteFile("missing.test", bigBuff, 0644)
empty := make([]byte, chunkSize)
f, err := os.OpenFile("missing.test", os.O_RDWR, 0644)
assert.Nil(t, err)
@ -139,7 +139,7 @@ func TestMissingChunks(t *testing.T) {
os.Remove("missing.test")
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
log.Fatal(err)
}
@ -171,7 +171,7 @@ func TestMissingChunks(t *testing.T) {
func TestHashFile(t *testing.T) {
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
tmpfile, err := os.CreateTemp("", "example")
if err != nil {
log.Fatal(err)
}