start over again

This commit is contained in:
Zack Scholl 2019-11-08 15:43:50 -08:00
parent a0d9d74d5d
commit a124627d09
27 changed files with 223 additions and 4230 deletions

View File

@ -1,23 +0,0 @@
language: go
go:
- tip
env:
- "PATH=/home/travis/gopath/bin:$PATH"
install: true
script:
- env GO111MODULE=on go build -v
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/compress
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/croc
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/crypt
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/tcp
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/utils
- env GO111MODULE=on go test -v -cover github.com/schollz/croc/v6/src/comm
branches:
except:
- dev
- win

View File

@ -1,15 +0,0 @@
FROM golang:1.12-alpine as builder
RUN apk add --no-cache git
WORKDIR /go/croc
COPY . .
RUN go build -v
FROM alpine:latest
EXPOSE 9009
EXPOSE 9010
EXPOSE 9011
EXPOSE 9012
EXPOSE 9013
COPY --from=builder /go/croc/croc /croc
ENTRYPOINT ["/croc"]
CMD ["relay"]

21
LICENSE
View File

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2017 Zack
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

141
README.md
View File

@ -1,138 +1,7 @@
<p align="center">
<img
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-v6.2.0-brightgreen.svg?style=flat-square" alt="Version"></a>
<a href="https://coveralls.io/github/schollz/croc"><img src="https://img.shields.io/badge/coverage-80%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>
<a href="https://saythanks.io/to/schollz"><img src="https://img.shields.io/badge/Say%20Thanks-!-brightgreen.svg?style=flat-square" alt="Say thanks"></a>
</p>
<p align="center"><code>curl https://getcroc.schollz.com | bash</code></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 does **all** of the following:
- allows **any two computers** to transfer data (using a relay)
- provides **end-to-end encryption** (using PAKE)
- enables easy **cross-platform** transfers (Windows, Linux, Mac)
- allows **multiple file** transfers
- allows **resuming transfers** that are interrupted
- local server or port-forwarding **not needed**
- **faster** than [wormhole](https://github.com/warner/magic-wormhole), [rsync](https://linux.die.net/man/1/rsync), [scp](https://linux.die.net/man/1/scp) through compression and multiplexing (speedups 1.5x to 4x)
For more information about `croc`, see [my blog post](https://schollz.com/software/croc6).
## Install
Download [the latest release for your system](https://github.com/schollz/croc/releases/latest), or install a release from the command-line:
```
$ curl https://getcroc.schollz.com | bash
Work in progress.
This will be yet another re-write of croc (the 7th!). This time it should be
compatible with a browser as it will use WebRTC by default for data transfer and
it the main relay (for PAKE and WebRTC offer exchange) will be made using websockets.
```
On macOS you can install the latest release with [Homebrew](https://brew.sh/):
```
$ brew install schollz/tap/croc
```
On Windows you can install the latest release with [Scoop](https://scoop.sh/):
```
$ scoop bucket add schollz-bucket https://github.com/schollz/scoop-bucket.git
$ scoop install croc
```
Or, you can [install Go](https://golang.org/dl/) and build from source (requires Go 1.12+):
```
$ go get -v github.com/schollz/croc
```
## Usage
To send a file, simply do:
```
$ croc send [file(s)-or-folder]
Sending 'file-or-folder' (X MB)
Code is: code-phrase
```
Then to receive the file (or folder) on another computer, you can just do
```
$ croc code-phrase
```
The code phrase is used to establish password-authenticated key agreement ([PAKE](https://en.wikipedia.org/wiki/Password-authenticated_key_agreement)) which generates a secret key for the sender and recipient to use for end-to-end encryption.
There are a number of configurable options (see `--help`). A set of options (like custom relay, ports, and code phrase) can be set using `--remember`.
### Custom code phrase
You can send with your own code phrase (must be more than 4 characters).
```
$ croc send --code [code-phrase] [file(s)-or-folder]
```
### Use pipes - stdin and stdout
You can pipe to `croc`:
```
$ cat [filename] | croc send
```
In this case `croc` will automatically use the stdin data and send and assign a filename like "croc-stdin-123456789". To receive to `stdout` at you can always just use the `--yes` will automatically approve the transfer and pipe it out to `stdout`.
```
$ croc --yes [code-phrase] > out
```
All of the other text printed to the console is going to `stderr` so it will not interfere with the message going to `stdout`.
### Self-host relay
The relay is needed to staple the parallel incoming and outgoing connections. By default, `croc` uses a public relay but you can also run your own relay:
```
$ croc relay
```
Make sure to open up TCP ports (see `croc relay --help` for which ports to open).
You can send files using your relay by entering `--relay` to change the relay that you are using if you want to custom host your own.
```
$ croc --relay "myrelay.example.com:9009" send [filename]
```
If it's easier you can also run a relay with Docker:
```
$ docker run -d -p 9009:9009 -p 9010:9010 -p 9011:9011 -p 9012:9012 -p 9013:9013 schollz/croc
```
## License
MIT
## Acknowledgements
`croc` has been through many iterations, and I am awed by all the great contributions! If you feel like contributing, in any way, by all means you can send an Issue, a PR, ask a question, or tweet me ([@yakczar](http://ctt.ec/Rq054)).
Thanks [@warner](https://github.com/warner) for the [idea](https://github.com/warner/magic-wormhole), [@tscholl2](https://github.com/tscholl2) for the [encryption gists](https://gist.github.com/tscholl2/dc7dc15dc132ea70a98e8542fefffa28), [@skorokithakis](https://github.com/skorokithakis) for [code on proxying two connections](https://www.stavros.io/posts/proxying-two-connections-go/). Finally thanks for making pull requests [@meyermarcel](https://github.com/meyermarcel), [@Girbons](https://github.com/Girbons), [@techtide](https://github.com/techtide), [@heymatthew](https://github.com/heymatthew), [@Lunsford94](https://github.com/Lunsford94), [@lummie](https://github.com/lummie), [@jesuiscamille](https://github.com/jesuiscamille), [@threefjord](https://github.com/threefjord), [@marcossegovia](https://github.com/marcossegovia), [@csleong98](https://github.com/csleong98), [@afotescu](https://github.com/afotescu), [@callmefever](https://github.com/callmefever), [@El-JojA](https://github.com/El-JojA), [@anatolyyyyyy](https://github.com/anatolyyyyyy), [@goggle](https://github.com/goggle), [@smileboywtu](https://github.com/smileboywtu), [@nicolashardy](https://github.com/nicolashardy)!

30
go.mod
View File

@ -1,30 +0,0 @@
module github.com/schollz/croc/v6
go 1.13
require (
github.com/OneOfOne/xxhash v1.2.5 // indirect
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/fatih/color v1.7.0 // indirect
github.com/kalafut/imohash v1.0.0
github.com/kr/pretty v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.4 // indirect
github.com/mattn/go-isatty v0.0.9 // indirect
github.com/pkg/errors v0.8.1
github.com/schollz/logger v1.0.1
github.com/schollz/mnemonicode v1.0.1
github.com/schollz/pake v1.1.1
github.com/schollz/peerdiscovery v1.4.1
github.com/schollz/progressbar/v2 v2.14.0
github.com/schollz/spinner v0.0.0-20180925172146-6bbc5f7804f9
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/urfave/cli v1.22.1
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc
golang.org/x/net v0.0.0-20191003171128-d98b1b443823 // indirect
golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2 // indirect
golang.org/x/text v0.3.2 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
)

102
go.sum
View File

@ -1,102 +0,0 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
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/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/kalafut/imohash v1.0.0 h1:LgCJ+p/BwM2HKpOxFopkeddpzVCfm15EtXMroXD1SYE=
github.com/kalafut/imohash v1.0.0/go.mod h1:c3RHT80ZAp5C/aYgQI92ZlrOymqkZnRDprU87kg75HI=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/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-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/schollz/logger v1.0.1 h1:BuBAU+euqphM0Ny9qFVScl4RSxatis4nCHIkOxO2cUU=
github.com/schollz/logger v1.0.1/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 v1.1.1 h1:QKeojDWzdAdtRC4m89b6HAxw/8gjqrVu7r4SAOxOFg8=
github.com/schollz/pake v1.1.1/go.mod h1:aWMxQ1jwqZRwk3StflHcdyzPR+CyW5W7+WIZD6Y3dEY=
github.com/schollz/peerdiscovery v1.4.1 h1:xtZ/D8/4eq9O6UEhRupZZiJm4BA8+u1IVUgeHo5VPm4=
github.com/schollz/peerdiscovery v1.4.1/go.mod h1:WDdk0/JVyVHVIA/bmhzTkUg32dhJ20O4tExNqV1u6sk=
github.com/schollz/progressbar/v2 v2.13.2 h1:3L9bP5KQOGEnFP8P5V8dz+U0yo5I29iY5Oa9s9EAwn0=
github.com/schollz/progressbar/v2 v2.13.2/go.mod h1:6YZjqdthH6SCZKv2rqGryrxPtfmRB/DWZxSMfCXPyD8=
github.com/schollz/progressbar/v2 v2.14.0 h1:vo7bdkI9E4/CIk9DnL5uVIaybLQiVtiCC2vO+u9j5IM=
github.com/schollz/progressbar/v2 v2.14.0/go.mod h1:6YZjqdthH6SCZKv2rqGryrxPtfmRB/DWZxSMfCXPyD8=
github.com/schollz/spinner v0.0.0-20180925172146-6bbc5f7804f9 h1:y08o5oQ/slxXE/F0uh5dd8mdVvb+w4NLcNSDSq4c2F0=
github.com/schollz/spinner v0.0.0-20180925172146-6bbc5f7804f9/go.mod h1:kCMoQsqzx4MzGJWaALr6tKyCnlrY0kILGLkA1FOiLF4=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/tscholl2/siec v0.0.0-20180721101609-21667da05937 h1:lhssCpSe3TjKcbvUoPzFMuv9oUyZDgI3Cmgolfw2C90=
github.com/tscholl2/siec v0.0.0-20180721101609-21667da05937/go.mod h1:KL9+ubr1JZdaKjgAaHr+tCytEncXBa1pR6FjbTsOJnw=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc h1:c0o/qxkaO2LF5t6fQrT4b5hzyggAkLLlCUjqfRxd8Q4=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191003171128-d98b1b443823 h1:Ypyv6BNJh07T1pUSrehkLemqPKXhus2MkfktJ91kRh4=
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed h1:5TJcLJn2a55mJjzYk0yOoqN8X1OdvBDUnaZaKKyQtkY=
golang.org/x/sys v0.0.0-20191002091554-b397fe3ad8ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2 h1:I7efaDQAsIQmkTF+WSdcydwVWzK07Yuz8IFF8rNkDe0=
golang.org/x/sys v0.0.0-20191023151326-f89234f9a2c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
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-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/tylerb/is.v1 v1.1.2 h1:AB/MANFml2ySf+adwcinvajyHvsYltAOD+rb/8njfSU=
gopkg.in/tylerb/is.v1 v1.1.2/go.mod h1:9yQB2tyIhZ5oph6Kk5Sq7cJMd9c5Jpa1p3hr9kxzPqo=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -1,86 +0,0 @@
project_name: croc
build:
main: main.go
binary: croc
ldflags: -s -w -X main.Version="v{{.Version}}-{{.Date}}"
env:
- CGO_ENABLED=0
goos:
- darwin
- linux
- windows
- freebsd
- netbsd
- openbsd
- dragonfly
goarch:
- amd64
- 386
- arm
- arm64
goarm:
- 7
nfpm:
formats:
- deb
vendor: "schollz.com"
homepage: "https://schollz.com/software/croc/"
maintainer: "Zack Scholl <zack.scholl@gmail.com>"
description: "A simple, secure, and fast way to transfer data."
license: "MIT"
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
replacements:
amd64: 64bit
386: 32bit
arm: ARM
arm64: ARM64
darwin: macOS
linux: Linux
windows: Windows
openbsd: OpenBSD
netbsd: NetBSD
freebsd: FreeBSD
dragonfly: DragonFlyBSD
archives:
-
format: tar.gz
format_overrides:
- goos: windows
format: zip
name_template: "{{.ProjectName}}_{{.Version}}_{{.Os}}-{{.Arch}}"
replacements:
amd64: 64bit
386: 32bit
arm: ARM
arm64: ARM64
darwin: macOS
linux: Linux
windows: Windows
openbsd: OpenBSD
netbsd: NetBSD
freebsd: FreeBSD
dragonfly: DragonFlyBSD
files:
- README.md
- LICENSE
brew:
github:
owner: schollz
name: homebrew-tap
folder: Formula
description: "croc is a tool that allows any two computers to simply and securely transfer files and folders."
homepage: "https://schollz.com/software/croc/"
install: |
bin.install "croc"
test: |
system "#{bin}/croc --version"
scoop:
bucket:
owner: schollz
name: scoop-bucket
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

18
main.go
View File

@ -1,18 +0,0 @@
package main
//go:generate git tag -af v$VERSION -m "v$VERSION"
//go:generate go run src/install/updateversion.go
//go:generate git commit -am "bump $VERSION"
//go:generate git tag -af v$VERSION -m "v$VERSION"
import (
"fmt"
"github.com/schollz/croc/v6/src/cli"
)
func main() {
if err := cli.Run(); err != nil {
fmt.Println(err)
}
}

View File

@ -1,379 +0,0 @@
package cli
import (
"encoding/json"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/schollz/croc/v6/src/croc"
"github.com/schollz/croc/v6/src/models"
"github.com/schollz/croc/v6/src/tcp"
"github.com/schollz/croc/v6/src/utils"
log "github.com/schollz/logger"
"github.com/urfave/cli"
)
// Version specifies the version
var Version string
// Run will run the command line proram
func Run() (err error) {
// use all of the processors
runtime.GOMAXPROCS(runtime.NumCPU())
app := cli.NewApp()
app.Name = "croc"
if Version == "" {
Version = "v6.2.0-ffddd3e"
}
app.Version = Version
app.Compiled = time.Now()
app.Usage = "easily and securely transfer stuff from one computer to another"
app.UsageText = "croc allows any two computers to directly and securely transfer files"
// app.ArgsUsage = "[args and such]"
app.Commands = []cli.Command{
{
Name: "send",
Usage: "send a file",
Description: "send a file over the relay",
ArgsUsage: "[filename]",
Flags: []cli.Flag{
cli.StringFlag{Name: "code, c", Usage: "codephrase used to connect to relay"},
cli.BoolFlag{Name: "no-local", Usage: "disable local relay when sending"},
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)
},
},
{
Name: "relay",
Description: "start relay",
HelpName: "croc relay",
Action: func(c *cli.Context) error {
return relay(c)
},
Flags: []cli.Flag{
cli.StringFlag{Name: "ports", Value: "9009,9010,9011,9012,9013", Usage: "ports of the relay"},
},
},
}
app.Flags = []cli.Flag{
cli.BoolFlag{Name: "remember", Usage: "save these settings to reuse next time"},
cli.BoolFlag{Name: "debug", Usage: "increase verbosity (a lot)"},
cli.BoolFlag{Name: "yes", Usage: "automatically agree to all prompts"},
cli.BoolFlag{Name: "stdout", Usage: "redirect file to stdout"},
cli.BoolFlag{Name: "ask", Usage: "make sure sender and recipient are prompted"},
cli.StringFlag{Name: "relay", Value: models.DEFAULT_RELAY, Usage: "address of the relay"},
cli.StringFlag{Name: "out", Value: ".", Usage: "specify an output folder to receive the file"},
}
app.EnableBashCompletion = true
app.HideHelp = false
app.HideVersion = false
app.BashComplete = func(c *cli.Context) {
fmt.Fprintf(c.App.Writer, "send\nreceive\relay")
}
app.Action = func(c *cli.Context) error {
// if trying to send but forgot send, let the user know
if c.Args().First() != "" && utils.Exists(c.Args().First()) {
_, fname := filepath.Split(c.Args().First())
yn := utils.GetInput(fmt.Sprintf("Did you mean to send '%s'? (y/n) ", fname))
if strings.ToLower(yn) == "y" {
return send(c)
}
}
return receive(c)
}
return app.Run(os.Args)
}
func getConfigDir() (homedir string, err error) {
homedir, err = os.UserHomeDir()
if err != nil {
log.Error(err)
return
}
homedir = path.Join(homedir, ".config", "croc")
if _, err = os.Stat(homedir); os.IsNotExist(err) {
log.Debugf("creating home directory %s", homedir)
err = os.MkdirAll(homedir, 0700)
}
return
}
func setDebugLevel(c *cli.Context) {
if c.GlobalBool("debug") {
log.SetLevel("debug")
log.Debug("debug mode on")
} else {
log.SetLevel("info")
}
}
func getConfigFile() string {
configFile, err := getConfigDir()
if err != nil {
log.Error(err)
return ""
}
return path.Join(configFile, "send.json")
}
func send(c *cli.Context) (err error) {
setDebugLevel(c)
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: true,
Debug: c.GlobalBool("debug"),
NoPrompt: c.GlobalBool("yes"),
RelayAddress: c.GlobalString("relay"),
Stdout: c.GlobalBool("stdout"),
DisableLocal: c.Bool("no-local"),
RelayPorts: strings.Split(c.String("ports"), ","),
Ask: c.GlobalBool("ask"),
}
b, errOpen := ioutil.ReadFile(getConfigFile())
if errOpen == nil && !c.GlobalBool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
if err != nil {
log.Error(err)
return
}
// update anything that isn't explicitly set
if !c.GlobalIsSet("relay") {
crocOptions.RelayAddress = rememberedOptions.RelayAddress
}
if !c.IsSet("no-local") {
crocOptions.DisableLocal = rememberedOptions.DisableLocal
}
if !c.IsSet("ports") {
crocOptions.RelayPorts = rememberedOptions.RelayPorts
}
if !c.IsSet("code") {
crocOptions.SharedSecret = rememberedOptions.SharedSecret
}
}
var fnames []string
stat, _ := os.Stdin.Stat()
if (stat.Mode() & os.ModeCharDevice) == 0 {
fnames, err = getStdin()
if err != nil {
return
}
defer func() {
err = os.Remove(fnames[0])
if err != nil {
log.Error(err)
}
}()
} else {
fnames = append([]string{c.Args().First()}, c.Args().Tail()...)
}
if len(fnames) == 0 {
return errors.New("must specify file: croc send [filename]")
}
if len(crocOptions.SharedSecret) == 0 {
// generate code phrase
crocOptions.SharedSecret = utils.GetRandomName()
}
paths, haveFolder, err := getPaths(fnames)
if err != nil {
return
}
cr, err := croc.New(crocOptions)
if err != nil {
return
}
// save the config
saveConfig(c, crocOptions)
err = cr.Send(croc.TransferOptions{
PathToFiles: paths,
KeepPathInRemote: haveFolder,
})
return
}
func getStdin() (fnames []string, err error) {
f, err := ioutil.TempFile(".", "croc-stdin-")
if err != nil {
return
}
_, err = io.Copy(f, os.Stdin)
if err != nil {
return
}
err = f.Close()
if err != nil {
return
}
fnames = []string{f.Name()}
return
}
func getPaths(fnames []string) (paths []string, haveFolder bool, err error) {
haveFolder = false
paths = []string{}
for _, fname := range fnames {
stat, errStat := os.Stat(fname)
if errStat != nil {
err = errStat
return
}
if stat.IsDir() {
haveFolder = true
err = filepath.Walk(fname,
func(pathName string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() {
paths = append(paths, filepath.ToSlash(pathName))
}
return nil
})
if err != nil {
return
}
} else {
paths = append(paths, filepath.ToSlash(fname))
}
}
return
}
func saveConfig(c *cli.Context, crocOptions croc.Options) {
if c.GlobalBool("remember") {
configFile := getConfigFile()
log.Debug("saving config file")
var bConfig []byte
// if the code wasn't set, don't save it
if c.String("code") == "" {
crocOptions.SharedSecret = ""
}
bConfig, err := json.MarshalIndent(crocOptions, "", " ")
if err != nil {
log.Error(err)
return
}
err = ioutil.WriteFile(configFile, bConfig, 0644)
if err != nil {
log.Error(err)
return
}
log.Debugf("wrote %s", configFile)
}
}
func receive(c *cli.Context) (err error) {
crocOptions := croc.Options{
SharedSecret: c.String("code"),
IsSender: false,
Debug: c.GlobalBool("debug"),
NoPrompt: c.GlobalBool("yes"),
RelayAddress: c.GlobalString("relay"),
Stdout: c.GlobalBool("stdout"),
Ask: c.GlobalBool("ask"),
}
if c.Args().First() != "" {
crocOptions.SharedSecret = c.Args().First()
}
// load options here
setDebugLevel(c)
configFile, err := getConfigDir()
if err != nil {
log.Error(err)
return
}
configFile = path.Join(configFile, "receive.json")
b, errOpen := ioutil.ReadFile(configFile)
if errOpen == nil && !c.GlobalBool("remember") {
var rememberedOptions croc.Options
err = json.Unmarshal(b, &rememberedOptions)
if err != nil {
log.Error(err)
return
}
// update anything that isn't explicitly set
if !c.GlobalIsSet("relay") {
crocOptions.RelayAddress = rememberedOptions.RelayAddress
}
if !c.GlobalIsSet("yes") {
crocOptions.NoPrompt = rememberedOptions.NoPrompt
}
if crocOptions.SharedSecret == "" {
crocOptions.SharedSecret = rememberedOptions.SharedSecret
}
}
if crocOptions.SharedSecret == "" {
crocOptions.SharedSecret = utils.GetInput("Enter receive code: ")
}
if c.GlobalString("out") != "" {
os.Chdir(c.GlobalString("out"))
}
cr, err := croc.New(crocOptions)
if err != nil {
return
}
// save the config
if c.GlobalBool("remember") {
log.Debug("saving config file")
var bConfig []byte
bConfig, err = json.MarshalIndent(crocOptions, "", " ")
if err != nil {
log.Error(err)
return
}
err = ioutil.WriteFile(configFile, bConfig, 0644)
if err != nil {
log.Error(err)
return
}
log.Debugf("wrote %s", configFile)
}
err = cr.Receive()
return
}
func relay(c *cli.Context) (err error) {
debugString := "info"
if c.GlobalBool("debug") {
debugString = "debug"
}
ports := strings.Split(c.String("ports"), ",")
tcpPorts := strings.Join(ports[1:], ",")
for i, port := range ports {
if i == 0 {
continue
}
go func(portStr string) {
err = tcp.Run(debugString, portStr)
if err != nil {
panic(err)
}
}(port)
}
return tcp.Run(debugString, ports[0], tcpPorts)
}

View File

@ -1,122 +0,0 @@
package comm
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"time"
"github.com/pkg/errors"
)
// Comm is some basic TCP communication
type Comm struct {
connection net.Conn
}
// NewConnection gets a new comm to a tcp address
func NewConnection(address string, timelimit ...time.Duration) (c *Comm, err error) {
tlimit := 30 * time.Second
if len(timelimit) > 0 {
tlimit = timelimit[0]
}
connection, err := net.DialTimeout("tcp", address, tlimit)
if err != nil {
return
}
c = New(connection)
return
}
// New returns a new comm
func New(c net.Conn) *Comm {
c.SetReadDeadline(time.Now().Add(3 * time.Hour))
c.SetDeadline(time.Now().Add(3 * time.Hour))
c.SetWriteDeadline(time.Now().Add(3 * time.Hour))
comm := new(Comm)
comm.connection = c
return comm
}
// Connection returns the net.Conn connection
func (c *Comm) Connection() net.Conn {
return c.connection
}
// Close closes the connection
func (c *Comm) Close() {
c.connection.Close()
}
func (c *Comm) Write(b []byte) (int, error) {
header := new(bytes.Buffer)
err := binary.Write(header, binary.LittleEndian, uint32(len(b)))
if err != nil {
fmt.Println("binary.Write failed:", err)
}
tmpCopy := append(header.Bytes(), b...)
n, err := c.connection.Write(tmpCopy)
if n != len(tmpCopy) {
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("wanted to write %d but wrote %d", len(b), n))
} else {
err = fmt.Errorf("wanted to write %d but wrote %d", len(b), n)
}
}
// log.Printf("wanted to write %d but wrote %d", n, len(b))
return n, err
}
func (c *Comm) Read() (buf []byte, numBytes int, bs []byte, err error) {
// read until we get 4 bytes for the header
var header []byte
numBytes = 4
for {
tmp := make([]byte, numBytes-len(header))
n, errRead := c.connection.Read(tmp)
if errRead != nil {
err = errRead
return
}
header = append(header, tmp[:n]...)
if numBytes == len(header) {
break
}
}
var numBytesUint32 uint32
rbuf := bytes.NewReader(header)
err = binary.Read(rbuf, binary.LittleEndian, &numBytesUint32)
if err != nil {
fmt.Println("binary.Read failed:", err)
}
numBytes = int(numBytesUint32)
buf = make([]byte, 0)
for {
// log.Debugf("bytes: %d/%d",len(buf),numBytes)
tmp := make([]byte, numBytes-len(buf))
n, errRead := c.connection.Read(tmp)
if errRead != nil {
err = errRead
return
}
buf = append(buf, tmp[:n]...)
if numBytes == len(buf) {
break
}
}
return
}
// Send a message
func (c *Comm) Send(message []byte) (err error) {
_, err = c.Write(message)
return
}
// Receive a message
func (c *Comm) Receive() (b []byte, err error) {
b, _, _, err = c.Read()
return
}

View File

@ -1,65 +0,0 @@
package comm
import (
"crypto/rand"
"net"
"testing"
"time"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
func TestComm(t *testing.T) {
token := make([]byte, 40000000)
rand.Read(token)
port := "8001"
go func() {
log.Debugf("starting TCP server on " + port)
server, err := net.Listen("tcp", "0.0.0.0:"+port)
if err != nil {
log.Error(err)
}
defer server.Close()
// spawn a new goroutine whenever a client connects
for {
connection, err := server.Accept()
if err != nil {
log.Error(err)
}
log.Debugf("client %s connected", connection.RemoteAddr().String())
go func(port string, connection net.Conn) {
c := New(connection)
err = c.Send([]byte("hello, world"))
assert.Nil(t, err)
data, err := c.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte("hello, computer"), data)
data, err = c.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte{'\x00'}, data)
data, err = c.Receive()
assert.Nil(t, err)
assert.Equal(t, token, data)
}(port, connection)
}
}()
time.Sleep(300 * time.Millisecond)
a, err := NewConnection("localhost:"+port, 10*time.Minute)
assert.Nil(t, err)
data, err := a.Receive()
assert.Equal(t, []byte("hello, world"), data)
assert.Nil(t, err)
assert.Nil(t, a.Send([]byte("hello, computer")))
assert.Nil(t, a.Send([]byte{'\x00'}))
assert.Nil(t, a.Send(token))
_ = a.Connection()
a.Close()
assert.NotNil(t, a.Send(token))
_, err = a.Write(token)
assert.NotNil(t, err)
}

View File

@ -1,43 +0,0 @@
package compress
import (
"bytes"
"compress/flate"
"io"
)
// CompressWithOption returns compressed data using the specified level
func CompressWithOption(src []byte, level int) []byte {
compressedData := new(bytes.Buffer)
compress(src, compressedData, level)
return compressedData.Bytes()
}
// Compress returns a compressed byte slice.
func Compress(src []byte) []byte {
compressedData := new(bytes.Buffer)
compress(src, compressedData, -2)
return compressedData.Bytes()
}
// Decompress returns a decompressed byte slice.
func Decompress(src []byte) []byte {
compressedData := bytes.NewBuffer(src)
deCompressedData := new(bytes.Buffer)
decompress(compressedData, deCompressedData)
return deCompressedData.Bytes()
}
// compress uses flate to compress a byte slice to a corresponding level
func compress(src []byte, dest io.Writer, level int) {
compressor, _ := flate.NewWriter(dest, level)
compressor.Write(src)
compressor.Close()
}
// compress uses flate to decompress an io.Reader
func decompress(src io.Reader, dest io.Writer) {
decompressor := flate.NewReader(src)
io.Copy(dest, decompressor)
decompressor.Close()
}

View File

@ -1,97 +0,0 @@
package compress
import (
"crypto/rand"
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
var fable = []byte(`The Frog and the Crocodile
Once, there was a frog who lived in the middle of a swamp. His entire family had lived in that swamp for generations, but this particular frog decided that he had had quite enough wetness to last him a lifetime. He decided that he was going to find a dry place to live instead.
The only thing that separated him from dry land was a swampy, muddy, swiftly flowing river. But the river was home to all sorts of slippery, slittering snakes that loved nothing better than a good, plump frog for dinner, so Frog didn't dare try to swim across.
So for many days, the frog stayed put, hopping along the bank, trying to think of a way to get across.
The snakes hissed and jeered at him, daring him to come closer, but he refused. Occasionally they would slither closer, jaws open to attack, but the frog always leaped out of the way. But no matter how far upstream he searched or how far downstream, the frog wasn't able to find a way across the water.
He had felt certain that there would be a bridge, or a place where the banks came together, yet all he found was more reeds and water. After a while, even the snakes stopped teasing him and went off in search of easier prey.
The frog sighed in frustration and sat to sulk in the rushes. Suddenly, he spotted two big eyes staring at him from the water. The giant log-shaped animal opened its mouth and asked him, "What are you doing, Frog? Surely there are enough flies right there for a meal."
The frog croaked in surprise and leaped away from the crocodile. That creature could swallow him whole in a moment without thinking about it! Once he was a satisfied that he was a safe distance away, he answered. "I'm tired of living in swampy waters, and I want to travel to the other side of the river. But if I swim across, the snakes will eat me."
The crocodile harrumphed in agreement and sat, thinking, for a while. "Well, if you're afraid of the snakes, I could give you a ride across," he suggested.
"Oh no, I don't think so," Frog answered quickly. "You'd eat me on the way over, or go underwater so the snakes could get me!"
"Now why would I let the snakes get you? I think they're a terrible nuisance with all their hissing and slithering! The river would be much better off without them altogether! Anyway, if you're so worried that I might eat you, you can ride on my tail."
The frog considered his offer. He did want to get to dry ground very badly, and there didn't seem to be any other way across the river. He looked at the crocodile from his short, squat buggy eyes and wondered about the crocodile's motives. But if he rode on the tail, the croc couldn't eat him anyway. And he was right about the snakes--no self-respecting crocodile would give a meal to the snakes.
"Okay, it sounds like a good plan to me. Turn around so I can hop on your tail."
The crocodile flopped his tail into the marshy mud and let the frog climb on, then he waddled out to the river. But he couldn't stick his tail into the water as a rudder because the frog was on it -- and if he put his tail in the water, the snakes would eat the frog. They clumsily floated downstream for a ways, until the crocodile said, "Hop onto my back so I can steer straight with my tail." The frog moved, and the journey smoothed out.
From where he was sitting, the frog couldn't see much except the back of Crocodile's head. "Why don't you hop up on my head so you can see everything around us?" Crocodile invited. `)
func BenchmarkCompressLevelMinusTwo(b *testing.B) {
for i := 0; i < b.N; i++ {
CompressWithOption(fable, -2)
}
}
func BenchmarkCompressLevelNine(b *testing.B) {
for i := 0; i < b.N; i++ {
CompressWithOption(fable, 9)
}
}
func BenchmarkCompressLevelMinusTwoBinary(b *testing.B) {
data := make([]byte, 1000000)
rand.Read(data)
for i := 0; i < b.N; i++ {
CompressWithOption(data, -2)
}
}
func BenchmarkCompressLevelNineBinary(b *testing.B) {
data := make([]byte, 1000000)
rand.Read(data)
for i := 0; i < b.N; i++ {
CompressWithOption(data, 9)
}
}
func TestCompress(t *testing.T) {
compressedB := CompressWithOption(fable, 9)
dataRateSavings := 100 * (1.0 - float64(len(compressedB))/float64(len(fable)))
fmt.Printf("Level 9: %2.0f%% percent space savings\n", dataRateSavings)
assert.True(t, len(compressedB) < len(fable))
assert.Equal(t, fable, Decompress(compressedB))
compressedB = CompressWithOption(fable, -2)
dataRateSavings = 100 * (1.0 - float64(len(compressedB))/float64(len(fable)))
fmt.Printf("Level -2: %2.0f%% percent space savings\n", dataRateSavings)
assert.True(t, len(compressedB) < len(fable))
compressedB = Compress(fable)
dataRateSavings = 100 * (1.0 - float64(len(compressedB))/float64(len(fable)))
fmt.Printf("Level -2: %2.0f%% percent space savings\n", dataRateSavings)
assert.True(t, len(compressedB) < len(fable))
data := make([]byte, 4096)
rand.Read(data)
compressedB = CompressWithOption(data, -2)
dataRateSavings = 100 * (1.0 - float64(len(compressedB))/float64(len(data)))
fmt.Printf("random, Level -2: %2.0f%% percent space savings\n", dataRateSavings)
rand.Read(data)
compressedB = CompressWithOption(data, 9)
dataRateSavings = 100 * (1.0 - float64(len(compressedB))/float64(len(data)))
fmt.Printf("random, Level 9: %2.0f%% percent space savings\n", dataRateSavings)
}

File diff suppressed because it is too large Load Diff

View File

@ -1,161 +0,0 @@
package croc
import (
"io/ioutil"
"os"
"sync"
"testing"
"time"
"github.com/schollz/croc/v6/src/tcp"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
func TestCroc(t *testing.T) {
log.SetLevel("trace")
defer os.Remove("README.md")
go tcp.Run("debug", "8081", "8082,8083,8084,8085")
go tcp.Run("debug", "8082")
go tcp.Run("debug", "8083")
go tcp.Run("debug", "8084")
go tcp.Run("debug", "8085")
time.Sleep(300 * time.Millisecond)
log.Debug("setting up sender")
sender, err := New(Options{
IsSender: true,
SharedSecret: "test",
Debug: true,
RelayAddress: "localhost:8081",
RelayPorts: []string{"8081"},
Stdout: false,
NoPrompt: true,
DisableLocal: true,
})
if err != nil {
panic(err)
}
log.Debug("setting up receiver")
receiver, err := New(Options{
IsSender: false,
SharedSecret: "test",
Debug: true,
RelayAddress: "localhost:8081",
Stdout: false,
NoPrompt: true,
DisableLocal: true,
})
if err != nil {
panic(err)
}
var wg sync.WaitGroup
wg.Add(2)
go func() {
sender.Send(TransferOptions{
PathToFiles: []string{"../../README.md"},
})
wg.Done()
}()
time.Sleep(100 * time.Millisecond)
go func() {
receiver.Receive()
wg.Done()
}()
wg.Wait()
}
func TestCrocLocal(t *testing.T) {
log.SetLevel("trace")
defer os.Remove("LICENSE")
defer os.Remove("touched")
time.Sleep(300 * time.Millisecond)
log.Debug("setting up sender")
sender, err := New(Options{
IsSender: true,
SharedSecret: "test",
Debug: true,
RelayAddress: "localhost:8181",
RelayPorts: []string{"8181", "8182"},
Stdout: true,
NoPrompt: true,
DisableLocal: false,
})
if err != nil {
panic(err)
}
time.Sleep(1 * time.Second)
log.Debug("setting up receiver")
receiver, err := New(Options{
IsSender: false,
SharedSecret: "test",
Debug: true,
RelayAddress: "localhost:8181",
Stdout: true,
NoPrompt: true,
DisableLocal: false,
})
if err != nil {
panic(err)
}
var wg sync.WaitGroup
os.Create("touched")
wg.Add(2)
go func() {
sender.Send(TransferOptions{
PathToFiles: []string{"../../LICENSE", "touched"},
KeepPathInRemote: false,
})
wg.Done()
}()
time.Sleep(100 * time.Millisecond)
go func() {
receiver.Receive()
wg.Done()
}()
wg.Wait()
}
func TestCrocError(t *testing.T) {
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
panic(err)
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil {
panic(err)
}
if err := tmpfile.Close(); err != nil {
panic(err)
}
Debug(false)
log.SetLevel("warn")
sender, _ := New(Options{
IsSender: true,
SharedSecret: "test33",
Debug: true,
RelayAddress: "doesntexistok.com:8381",
RelayPorts: []string{"8381", "8382"},
Stdout: true,
NoPrompt: true,
DisableLocal: true,
})
err = sender.Send(TransferOptions{
PathToFiles: []string{tmpfile.Name()},
KeepPathInRemote: true,
})
log.Debug(err)
assert.NotNil(t, err)
}

View File

@ -1,86 +0,0 @@
package crypt
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"golang.org/x/crypto/pbkdf2"
)
// Encryption is the basic type for storing
// the key, passphrase and salt
type Encryption struct {
key []byte
passphrase []byte
salt []byte
}
// New generates a new Encryption, using the supplied passphrase and
// an optional supplied salt.
// Passing nil passphrase will not use decryption.
func New(passphrase []byte, salt []byte) (e Encryption, err error) {
if passphrase == nil {
e = Encryption{nil, nil, nil}
return
}
e.passphrase = passphrase
if salt == nil {
e.salt = make([]byte, 8)
// http://www.ietf.org/rfc/rfc2898.txt
// Salt.
rand.Read(e.salt)
} else {
e.salt = salt
}
e.key = pbkdf2.Key([]byte(passphrase), e.salt, 100, 32, sha256.New)
return
}
// Salt returns the salt bytes
func (e Encryption) Salt() []byte {
return e.salt
}
// Encrypt will generate an Encryption, prefixed with the IV
func (e Encryption) Encrypt(plaintext []byte) (encrypted []byte, err error) {
if e.passphrase == nil {
encrypted = plaintext
return
}
// generate a random iv each time
// http://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
// Section 8.2
ivBytes := make([]byte, 12)
rand.Read(ivBytes)
b, err := aes.NewCipher(e.key)
if err != nil {
return
}
aesgcm, err := cipher.NewGCM(b)
if err != nil {
return
}
encrypted = aesgcm.Seal(nil, ivBytes, plaintext, nil)
encrypted = append(ivBytes, encrypted...)
return
}
// Decrypt an Encryption
func (e Encryption) Decrypt(encrypted []byte) (plaintext []byte, err error) {
if e.passphrase == nil {
plaintext = encrypted
return
}
b, err := aes.NewCipher(e.key)
if err != nil {
return
}
aesgcm, err := cipher.NewGCM(b)
if err != nil {
return
}
plaintext, err = aesgcm.Open(nil, encrypted[:12], encrypted[12:], nil)
return
}

View File

@ -1,60 +0,0 @@
package crypt
import (
"testing"
"github.com/stretchr/testify/assert"
)
func BenchmarkEncryptionNew(b *testing.B) {
for i := 0; i < b.N; i++ {
bob, _ := New([]byte("password"), nil)
bob.Encrypt([]byte("hello, world"))
}
}
func BenchmarkEncryption(b *testing.B) {
bob, _ := New([]byte("password"), nil)
for i := 0; i < b.N; i++ {
bob.Encrypt([]byte("hello, world"))
}
}
func TestEncryption(t *testing.T) {
bob, err := New([]byte("password"), nil)
assert.Nil(t, err)
jane, err := New([]byte("password"), bob.Salt())
assert.Nil(t, err)
enc, err := bob.Encrypt([]byte("hello, world"))
assert.Nil(t, err)
dec, err := jane.Decrypt(enc)
assert.Nil(t, err)
assert.Equal(t, dec, []byte("hello, world"))
jane2, err := New([]byte("password"), nil)
assert.Nil(t, err)
dec, err = jane2.Decrypt(enc)
assert.NotNil(t, err)
assert.NotEqual(t, dec, []byte("hello, world"))
jane3, err := New([]byte("passwordwrong"), bob.Salt())
assert.Nil(t, err)
dec, err = jane3.Decrypt(enc)
assert.NotNil(t, err)
assert.NotEqual(t, dec, []byte("hello, world"))
}
func TestNoEncryption(t *testing.T) {
bob, err := New(nil, nil)
assert.Nil(t, err)
jane, err := New(nil, nil)
assert.Nil(t, err)
enc, err := bob.Encrypt([]byte("hello, world"))
assert.Nil(t, err)
dec, err := jane.Decrypt(enc)
assert.Nil(t, err)
assert.Equal(t, dec, []byte("hello, world"))
assert.Equal(t, enc, []byte("hello, world"))
}

View File

@ -1,673 +0,0 @@
#!/bin/bash -
#===============================================================================
#
# FILE: default.txt
#
# USAGE: curl https://getcroc.schollz.com | bash
# OR
# wget -qO- https://getcroc.schollz.com | bash
#
# DESCRIPTION: croc Installer Script.
#
# This script installs croc into a specified prefix.
# Default prefix = /usr/local/bin
#
# OPTIONS: -p, --prefix "${INSTALL_PREFIX}"
# Prefix to install croc into. Defaults to /usr/local/bin
# REQUIREMENTS: bash, uname, tar/unzip, curl/wget, sudo (if not run
# as root), install, mktemp, sha256sum/shasum/sha256
#
# BUGS: ...hopefully not. Please report.
#
# NOTES: Homepage: https://schollz.com/software/croc
# Issues: https://github.com/schollz/croc/issues
#
# CREATED: 08/10/2019 16:41
# REVISION: 0.9.1
#===============================================================================
set -o nounset # Treat unset variables as an error
#-------------------------------------------------------------------------------
# DEFAULTS
#-------------------------------------------------------------------------------
PREFIX="${PREFIX:-}"
ANDROID_ROOT="${ANDROID_ROOT:-}"
# Termux on Android has ${PREFIX} set which already ends with '/usr'
if [[ -n "${ANDROID_ROOT}" && -n "${PREFIX}" ]]; then
INSTALL_PREFIX="${PREFIX}/bin"
else
INSTALL_PREFIX="/usr/local/bin"
fi
#-------------------------------------------------------------------------------
# FUNCTIONS
#-------------------------------------------------------------------------------
#--- FUNCTION ----------------------------------------------------------------
# NAME: print_banner
# DESCRIPTION: Prints a banner
# PARAMETERS: none
# RETURNS: 0
#-------------------------------------------------------------------------------
print_banner() {
cat <<-'EOF'
=================================================
____
/ ___|_ __ ___ ___
| | | '__/ _ \ / __|
| |___| | | (_) | (__
\____|_| \___/ \___|
___ _ _ _
|_ _|_ __ ___| |_ __ _| | | ___ _ __
| || '_ \/ __| __/ _` | | |/ _ \ '__|
| || | | \__ \ || (_| | | | __/ |
|___|_| |_|___/\__\__,_|_|_|\___|_|
==================================================
EOF
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: print_help
# DESCRIPTION: Prints out a help message
# PARAMETERS: none
# RETURNS: 0
#-------------------------------------------------------------------------------
print_help() {
local help_header
local help_message
help_header="croc Installer Script"
help_message="Usage:
-p INSTALL_PREFIX
Prefix to install croc into. Directory must already exist.
Default = /usr/local/bin ('\${PREFIX}/bin' on Termux for Android)
-h
Prints this helpfull message and exit."
echo "${help_header}"
echo ""
echo "${help_message}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: print_message
# DESCRIPTION: Prints a message all fancy like
# PARAMETERS: $1 = Message to print
# $2 = Severity. info, ok, error, warn
# RETURNS: Formatted Message to stdout
#-------------------------------------------------------------------------------
print_message() {
local message
local severity
local red
local green
local yellow
local nc
message="${1}"
severity="${2}"
red='\e[0;31m'
green='\e[0;32m'
yellow='\e[1;33m'
nc='\e[0m'
case "${severity}" in
"info" ) echo -e "${nc}${message}${nc}";;
"ok" ) echo -e "${green}${message}${nc}";;
"error" ) echo -e "${red}${message}${nc}";;
"warn" ) echo -e "${yellow}${message}${nc}";;
esac
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: make_tempdir
# DESCRIPTION: Makes a temp dir using mktemp if available
# PARAMETERS: $1 = Directory template
# RETURNS: 0 = Created temp dir. Also prints temp file path to stdout
# 1 = Failed to create temp dir
# 20 = Failed to find mktemp
#-------------------------------------------------------------------------------
make_tempdir() {
local template
local tempdir
local tempdir_rcode
template="${1}.XXXXXX"
if command -v mktemp >/dev/null 2>&1; then
tempdir="$(mktemp -d -t "${template}")"
tempdir_rcode="${?}"
if [[ "${tempdir_rcode}" == "0" ]]; then
echo "${tempdir}"
return 0
else
return 1
fi
else
return 20
fi
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: determine_os
# DESCRIPTION: Attempts to determin host os using uname
# PARAMETERS: none
# RETURNS: 0 = OS Detected. Also prints detected os to stdout
# 1 = Unkown OS
# 20 = 'uname' not found in path
#-------------------------------------------------------------------------------
determine_os() {
local uname_out
if command -v uname >/dev/null 2>&1; then
uname_out="$(uname)"
if [[ "${uname_out}" == "" ]]; then
return 1
else
echo "${uname_out}"
return 0
fi
else
return 20
fi
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: determine_arch
# DESCRIPTION: Attempt to determin architecture of host
# PARAMETERS: none
# RETURNS: 0 = Arch Detected. Also prints detected arch to stdout
# 1 = Unkown arch
# 20 = 'uname' not found in path
#-------------------------------------------------------------------------------
determine_arch() {
local uname_out
if command -v uname >/dev/null 2>&1; then
uname_out="$(uname -m)"
if [[ "${uname_out}" == "" ]]; then
return 1
else
echo "${uname_out}"
return 0
fi
else
return 20
fi
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: download_file
# DESCRIPTION: Downloads a file into the specified directory. Attempts to
# use curl, then wget. If neither is found, fail.
# PARAMETERS: $1 = url of file to download
# $2 = location to download file into on host system
# RETURNS: If curl or wget found, returns the return code of curl or wget
# 20 = Could not find curl and wget
#-------------------------------------------------------------------------------
download_file() {
local url
local dir
local filename
local rcode
url="${1}"
dir="${2}"
filename="${3}"
if command -v curl >/dev/null 2>&1; then
curl -fsSL "${url}" -o "${dir}/${filename}"
rcode="${?}"
elif command -v wget >/dev/null 2>&1; then
wget --quiet "${url}" -O "${dir}/${filename}"
rcode="${?}"
else
rcode="20"
fi
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: checksum_check
# DESCRIPTION: Attempt to verify checksum of downloaded file to ensure
# integrity. Tries multiple tools before faling.
# PARAMETERS: $1 = path to checksum file
# $2 = location of file to check
# $3 = working directory
# RETURNS: 0 = checkusm verified
# 1 = checksum verification failed
# 20 = failed to determine tool to use to check checksum
# 30 = failed to change into or go back from working dir
#-------------------------------------------------------------------------------
checksum_check() {
local checksum_file
local file
local dir
local rcode
local shasum_1
local shasum_2
local shasum_c
checksum_file="${1}"
file="${2}"
dir="${3}"
cd "${dir}" || return 30
if command -v sha256sum >/dev/null 2>&1; then
## Not all sha256sum versions seem to have --ignore-missing, so filter the checksum file
## to only include the file we downloaded.
grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt
shasum_c="$(sha256sum -c "filtered_checksum.txt")"
rcode="${?}"
elif command -v shasum >/dev/null 2>&1; then
## With shasum on FreeBSD, we don't get to --ignore-missing, so filter the checksum file
## to only include the file we downloaded.
grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt
shasum_c="$(shasum -a 256 -c "filtered_checksum.txt")"
rcode="${?}"
elif command -v sha256 >/dev/null 2>&1; then
## With sha256 on FreeBSD, we don't get to --ignore-missing, so filter the checksum file
## to only include the file we downloaded.
## Also sha256 -c option seems to fail, so fall back to an if statement
grep "$(basename "${file}")" "${checksum_file}" > filtered_checksum.txt
shasum_1="$(sha256 -q "${file}")"
shasum_2="$(awk '{print $1}' filtered_checksum.txt)"
if [[ "${shasum_1}" == "${shasum_2}" ]]; then
rcode="0"
else
rcode="1"
fi
shasum_c="Expected: ${shasum_1}, Got: ${shasum_2}"
else
return 20
fi
cd - >/dev/null 2>&1 || return 30
if [[ "${rcode}" -gt "0" ]]; then
echo "${shasum_c}"
fi
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: extract_file
# DESCRIPTION: Extracts a file into a location. Attempts to determine which
# tool to use by checking file extention.
# PARAMETERS: $1 = file to extract
# $2 = location to extract file into
# $3 = extention
# RETURNS: Return code of the tool used to extract the file
# 20 = Failed to determine which tool to use
# 30 = Failed to find tool in path
#-------------------------------------------------------------------------------
extract_file() {
local file
local dir
local ext
local rcode
file="${1}"
dir="${2}"
ext="${3}"
case "${ext}" in
"zip" ) if command -v unzip >/dev/null 2>&1; then
unzip "${file}" -d "${dir}"
rcode="${?}"
else
rcode="30"
fi
;;
"tar.gz" ) if command -v tar >/dev/null 2>&1; then
tar -xf "${file}" -C "${dir}"
rcode="${?}"
else
rcode="31"
fi
;;
* ) rcode="20";;
esac
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: install_file_freebsd
# DESCRIPTION: Installs a file into a location using 'install'. If EUID not
# 0, then attempt to use sudo.
# PARAMETERS: $1 = file to install
# $2 = location to install file into
# RETURNS: 0 = File Installed
# 1 = File not installed
# 20 = Could not find install command
# 21 = Could not find sudo command
#-------------------------------------------------------------------------------
install_file_freebsd() {
local file
local prefix
local rcode
file="${1}"
prefix="${2}"
if command -v install >/dev/null 2>&1; then
if [[ "${EUID}" == "0" ]]; then
install -C -b -B '_old' -m 755 "${file}" "${prefix}"
rcode="${?}"
else
if command -v sudo >/dev/null 2>&1; then
sudo install -C -b -B '_old' -m 755 "${file}" "${prefix}"
rcode="${?}"
else
rcode="21"
fi
fi
else
rcode="20"
fi
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: install_file_linux
# DESCRIPTION: Installs a file into a location using 'install'. If EUID not
# 0, then attempt to use sudo (unless on android).
# PARAMETERS: $1 = file to install
# $2 = location to install file into
# RETURNS: 0 = File Installed
# 1 = File not installed
# 20 = Could not find install command
# 21 = Could not find sudo command
#-------------------------------------------------------------------------------
install_file_linux() {
local file
local prefix
local rcode
file="${1}"
prefix="${2}"
if command -v install >/dev/null 2>&1; then
if [[ "${EUID}" == "0" ]]; then
install -C -b -S '_old' -m 755 -t "${prefix}" "${file}"
rcode="${?}"
else
if command -v sudo >/dev/null 2>&1; then
sudo install -C -b -S '_old' -m 755 "${file}" "${prefix}"
rcode="${?}"
elif [[ "${ANDROID_ROOT}" != "" ]]; then
install -C -b -S '_old' -m 755 -t "${prefix}" "${file}"
rcode="${?}"
else
rcode="21"
fi
fi
else
rcode="20"
fi
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: install_file_cygwin
# DESCRIPTION: Installs a file into a location using 'install'. If EUID not
# 0, then attempt to use sudo.
# Not really 100% sure this is how to install croc in cygwin.
# PARAMETERS: $1 = file to install
# $2 = location to install file into
# RETURNS: 0 = File Installed
# 20 = Could not find install command
# 21 = Could not find sudo command
#-------------------------------------------------------------------------------
install_file_cygwin() {
local file
local prefix
local rcode
file="${1}"
prefix="${2}"
if command -v install >/dev/null 2>&1; then
if [[ "${EUID}" == "0" ]]; then
install -m 755 "${prefix}" "${file}"
rcode="${?}"
else
if command -v sudo >/dev/null 2>&1; then
sudo install -m 755 "${file}" "${prefix}"
rcode="${?}"
else
rcode="21"
fi
fi
else
rcode="20"
fi
return "${rcode}"
}
#--- FUNCTION ----------------------------------------------------------------
# NAME: main
# DESCRIPTION: Put it all together in a logical way
# ...at least that is the hope...
# PARAMETERS: 1 = prefix
# RETURNS: 0 = All good
# 1 = Something done broke
#-------------------------------------------------------------------------------
main() {
local prefix
local tmpdir
local tmpdir_rcode
local croc_arch
local croc_arch_rcode
local croc_os
local croc_os_rcode
local croc_base_url
local croc_url
local croc_file
local croc_checksum_file
local croc_bin_name
local croc_version
local croc_dl_ext
local download_file_rcode
local download_checksum_file_rcode
local checksum_check_rcode
local extract_file_rcode
local install_file_rcode
croc_bin_name="croc"
croc_version="6.2.0"
croc_dl_ext="tar.gz"
croc_base_url="https://github.com/schollz/croc/releases/download"
prefix="${1}"
print_banner
print_message "== Install prefix set to ${prefix}" "info"
tmpdir="$(make_tempdir "${croc_bin_name}")"
tmpdir_rcode="${?}"
if [[ "${tmpdir_rcode}" == "0" ]]; then
print_message "== Created temp dir at ${tmpdir}" "info"
elif [[ "${tmpdir_rcode}" == "1" ]]; then
print_message "== Failed to create temp dir at ${tmpdir}" "error"
else
print_message "== 'mktemp' not found in path. Is it installed?" "error"
exit 1
fi
croc_arch="$(determine_arch)"
croc_arch_rcode="${?}"
if [[ "${croc_arch_rcode}" == "0" ]]; then
print_message "== Architecture detected as ${croc_arch}" "info"
elif [[ "${croc_arch_rcode}" == "1" ]]; then
print_message "== Architecture not detected" "error"
exit 1
else
print_message "== 'uname' not found in path. Is it installed?" "error"
exit 1
fi
croc_os="$(determine_os)"
croc_os_rcode="${?}"
if [[ "${croc_os_rcode}" == "0" ]]; then
print_message "== OS detected as ${croc_os}" "info"
elif [[ "${croc_os_rcode}" == "1" ]]; then
print_message "== OS not detected" "error"
exit 1
else
print_message "== 'uname' not found in path. Is it installed?" "error"
exit 1
fi
case "${croc_os}" in
"Darwin" ) croc_os="macOS";;
"CYGWIN"* ) croc_os="Windows";
croc_dl_ext="zip";
print_message "== Cygwin is currently unsupported." "error";
exit 1;;
esac
case "${croc_arch}" in
"x86_64" ) croc_arch="64bit";;
"amd64" ) croc_arch="64bit";;
"aarch64" ) croc_arch="ARM64";;
"armv7l" ) croc_arch="ARM";;
"i686" ) croc_arch="32bit";;
* ) croc_arch="unknown";;
esac
croc_file="${croc_bin_name}_${croc_version}_${croc_os}-${croc_arch}.${croc_dl_ext}"
croc_checksum_file="${croc_bin_name}_${croc_version}_checksums.txt"
croc_url="${croc_base_url}/v${croc_version}/${croc_file}"
croc_checksum_url="${croc_base_url}/v${croc_version}/${croc_checksum_file}"
download_file "${croc_url}" "${tmpdir}" "${croc_file}"
download_file_rcode="${?}"
if [[ "${download_file_rcode}" == "0" ]]; then
print_message "== Downloaded croc archive into ${tmpdir}" "info"
elif [[ "${download_file_rcode}" == "1" ]]; then
print_message "== Failed to download croc archive" "error"
exit 1
elif [[ "${download_file_rcode}" == "20" ]]; then
print_message "== Failed to locate curl or wget" "error"
exit 1
else
print_message "== Return code of download tool returned an unexpected value of ${download_file_rcode}" "error"
exit 1
fi
download_file "${croc_checksum_url}" "${tmpdir}" "${croc_checksum_file}"
download_checksum_file_rcode="${?}"
if [[ "${download_checksum_file_rcode}" == "0" ]]; then
print_message "== Downloaded croc checksums file into ${tmpdir}" "info"
elif [[ "${download_checksum_file_rcode}" == "1" ]]; then
print_message "== Failed to download croc checksums" "error"
exit 1
elif [[ "${download_checksum_file_rcode}" == "20" ]]; then
print_message "== Failed to locate curl or wget" "error"
exit 1
else
print_message "== Return code of download tool returned an unexpected value of ${download_checksum_file_rcode}" "error"
exit 1
fi
checksum_check "${tmpdir}/${croc_checksum_file}" "${tmpdir}/${croc_file}" "${tmpdir}"
checksum_check_rcode="${?}"
if [[ "${checksum_check_rcode}" == "0" ]]; then
print_message "== Checksum of ${tmpdir}/${croc_file} verified" "ok"
elif [[ "${checksum_check_rcode}" == "1" ]]; then
print_message "== Failed to verify checksum of ${tmpdir}/${croc_file}" "error"
exit 1
elif [[ "${checksum_check_rcode}" == "20" ]]; then
print_message "== Failed to find tool to verify sha256 sums" "error"
exit 1
elif [[ "${checksum_check_rcode}" == "30" ]]; then
print_message "== Failed to change into working directory ${tmpdir}" "error"
exit 1
else
print_message "== Unknown return code returned while checking checksum of ${tmpdir}/${croc_file}. Returned ${checksum_check_rcode}" "error"
exit 1
fi
extract_file "${tmpdir}/${croc_file}" "${tmpdir}/" "${croc_dl_ext}"
extract_file_rcode="${?}"
if [[ "${extract_file_rcode}" == "0" ]]; then
print_message "== Extracted ${croc_file} to ${tmpdir}/" "info"
elif [[ "${extract_file_rcode}" == "1" ]]; then
print_message "== Failed to extract ${croc_file}" "error"
exit 1
elif [[ "${extract_file_rcode}" == "20" ]]; then
print_message "== Failed to determine which extraction tool to use" "error"
exit 1
elif [[ "${extract_file_rcode}" == "30" ]]; then
print_message "== Failed to find 'unzip' in path" "error"
exit 1
elif [[ "${extract_file_rcode}" == "31" ]]; then
print_message "== Failed to find 'tar' in path" "error"
exit 1
else
print_message "== Unknown error returned from extraction attempt" "error"
exit 1
fi
case "${croc_os}" in
"Linux" ) install_file_linux "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;
"FreeBSD" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;
"macOS" ) install_file_freebsd "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;
"Windows" ) install_file_cygwin "${tmpdir}/${croc_bin_name}" "${prefix}/";
install_file_rcode="${?}";;
esac
if [[ "${install_file_rcode}" == "0" ]]; then
print_message "== Installed ${croc_bin_name} to ${prefix}/" "ok"
elif [[ "${install_file_rcode}" == "1" ]]; then
print_message "== Failed to install ${croc_bin_name}" "error"
exit 1
elif [[ "${install_file_rcode}" == "20" ]]; then
print_message "== Failed to locate 'install' command" "error"
exit 1
elif [[ "${install_file_rcode}" == "21" ]]; then
print_message "== Failed to locate 'sudo' command" "error"
exit 1
else
print_message "== Install attempt returned an unexpected value of ${install_file_rcode}" "error"
exit 1
fi
print_message "== Installation complete" "ok"
exit 0
}
#-------------------------------------------------------------------------------
# ARGUMENT PARSING
#-------------------------------------------------------------------------------
OPTS="hp:"
while getopts "${OPTS}" optchar; do
case "${optchar}" in
'h' ) print_help
exit 0
;;
'p' ) INSTALL_PREFIX="${OPTARG}"
;;
/? ) print_message "Unknown option ${OPTARG}" "warn"
;;
esac
done
#-------------------------------------------------------------------------------
# CALL MAIN
#-------------------------------------------------------------------------------
main "${INSTALL_PREFIX}"

View File

@ -1,81 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"os/exec"
"strings"
)
func main() {
err := run()
if err != nil {
fmt.Println(err)
}
}
func run() (err error) {
version, err := exec.Command("git", "describe", "--abbrev=0").Output()
if err != nil {
return
}
versionNew := strings.TrimSpace(string(version))
versionHash, err := exec.Command("git", "rev-parse", "--short", "HEAD").Output()
if err != nil {
return
}
versionHashNew := strings.TrimSpace(string(versionHash))
fmt.Println(versionNew)
fmt.Println(versionHashNew)
err = replaceInFile("src/cli/cli.go", `Version = "`, `"`, versionNew+"-"+versionHashNew)
if err == nil {
fmt.Printf("updated cli.go to version %s\n", versionNew)
}
err = replaceInFile("README.md", `version-`, `-b`, strings.Split(versionNew, "-")[0])
if err == nil {
fmt.Printf("updated README to version %s\n", strings.Split(versionNew, "-")[0])
}
err = replaceInFile("src/install/default.txt", `croc_version="`, `"`, strings.Split(versionNew, "-")[0][1:])
if err == nil {
fmt.Printf("updated default.txt to version %s\n", strings.Split(versionNew, "-")[0][1:])
}
return
}
func replaceInFile(fname, start, end, replacement string) (err error) {
b, err := ioutil.ReadFile(fname)
if err != nil {
return
}
oldVersion := GetStringInBetween(string(b), start, end)
if oldVersion == "" {
err = fmt.Errorf("nothing")
return
}
newF := strings.Replace(
string(b),
fmt.Sprintf("%s%s%s", start, oldVersion, end),
fmt.Sprintf("%s%s%s", start, replacement, end),
1,
)
err = ioutil.WriteFile(fname, []byte(newF), 0644)
return
}
// GetStringInBetween Returns empty string if no start string found
func GetStringInBetween(str string, start string, end string) (result string) {
s := strings.Index(str, start)
if s == -1 {
return
}
s += len(start)
e := strings.Index(str[s:], end)
if e == -1 {
return
}
e += s
return str[s:e]
}

View File

@ -1,56 +0,0 @@
package message
import (
"encoding/json"
"github.com/schollz/croc/v6/src/comm"
"github.com/schollz/croc/v6/src/compress"
"github.com/schollz/croc/v6/src/crypt"
log "github.com/schollz/logger"
)
// Message is the possible payload for messaging
type Message struct {
Type string `json:"t,omitempty"`
Message string `json:"m,omitempty"`
Bytes []byte `json:"b,omitempty"`
Num int `json:"n,omitempty"`
}
func (m Message) String() string {
b, _ := json.Marshal(m)
return string(b)
}
// Send will send out
func Send(c *comm.Comm, key crypt.Encryption, m Message) (err error) {
mSend, err := Encode(key, m)
if err != nil {
return
}
log.Debugf("writing %s message (%d bytes)", m.Type, len(mSend))
_, err = c.Write(mSend)
return
}
// Encode will convert to bytes
func Encode(key crypt.Encryption, m Message) (b []byte, err error) {
b, err = json.Marshal(m)
if err != nil {
return
}
b = compress.Compress(b)
b, err = key.Encrypt(b)
return
}
// Decode will convert from bytes
func Decode(key crypt.Encryption, b []byte) (m Message, err error) {
b, err = key.Decrypt(b)
if err != nil {
return
}
b = compress.Decompress(b)
err = json.Unmarshal(b, &m)
return
}

View File

@ -1,75 +0,0 @@
package message
import (
"crypto/rand"
"fmt"
"net"
"testing"
"time"
"github.com/schollz/croc/v6/src/comm"
"github.com/schollz/croc/v6/src/crypt"
log "github.com/schollz/logger"
"github.com/stretchr/testify/assert"
)
func TestMessage(t *testing.T) {
m := Message{Type: "message", Message: "hello, world"}
e, err := crypt.New(nil, nil)
assert.Nil(t, err)
fmt.Println(e.Salt())
b, err := Encode(e, m)
assert.Nil(t, err)
fmt.Printf("%x\n", b)
m2, err := Decode(e, b)
assert.Nil(t, err)
assert.Equal(t, m, m2)
assert.Equal(t, `{"t":"message","m":"hello, world"}`, m.String())
}
func TestSend(t *testing.T) {
token := make([]byte, 40000000)
rand.Read(token)
port := "8801"
go func() {
log.Debugf("starting TCP server on " + port)
server, err := net.Listen("tcp", "0.0.0.0:"+port)
if err != nil {
log.Error(err)
}
defer server.Close()
// spawn a new goroutine whenever a client connects
for {
connection, err := server.Accept()
if err != nil {
log.Error(err)
}
log.Debugf("client %s connected", connection.RemoteAddr().String())
go func(port string, connection net.Conn) {
c := comm.New(connection)
err = c.Send([]byte("hello, world"))
assert.Nil(t, err)
data, err := c.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte("hello, computer"), data)
data, err = c.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte{'\x00'}, data)
data, err = c.Receive()
assert.Nil(t, err)
assert.Equal(t, token, data)
}(port, connection)
}
}()
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"}
e, err := crypt.New(nil, nil)
assert.Nil(t, err)
assert.Nil(t, Send(a, e, m))
}

View File

@ -1,7 +0,0 @@
package models
// TCP_BUFFER_SIZE is the maximum packet size
const TCP_BUFFER_SIZE = 1024 * 64
// DEFAULT_RELAY is the default relay used (can be set using --relay)
const DEFAULT_RELAY = "142.93.177.120:9009"

View File

@ -1,290 +0,0 @@
package tcp
import (
"bytes"
"fmt"
"net"
"strings"
"sync"
"time"
"github.com/pkg/errors"
"github.com/schollz/croc/v6/src/comm"
"github.com/schollz/croc/v6/src/models"
log "github.com/schollz/logger"
)
type server struct {
port string
debugLevel string
banner string
rooms roomMap
}
type roomInfo struct {
first *comm.Comm
second *comm.Comm
opened time.Time
full bool
}
type roomMap struct {
rooms map[string]roomInfo
sync.Mutex
}
var timeToRoomDeletion = 10 * time.Minute
// Run starts a tcp listener, run async
func Run(debugLevel, port string, banner ...string) (err error) {
s := new(server)
s.port = port
s.debugLevel = debugLevel
if len(banner) > 0 {
s.banner = banner[0]
}
return s.start()
}
func (s *server) start() (err error) {
log.SetLevel(s.debugLevel)
s.rooms.Lock()
s.rooms.rooms = make(map[string]roomInfo)
s.rooms.Unlock()
// delete old rooms
go func() {
for {
time.Sleep(timeToRoomDeletion)
roomsToDelete := []string{}
s.rooms.Lock()
for room := range s.rooms.rooms {
if time.Since(s.rooms.rooms[room].opened) > 3*time.Hour {
roomsToDelete = append(roomsToDelete, room)
}
}
s.rooms.Unlock()
for _, room := range roomsToDelete {
s.deleteRoom(room)
}
}
}()
err = s.run()
if err != nil {
log.Error(err)
}
return
}
func (s *server) run() (err error) {
log.Infof("starting TCP server on " + s.port)
server, err := net.Listen("tcp", ":"+s.port)
if err != nil {
return errors.Wrap(err, "Error listening on :"+s.port)
}
defer server.Close()
// spawn a new goroutine whenever a client connects
for {
connection, err := server.Accept()
if err != nil {
return errors.Wrap(err, "problem accepting connection")
}
log.Debugf("client %s connected", connection.RemoteAddr().String())
go func(port string, connection net.Conn) {
errCommunication := s.clientCommuncation(port, comm.New(connection))
if errCommunication != nil {
log.Warnf("relay-%s: %s", connection.RemoteAddr().String(), errCommunication.Error())
}
}(s.port, connection)
}
}
func (s *server) clientCommuncation(port string, c *comm.Comm) (err error) {
// send ok to tell client they are connected
banner := s.banner
if len(banner) == 0 {
banner = "ok"
}
log.Debugf("sending '%s'", banner)
err = c.Send([]byte(banner + "|||" + c.Connection().RemoteAddr().String()))
if err != nil {
return
}
// wait for client to tell me which room they want
log.Debug("waiting for answer")
roomBytes, err := c.Receive()
if err != nil {
return
}
room := string(roomBytes)
s.rooms.Lock()
// create the room if it is new
if _, ok := s.rooms.rooms[room]; !ok {
s.rooms.rooms[room] = roomInfo{
first: c,
opened: time.Now(),
}
s.rooms.Unlock()
// tell the client that they got the room
err = c.Send([]byte("ok"))
if err != nil {
log.Error(err)
s.deleteRoom(room)
return
}
log.Debugf("room %s has 1", room)
return nil
}
if s.rooms.rooms[room].full {
s.rooms.Unlock()
err = c.Send([]byte("room full"))
if err != nil {
log.Error(err)
s.deleteRoom(room)
return
}
return nil
}
log.Debugf("room %s has 2", room)
s.rooms.rooms[room] = roomInfo{
first: s.rooms.rooms[room].first,
second: c,
opened: s.rooms.rooms[room].opened,
full: true,
}
otherConnection := s.rooms.rooms[room].first
s.rooms.Unlock()
// second connection is the sender, time to staple connections
var wg sync.WaitGroup
wg.Add(1)
// start piping
go func(com1, com2 *comm.Comm, wg *sync.WaitGroup) {
log.Debug("starting pipes")
pipe(com1.Connection(), com2.Connection())
wg.Done()
log.Debug("done piping")
}(otherConnection, c, &wg)
// tell the sender everything is ready
err = c.Send([]byte("ok"))
if err != nil {
s.deleteRoom(room)
return
}
wg.Wait()
// delete room
s.deleteRoom(room)
return nil
}
func (s *server) deleteRoom(room string) {
s.rooms.Lock()
defer s.rooms.Unlock()
if _, ok := s.rooms.rooms[room]; !ok {
return
}
log.Debugf("deleting room: %s", room)
if s.rooms.rooms[room].first != nil {
s.rooms.rooms[room].first.Close()
}
if s.rooms.rooms[room].second != nil {
s.rooms.rooms[room].second.Close()
}
s.rooms.rooms[room] = roomInfo{first: nil, second: nil}
delete(s.rooms.rooms, room)
}
// chanFromConn creates a channel from a Conn object, and sends everything it
// Read()s from the socket to the channel.
func chanFromConn(conn net.Conn) chan []byte {
c := make(chan []byte, 1)
go func() {
b := make([]byte, models.TCP_BUFFER_SIZE)
for {
n, err := conn.Read(b)
if n > 0 {
res := make([]byte, n)
// Copy the buffer so it doesn't get changed while read by the recipient.
copy(res, b[:n])
c <- res
}
if err != nil {
log.Debug(err)
c <- nil
break
}
}
log.Debug("exiting")
}()
return c
}
// pipe creates a full-duplex pipe between the two sockets and
// transfers data from one to the other.
func pipe(conn1 net.Conn, conn2 net.Conn) {
chan1 := chanFromConn(conn1)
chan2 := chanFromConn(conn2)
for {
select {
case b1 := <-chan1:
if b1 == nil {
return
}
conn2.Write(b1)
case b2 := <-chan2:
if b2 == nil {
return
}
conn1.Write(b2)
}
}
}
// ConnectToTCPServer will initiate a new connection
// to the specified address, room with optional time limit
func ConnectToTCPServer(address, room string, timelimit ...time.Duration) (c *comm.Comm, banner string, ipaddr string, err error) {
if len(timelimit) > 0 {
c, err = comm.NewConnection(address, timelimit[0])
} else {
c, err = comm.NewConnection(address)
}
if err != nil {
return
}
log.Debug("waiting for first ok")
data, err := c.Receive()
if err != nil {
return
}
banner = strings.Split(string(data), "|||")[0]
ipaddr = strings.Split(string(data), "|||")[1]
log.Debug("sending room")
err = c.Send([]byte(room))
if err != nil {
return
}
log.Debug("waiting for room confirmation")
data, err = c.Receive()
if err != nil {
return
}
if !bytes.Equal(data, []byte("ok")) {
err = fmt.Errorf("got bad response: %s", data)
return
}
log.Debug("all set")
return
}

View File

@ -1,37 +0,0 @@
package tcp
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestTCP(t *testing.T) {
timeToRoomDeletion = 100 * time.Millisecond
go Run("debug", "8281", "8282")
time.Sleep(100 * time.Millisecond)
c1, banner, _, err := ConnectToTCPServer("localhost:8281", "testRoom", 1*time.Minute)
assert.Equal(t, banner, "8282")
assert.Nil(t, err)
c2, _, _, err := ConnectToTCPServer("localhost:8281", "testRoom")
assert.Nil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "testRoom")
assert.NotNil(t, err)
_, _, _, err = ConnectToTCPServer("localhost:8281", "testRoom", 1*time.Nanosecond)
assert.NotNil(t, err)
// try sending data
assert.Nil(t, c1.Send([]byte("hello, c2")))
data, err := c2.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte("hello, c2"), data)
assert.Nil(t, c2.Send([]byte("hello, c1")))
data, err = c1.Receive()
assert.Nil(t, err)
assert.Equal(t, []byte("hello, c1"), data)
c1.Close()
time.Sleep(300 * time.Millisecond)
}

View File

@ -1,234 +0,0 @@
package utils
import (
"bufio"
"bytes"
"crypto/md5"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"io/ioutil"
"log"
"math"
"net"
"net/http"
"os"
"strings"
"github.com/cespare/xxhash"
"github.com/kalafut/imohash"
"github.com/schollz/mnemonicode"
)
// Exists reports whether the named file or directory exists.
func Exists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// GetInput returns the input with a given prompt
func GetInput(prompt string) string {
reader := bufio.NewReader(os.Stdin)
fmt.Fprintf(os.Stderr, "%s", prompt)
text, _ := reader.ReadString('\n')
return strings.TrimSpace(text)
}
// HashFile returns the hash of a file
func HashFile(fname string) (hash256 []byte, err error) {
return IMOHashFile(fname)
}
// MD5HashFile returns MD5 hash
func MD5HashFile(fname string) (hash256 []byte, err error) {
f, err := os.Open(fname)
if err != nil {
return
}
defer f.Close()
h := md5.New()
if _, err = io.Copy(h, f); err != nil {
return
}
hash256 = h.Sum(nil)
return
}
// IMOHashFile returns imohash
func IMOHashFile(fname string) (hash []byte, err error) {
b, err := imohash.SumFile(fname)
hash = b[:]
return
}
// XXHashFile returns the xxhash of a file
func XXHashFile(fname string) (hash256 []byte, err error) {
f, err := os.Open(fname)
if err != nil {
return
}
defer f.Close()
h := xxhash.New()
if _, err = io.Copy(h, f); err != nil {
return
}
hash256 = h.Sum(nil)
return
}
// SHA256 returns sha256 sum
func SHA256(s string) string {
sha := sha256.New()
sha.Write([]byte(s))
return fmt.Sprintf("%x", sha.Sum(nil))
}
// PublicIP returns public ip address
func PublicIP() (ip string, err error) {
resp, err := http.Get("https://canhazip.com")
if err != nil {
return
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
ip = strings.TrimSpace(string(bodyBytes))
}
return
}
// LocalIP returns local ip address
func LocalIP() string {
conn, err := net.Dial("udp", "8.8.8.8:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.String()
}
// GetRandomName returns mnemoicoded random name
func GetRandomName() string {
result := []string{}
bs := make([]byte, 4)
rand.Read(bs)
result = mnemonicode.EncodeWordList(result, bs)
return strings.Join(result, "-")
}
// ByteCountDecimal converts bytes to human readable byte string
func ByteCountDecimal(b int64) string {
const unit = 1000
if b < unit {
return fmt.Sprintf("%d B", b)
}
div, exp := int64(unit), 0
for n := b / unit; n >= unit; n /= unit {
div *= unit
exp++
}
return fmt.Sprintf("%.1f %cB", float64(b)/float64(div), "kMGTPE"[exp])
}
// MissingChunks returns the positions of missing chunks.
// If file doesn't exist, it returns an empty chunk list (all chunks).
// If the file size is not the same as requested, it returns an empty chunk list (all chunks).
func MissingChunks(fname string, fsize int64, chunkSize int) (chunkRanges []int64) {
f, err := os.Open(fname)
if err != nil {
return
}
defer f.Close()
fstat, err := os.Stat(fname)
if fstat.Size() != fsize || err != nil {
return
}
emptyBuffer := make([]byte, chunkSize)
chunkNum := 0
chunks := make([]int64, int64(math.Ceil(float64(fsize)/float64(chunkSize))))
var currentLocation int64
for {
buffer := make([]byte, chunkSize)
bytesread, err := f.Read(buffer)
if err != nil {
break
}
if bytes.Equal(buffer[:bytesread], emptyBuffer[:bytesread]) {
chunks[chunkNum] = currentLocation
chunkNum++
}
currentLocation += int64(bytesread)
}
if chunkNum == 0 {
chunkRanges = []int64{}
} else {
chunks = chunks[:chunkNum]
chunkRanges = []int64{int64(chunkSize), chunks[0]}
curCount := 0
for i, chunk := range chunks {
if i == 0 {
continue
}
curCount++
if chunk-chunks[i-1] > int64(chunkSize) {
chunkRanges = append(chunkRanges, int64(curCount))
chunkRanges = append(chunkRanges, chunk)
curCount = 0
}
}
chunkRanges = append(chunkRanges, int64(curCount+1))
chunks = chunkRanges
}
return
}
// ChunkRangesToChunks converts chunk ranges to list
func ChunkRangesToChunks(chunkRanges []int64) (chunks []int64) {
if len(chunkRanges) == 0 {
return
}
chunkSize := chunkRanges[0]
chunks = []int64{}
for i := 1; i < len(chunkRanges); i += 2 {
for j := int64(0); j < (chunkRanges[i+1]); j++ {
chunks = append(chunks, chunkRanges[i]+j*chunkSize)
}
}
return
}
// GetLocalIPs returns all local ips
func GetLocalIPs() (ips []string, err error) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return
}
ips = []string{}
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
ips = append(ips, ipnet.IP.String())
}
}
}
return
}

View File

@ -1,183 +0,0 @@
package utils
import (
"fmt"
"io/ioutil"
"log"
"math/rand"
"os"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func bigFile() {
rand.Seed(0)
bigBuff := make([]byte, 75000000)
rand.Read(bigBuff)
ioutil.WriteFile("bigfile.test", bigBuff, 0666)
}
func BenchmarkMD5(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
MD5HashFile("bigfile.test")
}
}
func BenchmarkXXHash(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
XXHashFile("bigfile.test")
}
}
func BenchmarkImoHash(b *testing.B) {
bigFile()
b.ResetTimer()
for i := 0; i < b.N; i++ {
IMOHashFile("bigfile.test")
}
}
func TestExists(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
fmt.Println(GetLocalIPs())
assert.True(t, Exists("bigfile.test"))
assert.False(t, Exists("doesnotexist"))
}
func TestMD5HashFile(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
b, err := MD5HashFile("bigfile.test")
assert.Nil(t, err)
assert.Equal(t, "9fed05acbacbc6a36555c998501c21f6", fmt.Sprintf("%x", b))
_, err = MD5HashFile("bigfile.test.nofile")
assert.NotNil(t, err)
}
func TestIMOHashFile(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
b, err := IMOHashFile("bigfile.test")
assert.Nil(t, err)
assert.Equal(t, "c0d1e123a96a598ea801cc503d3db8c0", fmt.Sprintf("%x", b))
}
func TestXXHashFile(t *testing.T) {
bigFile()
defer os.Remove("bigfile.test")
b, err := XXHashFile("bigfile.test")
assert.Nil(t, err)
assert.Equal(t, "f2da6ee7e75e8324", fmt.Sprintf("%x", b))
_, err = XXHashFile("nofile")
assert.NotNil(t, err)
}
func TestSHA256(t *testing.T) {
assert.Equal(t, "09ca7e4eaa6e8ae9c7d261167129184883644d07dfba7cbfbc4c8a2e08360d5b", SHA256("hello, world"))
}
func TestByteCountDecimal(t *testing.T) {
assert.Equal(t, "10.0 kB", ByteCountDecimal(10000))
assert.Equal(t, "50 B", ByteCountDecimal(50))
assert.Equal(t, "12.4 MB", ByteCountDecimal(12378517))
}
func TestMissingChunks(t *testing.T) {
fileSize := 100
chunkSize := 10
rand.Seed(1)
bigBuff := make([]byte, fileSize)
rand.Read(bigBuff)
ioutil.WriteFile("missing.test", bigBuff, 0644)
empty := make([]byte, chunkSize)
f, err := os.OpenFile("missing.test", os.O_RDWR, 0644)
assert.Nil(t, err)
for block := 0; block < fileSize/chunkSize; block++ {
if block == 0 || block == 4 || block == 5 || block >= 7 {
f.WriteAt(empty, int64(block*chunkSize))
}
}
f.Close()
chunkRanges := MissingChunks("missing.test", int64(fileSize), chunkSize)
assert.Equal(t, []int64{10, 0, 1, 40, 2, 70, 3}, chunkRanges)
chunks := ChunkRangesToChunks(chunkRanges)
assert.Equal(t, []int64{0, 40, 50, 70, 80, 90}, chunks)
os.Remove("missing.test")
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil {
panic(err)
}
if err := tmpfile.Close(); err != nil {
panic(err)
}
chunkRanges = MissingChunks(tmpfile.Name(), int64(len(content)), chunkSize)
assert.Empty(t, chunkRanges)
chunkRanges = MissingChunks(tmpfile.Name(), int64(len(content)+10), chunkSize)
assert.Empty(t, chunkRanges)
chunkRanges = MissingChunks(tmpfile.Name()+"ok", int64(len(content)), chunkSize)
assert.Empty(t, chunkRanges)
chunks = ChunkRangesToChunks(chunkRanges)
assert.Empty(t, chunks)
}
// func Test1(t *testing.T) {
// chunkRanges := MissingChunks("../../m/bigfile.test", int64(75000000), 1024*64/2)
// fmt.Println(chunkRanges)
// fmt.Println(ChunkRangesToChunks((chunkRanges)))
// assert.Nil(t, nil)
// }
func TestHashFile(t *testing.T) {
content := []byte("temporary file's content")
tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
log.Fatal(err)
}
defer os.Remove(tmpfile.Name()) // clean up
if _, err := tmpfile.Write(content); err != nil {
panic(err)
}
if err := tmpfile.Close(); err != nil {
panic(err)
}
hashed, err := HashFile(tmpfile.Name())
assert.Nil(t, err)
assert.Equal(t, "18c9673a4bb8325d323e7f24fda9ae1e", fmt.Sprintf("%x", hashed))
}
func TestPublicIP(t *testing.T) {
ip, err := PublicIP()
fmt.Println(ip)
assert.True(t, strings.Contains(ip, "."))
assert.Nil(t, err)
}
func TestLocalIP(t *testing.T) {
ip := LocalIP()
fmt.Println(ip)
assert.True(t, strings.Contains(ip, "."))
}
func TestGetRandomName(t *testing.T) {
name := GetRandomName()
assert.NotEmpty(t, name)
}

218
src/webrtc/webrtc.go Normal file
View File

@ -0,0 +1,218 @@
package webrtc
import (
"crypto/md5"
"crypto/rand"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/pion/webrtc/v2"
log "github.com/schollz/logger"
)
const (
bufferedAmountLowThreshold uint64 = 512 * 1024 // 512 KB
maxBufferedAmount uint64 = 1024 * 1024 // 1 MB
maxPacketSize uint64 = 65535
)
func setRemoteDescription(pc *webrtc.PeerConnection, sdp []byte) (err error) {
var desc webrtc.SessionDescription
err = json.Unmarshal(sdp, &desc)
if err != nil {
log.Error(err)
return
}
// Apply the desc as the remote description
err = pc.SetRemoteDescription(desc)
if err != nil {
log.Error(err)
}
return
}
func createOfferer(finished chan<- error) (pc *webrtc.PeerConnection, err error) {
// Prepare the configuration
config := webrtc.Configuration{
ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}},
}
// Create a new PeerConnection
pc, err = webrtc.NewPeerConnection(config)
if err != nil {
log.Error(err)
return
}
ordered := false
maxRetransmits := uint16(0)
options := &webrtc.DataChannelInit{
Ordered: &ordered,
MaxRetransmits: &maxRetransmits,
}
sendMoreCh := make(chan struct{})
// Create a datachannel with label 'data'
dc, err := pc.CreateDataChannel("data", options)
if err != nil {
log.Error(err)
return
}
// Register channel opening handling
sendData := func(buf []byte) error {
fmt.Printf("sent message: %x\n", md5.Sum(buf))
err := dc.Send(buf)
if err != nil {
return err
}
if dc.BufferedAmount()+uint64(len(buf)) > maxBufferedAmount {
// wait until the bufferedAmount becomes lower than the threshold
<-sendMoreCh
}
return nil
}
dc.OnOpen(func() {
fmt.Println(time.Now())
log.Debugf("OnOpen: %s-%d. Start sending a series of 1024-byte packets as fast as it can\n", dc.Label(), dc.ID())
its := 0
for {
buf := make([]byte, maxPacketSize)
rand.Read(buf)
its++
if its == 3 {
buf = []byte{1, 2, 3}
finished <- errors.New("done")
}
err2 := sendData(buf)
if err2 != nil {
finished <- err2
return
}
time.Sleep(1 * time.Second)
if its == 3 {
break
}
}
})
// Set bufferedAmountLowThreshold so that we can get notified when
// we can send more
dc.SetBufferedAmountLowThreshold(bufferedAmountLowThreshold)
// This callback is made when the current bufferedAmount becomes lower than the threadshold
dc.OnBufferedAmountLow(func() {
sendMoreCh <- struct{}{}
})
// Register the OnMessage to handle incoming messages
dc.OnMessage(func(dcMsg webrtc.DataChannelMessage) {
fmt.Printf("got message: %x\n", md5.Sum(dcMsg.Data))
})
return pc, nil
}
func test1() {
finished := make(chan error, 1)
var sender bool
flag.BoolVar(&sender, "sender", false, "set as sender")
flag.Parse()
log.SetLevel("debug")
if sender {
os.Remove("answer.json")
os.Remove("offer.json")
offerPC, err := createOfferer(finished)
if err != nil {
log.Error(err)
}
// Now, create an offer
offer, err := offerPC.CreateOffer(nil)
if err != nil {
log.Error(err)
}
err = offerPC.SetLocalDescription(offer)
if err != nil {
log.Error(err)
}
desc, err := json.Marshal(offer)
if err != nil {
log.Error(err)
}
fmt.Println(string(desc))
err = ioutil.WriteFile("offer.json", desc, 0644)
if err != nil {
log.Error(err)
}
// wait for answer
for {
b, errFile := ioutil.ReadFile("answer.json")
if errFile != nil {
time.Sleep(3 * time.Second)
continue
}
fmt.Println(string(b))
fmt.Println(time.Now())
err = setRemoteDescription(offerPC, b)
if err != nil {
log.Error(err)
}
break
}
} else {
answerPC, err := createOfferer(finished)
if err != nil {
log.Error(err)
}
b, err := ioutil.ReadFile("offer.json")
if err != nil {
log.Error(err)
}
err = setRemoteDescription(answerPC, b)
if err != nil {
log.Error(err)
}
answer, err := answerPC.CreateAnswer(nil)
if err != nil {
log.Error(err)
}
err = answerPC.SetLocalDescription(answer)
if err != nil {
log.Error(err)
}
desc2, err := json.Marshal(answer)
if err != nil {
log.Error(err)
}
fmt.Println(string(desc2))
err = ioutil.WriteFile("answer.json", desc2, 0644)
if err != nil {
log.Error(err)
}
}
// Block forever
err := <-finished
if err != nil {
fmt.Println(err)
} else {
fmt.Println("finished gracefully")
}
}