This repository has been archived on 2021-07-08. You can view files and clone it, but cannot push or open issues or pull requests.
opensend/main.go

308 lines
11 KiB
Go
Raw Normal View History

/*
Copyright © 2021 Arsen Musayelyan
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
2020-12-03 10:12:43 +00:00
package main
import (
"bufio"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
2021-06-19 07:01:31 +00:00
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
flag "github.com/spf13/pflag"
2020-12-03 10:12:43 +00:00
)
2020-12-31 07:54:18 +00:00
var workDir *string
var destDir *string
2020-12-03 10:12:43 +00:00
func main() {
// Use ConsoleWriter logger
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
2020-12-03 10:12:43 +00:00
2020-12-31 07:54:18 +00:00
// Create --send-to flag to send to a specific IP
sendTo := flag.String("send-to", "", "Use IP address of receiver instead of mDNS")
// Create --dest-dir flag to save to a specified folder
destDir = flag.String("dest-dir", "", "Destination directory for files or dirs sent over opensend")
// Create --work-dir flag to perform operations in a specified directory
2020-12-31 07:54:18 +00:00
workDir = flag.String("work-dir", "", "Working directory for opensend")
// Create --config to select config file to use
2020-12-31 07:54:18 +00:00
givenCfgPath := flag.String("config", "", "Opensend config to use")
// Create --skip-mdns to skip service registration
2021-01-09 02:34:08 +00:00
skipMdns := flag.Bool("skip-mdns", false, "Skip zeroconf service registration (use if mdns fails)")
2020-12-31 07:54:18 +00:00
// Create -t flag for type
2021-01-09 02:34:08 +00:00
actionType := flag.StringP("type", "t", "", "Type of data being sent")
2020-12-31 07:54:18 +00:00
// Create -d flag for data
2021-01-09 02:34:08 +00:00
actionData := flag.StringP("data", "d", "", "Data to send")
2020-12-31 07:54:18 +00:00
// Create -s flag for sending
2021-01-09 02:34:08 +00:00
sendFlag := flag.BoolP("send", "s", false, "Send data")
2020-12-31 07:54:18 +00:00
// Create -r flag for receiving
2021-01-09 02:34:08 +00:00
recvFlag := flag.BoolP("receive", "r", false, "Receive data")
targetFlag := flag.StringP("target", "T", "", "Target as defined in opensend.toml")
2021-01-30 04:42:15 +00:00
loopFlag := flag.BoolP("loop", "L", false, "Continuously wait for connections and handle them concurrently")
2020-12-31 07:54:18 +00:00
// Parse flags
flag.Parse()
2021-01-09 02:34:08 +00:00
// Declare config variable
var config *Config
// If config flag not provided
if *givenCfgPath == "" {
// Get config path
confPath := GetConfigPath()
// Read config at path
config = NewConfig(confPath)
} else {
// Otherwise, read config at provided path
2020-12-31 07:54:18 +00:00
config = NewConfig(*givenCfgPath)
}
2021-01-09 02:34:08 +00:00
// If work directory flag not provided
2020-12-31 07:54:18 +00:00
if *workDir == "" {
2021-01-09 02:34:08 +00:00
// If send flag provided
2020-12-31 07:54:18 +00:00
if *sendFlag {
2021-01-09 02:34:08 +00:00
// Set work directory to sender as defined in config
2020-12-31 07:54:18 +00:00
*workDir = ExpandPath(config.Sender.WorkDir)
} else {
2021-01-09 02:34:08 +00:00
// Otherwise set work directory to receiver as defined in config
2020-12-31 07:54:18 +00:00
*workDir = ExpandPath(config.Receiver.WorkDir)
}
2020-12-21 07:18:42 +00:00
}
2020-12-03 10:12:43 +00:00
// If destination directory flag not provided
if *destDir == "" {
// If receiver flag provided
if *recvFlag {
// Set destination directory to receiver as defined in config
*destDir = ExpandPath(config.Receiver.DestDir)
}
}
2021-01-09 02:34:08 +00:00
// If target flag provided
if *targetFlag != "" {
// Set IP to target's IP
*sendTo = config.Targets[*targetFlag].IP
}
2020-12-03 10:12:43 +00:00
// Create channel for signals
sig := make(chan os.Signal, 1)
// Send message on channel upon reception of SIGINT or SIGTERM
2021-06-19 07:01:31 +00:00
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
2020-12-03 10:12:43 +00:00
// Intercept signal
go func() {
2021-06-19 07:01:31 +00:00
signal := <-sig
// Warn user that a signal has been received and that opensend is shutting down
log.Warn().Str("signal", signal.String()).Msg("Signal received. Shutting down.")
// Remove opensend directory to avoid future conflicts
_ = os.RemoveAll(*workDir)
// Exit with code 0
os.Exit(0)
2020-12-03 10:12:43 +00:00
}()
// Create opensend dir ignoring errors
2020-12-31 07:54:18 +00:00
_ = os.Mkdir(*workDir, 0755)
2020-12-03 10:12:43 +00:00
// If -s given
if *sendFlag {
2020-12-03 21:01:27 +00:00
if *actionType == "" || *actionData == "" {
log.Fatal().Msg("Valid action type and data is required to send")
}
2020-12-03 16:58:10 +00:00
// Create 32 byte buffer
sharedKeyBytes := make([]byte, 32)
// Read random bytes into buffer
_, err := io.ReadFull(rand.Reader, sharedKeyBytes)
2020-12-21 07:18:42 +00:00
if err != nil {
log.Fatal().Err(err).Msg("Error generating random bytes")
}
2020-12-03 16:58:10 +00:00
// Encode random bytes to hexadecimal
sharedKey := hex.EncodeToString(sharedKeyBytes)
// Notify user a key has been created
log.Info().Msg("Generated random shared key")
2020-12-03 21:01:27 +00:00
// Create variable to store chosen IP
var choiceIP string
// If IP is provided via --send-to
if *sendTo != "" {
// Notify user that provided IP is being used
log.Info().Msg("IP provided. Skipping discovery.")
// Set chosen IP to provided
choiceIP = *sendTo
2020-12-21 07:18:42 +00:00
// Otherwise
2020-12-03 21:01:27 +00:00
} else {
// Notify user device discovery is beginning
log.Info().Msg("Discovering opensend receivers")
// Discover all _opensend._tcp.local. mDNS services
discoveredReceivers, discoveredIPs := DiscoverReceivers()
// Create reader for STDIN
reader := bufio.NewReader(os.Stdin)
// Print hostnames of each receiver
for index, receiver := range discoveredReceivers {
// Print hostname and index+1
fmt.Println("["+strconv.Itoa(index+1)+"]", receiver)
}
// Prompt user for choice
fmt.Print("Choose a receiver: ")
choiceStr, _ := reader.ReadString('\n')
// Convert input to int after trimming spaces
choiceInt, err := strconv.Atoi(strings.TrimSpace(choiceStr))
2020-12-21 07:18:42 +00:00
if err != nil {
log.Fatal().Err(err).Msg("Error converting choice to int")
}
2020-12-03 21:01:27 +00:00
// Set choiceIndex to choiceInt-1 to allow for indexing
choiceIndex := choiceInt - 1
// Get IP of chosen receiver
choiceIP = discoveredIPs[choiceIndex]
2020-12-03 10:12:43 +00:00
}
// Instantiate Config object
2020-12-31 07:54:18 +00:00
parameters := NewParameters(*actionType, *actionData)
// Validate data in config struct
2020-12-31 07:54:18 +00:00
parameters.Validate()
// Collect any files that may be required for transaction into opensend directory
2020-12-31 07:54:18 +00:00
parameters.CollectFiles(*workDir)
// Create config file in opensend directory
2020-12-31 07:54:18 +00:00
parameters.CreateFile(*workDir)
2020-12-03 16:58:10 +00:00
// Notify user of key exchange
log.Info().Msg("Performing key exchange")
2020-12-03 10:12:43 +00:00
// Exchange RSA keys with receiver
rawKey := SenderKeyExchange(choiceIP)
2020-12-03 16:58:10 +00:00
// Inform user receiver key has been received
log.Info().Msg("Receiver key received")
2020-12-03 10:12:43 +00:00
// Encrypt shared key using RSA public key
key := EncryptKey(sharedKey, rawKey)
2020-12-05 06:45:38 +00:00
// Save encrypted key in opensend directory as key.aes
2020-12-31 07:54:18 +00:00
SaveEncryptedKey(key, *workDir+"/key.aes")
2020-12-03 16:58:10 +00:00
// Notify user file encryption is beginning
log.Info().Msg("Encrypting files")
2020-12-03 10:12:43 +00:00
// Encrypt all files in opensend directory using shared key
2020-12-31 07:54:18 +00:00
EncryptFiles(*workDir, sharedKey)
2020-12-03 16:58:10 +00:00
// Notify user server has started
log.Info().Msg("Server started on port 9898")
2020-12-03 10:12:43 +00:00
// Send all files in opensend directory using an HTTP server on port 9898
2020-12-31 07:54:18 +00:00
SendFiles(*workDir)
2021-01-30 04:42:15 +00:00
} else if *recvFlag && *loopFlag {
// Declare zeroconf shutdown variable
var zeroconfShutdown func()
for {
// Create opensend dir ignoring errors
_ = os.Mkdir(*workDir, 0755)
// If --skip-mdns is not given
if !*skipMdns {
// Register {hostname}._opensend._tcp.local. mDNS service and pass shutdown function
zeroconfShutdown = RegisterService()
}
// Notify user keypair is being generated
log.Info().Msg("Generating RSA keypair")
// Generate keypair
privateKey, publicKey := GenerateRSAKeypair()
// Notify user opensend is waiting for key exchange
log.Info().Msg("Waiting for sender key exchange")
// Exchange keys with sender
senderIP := ReceiverKeyExchange(publicKey)
// If --skip-mdns is not given
if !*skipMdns {
// Shutdown zeroconf service as connection will be unavailable during transfer
zeroconfShutdown()
}
// Sleep 300ms to allow sender time to start HTTP server
time.Sleep(300 * time.Millisecond)
// Notify user files are being received
log.Info().Msg("Receiving files from server (This may take a while)")
// Connect to sender's TCP socket
2021-06-19 07:01:31 +00:00
connection := NewSender(senderIP)
2021-01-30 04:42:15 +00:00
// Get files from sender and place them into the opensend directory
RecvFiles(connection)
// Get encrypted shared key from sender
encryptedKey := GetKey(connection)
// Send stop signal to sender's HTTP server
SendSrvStopSignal(connection)
// Decrypt shared key
sharedKey := DecryptKey(encryptedKey, privateKey)
// Notify user file decryption is beginning
log.Info().Msg("Decrypting files")
// Decrypt all files in opensend directory using shared key
DecryptFiles(*workDir, sharedKey)
// Instantiate Config
parameters := &Parameters{}
// Read config file in opensend directory
2021-06-19 07:01:31 +00:00
parameters.ReadFile(*workDir + "/parameters.msgpack")
2021-01-30 04:42:15 +00:00
// Notify user that action is being executed
2021-06-19 07:01:31 +00:00
log.Info().Msg("Executing action")
// Execute MessagePack action using files within opensend directory
2021-01-30 04:42:15 +00:00
parameters.ExecuteAction(*workDir, *destDir)
// Remove opensend directory
err := os.RemoveAll(*workDir)
if err != nil {
log.Fatal().Err(err).Msg("Error removing opensend directory")
}
}
2020-12-03 10:12:43 +00:00
} else if *recvFlag {
// If --skip-mdns is not given
if !*skipMdns {
// Register {hostname}._opensend._tcp.local. mDNS service and pass shutdown function
zeroconfShutdown := RegisterService()
// Shutdown zeroconf server at the end of main()
defer zeroconfShutdown()
}
2020-12-05 06:45:38 +00:00
// Notify user keypair is being generated
log.Info().Msg("Generating RSA keypair")
2020-12-03 10:12:43 +00:00
// Generate keypair
privateKey, publicKey := GenerateRSAKeypair()
2020-12-05 06:45:38 +00:00
// Notify user opensend is waiting for key exchange
log.Info().Msg("Waiting for sender key exchange")
2020-12-03 10:12:43 +00:00
// Exchange keys with sender
senderIP := ReceiverKeyExchange(publicKey)
// Sleep 300ms to allow sender time to start HTTP server
2020-12-21 07:18:42 +00:00
time.Sleep(300 * time.Millisecond)
2020-12-03 16:58:10 +00:00
// Notify user files are being received
log.Info().Msg("Receiving files from server (This may take a while)")
// Connect to sender's TCP socket
2021-06-19 07:01:31 +00:00
sender := NewSender(senderIP)
2020-12-03 10:12:43 +00:00
// Get files from sender and place them into the opensend directory
2021-06-19 07:01:31 +00:00
RecvFiles(sender)
2020-12-03 10:12:43 +00:00
// Get encrypted shared key from sender
2021-06-19 07:01:31 +00:00
encryptedKey := GetKey(sender)
2020-12-03 10:12:43 +00:00
// Send stop signal to sender's HTTP server
2021-06-19 07:01:31 +00:00
SendSrvStopSignal(sender)
2020-12-03 10:12:43 +00:00
// Decrypt shared key
sharedKey := DecryptKey(encryptedKey, privateKey)
2020-12-03 16:58:10 +00:00
// Notify user file decryption is beginning
log.Info().Msg("Decrypting files")
2020-12-03 10:12:43 +00:00
// Decrypt all files in opensend directory using shared key
2020-12-31 07:54:18 +00:00
DecryptFiles(*workDir, sharedKey)
2020-12-03 10:12:43 +00:00
// Instantiate Config
2020-12-31 07:54:18 +00:00
parameters := &Parameters{}
2020-12-03 10:12:43 +00:00
// Read config file in opensend directory
2021-06-19 07:01:31 +00:00
parameters.ReadFile(*workDir + "/parameters.msgpack")
2020-12-03 16:58:10 +00:00
// Notify user that action is being executed
2021-06-19 07:01:31 +00:00
log.Info().Msg("Executing Action")
// Execute MessagePack action using files within opensend directory
2020-12-31 07:54:18 +00:00
parameters.ExecuteAction(*workDir, *destDir)
2020-12-03 21:01:27 +00:00
} else {
flag.Usage()
2020-12-03 21:01:27 +00:00
log.Fatal().Msg("You must choose sender or receiver mode using -s or -r")
2020-12-03 10:12:43 +00:00
}
// Remove opensend directory
2020-12-31 07:54:18 +00:00
err := os.RemoveAll(*workDir)
2020-12-21 07:18:42 +00:00
if err != nil {
log.Fatal().Err(err).Msg("Error removing opensend directory")
}
}