-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
1,049 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Build tunnel | ||
FROM docker.io/library/golang:latest AS build | ||
WORKDIR /build | ||
COPY . . | ||
|
||
RUN make build-tunnel; \ | ||
chmod +x mdrop-tunnel; | ||
|
||
# Create Private Environment | ||
FROM docker.io/library/alpine:latest | ||
LABEL org.opencontainers.image.authors="Ikramullah <[email protected]>,Syahrial Agni Prasetya <[email protected]>" | ||
WORKDIR / | ||
|
||
RUN set -ex; \ | ||
apk update; \ | ||
apk add openssh bash; | ||
|
||
COPY ./tunnel.conf /etc/ssh/sshd_config.d/ | ||
COPY ./entrypoint.sh . | ||
COPY --from=build /build/mdrop-tunnel /usr/bin/mdrop-tunnel | ||
|
||
RUN adduser tunnel -HD; \ | ||
passwd tunnel -d; \ | ||
ssh-keygen -A; | ||
|
||
EXPOSE 22 | ||
|
||
ENTRYPOINT [ "/entrypoint.sh" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
os-archs=darwin:amd64 darwin:arm64 freebsd:amd64 linux:amd64 linux:arm:7 linux:arm:5 linux:arm64 windows:amd64 windows:arm64 linux:mips64 linux:mips64le linux:mips:softfloat linux:mipsle:softfloat linux:riscv64 android:arm64 | ||
|
||
restore: | ||
go get -C ./cmd/mdrop-client | ||
go get -C ./cmd/mdrop-tunnel-tools | ||
go get -C ./internal | ||
|
||
build-client: restore | ||
go build -C ./cmd/mdrop-client -ldflags="-linkmode external -extldflags -static -w -s" -o "../../mdrop" | ||
|
||
build-tunnel: restore | ||
go build -C ./cmd/mdrop-tunnel-tools -ldflags="-linkmode external -extldflags -static -w -s" -o "../../mdrop-tunnel" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
# mdrop | ||
|
||
TCP-over-SSH P2P Tunneling Based File Transfer. Inspired by ShareDrop and Apple AirDrop. Written on Golang and C#. | ||
TCP-over-SSH P2P Tunneling Based File Transfer. Inspired by ShareDrop and Apple AirDrop. | ||
|
||
*Reserved.* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/mplus-oss/mdrop/internal" | ||
) | ||
|
||
func AuthCommand() { | ||
errChan := make(chan error, 0) | ||
|
||
// Set prompt | ||
config, err := authPrompt() | ||
if err != nil { | ||
internal.PrintErrorWithExit("authPromptError", err, 1) | ||
} | ||
|
||
// Connect to tunnel | ||
fmt.Println("Connecting to tunnel...") | ||
sshConfig := internal.SSHParameter{ | ||
ConfigFile: config, | ||
Command: []string{"ping"}, | ||
} | ||
go func() { | ||
sshOutput, err := StartShellTunnel(sshConfig, false) | ||
if err != nil { | ||
errChan <- err | ||
} | ||
if strings.Contains(sshOutput, "Pong!") { | ||
errChan <- nil | ||
} else { | ||
errChan <- errors.New("Unexpected output from Tunnel") | ||
} | ||
}() | ||
err = <-errChan | ||
if err != nil { | ||
internal.PrintErrorWithExit("authTunnelError", err, 1) | ||
} | ||
|
||
// Write config file | ||
err = config.WriteConfig() | ||
if err != nil { | ||
internal.PrintErrorWithExit("authWriteConfig", err, 1) | ||
} | ||
} | ||
|
||
func authPrompt() (config internal.ConfigFile, err error) { | ||
reader := bufio.NewReader(os.Stdin) | ||
|
||
fmt.Print("Hostname: ") | ||
hostname, err := reader.ReadString('\n') | ||
if err != nil { | ||
return internal.ConfigFile{}, err | ||
} | ||
hostname = strings.Replace(hostname, "\n", "", -1) | ||
|
||
fmt.Print("Port [22]: ") | ||
port, err := reader.ReadString('\n') | ||
if err != nil { | ||
return internal.ConfigFile{}, err | ||
} | ||
port = strings.Replace(port, "\n", "", -1) | ||
if port == "" { | ||
port = "22" | ||
} | ||
portInt, err := strconv.Atoi(port) | ||
if err != nil { | ||
return internal.ConfigFile{}, err | ||
} | ||
|
||
fmt.Print("Proxy [Set blank if none]: ") | ||
proxy, err := reader.ReadString('\n') | ||
if err != nil { | ||
return internal.ConfigFile{}, err | ||
} | ||
proxy = strings.Replace(proxy, "\n", "", -1) | ||
|
||
config = internal.ConfigFile{ | ||
Host: hostname, | ||
Port: portInt, | ||
Proxy: proxy, | ||
} | ||
return config, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package main | ||
|
||
import ( | ||
"bufio" | ||
"crypto/sha256" | ||
"errors" | ||
"flag" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/mplus-oss/mdrop/internal" | ||
"github.com/schollz/progressbar/v3" | ||
) | ||
|
||
func GetCommand(args []string) { | ||
errChan := make(chan error, 1) | ||
|
||
reader := bufio.NewReader(os.Stdin) | ||
flag := flag.NewFlagSet("mdrop get", flag.ExitOnError) | ||
var ( | ||
help = flag.Bool("help", false, "Print this message") | ||
fileNameOpt = flag.String("file", "", "Set filename") | ||
localPort = flag.Int("localPort", 6000, "Specified sender port remoted on local") | ||
) | ||
flag.Parse(args) | ||
|
||
token := flag.Arg(0) | ||
if *help || token == "" { | ||
fmt.Println("Command: mdrop get [options] <token>") | ||
flag.Usage() | ||
os.Exit(1) | ||
} | ||
|
||
// Parse token | ||
sender := TokenTransferJSON{} | ||
err := sender.ParseToken(token, &sender) | ||
if err != nil { | ||
internal.PrintErrorWithExit("getParseTokenError", err, 1) | ||
} | ||
|
||
// Parse Config File | ||
var config internal.ConfigFile | ||
err = config.ParseConfig(&config) | ||
if err != nil { | ||
internal.PrintErrorWithExit("getParseConfigError", err, 1) | ||
} | ||
if sender.Host != config.Host { | ||
internal.PrintErrorWithExit("getHostNotMatch", errors.New("Host not match"), 1) | ||
} | ||
|
||
// Create tunnel before remote | ||
fmt.Println("Connecting to tunnel for fetch port...") | ||
sshConfig := internal.SSHParameter{ | ||
ConfigFile: config, | ||
Command: []string{"connect"}, | ||
LocalPort: *localPort, | ||
RemotePort: sender.RemotePort, | ||
IsRemote: false, | ||
} | ||
go func() { | ||
_, err := StartShellTunnel(sshConfig, true) | ||
if err != nil { | ||
errChan <- err | ||
} | ||
}() | ||
|
||
// Check tunnel | ||
fmt.Println("Connecting to sender...") | ||
GetTcpReadyConnect(*localPort) | ||
client := http.Client{} | ||
resp, err := client.Get( | ||
fmt.Sprintf("http://localhost:%v/checksum", *localPort), | ||
) | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendHttpClientChecksum", err, 1) | ||
} | ||
if resp.StatusCode != http.StatusOK { | ||
internal.PrintErrorWithExit("sendHttpClientResponseChecksum", err, 1) | ||
} | ||
checksumBytes, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendHttpClientReadChecksum", err, 1) | ||
} | ||
checksum := string(checksumBytes) | ||
|
||
resp, err = client.Post( | ||
fmt.Sprintf("http://localhost:%v/receive", *localPort), | ||
"binary/octet-stream", | ||
nil, | ||
) | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendHttpClient", err, 1) | ||
} | ||
if resp.StatusCode != http.StatusOK { | ||
internal.PrintErrorWithExit("sendHttpClientResponse", err, 1) | ||
} | ||
defer resp.Body.Close() | ||
|
||
// Set filename from header or from output | ||
fileName := resp.Header.Get("X-Attachment-Name") | ||
if fileName == "" { | ||
internal.PrintErrorWithExit("sendHttpClientInvalidAttachmentName", err, 1) | ||
} | ||
if *fileNameOpt != "" { | ||
fileName = *fileNameOpt | ||
} | ||
fmt.Println("File found:", fileName) | ||
|
||
// Check if there's duplicate file | ||
filePath, err := os.Getwd() | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendFileWorkDir", err, 1) | ||
} | ||
if fileStatus, _ := os.Stat(filePath+"/"+fileName); fileStatus != nil { | ||
fmt.Print("There's duplicate file. Action? [(R)eplace/R(e)name/(C)ancel] [Default: R] -> ") | ||
prompt, err := reader.ReadString('\n') | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendPromptError", err, 1) | ||
} | ||
prompt = strings.Replace(prompt, "\n", "", -1) | ||
if strings.ToLower(prompt) == "c" { | ||
internal.PrintErrorWithExit("sendPromptCancel", errors.New("Canceled by action"), 0) | ||
} | ||
if strings.ToLower(prompt) == "e" { | ||
fmt.Print("Change filename ["+fileName+"]: ") | ||
prompt, err = reader.ReadString('\n') | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendPromptError", err, 1) | ||
} | ||
prompt = strings.Replace(prompt, "\n", "", -1) | ||
if prompt == fileName { | ||
internal.PrintErrorWithExit("sendPromptDuplicateFilename", errors.New("Canceled by action"), 0) | ||
} | ||
fileName = prompt | ||
} | ||
} | ||
filePath += "/"+fileName | ||
|
||
// Create file | ||
file, err := os.Create(filePath) | ||
if err != nil { | ||
internal.PrintErrorWithExit("sendFileCreation", err, 1) | ||
} | ||
|
||
// Downloading file | ||
fmt.Println("Downloading...") | ||
bar := progressbar.DefaultBytes(resp.ContentLength, fileName) | ||
_, err = io.Copy(io.MultiWriter(bar, file), resp.Body) | ||
if err != nil { | ||
errMsg := err.Error() | ||
if strings.Contains(errMsg, "EOF") { | ||
err = errors.New("Broken pipe from sender because forced close or terminated.") | ||
} | ||
internal.PrintErrorWithExit("sendStreamFile", err, 1) | ||
} | ||
|
||
// Check checksum | ||
fmt.Println("Checking checksum...") | ||
fileDownloaded, err := os.Open(filePath) | ||
if err != nil { | ||
internal.PrintErrorWithExit("checksumFileOpen", err, 1) | ||
} | ||
hash := sha256.New() | ||
if _, err := io.Copy(hash, fileDownloaded); err != nil { | ||
internal.PrintErrorWithExit("checksumHashSum", err, 1) | ||
} | ||
checksumLocal := fmt.Sprintf("%x", hash.Sum(nil)) | ||
if checksumLocal != checksum { | ||
internal.PrintErrorWithExit("checksumMismatch", errors.New("Checksum mismatch with sender"), 1) | ||
} | ||
fileDownloaded.Close() | ||
|
||
fmt.Println("Download success!") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/mplus-oss/mdrop/internal" | ||
) | ||
|
||
func GetTcpReadyConnect(localPort int) { | ||
isConnected := false | ||
for i := 1; i <= 60; i++ { | ||
timeout := time.Second | ||
_, err := net.DialTimeout("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(localPort)), timeout) | ||
if err == nil { | ||
isConnected = true | ||
break | ||
} | ||
time.Sleep(timeout) | ||
} | ||
|
||
if isConnected { | ||
fmt.Println("Tunnel is ready!") | ||
} else { | ||
internal.PrintErrorWithExit("getTcpReadyConnectError", errors.New("Tunnel is timeout."), 1) | ||
} | ||
} |
Oops, something went wrong.