This commit is contained in:
parent
c708c17177
commit
8a439c74de
113
config.go
113
config.go
@ -26,7 +26,7 @@ func NewConfig(actionType string, actionData string) *Config {
|
||||
return &Config{ActionType: actionType, ActionData: actionData}
|
||||
}
|
||||
|
||||
func (config *Config) Validate() {
|
||||
func (config *Config) Validate() {
|
||||
if config.ActionType == "url" {
|
||||
// Parse URL in config
|
||||
urlParser, err := url.Parse(config.ActionData)
|
||||
@ -52,15 +52,21 @@ func (config *Config) CreateFile(dir string) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create config file at given directory
|
||||
configFile, err := os.Create(dir + "/config.json")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating config file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating config file")
|
||||
}
|
||||
// Close config file at the end of this function
|
||||
defer configFile.Close()
|
||||
// Marshal given Config struct into a []byte
|
||||
jsonData, err := json.Marshal(config)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error encoding JSON") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error encoding JSON")
|
||||
}
|
||||
// Write []byte to previously created config file
|
||||
bytesWritten, err := configFile.Write(jsonData)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing JSON to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing JSON to file")
|
||||
}
|
||||
// Log bytes written
|
||||
log.Info().Str("file", "config.json").Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||
}
|
||||
@ -73,23 +79,31 @@ func (config *Config) CollectFiles(dir string) {
|
||||
if config.ActionType == "file" {
|
||||
// Open file path in config.ActionData
|
||||
src, err := os.Open(config.ActionData)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error opening file from config") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening file from config")
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Create new file with the same name at given directory
|
||||
dst, err := os.Create(dir + "/" + filepath.Base(config.ActionData))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close new file at the end of this function
|
||||
defer dst.Close()
|
||||
// Copy data from source file to destination file
|
||||
_, err = io.Copy(dst, src)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error copying data to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error copying data to file")
|
||||
}
|
||||
// Replace file path in config.ActionData with file name
|
||||
config.ActionData = filepath.Base(config.ActionData)
|
||||
} else if config.ActionType == "dir" {
|
||||
// Create tar archive
|
||||
tarFile, err := os.Create(dir + "/" + filepath.Base(config.ActionData) + ".tar")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close tar file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create writer for tar archive
|
||||
@ -99,27 +113,41 @@ func (config *Config) CollectFiles(dir string) {
|
||||
// Walk given directory
|
||||
err = filepath.Walk(config.ActionData, func(path string, info os.FileInfo, err error) error {
|
||||
// Return if error walking
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Skip if file is not normal mode
|
||||
if !info.Mode().IsRegular() { return nil }
|
||||
if !info.Mode().IsRegular() {
|
||||
return nil
|
||||
}
|
||||
// Create tar header for file
|
||||
header, err := tar.FileInfoHeader(info, info.Name())
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Change header name to reflect decompressed filepath
|
||||
header.Name = strings.TrimPrefix(strings.ReplaceAll(path, config.ActionData, ""), string(filepath.Separator))
|
||||
// Write header to archive
|
||||
if err := tarArchiver.WriteHeader(header); err != nil { return err }
|
||||
if err := tarArchiver.WriteHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
// Open source file
|
||||
src, err := os.Open(path)
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Copy source bytes to tar archive
|
||||
if _, err := io.Copy(tarArchiver, src); err != nil { return err }
|
||||
if _, err := io.Copy(tarArchiver, src); err != nil {
|
||||
return err
|
||||
}
|
||||
// Return at the end of the function
|
||||
return nil
|
||||
})
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating tar archive") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating tar archive")
|
||||
}
|
||||
// Set config data to base path for receiver
|
||||
config.ActionData = filepath.Base(config.ActionData)
|
||||
}
|
||||
@ -131,10 +159,14 @@ func (config *Config) ReadFile(filePath string) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Read file at filePath
|
||||
fileData, err := ioutil.ReadFile(filePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading config file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading config file")
|
||||
}
|
||||
// Unmarshal data from JSON into config struct
|
||||
err = json.Unmarshal(fileData, config)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decoding JSON") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decoding JSON")
|
||||
}
|
||||
}
|
||||
|
||||
// Execute action specified in config
|
||||
@ -145,18 +177,24 @@ func (config *Config) ExecuteAction(srcDir string, destDir string) {
|
||||
if config.ActionType == "file" {
|
||||
// Open file from config at given directory
|
||||
src, err := os.Open(srcDir + "/" + config.ActionData)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading file from config") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading file from config")
|
||||
}
|
||||
// Close source file at the end of this function
|
||||
defer src.Close()
|
||||
// Create file in user's Downloads directory
|
||||
dst, err := os.Create(filepath.Clean(destDir) + "/" + config.ActionData)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close destination file at the end of this function
|
||||
defer dst.Close()
|
||||
// Copy data from source file to destination file
|
||||
_, err = io.Copy(dst, src)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error copying data to file") }
|
||||
// If action is url
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error copying data to file")
|
||||
}
|
||||
// If action is url
|
||||
} else if config.ActionType == "url" {
|
||||
// Parse received URL
|
||||
urlParser, err := url.Parse(config.ActionData)
|
||||
@ -164,34 +202,41 @@ func (config *Config) ExecuteAction(srcDir string, destDir string) {
|
||||
if err != nil {
|
||||
// Alert user of invalid url
|
||||
log.Fatal().Err(err).Msg("Invalid URL")
|
||||
// If scheme is not detected
|
||||
// If scheme is not detected
|
||||
} else if urlParser.Scheme == "" {
|
||||
// Alert user of invalid scheme
|
||||
log.Fatal().Msg("Invalid URL scheme")
|
||||
// If host is not detected
|
||||
// If host is not detected
|
||||
} else if urlParser.Host == "" {
|
||||
// Alert user of invalid host
|
||||
log.Fatal().Msg("Invalid URL host")
|
||||
}
|
||||
// Attempt to open URL in browser
|
||||
err = browser.OpenURL(config.ActionData)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error opening browser") }
|
||||
// If action is dir
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening browser")
|
||||
}
|
||||
// If action is dir
|
||||
} else if config.ActionType == "dir" {
|
||||
// Set destination directory to ~/Downloads/{dir name}
|
||||
dstDir := filepath.Clean(destDir) + "/" + config.ActionData
|
||||
// Try to create destination directory
|
||||
err := os.MkdirAll(dstDir, 0755)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating directory") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating directory")
|
||||
}
|
||||
// Try to open tar archive file
|
||||
tarFile, err := os.Open(srcDir + "/" + config.ActionData + ".tar")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error opening tar archive") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening tar archive")
|
||||
}
|
||||
// Close tar archive file at the end of this function
|
||||
defer tarFile.Close()
|
||||
// Create tar reader to unarchive tar archive
|
||||
tarUnarchiver := tar.NewReader(tarFile)
|
||||
// Loop to recursively unarchive tar file
|
||||
unarchiveLoop: for {
|
||||
unarchiveLoop:
|
||||
for {
|
||||
// Jump to next header in tar archive
|
||||
header, err := tarUnarchiver.Next()
|
||||
switch {
|
||||
@ -215,15 +260,19 @@ func (config *Config) ExecuteAction(srcDir string, destDir string) {
|
||||
_ = os.MkdirAll(filepath.Dir(targetPath), 0755)
|
||||
// Create file with mode contained in header at target path
|
||||
dstFile, err := os.OpenFile(targetPath, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file during unarchiving") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file during unarchiving")
|
||||
}
|
||||
// Copy data from tar archive into file
|
||||
_, err = io.Copy(dstFile, tarUnarchiver)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error copying data to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error copying data to file")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Catchall
|
||||
// Catchall
|
||||
} else {
|
||||
// Log unknown action type
|
||||
log.Fatal().Msg("Unknown action type " + config.ActionType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,9 @@ func DiscoverReceivers() ([]string, []string) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create zeroconf resolver
|
||||
resolver, err := zeroconf.NewResolver(nil)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating zeroconf resolver") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating zeroconf resolver")
|
||||
}
|
||||
// Create channel for zeroconf entries
|
||||
entries := make(chan *zeroconf.ServiceEntry)
|
||||
// Create slice to store hostnames of discovered receivers
|
||||
@ -39,7 +41,9 @@ func DiscoverReceivers() ([]string, []string) {
|
||||
defer cancel()
|
||||
// Browse for mDNS entries
|
||||
err = resolver.Browse(ctx, "_opensend._tcp", "local.", entries)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error browsing zeroconf services") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error browsing zeroconf services")
|
||||
}
|
||||
|
||||
// Send Done signal to context
|
||||
<-ctx.Done()
|
||||
@ -53,7 +57,9 @@ func RegisterService() func() {
|
||||
hostname, _ := os.Hostname()
|
||||
// Register zeroconf service {hostname}._opensend._tcp.local.
|
||||
server, err := zeroconf.Register(hostname, "_opensend._tcp", "local.", 9797, []string{"txtv=0", "lo=1", "la=2"}, nil)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error registering zeroconf service") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error registering zeroconf service")
|
||||
}
|
||||
// Return server.Shutdown() function to allow for shutdown in main()
|
||||
return server.Shutdown
|
||||
}
|
||||
}
|
||||
|
@ -24,20 +24,28 @@ func CompressAndEncryptFile(filePath string, newFilePath string, sharedKey strin
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Read data from file
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error opening file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error opening file")
|
||||
}
|
||||
// Create buffer for compressed data
|
||||
compressedBuffer := new(bytes.Buffer)
|
||||
// Create Zstd encoder
|
||||
zstdEncoder, err := zstd.NewWriter(compressedBuffer)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating Zstd encoder") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating Zstd encoder")
|
||||
}
|
||||
// Copy file data to Zstd encoder
|
||||
_, err = io.Copy(zstdEncoder, file)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading file")
|
||||
}
|
||||
// Close Zstd encoder
|
||||
zstdEncoder.Close()
|
||||
// Read compressed data into data variable
|
||||
data, err := ioutil.ReadAll(compressedBuffer)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading compressed buffer") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading compressed buffer")
|
||||
}
|
||||
// Create md5 hash of password in order to make it the required size
|
||||
md5Hash := md5.New()
|
||||
md5Hash.Write([]byte(sharedKey))
|
||||
@ -45,25 +53,35 @@ func CompressAndEncryptFile(filePath string, newFilePath string, sharedKey strin
|
||||
hashedKey := hex.EncodeToString(md5Hash.Sum(nil))
|
||||
// Create new AES cipher
|
||||
block, err := aes.NewCipher([]byte(hashedKey))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating AES cipher") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating AES cipher")
|
||||
}
|
||||
// Create GCM for AES cipher
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating GCM")
|
||||
}
|
||||
// Make byte slice for nonce
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
// Read random bytes into nonce slice
|
||||
_, err = io.ReadFull(rand.Reader, nonce)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating nonce") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating nonce")
|
||||
}
|
||||
// Encrypt data
|
||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
||||
// Create new file
|
||||
newFile, err := os.Create(newFilePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Defer file close
|
||||
defer newFile.Close()
|
||||
// Write ciphertext to new file
|
||||
bytesWritten, err := newFile.Write(ciphertext)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing to file")
|
||||
}
|
||||
// Log bytes written and to which file
|
||||
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||
}
|
||||
@ -74,7 +92,9 @@ func DecryptAndDecompressFile(filePath string, newFilePath string, sharedKey str
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Read data from file
|
||||
data, err := ioutil.ReadFile(filePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading file")
|
||||
}
|
||||
// Create md5 hash of password in order to make it the required size
|
||||
md5Hash := md5.New()
|
||||
md5Hash.Write([]byte(sharedKey))
|
||||
@ -83,25 +103,35 @@ func DecryptAndDecompressFile(filePath string, newFilePath string, sharedKey str
|
||||
block, _ := aes.NewCipher([]byte(hashedKey))
|
||||
// Create GCM for AES cipher
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating GCM") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating GCM")
|
||||
}
|
||||
// Get standard GCM nonce size
|
||||
nonceSize := gcm.NonceSize()
|
||||
// Get nonce and ciphertext from data
|
||||
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
|
||||
// Decrypt data
|
||||
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decrypting data") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decrypting data")
|
||||
}
|
||||
// Create new Zstd decoder
|
||||
zstdDecoder, err := zstd.NewReader(bytes.NewBuffer(plaintext))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating Zstd decoder") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating Zstd decoder")
|
||||
}
|
||||
// Create new file
|
||||
newFile, err := os.Create(newFilePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close new file at the end of this function
|
||||
defer newFile.Close()
|
||||
// Write decompressed plaintext to new file
|
||||
bytesWritten, err := io.Copy(newFile, zstdDecoder)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing to file")
|
||||
}
|
||||
zstdDecoder.Close()
|
||||
// Log bytes written and to which file
|
||||
log.Info().Str("file", filepath.Base(newFilePath)).Msg("Wrote " + strconv.Itoa(int(bytesWritten)) + " bytes")
|
||||
@ -114,19 +144,25 @@ func EncryptFiles(dir string, sharedKey string) {
|
||||
// Walk given directory
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
// If error reading, return err
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If file is not a directory and is not the key
|
||||
if !info.IsDir() && !strings.Contains(path, "key.aes"){
|
||||
if !info.IsDir() && !strings.Contains(path, "key.aes") {
|
||||
// Compress and Encrypt the file using shared key, appending .zst.enc
|
||||
CompressAndEncryptFile(path, path + ".zst.enc", sharedKey)
|
||||
CompressAndEncryptFile(path, path+".zst.enc", sharedKey)
|
||||
// Remove unencrypted file
|
||||
err := os.Remove(path)
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Return nil if no error occurs
|
||||
return nil
|
||||
})
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error encrypting files") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error encrypting files")
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt files in given directory using shared key
|
||||
@ -136,7 +172,9 @@ func DecryptFiles(dir string, sharedKey string) {
|
||||
// Walk given directory
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
// If error reading, return err
|
||||
if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// If file is not a directory and is encrypted
|
||||
if !info.IsDir() && strings.Contains(path, ".enc") {
|
||||
// Decrypt and decompress the file using the shared key, removing .zst.enc
|
||||
@ -145,5 +183,7 @@ func DecryptFiles(dir string, sharedKey string) {
|
||||
// Return nil if no errors occurred
|
||||
return nil
|
||||
})
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decrypting files") }
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decrypting files")
|
||||
}
|
||||
}
|
||||
|
95
files.go
95
files.go
@ -23,12 +23,16 @@ func SaveEncryptedKey(encryptedKey []byte, filePath string) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create file at given file path
|
||||
keyFile, err := os.Create(filePath)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Close file at the end of this function
|
||||
defer keyFile.Close()
|
||||
// Write encrypted key to file
|
||||
bytesWritten, err := keyFile.Write(encryptedKey)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing key to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing key to file")
|
||||
}
|
||||
// Log bytes written
|
||||
log.Info().Str("file", filepath.Base(filePath)).Msg("Wrote " + strconv.Itoa(bytesWritten) + " bytes")
|
||||
}
|
||||
@ -39,42 +43,59 @@ func SendFiles(dir string) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create TCP listener on port 9898
|
||||
listener, err := net.Listen("tcp", ":9898")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error starting listener")
|
||||
}
|
||||
// Accept connection on listener
|
||||
connection, err := listener.Accept()
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error accepting connection") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error accepting connection")
|
||||
}
|
||||
// Close connection at the end of this function
|
||||
defer connection.Close()
|
||||
// Create for loop to listen for messages on connection
|
||||
connectionLoop: for {
|
||||
connectionLoop:
|
||||
for {
|
||||
// Use ConsoleWriter logger with TCPFatalHook
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(TCPFatalHook{conn: connection})
|
||||
// Attempt to read new message on connection
|
||||
data, err := bufio.NewReader(connection).ReadString('\n')
|
||||
// If no message detected, try again
|
||||
if err != nil && err.Error() == "EOF" { continue }
|
||||
if err != nil && err.Error() == "EOF" {
|
||||
continue
|
||||
}
|
||||
// If non-EOF error, fatally log
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading data") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading data")
|
||||
}
|
||||
// Process received data
|
||||
processedData := strings.Split(strings.TrimSpace(data), ";")
|
||||
// If processedData is empty, alert the user of invalid data
|
||||
if len(processedData) < 1 { log.Fatal().Str("data", data).Msg("Received data invalid") }
|
||||
if len(processedData) < 1 {
|
||||
log.Fatal().Str("data", data).Msg("Received data invalid")
|
||||
}
|
||||
switch processedData[0] {
|
||||
case "key":
|
||||
// Inform user client has requested key
|
||||
log.Info().Msg("Key requested")
|
||||
// Read saved key
|
||||
key, err := ioutil.ReadFile(dir + "/key.aes")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading key")
|
||||
}
|
||||
// Write saved key to ResponseWriter
|
||||
_, err = fmt.Fprintln(connection, "OK;" + hex.EncodeToString(key) + ";")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||
_, err = fmt.Fprintln(connection, "OK;"+hex.EncodeToString(key)+";")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing response")
|
||||
}
|
||||
case "index":
|
||||
// Inform user a client has requested the file index
|
||||
log.Info().Msg("Index requested")
|
||||
// Get directory listing
|
||||
dirListing, err := ioutil.ReadDir(dir)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading directory") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading directory")
|
||||
}
|
||||
// Create new slice to house filenames for index
|
||||
var indexSlice []string
|
||||
// For each file in listing
|
||||
@ -88,8 +109,10 @@ func SendFiles(dir string) {
|
||||
// Join index slice into string
|
||||
indexStr := strings.Join(indexSlice, "|")
|
||||
// Write index to ResponseWriter
|
||||
_, err = fmt.Fprintln(connection, "OK;" + indexStr + ";")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||
_, err = fmt.Fprintln(connection, "OK;"+indexStr+";")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing response")
|
||||
}
|
||||
case "file":
|
||||
// If processedData only has one entry
|
||||
if len(processedData) == 1 {
|
||||
@ -114,8 +137,10 @@ func SendFiles(dir string) {
|
||||
log.Info().Str("file", file).Msg("File requested")
|
||||
}
|
||||
// Write file as hex to connection
|
||||
_, err = fmt.Fprintln(connection, "OK;" + hex.EncodeToString(fileData) + ";")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing response") }
|
||||
_, err = fmt.Fprintln(connection, "OK;"+hex.EncodeToString(fileData)+";")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing response")
|
||||
}
|
||||
case "stop":
|
||||
// Alert user that stop signal has been received
|
||||
log.Info().Msg("Received stop signal")
|
||||
@ -162,40 +187,56 @@ func RecvFiles(connection net.Conn) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Request index from sender
|
||||
_, err := fmt.Fprintln(connection, "index;")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error sending index request") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error sending index request")
|
||||
}
|
||||
// Read received message
|
||||
message, err := bufio.NewReader(connection).ReadString('\n')
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error getting index") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting index")
|
||||
}
|
||||
// Process received message
|
||||
procMessage := strings.Split(strings.TrimSpace(message), ";")
|
||||
// If non-ok code returned, fatally log
|
||||
if procMessage[0] != "OK" { log.Fatal().Err(err).Msg("Sender reported error") }
|
||||
if procMessage[0] != "OK" {
|
||||
log.Fatal().Err(err).Msg("Sender reported error")
|
||||
}
|
||||
// Get index from message
|
||||
index := strings.Split(strings.TrimSpace(procMessage[1]), "|")
|
||||
for _, file := range index {
|
||||
// Get current file in index
|
||||
_, err = fmt.Fprintln(connection, "file;" + file + ";")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error sending file request") }
|
||||
_, err = fmt.Fprintln(connection, "file;"+file+";")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error sending file request")
|
||||
}
|
||||
// Read received message
|
||||
message, err := bufio.NewReader(connection).ReadString('\n')
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error getting file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting file")
|
||||
}
|
||||
// Process received message
|
||||
procMessage := strings.Split(message, ";")
|
||||
// If non-ok code returned
|
||||
if procMessage[0] != "OK" {
|
||||
// fatally log
|
||||
log.Fatal().Err(err).Msg("Sender reported error")
|
||||
// Otherwise
|
||||
// Otherwise
|
||||
} else {
|
||||
// Create new file at index filepath
|
||||
newFile, err := os.Create(opensendDir + "/" + file)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error creating file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error creating file")
|
||||
}
|
||||
// Decode file data from hex string
|
||||
fileData, err := hex.DecodeString(strings.TrimSpace(procMessage[1]))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decoding hex") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decoding hex")
|
||||
}
|
||||
// Copy response body to new file
|
||||
bytesWritten, err := io.Copy(newFile, bytes.NewBuffer(fileData))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error writing to file") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error writing to file")
|
||||
}
|
||||
// Log bytes written
|
||||
log.Info().Str("file", filepath.Base(file)).Msg("Wrote " + strconv.Itoa(int(bytesWritten)) + " bytes")
|
||||
// Close new file
|
||||
@ -210,4 +251,4 @@ func SendSrvStopSignal(connection net.Conn) {
|
||||
_, _ = fmt.Fprintln(connection, "stop;")
|
||||
// Close connection
|
||||
_ = connection.Close()
|
||||
}
|
||||
}
|
||||
|
32
keyCrypto.go
32
keyCrypto.go
@ -20,7 +20,9 @@ func GenerateRSAKeypair() (*rsa.PrivateKey, *rsa.PublicKey) {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Generate private/public RSA keypair
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error generating RSA keypair") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error generating RSA keypair")
|
||||
}
|
||||
// Get public key
|
||||
publicKey := privateKey.PublicKey
|
||||
// Return keypair
|
||||
@ -33,23 +35,31 @@ func GetKey(connection net.Conn) []byte {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Send key request to connection
|
||||
_, err := fmt.Fprintln(connection, "key;")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error sending key request") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error sending key request")
|
||||
}
|
||||
// Read received message
|
||||
message, err := bufio.NewReader(connection).ReadString('\n')
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error getting key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting key")
|
||||
}
|
||||
// Process received message
|
||||
procMessage := strings.Split(strings.TrimSpace(message), ";")
|
||||
// If ok code returned
|
||||
if procMessage[0] == "OK" {
|
||||
// Decode received hex string into key
|
||||
key, err := hex.DecodeString(procMessage[1])
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error reading key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error reading key")
|
||||
}
|
||||
// Return key
|
||||
return key
|
||||
// Otherwise
|
||||
// Otherwise
|
||||
} else {
|
||||
// Fatally log
|
||||
if err != nil { log.Fatal().Msg("Server reported error") }
|
||||
if err != nil {
|
||||
log.Fatal().Msg("Server reported error")
|
||||
}
|
||||
}
|
||||
// Return nil if all else fails
|
||||
return nil
|
||||
@ -61,7 +71,9 @@ func EncryptKey(sharedKey string, recvPubKey *rsa.PublicKey) []byte {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Encrypt shared key using RSA
|
||||
encryptedSharedKey, err := rsa.EncryptOAEP(sha256.New(), rand.Reader, recvPubKey, []byte(sharedKey), nil)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error encrypting shared key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error encrypting shared key")
|
||||
}
|
||||
// Return encrypted key
|
||||
return encryptedSharedKey
|
||||
}
|
||||
@ -70,9 +82,11 @@ func EncryptKey(sharedKey string, recvPubKey *rsa.PublicKey) []byte {
|
||||
func DecryptKey(encryptedKey []byte, privateKey *rsa.PrivateKey) string {
|
||||
// Decrypt shared key using RSA
|
||||
decryptedKey, err := rsa.DecryptOAEP(sha256.New(), rand.Reader, privateKey, encryptedKey, nil)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decrypting shared key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decrypting shared key")
|
||||
}
|
||||
// Get string of decrypted key
|
||||
sharedKey := string(decryptedKey)
|
||||
// Return shared key
|
||||
return sharedKey
|
||||
}
|
||||
}
|
||||
|
@ -15,20 +15,26 @@ func ReceiverKeyExchange(key *rsa.PublicKey) string {
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Create TCP listener on port 9797
|
||||
listener, err := net.Listen("tcp", ":9797")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error starting listener") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error starting listener")
|
||||
}
|
||||
// Create string for sender address
|
||||
var senderAddr string
|
||||
for {
|
||||
// Accept connection on listener
|
||||
connection, err := listener.Accept()
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error accepting connections") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error accepting connections")
|
||||
}
|
||||
// Get sender address and store it in senderAddr
|
||||
senderAddr = connection.RemoteAddr().String()
|
||||
// Create gob encoder with connection as io.Writer
|
||||
encoder := gob.NewEncoder(connection)
|
||||
// Encode key into connection
|
||||
err = encoder.Encode(key)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error encoding key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error encoding key")
|
||||
}
|
||||
return senderAddr
|
||||
}
|
||||
}
|
||||
@ -38,15 +44,19 @@ func SenderKeyExchange(receiverIP string) *rsa.PublicKey {
|
||||
// Use ConsoleWriter logger
|
||||
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Hook(FatalHook{})
|
||||
// Connect to TCP socket on receiver IP port 9797
|
||||
connection, err := net.Dial("tcp", receiverIP + ":9797")
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error connecting to sender") }
|
||||
connection, err := net.Dial("tcp", receiverIP+":9797")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error connecting to sender")
|
||||
}
|
||||
// Create gob decoder
|
||||
decoder := gob.NewDecoder(connection)
|
||||
// Instantiate rsa.PublicKey struct
|
||||
recvPubKey := &rsa.PublicKey{}
|
||||
// Decode key
|
||||
err = decoder.Decode(recvPubKey)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error decoding key") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error decoding key")
|
||||
}
|
||||
// Return received key
|
||||
return recvPubKey
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
// Fatal hook to run in case of Fatal error
|
||||
type FatalHook struct {}
|
||||
type FatalHook struct{}
|
||||
|
||||
// Run function on trigger
|
||||
func (hook FatalHook) Run(_ *zerolog.Event, level zerolog.Level, _ string) {
|
||||
@ -36,4 +36,4 @@ func (hook TCPFatalHook) Run(_ *zerolog.Event, level zerolog.Level, _ string) {
|
||||
_ = os.RemoveAll(opensendDir)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
30
main.go
30
main.go
@ -25,7 +25,9 @@ func main() {
|
||||
|
||||
// Get user's home directory
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error getting home directory") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error getting home directory")
|
||||
}
|
||||
// Define opensend directory as ~/.opensend
|
||||
opensendDir = homeDir + "/.opensend"
|
||||
|
||||
@ -50,11 +52,11 @@ func main() {
|
||||
// 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", homeDir + "/Downloads", "Destination directory for files or dirs sent over opensend")
|
||||
destDir := flag.String("dest-dir", homeDir+"/Downloads", "Destination directory for files or dirs sent over opensend")
|
||||
// Create --skip-mdns to skip service registration
|
||||
skipMdns := flag.Bool("skip-mdns", false, "Skip zeroconf service registration (use if mdns fails)")
|
||||
// Create -t flag for type
|
||||
actionType := flag.String("t", "","Type of data being sent")
|
||||
actionType := flag.String("t", "", "Type of data being sent")
|
||||
// Create -d flag for data
|
||||
actionData := flag.String("d", "", "Data to send")
|
||||
// Create -s flag for sending
|
||||
@ -75,7 +77,9 @@ func main() {
|
||||
sharedKeyBytes := make([]byte, 32)
|
||||
// Read random bytes into buffer
|
||||
_, err := io.ReadFull(rand.Reader, sharedKeyBytes)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error generating random bytes") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error generating random bytes")
|
||||
}
|
||||
// Encode random bytes to hexadecimal
|
||||
sharedKey := hex.EncodeToString(sharedKeyBytes)
|
||||
// Notify user a key has been created
|
||||
@ -88,7 +92,7 @@ func main() {
|
||||
log.Info().Msg("IP provided. Skipping discovery.")
|
||||
// Set chosen IP to provided
|
||||
choiceIP = *sendTo
|
||||
// Otherwise
|
||||
// Otherwise
|
||||
} else {
|
||||
// Notify user device discovery is beginning
|
||||
log.Info().Msg("Discovering opensend receivers")
|
||||
@ -106,7 +110,9 @@ func main() {
|
||||
choiceStr, _ := reader.ReadString('\n')
|
||||
// Convert input to int after trimming spaces
|
||||
choiceInt, err := strconv.Atoi(strings.TrimSpace(choiceStr))
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error converting choice to int") }
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error converting choice to int")
|
||||
}
|
||||
// Set choiceIndex to choiceInt-1 to allow for indexing
|
||||
choiceIndex := choiceInt - 1
|
||||
// Get IP of chosen receiver
|
||||
@ -129,7 +135,7 @@ func main() {
|
||||
// Encrypt shared key using RSA public key
|
||||
key := EncryptKey(sharedKey, rawKey)
|
||||
// Save encrypted key in opensend directory as key.aes
|
||||
SaveEncryptedKey(key, opensendDir + "/key.aes")
|
||||
SaveEncryptedKey(key, opensendDir+"/key.aes")
|
||||
// Notify user file encryption is beginning
|
||||
log.Info().Msg("Encrypting files")
|
||||
// Encrypt all files in opensend directory using shared key
|
||||
@ -138,7 +144,7 @@ func main() {
|
||||
log.Info().Msg("Server started on port 9898")
|
||||
// Send all files in opensend directory using an HTTP server on port 9898
|
||||
SendFiles(opensendDir)
|
||||
// If -r given
|
||||
// If -r given
|
||||
} else if *recvFlag {
|
||||
// If --skip-mdns is not given
|
||||
if !*skipMdns {
|
||||
@ -156,7 +162,7 @@ func main() {
|
||||
// Exchange keys with sender
|
||||
senderIP := ReceiverKeyExchange(publicKey)
|
||||
// Sleep 300ms to allow sender time to start HTTP server
|
||||
time.Sleep(300*time.Millisecond)
|
||||
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
|
||||
@ -187,5 +193,7 @@ func main() {
|
||||
}
|
||||
// Remove opensend directory
|
||||
err = os.RemoveAll(opensendDir)
|
||||
if err != nil { log.Fatal().Err(err).Msg("Error removing opensend directory") }
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Error removing opensend directory")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user