mirror of https://github.com/schollz/croc.git
Merge branch 'master' into master
This commit is contained in:
commit
89387a3168
|
@ -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 ./...
|
|
@ -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
|
|
@ -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
|
||||
|
|
23
README.md
23
README.md
|
@ -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
38
go.mod
|
@ -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
57
go.sum
|
@ -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=
|
||||
|
|
136
src/cli/cli.go
136
src/cli/cli.go
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
351
src/croc/croc.go
351
src/croc/croc.go
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#! /bin/bash
|
||||
|
||||
: ${PROG:=$(basename ${BASH_SOURCE})}
|
||||
|
||||
_cli_bash_autocomplete() {
|
||||
|
|
|
@ -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="${?}";;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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"`
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue